diff --git a/core/types/transaction.go b/core/types/transaction.go index 14b0e4174d..95deac36e3 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "math/big" + "sync/atomic" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" @@ -20,6 +21,10 @@ func IsContractAddr(addr []byte) bool { type Transaction struct { data txdata + // caches + hash atomic.Value + size atomic.Value + from atomic.Value } type txdata struct { @@ -88,7 +93,12 @@ func (tx *Transaction) EncodeRLP(w io.Writer) error { } func (tx *Transaction) DecodeRLP(s *rlp.Stream) error { - return s.Decode(&tx.data) + _, size, _ := s.Kind() + err := s.Decode(&tx.data) + if err == nil { + tx.size.Store(common.StorageSize(rlp.ListSize(size))) + } + return err } func (tx *Transaction) Data() []byte { return common.CopyBytes(tx.data.Payload) } @@ -107,6 +117,9 @@ func (tx *Transaction) To() *common.Address { } func (tx *Transaction) Hash() common.Hash { + if hash := tx.hash.Load(); hash != nil { + return hash.(common.Hash) + } v := rlpHash([]interface{}{ tx.data.AccountNonce, tx.data.Price, @@ -115,21 +128,31 @@ func (tx *Transaction) Hash() common.Hash { tx.data.Amount, tx.data.Payload, }) + tx.hash.Store(v) return v } func (tx *Transaction) Size() common.StorageSize { - v, _, _ := rlp.EncodeToReader(&tx.data) - return common.StorageSize(v) + if size := tx.size.Load(); size != nil { + return size.(common.StorageSize) + } + c := writeCounter(0) + rlp.Encode(&c, &tx.data) + tx.size.Store(common.StorageSize(c)) + return common.StorageSize(c) } func (tx *Transaction) From() (common.Address, error) { - pubkey, err := tx.PublicKey() + if from := tx.from.Load(); from != nil { + return from.(common.Address), nil + } + pubkey, err := tx.publicKey() if err != nil { return common.Address{}, err } var addr common.Address copy(addr[:], crypto.Sha3(pubkey[1:])[12:]) + tx.from.Store(addr) return addr, nil } @@ -144,7 +167,7 @@ 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) { +func (tx *Transaction) publicKey() ([]byte, error) { if !crypto.ValidateSignatureValues(tx.data.V, tx.data.R, tx.data.S) { return nil, errors.New("invalid v, r, s values") }