From 654564e164b3b6f7f4ba1e8bbd6fcd64776068fa Mon Sep 17 00:00:00 2001
From: Felix Lange <fjl@twurst.com>
Date: Thu, 11 Jun 2015 14:05:32 +0200
Subject: 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      | 246 +++++++++++++++++++++--------------------
 core/types/transaction_test.go |  32 ++----
 5 files changed, 177 insertions(+), 230 deletions(-)

(limited to 'core')

diff --git a/core/transaction_pool.go b/core/transaction_pool.go
index 34a1733d7..45db04eef 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 b763c196d..ff8b9c730 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 b52ddffdc..03e6881be 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 a03a6b847..14b0e4174 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
-}
-
-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),
-	}
+	data txdata
+}
+
+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) Hash() common.Hash {
-	return rlpHash([]interface{}{
-		tx.AccountNonce, tx.Price, tx.GasLimit, tx.Recipient, tx.Amount, tx.Payload,
-	})
-}
-
-// 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 (self *Transaction) Data() []byte {
-	return self.Payload
+func (tx *Transaction) EncodeRLP(w io.Writer) error {
+	return rlp.Encode(w, &tx.data)
 }
 
-func (self *Transaction) Gas() *big.Int {
-	return self.GasLimit
+func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
+	return s.Decode(&tx.data)
 }
 
-func (self *Transaction) GasPrice() *big.Int {
-	return self.Price
-}
+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 (self *Transaction) Value() *big.Int {
-	return self.Amount
+func (tx *Transaction) To() *common.Address {
+	if tx.data.Recipient == nil {
+		return nil
+	} else {
+		to := *tx.data.Recipient
+		return &to
+	}
 }
 
-func (self *Transaction) Nonce() uint64 {
-	return self.AccountNonce
+func (tx *Transaction) Hash() common.Hash {
+	v := rlpHash([]interface{}{
+		tx.data.AccountNonce,
+		tx.data.Price,
+		tx.data.GasLimit,
+		tx.data.Recipient,
+		tx.data.Amount,
+		tx.data.Payload,
+	})
+	return v
 }
 
-func (self *Transaction) SetNonce(AccountNonce uint64) {
-	self.AccountNonce = AccountNonce
+func (tx *Transaction) Size() common.StorageSize {
+	v, _, _ := rlp.EncodeToReader(&tx.data)
+	return common.StorageSize(v)
 }
 
-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 492059c28..dd9c5e87b 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")
 	}
-- 
cgit v1.2.3