From 654564e164b3b6f7f4ba1e8bbd6fcd64776068fa Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 11 Jun 2015 14:05:32 +0200 Subject: [PATCH] core/types: make transactions immutable --- core/transaction_pool.go | 19 ++- core/transaction_pool_test.go | 91 ++++-------- core/types/block_test.go | 19 +-- core/types/transaction.go | 252 +++++++++++++++++---------------- core/types/transaction_test.go | 32 ++--- eth/protocol_test.go | 4 +- tests/transaction_test_util.go | 45 +++--- xeth/xeth.go | 34 ++--- 8 files changed, 218 insertions(+), 278 deletions(-) diff --git a/core/transaction_pool.go b/core/transaction_pool.go index 34a1733d71..45db04eef4 100644 --- a/core/transaction_pool.go +++ b/core/transaction_pool.go @@ -162,27 +162,25 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error { // Check the transaction doesn't exceed the current // block limit gas. - if pool.gasLimit().Cmp(tx.GasLimit) < 0 { + if pool.gasLimit().Cmp(tx.Gas()) < 0 { return ErrGasLimit } // Transactions can't be negative. This may never happen // using RLP decoded transactions but may occur if you create // a transaction using the RPC for example. - if tx.Amount.Cmp(common.Big0) < 0 { + if tx.Value().Cmp(common.Big0) < 0 { return ErrNegativeValue } // Transactor should have enough funds to cover the costs // cost == V + GP * GL - total := new(big.Int).Mul(tx.Price, tx.GasLimit) - total.Add(total, tx.Value()) - if pool.currentState().GetBalance(from).Cmp(total) < 0 { + if pool.currentState().GetBalance(from).Cmp(tx.Cost()) < 0 { return ErrInsufficientFunds } // Should supply enough intrinsic gas - if tx.GasLimit.Cmp(IntrinsicGas(tx)) < 0 { + if tx.Gas().Cmp(IntrinsicGas(tx)) < 0 { return ErrIntrinsicGas } @@ -238,7 +236,7 @@ func (pool *TxPool) addTx(hash common.Hash, addr common.Address, tx *types.Trans // Increment the nonce on the pending state. This can only happen if // the nonce is +1 to the previous one. - pool.pendingState.SetNonce(addr, tx.AccountNonce+1) + pool.pendingState.SetNonce(addr, tx.Nonce()+1) // Notify the subscribers. This event is posted in a goroutine // because it's possible that somewhere during the post "Remove transaction" // gets called which will then wait for the global tx pool lock and deadlock. @@ -341,7 +339,7 @@ func (pool *TxPool) checkQueue() { trueNonce := pool.currentState().GetNonce(address) addq := addq[:0] for hash, tx := range txs { - if tx.AccountNonce < trueNonce { + if tx.Nonce() < trueNonce { // Drop queued transactions whose nonce is lower than // the account nonce because they have been processed. delete(txs, hash) @@ -362,8 +360,7 @@ func (pool *TxPool) checkQueue() { delete(pool.queue[address], e.hash) continue } - - if e.AccountNonce > guessedNonce { + if e.Nonce() > guessedNonce { break } delete(txs, e.hash) @@ -418,4 +415,4 @@ type txQueueEntry struct { func (q txQueue) Len() int { return len(q) } func (q txQueue) Swap(i, j int) { q[i], q[j] = q[j], q[i] } -func (q txQueue) Less(i, j int) bool { return q[i].AccountNonce < q[j].AccountNonce } +func (q txQueue) Less(i, j int) bool { return q[i].Nonce() < q[j].Nonce() } diff --git a/core/transaction_pool_test.go b/core/transaction_pool_test.go index b763c196d0..ff8b9c730f 100644 --- a/core/transaction_pool_test.go +++ b/core/transaction_pool_test.go @@ -13,8 +13,9 @@ import ( "github.com/ethereum/go-ethereum/event" ) -func transaction() *types.Transaction { - return types.NewTransactionMessage(common.Address{}, big.NewInt(100), big.NewInt(100), big.NewInt(100), nil) +func transaction(nonce uint64, gaslimit *big.Int, key *ecdsa.PrivateKey) *types.Transaction { + tx, _ := types.NewTransaction(nonce, common.Address{}, big.NewInt(100), gaslimit, big.NewInt(1), nil).SignECDSA(key) + return tx } func setupTxPool() (*TxPool, *ecdsa.PrivateKey) { @@ -29,43 +30,34 @@ func setupTxPool() (*TxPool, *ecdsa.PrivateKey) { func TestInvalidTransactions(t *testing.T) { pool, key := setupTxPool() - tx := transaction() - tx.SignECDSA(key) - err := pool.Add(tx) - if err != ErrNonExistentAccount { + tx := transaction(0, big.NewInt(100), key) + if err := pool.Add(tx); err != ErrNonExistentAccount { t.Error("expected", ErrNonExistentAccount) } from, _ := tx.From() pool.currentState().AddBalance(from, big.NewInt(1)) - err = pool.Add(tx) - if err != ErrInsufficientFunds { + if err := pool.Add(tx); err != ErrInsufficientFunds { t.Error("expected", ErrInsufficientFunds) } balance := new(big.Int).Add(tx.Value(), new(big.Int).Mul(tx.Gas(), tx.GasPrice())) pool.currentState().AddBalance(from, balance) - err = pool.Add(tx) - if err != ErrIntrinsicGas { + if err := pool.Add(tx); err != ErrIntrinsicGas { t.Error("expected", ErrIntrinsicGas, "got", err) } pool.currentState().SetNonce(from, 1) pool.currentState().AddBalance(from, big.NewInt(0xffffffffffffff)) - tx.GasLimit = big.NewInt(100000) - tx.Price = big.NewInt(1) - tx.SignECDSA(key) - - err = pool.Add(tx) - if err != ErrNonce { + tx = transaction(0, big.NewInt(100000), key) + if err := pool.Add(tx); err != ErrNonce { t.Error("expected", ErrNonce) } } func TestTransactionQueue(t *testing.T) { pool, key := setupTxPool() - tx := transaction() - tx.SignECDSA(key) + tx := transaction(0, big.NewInt(100), key) from, _ := tx.From() pool.currentState().AddBalance(from, big.NewInt(1)) pool.queueTx(tx.Hash(), tx) @@ -75,9 +67,7 @@ func TestTransactionQueue(t *testing.T) { t.Error("expected valid txs to be 1 is", len(pool.pending)) } - tx = transaction() - tx.SetNonce(1) - tx.SignECDSA(key) + tx = transaction(1, big.NewInt(100), key) from, _ = tx.From() pool.currentState().SetNonce(from, 2) pool.queueTx(tx.Hash(), tx) @@ -91,12 +81,9 @@ func TestTransactionQueue(t *testing.T) { } pool, key = setupTxPool() - tx1, tx2, tx3 := transaction(), transaction(), transaction() - tx2.SetNonce(10) - tx3.SetNonce(11) - tx1.SignECDSA(key) - tx2.SignECDSA(key) - tx3.SignECDSA(key) + tx1 := transaction(0, big.NewInt(100), key) + tx2 := transaction(10, big.NewInt(100), key) + tx3 := transaction(11, big.NewInt(100), key) pool.queueTx(tx1.Hash(), tx1) pool.queueTx(tx2.Hash(), tx2) pool.queueTx(tx3.Hash(), tx3) @@ -114,8 +101,7 @@ func TestTransactionQueue(t *testing.T) { func TestRemoveTx(t *testing.T) { pool, key := setupTxPool() - tx := transaction() - tx.SignECDSA(key) + tx := transaction(0, big.NewInt(100), key) from, _ := tx.From() pool.currentState().AddBalance(from, big.NewInt(1)) pool.queueTx(tx.Hash(), tx) @@ -142,13 +128,10 @@ func TestRemoveTx(t *testing.T) { func TestNegativeValue(t *testing.T) { pool, key := setupTxPool() - tx := transaction() - tx.Value().Set(big.NewInt(-1)) - tx.SignECDSA(key) + tx, _ := types.NewTransaction(0, common.Address{}, big.NewInt(-1), big.NewInt(100), big.NewInt(1), nil).SignECDSA(key) from, _ := tx.From() pool.currentState().AddBalance(from, big.NewInt(1)) - err := pool.Add(tx) - if err != ErrNegativeValue { + if err := pool.Add(tx); err != ErrNegativeValue { t.Error("expected", ErrNegativeValue, "got", err) } } @@ -165,20 +148,15 @@ func TestTransactionChainFork(t *testing.T) { } resetState() - tx := transaction() - tx.GasLimit = big.NewInt(100000) - tx.SignECDSA(key) - - err := pool.add(tx) - if err != nil { + tx := transaction(0, big.NewInt(100000), key) + if err := pool.add(tx); err != nil { t.Error("didn't expect error", err) } pool.RemoveTransactions([]*types.Transaction{tx}) // reset the pool's internal state resetState() - err = pool.add(tx) - if err != nil { + if err := pool.add(tx); err != nil { t.Error("didn't expect error", err) } } @@ -195,24 +173,14 @@ func TestTransactionDoubleNonce(t *testing.T) { } resetState() - tx := transaction() - tx.GasLimit = big.NewInt(100000) - tx.SignECDSA(key) - - err := pool.add(tx) - if err != nil { + tx := transaction(0, big.NewInt(100000), key) + tx2 := transaction(0, big.NewInt(1000000), key) + if err := pool.add(tx); err != nil { t.Error("didn't expect error", err) } - - tx2 := transaction() - tx2.GasLimit = big.NewInt(1000000) - tx2.SignECDSA(key) - - err = pool.add(tx2) - if err != nil { + if err := pool.add(tx2); err != nil { t.Error("didn't expect error", err) } - if len(pool.pending) != 2 { t.Error("expected 2 pending txs. Got", len(pool.pending)) } @@ -222,20 +190,13 @@ func TestMissingNonce(t *testing.T) { pool, key := setupTxPool() addr := crypto.PubkeyToAddress(key.PublicKey) pool.currentState().AddBalance(addr, big.NewInt(100000000000000)) - tx := transaction() - tx.AccountNonce = 1 - tx.GasLimit = big.NewInt(100000) - tx.SignECDSA(key) - - err := pool.add(tx) - if err != nil { + tx := transaction(1, big.NewInt(100000), key) + if err := pool.add(tx); err != nil { t.Error("didn't expect error", err) } - if len(pool.pending) != 0 { t.Error("expected 0 pending transactions, got", len(pool.pending)) } - if len(pool.queue[addr]) != 1 { t.Error("expected 1 queued transaction, got", len(pool.queue[addr])) } diff --git a/core/types/block_test.go b/core/types/block_test.go index b52ddffdc1..03e6881beb 100644 --- a/core/types/block_test.go +++ b/core/types/block_test.go @@ -13,7 +13,6 @@ import ( // from bcValidBlockTest.json, "SimpleTx" func TestBlockEncoding(t *testing.T) { blockEnc := common.FromHex("f90260f901f9a083cafc574e1f51ba9dc0568fc617a08ea2429fb384059c972f13b19fa1c8dd55a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017a05fe50b260da6308036625b850b5d6ced6d0a9f814c0688bc91ffb7b7a3a54b67a0bc37d79753ad738a6dac4921e57392f145d8887476de3f783dfa7edae9283e52b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001832fefd8825208845506eb0780a0bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff49888a13a5a8c8f2bb1c4f861f85f800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba09bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094fa08a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1c0") - var block Block if err := rlp.DecodeBytes(blockEnc, &block); err != nil { t.Fatal("decode error: ", err) @@ -35,20 +34,10 @@ func TestBlockEncoding(t *testing.T) { check("Time", block.Time(), int64(1426516743)) check("Size", block.Size(), common.StorageSize(len(blockEnc))) - to := common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87") - check("Transactions", block.Transactions(), Transactions{ - { - Payload: []byte{}, - Amount: big.NewInt(10), - Price: big.NewInt(10), - GasLimit: big.NewInt(50000), - AccountNonce: 0, - V: 27, - R: common.String2Big("0x9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f"), - S: common.String2Big("0x8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1"), - Recipient: &to, - }, - }) + tx1 := NewTransaction(0, common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), big.NewInt(10), big.NewInt(50000), big.NewInt(10), nil) + tx1, _ = tx1.WithSignature(common.Hex2Bytes("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b100")) + check("len(Transactions)", len(block.Transactions()), 1) + check("Transactions[0].Hash", block.Transactions()[0].Hash(), tx1.Hash()) ourBlockEnc, err := rlp.EncodeToBytes(&block) if err != nil { diff --git a/core/types/transaction.go b/core/types/transaction.go index a03a6b847e..14b0e4174d 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -4,6 +4,7 @@ import ( "crypto/ecdsa" "errors" "fmt" + "io" "math/big" "github.com/ethereum/go-ethereum/common" @@ -18,38 +19,59 @@ func IsContractAddr(addr []byte) bool { } type Transaction struct { - AccountNonce uint64 - Price *big.Int - GasLimit *big.Int - Recipient *common.Address `rlp:"nil"` // nil means contract creation - Amount *big.Int - Payload []byte - V byte - R, S *big.Int + data txdata } -func NewContractCreationTx(amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction { - return &Transaction{ - Recipient: nil, - Amount: amount, - GasLimit: gasLimit, - Price: gasPrice, - Payload: data, - R: new(big.Int), - S: new(big.Int), - } +type txdata struct { + AccountNonce uint64 + Price, GasLimit *big.Int + Recipient *common.Address `rlp:"nil"` // nil means contract creation + Amount *big.Int + Payload []byte + V byte // signature + R, S *big.Int // signature } -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, - R: new(big.Int), - S: new(big.Int), +func NewContractCreation(nonce uint64, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction { + if len(data) > 0 { + data = common.CopyBytes(data) } + return &Transaction{data: txdata{ + AccountNonce: nonce, + Recipient: nil, + Amount: new(big.Int).Set(amount), + GasLimit: new(big.Int).Set(gasLimit), + Price: new(big.Int).Set(gasPrice), + Payload: data, + R: new(big.Int), + S: new(big.Int), + }} +} + +func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction { + if len(data) > 0 { + data = common.CopyBytes(data) + } + d := txdata{ + AccountNonce: nonce, + Recipient: &to, + Payload: data, + Amount: new(big.Int), + GasLimit: new(big.Int), + Price: new(big.Int), + R: new(big.Int), + S: new(big.Int), + } + if amount != nil { + d.Amount.Set(amount) + } + if gasLimit != nil { + d.GasLimit.Set(gasLimit) + } + if gasPrice != nil { + d.Price.Set(gasPrice) + } + return &Transaction{data: d} } func NewTransactionFromBytes(data []byte) *Transaction { @@ -61,112 +83,110 @@ func NewTransactionFromBytes(data []byte) *Transaction { return tx } +func (tx *Transaction) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, &tx.data) +} + +func (tx *Transaction) DecodeRLP(s *rlp.Stream) error { + return s.Decode(&tx.data) +} + +func (tx *Transaction) Data() []byte { return common.CopyBytes(tx.data.Payload) } +func (tx *Transaction) Gas() *big.Int { return new(big.Int).Set(tx.data.GasLimit) } +func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.data.Price) } +func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.data.Amount) } +func (tx *Transaction) Nonce() uint64 { return tx.data.AccountNonce } + +func (tx *Transaction) To() *common.Address { + if tx.data.Recipient == nil { + return nil + } else { + to := *tx.data.Recipient + return &to + } +} + func (tx *Transaction) Hash() common.Hash { - return rlpHash([]interface{}{ - tx.AccountNonce, tx.Price, tx.GasLimit, tx.Recipient, tx.Amount, tx.Payload, + v := rlpHash([]interface{}{ + tx.data.AccountNonce, + tx.data.Price, + tx.data.GasLimit, + tx.data.Recipient, + tx.data.Amount, + tx.data.Payload, }) + return v } -// Size returns the encoded RLP size of tx. -func (self *Transaction) Size() common.StorageSize { - c := writeCounter(0) - rlp.Encode(&c, self) - return common.StorageSize(c) +func (tx *Transaction) Size() common.StorageSize { + v, _, _ := rlp.EncodeToReader(&tx.data) + return common.StorageSize(v) } -func (self *Transaction) Data() []byte { - return self.Payload -} - -func (self *Transaction) Gas() *big.Int { - return self.GasLimit -} - -func (self *Transaction) GasPrice() *big.Int { - return self.Price -} - -func (self *Transaction) Value() *big.Int { - return self.Amount -} - -func (self *Transaction) Nonce() uint64 { - return self.AccountNonce -} - -func (self *Transaction) SetNonce(AccountNonce uint64) { - self.AccountNonce = AccountNonce -} - -func (self *Transaction) From() (common.Address, error) { - pubkey, err := self.PublicKey() +func (tx *Transaction) From() (common.Address, error) { + pubkey, err := tx.PublicKey() if err != nil { return common.Address{}, err } - var addr common.Address copy(addr[:], crypto.Sha3(pubkey[1:])[12:]) return addr, nil } -// 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 +// Cost returns amount + gasprice * gaslimit. +func (tx *Transaction) Cost() *big.Int { + total := new(big.Int).Mul(tx.data.Price, tx.data.GasLimit) + total.Add(total, tx.data.Amount) + return total } -func (tx *Transaction) GetSignatureValues() (v byte, r []byte, s []byte) { - v = byte(tx.V) - r = common.LeftPadBytes(tx.R.Bytes(), 32) - s = common.LeftPadBytes(tx.S.Bytes(), 32) - return +func (tx *Transaction) SignatureValues() (v byte, r *big.Int, s *big.Int) { + return tx.data.V, new(big.Int).Set(tx.data.R), new(big.Int).Set(tx.data.S) } func (tx *Transaction) PublicKey() ([]byte, error) { - if !crypto.ValidateSignatureValues(tx.V, tx.R, tx.S) { + if !crypto.ValidateSignatureValues(tx.data.V, tx.data.R, tx.data.S) { return nil, errors.New("invalid v, r, s values") } - hash := tx.Hash() - v, r, s := tx.GetSignatureValues() - sig := append(r, s...) - sig = append(sig, v-27) + // encode the signature in uncompressed format + r, s := tx.data.R.Bytes(), tx.data.S.Bytes() + sig := make([]byte, 65) + copy(sig[32-len(r):32], r) + copy(sig[64-len(s):64], s) + sig[64] = tx.data.V - 27 - p, err := crypto.SigToPub(hash[:], sig) + // recover the public key from the signature + hash := tx.Hash() + pub, err := crypto.Ecrecover(hash[:], sig) if err != nil { glog.V(logger.Error).Infof("Could not get pubkey from signature: ", err) return nil, err } - - pubkey := crypto.FromECDSAPub(p) - if len(pubkey) == 0 || pubkey[0] != 4 { + if len(pub) == 0 || pub[0] != 4 { return nil, errors.New("invalid public key") } - return pubkey, nil + return pub, nil } -func (tx *Transaction) SetSignatureValues(sig []byte) error { - tx.R = common.Bytes2Big(sig[:32]) - tx.S = common.Bytes2Big(sig[32:64]) - tx.V = sig[64] + 27 - return nil +func (tx *Transaction) WithSignature(sig []byte) (*Transaction, error) { + if len(sig) != 65 { + panic(fmt.Sprintf("wrong size for signature: got %d, want 65", len(sig))) + } + cpy := &Transaction{data: tx.data} + cpy.data.R = new(big.Int).SetBytes(sig[:32]) + cpy.data.S = new(big.Int).SetBytes(sig[32:64]) + cpy.data.V = sig[64] + 27 + return cpy, nil } -func (tx *Transaction) SignECDSA(prv *ecdsa.PrivateKey) error { +func (tx *Transaction) SignECDSA(prv *ecdsa.PrivateKey) (*Transaction, error) { h := tx.Hash() sig, err := crypto.Sign(h[:], prv) if err != nil { - return err + return nil, err } - tx.SetSignatureValues(sig) - return nil -} - -// TODO: remove -func (tx *Transaction) RlpData() interface{} { - data := []interface{}{tx.AccountNonce, tx.Price, tx.GasLimit, tx.Recipient, tx.Amount, tx.Payload} - return append(data, tx.V, tx.R.Bytes(), tx.S.Bytes()) + return tx.WithSignature(sig) } func (tx *Transaction) String() string { @@ -176,12 +196,12 @@ func (tx *Transaction) String() string { } else { from = fmt.Sprintf("%x", f[:]) } - if t := tx.To(); t == nil { + if tx.data.Recipient == nil { to = "[contract creation]" } else { - to = fmt.Sprintf("%x", t[:]) + to = fmt.Sprintf("%x", tx.data.Recipient[:]) } - enc, _ := rlp.EncodeToBytes(tx) + enc, _ := rlp.EncodeToBytes(&tx.data) return fmt.Sprintf(` TX(%x) Contract: %v @@ -198,36 +218,24 @@ func (tx *Transaction) String() string { Hex: %x `, tx.Hash(), - len(tx.Recipient) == 0, + len(tx.data.Recipient) == 0, from, to, - tx.AccountNonce, - tx.Price, - tx.GasLimit, - tx.Amount, - tx.Payload, - tx.V, - tx.R, - tx.S, + tx.data.AccountNonce, + tx.data.Price, + tx.data.GasLimit, + tx.data.Amount, + tx.data.Payload, + tx.data.V, + tx.data.R, + tx.data.S, enc, ) } -// Transaction slice type for basic sorting +// Transaction slice type for basic sorting. type Transactions []*Transaction -// TODO: remove -func (self Transactions) RlpData() interface{} { - // Marshal the transactions of this block - enc := make([]interface{}, len(self)) - for i, tx := range self { - // Cast it to a string (safe) - enc[i] = tx.RlpData() - } - - return enc -} - func (s Transactions) Len() int { return len(s) } func (s Transactions) Swap(i, j int) { s[i], s[j] = s[j], s[i] } @@ -239,5 +247,5 @@ func (s Transactions) GetRlp(i int) []byte { type TxByNonce struct{ Transactions } func (s TxByNonce) Less(i, j int) bool { - return s.Transactions[i].AccountNonce < s.Transactions[j].AccountNonce + return s.Transactions[i].data.AccountNonce < s.Transactions[j].data.AccountNonce } diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go index 492059c28a..dd9c5e87b0 100644 --- a/core/types/transaction_test.go +++ b/core/types/transaction_test.go @@ -15,40 +15,35 @@ import ( // at github.com/ethereum/tests. var ( - emptyTx = NewTransactionMessage( + emptyTx = NewTransaction( + 0, 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.String2Big("0x98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a"), - S: common.String2Big("0x8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3"), - } + rightvrsTx, _ = NewTransaction( + 3, + common.HexToAddress("b94f5374fce5edbc8e2a8697c15331677e6ebf0b"), + big.NewInt(10), + big.NewInt(2000), + big.NewInt(1), + common.FromHex("5544"), + ).WithSignature( + common.Hex2Bytes("98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a301"), + ) ) 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) @@ -72,19 +67,16 @@ func defaultTestKey() (*ecdsa.PrivateKey, common.Address) { func TestRecipientEmpty(t *testing.T) { _, addr := defaultTestKey() - tx, err := decodeTx(common.Hex2Bytes("f8498080808080011ca09b16de9d5bdee2cf56c28d16275a4da68cd30273e2525f3959f5d62557489921a0372ebd8fb3345f7db7b5a86d42e24d36e983e259b0664ceb8c227ec9af572f3d")) if err != nil { t.Error(err) t.FailNow() } - from, err := tx.From() if err != nil { t.Error(err) t.FailNow() } - if addr != from { t.Error("derived address doesn't match") } diff --git a/eth/protocol_test.go b/eth/protocol_test.go index 6e0eef59c9..60fa354436 100644 --- a/eth/protocol_test.go +++ b/eth/protocol_test.go @@ -234,7 +234,7 @@ func (pool *fakeTxPool) GetTransactions() types.Transactions { func newtx(from *crypto.Key, nonce uint64, datasize int) *types.Transaction { data := make([]byte, datasize) - tx := types.NewTransactionMessage(common.Address{}, big.NewInt(0), big.NewInt(100000), big.NewInt(0), data) - tx.SetNonce(nonce) + tx := types.NewTransaction(nonce, common.Address{}, big.NewInt(0), big.NewInt(100000), big.NewInt(0), data) + tx, _ = tx.SignECDSA(from.PrivateKey) return tx } diff --git a/tests/transaction_test_util.go b/tests/transaction_test_util.go index 45caf26fda..1c92090dbb 100644 --- a/tests/transaction_test_util.go +++ b/tests/transaction_test_util.go @@ -152,54 +152,53 @@ func verifyTxFields(txTest TransactionTest, decodedTx *types.Transaction) (err e } expectedData := mustConvertBytes(txTest.Transaction.Data) - if !bytes.Equal(expectedData, decodedTx.Payload) { - return fmt.Errorf("Tx input data mismatch: %#v %#v", expectedData, decodedTx.Payload) + if !bytes.Equal(expectedData, decodedTx.Data()) { + return fmt.Errorf("Tx input data mismatch: %#v %#v", expectedData, decodedTx.Data()) } expectedGasLimit := mustConvertBigInt(txTest.Transaction.GasLimit, 16) - if expectedGasLimit.Cmp(decodedTx.GasLimit) != 0 { - return fmt.Errorf("GasLimit mismatch: %v %v", expectedGasLimit, decodedTx.GasLimit) + if expectedGasLimit.Cmp(decodedTx.Gas()) != 0 { + return fmt.Errorf("GasLimit mismatch: %v %v", expectedGasLimit, decodedTx.Gas()) } expectedGasPrice := mustConvertBigInt(txTest.Transaction.GasPrice, 16) - if expectedGasPrice.Cmp(decodedTx.Price) != 0 { - return fmt.Errorf("GasPrice mismatch: %v %v", expectedGasPrice, decodedTx.Price) + if expectedGasPrice.Cmp(decodedTx.GasPrice()) != 0 { + return fmt.Errorf("GasPrice mismatch: %v %v", expectedGasPrice, decodedTx.GasPrice()) } expectedNonce := mustConvertUint(txTest.Transaction.Nonce, 16) - if expectedNonce != decodedTx.AccountNonce { - return fmt.Errorf("Nonce mismatch: %v %v", expectedNonce, decodedTx.AccountNonce) + if expectedNonce != decodedTx.Nonce() { + return fmt.Errorf("Nonce mismatch: %v %v", expectedNonce, decodedTx.Nonce()) } - expectedR := common.Bytes2Big(mustConvertBytes(txTest.Transaction.R)) - if expectedR.Cmp(decodedTx.R) != 0 { - return fmt.Errorf("R mismatch: %v %v", expectedR, decodedTx.R) + v, r, s := decodedTx.SignatureValues() + expectedR := mustConvertBigInt(txTest.Transaction.R, 16) + if r.Cmp(expectedR) != 0 { + return fmt.Errorf("R mismatch: %v %v", expectedR, r) } - - expectedS := common.Bytes2Big(mustConvertBytes(txTest.Transaction.S)) - if expectedS.Cmp(decodedTx.S) != 0 { - return fmt.Errorf("S mismatch: %v %v", expectedS, decodedTx.S) + expectedS := mustConvertBigInt(txTest.Transaction.S, 16) + if s.Cmp(expectedS) != 0 { + return fmt.Errorf("S mismatch: %v %v", expectedS, s) } - expectedV := mustConvertUint(txTest.Transaction.V, 16) - if expectedV != uint64(decodedTx.V) { - return fmt.Errorf("V mismatch: %v %v", expectedV, uint64(decodedTx.V)) + if uint64(v) != expectedV { + return fmt.Errorf("V mismatch: %v %v", expectedV, v) } expectedTo := mustConvertAddress(txTest.Transaction.To) - if decodedTx.Recipient == nil { + if decodedTx.To() == nil { if expectedTo != common.BytesToAddress([]byte{}) { // "empty" or "zero" address return fmt.Errorf("To mismatch when recipient is nil (contract creation): %v", expectedTo) } } else { - if expectedTo != *decodedTx.Recipient { - return fmt.Errorf("To mismatch: %v %v", expectedTo, *decodedTx.Recipient) + if expectedTo != *decodedTx.To() { + return fmt.Errorf("To mismatch: %v %v", expectedTo, *decodedTx.To()) } } expectedValue := mustConvertBigInt(txTest.Transaction.Value, 16) - if expectedValue.Cmp(decodedTx.Amount) != 0 { - return fmt.Errorf("Value mismatch: %v %v", expectedValue, decodedTx.Amount) + if expectedValue.Cmp(decodedTx.Value()) != 0 { + return fmt.Errorf("Value mismatch: %v %v", expectedValue, decodedTx.Value()) } return nil diff --git a/xeth/xeth.go b/xeth/xeth.go index 99e17423ad..11d30ebaca 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -946,51 +946,45 @@ func (self *XEth) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceS // TODO: align default values to have the same type, e.g. not depend on // common.Value conversions later on - - var tx *types.Transaction - if contractCreation { - tx = types.NewContractCreationTx(value, gas, price, data) - } else { - tx = types.NewTransactionMessage(to, value, gas, price, data) - } - - state := self.backend.TxPool().State() - var nonce uint64 if len(nonceStr) != 0 { nonce = common.Big(nonceStr).Uint64() } else { + state := self.backend.TxPool().State() nonce = state.GetNonce(from) } - tx.SetNonce(nonce) + var tx *types.Transaction + if contractCreation { + tx = types.NewContractCreation(nonce, value, gas, price, data) + } else { + tx = types.NewTransaction(nonce, to, value, gas, price, data) + } - if err := self.sign(tx, from, false); err != nil { + signed, err := self.sign(tx, from, false) + if err != nil { return "", err } - if err := self.backend.TxPool().Add(tx); err != nil { + if err = self.backend.TxPool().Add(signed); err != nil { return "", err } - //state.SetNonce(from, nonce+1) if contractCreation { addr := core.AddressFromMessage(tx) glog.V(logger.Info).Infof("Tx(%x) created: %x\n", tx.Hash(), addr) - - return core.AddressFromMessage(tx).Hex(), nil + return addr.Hex(), nil } else { glog.V(logger.Info).Infof("Tx(%x) to: %x\n", tx.Hash(), tx.To()) } return tx.Hash().Hex(), nil } -func (self *XEth) sign(tx *types.Transaction, from common.Address, didUnlock bool) error { +func (self *XEth) sign(tx *types.Transaction, from common.Address, didUnlock bool) (*types.Transaction, error) { hash := tx.Hash() sig, err := self.doSign(from, hash, didUnlock) if err != nil { - return err + return tx, err } - tx.SetSignatureValues(sig) - return nil + return tx.WithSignature(sig) } // callmsg is the message type used for call transations.