Browse Source

core/types: use package rlp instead of common.Decode

Felix Lange 10 years ago
parent
commit
c298148a7f
4 changed files with 155 additions and 60 deletions
  1. 77 22
      core/types/block.go
  2. 49 8
      core/types/block_test.go
  3. 3 1
      core/types/derive_sha.go
  4. 26 29
      core/types/transaction.go

+ 77 - 22
core/types/block.go

@@ -3,12 +3,13 @@ package types
 import (
 	"encoding/binary"
 	"fmt"
+	"io"
 	"math/big"
 	"sort"
 	"time"
 
 	"github.com/ethereum/go-ethereum/common"
-	"github.com/ethereum/go-ethereum/crypto"
+	"github.com/ethereum/go-ethereum/crypto/sha3"
 	"github.com/ethereum/go-ethereum/rlp"
 )
 
@@ -45,6 +46,14 @@ type Header struct {
 	Nonce [8]byte
 }
 
+func (self *Header) Hash() common.Hash {
+	return rlpHash(self.rlpData(true))
+}
+
+func (self *Header) HashNoNonce() common.Hash {
+	return rlpHash(self.rlpData(false))
+}
+
 func (self *Header) rlpData(withNonce bool) []interface{} {
 	fields := []interface{}{
 		self.ParentHash,
@@ -64,7 +73,6 @@ func (self *Header) rlpData(withNonce bool) []interface{} {
 	if withNonce {
 		fields = append(fields, self.MixDigest, self.Nonce)
 	}
-
 	return fields
 }
 
@@ -72,12 +80,11 @@ func (self *Header) RlpData() interface{} {
 	return self.rlpData(true)
 }
 
-func (self *Header) Hash() common.Hash {
-	return common.BytesToHash(crypto.Sha3(common.Encode(self.rlpData(true))))
-}
-
-func (self *Header) HashNoNonce() common.Hash {
-	return common.BytesToHash(crypto.Sha3(common.Encode(self.rlpData(false))))
+func rlpHash(x interface{}) (h common.Hash) {
+	hw := sha3.NewKeccak256()
+	rlp.Encode(hw, x)
+	hw.Sum(h[:0])
+	return h
 }
 
 type Block struct {
@@ -95,6 +102,26 @@ type Block struct {
 	Reward   *big.Int
 }
 
+// StorageBlock defines the RLP encoding of a Block stored in the
+// state database. The StorageBlock encoding contains fields that
+// would otherwise need to be recomputed.
+type StorageBlock Block
+
+// "external" block encoding. used for eth protocol, etc.
+type extblock struct {
+	Header *Header
+	Txs    []*Transaction
+	Uncles []*Header
+}
+
+// "storage" block encoding. used for database.
+type storageblock struct {
+	Header *Header
+	Txs    []*Transaction
+	Uncles []*Header
+	TD     *big.Int
+}
+
 func NewBlock(parentHash common.Hash, coinbase common.Address, root common.Hash, difficulty *big.Int, nonce uint64, extra string) *Block {
 	header := &Header{
 		Root:       root,
@@ -107,9 +134,7 @@ func NewBlock(parentHash common.Hash, coinbase common.Address, root common.Hash,
 		GasLimit:   new(big.Int),
 	}
 	header.SetNonce(nonce)
-
 	block := &Block{header: header, Reward: new(big.Int)}
-
 	return block
 }
 
@@ -122,22 +147,40 @@ func NewBlockWithHeader(header *Header) *Block {
 }
 
 func (self *Block) DecodeRLP(s *rlp.Stream) error {
-	var extblock struct {
-		Header *Header
-		Txs    []*Transaction
-		Uncles []*Header
-		TD     *big.Int // optional
+	var eb extblock
+	if err := s.Decode(&eb); err != nil {
+		return err
 	}
-	if err := s.Decode(&extblock); err != nil {
+	self.header, self.uncles, self.transactions = eb.Header, eb.Uncles, eb.Txs
+	return nil
+}
+
+func (self Block) EncodeRLP(w io.Writer) error {
+	return rlp.Encode(w, extblock{
+		Header: self.header,
+		Txs:    self.transactions,
+		Uncles: self.uncles,
+	})
+}
+
+func (self *StorageBlock) DecodeRLP(s *rlp.Stream) error {
+	var sb storageblock
+	if err := s.Decode(&sb); err != nil {
 		return err
 	}
-	self.header = extblock.Header
-	self.uncles = extblock.Uncles
-	self.transactions = extblock.Txs
-	self.Td = extblock.TD
+	self.header, self.uncles, self.transactions, self.Td = sb.Header, sb.Uncles, sb.Txs, sb.TD
 	return nil
 }
 
+func (self StorageBlock) EncodeRLP(w io.Writer) error {
+	return rlp.Encode(w, storageblock{
+		Header: self.header,
+		Txs:    self.transactions,
+		Uncles: self.uncles,
+		TD:     self.Td,
+	})
+}
+
 func (self *Block) Header() *Header {
 	return self.header
 }
@@ -148,7 +191,7 @@ func (self *Block) Uncles() []*Header {
 
 func (self *Block) SetUncles(uncleHeaders []*Header) {
 	self.uncles = uncleHeaders
-	self.header.UncleHash = common.BytesToHash(crypto.Sha3(common.Encode(uncleHeaders)))
+	self.header.UncleHash = rlpHash(uncleHeaders)
 }
 
 func (self *Block) Transactions() Transactions {
@@ -213,7 +256,6 @@ func (self *Block) GasLimit() *big.Int       { return self.header.GasLimit }
 func (self *Block) GasUsed() *big.Int        { return self.header.GasUsed }
 func (self *Block) Root() common.Hash        { return self.header.Root }
 func (self *Block) SetRoot(root common.Hash) { self.header.Root = root }
-func (self *Block) Size() common.StorageSize { return common.StorageSize(len(common.Encode(self))) }
 func (self *Block) GetTransaction(i int) *Transaction {
 	if len(self.transactions) > i {
 		return self.transactions[i]
@@ -227,6 +269,19 @@ func (self *Block) GetUncle(i int) *Header {
 	return nil
 }
 
+func (self *Block) Size() common.StorageSize {
+	c := writeCounter(0)
+	rlp.Encode(&c, self)
+	return common.StorageSize(c)
+}
+
+type writeCounter common.StorageSize
+
+func (c *writeCounter) Write(b []byte) (int, error) {
+	*c += writeCounter(len(b))
+	return len(b), nil
+}
+
 // Implement pow.Block
 func (self *Block) Difficulty() *big.Int     { return self.header.Difficulty }
 func (self *Block) HashNoNonce() common.Hash { return self.header.HashNoNonce() }

+ 49 - 8
core/types/block_test.go

@@ -1,19 +1,60 @@
 package types
 
 import (
+	"bytes"
 	"math/big"
+	"reflect"
 	"testing"
 
 	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/rlp"
 )
 
-// XXX Tests doesn't really do anything. This tests exists while working on the fixed size conversions
-func TestConversion(t *testing.T) {
-	var (
-		parent   common.Hash
-		coinbase common.Address
-		hash     common.Hash
-	)
+// from bcValidBlockTest.json, "SimpleTx"
+func TestBlockEncoding(t *testing.T) {
+	blockEnc := common.FromHex("f90260f901f9a083cafc574e1f51ba9dc0568fc617a08ea2429fb384059c972f13b19fa1c8dd55a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017a05fe50b260da6308036625b850b5d6ced6d0a9f814c0688bc91ffb7b7a3a54b67a0bc37d79753ad738a6dac4921e57392f145d8887476de3f783dfa7edae9283e52b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001832fefd8825208845506eb0780a0bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff49888a13a5a8c8f2bb1c4f861f85f800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba09bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094fa08a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1c0")
 
-	NewBlock(parent, coinbase, hash, big.NewInt(0), 0, "")
+	var block Block
+	if err := rlp.DecodeBytes(blockEnc, &block); err != nil {
+		t.Fatal("decode error: ", err)
+	}
+
+	check := func(f string, got, want interface{}) {
+		if !reflect.DeepEqual(got, want) {
+			t.Errorf("%s mismatch: got %v, want %v", f, got, want)
+		}
+	}
+	check("Difficulty", block.Difficulty(), big.NewInt(131072))
+	check("GasLimit", block.GasLimit(), big.NewInt(3141592))
+	check("GasUsed", block.GasUsed(), big.NewInt(21000))
+	check("Coinbase", block.Coinbase(), common.HexToAddress("8888f1f195afa192cfee860698584c030f4c9db1"))
+	check("MixDigest", block.MixDigest(), common.HexToHash("bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff498"))
+	check("Root", block.Root(), common.HexToHash("ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017"))
+	check("Hash", block.Hash(), common.HexToHash("0a5843ac1cb04865017cb35a57b50b07084e5fcee39b5acadade33149f4fff9e"))
+	check("Nonce", block.Nonce(), uint64(0xa13a5a8c8f2bb1c4))
+	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.FromHex("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f"),
+			S:            common.FromHex("8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1"),
+			Recipient:    &to,
+		},
+	})
+
+	ourBlockEnc, err := rlp.EncodeToBytes(&block)
+	if err != nil {
+		t.Fatal("encode error: ", err)
+	}
+	if !bytes.Equal(ourBlockEnc, blockEnc) {
+		t.Errorf("encoded block mismatch:\ngot:  %x\nwant: %x", ourBlockEnc, blockEnc)
+	}
 }

+ 3 - 1
core/types/derive_sha.go

@@ -3,6 +3,7 @@ package types
 import (
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/ethdb"
+	"github.com/ethereum/go-ethereum/rlp"
 	"github.com/ethereum/go-ethereum/trie"
 )
 
@@ -15,7 +16,8 @@ func DeriveSha(list DerivableList) common.Hash {
 	db, _ := ethdb.NewMemDatabase()
 	trie := trie.New(nil, db)
 	for i := 0; i < list.Len(); i++ {
-		trie.Update(common.Encode(i), list.GetRlp(i))
+		key, _ := rlp.EncodeToBytes(i)
+		trie.Update(key, list.GetRlp(i))
 	}
 
 	return common.BytesToHash(trie.Root())

+ 26 - 29
core/types/transaction.go

@@ -1,16 +1,14 @@
 package types
 
 import (
-	"bytes"
+	"crypto/ecdsa"
 	"errors"
 	"fmt"
-	"io"
 	"math/big"
 
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/crypto/secp256k1"
-	"github.com/ethereum/go-ethereum/crypto/sha3"
 	"github.com/ethereum/go-ethereum/rlp"
 )
 
@@ -38,16 +36,18 @@ func NewTransactionMessage(to common.Address, amount, gasAmount, gasPrice *big.I
 }
 
 func NewTransactionFromBytes(data []byte) *Transaction {
+	// TODO: remove this function if possible. callers would
+	// much better off decoding into transaction directly.
+	// it's not that hard.
 	tx := new(Transaction)
-	rlp.Decode(bytes.NewReader(data), tx)
+	rlp.DecodeBytes(data, tx)
 	return tx
 }
 
-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[:0])
-	return a
+func (tx *Transaction) Hash() common.Hash {
+	return rlpHash([]interface{}{
+		tx.AccountNonce, tx.Price, tx.GasLimit, tx.Recipient, tx.Amount, tx.Payload,
+	})
 }
 
 func (self *Transaction) Data() []byte {
@@ -122,17 +122,14 @@ 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,
-	})
+func (tx *Transaction) SignECDSA(prv *ecdsa.PrivateKey) error {
+	h := tx.Hash()
+	sig, err := crypto.Sign(h[:], prv)
+	if err != nil {
+		return err
+	}
+	tx.SetSignatureValues(sig)
+	return nil
 }
 
 // TODO: remove
@@ -141,11 +138,6 @@ func (tx *Transaction) RlpData() interface{} {
 	return append(data, tx.V, new(big.Int).SetBytes(tx.R).Bytes(), new(big.Int).SetBytes(tx.S).Bytes())
 }
 
-// TODO: remove
-func (tx *Transaction) RlpEncode() []byte {
-	return common.Encode(tx)
-}
-
 func (tx *Transaction) String() string {
 	var from, to string
 	if f, err := tx.From(); err != nil {
@@ -158,6 +150,7 @@ func (tx *Transaction) String() string {
 	} else {
 		to = fmt.Sprintf("%x", t[:])
 	}
+	enc, _ := rlp.EncodeToBytes(tx)
 	return fmt.Sprintf(`
 	TX(%x)
 	Contract: %v
@@ -185,7 +178,7 @@ func (tx *Transaction) String() string {
 		tx.V,
 		tx.R,
 		tx.S,
-		common.Encode(tx),
+		enc,
 	)
 }
 
@@ -204,9 +197,13 @@ func (self Transactions) RlpData() interface{} {
 	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] }
-func (s Transactions) GetRlp(i int) []byte { return common.Rlp(s[i]) }
+func (s Transactions) Len() int      { return len(s) }
+func (s Transactions) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
+func (s Transactions) GetRlp(i int) []byte {
+	enc, _ := rlp.EncodeToBytes(s[i])
+	return enc
+}
 
 type TxByNonce struct{ Transactions }