diff --git a/src/cli/transaction_sign.go b/src/cli/transaction_sign.go index b769819..d3b7e81 100644 --- a/src/cli/transaction_sign.go +++ b/src/cli/transaction_sign.go @@ -1,7 +1,9 @@ package cli import ( + "errors" "fmt" + "github.com/skycoin/hardware-wallet-go/src/skywallet/wire" "os" "runtime" @@ -88,89 +90,89 @@ func transactionSignCmd() gcli.Command { } if len(inputs) > 7 || len(outputs) > 7 { - println("Start big transaction protocol.") + state := 0 + index := 0 + // Building an send first SignTx message msg, err := device.SignTx(len(outputs), len(inputs), coinName, version, lockTime, txHash) if err != nil { log.Error(err) return } - if msg.Kind != uint16(messages.MessageType_MessageType_TxRequest) { - log.Error("Unexpected response message type from hardware wallet.") - return - } - txRequest := &messages.TxRequest{} - err = proto.Unmarshal(msg.Data, txRequest) - if err != nil { - log.Error(err) - return - } - if *txRequest.RequestType != messages.TxRequest_TXINPUT { - log.Error("Unexpected reuqest type") - return - } - - var txInputs []*messages.TxAck_TransactionType_TxInputType - var txOutputs []*messages.TxAck_TransactionType_TxOutputType - - // Sending Inputs - for i, input := range inputs { - if len(txInputs) == 7 { - msg, err := device.TxAck(txInputs, []*messages.TxAck_TransactionType_TxOutputType{}, version, lockTime) - if err != nil { - log.Error(err) - return - } - if msg.Kind != uint16(messages.MessageType_MessageType_TxRequest) { - log.Error("Unexpected response message type from hardware wallet.") - return - } - txInputs = []*messages.TxAck_TransactionType_TxInputType{} - } - var txInput messages.TxAck_TransactionType_TxInputType - txInput.AddressN = []uint32{*proto.Uint32(uint32(inputIndex[i]))} - txInput.HashIn = proto.String(input) - txInputs = append(txInputs, &txInput) - } - if len(txInputs) != 0 { - _, err := device.TxAck(txInputs, txOutputs, version, lockTime) + for { if err != nil { log.Error(err) return } - txInputs = []*messages.TxAck_TransactionType_TxInputType{} - } - - // Sending Outputs - for i, output := range outputs { - if len(txOutputs) == 7 { - msg, err := device.TxAck(txInputs, txOutputs, version, lockTime) + switch msg.Kind { + case uint16(messages.MessageType_MessageType_TxRequest): + txRequest := &messages.TxRequest{} + err = proto.Unmarshal(msg.Data, txRequest) if err != nil { log.Error(err) return } - if msg.Kind != uint16(messages.MessageType_MessageType_TxRequest) { - log.Error("Unexpected response message type from hardware wallet.") + switch *txRequest.RequestType { + case messages.TxRequest_TXINPUT: + if state == 0 { + // Sending Inputs + msg, err = sendInputs(device,&inputs,&inputIndex,version,lockTime,&index,&state) + } else if state == 2 { + // Printing Signatures + txRequest := &messages.TxRequest{} + err = proto.Unmarshal(msg.Data, txRequest) + if err != nil { + log.Error(err) + return + } + for _, sign := range txRequest.SignResult { + println(*sign.Signature) + } + // Sending Inputs for signatures + msg, err = sendInputs(device,&inputs,&inputIndex,version,lockTime,&index,&state) + } else { + log.Error("Protocol error: unexpected TxRequest type") + return + } + case messages.TxRequest_TXOUTPUT: + if state == 1 { + // Sending Outputs + msg, err = sendOutputs(device,&outputs,&addressIndex,&coins,&hours,version,lockTime,&index,&state) + } else { + log.Error("Protocol error: unexpected TxRequest type") + return + } + case messages.TxRequest_TXFINISHED: + if state == 3 { + txRequest := &messages.TxRequest{} + err = proto.Unmarshal(msg.Data, txRequest) + if err != nil { + log.Error(err) + return + } + for _, sign := range txRequest.SignResult { + println(*sign.Signature) + } + return + } else { + log.Error("protocol error: unexpected TXFINISHED message") + return + } + } + case uint16(messages.MessageType_MessageType_Failure): + failMsg, err := skyWallet.DecodeFailMsg(msg) + if err != nil { + log.Error(err) return } - txOutputs = []*messages.TxAck_TransactionType_TxOutputType{} - } - var txOutput messages.TxAck_TransactionType_TxOutputType - txOutput.Address = proto.String(output) - if i < len(addressIndex) { - txOutput.AddressN = []uint32{uint32(addressIndex[i])} - } - txOutput.Coins = proto.Uint64(uint64(coins[i])) - txOutput.Hours = proto.Uint64(uint64(hours[i])) - txOutputs = append(txOutputs, &txOutput) - } - if len(txOutputs) != 0 { - _, err := device.TxAck(txInputs, txOutputs, version, lockTime) - if err != nil { - log.Error(err) + fmt.Printf("Failed with message: %s\n", failMsg) + return + case uint16(messages.MessageType_MessageType_ButtonRequest): + msg, err = device.ButtonAck() + default: + log.Error("Unexpected response message type from hardware wallet.") return } - txOutputs = []*messages.TxAck_TransactionType_TxOutputType{} } } else { var transactionInputs []*messages.SkycoinTransactionInput @@ -253,3 +255,49 @@ func transactionSignCmd() gcli.Command { }, } } + +func sendInputs(device *skyWallet.Device, inputs *[]string, inputIndex *[]int, version int, lockTime int,index *int,state *int)(wire.Message, error){ + var txInputs []*messages.TxAck_TransactionType_TxInputType + startIndex := *index + for i, input := range (*inputs)[*index:] { + if len(txInputs) == 7 { + return device.TxAck(txInputs, []*messages.TxAck_TransactionType_TxOutputType{}, version, lockTime) + } + var txInput messages.TxAck_TransactionType_TxInputType + txInput.AddressN = []uint32{*proto.Uint32(uint32((*inputIndex)[startIndex+i]))} + txInput.HashIn = proto.String(input) + txInputs = append(txInputs, &txInput) + *index++ + } + if len(txInputs) != 0 { + *index = 0 + *state++ + return device.TxAck(txInputs, nil, version, lockTime) + } + return wire.Message{}, errors.New("empty inputs") +} + +func sendOutputs(device *skyWallet.Device, outputs *[]string, addressIndex *[]int, coins *[]int64, hours *[]int64, version int, lockTime int, index *int, state *int)(wire.Message, error){ + var txOutputs []*messages.TxAck_TransactionType_TxOutputType + startIndex := *index + for i, output := range (*outputs)[*index:] { + if len(txOutputs) == 7 { + return device.TxAck(nil, txOutputs, version, lockTime) + } + var txOutput messages.TxAck_TransactionType_TxOutputType + txOutput.Address = proto.String(output) + if i < len(*addressIndex) { + txOutput.AddressN = []uint32{uint32((*addressIndex)[startIndex+i])} + } + txOutput.Coins = proto.Uint64(uint64((*coins)[startIndex+i])) + txOutput.Hours = proto.Uint64(uint64((*hours)[startIndex+i])) + txOutputs = append(txOutputs, &txOutput) + *index++ + } + if len(txOutputs) != 0 { + *index = 0 + *state++ + return device.TxAck(nil, txOutputs, version, lockTime) + } + return wire.Message{}, errors.New("empty outputs") +}