From b468c82d8a2ba5cf3936da4d55e443bc635462da Mon Sep 17 00:00:00 2001 From: Roman Gelembjuk Date: Tue, 1 May 2018 10:38:41 +0300 Subject: [PATCH] refactoring. remove references to tranactions from wallets --- lib/nodeclient/nodeclient.go | 31 +++++++++++++++++++++++ lib/transaction/transaction.go | 17 +++++++++++++ lib/utils/crypt.go | 41 ++++++++++++++++++++++++++++++ lib/wallet/cli.go | 17 +++---------- node/node_transactions.go | 42 +++++++++++++++++++++++++------ node/nodeserver.go | 3 +++ node/nodeserver_handle.go | 46 ++++++++++++++++++++++++++++------ wallet/main.go | 3 ++- 8 files changed, 171 insertions(+), 29 deletions(-) diff --git a/lib/nodeclient/nodeclient.go b/lib/nodeclient/nodeclient.go index 82f6bed..4f5fa40 100644 --- a/lib/nodeclient/nodeclient.go +++ b/lib/nodeclient/nodeclient.go @@ -69,6 +69,13 @@ type ComNewTransaction struct { TX []byte } +// New Transaction Data command. It includes prepared TX and signatures for imputs +type ComNewTransactionData struct { + Address string + TX []byte + Signatures [][]byte +} + // To Request new transaction by wallet. // Wallet sends address where to send and amount to send // and own pubkey. Server returns transaction but wihout signatures @@ -353,6 +360,30 @@ func (c *NodeClient) SendNewTransaction(addr netlib.NodeAddr, from string, tx [] return NewTX, nil } +// Send new transaction from a wallet to a node +func (c *NodeClient) SendNewTransactionData(addr netlib.NodeAddr, from string, txBytes []byte, signatures [][]byte) ([]byte, error) { + data := ComNewTransactionData{} + data.Address = from + data.TX = txBytes + data.Signatures = signatures + + request, err := c.BuildCommandData("txdata", &data) + + NewTXID := []byte{} + + if err != nil { + return nil, err + } + + err = c.SendDataWaitResponse(addr, request, &NewTXID) + + if err != nil { + return nil, err + } + // no data are returned. only success or not + return NewTXID, nil +} + // Request to prepare new transaction by wallet. // It returns a transaction without signature. // Wallet has to sign it and then use SendNewTransaction to send completed transaction diff --git a/lib/transaction/transaction.go b/lib/transaction/transaction.go index 1e27ad4..b4b97ee 100644 --- a/lib/transaction/transaction.go +++ b/lib/transaction/transaction.go @@ -234,6 +234,23 @@ func (tx *Transaction) SignData(privKey ecdsa.PrivateKey, PubKey []byte, DataToS return nil } +// Sets signatures for inputs. Signatures were created separately for data set prepared before +// in the function PrepareSignData +func (tx *Transaction) SetSignatures(signatures [][]byte) error { + if tx.IsCoinbase() { + return nil + } + + for inID, _ := range tx.Vin { + + tx.Vin[inID].Signature = signatures[inID] + } + // when transaction is complete, we can add ID to it + tx.Hash() + + return nil +} + // Verify verifies signatures of Transaction inputs // And total amount of inputs and outputs func (tx *Transaction) Verify(prevTXs map[int]*Transaction) error { diff --git a/lib/utils/crypt.go b/lib/utils/crypt.go index 7c973a6..2ecedce 100644 --- a/lib/utils/crypt.go +++ b/lib/utils/crypt.go @@ -50,3 +50,44 @@ func VerifySignature(signature []byte, message []byte, PubKey []byte) (bool, err return ecdsa.Verify(&rawPubKey, data, &r, &s), nil } + +func SignDataSet(PubKey []byte, privKey ecdsa.PrivateKey, dataSetsToSign [][]byte) ([][]byte, error) { + signatures := [][]byte{} + + for _, dataToSign := range dataSetsToSign { + + attempt := 1 + + var signature []byte + var err error + + for { + // we can do more 1 attempt to sign. we found some cases where verification of signature fails + // we don't know the reason + signature, err = SignData(privKey, dataToSign) + + if err != nil { + return nil, err + } + + attempt = attempt + 1 + + v, err := VerifySignature(signature, dataToSign, PubKey) + + if err != nil { + return nil, err + } + + if v { + break + } + + if attempt > 10 { + break + } + } + signatures = append(signatures, signature) + } + + return signatures, nil +} diff --git a/lib/wallet/cli.go b/lib/wallet/cli.go index 2cfd99a..c085b14 100644 --- a/lib/wallet/cli.go +++ b/lib/wallet/cli.go @@ -8,7 +8,6 @@ import ( "github.com/gelembjuk/democoin/lib/net" "github.com/gelembjuk/democoin/lib/nodeclient" - "github.com/gelembjuk/democoin/lib/transaction" "github.com/gelembjuk/democoin/lib/utils" ) @@ -291,24 +290,16 @@ func (wc *WalletCLI) commandSend() error { if err != nil { return err } + // Sign transaction data + signatures, err := utils.SignDataSet(walletobj.GetPublicKey(), walletobj.GetPrivateKey(), DataToSign) - TX := transaction.Transaction{} - TX.DeserializeTransaction(TXBytes) - // Sign transaction. - TX.SignData(walletobj.GetPrivateKey(), walletobj.GetPublicKey(), DataToSign) - - // Sends final complete transaction - TXBytes, _ = TX.Serialize() - TXBytes, err = wc.NodeCLI.SendNewTransaction(wc.Node, wc.Input.Address, TXBytes) + NewTXID, err := wc.NodeCLI.SendNewTransactionData(wc.Node, wc.Input.Address, TXBytes, signatures) if err != nil { return err } - TX = transaction.Transaction{} - TX.DeserializeTransaction(TXBytes) - - fmt.Printf("Success. New transaction: %x\n", TX.ID) + fmt.Printf("Success. New transaction: %x\n", NewTXID) return nil } diff --git a/node/node_transactions.go b/node/node_transactions.go index be62bc3..b25663b 100644 --- a/node/node_transactions.go +++ b/node/node_transactions.go @@ -291,6 +291,29 @@ func (n *NodeTransactions) IterateUnapprovedTransactions(callback UnApprovedTran return n.UnapprovedTXs.IterateTransactions(callback) } +func (n *NodeTransactions) ReceivedNewTransactionData(txBytes []byte, Signatures [][]byte) (*transaction.Transaction, error) { + tx := transaction.Transaction{} + err := tx.DeserializeTransaction(txBytes) + + if err != nil { + return nil, err + } + + err = tx.SetSignatures(Signatures) + + if err != nil { + return nil, err + } + + err = n.ReceivedNewTransaction(&tx) + + if err != nil { + return nil, err + } + + return &tx, nil +} + // New transaction reveived from other node. We need to verify and add to cache of unapproved func (n *NodeTransactions) ReceivedNewTransaction(tx *transaction.Transaction) error { // verify this transaction @@ -309,7 +332,7 @@ func (n *NodeTransactions) ReceivedNewTransaction(tx *transaction.Transaction) e // Request to make new transaction and prepare data to sign // This function should find good input transactions for this amount // Including inputs from unapproved transactions if no good approved transactions yet -func (n *NodeTransactions) PrepareNewTransaction(PubKey []byte, to string, amount float64) (*transaction.Transaction, [][]byte, error) { +func (n *NodeTransactions) PrepareNewTransaction(PubKey []byte, to string, amount float64) ([]byte, [][]byte, error) { amount, err := strconv.ParseFloat(fmt.Sprintf("%.8f", amount), 64) if err != nil { @@ -356,7 +379,7 @@ func (n *NodeTransactions) PrepareNewTransaction(PubKey []byte, to string, amoun // func (n *NodeTransactions) PrepareNewTransactionComplete(PubKey []byte, to string, amount float64, - inputs []transaction.TXInput, totalamount float64, prevTXs map[string]transaction.Transaction) (*transaction.Transaction, [][]byte, error) { + inputs []transaction.TXInput, totalamount float64, prevTXs map[string]transaction.Transaction) ([]byte, [][]byte, error) { var outputs []transaction.TXOutput @@ -384,7 +407,13 @@ func (n *NodeTransactions) PrepareNewTransactionComplete(PubKey []byte, to strin return nil, nil, err } - return &tx, signdata, nil + txBytes, err := tx.Serialize() + + if err != nil { + return nil, nil, err + } + + return txBytes, signdata, nil } // Send amount of money if a node is not running. @@ -407,21 +436,20 @@ func (n *NodeTransactions) Send(PubKey []byte, privKey ecdsa.PrivateKey, to stri return nil, errors.New("Recipient address is not valid") } - NewTX, DataToSign, err := n.PrepareNewTransaction(PubKey, to, amount) + txBytes, DataToSign, err := n.PrepareNewTransaction(PubKey, to, amount) if err != nil { return nil, errors.New(fmt.Sprintf("Prepare error: %s", err.Error())) } - err = NewTX.SignData(privKey, PubKey, DataToSign) + signatures, err := utils.SignDataSet(PubKey, privKey, DataToSign) if err != nil { return nil, errors.New(fmt.Sprintf("Sign error: %s", err.Error())) } - err = n.ReceivedNewTransaction(NewTX) + NewTX, err := n.ReceivedNewTransactionData(txBytes, signatures) if err != nil { - n.Logger.Trace.Printf("Sending Error for %x: %s", NewTX.ID, err.Error()) return nil, errors.New(fmt.Sprintf("Final ading TX error: %s", err.Error())) } diff --git a/node/nodeserver.go b/node/nodeserver.go index 41f7bbc..b5f7102 100644 --- a/node/nodeserver.go +++ b/node/nodeserver.go @@ -223,6 +223,9 @@ func (s *NodeServer) handleConnection(conn net.Conn) { case "txfull": rerr = requestobj.handleTxFull() + case "txdata": + rerr = requestobj.handleTxData() + case "txrequest": rerr = requestobj.handleTxRequest() diff --git a/node/nodeserver_handle.go b/node/nodeserver_handle.go index 08c78fc..12a9307 100644 --- a/node/nodeserver_handle.go +++ b/node/nodeserver_handle.go @@ -165,10 +165,8 @@ func (s *NodeServerRequest) handleGetBalance() error { return nil } -/* -* Accepts new transaction. Adds to the list of unapproved. then try to build a block -* This is the request from wallet. Not from other node. - */ +// Accepts new transaction. Adds to the list of unapproved. then try to build a block +// This is the request from wallet. Not from other node. func (s *NodeServerRequest) handleTxFull() error { s.HasResponse = true @@ -202,6 +200,40 @@ func (s *NodeServerRequest) handleTxFull() error { return nil } +// Accepts new transaction data. It is prepared transaction without signatures +// Signatures are received too. Complete TX must be constructed and verified. +// If all is ok TXt is added to unapproved and ID returned +func (s *NodeServerRequest) handleTxData() error { + s.HasResponse = true + + var payload nodeclient.ComNewTransactionData + + err := s.parseRequestData(&payload) + + if err != nil { + return err + } + + TX, err := s.Node.NodeTX.ReceivedNewTransactionData(payload.TX, payload.Signatures) + + if err != nil { + return errors.New(fmt.Sprintf("Transaction accepting error: %s", err.Error())) + } + + s.Logger.Trace.Printf("Acceppted new transaction from %s\n", payload.Address) + + // send internal command to try to mine new block + + s.S.TryToMakeNewBlock(TX.ID) + + s.Response, err = net.GobEncode(TX.ID) + + if err != nil { + return errors.New(fmt.Sprintf("TXFull Response Error: %s", err.Error())) + } + return nil +} + /* * Request for new transaction from light client. Builds a transaction without sign. * Returns also list of previous transactions selected for input. it is used for signature on client side @@ -219,7 +251,7 @@ func (s *NodeServerRequest) handleTxRequest() error { result := nodeclient.ComRequestTransactionData{} - TX, DataToSign, err := s.Node.NodeTX. + TXBytes, DataToSign, err := s.Node.NodeTX. PrepareNewTransaction(payload.PubKey, payload.To, payload.Amount) if err != nil { @@ -227,15 +259,13 @@ func (s *NodeServerRequest) handleTxRequest() error { } result.DataToSign = DataToSign - result.TX, _ = TX.Serialize() + result.TX = TXBytes s.Response, err = net.GobEncode(result) if err != nil { return err } - address, _ := utils.PubKeyToAddres(payload.PubKey) - s.Logger.Trace.Printf("Return prepared transaction %x for %s\n", TX.ID, address) return nil } diff --git a/wallet/main.go b/wallet/main.go index fa6c801..ed15f96 100644 --- a/wallet/main.go +++ b/wallet/main.go @@ -5,6 +5,7 @@ import ( "os" "github.com/gelembjuk/democoin/lib" + "github.com/gelembjuk/democoin/lib/utils" "github.com/gelembjuk/democoin/lib/wallet" ) @@ -31,7 +32,7 @@ func main() { os.Exit(0) } - logger := lib.CreateLogger() + logger := utils.CreateLogger() if input.LogDest != "stdout" { logger.LogToFiles(input.DataDir, "log_trace.txt", "log_info.txt", "log_warning.txt", "log_error.txt")