From c298148a7f0890cd5ed62d2a91cc81431b2731f1 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 18 Mar 2015 13:24:34 +0100 Subject: core/types: use package rlp instead of common.Decode --- core/types/block.go | 99 ++++++++++++++++++++++++++++++++++++----------- core/types/block_test.go | 57 +++++++++++++++++++++++---- core/types/derive_sha.go | 4 +- core/types/transaction.go | 55 +++++++++++++------------- 4 files changed, 155 insertions(+), 60 deletions(-) (limited to 'core') diff --git a/core/types/block.go b/core/types/block.go index aca23aa04..6f26358fb 100644 --- a/core/types/block.go +++ b/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() } diff --git a/core/types/block_test.go b/core/types/block_test.go index 16ba3043f..e4f6c38a5 100644 --- a/core/types/block_test.go +++ b/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) + } } diff --git a/core/types/derive_sha.go b/core/types/derive_sha.go index 0fc45a508..10e3d7446 100644 --- a/core/types/derive_sha.go +++ b/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()) diff --git a/core/types/transaction.go b/core/types/transaction.go index 24035a3ae..391fb46f5 100644 --- a/core/types/transaction.go +++ b/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 } -- cgit v1.2.3 From b5b83db450974f70f4bc25f280cc6ec9193f8e19 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 18 Mar 2015 13:36:48 +0100 Subject: core: use package rlp to encode blocks This also changes the chain export format so there is no enclosing list around the blocks, which enables streaming export. --- core/chain_manager.go | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) (limited to 'core') diff --git a/core/chain_manager.go b/core/chain_manager.go index 5316a3423..060805428 100644 --- a/core/chain_manager.go +++ b/core/chain_manager.go @@ -3,6 +3,7 @@ package core import ( "bytes" "fmt" + "io" "math/big" "sync" @@ -254,22 +255,20 @@ func (bc *ChainManager) ResetWithGenesisBlock(gb *types.Block) { bc.currentBlock = bc.genesisBlock } -func (self *ChainManager) Export() []byte { +// Export writes the active chain to the given writer. +func (self *ChainManager) Export(w io.Writer) error { self.mu.RLock() defer self.mu.RUnlock() - chainlogger.Infof("exporting %v blocks...\n", self.currentBlock.Header().Number) - - blocks := make([]*types.Block, int(self.currentBlock.NumberU64())+1) for block := self.currentBlock; block != nil; block = self.GetBlock(block.Header().ParentHash) { - blocks[block.NumberU64()] = block + if err := block.EncodeRLP(w); err != nil { + return err + } } - - return common.Encode(blocks) + return nil } func (bc *ChainManager) insert(block *types.Block) { - //encodedBlock := common.Encode(block) bc.blockDb.Put([]byte("LastBlock"), block.Hash().Bytes()) bc.currentBlock = block bc.lastBlockHash = block.Hash() @@ -279,10 +278,9 @@ func (bc *ChainManager) insert(block *types.Block) { } func (bc *ChainManager) write(block *types.Block) { - encodedBlock := common.Encode(block.RlpDataForStorage()) - + enc, _ := rlp.EncodeToBytes((*types.StorageBlock)(block)) key := append(blockHashPre, block.Hash().Bytes()...) - bc.blockDb.Put(key, encodedBlock) + bc.blockDb.Put(key, enc) } // Accessors @@ -324,13 +322,12 @@ func (self *ChainManager) GetBlock(hash common.Hash) *types.Block { if len(data) == 0 { return nil } - var block types.Block + var block types.StorageBlock if err := rlp.Decode(bytes.NewReader(data), &block); err != nil { - fmt.Println(err) + chainlogger.Errorf("invalid block RLP for hash %x: %v", hash, err) return nil } - - return &block + return (*types.Block)(&block) } func (self *ChainManager) GetBlockByNumber(num uint64) *types.Block { -- cgit v1.2.3 From a59dd393e71cc52b1f96973aef884af619166f38 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 18 Mar 2015 13:38:47 +0100 Subject: core: fix tests --- core/block_processor_test.go | 5 +++-- core/chain_manager_test.go | 9 ++++----- core/genesis.go | 4 ---- core/transaction_pool.go | 2 +- core/transaction_pool_test.go | 12 +++++------- 5 files changed, 13 insertions(+), 19 deletions(-) (limited to 'core') diff --git a/core/block_processor_test.go b/core/block_processor_test.go index ad29404e1..64add7e8b 100644 --- a/core/block_processor_test.go +++ b/core/block_processor_test.go @@ -4,6 +4,7 @@ import ( "math/big" "testing" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/pow/ezp" @@ -19,7 +20,7 @@ func proc() (*BlockProcessor, *ChainManager) { func TestNumber(t *testing.T) { bp, chain := proc() - block1 := chain.NewBlock(nil) + block1 := chain.NewBlock(common.Address{}) block1.Header().Number = big.NewInt(3) err := bp.ValidateHeader(block1.Header(), chain.Genesis().Header()) @@ -27,7 +28,7 @@ func TestNumber(t *testing.T) { t.Errorf("expected block number error") } - block1 = chain.NewBlock(nil) + block1 = chain.NewBlock(common.Address{}) err = bp.ValidateHeader(block1.Header(), chain.Genesis().Header()) if err == BlockNumberErr { t.Errorf("didn't expect block number error") diff --git a/core/chain_manager_test.go b/core/chain_manager_test.go index 898d37f9c..e49e594a3 100644 --- a/core/chain_manager_test.go +++ b/core/chain_manager_test.go @@ -1,7 +1,6 @@ package core import ( - "bytes" "fmt" "math/big" "os" @@ -35,7 +34,7 @@ func testFork(t *testing.T, bman *BlockProcessor, i, N int, f func(td1, td2 *big // asert the bmans have the same block at i bi1 := bman.bc.GetBlockByNumber(uint64(i)).Hash() bi2 := bman2.bc.GetBlockByNumber(uint64(i)).Hash() - if bytes.Compare(bi1, bi2) != 0 { + if bi1 != bi2 { t.Fatal("chains do not have the same hash at height", i) } @@ -270,11 +269,11 @@ func TestChainInsertions(t *testing.T) { <-done } - if bytes.Equal(chain2[len(chain2)-1].Hash(), chainMan.CurrentBlock().Hash()) { + if chain2[len(chain2)-1].Hash() != chainMan.CurrentBlock().Hash() { t.Error("chain2 is canonical and shouldn't be") } - if !bytes.Equal(chain1[len(chain1)-1].Hash(), chainMan.CurrentBlock().Hash()) { + if chain1[len(chain1)-1].Hash() != chainMan.CurrentBlock().Hash() { t.Error("chain1 isn't canonical and should be") } } @@ -320,7 +319,7 @@ func TestChainMultipleInsertions(t *testing.T) { <-done } - if !bytes.Equal(chains[longest][len(chains[longest])-1].Hash(), chainMan.CurrentBlock().Hash()) { + if chains[longest][len(chains[longest])-1].Hash() != chainMan.CurrentBlock().Hash() { t.Error("Invalid canonical chain") } } diff --git a/core/genesis.go b/core/genesis.go index 70845b502..3e00533ae 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -8,7 +8,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/state" ) @@ -19,9 +18,6 @@ import ( var ZeroHash256 = make([]byte, 32) var ZeroHash160 = make([]byte, 20) var ZeroHash512 = make([]byte, 64) -var EmptyShaList = crypto.Sha3(common.Encode([]interface{}{})) -var EmptyListRoot = crypto.Sha3(common.Encode("")) - var GenesisDiff = big.NewInt(131072) var GenesisGasLimit = big.NewInt(3141592) diff --git a/core/transaction_pool.go b/core/transaction_pool.go index 7c50e5e53..f2fe5c748 100644 --- a/core/transaction_pool.go +++ b/core/transaction_pool.go @@ -63,7 +63,7 @@ func NewTxPool(eventMux *event.TypeMux) *TxPool { func (pool *TxPool) ValidateTransaction(tx *types.Transaction) error { // Validate sender if _, err := tx.From(); err != nil { - return err + return ErrInvalidSender } // Validate curve param v, _, _ := tx.Curve() diff --git a/core/transaction_pool_test.go b/core/transaction_pool_test.go index 418cb0415..bf9012573 100644 --- a/core/transaction_pool_test.go +++ b/core/transaction_pool_test.go @@ -4,10 +4,10 @@ import ( "crypto/ecdsa" "testing" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/state" ) @@ -21,11 +21,11 @@ func SQ() stateQuery { } func (self stateQuery) GetAccount(addr []byte) *state.StateObject { - return state.NewStateObject(addr, self.db) + return state.NewStateObject(common.BytesToAddress(addr), self.db) } func transaction() *types.Transaction { - return types.NewTransactionMessage(make([]byte, 20), common.Big0, common.Big0, common.Big0, nil) + return types.NewTransactionMessage(common.Address{}, common.Big0, common.Big0, common.Big0, nil) } func setup() (*TxPool, *ecdsa.PrivateKey) { @@ -88,10 +88,8 @@ func TestRemoveInvalid(t *testing.T) { func TestInvalidSender(t *testing.T) { pool, _ := setup() - tx := new(types.Transaction) - tx.V = 28 - err := pool.ValidateTransaction(tx) + err := pool.ValidateTransaction(new(types.Transaction)) if err != ErrInvalidSender { - t.Error("expected %v, got %v", ErrInvalidSender, err) + t.Errorf("expected %v, got %v", ErrInvalidSender, err) } } -- cgit v1.2.3