diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/.gitignore | 12 | ||||
-rw-r--r-- | core/asm.go | 50 | ||||
-rw-r--r-- | core/block_processor.go | 332 | ||||
-rw-r--r-- | core/block_processor_test.go | 34 | ||||
-rw-r--r-- | core/chain_manager.go | 438 | ||||
-rw-r--r-- | core/chain_manager_test.go | 154 | ||||
-rw-r--r-- | core/error.go | 130 | ||||
-rw-r--r-- | core/events.go | 18 | ||||
-rw-r--r-- | core/execution.go | 77 | ||||
-rw-r--r-- | core/fees.go | 7 | ||||
-rw-r--r-- | core/filter.go | 187 | ||||
-rw-r--r-- | core/filter_test.go | 1 | ||||
-rw-r--r-- | core/genesis.go | 70 | ||||
-rw-r--r-- | core/helper_test.go | 84 | ||||
-rw-r--r-- | core/manager.go | 20 | ||||
-rw-r--r-- | core/state_transition.go | 274 | ||||
-rw-r--r-- | core/transaction_pool.go | 201 | ||||
-rw-r--r-- | core/transaction_pool_test.go | 97 | ||||
-rw-r--r-- | core/types/block.go | 279 | ||||
-rw-r--r-- | core/types/block_test.go | 1 | ||||
-rw-r--r-- | core/types/bloom9.go | 55 | ||||
-rw-r--r-- | core/types/bloom9_test.go | 31 | ||||
-rw-r--r-- | core/types/common.go | 7 | ||||
-rw-r--r-- | core/types/derive_sha.go | 22 | ||||
-rw-r--r-- | core/types/receipt.go | 81 | ||||
-rw-r--r-- | core/types/transaction.go | 226 | ||||
-rw-r--r-- | core/types/transaction_test.go | 1 | ||||
-rw-r--r-- | core/vm_env.go | 72 |
28 files changed, 2961 insertions, 0 deletions
diff --git a/core/.gitignore b/core/.gitignore new file mode 100644 index 000000000..f725d58d1 --- /dev/null +++ b/core/.gitignore @@ -0,0 +1,12 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. +# +# If you find yourself ignoring temporary files generated by your text editor +# or operating system, you probably want to add a global ignore instead: +# git config --global core.excludesfile ~/.gitignore_global + +/tmp +*/**/*un~ +*un~ +.DS_Store +*/**/.DS_Store + diff --git a/core/asm.go b/core/asm.go new file mode 100644 index 000000000..a8b3023f4 --- /dev/null +++ b/core/asm.go @@ -0,0 +1,50 @@ +package core + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/vm" +) + +func Disassemble(script []byte) (asm []string) { + pc := new(big.Int) + for { + if pc.Cmp(big.NewInt(int64(len(script)))) >= 0 { + return + } + + // Get the memory location of pc + val := script[pc.Int64()] + // Get the opcode (it must be an opcode!) + op := vm.OpCode(val) + + asm = append(asm, fmt.Sprintf("%04v: %v", pc, op)) + + switch op { + case vm.PUSH1, vm.PUSH2, vm.PUSH3, vm.PUSH4, vm.PUSH5, vm.PUSH6, vm.PUSH7, vm.PUSH8, + vm.PUSH9, vm.PUSH10, vm.PUSH11, vm.PUSH12, vm.PUSH13, vm.PUSH14, vm.PUSH15, + vm.PUSH16, vm.PUSH17, vm.PUSH18, vm.PUSH19, vm.PUSH20, vm.PUSH21, vm.PUSH22, + vm.PUSH23, vm.PUSH24, vm.PUSH25, vm.PUSH26, vm.PUSH27, vm.PUSH28, vm.PUSH29, + vm.PUSH30, vm.PUSH31, vm.PUSH32: + pc.Add(pc, ethutil.Big1) + a := int64(op) - int64(vm.PUSH1) + 1 + if int(pc.Int64()+a) > len(script) { + return + } + + data := script[pc.Int64() : pc.Int64()+a] + if len(data) == 0 { + data = []byte{0} + } + asm = append(asm, fmt.Sprintf("%04v: 0x%x", pc, data)) + + pc.Add(pc, big.NewInt(a-1)) + } + + pc.Add(pc, ethutil.Big1) + } + + return asm +} diff --git a/core/block_processor.go b/core/block_processor.go new file mode 100644 index 000000000..5e943bda5 --- /dev/null +++ b/core/block_processor.go @@ -0,0 +1,332 @@ +package core + +import ( + "bytes" + "fmt" + "math/big" + "sync" + "time" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethutil" + "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" +) + +type PendingBlockEvent struct { + Block *types.Block +} + +var statelogger = logger.NewLogger("BLOCK") + +type BlockProcessor struct { + db ethutil.Database + // Mutex for locking the block processor. Blocks can only be handled one at a time + mutex sync.Mutex + // Canonical block chain + bc *ChainManager + // non-persistent key/value memory storage + mem map[string]*big.Int + // Proof of work used for validating + Pow pow.PoW + + txpool *TxPool + + // The last attempted block is mainly used for debugging purposes + // This does not have to be a valid block and will be set during + // 'Process' & canonical validation. + lastAttemptedBlock *types.Block + + events event.Subscription + + eventMux *event.TypeMux +} + +func NewBlockProcessor(db ethutil.Database, txpool *TxPool, chainManager *ChainManager, eventMux *event.TypeMux) *BlockProcessor { + sm := &BlockProcessor{ + db: db, + mem: make(map[string]*big.Int), + Pow: ezp.New(), + bc: chainManager, + eventMux: eventMux, + txpool: txpool, + } + + return sm +} + +func (sm *BlockProcessor) TransitionState(statedb *state.StateDB, parent, block *types.Block, transientProcess bool) (receipts types.Receipts, err error) { + coinbase := statedb.GetOrNewStateObject(block.Header().Coinbase) + coinbase.SetGasPool(block.Header().GasLimit) + + // Process the transactions on to parent state + receipts, _, _, _, err = sm.ApplyTransactions(coinbase, statedb, block, block.Transactions(), transientProcess) + if err != nil { + return nil, err + } + + return receipts, nil +} + +func (self *BlockProcessor) ApplyTransaction(coinbase *state.StateObject, statedb *state.StateDB, block *types.Block, tx *types.Transaction, usedGas *big.Int, transientProcess bool) (*types.Receipt, *big.Int, error) { + // If we are mining this block and validating we want to set the logs back to 0 + statedb.EmptyLogs() + + txGas := new(big.Int).Set(tx.Gas()) + + cb := statedb.GetStateObject(coinbase.Address()) + st := NewStateTransition(NewEnv(statedb, self.bc, tx, block), tx, cb) + _, err := st.TransitionState() + if err != nil && (IsNonceErr(err) || state.IsGasLimitErr(err)) { + return nil, nil, err + } + + txGas.Sub(txGas, st.gas) + + // Update the state with pending changes + statedb.Update(txGas) + + cumulative := new(big.Int).Set(usedGas.Add(usedGas, txGas)) + receipt := types.NewReceipt(statedb.Root(), cumulative) + receipt.SetLogs(statedb.Logs()) + receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) + chainlogger.Debugln(receipt) + + // Notify all subscribers + if !transientProcess { + go self.eventMux.Post(TxPostEvent{tx}) + logs := statedb.Logs() + go self.eventMux.Post(logs) + } + + return receipt, txGas, err +} + +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 ( + receipts types.Receipts + handled, unhandled types.Transactions + erroneous types.Transactions + totalUsedGas = big.NewInt(0) + err error + cumulativeSum = new(big.Int) + ) + + for _, tx := range txs { + receipt, txGas, err := self.ApplyTransaction(coinbase, statedb, block, tx, totalUsedGas, transientProcess) + if err != nil { + switch { + case IsNonceErr(err): + return nil, nil, nil, nil, err + case state.IsGasLimitErr(err): + return nil, nil, nil, nil, err + default: + statelogger.Infoln(err) + erroneous = append(erroneous, tx) + err = nil + } + } + receipts = append(receipts, receipt) + handled = append(handled, tx) + + cumulativeSum.Add(cumulativeSum, new(big.Int).Mul(txGas, tx.GasPrice())) + } + + block.Reward = cumulativeSum + block.Header().GasUsed = totalUsedGas + + if transientProcess { + go self.eventMux.Post(PendingBlockEvent{block}) + } + + return receipts, handled, unhandled, erroneous, err +} + +// Process block will attempt to process the given block's transactions and applies them +// on top of the block's parent state (given it exists) and will return wether it was +// successful or not. +func (sm *BlockProcessor) Process(block *types.Block) (td *big.Int, err error) { + // Processing a blocks may never happen simultaneously + sm.mutex.Lock() + defer sm.mutex.Unlock() + + header := block.Header() + if sm.bc.HasBlock(header.Hash()) { + return nil, &KnownBlockError{header.Number, header.Hash()} + } + + if !sm.bc.HasBlock(header.ParentHash) { + return nil, ParentError(header.ParentHash) + } + parent := sm.bc.GetBlock(header.ParentHash) + + return sm.processWithParent(block, parent) +} + +func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (td *big.Int, err error) { + sm.lastAttemptedBlock = block + + // Create a new state based on the parent's root (e.g., create copy) + state := state.New(parent.Root(), sm.db) + + // Block validation + if err = sm.ValidateBlock(block, parent); err != nil { + return + } + + receipts, err := sm.TransitionState(state, parent, block, false) + if err != nil { + return + } + + header := block.Header() + + // Validate the received block's bloom with the one derived from the generated receipts. + // For valid blocks this should always validate to true. + rbloom := types.CreateBloom(receipts) + if bytes.Compare(rbloom, header.Bloom) != 0 { + err = fmt.Errorf("unable to replicate block's bloom=%x", rbloom) + return + } + + // The transactions Trie's root (R = (Tr [[H1, T1], [H2, T2], ... [Hn, Tn]])) + // can be used by light clients to make sure they've received the correct Txs + txSha := types.DeriveSha(block.Transactions()) + if bytes.Compare(txSha, header.TxHash) != 0 { + err = fmt.Errorf("validating transaction root. received=%x got=%x", header.TxHash, txSha) + return + } + + // 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) + state.Sync() + chainlogger.Infof("%s\n", state.Dump()) + err = fmt.Errorf("validating receipt root. received=%x got=%x", header.ReceiptHash, receiptSha) + return + } + + // Accumulate static rewards; block reward, uncle's and uncle inclusion. + if err = sm.AccumulateRewards(state, block, parent); err != nil { + return + } + + // Commit state objects/accounts to a temporary trie (does not save) + // used to calculate the state root. + state.Update(ethutil.Big0) + if !bytes.Equal(header.Root, state.Root()) { + err = fmt.Errorf("invalid merkle root. received=%x got=%x", header.Root, state.Root()) + return + } + + // Calculate the td for this block + td = CalculateTD(block, parent) + // Sync the current block's state to the database + state.Sync() + // Remove transactions from the pool + sm.txpool.RemoveSet(block.Transactions()) + + chainlogger.Infof("processed block #%d (%x...)\n", header.Number, block.Hash()[0:4]) + + return td, nil +} + +// 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)) + } + + 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) + } + + 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) + } + + if block.Time() < parent.Time() { + return ValidationError("Block timestamp not after prev block (%v - %v)", block.Header().Time, parent.Header().Time) + } + + if block.Time() > time.Now().Unix() { + return BlockFutureErr + } + + 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)) + } + + return nil +} + +func (sm *BlockProcessor) AccumulateRewards(statedb *state.StateDB, block, parent *types.Block) error { + reward := new(big.Int).Set(BlockReward) + + ancestors := set.New() + for _, ancestor := range sm.bc.GetAncestors(block, 7) { + ancestors.Add(string(ancestor.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.ParentHash)) { + return UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.ParentHash[0:4])) + } + + if !sm.Pow.Verify(types.NewBlockWithHeader(uncle)) { + return ValidationError("Uncle's nonce is invalid (= %v)", ethutil.Bytes2Hex(uncle.Nonce)) + } + + r := new(big.Int) + r.Mul(BlockReward, big.NewInt(15)).Div(r, big.NewInt(16)) + + statedb.AddBalance(uncle.Coinbase, r) + + reward.Add(reward, new(big.Int).Div(BlockReward, big.NewInt(32))) + } + + // Get the account associated with the coinbase + statedb.AddBalance(block.Header().Coinbase, reward) + + return nil +} + +func (sm *BlockProcessor) GetLogs(block *types.Block) (logs state.Logs, err error) { + if !sm.bc.HasBlock(block.Header().ParentHash) { + return nil, ParentError(block.Header().ParentHash) + } + + sm.lastAttemptedBlock = block + + var ( + parent = sm.bc.GetBlock(block.Header().ParentHash) + state = state.New(parent.Root(), sm.db) + ) + + sm.TransitionState(state, parent, block, true) + sm.AccumulateRewards(state, block, parent) + + return state.Logs(), nil +} diff --git a/core/block_processor_test.go b/core/block_processor_test.go new file mode 100644 index 000000000..35aeaa714 --- /dev/null +++ b/core/block_processor_test.go @@ -0,0 +1,34 @@ +package core + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" +) + +func proc() (*BlockProcessor, *ChainManager) { + db, _ := ethdb.NewMemDatabase() + var mux event.TypeMux + + chainMan := NewChainManager(db, &mux) + return NewBlockProcessor(db, nil, chainMan, &mux), chainMan +} + +func TestNumber(t *testing.T) { + bp, chain := proc() + block1 := chain.NewBlock(nil) + block1.Header().Number = big.NewInt(3) + + err := bp.ValidateBlock(block1, chain.Genesis()) + if err != BlockNumberErr { + t.Errorf("expected block number error") + } + + block1 = chain.NewBlock(nil) + err = bp.ValidateBlock(block1, chain.Genesis()) + if err == BlockNumberErr { + t.Errorf("didn't expect block number error") + } +} diff --git a/core/chain_manager.go b/core/chain_manager.go new file mode 100644 index 000000000..2f6c36382 --- /dev/null +++ b/core/chain_manager.go @@ -0,0 +1,438 @@ +package core + +import ( + "bytes" + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/state" +) + +var chainlogger = logger.NewLogger("CHAIN") + +type ChainEvent struct { + Block *types.Block + Td *big.Int +} + +type StateQuery interface { + GetAccount(addr []byte) *state.StateObject +} + +func CalcDifficulty(block, parent *types.Block) *big.Int { + diff := new(big.Int) + + //adjust := new(big.Int).Rsh(parent.Difficulty(), 10) + //if block.Time() >= parent.Time()+8 { + adjust := new(big.Int).Div(parent.Difficulty(), big.NewInt(2048)) + if (block.Time() - parent.Time()) < 8 { + diff.Add(parent.Difficulty(), adjust) + } else { + diff.Sub(parent.Difficulty(), adjust) + } + + return diff +} + +func CalculateTD(block, parent *types.Block) *big.Int { + uncleDiff := new(big.Int) + for _, uncle := range block.Uncles() { + uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty) + } + + // TD(genesis_block) = 0 and TD(B) = TD(B.parent) + sum(u.difficulty for u in B.uncles) + B.difficulty + td := new(big.Int) + td = td.Add(parent.Td, uncleDiff) + td = td.Add(td, block.Header().Difficulty) + + return td +} + +func CalcGasLimit(parent, block *types.Block) *big.Int { + if block.Number().Cmp(big.NewInt(0)) == 0 { + return ethutil.BigPow(10, 6) + } + + // ((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()) + + result := new(big.Int).Add(previous, curInt) + result.Div(result, big.NewInt(1024)) + + min := big.NewInt(125000) + + return ethutil.BigMax(min, result) +} + +type ChainManager struct { + //eth EthManager + db ethutil.Database + processor types.BlockProcessor + eventMux *event.TypeMux + genesisBlock *types.Block + // Last known total difficulty + mu sync.RWMutex + tsmu sync.RWMutex + td *big.Int + currentBlock *types.Block + lastBlockHash []byte + + transState *state.StateDB + txState *state.StateDB +} + +func NewChainManager(db ethutil.Database, mux *event.TypeMux) *ChainManager { + bc := &ChainManager{db: db, genesisBlock: GenesisBlock(db), eventMux: mux} + bc.setLastBlock() + bc.transState = bc.State().Copy() + bc.txState = bc.State().Copy() + + return bc +} + +func (self *ChainManager) Td() *big.Int { + self.mu.RLock() + defer self.mu.RUnlock() + + return self.td +} + +func (self *ChainManager) LastBlockHash() []byte { + self.mu.RLock() + defer self.mu.RUnlock() + + return self.lastBlockHash +} + +func (self *ChainManager) CurrentBlock() *types.Block { + self.mu.RLock() + defer self.mu.RUnlock() + + return self.currentBlock +} + +func (self *ChainManager) Status() (td *big.Int, currentBlock []byte, genesisBlock []byte) { + self.mu.RLock() + defer self.mu.RUnlock() + + return self.td, self.currentBlock.Hash(), self.Genesis().Hash() +} + +func (self *ChainManager) SetProcessor(proc types.BlockProcessor) { + self.processor = proc +} + +func (self *ChainManager) State() *state.StateDB { + return state.New(self.CurrentBlock().Root(), self.db) +} + +func (self *ChainManager) TransState() *state.StateDB { + self.tsmu.RLock() + defer self.tsmu.RUnlock() + + return self.transState +} + +func (self *ChainManager) TxState() *state.StateDB { + self.tsmu.RLock() + defer self.tsmu.RUnlock() + + return self.txState +} + +func (self *ChainManager) setTxState(state *state.StateDB) { + self.tsmu.Lock() + defer self.tsmu.Unlock() + self.txState = state +} + +func (self *ChainManager) setTransState(statedb *state.StateDB) { + self.transState = statedb +} + +func (bc *ChainManager) setLastBlock() { + data, _ := bc.db.Get([]byte("LastBlock")) + if len(data) != 0 { + var block types.Block + rlp.Decode(bytes.NewReader(data), &block) + bc.currentBlock = &block + bc.lastBlockHash = block.Hash() + + // Set the last know difficulty (might be 0x0 as initial value, Genesis) + bc.td = ethutil.BigD(bc.db.LastKnownTD()) + } else { + bc.Reset() + } + + chainlogger.Infof("Last block (#%v) %x TD=%v\n", bc.currentBlock.Number(), bc.currentBlock.Hash(), bc.td) +} + +// Block creation & chain handling +func (bc *ChainManager) NewBlock(coinbase []byte) *types.Block { + bc.mu.RLock() + defer bc.mu.RUnlock() + + var root []byte + parentHash := ZeroHash256 + + if bc.currentBlock != nil { + root = bc.currentBlock.Header().Root + parentHash = bc.lastBlockHash + } + + block := types.NewBlock( + parentHash, + coinbase, + root, + ethutil.BigPow(2, 32), + nil, + "") + block.SetUncles(nil) + block.SetTransactions(nil) + block.SetReceipts(nil) + + parent := bc.currentBlock + if parent != nil { + header := block.Header() + header.Difficulty = CalcDifficulty(block, parent) + header.Number = new(big.Int).Add(parent.Header().Number, ethutil.Big1) + header.GasLimit = CalcGasLimit(parent, block) + + } + + return block +} + +func (bc *ChainManager) Reset() { + bc.mu.Lock() + defer bc.mu.Unlock() + + for block := bc.currentBlock; block != nil; block = bc.GetBlock(block.Header().ParentHash) { + bc.db.Delete(block.Hash()) + } + + // Prepare the genesis block + bc.write(bc.genesisBlock) + bc.insert(bc.genesisBlock) + bc.currentBlock = bc.genesisBlock + + bc.setTotalDifficulty(ethutil.Big("0")) +} + +func (self *ChainManager) Export() []byte { + 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 + } + + return ethutil.Encode(blocks) +} + +func (bc *ChainManager) insert(block *types.Block) { + encodedBlock := ethutil.Encode(block) + bc.db.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) +} + +// Accessors +func (bc *ChainManager) Genesis() *types.Block { + return bc.genesisBlock +} + +// Block fetching methods +func (bc *ChainManager) HasBlock(hash []byte) bool { + data, _ := bc.db.Get(hash) + return len(data) != 0 +} + +func (self *ChainManager) GetBlockHashesFromHash(hash []byte, max uint64) (chain [][]byte) { + block := self.GetBlock(hash) + 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 + block = self.GetBlock(parentHash) + if block == nil { + chainlogger.Infof("GetBlockHashesFromHash Parent UNKNOWN %x\n", parentHash) + break + } + + chain = append(chain, block.Hash()) + if block.Header().Number.Cmp(ethutil.Big0) <= 0 { + break + } + } + + return +} + +func (self *ChainManager) GetBlock(hash []byte) *types.Block { + data, _ := self.db.Get(hash) + if len(data) == 0 { + return nil + } + var block types.Block + if err := rlp.Decode(bytes.NewReader(data), &block); err != nil { + fmt.Println(err) + return nil + } + + return &block +} + +func (self *ChainManager) GetUnclesInChain(block *types.Block, length int) (uncles []*types.Header) { + for i := 0; block != nil && i < length; i++ { + uncles = append(uncles, block.Uncles()...) + block = self.GetBlock(block.ParentHash()) + } + + return +} + +func (self *ChainManager) GetAncestors(block *types.Block, length int) (blocks []*types.Block) { + for i := 0; i < length; i++ { + block = self.GetBlock(block.ParentHash()) + if block == nil { + break + } + + blocks = append(blocks, block) + } + + return +} + +func (self *ChainManager) GetBlockByNumber(num uint64) *types.Block { + self.mu.RLock() + defer self.mu.RUnlock() + + var block *types.Block + + if num <= self.currentBlock.Number().Uint64() { + block = self.currentBlock + for ; block != nil; block = self.GetBlock(block.Header().ParentHash) { + if block.Header().Number.Uint64() == num { + break + } + } + } + + return block +} + +func (bc *ChainManager) setTotalDifficulty(td *big.Int) { + bc.db.Put([]byte("LTD"), td.Bytes()) + bc.td = td +} + +func (self *ChainManager) CalcTotalDiff(block *types.Block) (*big.Int, error) { + parent := self.GetBlock(block.Header().ParentHash) + if parent == nil { + return nil, fmt.Errorf("Unable to calculate total diff without known parent %x", block.Header().ParentHash) + } + + parentTd := parent.Td + + uncleDiff := new(big.Int) + for _, uncle := range block.Uncles() { + uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty) + } + + td := new(big.Int) + td = td.Add(parentTd, uncleDiff) + td = td.Add(td, block.Header().Difficulty) + + return td, nil +} + +func (bc *ChainManager) Stop() { + if bc.CurrentBlock != nil { + chainlogger.Infoln("Stopped") + } +} + +func (self *ChainManager) InsertChain(chain types.Blocks) error { + self.tsmu.Lock() + defer self.tsmu.Unlock() + + for _, 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) + if err != nil { + if IsKnownBlockErr(err) { + continue + } + + h := block.Header() + chainlogger.Infof("block #%v process failed (%x)\n", h.Number, h.Hash()[:4]) + chainlogger.Infoln(block) + chainlogger.Infoln(err) + return err + } + block.Td = td + + var canonical, split bool + self.mu.Lock() + { + // Write block to database. Eventually we'll have to improve on this and throw away blocks that are + // not in the canonical chain. + self.write(block) + cblock := self.currentBlock + // Compare the TD of the last known block in the canonical chain to make sure it's greater. + // At this point it's possible that a different chain (fork) becomes the new canonical chain. + 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 + } + + self.setTotalDifficulty(td) + self.insert(block) + + canonical = true + } + } + self.mu.Unlock() + + if canonical { + 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}) + } + } + + return nil +} + +// 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 new file mode 100644 index 000000000..bc3a264d1 --- /dev/null +++ b/core/chain_manager_test.go @@ -0,0 +1,154 @@ +package core + +import ( + "bytes" + "fmt" + "os" + "path" + "runtime" + "strconv" + "testing" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/rlp" +) + +func init() { + runtime.GOMAXPROCS(runtime.NumCPU()) + ethutil.ReadConfig("/tmp/ethtest", "/tmp/ethtest", "ETH") +} + +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 { + return nil, err + } + defer fh.Close() + + var chain types.Blocks + if err := rlp.Decode(fh, &chain); err != nil { + return nil, err + } + + return chain, nil +} + +func insertChain(done chan bool, chainMan *ChainManager, chain types.Blocks, t *testing.T) { + err := chainMan.InsertChain(chain) + if err != nil { + fmt.Println(err) + t.FailNow() + } + done <- true +} + +func TestChainInsertions(t *testing.T) { + t.Skip() // travil fails. + + db, _ := ethdb.NewMemDatabase() + + chain1, err := loadChain("valid1", t) + if err != nil { + fmt.Println(err) + t.FailNow() + } + + chain2, err := loadChain("valid2", t) + if err != nil { + fmt.Println(err) + t.FailNow() + } + + var eventMux event.TypeMux + chainMan := NewChainManager(db, &eventMux) + txPool := NewTxPool(&eventMux) + blockMan := NewBlockProcessor(db, txPool, chainMan, &eventMux) + chainMan.SetProcessor(blockMan) + + const max = 2 + done := make(chan bool, max) + + go insertChain(done, chainMan, chain1, t) + go insertChain(done, chainMan, chain2, t) + + for i := 0; i < max; i++ { + <-done + } + + if bytes.Equal(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()) { + t.Error("chain1 isn't canonical and should be") + } +} + +func TestChainMultipleInsertions(t *testing.T) { + t.Skip() // travil fails. + + db, _ := ethdb.NewMemDatabase() + + const max = 4 + chains := make([]types.Blocks, max) + var longest int + for i := 0; i < max; i++ { + var err error + name := "valid" + strconv.Itoa(i+1) + chains[i], err = loadChain(name, t) + if len(chains[i]) >= len(chains[longest]) { + longest = i + } + fmt.Println("loaded", name, "with a length of", len(chains[i])) + if err != nil { + fmt.Println(err) + t.FailNow() + } + } + var eventMux event.TypeMux + chainMan := NewChainManager(db, &eventMux) + txPool := NewTxPool(&eventMux) + blockMan := NewBlockProcessor(db, txPool, chainMan, &eventMux) + chainMan.SetProcessor(blockMan) + done := make(chan bool, max) + for i, chain := range chains { + // XXX the go routine would otherwise reference the same (chain[3]) variable and fail + i := i + chain := chain + go func() { + insertChain(done, chainMan, chain, t) + fmt.Println(i, "done") + }() + } + + for i := 0; i < max; i++ { + <-done + } + + if !bytes.Equal(chains[longest][len(chains[longest])-1].Hash(), chainMan.CurrentBlock().Hash()) { + t.Error("Invalid canonical chain") + } +} + +func TestGetAncestors(t *testing.T) { + t.Skip() // travil fails. + + db, _ := ethdb.NewMemDatabase() + var eventMux event.TypeMux + chainMan := NewChainManager(db, &eventMux) + chain, err := loadChain("valid1", t) + if err != nil { + fmt.Println(err) + t.FailNow() + } + + for _, block := range chain { + chainMan.write(block) + } + + ancestors := chainMan.GetAncestors(chain[len(chain)-1], 4) + fmt.Println(ancestors) +} diff --git a/core/error.go b/core/error.go new file mode 100644 index 000000000..e86bacb2d --- /dev/null +++ b/core/error.go @@ -0,0 +1,130 @@ +package core + +import ( + "errors" + "fmt" + "math/big" +) + +var ( + BlockNumberErr = errors.New("block number invalid") + BlockFutureErr = errors.New("block time is in the future") +) + +// Parent error. In case a parent is unknown this error will be thrown +// by the block manager +type ParentErr struct { + Message string +} + +func (err *ParentErr) Error() string { + return err.Message +} + +func ParentError(hash []byte) error { + return &ParentErr{Message: fmt.Sprintf("Block's parent unkown %x", hash)} +} + +func IsParentErr(err error) bool { + _, ok := err.(*ParentErr) + + return ok +} + +type UncleErr struct { + Message string +} + +func (err *UncleErr) Error() string { + return err.Message +} + +func UncleError(str string) error { + return &UncleErr{Message: str} +} + +func IsUncleErr(err error) bool { + _, ok := err.(*UncleErr) + + return ok +} + +// Block validation error. If any validation fails, this error will be thrown +type ValidationErr struct { + Message string +} + +func (err *ValidationErr) Error() string { + return err.Message +} + +func ValidationError(format string, v ...interface{}) *ValidationErr { + return &ValidationErr{Message: fmt.Sprintf(format, v...)} +} + +func IsValidationErr(err error) bool { + _, ok := err.(*ValidationErr) + + return ok +} + +type NonceErr struct { + Message string + Is, Exp uint64 +} + +func (err *NonceErr) Error() string { + return err.Message +} + +func NonceError(is, exp uint64) *NonceErr { + return &NonceErr{Message: fmt.Sprintf("Nonce err. Is %d, expected %d", is, exp), Is: is, Exp: exp} +} + +func IsNonceErr(err error) bool { + _, ok := err.(*NonceErr) + + return ok +} + +type OutOfGasErr struct { + Message string +} + +func OutOfGasError() *OutOfGasErr { + return &OutOfGasErr{Message: "Out of gas"} +} +func (self *OutOfGasErr) Error() string { + return self.Message +} + +func IsOutOfGasErr(err error) bool { + _, ok := err.(*OutOfGasErr) + + return ok +} + +type TDError struct { + a, b *big.Int +} + +func (self *TDError) Error() string { + return fmt.Sprintf("incoming chain has a lower or equal TD (%v <= %v)", self.a, self.b) +} +func IsTDError(e error) bool { + _, ok := e.(*TDError) + return ok +} + +type KnownBlockError struct { + number *big.Int + hash []byte +} + +func (self *KnownBlockError) Error() string { + return fmt.Sprintf("block %v already known (%x)", self.number, self.hash[0:4]) +} +func IsKnownBlockErr(e error) bool { + _, ok := e.(*KnownBlockError) + return ok +} diff --git a/core/events.go b/core/events.go new file mode 100644 index 000000000..4cbbc609c --- /dev/null +++ b/core/events.go @@ -0,0 +1,18 @@ +package core + +import "github.com/ethereum/go-ethereum/core/types" + +// TxPreEvent is posted when a transaction enters the transaction pool. +type TxPreEvent struct{ Tx *types.Transaction } + +// TxPostEvent is posted when a transaction has been processed. +type TxPostEvent struct{ Tx *types.Transaction } + +// NewBlockEvent is posted when a block has been imported. +type NewBlockEvent struct{ Block *types.Block } + +// NewMinedBlockEvent is posted when a block has been imported. +type NewMinedBlockEvent struct{ Block *types.Block } + +// ChainSplit is posted when a new head is detected +type ChainSplitEvent struct{ Block *types.Block } diff --git a/core/execution.go b/core/execution.go new file mode 100644 index 000000000..f7d5a8945 --- /dev/null +++ b/core/execution.go @@ -0,0 +1,77 @@ +package core + +import ( + "fmt" + "math/big" + "time" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/state" + "github.com/ethereum/go-ethereum/vm" +) + +type Execution struct { + env vm.Environment + address, input []byte + Gas, price, value *big.Int +} + +func NewExecution(env vm.Environment, address, input []byte, gas, gasPrice, value *big.Int) *Execution { + return &Execution{env: env, address: address, input: input, Gas: gas, price: gasPrice, value: value} +} + +func (self *Execution) Addr() []byte { + return self.address +} + +func (self *Execution) Call(codeAddr []byte, caller vm.ContextRef) ([]byte, error) { + // Retrieve the executing code + code := self.env.State().GetCode(codeAddr) + + return self.exec(code, codeAddr, caller) +} + +func (self *Execution) exec(code, contextAddr []byte, caller vm.ContextRef) (ret []byte, err error) { + env := self.env + evm := vm.NewVm(env) + if env.Depth() == vm.MaxCallDepth { + caller.ReturnGas(self.Gas, self.price) + + return nil, vm.DepthError{} + } + + vsnapshot := env.State().Copy() + if len(self.address) == 0 { + // Generate a new address + nonce := env.State().GetNonce(caller.Address()) + self.address = crypto.CreateAddress(caller.Address(), nonce) + env.State().SetNonce(caller.Address(), nonce+1) + } + + from, to := env.State().GetStateObject(caller.Address()), env.State().GetOrNewStateObject(self.address) + err = env.Transfer(from, to, self.value) + if err != nil { + env.State().Set(vsnapshot) + + caller.ReturnGas(self.Gas, self.price) + + return nil, fmt.Errorf("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) + } + + return +} + +func (self *Execution) Create(caller vm.ContextRef) (ret []byte, err error, account *state.StateObject) { + ret, err = self.exec(self.input, nil, caller) + account = self.env.State().GetStateObject(self.address) + + return +} diff --git a/core/fees.go b/core/fees.go new file mode 100644 index 000000000..bbce01b84 --- /dev/null +++ b/core/fees.go @@ -0,0 +1,7 @@ +package core + +import ( + "math/big" +) + +var BlockReward *big.Int = big.NewInt(1.5e+18) diff --git a/core/filter.go b/core/filter.go new file mode 100644 index 000000000..cdf7b282d --- /dev/null +++ b/core/filter.go @@ -0,0 +1,187 @@ +package core + +import ( + "bytes" + "math" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/state" +) + +type AccountChange struct { + Address, StateAddress []byte +} + +type FilterOptions struct { + Earliest int64 + Latest int64 + + Address [][]byte + Topics [][]byte + + Skip int + Max int +} + +// Filtering interface +type Filter struct { + eth Backend + earliest int64 + latest int64 + skip int + address [][]byte + max int + topics [][]byte + + BlockCallback func(*types.Block) + PendingCallback func(*types.Block) + LogsCallback func(state.Logs) +} + +// Create a new filter which uses a bloom filter on blocks to figure out whether a particular block +// is interesting or not. +func NewFilter(eth Backend) *Filter { + return &Filter{eth: eth} +} + +func (self *Filter) SetOptions(options FilterOptions) { + self.earliest = options.Earliest + self.latest = options.Latest + self.skip = options.Skip + self.max = options.Max + self.address = options.Address + self.topics = options.Topics + +} + +// Set the earliest and latest block for filtering. +// -1 = latest block (i.e., the current block) +// hash = particular hash from-to +func (self *Filter) SetEarliestBlock(earliest int64) { + self.earliest = earliest +} + +func (self *Filter) SetLatestBlock(latest int64) { + self.latest = latest +} + +func (self *Filter) SetAddress(addr [][]byte) { + self.address = addr +} + +func (self *Filter) SetTopics(topics [][]byte) { + self.topics = topics +} + +func (self *Filter) SetMax(max int) { + self.max = max +} + +func (self *Filter) SetSkip(skip int) { + self.skip = skip +} + +// Run filters logs with the current parameters set +func (self *Filter) Find() state.Logs { + earliestBlock := self.eth.ChainManager().CurrentBlock() + var earliestBlockNo uint64 = uint64(self.earliest) + if self.earliest == -1 { + earliestBlockNo = earliestBlock.NumberU64() + } + var latestBlockNo uint64 = uint64(self.latest) + if self.latest == -1 { + latestBlockNo = earliestBlock.NumberU64() + } + + var ( + logs state.Logs + block = self.eth.ChainManager().GetBlockByNumber(latestBlockNo) + quit bool + ) + for i := 0; !quit && block != nil; i++ { + // Quit on latest + switch { + case block.NumberU64() == earliestBlockNo, block.NumberU64() == 0: + quit = true + case self.max <= len(logs): + break + } + + // Use bloom filtering to see if this block is interesting given the + // current parameters + if self.bloomFilter(block) { + // Get the logs of the block + unfiltered, err := self.eth.BlockProcessor().GetLogs(block) + if err != nil { + chainlogger.Warnln("err: filter get logs ", err) + + break + } + + logs = append(logs, self.FilterLogs(unfiltered)...) + } + + block = self.eth.ChainManager().GetBlock(block.ParentHash()) + } + + skip := int(math.Min(float64(len(logs)), float64(self.skip))) + + return logs[skip:] +} + +func includes(addresses [][]byte, a []byte) bool { + for _, addr := range addresses { + if !bytes.Equal(addr, a) { + return false + } + } + + return true +} + +func (self *Filter) FilterLogs(logs state.Logs) state.Logs { + var ret state.Logs + + // Filter the logs for interesting stuff +Logs: + for _, log := range logs { + if !includes(self.address, log.Address()) { + continue + } + + max := int(math.Min(float64(len(self.topics)), float64(len(log.Topics())))) + for i := 0; i < max; i++ { + if !bytes.Equal(log.Topics()[i], self.topics[i]) { + continue Logs + } + } + + ret = append(ret, log) + } + + return ret +} + +func (self *Filter) bloomFilter(block *types.Block) bool { + if len(self.address) > 0 { + var included bool + for _, addr := range self.address { + if types.BloomLookup(block.Bloom(), addr) { + included = true + break + } + } + + if !included { + return false + } + } + + for _, topic := range self.topics { + if !types.BloomLookup(block.Bloom(), topic) { + return false + } + } + + return true +} diff --git a/core/filter_test.go b/core/filter_test.go new file mode 100644 index 000000000..9a8bc9592 --- /dev/null +++ b/core/filter_test.go @@ -0,0 +1 @@ +package core diff --git a/core/genesis.go b/core/genesis.go new file mode 100644 index 000000000..decffc541 --- /dev/null +++ b/core/genesis.go @@ -0,0 +1,70 @@ +package core + +import ( + "encoding/json" + "fmt" + "math/big" + "os" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/state" +) + +/* + * This is the special genesis block. + */ + +var ZeroHash256 = make([]byte, 32) +var ZeroHash160 = make([]byte, 20) +var ZeroHash512 = make([]byte, 64) +var EmptyShaList = crypto.Sha3(ethutil.Encode([]interface{}{})) +var EmptyListRoot = crypto.Sha3(ethutil.Encode("")) + +func GenesisBlock(db ethutil.Database) *types.Block { + genesis := types.NewBlock(ZeroHash256, ZeroHash160, nil, big.NewInt(131072), crypto.Sha3(big.NewInt(42).Bytes()), "") + genesis.Header().Number = ethutil.Big0 + genesis.Header().GasLimit = big.NewInt(1000000) + genesis.Header().GasUsed = ethutil.Big0 + genesis.Header().Time = 0 + genesis.Td = ethutil.Big0 + + genesis.SetUncles([]*types.Header{}) + genesis.SetTransactions(types.Transactions{}) + genesis.SetReceipts(types.Receipts{}) + + var accounts map[string]struct{ Balance string } + err := json.Unmarshal(genesisData, &accounts) + if err != nil { + fmt.Println("enable to decode genesis json data:", err) + os.Exit(1) + } + + statedb := state.New(genesis.Root(), db) + for addr, account := range accounts { + codedAddr := ethutil.Hex2Bytes(addr) + accountState := statedb.GetAccount(codedAddr) + accountState.SetBalance(ethutil.Big(account.Balance)) + statedb.UpdateStateObject(accountState) + } + statedb.Sync() + genesis.Header().Root = statedb.Root() + + return genesis +} + +var genesisData = []byte(`{ + "dbdbdb2cbd23b783741e8d7fcf51e459b497e4a6": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, + "e4157b34ea9615cfbde6b4fda419828124b70c78": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, + "b9c015918bdaba24b4ff057a92a3873d6eb201be": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, + "6c386a4b26f73c802f34673f7248bb118f97424a": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, + "cd2a3d9f938e13cd947ec05abc7fe734df8dd826": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, + "2ef47100e0787b915105fd5e3f4ff6752079d5cb": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, + "e6716f9544a56c530d868e4bfbacb172315bdead": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, + "1a26338f0d905e295fccb71fa9ea849ffa12aaf4": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, + "b0afc46d9ce366d06ab4952ca27db1d9557ae9fd": {"balance": "154162184000000000000000"}, + "f6b1e9dc460d4d62cc22ec5f987d726929c0f9f0": {"balance": "102774789000000000000000"}, + "cc45122d8b7fa0b1eaa6b29e0fb561422a9239d0": {"balance": "51387394000000000000000"}, + "b7576e9d314df41ec5506494293afb1bd5d3f65d": {"balance": "69423399000000000000000"} +}`) diff --git a/core/helper_test.go b/core/helper_test.go new file mode 100644 index 000000000..473576e3f --- /dev/null +++ b/core/helper_test.go @@ -0,0 +1,84 @@ +package core + +import ( + "container/list" + "fmt" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/event" +) + +// Implement our EthTest Manager +type TestManager struct { + // stateManager *StateManager + eventMux *event.TypeMux + + db ethutil.Database + txPool *TxPool + blockChain *ChainManager + Blocks []*types.Block +} + +func (s *TestManager) IsListening() bool { + return false +} + +func (s *TestManager) IsMining() bool { + return false +} + +func (s *TestManager) PeerCount() int { + return 0 +} + +func (s *TestManager) Peers() *list.List { + return list.New() +} + +func (s *TestManager) ChainManager() *ChainManager { + return s.blockChain +} + +func (tm *TestManager) TxPool() *TxPool { + return tm.txPool +} + +// func (tm *TestManager) StateManager() *StateManager { +// return tm.stateManager +// } + +func (tm *TestManager) EventMux() *event.TypeMux { + return tm.eventMux +} +func (tm *TestManager) KeyManager() *crypto.KeyManager { + return nil +} + +func (tm *TestManager) Db() ethutil.Database { + return tm.db +} + +func NewTestManager() *TestManager { + ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "ETH") + + db, err := ethdb.NewMemDatabase() + if err != nil { + fmt.Println("Could not create mem-db, failing") + return nil + } + + testManager := &TestManager{} + testManager.eventMux = new(event.TypeMux) + testManager.db = db + // testManager.txPool = NewTxPool(testManager) + // testManager.blockChain = NewChainManager(testManager) + // testManager.stateManager = NewStateManager(testManager) + + // Start the tx pool + testManager.txPool.Start() + + return testManager +} diff --git a/core/manager.go b/core/manager.go new file mode 100644 index 000000000..bb039d063 --- /dev/null +++ b/core/manager.go @@ -0,0 +1,20 @@ +package core + +import ( + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/p2p" +) + +type Backend interface { + BlockProcessor() *BlockProcessor + ChainManager() *ChainManager + TxPool() *TxPool + PeerCount() int + IsListening() bool + Peers() []*p2p.Peer + KeyManager() *crypto.KeyManager + Db() ethutil.Database + EventMux() *event.TypeMux +} diff --git a/core/state_transition.go b/core/state_transition.go new file mode 100644 index 000000000..8626504f9 --- /dev/null +++ b/core/state_transition.go @@ -0,0 +1,274 @@ +package core + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/state" + "github.com/ethereum/go-ethereum/vm" +) + +const tryJit = false + +var ( + GasTx = big.NewInt(21000) + GasTxNonZeroByte = big.NewInt(37) + GasTxZeroByte = big.NewInt(2) +) + +/* + * The State transitioning model + * + * A state transition is a change made when a transaction is applied to the current world state + * The state transitioning model does all all the necessary work to work out a valid new state root. + * 1) Nonce handling + * 2) Pre pay / buy gas of the coinbase (miner) + * 3) Create a new state object if the recipient is \0*32 + * 4) Value transfer + * == If contract creation == + * 4a) Attempt to run transaction data + * 4b) If valid, use result as code for the new state object + * == end == + * 5) Run Script section + * 6) Derive new state root + */ +type StateTransition struct { + coinbase []byte + msg Message + gas, gasPrice *big.Int + initialGas *big.Int + value *big.Int + data []byte + state *state.StateDB + + cb, rec, sen *state.StateObject + + env vm.Environment +} + +type Message interface { + Hash() []byte + + From() []byte + To() []byte + + GasPrice() *big.Int + Gas() *big.Int + Value() *big.Int + + Nonce() uint64 + Data() []byte +} + +func AddressFromMessage(msg Message) []byte { + // Generate a new address + return crypto.Sha3(ethutil.NewValue([]interface{}{msg.From(), msg.Nonce()}).Encode())[12:] +} + +func MessageCreatesContract(msg Message) bool { + return len(msg.To()) == 0 +} + +func MessageGasValue(msg Message) *big.Int { + return new(big.Int).Mul(msg.Gas(), msg.GasPrice()) +} + +func NewStateTransition(env vm.Environment, msg Message, coinbase *state.StateObject) *StateTransition { + return &StateTransition{ + coinbase: coinbase.Address(), + env: env, + msg: msg, + gas: new(big.Int), + gasPrice: new(big.Int).Set(msg.GasPrice()), + initialGas: new(big.Int), + value: msg.Value(), + data: msg.Data(), + state: env.State(), + cb: coinbase, + } +} + +func (self *StateTransition) Coinbase() *state.StateObject { + return self.state.GetOrNewStateObject(self.coinbase) +} +func (self *StateTransition) From() *state.StateObject { + return self.state.GetOrNewStateObject(self.msg.From()) +} +func (self *StateTransition) To() *state.StateObject { + if self.msg != nil && MessageCreatesContract(self.msg) { + return nil + } + return self.state.GetOrNewStateObject(self.msg.To()) +} + +func (self *StateTransition) UseGas(amount *big.Int) error { + if self.gas.Cmp(amount) < 0 { + return OutOfGasError() + } + self.gas.Sub(self.gas, amount) + + return nil +} + +func (self *StateTransition) AddGas(amount *big.Int) { + self.gas.Add(self.gas, amount) +} + +func (self *StateTransition) BuyGas() error { + var err error + + sender := self.From() + if sender.Balance().Cmp(MessageGasValue(self.msg)) < 0 { + return fmt.Errorf("insufficient ETH for gas (%x). Req %v, has %v", sender.Address()[:4], MessageGasValue(self.msg), sender.Balance()) + } + + coinbase := self.Coinbase() + err = coinbase.BuyGas(self.msg.Gas(), self.msg.GasPrice()) + if err != nil { + return err + } + + self.AddGas(self.msg.Gas()) + self.initialGas.Set(self.msg.Gas()) + sender.SubBalance(MessageGasValue(self.msg)) + + return nil +} + +func (self *StateTransition) preCheck() (err error) { + var ( + msg = self.msg + sender = self.From() + ) + + // Make sure this transaction's nonce is correct + if sender.Nonce() != msg.Nonce() { + return NonceError(msg.Nonce(), sender.Nonce()) + } + + // Pre-pay gas / Buy gas of the coinbase account + if err = self.BuyGas(); err != nil { + return err + } + + return nil +} + +func (self *StateTransition) TransitionState() (ret []byte, err error) { + statelogger.Debugf("(~) %x\n", self.msg.Hash()) + + // XXX Transactions after this point are considered valid. + if err = self.preCheck(); err != nil { + return + } + + var ( + msg = self.msg + sender = self.From() + ) + + 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(GasTx); err != nil { + return + } + + // Pay data gas + var dgas int64 + for _, byt := range self.data { + if byt != 0 { + dgas += GasTxNonZeroByte.Int64() + } else { + dgas += GasTxZeroByte.Int64() + } + } + if err = self.UseGas(big.NewInt(dgas)); err != nil { + return + } + + //stateCopy := self.env.State().Copy() + vmenv := self.env + var ref vm.ContextRef + if MessageCreatesContract(msg) { + contract := MakeContract(msg, self.state) + ret, err, ref = vmenv.Create(sender, contract.Address(), self.msg.Data(), self.gas, self.gasPrice, self.value) + if err == nil { + dataGas := big.NewInt(int64(len(ret))) + dataGas.Mul(dataGas, vm.GasCreateByte) + if err := self.UseGas(dataGas); err == nil { + ref.SetCode(ret) + } + } + + /* + if vmenv, ok := vmenv.(*VMEnv); ok && tryJit { + statelogger.Infof("CREATE: re-running using JIT (PH=%x)\n", stateCopy.Root()[:4]) + // re-run using the JIT (validation for the JIT) + goodState := vmenv.State().Copy() + vmenv.state = stateCopy + vmenv.SetVmType(vm.JitVmTy) + vmenv.Create(sender, contract.Address(), self.msg.Data(), self.gas, self.gasPrice, self.value) + statelogger.Infof("DONE PH=%x STD_H=%x JIT_H=%x\n", stateCopy.Root()[:4], goodState.Root()[:4], vmenv.State().Root()[:4]) + self.state.Set(goodState) + } + */ + } else { + ret, err = vmenv.Call(self.From(), self.To().Address(), self.msg.Data(), self.gas, self.gasPrice, self.value) + + /* + if vmenv, ok := vmenv.(*VMEnv); ok && tryJit { + statelogger.Infof("CALL: re-running using JIT (PH=%x)\n", stateCopy.Root()[:4]) + // re-run using the JIT (validation for the JIT) + goodState := vmenv.State().Copy() + vmenv.state = stateCopy + vmenv.SetVmType(vm.JitVmTy) + vmenv.Call(self.From(), self.To().Address(), self.msg.Data(), self.gas, self.gasPrice, self.value) + statelogger.Infof("DONE PH=%x STD_H=%x JIT_H=%x\n", stateCopy.Root()[:4], goodState.Root()[:4], vmenv.State().Root()[:4]) + self.state.Set(goodState) + } + */ + } + + if err != nil { + self.UseGas(self.gas) + } + + return +} + +// Converts an transaction in to a state object +func MakeContract(msg Message, state *state.StateDB) *state.StateObject { + addr := AddressFromMessage(msg) + + contract := state.GetOrNewStateObject(addr) + contract.SetInitCode(msg.Data()) + + return contract +} + +func (self *StateTransition) RefundGas() { + coinbase, sender := self.Coinbase(), self.From() + // Return remaining gas + remaining := new(big.Int).Mul(self.gas, self.msg.GasPrice()) + sender.AddBalance(remaining) + + uhalf := new(big.Int).Div(self.GasUsed(), ethutil.Big2) + for addr, ref := range self.state.Refunds() { + refund := ethutil.BigMin(uhalf, ref) + self.gas.Add(self.gas, refund) + self.state.AddBalance([]byte(addr), refund.Mul(refund, self.msg.GasPrice())) + } + + coinbase.RefundGas(self.gas, self.msg.GasPrice()) +} + +func (self *StateTransition) GasUsed() *big.Int { + return new(big.Int).Sub(self.initialGas, self.gas) +} diff --git a/core/transaction_pool.go b/core/transaction_pool.go new file mode 100644 index 000000000..050cff3d8 --- /dev/null +++ b/core/transaction_pool.go @@ -0,0 +1,201 @@ +package core + +import ( + "errors" + "fmt" + "sync" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/logger" +) + +var ( + txplogger = logger.NewLogger("TXP") + + ErrInvalidSender = errors.New("Invalid sender") +) + +const txPoolQueueSize = 50 + +type TxPoolHook chan *types.Transaction +type TxMsg struct { + Tx *types.Transaction +} + +const ( + minGasPrice = 1000000 +) + +type TxProcessor interface { + ProcessTransaction(tx *types.Transaction) +} + +// The tx pool a thread safe transaction pool handler. In order to +// guarantee a non blocking pool we use a queue channel which can be +// independently read without needing access to the actual pool. +type TxPool struct { + mu sync.RWMutex + // Queueing channel for reading and writing incoming + // transactions to + queueChan chan *types.Transaction + // Quiting channel + quit chan bool + // The actual pool + //pool *list.List + txs map[string]*types.Transaction + + SecondaryProcessor TxProcessor + + subscribers []chan TxMsg + + eventMux *event.TypeMux +} + +func NewTxPool(eventMux *event.TypeMux) *TxPool { + return &TxPool{ + txs: make(map[string]*types.Transaction), + queueChan: make(chan *types.Transaction, txPoolQueueSize), + quit: make(chan bool), + eventMux: eventMux, + } +} + +func (pool *TxPool) ValidateTransaction(tx *types.Transaction) error { + if len(tx.To()) != 0 && len(tx.To()) != 20 { + return fmt.Errorf("Invalid recipient. len = %d", len(tx.To())) + } + + // Validate curve param + v, _, _ := tx.Curve() + if v > 28 || v < 27 { + return fmt.Errorf("tx.v != (28 || 27) => %v", v) + } + + // Validate sender address + senderAddr := tx.From() + if senderAddr == nil || len(senderAddr) != 20 { + return ErrInvalidSender + } + + /* XXX this kind of validation needs to happen elsewhere in the gui when sending txs. + Other clients should do their own validation. Value transfer could throw error + but doesn't necessarily invalidate the tx. Gas can still be payed for and miner + can still be rewarded for their inclusion and processing. + sender := pool.stateQuery.GetAccount(senderAddr) + totAmount := new(big.Int).Set(tx.Value()) + // Make sure there's enough in the sender's account. Having insufficient + // funds won't invalidate this transaction but simple ignores it. + if sender.Balance().Cmp(totAmount) < 0 { + return fmt.Errorf("Insufficient amount in sender's (%x) account", tx.From()) + } + */ + + return nil +} + +func (self *TxPool) addTx(tx *types.Transaction) { + self.txs[string(tx.Hash())] = tx +} + +func (self *TxPool) add(tx *types.Transaction) error { + if self.txs[string(tx.Hash())] != nil { + return fmt.Errorf("Known transaction (%x)", tx.Hash()[0:4]) + } + + err := self.ValidateTransaction(tx) + if err != nil { + return err + } + + self.addTx(tx) + + var to string + if len(tx.To()) > 0 { + to = ethutil.Bytes2Hex(tx.To()[:4]) + } else { + to = "[NEW_CONTRACT]" + } + + txplogger.Debugf("(t) %x => %s (%v) %x\n", tx.From()[:4], to, tx.Value, tx.Hash()) + + // Notify the subscribers + go self.eventMux.Post(TxPreEvent{tx}) + + return nil +} + +func (self *TxPool) Size() int { + return len(self.txs) +} + +func (self *TxPool) Add(tx *types.Transaction) error { + self.mu.Lock() + defer self.mu.Unlock() + return self.add(tx) +} +func (self *TxPool) AddTransactions(txs []*types.Transaction) { + self.mu.Lock() + defer self.mu.Unlock() + + for _, tx := range txs { + if err := self.add(tx); err != nil { + txplogger.Debugln(err) + } else { + txplogger.Debugf("tx %x\n", tx.Hash()[0:4]) + } + } +} + +func (self *TxPool) GetTransactions() (txs types.Transactions) { + self.mu.RLock() + defer self.mu.RUnlock() + + txs = make(types.Transactions, self.Size()) + i := 0 + for _, tx := range self.txs { + txs[i] = tx + i++ + } + + return +} + +func (pool *TxPool) RemoveInvalid(query StateQuery) { + pool.mu.Lock() + + var removedTxs types.Transactions + for _, tx := range pool.txs { + sender := query.GetAccount(tx.From()) + err := pool.ValidateTransaction(tx) + if err != nil || sender.Nonce() >= tx.Nonce() { + removedTxs = append(removedTxs, tx) + } + } + pool.mu.Unlock() + + pool.RemoveSet(removedTxs) +} + +func (self *TxPool) RemoveSet(txs types.Transactions) { + self.mu.Lock() + defer self.mu.Unlock() + + for _, tx := range txs { + delete(self.txs, string(tx.Hash())) + } +} + +func (pool *TxPool) Flush() { + pool.txs = make(map[string]*types.Transaction) +} + +func (pool *TxPool) Start() { +} + +func (pool *TxPool) Stop() { + pool.Flush() + + txplogger.Infoln("Stopped") +} diff --git a/core/transaction_pool_test.go b/core/transaction_pool_test.go new file mode 100644 index 000000000..b2d981f01 --- /dev/null +++ b/core/transaction_pool_test.go @@ -0,0 +1,97 @@ +package core + +import ( + "crypto/ecdsa" + "testing" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/state" +) + +// State query interface +type stateQuery struct{ db ethutil.Database } + +func SQ() stateQuery { + db, _ := ethdb.NewMemDatabase() + return stateQuery{db: db} +} + +func (self stateQuery) GetAccount(addr []byte) *state.StateObject { + return state.NewStateObject(addr, self.db) +} + +func transaction() *types.Transaction { + return types.NewTransactionMessage(make([]byte, 20), ethutil.Big0, ethutil.Big0, ethutil.Big0, nil) +} + +func setup() (*TxPool, *ecdsa.PrivateKey) { + var m event.TypeMux + key, _ := crypto.GenerateKey() + return NewTxPool(&m), key +} + +func TestTxAdding(t *testing.T) { + pool, key := setup() + tx1 := transaction() + tx1.SignECDSA(key) + err := pool.Add(tx1) + if err != nil { + t.Error(err) + } + + err = pool.Add(tx1) + if err == nil { + t.Error("added tx twice") + } +} + +func TestAddInvalidTx(t *testing.T) { + pool, _ := setup() + tx1 := transaction() + err := pool.Add(tx1) + if err == nil { + t.Error("expected error") + } +} + +func TestRemoveSet(t *testing.T) { + pool, _ := setup() + tx1 := transaction() + pool.addTx(tx1) + pool.RemoveSet(types.Transactions{tx1}) + if pool.Size() > 0 { + t.Error("expected pool size to be 0") + } +} + +func TestRemoveInvalid(t *testing.T) { + pool, key := setup() + tx1 := transaction() + pool.addTx(tx1) + pool.RemoveInvalid(SQ()) + if pool.Size() > 0 { + t.Error("expected pool size to be 0") + } + + tx1.SetNonce(1) + tx1.SignECDSA(key) + pool.addTx(tx1) + pool.RemoveInvalid(SQ()) + if pool.Size() != 1 { + t.Error("expected pool size to be 1, is", pool.Size()) + } +} + +func TestInvalidSender(t *testing.T) { + pool, _ := setup() + tx := new(types.Transaction) + tx.V = 28 + err := pool.ValidateTransaction(tx) + if err != ErrInvalidSender { + t.Error("expected %v, got %v", ErrInvalidSender, err) + } +} diff --git a/core/types/block.go b/core/types/block.go new file mode 100644 index 000000000..d57de1311 --- /dev/null +++ b/core/types/block.go @@ -0,0 +1,279 @@ +package types + +import ( + "bytes" + "fmt" + "math/big" + "sort" + "time" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/rlp" +) + +type Header struct { + // Hash to the previous block + ParentHash ethutil.Bytes + // Uncles of this block + UncleHash []byte + // The coin base address + Coinbase []byte + // Block Trie state + Root []byte + // Tx sha + TxHash []byte + // Receipt sha + ReceiptHash []byte + // Bloom + Bloom []byte + // Difficulty for the current block + Difficulty *big.Int + // The block number + Number *big.Int + // Gas limit + GasLimit *big.Int + // Gas used + GasUsed *big.Int + // Creation time + Time uint64 + // Extra data + Extra string + // Block Nonce for verification + Nonce ethutil.Bytes +} + +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} + if withNonce { + fields = append(fields, self.Nonce) + } + + return fields +} + +func (self *Header) RlpData() interface{} { + return self.rlpData(true) +} + +func (self *Header) Hash() []byte { + return crypto.Sha3(ethutil.Encode(self.rlpData(true))) +} + +func (self *Header) HashNoNonce() []byte { + return crypto.Sha3(ethutil.Encode(self.rlpData(false))) +} + +type Block struct { + // Preset Hash for mock + HeaderHash []byte + ParentHeaderHash []byte + header *Header + uncles []*Header + transactions Transactions + Td *big.Int + + receipts Receipts + Reward *big.Int +} + +func NewBlock(parentHash []byte, coinbase []byte, root []byte, difficulty *big.Int, nonce []byte, 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), + } + + block := &Block{header: header, Reward: new(big.Int)} + + return block +} + +func NewBlockWithHeader(header *Header) *Block { + return &Block{header: header} +} + +func (self *Block) DecodeRLP(s *rlp.Stream) error { + var extblock struct { + Header *Header + Txs []*Transaction + Uncles []*Header + TD *big.Int // optional + } + if err := s.Decode(&extblock); err != nil { + return err + } + self.header = extblock.Header + self.uncles = extblock.Uncles + self.transactions = extblock.Txs + self.Td = extblock.TD + return nil +} + +func (self *Block) Header() *Header { + return self.header +} + +func (self *Block) Uncles() []*Header { + return self.uncles +} + +func (self *Block) SetUncles(uncleHeaders []*Header) { + self.uncles = uncleHeaders + self.header.UncleHash = crypto.Sha3(ethutil.Encode(uncleHeaders)) +} + +func (self *Block) Transactions() Transactions { + return self.transactions +} + +func (self *Block) Transaction(hash []byte) *Transaction { + for _, transaction := range self.transactions { + if bytes.Equal(hash, transaction.Hash()) { + return transaction + } + } + return nil +} + +func (self *Block) SetTransactions(transactions Transactions) { + self.transactions = transactions + self.header.TxHash = DeriveSha(transactions) +} +func (self *Block) AddTransaction(transaction *Transaction) { + self.transactions = append(self.transactions, transaction) + self.SetTransactions(self.transactions) +} + +func (self *Block) Receipts() Receipts { + return self.receipts +} + +func (self *Block) SetReceipts(receipts Receipts) { + self.receipts = receipts + self.header.ReceiptHash = DeriveSha(receipts) + self.header.Bloom = CreateBloom(receipts) +} +func (self *Block) AddReceipt(receipt *Receipt) { + self.receipts = append(self.receipts, receipt) + self.SetReceipts(self.receipts) +} + +func (self *Block) RlpData() interface{} { + return []interface{}{self.header, self.transactions, self.uncles} +} + +func (self *Block) RlpDataForStorage() interface{} { + return []interface{}{self.header, self.transactions, self.uncles, self.Td /* TODO receipts */} +} + +// 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) 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) } +func (self *Block) GasLimit() *big.Int { return self.header.GasLimit } +func (self *Block) GasUsed() *big.Int { return self.header.GasUsed } +func (self *Block) Root() []byte { return self.header.Root } +func (self *Block) SetRoot(root []byte) { self.header.Root = root } +func (self *Block) Size() ethutil.StorageSize { return ethutil.StorageSize(len(ethutil.Encode(self))) } +func (self *Block) GetTransaction(i int) *Transaction { + if len(self.transactions) > i { + return self.transactions[i] + } + return nil +} +func (self *Block) GetUncle(i int) *Header { + if len(self.uncles) > i { + return self.uncles[i] + } + return nil +} + +// 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 { + if self.HeaderHash != nil { + return self.HeaderHash + } else { + return self.header.Hash() + } +} + +func (self *Block) ParentHash() []byte { + if self.ParentHeaderHash != nil { + return self.ParentHeaderHash + } else { + return self.header.ParentHash + } +} + +func (self *Block) String() string { + return fmt.Sprintf(`BLOCK(%x): Size: %v TD: %v { +NoNonce: %x +Header: +[ +%v +] +Transactions: +%v +Uncles: +%v +} +`, self.header.Hash(), self.Size(), self.Td, self.header.HashNoNonce(), self.header, self.transactions, self.uncles) +} + +func (self *Header) String() string { + return fmt.Sprintf(` + ParentHash: %x + UncleHash: %x + Coinbase: %x + Root: %x + TxSha %x + ReceiptSha: %x + Bloom: %x + Difficulty: %v + Number: %v + GasLimit: %v + 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) +} + +type Blocks []*Block + +type BlockBy func(b1, b2 *Block) bool + +func (self BlockBy) Sort(blocks Blocks) { + bs := blockSorter{ + blocks: blocks, + by: self, + } + sort.Sort(bs) +} + +type blockSorter struct { + blocks Blocks + by func(b1, b2 *Block) bool +} + +func (self blockSorter) Len() int { return len(self.blocks) } +func (self blockSorter) Swap(i, j int) { + self.blocks[i], self.blocks[j] = self.blocks[j], self.blocks[i] +} +func (self blockSorter) Less(i, j int) bool { return self.by(self.blocks[i], self.blocks[j]) } + +func Number(b1, b2 *Block) bool { return b1.Header().Number.Cmp(b2.Header().Number) < 0 } diff --git a/core/types/block_test.go b/core/types/block_test.go new file mode 100644 index 000000000..ab1254f4c --- /dev/null +++ b/core/types/block_test.go @@ -0,0 +1 @@ +package types diff --git a/core/types/bloom9.go b/core/types/bloom9.go new file mode 100644 index 000000000..578265a34 --- /dev/null +++ b/core/types/bloom9.go @@ -0,0 +1,55 @@ +package types + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/state" +) + +func CreateBloom(receipts Receipts) []byte { + bin := new(big.Int) + for _, receipt := range receipts { + bin.Or(bin, LogsBloom(receipt.logs)) + } + + return ethutil.LeftPadBytes(bin.Bytes(), 256) +} + +func LogsBloom(logs state.Logs) *big.Int { + bin := new(big.Int) + for _, log := range logs { + data := make([][]byte, len(log.Topics())+1) + data[0] = log.Address() + + for i, topic := range log.Topics() { + data[i+1] = topic + } + + for _, b := range data { + bin.Or(bin, ethutil.BigD(bloom9(crypto.Sha3(b)).Bytes())) + } + } + + return bin +} + +func bloom9(b []byte) *big.Int { + r := new(big.Int) + + for i := 0; i < 16; i += 2 { + t := big.NewInt(1) + b := uint(b[i+1]) + 1024*(uint(b[i])&1) + r.Or(r, t.Lsh(t, b)) + } + + return r +} + +func BloomLookup(bin, topic []byte) bool { + bloom := ethutil.BigD(bin) + cmp := bloom9(crypto.Sha3(topic)) + + return bloom.And(bloom, cmp).Cmp(cmp) == 0 +} diff --git a/core/types/bloom9_test.go b/core/types/bloom9_test.go new file mode 100644 index 000000000..74e00cac6 --- /dev/null +++ b/core/types/bloom9_test.go @@ -0,0 +1,31 @@ +package types + +/* +import ( + "testing" + + "github.com/ethereum/go-ethereum/state" +) + +func TestBloom9(t *testing.T) { + testCase := []byte("testtest") + bin := LogsBloom([]state.Log{ + {testCase, [][]byte{[]byte("hellohello")}, nil}, + }).Bytes() + res := BloomLookup(bin, testCase) + + if !res { + t.Errorf("Bloom lookup failed") + } +} + + +func TestAddress(t *testing.T) { + block := &Block{} + block.Coinbase = ethutil.Hex2Bytes("22341ae42d6dd7384bc8584e50419ea3ac75b83f") + fmt.Printf("%x\n", crypto.Sha3(block.Coinbase)) + + bin := CreateBloom(block) + fmt.Printf("bin = %x\n", ethutil.LeftPadBytes(bin, 64)) +} +*/ diff --git a/core/types/common.go b/core/types/common.go new file mode 100644 index 000000000..795374959 --- /dev/null +++ b/core/types/common.go @@ -0,0 +1,7 @@ +package types + +import "math/big" + +type BlockProcessor interface { + Process(*Block) (*big.Int, error) +} diff --git a/core/types/derive_sha.go b/core/types/derive_sha.go new file mode 100644 index 000000000..b2c442210 --- /dev/null +++ b/core/types/derive_sha.go @@ -0,0 +1,22 @@ +package types + +import ( + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/trie" +) + +type DerivableList interface { + Len() int + GetRlp(i int) []byte +} + +func DeriveSha(list DerivableList) []byte { + db, _ := ethdb.NewMemDatabase() + trie := trie.New(nil, db) + for i := 0; i < list.Len(); i++ { + trie.Update(ethutil.Encode(i), list.GetRlp(i)) + } + + return trie.Root() +} diff --git a/core/types/receipt.go b/core/types/receipt.go new file mode 100644 index 000000000..49e68e233 --- /dev/null +++ b/core/types/receipt.go @@ -0,0 +1,81 @@ +package types + +import ( + "bytes" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/state" +) + +type Receipt struct { + PostState []byte + CumulativeGasUsed *big.Int + Bloom []byte + logs state.Logs +} + +func NewReceipt(root []byte, cumalativeGasUsed *big.Int) *Receipt { + return &Receipt{PostState: ethutil.CopyBytes(root), CumulativeGasUsed: new(big.Int).Set(cumalativeGasUsed)} +} + +func NewRecieptFromValue(val *ethutil.Value) *Receipt { + r := &Receipt{} + r.RlpValueDecode(val) + + return r +} + +func (self *Receipt) SetLogs(logs state.Logs) { + self.logs = logs +} + +func (self *Receipt) RlpValueDecode(decoder *ethutil.Value) { + self.PostState = decoder.Get(0).Bytes() + self.CumulativeGasUsed = decoder.Get(1).BigInt() + self.Bloom = decoder.Get(2).Bytes() + + it := decoder.Get(3).NewIterator() + for it.Next() { + self.logs = append(self.logs, state.NewLogFromValue(it.Value())) + } +} + +func (self *Receipt) RlpData() interface{} { + return []interface{}{self.PostState, self.CumulativeGasUsed, self.Bloom, self.logs.RlpData()} +} + +func (self *Receipt) RlpEncode() []byte { + return ethutil.Encode(self.RlpData()) +} + +func (self *Receipt) Cmp(other *Receipt) bool { + if bytes.Compare(self.PostState, other.PostState) != 0 { + return false + } + + return true +} + +func (self *Receipt) String() string { + return fmt.Sprintf("receipt{med=%x cgas=%v bloom=%x logs=%v}", self.PostState, self.CumulativeGasUsed, self.Bloom, self.logs) +} + +type Receipts []*Receipt + +func (self Receipts) RlpData() interface{} { + data := make([]interface{}, len(self)) + for i, receipt := range self { + data[i] = receipt.RlpData() + } + + return data +} + +func (self Receipts) RlpEncode() []byte { + return ethutil.Encode(self.RlpData()) +} + +func (self Receipts) Len() int { return len(self) } +func (self Receipts) GetRlp(i int) []byte { return ethutil.Rlp(self[i]) } diff --git a/core/types/transaction.go b/core/types/transaction.go new file mode 100644 index 000000000..7a1d6104e --- /dev/null +++ b/core/types/transaction.go @@ -0,0 +1,226 @@ +package types + +import ( + "bytes" + "crypto/ecdsa" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/secp256k1" + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/rlp" +) + +func IsContractAddr(addr []byte) bool { + return len(addr) == 0 +} + +type Transaction struct { + AccountNonce uint64 + Price *big.Int + GasLimit *big.Int + Recipient []byte + Amount *big.Int + Payload []byte + V uint64 + R, S []byte +} + +func NewContractCreationTx(Amount, gasAmount, price *big.Int, data []byte) *Transaction { + return NewTransactionMessage(nil, Amount, gasAmount, price, data) +} + +func NewTransactionMessage(to []byte, Amount, gasAmount, price *big.Int, data []byte) *Transaction { + return &Transaction{Recipient: to, Amount: Amount, Price: price, GasLimit: gasAmount, Payload: data} +} + +func NewTransactionFromBytes(data []byte) *Transaction { + tx := &Transaction{} + tx.RlpDecode(data) + + return tx +} + +func NewTransactionFromAmount(val *ethutil.Value) *Transaction { + tx := &Transaction{} + tx.RlpValueDecode(val) + + return tx +} + +func (tx *Transaction) Hash() []byte { + data := []interface{}{tx.AccountNonce, tx.Price, tx.GasLimit, tx.Recipient, tx.Amount, tx.Payload} + + return crypto.Sha3(ethutil.Encode(data)) +} + +func (self *Transaction) Data() []byte { + return self.Payload +} + +func (self *Transaction) Gas() *big.Int { + return self.GasLimit +} + +func (self *Transaction) GasPrice() *big.Int { + return self.Price +} + +func (self *Transaction) Value() *big.Int { + return self.Amount +} + +func (self *Transaction) Nonce() uint64 { + return self.AccountNonce +} + +func (self *Transaction) SetNonce(AccountNonce uint64) { + self.AccountNonce = AccountNonce +} + +func (self *Transaction) From() []byte { + return self.sender() +} + +func (self *Transaction) To() []byte { + return self.Recipient +} + +func (tx *Transaction) Curve() (v byte, r []byte, s []byte) { + v = byte(tx.V) + r = ethutil.LeftPadBytes(tx.R, 32) + s = ethutil.LeftPadBytes(tx.S, 32) + + return +} + +func (tx *Transaction) Signature(key []byte) []byte { + hash := tx.Hash() + + sig, _ := secp256k1.Sign(hash, key) + + return sig +} + +func (tx *Transaction) PublicKey() []byte { + hash := tx.Hash() + + v, r, s := tx.Curve() + + sig := append(r, s...) + sig = append(sig, v-27) + + //pubkey := crypto.Ecrecover(append(hash, sig...)) + pubkey, _ := secp256k1.RecoverPubkey(hash, sig) + + return pubkey +} + +func (tx *Transaction) sender() []byte { + pubkey := tx.PublicKey() + + // Validate the returned key. + // Return nil if public key isn't in full format + if len(pubkey) == 0 || pubkey[0] != 4 { + return nil + } + + return crypto.Sha3(pubkey[1:])[12:] +} + +func (tx *Transaction) Sign(privk []byte) error { + + sig := tx.Signature(privk) + + tx.R = sig[:32] + tx.S = sig[32:64] + tx.V = uint64(sig[64] + 27) + + return nil +} + +func (tx *Transaction) SignECDSA(key *ecdsa.PrivateKey) error { + return tx.Sign(crypto.FromECDSA(key)) +} + +func (tx *Transaction) RlpData() interface{} { + data := []interface{}{tx.AccountNonce, tx.Price, tx.GasLimit, tx.Recipient, tx.Amount, tx.Payload} + + return append(data, tx.V, new(big.Int).SetBytes(tx.R).Bytes(), new(big.Int).SetBytes(tx.S).Bytes()) +} + +func (tx *Transaction) RlpEncode() []byte { + return ethutil.Encode(tx) +} + +func (tx *Transaction) RlpDecode(data []byte) { + rlp.Decode(bytes.NewReader(data), tx) +} + +func (tx *Transaction) RlpValueDecode(decoder *ethutil.Value) { + tx.AccountNonce = decoder.Get(0).Uint() + tx.Price = decoder.Get(1).BigInt() + tx.GasLimit = decoder.Get(2).BigInt() + tx.Recipient = decoder.Get(3).Bytes() + tx.Amount = decoder.Get(4).BigInt() + tx.Payload = decoder.Get(5).Bytes() + tx.V = decoder.Get(6).Uint() + tx.R = decoder.Get(7).Bytes() + tx.S = decoder.Get(8).Bytes() +} + +func (tx *Transaction) String() string { + return fmt.Sprintf(` + TX(%x) + Contract: %v + From: %x + To: %x + Nonce: %v + GasPrice: %v + GasLimit %v + Value: %v + Data: 0x%x + V: 0x%x + R: 0x%x + S: 0x%x + Hex: %x +`, + tx.Hash(), + len(tx.Recipient) == 0, + tx.From(), + tx.To(), + tx.AccountNonce, + tx.Price, + tx.GasLimit, + tx.Amount, + tx.Payload, + tx.V, + tx.R, + tx.S, + ethutil.Encode(tx), + ) +} + +// Transaction slice type for basic sorting +type Transactions []*Transaction + +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] } +func (s Transactions) GetRlp(i int) []byte { return ethutil.Rlp(s[i]) } + +type TxByNonce struct{ Transactions } + +func (s TxByNonce) Less(i, j int) bool { + return s.Transactions[i].AccountNonce < s.Transactions[j].AccountNonce +} diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go new file mode 100644 index 000000000..ab1254f4c --- /dev/null +++ b/core/types/transaction_test.go @@ -0,0 +1 @@ +package types diff --git a/core/vm_env.go b/core/vm_env.go new file mode 100644 index 000000000..c7491bcdc --- /dev/null +++ b/core/vm_env.go @@ -0,0 +1,72 @@ +package core + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/state" + "github.com/ethereum/go-ethereum/vm" +) + +type VMEnv struct { + state *state.StateDB + block *types.Block + msg Message + depth int + chain *ChainManager + typ vm.Type +} + +func NewEnv(state *state.StateDB, chain *ChainManager, msg Message, block *types.Block) *VMEnv { + return &VMEnv{ + chain: chain, + state: state, + block: block, + msg: msg, + typ: vm.StdVmTy, + } +} + +func (self *VMEnv) Origin() []byte { return self.msg.From() } +func (self *VMEnv) BlockNumber() *big.Int { return self.block.Number() } +func (self *VMEnv) Coinbase() []byte { return self.block.Coinbase() } +func (self *VMEnv) Time() int64 { return self.block.Time() } +func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty() } +func (self *VMEnv) GasLimit() *big.Int { return self.block.GasLimit() } +func (self *VMEnv) Value() *big.Int { return self.msg.Value() } +func (self *VMEnv) State() *state.StateDB { return self.state } +func (self *VMEnv) Depth() int { return self.depth } +func (self *VMEnv) SetDepth(i int) { self.depth = i } +func (self *VMEnv) VmType() vm.Type { return self.typ } +func (self *VMEnv) SetVmType(t vm.Type) { self.typ = t } +func (self *VMEnv) GetHash(n uint64) []byte { + if block := self.chain.GetBlockByNumber(n); block != nil { + return block.Hash() + } + + return nil +} +func (self *VMEnv) AddLog(log state.Log) { + self.state.AddLog(log) +} +func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) error { + return vm.Transfer(from, to, amount) +} + +func (self *VMEnv) vm(addr, data []byte, gas, price, value *big.Int) *Execution { + return NewExecution(self, addr, data, gas, price, value) +} + +func (self *VMEnv) Call(me vm.ContextRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) { + exe := self.vm(addr, data, gas, price, value) + return exe.Call(addr, me) +} +func (self *VMEnv) CallCode(me vm.ContextRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) { + exe := self.vm(me.Address(), data, gas, price, value) + return exe.Call(addr, me) +} + +func (self *VMEnv) Create(me vm.ContextRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error, vm.ContextRef) { + exe := self.vm(addr, data, gas, price, value) + return exe.Create(me) +} |