diff options
-rw-r--r-- | core/block_processor.go | 3 | ||||
-rw-r--r-- | core/state_transition.go | 5 | ||||
-rw-r--r-- | core/transaction_pool.go | 54 | ||||
-rw-r--r-- | core/types/transaction.go | 76 | ||||
-rw-r--r-- | core/types/transaction_test.go | 57 | ||||
-rw-r--r-- | crypto/crypto.go | 2 | ||||
-rw-r--r-- | crypto/crypto_test.go | 8 | ||||
-rw-r--r-- | pow/ezp/pow.go | 11 |
8 files changed, 144 insertions, 72 deletions
diff --git a/core/block_processor.go b/core/block_processor.go index fd4009037..b12f88c47 100644 --- a/core/block_processor.go +++ b/core/block_processor.go @@ -81,7 +81,8 @@ func (self *BlockProcessor) ApplyTransaction(coinbase *state.StateObject, stated _, gas, err := ApplyMessage(NewEnv(statedb, self.bc, tx, block), tx, cb) if err != nil && (IsNonceErr(err) || state.IsGasLimitErr(err) || IsInvalidTxErr(err)) { // If the account is managed, remove the invalid nonce. - self.bc.TxState().RemoveNonce(tx.From(), tx.Nonce()) + from, _ := tx.From() + self.bc.TxState().RemoveNonce(from, tx.Nonce()) return nil, nil, err } diff --git a/core/state_transition.go b/core/state_transition.go index ce9835751..29de501b0 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -44,9 +44,10 @@ type StateTransition struct { env vm.Environment } +// Message represents a message sent to a contract. type Message interface { - From() common.Address - To() common.Address + From() (common.Address, error) + To() *common.Address GasPrice() *big.Int Gas() *big.Int diff --git a/core/transaction_pool.go b/core/transaction_pool.go index 515cc2040..2efc0511c 100644 --- a/core/transaction_pool.go +++ b/core/transaction_pool.go @@ -5,8 +5,8 @@ import ( "fmt" "sync" - "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/logger" ) @@ -20,9 +20,7 @@ var ( const txPoolQueueSize = 50 type TxPoolHook chan *types.Transaction -type TxMsg struct { - Tx *types.Transaction -} +type TxMsg struct{ Tx *types.Transaction } const ( minGasPrice = 1000000 @@ -44,7 +42,7 @@ type TxPool struct { quit chan bool // The actual pool //pool *list.List - txs map[string]*types.Transaction + txs map[common.Hash]*types.Transaction SecondaryProcessor TxProcessor @@ -55,7 +53,7 @@ type TxPool struct { func NewTxPool(eventMux *event.TypeMux) *TxPool { return &TxPool{ - txs: make(map[string]*types.Transaction), + txs: make(map[common.Hash]*types.Transaction), queueChan: make(chan *types.Transaction, txPoolQueueSize), quit: make(chan bool), eventMux: eventMux, @@ -63,21 +61,16 @@ func NewTxPool(eventMux *event.TypeMux) *TxPool { } func (pool *TxPool) ValidateTransaction(tx *types.Transaction) error { - if len(tx.To()) != 0 && len(tx.To()) != 20 { - return fmt.Errorf("Invalid recipient. len = %d", len(tx.To())) + // Validate sender + if _, err := tx.From(); err != nil { + return err } - // Validate curve param v, _, _ := tx.Curve() if v > 28 || v < 27 { return fmt.Errorf("tx.v != (28 || 27) => %v", v) } - - // Validate sender address - senderAddr := tx.From() - if senderAddr == nil || len(senderAddr) != 20 { - return ErrInvalidSender - } + return nil /* XXX this kind of validation needs to happen elsewhere in the gui when sending txs. Other clients should do their own validation. Value transfer could throw error @@ -91,19 +84,16 @@ func (pool *TxPool) ValidateTransaction(tx *types.Transaction) error { return fmt.Errorf("Insufficient amount in sender's (%x) account", tx.From()) } */ - - return nil } func (self *TxPool) addTx(tx *types.Transaction) { - self.txs[string(tx.Hash())] = tx + self.txs[tx.Hash()] = tx } func (self *TxPool) add(tx *types.Transaction) error { - if self.txs[string(tx.Hash())] != nil { + if self.txs[tx.Hash()] != nil { return fmt.Errorf("Known transaction (%x)", tx.Hash()[0:4]) } - err := self.ValidateTransaction(tx) if err != nil { return err @@ -111,18 +101,16 @@ func (self *TxPool) add(tx *types.Transaction) error { self.addTx(tx) - var to string - if len(tx.To()) > 0 { - to = common.Bytes2Hex(tx.To()[:4]) + var toname string + if to, valid := tx.To(); valid { + toname = common.Bytes2Hex(to[:4]) } else { - to = "[NEW_CONTRACT]" - } - var from string - if len(tx.From()) > 0 { - from = common.Bytes2Hex(tx.From()[:4]) - } else { - return errors.New(fmt.Sprintf("FROM ADDRESS MUST BE POSITIVE (was %v)", tx.From())) + toname = "[NEW_CONTRACT]" } + // we can ignore the error here because From is + // verified in ValidateTransaction. + f, _ := tx.From() + from := common.Bytes2Hex(f[:4]) txplogger.Debugf("(t) %x => %s (%v) %x\n", from, to, tx.Value, tx.Hash()) // Notify the subscribers @@ -140,6 +128,7 @@ func (self *TxPool) Add(tx *types.Transaction) error { defer self.mu.Unlock() return self.add(tx) } + func (self *TxPool) AddTransactions(txs []*types.Transaction) { self.mu.Lock() defer self.mu.Unlock() @@ -186,14 +175,13 @@ func (pool *TxPool) RemoveInvalid(query StateQuery) { func (self *TxPool) RemoveSet(txs types.Transactions) { self.mu.Lock() defer self.mu.Unlock() - for _, tx := range txs { - delete(self.txs, string(tx.Hash())) + delete(self.txs, tx.Hash()) } } func (pool *TxPool) Flush() { - pool.txs = make(map[string]*types.Transaction) + pool.txs = make(map[common.Hash]*types.Transaction) } func (pool *TxPool) Start() { diff --git a/core/types/transaction.go b/core/types/transaction.go index d55c5d5ae..24035a3ae 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -2,7 +2,9 @@ package types import ( "bytes" + "errors" "fmt" + "io" "math/big" "github.com/ethereum/go-ethereum/common" @@ -20,19 +22,19 @@ type Transaction struct { AccountNonce uint64 Price *big.Int GasLimit *big.Int - Recipient common.Address + Recipient *common.Address // nil means contract creation Amount *big.Int Payload []byte V byte R, S []byte } -func NewContractCreationTx(Amount, gasAmount, price *big.Int, data []byte) *Transaction { - return NewTransactionMessage(common.Address{}, Amount, gasAmount, price, data) +func NewContractCreationTx(amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction { + return &Transaction{Recipient: nil, Amount: amount, GasLimit: gasLimit, Price: gasPrice, Payload: data} } -func NewTransactionMessage(to common.Address, Amount, gasAmount, price *big.Int, data []byte) *Transaction { - return &Transaction{Recipient: to, Amount: Amount, Price: price, GasLimit: gasAmount, Payload: data} +func NewTransactionMessage(to common.Address, amount, gasAmount, gasPrice *big.Int, data []byte) *Transaction { + return &Transaction{Recipient: &to, Amount: amount, GasLimit: gasAmount, Price: gasPrice, Payload: data} } func NewTransactionFromBytes(data []byte) *Transaction { @@ -44,7 +46,7 @@ func NewTransactionFromBytes(data []byte) *Transaction { func (tx *Transaction) Hash() (a common.Hash) { h := sha3.NewKeccak256() rlp.Encode(h, []interface{}{tx.AccountNonce, tx.Price, tx.GasLimit, tx.Recipient, tx.Amount, tx.Payload}) - h.Sum(a[:]) + h.Sum(a[:0]) return a } @@ -72,19 +74,27 @@ func (self *Transaction) SetNonce(AccountNonce uint64) { self.AccountNonce = AccountNonce } -func (self *Transaction) From() common.Address { - return self.sender() +func (self *Transaction) From() (common.Address, error) { + pubkey := self.PublicKey() + if len(pubkey) == 0 || pubkey[0] != 4 { + return common.Address{}, errors.New("invalid public key") + } + var addr common.Address + copy(addr[:], crypto.Sha3(pubkey[1:])) + return addr, nil } -func (self *Transaction) To() common.Address { - return self.Recipient +// To returns the recipient of the transaction. +// If transaction is a contract creation (with no recipient address) +// To returns nil. +func (tx *Transaction) To() *common.Address { + return tx.Recipient } func (tx *Transaction) Curve() (v byte, r []byte, s []byte) { v = byte(tx.V) r = common.LeftPadBytes(tx.R, 32) s = common.LeftPadBytes(tx.S, 32) - return } @@ -105,18 +115,6 @@ func (tx *Transaction) PublicKey() []byte { return pubkey } -func (tx *Transaction) sender() (a common.Address) { - pubkey := tx.PublicKey() - - // Validate the returned key. - // Return nil if public key isn't in full format - if len(pubkey) == 0 || pubkey[0] != 4 { - return a - } - copy(a[:], crypto.Sha3(pubkey[1:])) - return a -} - func (tx *Transaction) SetSignatureValues(sig []byte) error { tx.R = sig[:32] tx.S = sig[32:64] @@ -124,6 +122,19 @@ func (tx *Transaction) SetSignatureValues(sig []byte) error { return nil } +func (tx Transaction) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, []interface{}{ + tx.AccountNonce, + tx.Price, tx.GasLimit, + tx.Recipient, + tx.Amount, + tx.Payload, + tx.V, + tx.R, + tx.S, + }) +} + // TODO: remove func (tx *Transaction) RlpData() interface{} { data := []interface{}{tx.AccountNonce, tx.Price, tx.GasLimit, tx.Recipient, tx.Amount, tx.Payload} @@ -136,11 +147,22 @@ func (tx *Transaction) RlpEncode() []byte { } func (tx *Transaction) String() string { + var from, to string + if f, err := tx.From(); err != nil { + from = "[invalid sender]" + } else { + from = fmt.Sprintf("%x", f[:]) + } + if t := tx.To(); t == nil { + to = "[contract creation]" + } else { + to = fmt.Sprintf("%x", t[:]) + } return fmt.Sprintf(` TX(%x) Contract: %v - From: %x - To: %x + From: %s + To: %s Nonce: %v GasPrice: %v GasLimit %v @@ -153,8 +175,8 @@ func (tx *Transaction) String() string { `, tx.Hash(), len(tx.Recipient) == 0, - tx.From(), - tx.To(), + from, + to, tx.AccountNonce, tx.Price, tx.GasLimit, diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go index ab1254f4c..0b0dfe3ff 100644 --- a/core/types/transaction_test.go +++ b/core/types/transaction_test.go @@ -1 +1,58 @@ package types + +import ( + "bytes" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/rlp" +) + +// The values in those tests are from the Transaction Tests +// at github.com/ethereum/tests. + +var ( + emptyTx = NewTransactionMessage( + common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), + big.NewInt(0), big.NewInt(0), big.NewInt(0), + nil, + ) + + rightvrsRecipient = common.HexToAddress("b94f5374fce5edbc8e2a8697c15331677e6ebf0b") + rightvrsTx = &Transaction{ + Recipient: &rightvrsRecipient, + AccountNonce: 3, + Price: big.NewInt(1), + GasLimit: big.NewInt(2000), + Amount: big.NewInt(10), + Payload: common.FromHex("5544"), + V: 28, + R: common.FromHex("98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a"), + S: common.FromHex("8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3"), + } +) + +func TestTransactionHash(t *testing.T) { + // "EmptyTransaction" + if emptyTx.Hash() != common.HexToHash("c775b99e7ad12f50d819fcd602390467e28141316969f4b57f0626f74fe3b386") { + t.Errorf("empty transaction hash mismatch, got %x", emptyTx.Hash()) + } + + // "RightVRSTest" + if rightvrsTx.Hash() != common.HexToHash("fe7a79529ed5f7c3375d06b26b186a8644e0e16c373d7a12be41c62d6042b77a") { + t.Errorf("RightVRS transaction hash mismatch, got %x", rightvrsTx.Hash()) + } +} + +func TestTransactionEncode(t *testing.T) { + // "RightVRSTest" + txb, err := rlp.EncodeToBytes(rightvrsTx) + if err != nil { + t.Fatalf("encode error: %v", err) + } + should := common.FromHex("f86103018207d094b94f5374fce5edbc8e2a8697c15331677e6ebf0b0a8255441ca098ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4aa08887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3") + if !bytes.Equal(txb, should) { + t.Errorf("encoded RLP mismatch, got %x", txb) + } +} diff --git a/crypto/crypto.go b/crypto/crypto.go index e9582ac77..c3d47b629 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -43,7 +43,7 @@ func Sha3Hash(data ...[]byte) (h common.Hash) { for _, b := range data { d.Write(b) } - d.Sum(h[:]) + d.Sum(h[:0]) return h } diff --git a/crypto/crypto_test.go b/crypto/crypto_test.go index 754287641..63a9c3f5e 100644 --- a/crypto/crypto_test.go +++ b/crypto/crypto_test.go @@ -7,8 +7,8 @@ import ( "testing" "time" - "github.com/ethereum/go-ethereum/crypto/secp256k1" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto/secp256k1" ) // These tests are sanity checks. @@ -21,6 +21,12 @@ func TestSha3(t *testing.T) { checkhash(t, "Sha3-256", func(in []byte) []byte { return Sha3(in) }, msg, exp) } +func TestSha3Hash(t *testing.T) { + msg := []byte("abc") + exp, _ := hex.DecodeString("4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45") + checkhash(t, "Sha3-256-array", func(in []byte) []byte { h := Sha3Hash(in); return h[:] }, msg, exp) +} + func TestSha256(t *testing.T) { msg := []byte("abc") exp, _ := hex.DecodeString("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad") diff --git a/pow/ezp/pow.go b/pow/ezp/pow.go index 7eba95784..bd2927fe8 100644 --- a/pow/ezp/pow.go +++ b/pow/ezp/pow.go @@ -6,8 +6,8 @@ import ( "math/rand" "time" - "github.com/ethereum/go-ethereum/crypto/sha3" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto/sha3" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/pow" ) @@ -83,17 +83,14 @@ func (pow *EasyPow) Verify(block pow.Block) bool { return Verify(block) } -func verify(hash []byte, diff *big.Int, nonce uint64) bool { +func verify(hash common.Hash, diff *big.Int, nonce uint64) bool { sha := sha3.NewKeccak256() - n := make([]byte, 8) binary.PutUvarint(n, nonce) - d := append(hash, n...) - sha.Write(d) - + sha.Write(n) + sha.Write(hash[:]) verification := new(big.Int).Div(common.BigPow(2, 256), diff) res := common.BigD(sha.Sum(nil)) - return res.Cmp(verification) <= 0 } |