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 | 362 | ||||
-rw-r--r-- | core/block_processor_test.go | 35 | ||||
-rw-r--r-- | core/chain_makers.go | 142 | ||||
-rw-r--r-- | core/chain_manager.go | 513 | ||||
-rw-r--r-- | core/chain_manager_test.go | 346 | ||||
-rw-r--r-- | core/error.go | 164 | ||||
-rw-r--r-- | core/events.go | 28 | ||||
-rw-r--r-- | core/execution.go | 80 | ||||
-rw-r--r-- | core/fees.go | 7 | ||||
-rw-r--r-- | core/filter.go | 204 | ||||
-rw-r--r-- | core/filter_test.go | 1 | ||||
-rw-r--r-- | core/genesis.go | 75 | ||||
-rw-r--r-- | core/helper_test.go | 83 | ||||
-rw-r--r-- | core/manager.go | 19 | ||||
-rw-r--r-- | core/state_transition.go | 251 | ||||
-rw-r--r-- | core/transaction_pool.go | 206 | ||||
-rw-r--r-- | core/transaction_pool_test.go | 97 | ||||
-rw-r--r-- | core/types/block.go | 308 | ||||
-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 | 234 | ||||
-rw-r--r-- | core/types/transaction_test.go | 1 | ||||
-rw-r--r-- | core/vm_env.go | 72 |
29 files changed, 3487 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..fc3493fe1 --- /dev/null +++ b/core/asm.go @@ -0,0 +1,50 @@ +package core + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "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, common.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, common.Big1) + } + + return asm +} diff --git a/core/block_processor.go b/core/block_processor.go new file mode 100644 index 000000000..f67d6d006 --- /dev/null +++ b/core/block_processor.go @@ -0,0 +1,362 @@ +package core + +import ( + "bytes" + "fmt" + "math/big" + "sync" + "time" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/pow" + "github.com/ethereum/go-ethereum/rlp" + "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 common.Database + extraDb common.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, extra common.Database, pow pow.PoW, txpool *TxPool, chainManager *ChainManager, eventMux *event.TypeMux) *BlockProcessor { + sm := &BlockProcessor{ + db: db, + extraDb: extra, + mem: make(map[string]*big.Int), + Pow: pow, + 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() + + cb := statedb.GetStateObject(coinbase.Address()) + _, gas, err := ApplyMessage(NewEnv(statedb, self.bc, tx, block), tx, cb) + if err != nil && (IsNonceErr(err) || state.IsGasLimitErr(err) || IsInvalidTxErr(err)) { + // If the account is managed, remove the invalid nonce. + self.bc.TxState().RemoveNonce(tx.From(), tx.Nonce()) + return nil, nil, err + } + + // Update the state with pending changes + statedb.Update(nil) + + cumulative := new(big.Int).Set(usedGas.Add(usedGas, gas)) + 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, gas, 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 ( + 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 && (IsNonceErr(err) || state.IsGasLimitErr(err) || IsInvalidTxErr(err)) { + return nil, nil, nil, nil, err + } + + if err != nil { + statelogger.Infoln("TX err:", err) + } + 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.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 + } + + 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 { + 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(common.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()) + + for _, tx := range block.Transactions() { + putTx(sm.extraDb, tx) + } + + 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) 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.Difficulty) != 0 { + return fmt.Errorf("Difficulty check failed for block %v, %v", block.Difficulty, expd) + } + + // 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 equal or less than previous block (%v - %v)", block.Time, parent.Time) + } + + if int64(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(types.NewBlockWithHeader(block)) { + return ValidationError("Block's nonce is invalid (= %x)", block.Nonce) + } + + return nil +} + +func (sm *BlockProcessor) AccumulateRewards(statedb *state.StateDB, block, parent *types.Block) error { + 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) { + 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.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 (= %x)", 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 +} + +func putTx(db common.Database, tx *types.Transaction) { + rlpEnc, err := rlp.EncodeToBytes(tx) + if err != nil { + statelogger.Infoln("Failed encoding tx", err) + return + } + db.Put(tx.Hash(), rlpEnc) +} diff --git a/core/block_processor_test.go b/core/block_processor_test.go new file mode 100644 index 000000000..ad29404e1 --- /dev/null +++ b/core/block_processor_test.go @@ -0,0 +1,35 @@ +package core + +import ( + "math/big" + "testing" + + "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, db, &mux) + return NewBlockProcessor(db, db, ezp.New(), nil, chainMan, &mux), chainMan +} + +func TestNumber(t *testing.T) { + bp, chain := proc() + block1 := chain.NewBlock(nil) + block1.Header().Number = big.NewInt(3) + + err := bp.ValidateHeader(block1.Header(), chain.Genesis().Header()) + if err != BlockNumberErr { + t.Errorf("expected block number error") + } + + block1 = chain.NewBlock(nil) + 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..59c297dbe --- /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/common" + "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 common.Database, seed int) *types.Block { + return makeBlock(bman, parent, i, db, seed) +} + +func MakeChain(bman *BlockProcessor, parent *types.Block, max int, db common.Database, seed int) types.Blocks { + return makeChain(bman, parent, max, db, seed) +} + +func NewChainMan(block *types.Block, eventMux *event.TypeMux, db common.Database) *ChainManager { + return newChainManager(block, eventMux, db) +} + +func NewBlockProc(db common.Database, txpool *TxPool, cman *ChainManager, eventMux *event.TypeMux) *BlockProcessor { + return newBlockProcessor(db, txpool, cman, eventMux) +} + +func NewCanonical(n int, db common.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(), common.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, common.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 common.Database, seed int) *types.Block { + addr := common.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(common.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 common.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 common.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 common.Database, txpool *TxPool, cman *ChainManager, eventMux *event.TypeMux) *BlockProcessor { + bman := NewBlockProcessor(db, 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 common.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 new file mode 100644 index 000000000..ff91b0427 --- /dev/null +++ b/core/chain_manager.go @@ -0,0 +1,513 @@ +package core + +import ( + "bytes" + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/common" + "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") + jsonlogger = logger.NewJsonLogger() + + blockHashPre = []byte("block-hash-") + blockNumPre = []byte("block-num-") +) + +type StateQuery interface { + GetAccount(addr []byte) *state.StateObject +} + +func CalcDifficulty(block, parent *types.Header) *big.Int { + diff := new(big.Int) + + 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.Sub(parent.Difficulty, adjust) + } + + if diff.Cmp(GenesisDiff) < 0 { + return GenesisDiff + } + + 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 common.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)) + + return common.BigMax(GenesisGasLimit, result) +} + +type ChainManager struct { + //eth EthManager + blockDb common.Database + stateDb common.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.ManagedState + + quit chan struct{} +} + +func NewChainManager(blockDb, stateDb common.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() + // Take ownership of this particular state + bc.txState = state.ManageState(bc.State().Copy()) + go bc.update() + + 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.genesisBlock.Hash() +} + +func (self *ChainManager) SetProcessor(proc types.BlockProcessor) { + self.processor = proc +} + +func (self *ChainManager) State() *state.StateDB { + return state.New(self.CurrentBlock().Root(), self.stateDb) +} + +func (self *ChainManager) TransState() *state.StateDB { + self.tsmu.RLock() + defer self.tsmu.RUnlock() + + return self.transState +} + +func (self *ChainManager) TxState() *state.ManagedState { + self.tsmu.RLock() + defer self.tsmu.RUnlock() + + return self.txState +} + +func (self *ChainManager) setTxState(statedb *state.StateDB) { + self.tsmu.Lock() + defer self.tsmu.Unlock() + self.txState = state.ManageState(statedb) +} + +func (self *ChainManager) setTransState(statedb *state.StateDB) { + self.transState = statedb +} + +func (bc *ChainManager) setLastBlock() { + data, _ := bc.blockDb.Get([]byte("LastBlock")) + if len(data) != 0 { + block := bc.GetBlock(data) + bc.currentBlock = block + bc.lastBlockHash = block.Hash() + + // Set the last know difficulty (might be 0x0 as initial value, Genesis) + bc.td = common.BigD(bc.blockDb.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, + common.BigPow(2, 32), + 0, + "") + block.SetUncles(nil) + block.SetTransactions(nil) + block.SetReceipts(nil) + + parent := bc.currentBlock + if parent != nil { + header := block.Header() + header.Difficulty = CalcDifficulty(block.Header(), parent.Header()) + header.Number = new(big.Int).Add(parent.Header().Number, common.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.removeBlock(block) + } + + // Prepare the genesis block + bc.write(bc.genesisBlock) + bc.insert(bc.genesisBlock) + bc.currentBlock = bc.genesisBlock + + bc.setTotalDifficulty(common.Big("0")) +} + +func (bc *ChainManager) removeBlock(block *types.Block) { + bc.blockDb.Delete(append(blockHashPre, block.Hash()...)) +} + +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.removeBlock(block) + } + + // 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() + + 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 common.Encode(blocks) +} + +func (bc *ChainManager) insert(block *types.Block) { + //encodedBlock := common.Encode(block) + bc.blockDb.Put([]byte("LastBlock"), block.Hash()) + bc.currentBlock = block + bc.lastBlockHash = block.Hash() + + key := append(blockNumPre, block.Number().Bytes()...) + bc.blockDb.Put(key, bc.lastBlockHash) +} + +func (bc *ChainManager) write(block *types.Block) { + encodedBlock := common.Encode(block.RlpDataForStorage()) + + key := append(blockHashPre, block.Hash()...) + bc.blockDb.Put(key, encodedBlock) +} + +// Accessors +func (bc *ChainManager) Genesis() *types.Block { + return bc.genesisBlock +} + +// Block fetching methods +func (bc *ChainManager) HasBlock(hash []byte) bool { + data, _ := bc.blockDb.Get(append(blockHashPre, 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(common.Big0) <= 0 { + break + } + } + + return +} + +func (self *ChainManager) GetBlock(hash []byte) *types.Block { + data, _ := self.blockDb.Get(append(blockHashPre, 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) GetBlockByNumber(num uint64) *types.Block { + self.mu.RLock() + defer self.mu.RUnlock() + + key, _ := self.blockDb.Get(append(blockNumPre, big.NewInt(int64(num)).Bytes()...)) + if len(key) == 0 { + return nil + } + + return self.GetBlock(key) +} + +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 (bc *ChainManager) setTotalDifficulty(td *big.Int) { + bc.blockDb.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() { + 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() + + // 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) + if err != nil { + if IsKnownBlockErr(err) { + continue + } + + h := block.Header() + chainlogger.Infof("INVALID block #%v (%x)\n", h.Number, h.Hash()[:4]) + chainlogger.Infoln(err) + chainlogger.Debugln(block) + return err + } + block.Td = td + + self.mu.Lock() + cblock := self.currentBlock + { + // 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) + // 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, common.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) + + queue[i] = ChainSplitEvent{block} + queueEvent.splitCount++ + } + + self.setTotalDifficulty(td) + self.insert(block) + + /* XXX crashes + jsonlogger.LogJson(&logger.EthChainNewHead{ + BlockHash: common.Bytes2Hex(block.Hash()), + BlockNumber: block.Number(), + ChainHeadHash: common.Bytes2Hex(cblock.Hash()), + BlockPrevHash: common.Bytes2Hex(block.ParentHash()), + }) + */ + + self.setTransState(state.New(block.Root(), self.stateDb)) + self.setTxState(state.New(block.Root(), self.stateDb)) + + queue[i] = ChainEvent{block} + queueEvent.canonicalCount++ + } else { + queue[i] = ChainSideEvent{block} + queueEvent.sideCount++ + } + } + self.mu.Unlock() + + } + + // 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 new file mode 100644 index 000000000..898d37f9c --- /dev/null +++ b/core/chain_manager_test.go @@ -0,0 +1,346 @@ +package core + +import ( + "bytes" + "fmt" + "math/big" + "os" + "path" + "runtime" + "strconv" + "testing" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/rlp" +) + +func init() { + runtime.GOMAXPROCS(runtime.NumCPU()) +} + +// 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 { + 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 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. + + 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, db, &eventMux) + txPool := NewTxPool(&eventMux) + blockMan := NewBlockProcessor(db, db, nil, 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, db, &eventMux) + txPool := NewTxPool(&eventMux) + blockMan := NewBlockProcessor(db, db, nil, 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, 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..69e320eb0 --- /dev/null +++ b/core/error.go @@ -0,0 +1,164 @@ +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 unknown %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("Transaction w/ invalid nonce (%d / %d)", is, exp), Is: is, Exp: exp} +} + +func IsNonceErr(err error) bool { + _, ok := err.(*NonceErr) + + 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 +} + +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 +} + +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 new file mode 100644 index 000000000..23678ef60 --- /dev/null +++ b/core/events.go @@ -0,0 +1,28 @@ +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 } + +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 new file mode 100644 index 000000000..be45eeeb4 --- /dev/null +++ b/core/execution.go @@ -0,0 +1,80 @@ +package core + +import ( + "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, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", self.value, from.Balance()) + } + + snapshot := env.State().Copy() + start := time.Now() + + context := vm.NewContext(caller, to, self.value, self.Gas, self.price) + context.SetCallCode(contextAddr, code) + + ret, err = evm.Run(context, self.input) //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..d58aa8d7c --- /dev/null +++ b/core/filter.go @@ -0,0 +1,204 @@ +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} +} + +// SetOptions copies the filter options to the filter it self. The reason for this "silly" copy +// is simply because named arguments in this case is extremely nice and readable. +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 len(self.address) > 0 && !includes(self.address, log.Address()) { + continue + } + + logTopics := make([][]byte, len(self.topics)) + copy(logTopics, log.Topics()) + + for i, topics := range self.topics { + for _, topic := range topics { + var match bool + if bytes.Equal(log.Topics()[i], topic) { + match = true + } + if !match { + 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 _, sub := range self.topics { + var included bool + for _, topic := range sub { + if types.BloomLookup(block.Bloom(), topic) { + included = true + break + } + } + if !included { + 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..bfd51f196 --- /dev/null +++ b/core/genesis.go @@ -0,0 +1,75 @@ +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/common" + "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(common.Encode([]interface{}{})) +var EmptyListRoot = crypto.Sha3(common.Encode("")) + +var GenesisDiff = big.NewInt(131072) +var GenesisGasLimit = big.NewInt(3141592) + +func GenesisBlock(db common.Database) *types.Block { + genesis := types.NewBlock(ZeroHash256, ZeroHash160, nil, GenesisDiff, 42, "") + genesis.Header().Number = common.Big0 + genesis.Header().GasLimit = GenesisGasLimit + genesis.Header().GasUsed = common.Big0 + genesis.Header().Time = 0 + genesis.Header().MixDigest = make([]byte, 32) + + genesis.Td = common.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 := common.Hex2Bytes(addr) + accountState := statedb.GetAccount(codedAddr) + accountState.SetBalance(common.Big(account.Balance)) + statedb.UpdateStateObject(accountState) + } + statedb.Sync() + genesis.Header().Root = statedb.Root() + + return genesis +} + +var genesisData = []byte(`{ + "0000000000000000000000000000000000000001": {"balance": "1"}, + "0000000000000000000000000000000000000002": {"balance": "1"}, + "0000000000000000000000000000000000000003": {"balance": "1"}, + "0000000000000000000000000000000000000004": {"balance": "1"}, + "dbdbdb2cbd23b783741e8d7fcf51e459b497e4a6": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, + "e4157b34ea9615cfbde6b4fda419828124b70c78": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, + "b9c015918bdaba24b4ff057a92a3873d6eb201be": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, + "6c386a4b26f73c802f34673f7248bb118f97424a": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, + "cd2a3d9f938e13cd947ec05abc7fe734df8dd826": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, + "2ef47100e0787b915105fd5e3f4ff6752079d5cb": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, + "e6716f9544a56c530d868e4bfbacb172315bdead": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}, + "1a26338f0d905e295fccb71fa9ea849ffa12aaf4": {"balance": "1606938044258990275541962092341162602522202993782792835301376"} +}`) diff --git a/core/helper_test.go b/core/helper_test.go new file mode 100644 index 000000000..1e0ed178b --- /dev/null +++ b/core/helper_test.go @@ -0,0 +1,83 @@ +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/common" + "github.com/ethereum/go-ethereum/event" +) + +// Implement our EthTest Manager +type TestManager struct { + // stateManager *StateManager + eventMux *event.TypeMux + + db common.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() common.Database { + return tm.db +} + +func NewTestManager() *TestManager { + 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..9b5407a9e --- /dev/null +++ b/core/manager.go @@ -0,0 +1,19 @@ +package core + +import ( + "github.com/ethereum/go-ethereum/common" + "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 + BlockDb() common.Database + StateDb() common.Database + EventMux() *event.TypeMux +} diff --git a/core/state_transition.go b/core/state_transition.go new file mode 100644 index 000000000..279abee62 --- /dev/null +++ b/core/state_transition.go @@ -0,0 +1,251 @@ +package core + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/state" + "github.com/ethereum/go-ethereum/vm" +) + +const tryJit = false + +var () + +/* + * 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 { + 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(common.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 ApplyMessage(env vm.Environment, msg Message, coinbase *state.StateObject) ([]byte, *big.Int, error) { + return NewStateTransition(env, msg, coinbase).transitionState() +} + +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 { + if state.IsGasLimitErr(err) { + return err + } + return InvalidTxError(err) + } + + return nil +} + +func (self *StateTransition) transitionState() (ret []byte, usedGas *big.Int, 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() + ) + + // Transaction gas + if err = self.UseGas(vm.GasTx); err != nil { + return nil, 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.GasTxDataNonzeroByte.Int64() + } else { + dgas += vm.GasTxDataZeroByte.Int64() + } + } + if err = self.UseGas(big.NewInt(dgas)); err != nil { + return nil, nil, InvalidTxError(err) + } + + 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) + } else { + statelogger.Infoln("Insufficient gas for creating code. Require", dataGas, "and have", self.gas) + } + } + } else { + ret, err = vmenv.Call(self.From(), self.To().Address(), self.msg.Data(), self.gas, self.gasPrice, self.value) + } + + if err != nil && IsValueTransferErr(err) { + return nil, nil, InvalidTxError(err) + } + + self.refundGas() + self.state.AddBalance(self.coinbase, new(big.Int).Mul(self.gasUsed(), self.gasPrice)) + + return ret, self.gasUsed(), err +} + +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(), common.Big2) + for addr, ref := range self.state.Refunds() { + refund := common.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) +} + +// Converts an message 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 +} diff --git a/core/transaction_pool.go b/core/transaction_pool.go new file mode 100644 index 000000000..515cc2040 --- /dev/null +++ b/core/transaction_pool.go @@ -0,0 +1,206 @@ +package core + +import ( + "errors" + "fmt" + "sync" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/common" + "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 = common.Bytes2Hex(tx.To()[:4]) + } else { + to = "[NEW_CONTRACT]" + } + var from string + if len(tx.From()) > 0 { + from = common.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}) + + 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..418cb0415 --- /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/common" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/state" +) + +// State query interface +type stateQuery struct{ db common.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), common.Big0, common.Big0, common.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..2d65cdca6 --- /dev/null +++ b/core/types/block.go @@ -0,0 +1,308 @@ +package types + +import ( + "bytes" + "encoding/binary" + "fmt" + "math/big" + "sort" + "time" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/rlp" +) + +type Header struct { + // Hash to the previous block + ParentHash []byte + // 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 + // Mix digest for quick checking to prevent DOS + MixDigest []byte + // 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, + } + if withNonce { + fields = append(fields, self.MixDigest, self.Nonce) + } + + return fields +} + +func (self *Header) RlpData() interface{} { + return self.rlpData(true) +} + +func (self *Header) Hash() []byte { + return crypto.Sha3(common.Encode(self.rlpData(true))) +} + +func (self *Header) HashNoNonce() []byte { + return crypto.Sha3(common.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 uint64, extra string) *Block { + header := &Header{ + Root: root, + ParentHash: parentHash, + Coinbase: coinbase, + Difficulty: difficulty, + 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} +} + +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(common.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) MixDigest() []byte { return self.header.MixDigest } +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) } +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() common.StorageSize { return common.StorageSize(len(common.Encode(self))) } +func (self *Block) GetTransaction(i int) *Transaction { + if len(self.transactions) > i { + return self.transactions[i] + } + 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) 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 + 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.MixDigest, 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..af76f226f --- /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/common" + "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 common.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, common.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 := common.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..0841bb859 --- /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 = common.Hex2Bytes("22341ae42d6dd7384bc8584e50419ea3ac75b83f") + fmt.Printf("%x\n", crypto.Sha3(block.Coinbase)) + + bin := CreateBloom(block) + fmt.Printf("bin = %x\n", common.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..593a31f1c --- /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/common" + "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(common.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..be14d0e0e --- /dev/null +++ b/core/types/receipt.go @@ -0,0 +1,81 @@ +package types + +import ( + "bytes" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "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: common.CopyBytes(root), CumulativeGasUsed: new(big.Int).Set(cumalativeGasUsed)} +} + +func NewRecieptFromValue(val *common.Value) *Receipt { + r := &Receipt{} + r.RlpValueDecode(val) + + return r +} + +func (self *Receipt) SetLogs(logs state.Logs) { + self.logs = logs +} + +func (self *Receipt) RlpValueDecode(decoder *common.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 common.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 common.Encode(self.RlpData()) +} + +func (self Receipts) Len() int { return len(self) } +func (self Receipts) GetRlp(i int) []byte { return common.Rlp(self[i]) } diff --git a/core/types/transaction.go b/core/types/transaction.go new file mode 100644 index 000000000..dcd48af11 --- /dev/null +++ b/core/types/transaction.go @@ -0,0 +1,234 @@ +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/common" + "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 byte + 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 *common.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(common.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 = common.LeftPadBytes(tx.R, 32) + s = common.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:] +} + +// TODO: deprecate after new accounts & key stores are integrated +func (tx *Transaction) Sign(privk []byte) error { + + sig := tx.Signature(privk) + + tx.R = sig[:32] + tx.S = sig[32:64] + tx.V = sig[64] + 27 + + return nil +} + +func (tx *Transaction) SetSignatureValues(sig []byte) error { + tx.R = sig[:32] + tx.S = sig[32:64] + tx.V = 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 common.Encode(tx) +} + +func (tx *Transaction) RlpDecode(data []byte) { + rlp.Decode(bytes.NewReader(data), tx) +} + +func (tx *Transaction) RlpValueDecode(decoder *common.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).Byte() + 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, + common.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 common.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) +} |