diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/block_processor.go | 68 | ||||
-rw-r--r-- | core/block_processor_test.go | 9 | ||||
-rw-r--r-- | core/chain_makers.go | 142 | ||||
-rw-r--r-- | core/chain_manager.go | 166 | ||||
-rw-r--r-- | core/chain_manager_test.go | 204 | ||||
-rw-r--r-- | core/error.go | 36 | ||||
-rw-r--r-- | core/events.go | 10 | ||||
-rw-r--r-- | core/execution.go | 10 | ||||
-rw-r--r-- | core/filter.go | 2 | ||||
-rw-r--r-- | core/genesis.go | 17 | ||||
-rw-r--r-- | core/manager.go | 3 | ||||
-rw-r--r-- | core/state_transition.go | 30 | ||||
-rw-r--r-- | core/transaction_pool.go | 9 | ||||
-rw-r--r-- | core/types/block.go | 58 | ||||
-rw-r--r-- | core/types/bloom9.go | 7 |
15 files changed, 647 insertions, 124 deletions
diff --git a/core/block_processor.go b/core/block_processor.go index 7eaeb5be0..34c12729c 100644 --- a/core/block_processor.go +++ b/core/block_processor.go @@ -12,7 +12,6 @@ import ( "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/pow" - "github.com/ethereum/go-ethereum/pow/ezp" "github.com/ethereum/go-ethereum/state" "gopkg.in/fatih/set.v0" ) @@ -46,11 +45,11 @@ type BlockProcessor struct { eventMux *event.TypeMux } -func NewBlockProcessor(db ethutil.Database, txpool *TxPool, chainManager *ChainManager, eventMux *event.TypeMux) *BlockProcessor { +func NewBlockProcessor(db ethutil.Database, pow pow.PoW, txpool *TxPool, chainManager *ChainManager, eventMux *event.TypeMux) *BlockProcessor { sm := &BlockProcessor{ db: db, mem: make(map[string]*big.Int), - Pow: ezp.New(), + Pow: pow, bc: chainManager, eventMux: eventMux, txpool: txpool, @@ -105,6 +104,9 @@ func (self *BlockProcessor) ApplyTransaction(coinbase *state.StateObject, stated return receipt, txGas, err } +func (self *BlockProcessor) ChainManager() *ChainManager { + return self.bc +} func (self *BlockProcessor) ApplyTransactions(coinbase *state.StateObject, statedb *state.StateDB, block *types.Block, txs types.Transactions, transientProcess bool) (types.Receipts, types.Transactions, types.Transactions, types.Transactions, error) { var ( @@ -174,10 +176,15 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (td *big state := state.New(parent.Root(), sm.db) // Block validation - if err = sm.ValidateBlock(block, parent); err != nil { + if err = sm.ValidateHeader(block.Header(), parent.Header()); err != nil { return } + // There can be at most two uncles + if len(block.Uncles()) > 2 { + return nil, ValidationError("Block can only contain one uncle (contained %v)", len(block.Uncles())) + } + receipts, err := sm.TransitionState(state, parent, block, false) if err != nil { return @@ -204,7 +211,6 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (td *big // Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, R1]])) receiptSha := types.DeriveSha(receipts) if bytes.Compare(receiptSha, header.ReceiptHash) != 0 { - fmt.Println("receipts", receipts) err = fmt.Errorf("validating receipt root. received=%x got=%x", header.ReceiptHash, receiptSha) return } @@ -237,36 +243,38 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (td *big // Validates the current block. Returns an error if the block was invalid, // an uncle or anything that isn't on the current block chain. // Validation validates easy over difficult (dagger takes longer time = difficult) -func (sm *BlockProcessor) ValidateBlock(block, parent *types.Block) error { - if len(block.Header().Extra) > 1024 { - return fmt.Errorf("Block extra data too long (%d)", len(block.Header().Extra)) +func (sm *BlockProcessor) ValidateHeader(block, parent *types.Header) error { + if len(block.Extra) > 1024 { + return fmt.Errorf("Block extra data too long (%d)", len(block.Extra)) } expd := CalcDifficulty(block, parent) - if expd.Cmp(block.Header().Difficulty) != 0 { - return fmt.Errorf("Difficulty check failed for block %v, %v", block.Header().Difficulty, expd) + if expd.Cmp(block.Difficulty) != 0 { + return fmt.Errorf("Difficulty check failed for block %v, %v", block.Difficulty, expd) } - expl := CalcGasLimit(parent, block) - if expl.Cmp(block.Header().GasLimit) != 0 { - return fmt.Errorf("GasLimit check failed for block %v, %v", block.Header().GasLimit, expl) + // block.gasLimit - parent.gasLimit <= parent.gasLimit / 1024 + a := new(big.Int).Sub(block.GasLimit, parent.GasLimit) + b := new(big.Int).Div(parent.GasLimit, big.NewInt(1024)) + if a.Cmp(b) > 0 { + return fmt.Errorf("GasLimit check failed for block %v (%v > %v)", block.GasLimit, a, b) } - if block.Time() < parent.Time() { - return ValidationError("Block timestamp not after prev block (%v - %v)", block.Header().Time, parent.Header().Time) + if block.Time <= parent.Time { + return ValidationError("Block timestamp not after or equal to prev block (%v - %v)", block.Time, parent.Time) } - if block.Time() > time.Now().Unix() { + if int64(block.Time) > time.Now().Unix() { return BlockFutureErr } - if new(big.Int).Sub(block.Number(), parent.Number()).Cmp(big.NewInt(1)) != 0 { + if new(big.Int).Sub(block.Number, parent.Number).Cmp(big.NewInt(1)) != 0 { return BlockNumberErr } // Verify the nonce of the block. Return an error if it's not valid - if !sm.Pow.Verify(block) { - return ValidationError("Block's nonce is invalid (= %v)", ethutil.Bytes2Hex(block.Header().Nonce)) + if !sm.Pow.Verify(types.NewBlockWithHeader(block)) { + return ValidationError("Block's nonce is invalid (= %x)", block.Nonce) } return nil @@ -276,25 +284,41 @@ func (sm *BlockProcessor) AccumulateRewards(statedb *state.StateDB, block, paren reward := new(big.Int).Set(BlockReward) ancestors := set.New() + uncles := set.New() + ancestorHeaders := make(map[string]*types.Header) for _, ancestor := range sm.bc.GetAncestors(block, 7) { - ancestors.Add(string(ancestor.Hash())) + hash := string(ancestor.Hash()) + ancestorHeaders[hash] = ancestor.Header() + ancestors.Add(hash) + // Include ancestors uncles in the uncle set. Uncles must be unique. + for _, uncle := range ancestor.Uncles() { + uncles.Add(string(uncle.Hash())) + } } - uncles := set.New() uncles.Add(string(block.Hash())) for _, uncle := range block.Uncles() { if uncles.Has(string(uncle.Hash())) { // Error not unique return UncleError("Uncle not unique") } + uncles.Add(string(uncle.Hash())) + if ancestors.Has(string(uncle.Hash())) { + return UncleError("Uncle is ancestor") + } + if !ancestors.Has(string(uncle.ParentHash)) { return UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.ParentHash[0:4])) } + if err := sm.ValidateHeader(uncle, ancestorHeaders[string(uncle.ParentHash)]); err != nil { + return ValidationError(fmt.Sprintf("%v", err)) + } + if !sm.Pow.Verify(types.NewBlockWithHeader(uncle)) { - return ValidationError("Uncle's nonce is invalid (= %v)", ethutil.Bytes2Hex(uncle.Nonce)) + return ValidationError("Uncle's nonce is invalid (= %x)", uncle.Nonce) } r := new(big.Int) diff --git a/core/block_processor_test.go b/core/block_processor_test.go index 35aeaa714..a031c2669 100644 --- a/core/block_processor_test.go +++ b/core/block_processor_test.go @@ -6,14 +6,15 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/pow/ezp" ) func proc() (*BlockProcessor, *ChainManager) { db, _ := ethdb.NewMemDatabase() var mux event.TypeMux - chainMan := NewChainManager(db, &mux) - return NewBlockProcessor(db, nil, chainMan, &mux), chainMan + chainMan := NewChainManager(db, db, &mux) + return NewBlockProcessor(db, ezp.New(), nil, chainMan, &mux), chainMan } func TestNumber(t *testing.T) { @@ -21,13 +22,13 @@ func TestNumber(t *testing.T) { block1 := chain.NewBlock(nil) block1.Header().Number = big.NewInt(3) - err := bp.ValidateBlock(block1, chain.Genesis()) + err := bp.ValidateHeader(block1.Header(), chain.Genesis().Header()) if err != BlockNumberErr { t.Errorf("expected block number error") } block1 = chain.NewBlock(nil) - err = bp.ValidateBlock(block1, chain.Genesis()) + err = bp.ValidateHeader(block1.Header(), chain.Genesis().Header()) if err == BlockNumberErr { t.Errorf("didn't expect block number error") } diff --git a/core/chain_makers.go b/core/chain_makers.go new file mode 100644 index 000000000..b5c50dc3d --- /dev/null +++ b/core/chain_makers.go @@ -0,0 +1,142 @@ +package core + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/pow" + "github.com/ethereum/go-ethereum/state" +) + +// So we can generate blocks easily +type FakePow struct{} + +func (f FakePow) Search(block pow.Block, stop <-chan struct{}) (uint64, []byte, []byte) { + return 0, nil, nil +} +func (f FakePow) Verify(block pow.Block) bool { return true } +func (f FakePow) GetHashrate() int64 { return 0 } +func (f FakePow) Turbo(bool) {} + +// So we can deterministically seed different blockchains +var ( + CanonicalSeed = 1 + ForkSeed = 2 +) + +// Utility functions for making chains on the fly +// Exposed for sake of testing from other packages (eg. go-ethash) +func NewBlockFromParent(addr []byte, parent *types.Block) *types.Block { + return newBlockFromParent(addr, parent) +} + +func MakeBlock(bman *BlockProcessor, parent *types.Block, i int, db ethutil.Database, seed int) *types.Block { + return makeBlock(bman, parent, i, db, seed) +} + +func MakeChain(bman *BlockProcessor, parent *types.Block, max int, db ethutil.Database, seed int) types.Blocks { + return makeChain(bman, parent, max, db, seed) +} + +func NewChainMan(block *types.Block, eventMux *event.TypeMux, db ethutil.Database) *ChainManager { + return newChainManager(block, eventMux, db) +} + +func NewBlockProc(db ethutil.Database, txpool *TxPool, cman *ChainManager, eventMux *event.TypeMux) *BlockProcessor { + return newBlockProcessor(db, txpool, cman, eventMux) +} + +func NewCanonical(n int, db ethutil.Database) (*BlockProcessor, error) { + return newCanonical(n, db) +} + +// block time is fixed at 10 seconds +func newBlockFromParent(addr []byte, parent *types.Block) *types.Block { + block := types.NewBlock(parent.Hash(), addr, parent.Root(), ethutil.BigPow(2, 32), 0, "") + block.SetUncles(nil) + block.SetTransactions(nil) + block.SetReceipts(nil) + + header := block.Header() + header.Difficulty = CalcDifficulty(block.Header(), parent.Header()) + header.Number = new(big.Int).Add(parent.Header().Number, ethutil.Big1) + header.Time = parent.Header().Time + 10 + header.GasLimit = CalcGasLimit(parent, block) + + block.Td = parent.Td + + return block +} + +// Actually make a block by simulating what miner would do +// we seed chains by the first byte of the coinbase +func makeBlock(bman *BlockProcessor, parent *types.Block, i int, db ethutil.Database, seed int) *types.Block { + addr := ethutil.LeftPadBytes([]byte{byte(i)}, 20) + addr[0] = byte(seed) + block := newBlockFromParent(addr, parent) + state := state.New(block.Root(), db) + cbase := state.GetOrNewStateObject(addr) + cbase.SetGasPool(CalcGasLimit(parent, block)) + cbase.AddBalance(BlockReward) + state.Update(ethutil.Big0) + block.SetRoot(state.Root()) + return block +} + +// Make a chain with real blocks +// Runs ProcessWithParent to get proper state roots +func makeChain(bman *BlockProcessor, parent *types.Block, max int, db ethutil.Database, seed int) types.Blocks { + bman.bc.currentBlock = parent + blocks := make(types.Blocks, max) + for i := 0; i < max; i++ { + block := makeBlock(bman, parent, i, db, seed) + td, err := bman.processWithParent(block, parent) + if err != nil { + fmt.Println("process with parent failed", err) + panic(err) + } + block.Td = td + blocks[i] = block + parent = block + } + return blocks +} + +// Create a new chain manager starting from given block +// Effectively a fork factory +func newChainManager(block *types.Block, eventMux *event.TypeMux, db ethutil.Database) *ChainManager { + bc := &ChainManager{blockDb: db, stateDb: db, genesisBlock: GenesisBlock(db), eventMux: eventMux} + if block == nil { + bc.Reset() + } else { + bc.currentBlock = block + bc.td = block.Td + } + return bc +} + +// block processor with fake pow +func newBlockProcessor(db ethutil.Database, txpool *TxPool, cman *ChainManager, eventMux *event.TypeMux) *BlockProcessor { + bman := NewBlockProcessor(db, FakePow{}, txpool, newChainManager(nil, eventMux, db), eventMux) + return bman +} + +// Make a new, deterministic canonical chain by running InsertChain +// on result of makeChain +func newCanonical(n int, db ethutil.Database) (*BlockProcessor, error) { + eventMux := &event.TypeMux{} + txpool := NewTxPool(eventMux) + + bman := newBlockProcessor(db, txpool, newChainManager(nil, eventMux, db), eventMux) + bman.bc.SetProcessor(bman) + parent := bman.bc.CurrentBlock() + if n == 0 { + return bman, nil + } + lchain := makeChain(bman, parent, n, db, CanonicalSeed) + err := bman.bc.InsertChain(lchain) + return bman, err +} diff --git a/core/chain_manager.go b/core/chain_manager.go index 81f085c47..9dc41f421 100644 --- a/core/chain_manager.go +++ b/core/chain_manager.go @@ -14,26 +14,28 @@ import ( "github.com/ethereum/go-ethereum/state" ) -var chainlogger = logger.NewLogger("CHAIN") -var jsonlogger = logger.NewJsonLogger() - -type ChainEvent struct { - Block *types.Block - Td *big.Int -} +var ( + chainlogger = logger.NewLogger("CHAIN") + jsonlogger = logger.NewJsonLogger() +) type StateQuery interface { GetAccount(addr []byte) *state.StateObject } -func CalcDifficulty(block, parent *types.Block) *big.Int { +func CalcDifficulty(block, parent *types.Header) *big.Int { diff := new(big.Int) - adjust := new(big.Int).Rsh(parent.Difficulty(), 10) - if block.Time() >= parent.Time()+8 { - diff.Sub(parent.Difficulty(), adjust) + min := big.NewInt(2048) + adjust := new(big.Int).Div(parent.Difficulty, min) + if (block.Time - parent.Time) < 8 { + diff.Add(parent.Difficulty, adjust) } else { - diff.Add(parent.Difficulty(), adjust) + diff.Sub(parent.Difficulty, adjust) + } + + if diff.Cmp(GenesisDiff) < 0 { + return GenesisDiff } return diff @@ -59,7 +61,6 @@ func CalcGasLimit(parent, block *types.Block) *big.Int { } // ((1024-1) * parent.gasLimit + (gasUsed * 6 / 5)) / 1024 - previous := new(big.Int).Mul(big.NewInt(1024-1), parent.GasLimit()) current := new(big.Rat).Mul(new(big.Rat).SetInt(parent.GasUsed()), big.NewRat(6, 5)) curInt := new(big.Int).Div(current.Num(), current.Denom()) @@ -74,7 +75,8 @@ func CalcGasLimit(parent, block *types.Block) *big.Int { type ChainManager struct { //eth EthManager - db ethutil.Database + blockDb ethutil.Database + stateDb ethutil.Database processor types.BlockProcessor eventMux *event.TypeMux genesisBlock *types.Block @@ -87,13 +89,16 @@ type ChainManager struct { transState *state.StateDB txState *state.StateDB + + quit chan struct{} } -func NewChainManager(db ethutil.Database, mux *event.TypeMux) *ChainManager { - bc := &ChainManager{db: db, genesisBlock: GenesisBlock(db), eventMux: mux} +func NewChainManager(blockDb, stateDb ethutil.Database, mux *event.TypeMux) *ChainManager { + bc := &ChainManager{blockDb: blockDb, stateDb: stateDb, genesisBlock: GenesisBlock(stateDb), eventMux: mux, quit: make(chan struct{})} bc.setLastBlock() bc.transState = bc.State().Copy() bc.txState = bc.State().Copy() + go bc.update() return bc } @@ -131,7 +136,7 @@ func (self *ChainManager) SetProcessor(proc types.BlockProcessor) { } func (self *ChainManager) State() *state.StateDB { - return state.New(self.CurrentBlock().Root(), self.db) + return state.New(self.CurrentBlock().Root(), self.stateDb) } func (self *ChainManager) TransState() *state.StateDB { @@ -159,7 +164,7 @@ func (self *ChainManager) setTransState(statedb *state.StateDB) { } func (bc *ChainManager) setLastBlock() { - data, _ := bc.db.Get([]byte("LastBlock")) + data, _ := bc.blockDb.Get([]byte("LastBlock")) if len(data) != 0 { var block types.Block rlp.Decode(bytes.NewReader(data), &block) @@ -167,7 +172,7 @@ func (bc *ChainManager) setLastBlock() { bc.lastBlockHash = block.Hash() // Set the last know difficulty (might be 0x0 as initial value, Genesis) - bc.td = ethutil.BigD(bc.db.LastKnownTD()) + bc.td = ethutil.BigD(bc.blockDb.LastKnownTD()) } else { bc.Reset() } @@ -193,7 +198,7 @@ func (bc *ChainManager) NewBlock(coinbase []byte) *types.Block { coinbase, root, ethutil.BigPow(2, 32), - nil, + 0, "") block.SetUncles(nil) block.SetTransactions(nil) @@ -202,7 +207,7 @@ func (bc *ChainManager) NewBlock(coinbase []byte) *types.Block { parent := bc.currentBlock if parent != nil { header := block.Header() - header.Difficulty = CalcDifficulty(block, parent) + header.Difficulty = CalcDifficulty(block.Header(), parent.Header()) header.Number = new(big.Int).Add(parent.Header().Number, ethutil.Big1) header.GasLimit = CalcGasLimit(parent, block) @@ -216,7 +221,7 @@ func (bc *ChainManager) Reset() { defer bc.mu.Unlock() for block := bc.currentBlock; block != nil; block = bc.GetBlock(block.Header().ParentHash) { - bc.db.Delete(block.Hash()) + bc.blockDb.Delete(block.Hash()) } // Prepare the genesis block @@ -227,6 +232,21 @@ func (bc *ChainManager) Reset() { bc.setTotalDifficulty(ethutil.Big("0")) } +func (bc *ChainManager) ResetWithGenesisBlock(gb *types.Block) { + bc.mu.Lock() + defer bc.mu.Unlock() + + for block := bc.currentBlock; block != nil; block = bc.GetBlock(block.Header().ParentHash) { + bc.blockDb.Delete(block.Hash()) + } + + // Prepare the genesis block + bc.genesisBlock = gb + bc.write(bc.genesisBlock) + bc.insert(bc.genesisBlock) + bc.currentBlock = bc.genesisBlock +} + func (self *ChainManager) Export() []byte { self.mu.RLock() defer self.mu.RUnlock() @@ -243,14 +263,14 @@ func (self *ChainManager) Export() []byte { func (bc *ChainManager) insert(block *types.Block) { encodedBlock := ethutil.Encode(block) - bc.db.Put([]byte("LastBlock"), encodedBlock) + bc.blockDb.Put([]byte("LastBlock"), encodedBlock) bc.currentBlock = block bc.lastBlockHash = block.Hash() } func (bc *ChainManager) write(block *types.Block) { encodedBlock := ethutil.Encode(block.RlpDataForStorage()) - bc.db.Put(block.Hash(), encodedBlock) + bc.blockDb.Put(block.Hash(), encodedBlock) } // Accessors @@ -260,7 +280,7 @@ func (bc *ChainManager) Genesis() *types.Block { // Block fetching methods func (bc *ChainManager) HasBlock(hash []byte) bool { - data, _ := bc.db.Get(hash) + data, _ := bc.blockDb.Get(hash) return len(data) != 0 } @@ -269,7 +289,6 @@ func (self *ChainManager) GetBlockHashesFromHash(hash []byte, max uint64) (chain if block == nil { return } - // XXX Could be optimised by using a different database which only holds hashes (i.e., linked list) for i := uint64(0); i < max; i++ { parentHash := block.Header().ParentHash @@ -289,7 +308,7 @@ func (self *ChainManager) GetBlockHashesFromHash(hash []byte, max uint64) (chain } func (self *ChainManager) GetBlock(hash []byte) *types.Block { - data, _ := self.db.Get(hash) + data, _ := self.blockDb.Get(hash) if len(data) == 0 { return nil } @@ -343,7 +362,7 @@ func (self *ChainManager) GetBlockByNumber(num uint64) *types.Block { } func (bc *ChainManager) setTotalDifficulty(td *big.Int) { - bc.db.Put([]byte("LTD"), td.Bytes()) + bc.blockDb.Put([]byte("LTD"), td.Bytes()) bc.td = td } @@ -368,16 +387,24 @@ func (self *ChainManager) CalcTotalDiff(block *types.Block) (*big.Int, error) { } func (bc *ChainManager) Stop() { - if bc.CurrentBlock != nil { - chainlogger.Infoln("Stopped") - } + close(bc.quit) +} + +type queueEvent struct { + queue []interface{} + canonicalCount int + sideCount int + splitCount int } func (self *ChainManager) InsertChain(chain types.Blocks) error { - self.tsmu.Lock() - defer self.tsmu.Unlock() + //self.tsmu.Lock() + //defer self.tsmu.Unlock() - for _, block := range chain { + // A queued approach to delivering events. This is generally faster than direct delivery and requires much less mutex acquiring. + var queue = make([]interface{}, len(chain)) + var queueEvent = queueEvent{queue: queue} + for i, block := range chain { // Call in to the block processor and check for errors. It's likely that if one block fails // all others will fail too (unless a known block is returned). td, err := self.processor.Process(block) @@ -394,7 +421,6 @@ func (self *ChainManager) InsertChain(chain types.Blocks) error { } block.Td = td - var canonical, split bool self.mu.Lock() cblock := self.currentBlock { @@ -406,37 +432,73 @@ func (self *ChainManager) InsertChain(chain types.Blocks) error { if td.Cmp(self.td) > 0 { if block.Header().Number.Cmp(new(big.Int).Add(cblock.Header().Number, ethutil.Big1)) < 0 { chainlogger.Infof("Split detected. New head #%v (%x) TD=%v, was #%v (%x) TD=%v\n", block.Header().Number, block.Hash()[:4], td, cblock.Header().Number, cblock.Hash()[:4], self.td) - split = true + + queue[i] = ChainSplitEvent{block} + queueEvent.splitCount++ } self.setTotalDifficulty(td) self.insert(block) - canonical = true + jsonlogger.LogJson(&logger.EthChainNewHead{ + BlockHash: ethutil.Bytes2Hex(block.Hash()), + BlockNumber: block.Number(), + ChainHeadHash: ethutil.Bytes2Hex(cblock.Hash()), + BlockPrevHash: ethutil.Bytes2Hex(block.ParentHash()), + }) + + self.setTransState(state.New(block.Root(), self.stateDb)) + queue[i] = ChainEvent{block} + queueEvent.canonicalCount++ + } else { + queue[i] = ChainSideEvent{block} + queueEvent.sideCount++ } } self.mu.Unlock() - if canonical { - jsonlogger.LogJson(&logger.EthChainNewHead{ - BlockHash: ethutil.Bytes2Hex(block.Hash()), - BlockNumber: block.Number(), - ChainHeadHash: ethutil.Bytes2Hex(cblock.Hash()), - BlockPrevHash: ethutil.Bytes2Hex(block.ParentHash()), - }) - self.setTransState(state.New(block.Root(), self.db)) - self.eventMux.Post(ChainEvent{block, td}) - } - - if split { - self.setTxState(state.New(block.Root(), self.db)) - self.eventMux.Post(ChainSplitEvent{block}) - } } + // XXX put this in a goroutine? + go self.eventMux.Post(queueEvent) + return nil } +func (self *ChainManager) update() { + events := self.eventMux.Subscribe(queueEvent{}) + +out: + for { + select { + case ev := <-events.Chan(): + switch ev := ev.(type) { + case queueEvent: + for i, event := range ev.queue { + switch event := event.(type) { + case ChainEvent: + // We need some control over the mining operation. Acquiring locks and waiting for the miner to create new block takes too long + // and in most cases isn't even necessary. + if i == ev.canonicalCount { + self.eventMux.Post(ChainHeadEvent{event.Block}) + } + case ChainSplitEvent: + // On chain splits we need to reset the transaction state. We can't be sure whether the actual + // state of the accounts are still valid. + if i == ev.splitCount { + self.setTxState(state.New(event.Block.Root(), self.stateDb)) + } + } + + self.eventMux.Post(event) + } + } + case <-self.quit: + break out + } + } +} + // Satisfy state query interface func (self *ChainManager) GetAccount(addr []byte) *state.StateObject { return self.State().GetAccount(addr) diff --git a/core/chain_manager_test.go b/core/chain_manager_test.go index bc3a264d1..e78c2e980 100644 --- a/core/chain_manager_test.go +++ b/core/chain_manager_test.go @@ -3,6 +3,7 @@ package core import ( "bytes" "fmt" + "math/big" "os" "path" "runtime" @@ -21,6 +22,75 @@ func init() { ethutil.ReadConfig("/tmp/ethtest", "/tmp/ethtest", "ETH") } +// Test fork of length N starting from block i +func testFork(t *testing.T, bman *BlockProcessor, i, N int, f func(td1, td2 *big.Int)) { + // switch databases to process the new chain + db, err := ethdb.NewMemDatabase() + if err != nil { + t.Fatal("Failed to create db:", err) + } + // copy old chain up to i into new db with deterministic canonical + bman2, err := newCanonical(i, db) + if err != nil { + t.Fatal("could not make new canonical in testFork", err) + } + // 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 { + t.Fatal("chains do not have the same hash at height", i) + } + + bman2.bc.SetProcessor(bman2) + + // extend the fork + parent := bman2.bc.CurrentBlock() + chainB := makeChain(bman2, parent, N, db, ForkSeed) + err = bman2.bc.InsertChain(chainB) + if err != nil { + t.Fatal("Insert chain error for fork:", err) + } + + tdpre := bman.bc.Td() + // Test the fork's blocks on the original chain + td, err := testChain(chainB, bman) + if err != nil { + t.Fatal("expected chainB not to give errors:", err) + } + // Compare difficulties + f(tdpre, td) +} + +func printChain(bc *ChainManager) { + for i := bc.CurrentBlock().Number().Uint64(); i > 0; i-- { + b := bc.GetBlockByNumber(uint64(i)) + fmt.Printf("\t%x\n", b.Hash()) + } +} + +// process blocks against a chain +func testChain(chainB types.Blocks, bman *BlockProcessor) (*big.Int, error) { + td := new(big.Int) + for _, block := range chainB { + td2, err := bman.bc.processor.Process(block) + if err != nil { + if IsKnownBlockErr(err) { + continue + } + return nil, err + } + block.Td = td2 + td = td2 + + bman.bc.mu.Lock() + { + bman.bc.write(block) + } + bman.bc.mu.Unlock() + } + return td, nil +} + func loadChain(fn string, t *testing.T) (types.Blocks, error) { fh, err := os.OpenFile(path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "_data", fn), os.O_RDONLY, os.ModePerm) if err != nil { @@ -45,6 +115,130 @@ func insertChain(done chan bool, chainMan *ChainManager, chain types.Blocks, t * done <- true } +func TestExtendCanonical(t *testing.T) { + CanonicalLength := 5 + db, err := ethdb.NewMemDatabase() + if err != nil { + t.Fatal("Failed to create db:", err) + } + // make first chain starting from genesis + bman, err := newCanonical(CanonicalLength, db) + if err != nil { + t.Fatal("Could not make new canonical chain:", err) + } + f := func(td1, td2 *big.Int) { + if td2.Cmp(td1) <= 0 { + t.Error("expected chainB to have higher difficulty. Got", td2, "expected more than", td1) + } + } + // Start fork from current height (CanonicalLength) + testFork(t, bman, CanonicalLength, 1, f) + testFork(t, bman, CanonicalLength, 2, f) + testFork(t, bman, CanonicalLength, 5, f) + testFork(t, bman, CanonicalLength, 10, f) +} + +func TestShorterFork(t *testing.T) { + db, err := ethdb.NewMemDatabase() + if err != nil { + t.Fatal("Failed to create db:", err) + } + // make first chain starting from genesis + bman, err := newCanonical(10, db) + if err != nil { + t.Fatal("Could not make new canonical chain:", err) + } + f := func(td1, td2 *big.Int) { + if td2.Cmp(td1) >= 0 { + t.Error("expected chainB to have lower difficulty. Got", td2, "expected less than", td1) + } + } + // Sum of numbers must be less than 10 + // for this to be a shorter fork + testFork(t, bman, 0, 3, f) + testFork(t, bman, 0, 7, f) + testFork(t, bman, 1, 1, f) + testFork(t, bman, 1, 7, f) + testFork(t, bman, 5, 3, f) + testFork(t, bman, 5, 4, f) +} + +func TestLongerFork(t *testing.T) { + db, err := ethdb.NewMemDatabase() + if err != nil { + t.Fatal("Failed to create db:", err) + } + // make first chain starting from genesis + bman, err := newCanonical(10, db) + if err != nil { + t.Fatal("Could not make new canonical chain:", err) + } + f := func(td1, td2 *big.Int) { + if td2.Cmp(td1) <= 0 { + t.Error("expected chainB to have higher difficulty. Got", td2, "expected more than", td1) + } + } + // Sum of numbers must be greater than 10 + // for this to be a longer fork + testFork(t, bman, 0, 11, f) + testFork(t, bman, 0, 15, f) + testFork(t, bman, 1, 10, f) + testFork(t, bman, 1, 12, f) + testFork(t, bman, 5, 6, f) + testFork(t, bman, 5, 8, f) +} + +func TestEqualFork(t *testing.T) { + db, err := ethdb.NewMemDatabase() + if err != nil { + t.Fatal("Failed to create db:", err) + } + bman, err := newCanonical(10, db) + if err != nil { + t.Fatal("Could not make new canonical chain:", err) + } + f := func(td1, td2 *big.Int) { + if td2.Cmp(td1) != 0 { + t.Error("expected chainB to have equal difficulty. Got", td2, "expected ", td1) + } + } + // Sum of numbers must be equal to 10 + // for this to be an equal fork + testFork(t, bman, 0, 10, f) + testFork(t, bman, 1, 9, f) + testFork(t, bman, 2, 8, f) + testFork(t, bman, 5, 5, f) + testFork(t, bman, 6, 4, f) + testFork(t, bman, 9, 1, f) +} + +func TestBrokenChain(t *testing.T) { + db, err := ethdb.NewMemDatabase() + if err != nil { + t.Fatal("Failed to create db:", err) + } + bman, err := newCanonical(10, db) + if err != nil { + t.Fatal("Could not make new canonical chain:", err) + } + db2, err := ethdb.NewMemDatabase() + if err != nil { + t.Fatal("Failed to create db:", err) + } + bman2, err := newCanonical(10, db2) + if err != nil { + t.Fatal("Could not make new canonical chain:", err) + } + bman2.bc.SetProcessor(bman2) + parent := bman2.bc.CurrentBlock() + chainB := makeChain(bman2, parent, 5, db2, ForkSeed) + chainB = chainB[1:] + _, err = testChain(chainB, bman) + if err == nil { + t.Error("expected broken chain to return error") + } +} + func TestChainInsertions(t *testing.T) { t.Skip() // travil fails. @@ -63,9 +257,9 @@ func TestChainInsertions(t *testing.T) { } var eventMux event.TypeMux - chainMan := NewChainManager(db, &eventMux) + chainMan := NewChainManager(db, db, &eventMux) txPool := NewTxPool(&eventMux) - blockMan := NewBlockProcessor(db, txPool, chainMan, &eventMux) + blockMan := NewBlockProcessor(db, nil, txPool, chainMan, &eventMux) chainMan.SetProcessor(blockMan) const max = 2 @@ -109,9 +303,9 @@ func TestChainMultipleInsertions(t *testing.T) { } } var eventMux event.TypeMux - chainMan := NewChainManager(db, &eventMux) + chainMan := NewChainManager(db, db, &eventMux) txPool := NewTxPool(&eventMux) - blockMan := NewBlockProcessor(db, txPool, chainMan, &eventMux) + blockMan := NewBlockProcessor(db, nil, txPool, chainMan, &eventMux) chainMan.SetProcessor(blockMan) done := make(chan bool, max) for i, chain := range chains { @@ -138,7 +332,7 @@ func TestGetAncestors(t *testing.T) { db, _ := ethdb.NewMemDatabase() var eventMux event.TypeMux - chainMan := NewChainManager(db, &eventMux) + chainMan := NewChainManager(db, db, &eventMux) chain, err := loadChain("valid1", t) if err != nil { fmt.Println(err) diff --git a/core/error.go b/core/error.go index e86bacb2d..04e40646c 100644 --- a/core/error.go +++ b/core/error.go @@ -22,7 +22,7 @@ func (err *ParentErr) Error() string { } func ParentError(hash []byte) error { - return &ParentErr{Message: fmt.Sprintf("Block's parent unkown %x", hash)} + return &ParentErr{Message: fmt.Sprintf("Block's parent unknown %x", hash)} } func IsParentErr(err error) bool { @@ -87,6 +87,24 @@ func IsNonceErr(err error) bool { return ok } +type InvalidTxErr struct { + Message string +} + +func (err *InvalidTxErr) Error() string { + return err.Message +} + +func InvalidTxError(err error) *InvalidTxErr { + return &InvalidTxErr{fmt.Sprintf("%v", err)} +} + +func IsInvalidTxErr(err error) bool { + _, ok := err.(*InvalidTxErr) + + return ok +} + type OutOfGasErr struct { Message string } @@ -128,3 +146,19 @@ func IsKnownBlockErr(e error) bool { _, ok := e.(*KnownBlockError) return ok } + +type ValueTransferError struct { + message string +} + +func ValueTransferErr(str string, v ...interface{}) *ValueTransferError { + return &ValueTransferError{fmt.Sprintf(str, v...)} +} + +func (self *ValueTransferError) Error() string { + return self.message +} +func IsValueTransferErr(e error) bool { + _, ok := e.(*ValueTransferError) + return ok +} diff --git a/core/events.go b/core/events.go index 4cbbc609c..23678ef60 100644 --- a/core/events.go +++ b/core/events.go @@ -16,3 +16,13 @@ type NewMinedBlockEvent struct{ Block *types.Block } // ChainSplit is posted when a new head is detected type ChainSplitEvent struct{ Block *types.Block } + +type ChainEvent struct{ Block *types.Block } + +type ChainSideEvent struct{ Block *types.Block } + +type ChainHeadEvent struct{ Block *types.Block } + +// Mining operation events +type StartMining struct{} +type TopMining struct{} diff --git a/core/execution.go b/core/execution.go index 5e0cbd37e..4a69cce09 100644 --- a/core/execution.go +++ b/core/execution.go @@ -1,7 +1,6 @@ package core import ( - "fmt" "math/big" "time" @@ -26,7 +25,10 @@ func (self *Execution) Addr() []byte { func (self *Execution) Call(codeAddr []byte, caller vm.ContextRef) ([]byte, error) { // Retrieve the executing code - code := self.env.State().GetCode(codeAddr) + var code []byte + if self.env.State().GetStateObject(codeAddr) != nil { + code = self.env.State().GetCode(codeAddr) + } return self.exec(code, codeAddr, caller) } @@ -55,16 +57,16 @@ func (self *Execution) exec(code, contextAddr []byte, caller vm.ContextRef) (ret caller.ReturnGas(self.Gas, self.price) - return nil, fmt.Errorf("insufficient funds to transfer value. Req %v, has %v", self.value, from.Balance()) + return nil, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", self.value, from.Balance()) } snapshot := env.State().Copy() start := time.Now() ret, err = evm.Run(to, caller, code, self.value, self.Gas, self.price, self.input) + chainlogger.Debugf("vm took %v\n", time.Since(start)) if err != nil { env.State().Set(snapshot) } - chainlogger.Debugf("vm took %v\n", time.Since(start)) return } diff --git a/core/filter.go b/core/filter.go index c61c9e998..d58aa8d7c 100644 --- a/core/filter.go +++ b/core/filter.go @@ -147,7 +147,7 @@ func (self *Filter) FilterLogs(logs state.Logs) state.Logs { // Filter the logs for interesting stuff Logs: for _, log := range logs { - if !includes(self.address, log.Address()) { + if len(self.address) > 0 && !includes(self.address, log.Address()) { continue } diff --git a/core/genesis.go b/core/genesis.go index decffc541..a3f5dfb38 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -22,12 +22,17 @@ var ZeroHash512 = make([]byte, 64) var EmptyShaList = crypto.Sha3(ethutil.Encode([]interface{}{})) var EmptyListRoot = crypto.Sha3(ethutil.Encode("")) +var GenesisDiff = big.NewInt(131072) + func GenesisBlock(db ethutil.Database) *types.Block { - genesis := types.NewBlock(ZeroHash256, ZeroHash160, nil, big.NewInt(131072), crypto.Sha3(big.NewInt(42).Bytes()), "") + genesis := types.NewBlock(ZeroHash256, ZeroHash160, nil, GenesisDiff, 42, "") genesis.Header().Number = ethutil.Big0 genesis.Header().GasLimit = big.NewInt(1000000) genesis.Header().GasUsed = ethutil.Big0 genesis.Header().Time = 0 + genesis.Header().SeedHash = make([]byte, 32) + genesis.Header().MixDigest = make([]byte, 32) + genesis.Td = ethutil.Big0 genesis.SetUncles([]*types.Header{}) @@ -55,6 +60,10 @@ func GenesisBlock(db ethutil.Database) *types.Block { } var genesisData = []byte(`{ + "0000000000000000000000000000000000000001": {"balance": "1"}, + "0000000000000000000000000000000000000002": {"balance": "1"}, + "0000000000000000000000000000000000000003": {"balance": "1"}, + "0000000000000000000000000000000000000004": {"balance": "1"}, "dbdbdb2cbd23b783741e8d7fcf51e459b497e4a6": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, "e4157b34ea9615cfbde6b4fda419828124b70c78": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, "b9c015918bdaba24b4ff057a92a3873d6eb201be": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, @@ -62,9 +71,5 @@ var genesisData = []byte(`{ "cd2a3d9f938e13cd947ec05abc7fe734df8dd826": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, "2ef47100e0787b915105fd5e3f4ff6752079d5cb": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, "e6716f9544a56c530d868e4bfbacb172315bdead": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, - "1a26338f0d905e295fccb71fa9ea849ffa12aaf4": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, - "b0afc46d9ce366d06ab4952ca27db1d9557ae9fd": {"balance": "154162184000000000000000"}, - "f6b1e9dc460d4d62cc22ec5f987d726929c0f9f0": {"balance": "102774789000000000000000"}, - "cc45122d8b7fa0b1eaa6b29e0fb561422a9239d0": {"balance": "51387394000000000000000"}, - "b7576e9d314df41ec5506494293afb1bd5d3f65d": {"balance": "69423399000000000000000"} + "1a26338f0d905e295fccb71fa9ea849ffa12aaf4": {"balance": "1606938044258990275541962092341162602522202993782792835301376"} }`) diff --git a/core/manager.go b/core/manager.go index bb039d063..803069377 100644 --- a/core/manager.go +++ b/core/manager.go @@ -15,6 +15,7 @@ type Backend interface { IsListening() bool Peers() []*p2p.Peer KeyManager() *crypto.KeyManager - Db() ethutil.Database + BlockDb() ethutil.Database + StateDb() ethutil.Database EventMux() *event.TypeMux } diff --git a/core/state_transition.go b/core/state_transition.go index 7331fdd4a..f54acd6ee 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -12,6 +12,8 @@ import ( const tryJit = false +var () + /* * The State transitioning model * @@ -144,7 +146,7 @@ func (self *StateTransition) preCheck() (err error) { // Pre-pay gas / Buy gas of the coinbase account if err = self.BuyGas(); err != nil { - return err + return InvalidTxError(err) } return nil @@ -165,26 +167,26 @@ func (self *StateTransition) TransitionState() (ret []byte, err error) { defer self.RefundGas() - // Increment the nonce for the next transaction - self.state.SetNonce(sender.Address(), sender.Nonce()+1) - //sender.Nonce += 1 - // Transaction gas if err = self.UseGas(vm.GasTx); err != nil { - return + return nil, InvalidTxError(err) } + // Increment the nonce for the next transaction + self.state.SetNonce(sender.Address(), sender.Nonce()+1) + //sender.Nonce += 1 + // Pay data gas var dgas int64 for _, byt := range self.data { if byt != 0 { - dgas += vm.GasData.Int64() + dgas += vm.GasTxDataNonzeroByte.Int64() } else { - dgas += 1 // This is 1/5. If GasData changes this fails + dgas += vm.GasTxDataZeroByte.Int64() } } if err = self.UseGas(big.NewInt(dgas)); err != nil { - return + return nil, InvalidTxError(err) } //stateCopy := self.env.State().Copy() @@ -230,10 +232,16 @@ func (self *StateTransition) TransitionState() (ret []byte, err error) { */ } - if err != nil { - self.UseGas(self.gas) + if err != nil && IsValueTransferErr(err) { + return nil, InvalidTxError(err) } + /* + if err != nil { + self.UseGas(self.gas) + } + */ + return } diff --git a/core/transaction_pool.go b/core/transaction_pool.go index 050cff3d8..bd377f679 100644 --- a/core/transaction_pool.go +++ b/core/transaction_pool.go @@ -117,8 +117,13 @@ func (self *TxPool) add(tx *types.Transaction) error { } else { to = "[NEW_CONTRACT]" } - - txplogger.Debugf("(t) %x => %s (%v) %x\n", tx.From()[:4], to, tx.Value, tx.Hash()) + var from string + if len(tx.From()) > 0 { + from = ethutil.Bytes2Hex(tx.From()[:4]) + } else { + return errors.New(fmt.Sprintf("FROM ADDRESS MUST BE POSITIVE (was %v)", tx.From())) + } + txplogger.Debugf("(t) %x => %s (%v) %x\n", from, to, tx.Value, tx.Hash()) // Notify the subscribers go self.eventMux.Post(TxPreEvent{tx}) diff --git a/core/types/block.go b/core/types/block.go index d57de1311..31c7c2b87 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -2,6 +2,7 @@ package types import ( "bytes" + "encoding/binary" "fmt" "math/big" "sort" @@ -39,14 +40,33 @@ type Header struct { Time uint64 // Extra data Extra string - // Block Nonce for verification - Nonce ethutil.Bytes + // SeedHash used for light client verification + SeedHash ethutil.Bytes + // Mix digest for quick checking to prevent DOS + MixDigest ethutil.Bytes + // Nonce + Nonce []byte } func (self *Header) rlpData(withNonce bool) []interface{} { - fields := []interface{}{self.ParentHash, self.UncleHash, self.Coinbase, self.Root, self.TxHash, self.ReceiptHash, self.Bloom, self.Difficulty, self.Number, self.GasLimit, self.GasUsed, self.Time, self.Extra} + fields := []interface{}{ + self.ParentHash, + self.UncleHash, + self.Coinbase, + self.Root, + self.TxHash, + self.ReceiptHash, + self.Bloom, + self.Difficulty, + self.Number, + self.GasLimit, + self.GasUsed, + self.Time, + self.Extra, + self.SeedHash, + } if withNonce { - fields = append(fields, self.Nonce) + fields = append(fields, self.MixDigest, self.Nonce) } return fields @@ -77,24 +97,29 @@ type Block struct { Reward *big.Int } -func NewBlock(parentHash []byte, coinbase []byte, root []byte, difficulty *big.Int, nonce []byte, extra string) *Block { +func NewBlock(parentHash []byte, coinbase []byte, root []byte, difficulty *big.Int, nonce uint64, extra string) *Block { header := &Header{ Root: root, ParentHash: parentHash, Coinbase: coinbase, Difficulty: difficulty, - Nonce: nonce, Time: uint64(time.Now().Unix()), Extra: extra, GasUsed: new(big.Int), GasLimit: new(big.Int), } + header.setNonce(nonce) block := &Block{header: header, Reward: new(big.Int)} return block } +func (self *Header) setNonce(nonce uint64) { + self.Nonce = make([]byte, 8) + binary.BigEndian.PutUint64(self.Nonce, nonce) +} + func NewBlockWithHeader(header *Header) *Block { return &Block{header: header} } @@ -174,9 +199,17 @@ func (self *Block) RlpDataForStorage() interface{} { } // Header accessors (add as you need them) -func (self *Block) Number() *big.Int { return self.header.Number } -func (self *Block) NumberU64() uint64 { return self.header.Number.Uint64() } -func (self *Block) Nonce() []byte { return self.header.Nonce } +func (self *Block) Number() *big.Int { return self.header.Number } +func (self *Block) NumberU64() uint64 { return self.header.Number.Uint64() } +func (self *Block) MixDigest() []byte { return self.header.MixDigest } +func (self *Block) SeedHash() []byte { return self.header.SeedHash } +func (self *Block) Nonce() uint64 { + return binary.BigEndian.Uint64(self.header.Nonce) +} +func (self *Block) SetNonce(nonce uint64) { + self.header.setNonce(nonce) +} + func (self *Block) Bloom() []byte { return self.header.Bloom } func (self *Block) Coinbase() []byte { return self.header.Coinbase } func (self *Block) Time() int64 { return int64(self.header.Time) } @@ -200,7 +233,6 @@ func (self *Block) GetUncle(i int) *Header { // Implement pow.Block func (self *Block) Difficulty() *big.Int { return self.header.Difficulty } -func (self *Block) N() []byte { return self.header.Nonce } func (self *Block) HashNoNonce() []byte { return self.header.HashNoNonce() } func (self *Block) Hash() []byte { @@ -249,8 +281,10 @@ func (self *Header) String() string { GasUsed: %v Time: %v Extra: %v - Nonce: %x -`, self.ParentHash, self.UncleHash, self.Coinbase, self.Root, self.TxHash, self.ReceiptHash, self.Bloom, self.Difficulty, self.Number, self.GasLimit, self.GasUsed, self.Time, self.Extra, self.Nonce) + SeedHash: %x + MixDigest: %x + Nonce: %x`, + self.ParentHash, self.UncleHash, self.Coinbase, self.Root, self.TxHash, self.ReceiptHash, self.Bloom, self.Difficulty, self.Number, self.GasLimit, self.GasUsed, self.Time, self.Extra, self.SeedHash, self.MixDigest, self.Nonce) } type Blocks []*Block diff --git a/core/types/bloom9.go b/core/types/bloom9.go index c1841e553..578265a34 100644 --- a/core/types/bloom9.go +++ b/core/types/bloom9.go @@ -14,7 +14,7 @@ func CreateBloom(receipts Receipts) []byte { bin.Or(bin, LogsBloom(receipt.logs)) } - return ethutil.LeftPadBytes(bin.Bytes(), 64) + return ethutil.LeftPadBytes(bin.Bytes(), 256) } func LogsBloom(logs state.Logs) *big.Int { @@ -37,9 +37,10 @@ func LogsBloom(logs state.Logs) *big.Int { func bloom9(b []byte) *big.Int { r := new(big.Int) - for _, i := range []int{0, 2, 4} { + + for i := 0; i < 16; i += 2 { t := big.NewInt(1) - b := uint(b[i+1]) + 256*(uint(b[i])&1) + b := uint(b[i+1]) + 1024*(uint(b[i])&1) r.Or(r, t.Lsh(t, b)) } |