diff options
author | obscuren <geffobscura@gmail.com> | 2014-12-04 17:28:02 +0800 |
---|---|---|
committer | obscuren <geffobscura@gmail.com> | 2014-12-04 17:28:02 +0800 |
commit | 9008b155d3c8d2a32c4c8945f1174243d48d4e90 (patch) | |
tree | a55140b7d4802a0db14cbc265b8ca21d44d6f10a /core | |
parent | b6cb5272de96185b424d5c6c4a817d99f536d29b (diff) | |
download | go-tangerine-9008b155d3c8d2a32c4c8945f1174243d48d4e90.tar.gz go-tangerine-9008b155d3c8d2a32c4c8945f1174243d48d4e90.tar.zst go-tangerine-9008b155d3c8d2a32c4c8945f1174243d48d4e90.zip |
Renamed `chain` => `core`
Diffstat (limited to 'core')
-rw-r--r-- | core/.gitignore | 12 | ||||
-rw-r--r-- | core/asm.go | 50 | ||||
-rw-r--r-- | core/block_manager.go | 414 | ||||
-rw-r--r-- | core/chain_manager.go | 276 | ||||
-rw-r--r-- | core/chain_manager_test.go | 116 | ||||
-rw-r--r-- | core/dagger.go | 245 | ||||
-rw-r--r-- | core/dagger_test.go | 19 | ||||
-rw-r--r-- | core/error.go | 141 | ||||
-rw-r--r-- | core/events.go | 12 | ||||
-rw-r--r-- | core/execution.go | 80 | ||||
-rw-r--r-- | core/fees.go | 7 | ||||
-rw-r--r-- | core/filter.go | 200 | ||||
-rw-r--r-- | core/filter_test.go | 7 | ||||
-rw-r--r-- | core/genesis.go | 52 | ||||
-rw-r--r-- | core/helper_test.go | 93 | ||||
-rw-r--r-- | core/state_transition.go | 198 | ||||
-rw-r--r-- | core/transaction_pool.go | 236 | ||||
-rw-r--r-- | core/types/block.go | 413 | ||||
-rw-r--r-- | core/types/bloom9.go | 56 | ||||
-rw-r--r-- | core/types/bloom9_test.go | 31 | ||||
-rw-r--r-- | core/types/common.go | 11 | ||||
-rw-r--r-- | core/types/derive_sha.go | 20 | ||||
-rw-r--r-- | core/types/receipt.go | 68 | ||||
-rw-r--r-- | core/types/transaction.go | 225 | ||||
-rw-r--r-- | core/types/transaction_test.go | 1 | ||||
-rw-r--r-- | core/vm_env.go | 63 |
26 files changed, 3046 insertions, 0 deletions
diff --git a/core/.gitignore b/core/.gitignore new file mode 100644 index 000000000..f725d58d1 --- /dev/null +++ b/core/.gitignore @@ -0,0 +1,12 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. +# +# If you find yourself ignoring temporary files generated by your text editor +# or operating system, you probably want to add a global ignore instead: +# git config --global core.excludesfile ~/.gitignore_global + +/tmp +*/**/*un~ +*un~ +.DS_Store +*/**/.DS_Store + diff --git a/core/asm.go b/core/asm.go new file mode 100644 index 000000000..a8b3023f4 --- /dev/null +++ b/core/asm.go @@ -0,0 +1,50 @@ +package core + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/vm" +) + +func Disassemble(script []byte) (asm []string) { + pc := new(big.Int) + for { + if pc.Cmp(big.NewInt(int64(len(script)))) >= 0 { + return + } + + // Get the memory location of pc + val := script[pc.Int64()] + // Get the opcode (it must be an opcode!) + op := vm.OpCode(val) + + asm = append(asm, fmt.Sprintf("%04v: %v", pc, op)) + + switch op { + case vm.PUSH1, vm.PUSH2, vm.PUSH3, vm.PUSH4, vm.PUSH5, vm.PUSH6, vm.PUSH7, vm.PUSH8, + vm.PUSH9, vm.PUSH10, vm.PUSH11, vm.PUSH12, vm.PUSH13, vm.PUSH14, vm.PUSH15, + vm.PUSH16, vm.PUSH17, vm.PUSH18, vm.PUSH19, vm.PUSH20, vm.PUSH21, vm.PUSH22, + vm.PUSH23, vm.PUSH24, vm.PUSH25, vm.PUSH26, vm.PUSH27, vm.PUSH28, vm.PUSH29, + vm.PUSH30, vm.PUSH31, vm.PUSH32: + pc.Add(pc, ethutil.Big1) + a := int64(op) - int64(vm.PUSH1) + 1 + if int(pc.Int64()+a) > len(script) { + return + } + + data := script[pc.Int64() : pc.Int64()+a] + if len(data) == 0 { + data = []byte{0} + } + asm = append(asm, fmt.Sprintf("%04v: 0x%x", pc, data)) + + pc.Add(pc, big.NewInt(a-1)) + } + + pc.Add(pc, ethutil.Big1) + } + + return asm +} diff --git a/core/block_manager.go b/core/block_manager.go new file mode 100644 index 000000000..f0b78b675 --- /dev/null +++ b/core/block_manager.go @@ -0,0 +1,414 @@ +package core + +import ( + "bytes" + "container/list" + "errors" + "fmt" + "math/big" + "sync" + "time" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/state" + "github.com/ethereum/go-ethereum/wire" +) + +var statelogger = logger.NewLogger("BLOCK") + +type Peer interface { + Inbound() bool + LastSend() time.Time + LastPong() int64 + Host() []byte + Port() uint16 + Version() string + PingTime() string + Connected() *int32 + Caps() *ethutil.Value +} + +type EthManager interface { + BlockManager() *BlockManager + ChainManager() *ChainManager + TxPool() *TxPool + Broadcast(msgType wire.MsgType, data []interface{}) + PeerCount() int + IsMining() bool + IsListening() bool + Peers() *list.List + KeyManager() *crypto.KeyManager + ClientIdentity() wire.ClientIdentity + Db() ethutil.Database + EventMux() *event.TypeMux +} + +type BlockManager struct { + // 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 + // The ethereum manager interface + eth EthManager + // The managed states + // Transiently state. The trans state isn't ever saved, validated and + // it could be used for setting account nonces without effecting + // the main states. + transState *state.State + // Mining state. The mining state is used purely and solely by the mining + // operation. + miningState *state.State + + // 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 +} + +func NewBlockManager(ethereum EthManager) *BlockManager { + sm := &BlockManager{ + mem: make(map[string]*big.Int), + Pow: &EasyPow{}, + eth: ethereum, + bc: ethereum.ChainManager(), + } + sm.transState = ethereum.ChainManager().CurrentBlock.State().Copy() + sm.miningState = ethereum.ChainManager().CurrentBlock.State().Copy() + + return sm +} + +func (self *BlockManager) Start() { + statelogger.Debugln("Starting block manager") +} + +func (self *BlockManager) Stop() { + statelogger.Debugln("Stopping state manager") +} + +func (sm *BlockManager) CurrentState() *state.State { + return sm.eth.ChainManager().CurrentBlock.State() +} + +func (sm *BlockManager) TransState() *state.State { + return sm.transState +} + +func (sm *BlockManager) MiningState() *state.State { + return sm.miningState +} + +func (sm *BlockManager) NewMiningState() *state.State { + sm.miningState = sm.eth.ChainManager().CurrentBlock.State().Copy() + + return sm.miningState +} + +func (sm *BlockManager) ChainManager() *ChainManager { + return sm.bc +} + +func (sm *BlockManager) TransitionState(statedb *state.State, parent, block *types.Block) (receipts types.Receipts, err error) { + coinbase := statedb.GetOrNewStateObject(block.Coinbase) + coinbase.SetGasPool(block.CalcGasLimit(parent)) + + // Process the transactions on to current block + receipts, _, _, _, err = sm.ProcessTransactions(coinbase, statedb, block, parent, block.Transactions()) + if err != nil { + return nil, err + } + + return receipts, nil +} + +func (self *BlockManager) ProcessTransactions(coinbase *state.StateObject, state *state.State, block, parent *types.Block, txs types.Transactions) (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) + ) + +done: + for i, tx := range txs { + // If we are mining this block and validating we want to set the logs back to 0 + state.EmptyLogs() + + txGas := new(big.Int).Set(tx.Gas) + + cb := state.GetStateObject(coinbase.Address()) + st := NewStateTransition(cb, tx, state, block) + err = st.TransitionState() + if err != nil { + statelogger.Infoln(err) + switch { + case IsNonceErr(err): + err = nil // ignore error + continue + case IsGasLimitErr(err): + unhandled = txs[i:] + + break done + default: + statelogger.Infoln(err) + erroneous = append(erroneous, tx) + err = nil + continue + } + } + + txGas.Sub(txGas, st.gas) + cumulativeSum.Add(cumulativeSum, new(big.Int).Mul(txGas, tx.GasPrice)) + + // Update the state with pending changes + state.Update(txGas) + + cumulative := new(big.Int).Set(totalUsedGas.Add(totalUsedGas, txGas)) + receipt := types.NewReceipt(state.Root(), cumulative) + receipt.SetLogs(state.Logs()) + receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) + + // Notify all subscribers + go self.eth.EventMux().Post(TxPostEvent{tx}) + + receipts = append(receipts, receipt) + handled = append(handled, tx) + + if ethutil.Config.Diff && ethutil.Config.DiffType == "all" { + state.CreateOutputForDiff() + } + } + + block.Reward = cumulativeSum + block.GasUsed = totalUsedGas + + return receipts, handled, unhandled, erroneous, err +} + +func (sm *BlockManager) Process(block *types.Block) (td *big.Int, msgs state.Messages, err error) { + // Processing a blocks may never happen simultaneously + sm.mutex.Lock() + defer sm.mutex.Unlock() + + if sm.bc.HasBlock(block.Hash()) { + return nil, nil, &KnownBlockError{block.Number, block.Hash()} + } + + if !sm.bc.HasBlock(block.PrevHash) { + return nil, nil, ParentError(block.PrevHash) + } + parent := sm.bc.GetBlock(block.PrevHash) + + return sm.ProcessWithParent(block, parent) +} + +func (sm *BlockManager) ProcessWithParent(block, parent *types.Block) (td *big.Int, messages state.Messages, err error) { + sm.lastAttemptedBlock = block + + state := parent.State().Copy() + + // Defer the Undo on the Trie. If the block processing happened + // we don't want to undo but since undo only happens on dirty + // nodes this won't happen because Commit would have been called + // before that. + defer state.Reset() + + if ethutil.Config.Diff && ethutil.Config.DiffType == "all" { + fmt.Printf("## %x %x ##\n", block.Hash(), block.Number) + } + + _, err = sm.TransitionState(state, parent, block) + if err != nil { + return + } + + txSha := types.DeriveSha(block.Transactions()) + if bytes.Compare(txSha, block.TxSha) != 0 { + err = fmt.Errorf("validating transaction root. received=%x got=%x", block.TxSha, txSha) + return + } + + /* + receiptSha := types.DeriveSha(receipts) + if bytes.Compare(receiptSha, block.ReceiptSha) != 0 { + err = fmt.Errorf("validating receipt root. received=%x got=%x", block.ReceiptSha, receiptSha) + return + } + */ + + // Block validation + if err = sm.ValidateBlock(block, parent); err != nil { + return + } + + if err = sm.AccumelateRewards(state, block, parent); err != nil { + return + } + + /* + //block.receipts = receipts // although this isn't necessary it be in the future + rbloom := types.CreateBloom(receipts) + if bytes.Compare(rbloom, block.LogsBloom) != 0 { + err = fmt.Errorf("unable to replicate block's bloom=%x", rbloom) + return + } + */ + + state.Update(ethutil.Big0) + + if !block.State().Cmp(state) { + err = fmt.Errorf("invalid merkle root. received=%x got=%x", block.Root(), state.Root()) + return + } + + // Calculate the new total difficulty and sync back to the db + if td, ok := sm.CalculateTD(block); ok { + // Sync the current block's state to the database and cancelling out the deferred Undo + state.Sync() + + messages := state.Manifest().Messages + state.Manifest().Reset() + + chainlogger.Infof("Processed block #%d (%x...)\n", block.Number, block.Hash()[0:4]) + + sm.transState = state.Copy() + + sm.eth.TxPool().RemoveSet(block.Transactions()) + + return td, messages, nil + } else { + return nil, nil, errors.New("total diff failed") + } +} + +func (sm *BlockManager) CalculateTD(block *types.Block) (*big.Int, bool) { + 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(sm.bc.TD, uncleDiff) + td = td.Add(td, block.Difficulty) + + // The new TD will only be accepted if the new difficulty is + // is greater than the previous. + if td.Cmp(sm.bc.TD) > 0 { + return td, true + } + + return nil, false +} + +// 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 *BlockManager) ValidateBlock(block, parent *types.Block) error { + expd := CalcDifficulty(block, parent) + if expd.Cmp(block.Difficulty) < 0 { + return fmt.Errorf("Difficulty check failed for block %v, %v", block.Difficulty, expd) + } + + diff := block.Time - parent.Time + if diff < 0 { + return ValidationError("Block timestamp less then prev block %v (%v - %v)", diff, block.Time, sm.bc.CurrentBlock.Time) + } + + /* XXX + // New blocks must be within the 15 minute range of the last block. + if diff > int64(15*time.Minute) { + return ValidationError("Block is too far in the future of last block (> 15 minutes)") + } + */ + + // Verify the nonce of the block. Return an error if it's not valid + if !sm.Pow.Verify(block.HashNoNonce(), block.Difficulty, block.Nonce) { + return ValidationError("Block's nonce is invalid (= %v)", ethutil.Bytes2Hex(block.Nonce)) + } + + return nil +} + +func (sm *BlockManager) AccumelateRewards(statedb *state.State, block, parent *types.Block) error { + reward := new(big.Int).Set(BlockReward) + + knownUncles := ethutil.Set(parent.Uncles) + nonces := ethutil.NewSet(block.Nonce) + for _, uncle := range block.Uncles { + if nonces.Include(uncle.Nonce) { + // Error not unique + return UncleError("Uncle not unique") + } + + uncleParent := sm.bc.GetBlock(uncle.PrevHash) + if uncleParent == nil { + return UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.PrevHash[0:4])) + } + + if uncleParent.Number.Cmp(new(big.Int).Sub(parent.Number, big.NewInt(6))) < 0 { + return UncleError("Uncle too old") + } + + if knownUncles.Include(uncle.Hash()) { + return UncleError("Uncle in chain") + } + + nonces.Insert(uncle.Nonce) + + r := new(big.Int) + r.Mul(BlockReward, big.NewInt(15)).Div(r, big.NewInt(16)) + + uncleAccount := statedb.GetAccount(uncle.Coinbase) + uncleAccount.AddAmount(r) + + reward.Add(reward, new(big.Int).Div(BlockReward, big.NewInt(32))) + } + + // Get the account associated with the coinbase + account := statedb.GetAccount(block.Coinbase) + // Reward amount of ether to the coinbase address + account.AddAmount(reward) + + statedb.Manifest().AddMessage(&state.Message{ + To: block.Coinbase, From: block.Coinbase, + Input: nil, + Origin: nil, + Block: block.Hash(), Timestamp: block.Time, Coinbase: block.Coinbase, Number: block.Number, + Value: new(big.Int).Add(reward, block.Reward), + }) + + return nil +} + +func (sm *BlockManager) GetMessages(block *types.Block) (messages []*state.Message, err error) { + if !sm.bc.HasBlock(block.PrevHash) { + return nil, ParentError(block.PrevHash) + } + + sm.lastAttemptedBlock = block + + var ( + parent = sm.bc.GetBlock(block.PrevHash) + state = parent.State().Copy() + ) + + defer state.Reset() + + sm.TransitionState(state, parent, block) + sm.AccumelateRewards(state, block, parent) + + return state.Manifest().Messages, nil +} diff --git a/core/chain_manager.go b/core/chain_manager.go new file mode 100644 index 000000000..7acd171ec --- /dev/null +++ b/core/chain_manager.go @@ -0,0 +1,276 @@ +package core + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/logger" +) + +var chainlogger = logger.NewLogger("CHAIN") + +func AddTestNetFunds(block *types.Block) { + for _, addr := range []string{ + "51ba59315b3a95761d0863b05ccc7a7f54703d99", + "e4157b34ea9615cfbde6b4fda419828124b70c78", + "b9c015918bdaba24b4ff057a92a3873d6eb201be", + "6c386a4b26f73c802f34673f7248bb118f97424a", + "cd2a3d9f938e13cd947ec05abc7fe734df8dd826", + "2ef47100e0787b915105fd5e3f4ff6752079d5cb", + "e6716f9544a56c530d868e4bfbacb172315bdead", + "1a26338f0d905e295fccb71fa9ea849ffa12aaf4", + } { + codedAddr := ethutil.Hex2Bytes(addr) + account := block.State().GetAccount(codedAddr) + account.SetBalance(ethutil.Big("1606938044258990275541962092341162602522202993782792835301376")) //ethutil.BigPow(2, 200) + block.State().UpdateStateObject(account) + } +} + +func CalcDifficulty(block, parent *types.Block) *big.Int { + diff := new(big.Int) + + adjust := new(big.Int).Rsh(parent.Difficulty, 10) + if block.Time >= parent.Time+5 { + diff.Sub(parent.Difficulty, adjust) + } else { + diff.Add(parent.Difficulty, adjust) + } + + return diff +} + +type ChainManager struct { + //eth EthManager + processor types.BlockProcessor + eventMux *event.TypeMux + genesisBlock *types.Block + // Last known total difficulty + TD *big.Int + + LastBlockNumber uint64 + + CurrentBlock *types.Block + LastBlockHash []byte +} + +func NewChainManager(mux *event.TypeMux) *ChainManager { + bc := &ChainManager{} + bc.genesisBlock = types.NewBlockFromBytes(ethutil.Encode(Genesis)) + bc.eventMux = mux + + bc.setLastBlock() + + return bc +} + +func (self *ChainManager) SetProcessor(proc types.BlockProcessor) { + self.processor = proc +} + +func (bc *ChainManager) setLastBlock() { + data, _ := ethutil.Config.Db.Get([]byte("LastBlock")) + if len(data) != 0 { + // Prep genesis + AddTestNetFunds(bc.genesisBlock) + + block := types.NewBlockFromBytes(data) + bc.CurrentBlock = block + bc.LastBlockHash = block.Hash() + bc.LastBlockNumber = block.Number.Uint64() + + // Set the last know difficulty (might be 0x0 as initial value, Genesis) + bc.TD = ethutil.BigD(ethutil.Config.Db.LastKnownTD()) + } else { + bc.Reset() + } + + chainlogger.Infof("Last block (#%d) %x\n", bc.LastBlockNumber, bc.CurrentBlock.Hash()) +} + +// Block creation & chain handling +func (bc *ChainManager) NewBlock(coinbase []byte) *types.Block { + var root interface{} + hash := ZeroHash256 + + if bc.CurrentBlock != nil { + root = bc.CurrentBlock.Root() + hash = bc.LastBlockHash + } + + block := types.CreateBlock( + root, + hash, + coinbase, + ethutil.BigPow(2, 32), + nil, + "") + + parent := bc.CurrentBlock + if parent != nil { + block.Difficulty = CalcDifficulty(block, parent) + block.Number = new(big.Int).Add(bc.CurrentBlock.Number, ethutil.Big1) + block.GasLimit = block.CalcGasLimit(bc.CurrentBlock) + + } + + return block +} + +func (bc *ChainManager) Reset() { + AddTestNetFunds(bc.genesisBlock) + + bc.genesisBlock.Trie().Sync() + // Prepare the genesis block + bc.add(bc.genesisBlock) + bc.CurrentBlock = bc.genesisBlock + + bc.SetTotalDifficulty(ethutil.Big("0")) + + // Set the last know difficulty (might be 0x0 as initial value, Genesis) + bc.TD = ethutil.BigD(ethutil.Config.Db.LastKnownTD()) +} + +// Add a block to the chain and record addition information +func (bc *ChainManager) add(block *types.Block) { + bc.writeBlockInfo(block) + + bc.CurrentBlock = block + bc.LastBlockHash = block.Hash() + + encodedBlock := block.RlpEncode() + ethutil.Config.Db.Put(block.Hash(), encodedBlock) + ethutil.Config.Db.Put([]byte("LastBlock"), encodedBlock) + + //chainlogger.Infof("Imported block #%d (%x...)\n", block.Number, block.Hash()[0:4]) +} + +// Accessors +func (bc *ChainManager) Genesis() *types.Block { + return bc.genesisBlock +} + +// Block fetching methods +func (bc *ChainManager) HasBlock(hash []byte) bool { + data, _ := ethutil.Config.Db.Get(hash) + return len(data) != 0 +} + +func (self *ChainManager) GetChainHashesFromHash(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++ { + + chain = append(chain, block.Hash()) + + if block.Number.Cmp(ethutil.Big0) <= 0 { + break + } + + block = self.GetBlock(block.PrevHash) + } + + return +} + +func (self *ChainManager) GetBlock(hash []byte) *types.Block { + data, _ := ethutil.Config.Db.Get(hash) + if len(data) == 0 { + return nil + } + + return types.NewBlockFromBytes(data) +} + +func (self *ChainManager) GetBlockByNumber(num uint64) *types.Block { + block := self.CurrentBlock + for ; block != nil; block = self.GetBlock(block.PrevHash) { + if block.Number.Uint64() == num { + break + } + } + + if block != nil && block.Number.Uint64() == 0 && num != 0 { + return nil + } + + return block +} + +func (bc *ChainManager) SetTotalDifficulty(td *big.Int) { + ethutil.Config.Db.Put([]byte("LTD"), td.Bytes()) + bc.TD = td +} + +func (self *ChainManager) CalcTotalDiff(block *types.Block) (*big.Int, error) { + parent := self.GetBlock(block.PrevHash) + if parent == nil { + return nil, fmt.Errorf("Unable to calculate total diff without known parent %x", block.PrevHash) + } + + parentTd := parent.BlockInfo().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.Difficulty) + + return td, nil +} + +func (bc *ChainManager) BlockInfo(block *types.Block) types.BlockInfo { + bi := types.BlockInfo{} + data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...)) + bi.RlpDecode(data) + + return bi +} + +// Unexported method for writing extra non-essential block info to the db +func (bc *ChainManager) writeBlockInfo(block *types.Block) { + bc.LastBlockNumber++ + bi := types.BlockInfo{Number: bc.LastBlockNumber, Hash: block.Hash(), Parent: block.PrevHash, TD: bc.TD} + + // For now we use the block hash with the words "info" appended as key + ethutil.Config.Db.Put(append(block.Hash(), []byte("Info")...), bi.RlpEncode()) +} + +func (bc *ChainManager) Stop() { + if bc.CurrentBlock != nil { + chainlogger.Infoln("Stopped") + } +} + +func (self *ChainManager) InsertChain(chain types.Blocks) error { + for _, block := range chain { + td, messages, err := self.processor.Process(block) + if err != nil { + if IsKnownBlockErr(err) { + continue + } + + chainlogger.Infof("block #%v process failed (%x)\n", block.Number, block.Hash()[:4]) + chainlogger.Infoln(block) + chainlogger.Infoln(err) + return err + } + + self.add(block) + self.SetTotalDifficulty(td) + self.eventMux.Post(NewBlockEvent{block}) + self.eventMux.Post(messages) + } + + return nil +} diff --git a/core/chain_manager_test.go b/core/chain_manager_test.go new file mode 100644 index 000000000..51e70fb12 --- /dev/null +++ b/core/chain_manager_test.go @@ -0,0 +1,116 @@ +package core + +import ( + "fmt" + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum/chain/types" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/state" +) + +var TD *big.Int + +func init() { + ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "") + ethutil.Config.Db, _ = ethdb.NewMemDatabase() +} + +type fakeproc struct { +} + +func (self fakeproc) ProcessWithParent(a, b *types.Block) (*big.Int, state.Messages, error) { + TD = new(big.Int).Add(TD, big.NewInt(1)) + return TD, nil, nil +} + +func makechain(cman *ChainManager, max int) *BlockChain { + blocks := make(types.Blocks, max) + for i := 0; i < max; i++ { + addr := ethutil.LeftPadBytes([]byte{byte(i)}, 20) + block := cman.NewBlock(addr) + if i != 0 { + cman.CurrentBlock = blocks[i-1] + } + blocks[i] = block + } + return NewChain(blocks) +} + +func TestLongerFork(t *testing.T) { + cman := NewChainManager() + cman.SetProcessor(fakeproc{}) + + TD = big.NewInt(1) + chainA := makechain(cman, 5) + + TD = big.NewInt(1) + chainB := makechain(cman, 10) + + td, err := cman.TestChain(chainA) + if err != nil { + t.Error("unable to create new TD from chainA:", err) + } + cman.TD = td + + _, err = cman.TestChain(chainB) + if err != nil { + t.Error("expected chainB not to give errors:", err) + } +} + +func TestEqualFork(t *testing.T) { + cman := NewChainManager() + cman.SetProcessor(fakeproc{}) + + TD = big.NewInt(1) + chainA := makechain(cman, 5) + + TD = big.NewInt(2) + chainB := makechain(cman, 5) + + td, err := cman.TestChain(chainA) + if err != nil { + t.Error("unable to create new TD from chainA:", err) + } + cman.TD = td + + _, err = cman.TestChain(chainB) + if err != nil { + t.Error("expected chainB not to give errors:", err) + } +} + +func TestBrokenChain(t *testing.T) { + cman := NewChainManager() + cman.SetProcessor(fakeproc{}) + + TD = big.NewInt(1) + chain := makechain(cman, 5) + chain.Remove(chain.Front()) + + _, err := cman.TestChain(chain) + if err == nil { + t.Error("expected broken chain to return error") + } +} + +func BenchmarkChainTesting(b *testing.B) { + const chainlen = 1000 + + ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "") + ethutil.Config.Db, _ = ethdb.NewMemDatabase() + + cman := NewChainManager() + cman.SetProcessor(fakeproc{}) + + TD = big.NewInt(1) + chain := makechain(cman, chainlen) + + stime := time.Now() + cman.TestChain(chain) + fmt.Println(chainlen, "took", time.Since(stime)) +} diff --git a/core/dagger.go b/core/dagger.go new file mode 100644 index 000000000..8a042b34f --- /dev/null +++ b/core/dagger.go @@ -0,0 +1,245 @@ +package core + +import ( + "hash" + "math/big" + "math/rand" + "time" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/logger" + "github.com/obscuren/sha3" +) + +var powlogger = logger.NewLogger("POW") + +type PoW interface { + Search(block *types.Block, stop <-chan struct{}) []byte + Verify(hash []byte, diff *big.Int, nonce []byte) bool + GetHashrate() int64 + Turbo(bool) +} + +type EasyPow struct { + hash *big.Int + HashRate int64 + turbo bool +} + +func (pow *EasyPow) GetHashrate() int64 { + return pow.HashRate +} + +func (pow *EasyPow) Turbo(on bool) { + pow.turbo = on +} + +func (pow *EasyPow) Search(block *types.Block, stop <-chan struct{}) []byte { + r := rand.New(rand.NewSource(time.Now().UnixNano())) + hash := block.HashNoNonce() + diff := block.Difficulty + i := int64(0) + start := time.Now().UnixNano() + t := time.Now() + + for { + select { + case <-stop: + powlogger.Infoln("Breaking from mining") + pow.HashRate = 0 + return nil + default: + i++ + + if time.Since(t) > (1 * time.Second) { + elapsed := time.Now().UnixNano() - start + hashes := ((float64(1e9) / float64(elapsed)) * float64(i)) / 1000 + pow.HashRate = int64(hashes) + powlogger.Infoln("Hashing @", pow.HashRate, "khash") + + t = time.Now() + } + + sha := crypto.Sha3(big.NewInt(r.Int63()).Bytes()) + if pow.Verify(hash, diff, sha) { + return sha + } + } + + if !pow.turbo { + time.Sleep(20 * time.Microsecond) + } + } + + return nil +} + +func (pow *EasyPow) Verify(hash []byte, diff *big.Int, nonce []byte) bool { + sha := sha3.NewKeccak256() + + d := append(hash, nonce...) + sha.Write(d) + + verification := new(big.Int).Div(ethutil.BigPow(2, 256), diff) + res := ethutil.U256(ethutil.BigD(sha.Sum(nil))) + + /* + fmt.Printf("hash w/o nonce %x\n", hash) + fmt.Printf("2**256 / %v = %v\n", diff, verification) + fmt.Printf("%v <= %v\n", res, verification) + fmt.Printf("vlen: %d rlen: %d\n", len(verification.Bytes()), len(res.Bytes())) + */ + + return res.Cmp(verification) <= 0 +} + +func (pow *EasyPow) SetHash(hash *big.Int) { +} + +type Dagger struct { + hash *big.Int + xn *big.Int +} + +var Found bool + +func (dag *Dagger) Find(obj *big.Int, resChan chan int64) { + r := rand.New(rand.NewSource(time.Now().UnixNano())) + + for i := 0; i < 1000; i++ { + rnd := r.Int63() + + res := dag.Eval(big.NewInt(rnd)) + powlogger.Infof("rnd %v\nres %v\nobj %v\n", rnd, res, obj) + if res.Cmp(obj) < 0 { + // Post back result on the channel + resChan <- rnd + // Notify other threads we've found a valid nonce + Found = true + } + + // Break out if found + if Found { + break + } + } + + resChan <- 0 +} + +func (dag *Dagger) Search(hash, diff *big.Int) *big.Int { + // TODO fix multi threading. Somehow it results in the wrong nonce + amountOfRoutines := 1 + + dag.hash = hash + + obj := ethutil.BigPow(2, 256) + obj = obj.Div(obj, diff) + + Found = false + resChan := make(chan int64, 3) + var res int64 + + for k := 0; k < amountOfRoutines; k++ { + go dag.Find(obj, resChan) + + // Wait for each go routine to finish + } + for k := 0; k < amountOfRoutines; k++ { + // Get the result from the channel. 0 = quit + if r := <-resChan; r != 0 { + res = r + } + } + + return big.NewInt(res) +} + +func (dag *Dagger) Verify(hash, diff, nonce *big.Int) bool { + dag.hash = hash + + obj := ethutil.BigPow(2, 256) + obj = obj.Div(obj, diff) + + return dag.Eval(nonce).Cmp(obj) < 0 +} + +func DaggerVerify(hash, diff, nonce *big.Int) bool { + dagger := &Dagger{} + dagger.hash = hash + + obj := ethutil.BigPow(2, 256) + obj = obj.Div(obj, diff) + + return dagger.Eval(nonce).Cmp(obj) < 0 +} + +func (dag *Dagger) Node(L uint64, i uint64) *big.Int { + if L == i { + return dag.hash + } + + var m *big.Int + if L == 9 { + m = big.NewInt(16) + } else { + m = big.NewInt(3) + } + + sha := sha3.NewKeccak256() + sha.Reset() + d := sha3.NewKeccak256() + b := new(big.Int) + ret := new(big.Int) + + for k := 0; k < int(m.Uint64()); k++ { + d.Reset() + d.Write(dag.hash.Bytes()) + d.Write(dag.xn.Bytes()) + d.Write(big.NewInt(int64(L)).Bytes()) + d.Write(big.NewInt(int64(i)).Bytes()) + d.Write(big.NewInt(int64(k)).Bytes()) + + b.SetBytes(Sum(d)) + pk := b.Uint64() & ((1 << ((L - 1) * 3)) - 1) + sha.Write(dag.Node(L-1, pk).Bytes()) + } + + ret.SetBytes(Sum(sha)) + + return ret +} + +func Sum(sha hash.Hash) []byte { + //in := make([]byte, 32) + return sha.Sum(nil) +} + +func (dag *Dagger) Eval(N *big.Int) *big.Int { + pow := ethutil.BigPow(2, 26) + dag.xn = pow.Div(N, pow) + + sha := sha3.NewKeccak256() + sha.Reset() + ret := new(big.Int) + + for k := 0; k < 4; k++ { + d := sha3.NewKeccak256() + b := new(big.Int) + + d.Reset() + d.Write(dag.hash.Bytes()) + d.Write(dag.xn.Bytes()) + d.Write(N.Bytes()) + d.Write(big.NewInt(int64(k)).Bytes()) + + b.SetBytes(Sum(d)) + pk := (b.Uint64() & 0x1ffffff) + + sha.Write(dag.Node(9, pk).Bytes()) + } + + return ret.SetBytes(Sum(sha)) +} diff --git a/core/dagger_test.go b/core/dagger_test.go new file mode 100644 index 000000000..e80064e6b --- /dev/null +++ b/core/dagger_test.go @@ -0,0 +1,19 @@ +package core + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/ethutil" +) + +func BenchmarkDaggerSearch(b *testing.B) { + hash := big.NewInt(0) + diff := ethutil.BigPow(2, 36) + o := big.NewInt(0) // nonce doesn't matter. We're only testing against speed, not validity + + // Reset timer so the big generation isn't included in the benchmark + b.ResetTimer() + // Validate + DaggerVerify(hash, diff, o) +} diff --git a/core/error.go b/core/error.go new file mode 100644 index 000000000..11d8c1653 --- /dev/null +++ b/core/error.go @@ -0,0 +1,141 @@ +package core + +import ( + "fmt" + "math/big" +) + +// Parent error. In case a parent is unknown this error will be thrown +// by the block manager +type ParentErr struct { + Message string +} + +func (err *ParentErr) Error() string { + return err.Message +} + +func ParentError(hash []byte) error { + return &ParentErr{Message: fmt.Sprintf("Block's parent unkown %x", hash)} +} + +func IsParentErr(err error) bool { + _, ok := err.(*ParentErr) + + return ok +} + +type UncleErr struct { + Message string +} + +func (err *UncleErr) Error() string { + return err.Message +} + +func UncleError(str string) error { + return &UncleErr{Message: str} +} + +func IsUncleErr(err error) bool { + _, ok := err.(*UncleErr) + + return ok +} + +// Block validation error. If any validation fails, this error will be thrown +type ValidationErr struct { + Message string +} + +func (err *ValidationErr) Error() string { + return err.Message +} + +func ValidationError(format string, v ...interface{}) *ValidationErr { + return &ValidationErr{Message: fmt.Sprintf(format, v...)} +} + +func IsValidationErr(err error) bool { + _, ok := err.(*ValidationErr) + + return ok +} + +type GasLimitErr struct { + Message string + Is, Max *big.Int +} + +func IsGasLimitErr(err error) bool { + _, ok := err.(*GasLimitErr) + + return ok +} +func (err *GasLimitErr) Error() string { + return err.Message +} +func GasLimitError(is, max *big.Int) *GasLimitErr { + return &GasLimitErr{Message: fmt.Sprintf("GasLimit error. Max %s, transaction would take it to %s", max, is), Is: is, Max: max} +} + +type NonceErr struct { + Message string + Is, Exp uint64 +} + +func (err *NonceErr) Error() string { + return err.Message +} + +func NonceError(is, exp uint64) *NonceErr { + return &NonceErr{Message: fmt.Sprintf("Nonce err. Is %d, expected %d", is, exp), Is: is, Exp: exp} +} + +func IsNonceErr(err error) bool { + _, ok := err.(*NonceErr) + + return ok +} + +type OutOfGasErr struct { + Message string +} + +func OutOfGasError() *OutOfGasErr { + return &OutOfGasErr{Message: "Out of gas"} +} +func (self *OutOfGasErr) Error() string { + return self.Message +} + +func IsOutOfGasErr(err error) bool { + _, ok := err.(*OutOfGasErr) + + return ok +} + +type TDError struct { + a, b *big.Int +} + +func (self *TDError) Error() string { + return fmt.Sprintf("incoming chain has a lower or equal TD (%v <= %v)", self.a, self.b) +} +func IsTDError(e error) bool { + _, ok := e.(*TDError) + return ok +} + +type KnownBlockError struct { + number *big.Int + hash []byte +} + +func (self *KnownBlockError) Error() string { + return fmt.Sprintf("block %v already known (%x)", self.number, self.hash[0:4]) +} +func IsKnownBlockErr(e error) bool { + _, ok := e.(*KnownBlockError) + return ok +} diff --git a/core/events.go b/core/events.go new file mode 100644 index 000000000..deeba3e98 --- /dev/null +++ b/core/events.go @@ -0,0 +1,12 @@ +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 } diff --git a/core/execution.go b/core/execution.go new file mode 100644 index 000000000..9f9d9a5d9 --- /dev/null +++ b/core/execution.go @@ -0,0 +1,80 @@ +package core + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/state" + "github.com/ethereum/go-ethereum/vm" +) + +type Execution struct { + vm vm.VirtualMachine + address, input []byte + Gas, price, value *big.Int + object *state.StateObject + SkipTransfer bool +} + +func NewExecution(vm vm.VirtualMachine, address, input []byte, gas, gasPrice, value *big.Int) *Execution { + return &Execution{vm: vm, 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.ClosureRef) ([]byte, error) { + // Retrieve the executing code + code := self.vm.Env().State().GetCode(codeAddr) + + return self.exec(code, codeAddr, caller) +} + +func (self *Execution) exec(code, caddr []byte, caller vm.ClosureRef) (ret []byte, err error) { + env := self.vm.Env() + + chainlogger.Debugf("pre state %x\n", env.State().Root()) + snapshot := env.State().Copy() + defer func() { + if vm.IsDepthErr(err) || vm.IsOOGErr(err) { + env.State().Set(snapshot) + } + chainlogger.Debugf("post state %x\n", env.State().Root()) + }() + + from, to := env.State().GetStateObject(caller.Address()), env.State().GetOrNewStateObject(self.address) + // Skipping transfer is used on testing for the initial call + if !self.SkipTransfer { + err = env.Transfer(from, to, self.value) + } + + if err != nil { + caller.ReturnGas(self.Gas, self.price) + + err = fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.value, from.Balance) + } else { + self.object = to + // Pre-compiled contracts (address.go) 1, 2 & 3. + naddr := ethutil.BigD(caddr).Uint64() + if p := vm.Precompiled[naddr]; p != nil { + if self.Gas.Cmp(p.Gas) >= 0 { + ret = p.Call(self.input) + self.vm.Printf("NATIVE_FUNC(%x) => %x", naddr, ret) + self.vm.Endl() + } + } else { + ret, err = self.vm.Run(to, caller, code, self.value, self.Gas, self.price, self.input) + } + } + + return +} + +func (self *Execution) Create(caller vm.ClosureRef) (ret []byte, err error, account *state.StateObject) { + ret, err = self.exec(self.input, nil, caller) + account = self.vm.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..fe3665bf3 --- /dev/null +++ b/core/filter.go @@ -0,0 +1,200 @@ +package core + +import ( + "bytes" + "math" + "math/big" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/state" +) + +type AccountChange struct { + Address, StateAddress []byte +} + +// Filtering interface +type Filter struct { + eth EthManager + earliest int64 + latest int64 + skip int + from, to [][]byte + max int + + Altered []AccountChange + + BlockCallback func(*types.Block) + MessageCallback func(state.Messages) +} + +// 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 EthManager) *Filter { + return &Filter{eth: eth} +} + +func (self *Filter) AddAltered(address, stateAddress []byte) { + self.Altered = append(self.Altered, AccountChange{address, stateAddress}) +} + +// 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) SetFrom(addr [][]byte) { + self.from = addr +} + +func (self *Filter) AddFrom(addr []byte) { + self.from = append(self.from, addr) +} + +func (self *Filter) SetTo(addr [][]byte) { + self.to = addr +} + +func (self *Filter) AddTo(addr []byte) { + self.to = append(self.to, addr) +} + +func (self *Filter) SetMax(max int) { + self.max = max +} + +func (self *Filter) SetSkip(skip int) { + self.skip = skip +} + +// Run filters messages with the current parameters set +func (self *Filter) Find() []*state.Message { + var earliestBlockNo uint64 = uint64(self.earliest) + if self.earliest == -1 { + earliestBlockNo = self.eth.ChainManager().CurrentBlock.Number.Uint64() + } + var latestBlockNo uint64 = uint64(self.latest) + if self.latest == -1 { + latestBlockNo = self.eth.ChainManager().CurrentBlock.Number.Uint64() + } + + var ( + messages []*state.Message + block = self.eth.ChainManager().GetBlockByNumber(latestBlockNo) + quit bool + ) + for i := 0; !quit && block != nil; i++ { + // Quit on latest + switch { + case block.Number.Uint64() == earliestBlockNo, block.Number.Uint64() == 0: + quit = true + case self.max <= len(messages): + break + } + + // Use bloom filtering to see if this block is interesting given the + // current parameters + if self.bloomFilter(block) { + // Get the messages of the block + msgs, err := self.eth.BlockManager().GetMessages(block) + if err != nil { + chainlogger.Warnln("err: filter get messages ", err) + + break + } + + messages = append(messages, self.FilterMessages(msgs)...) + } + + block = self.eth.ChainManager().GetBlock(block.PrevHash) + } + + skip := int(math.Min(float64(len(messages)), float64(self.skip))) + + return messages[skip:] +} + +func includes(addresses [][]byte, a []byte) (found bool) { + for _, addr := range addresses { + if bytes.Compare(addr, a) == 0 { + return true + } + } + + return +} + +func (self *Filter) FilterMessages(msgs []*state.Message) []*state.Message { + var messages []*state.Message + + // Filter the messages for interesting stuff + for _, message := range msgs { + if len(self.to) > 0 && !includes(self.to, message.To) { + continue + } + + if len(self.from) > 0 && !includes(self.from, message.From) { + continue + } + + var match bool + if len(self.Altered) == 0 { + match = true + } + + for _, accountChange := range self.Altered { + if len(accountChange.Address) > 0 && bytes.Compare(message.To, accountChange.Address) != 0 { + continue + } + + if len(accountChange.StateAddress) > 0 && !includes(message.ChangedAddresses, accountChange.StateAddress) { + continue + } + + match = true + break + } + + if !match { + continue + } + + messages = append(messages, message) + } + + return messages +} + +func (self *Filter) bloomFilter(block *types.Block) bool { + var fromIncluded, toIncluded bool + if len(self.from) > 0 { + for _, from := range self.from { + if types.BloomLookup(block.LogsBloom, from) || bytes.Equal(block.Coinbase, from) { + fromIncluded = true + break + } + } + } else { + fromIncluded = true + } + + if len(self.to) > 0 { + for _, to := range self.to { + if types.BloomLookup(block.LogsBloom, ethutil.U256(new(big.Int).Add(ethutil.Big1, ethutil.BigD(to))).Bytes()) || bytes.Equal(block.Coinbase, to) { + toIncluded = true + break + } + } + } else { + toIncluded = true + } + + return fromIncluded && toIncluded +} diff --git a/core/filter_test.go b/core/filter_test.go new file mode 100644 index 000000000..d53b835b7 --- /dev/null +++ b/core/filter_test.go @@ -0,0 +1,7 @@ +package core + +// import "testing" + +// func TestFilter(t *testing.T) { +// NewFilter(NewTestManager()) +// } diff --git a/core/genesis.go b/core/genesis.go new file mode 100644 index 000000000..707154759 --- /dev/null +++ b/core/genesis.go @@ -0,0 +1,52 @@ +package core + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethutil" +) + +/* + * This is the special genesis block. + */ + +var ZeroHash256 = make([]byte, 32) +var ZeroHash160 = make([]byte, 20) +var ZeroHash512 = make([]byte, 64) +var EmptyShaList = crypto.Sha3(ethutil.Encode([]interface{}{})) +var EmptyListRoot = crypto.Sha3(ethutil.Encode("")) + +var GenesisHeader = []interface{}{ + // Previous hash (none) + ZeroHash256, + // Empty uncles + EmptyShaList, + // Coinbase + ZeroHash160, + // Root state + EmptyShaList, + // tx root + EmptyListRoot, + // receipt root + EmptyListRoot, + // bloom + ZeroHash512, + // Difficulty + //ethutil.BigPow(2, 22), + big.NewInt(131072), + // Number + ethutil.Big0, + // Block upper gas bound + big.NewInt(1000000), + // Block gas used + ethutil.Big0, + // Time + ethutil.Big0, + // Extra + nil, + // Nonce + crypto.Sha3(big.NewInt(42).Bytes()), +} + +var Genesis = []interface{}{GenesisHeader, []interface{}{}, []interface{}{}} diff --git a/core/helper_test.go b/core/helper_test.go new file mode 100644 index 000000000..2b1510ea4 --- /dev/null +++ b/core/helper_test.go @@ -0,0 +1,93 @@ +package core + +import ( + "container/list" + "fmt" + + "github.com/ethereum/go-ethereum/chain/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/wire" +) + +// Implement our EthTest Manager +type TestManager struct { + // stateManager *StateManager + eventMux *event.TypeMux + + db ethutil.Database + txPool *TxPool + blockChain *ChainManager + Blocks []*types.Block +} + +func (s *TestManager) IsListening() bool { + return false +} + +func (s *TestManager) IsMining() bool { + return false +} + +func (s *TestManager) PeerCount() int { + return 0 +} + +func (s *TestManager) Peers() *list.List { + return list.New() +} + +func (s *TestManager) ChainManager() *ChainManager { + return s.blockChain +} + +func (tm *TestManager) TxPool() *TxPool { + return tm.txPool +} + +// func (tm *TestManager) StateManager() *StateManager { +// return tm.stateManager +// } + +func (tm *TestManager) EventMux() *event.TypeMux { + return tm.eventMux +} +func (tm *TestManager) Broadcast(msgType wire.MsgType, data []interface{}) { + fmt.Println("Broadcast not implemented") +} + +func (tm *TestManager) ClientIdentity() wire.ClientIdentity { + return nil +} +func (tm *TestManager) KeyManager() *crypto.KeyManager { + return nil +} + +func (tm *TestManager) Db() ethutil.Database { + return tm.db +} + +func NewTestManager() *TestManager { + ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "ETH") + + db, err := ethdb.NewMemDatabase() + if err != nil { + fmt.Println("Could not create mem-db, failing") + return nil + } + ethutil.Config.Db = db + + 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/state_transition.go b/core/state_transition.go new file mode 100644 index 000000000..e3eea57de --- /dev/null +++ b/core/state_transition.go @@ -0,0 +1,198 @@ +package core + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/state" + "github.com/ethereum/go-ethereum/vm" +) + +/* + * 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, receiver []byte + tx *types.Transaction + gas, gasPrice *big.Int + value *big.Int + data []byte + state *state.State + block *types.Block + + cb, rec, sen *state.StateObject +} + +func NewStateTransition(coinbase *state.StateObject, tx *types.Transaction, state *state.State, block *types.Block) *StateTransition { + return &StateTransition{coinbase.Address(), tx.Recipient, tx, new(big.Int), new(big.Int).Set(tx.GasPrice), tx.Value, tx.Data, state, block, coinbase, nil, nil} +} + +func (self *StateTransition) Coinbase() *state.StateObject { + if self.cb != nil { + return self.cb + } + + self.cb = self.state.GetOrNewStateObject(self.coinbase) + return self.cb +} +func (self *StateTransition) Sender() *state.StateObject { + if self.sen != nil { + return self.sen + } + + self.sen = self.state.GetOrNewStateObject(self.tx.Sender()) + + return self.sen +} +func (self *StateTransition) Receiver() *state.StateObject { + if self.tx != nil && self.tx.CreatesContract() { + return nil + } + + if self.rec != nil { + return self.rec + } + + self.rec = self.state.GetOrNewStateObject(self.tx.Recipient) + return self.rec +} + +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.Sender() + if sender.Balance().Cmp(self.tx.GasValue()) < 0 { + return fmt.Errorf("Insufficient funds to pre-pay gas. Req %v, has %v", self.tx.GasValue(), sender.Balance()) + } + + coinbase := self.Coinbase() + err = coinbase.BuyGas(self.tx.Gas, self.tx.GasPrice) + if err != nil { + return err + } + + self.AddGas(self.tx.Gas) + sender.SubAmount(self.tx.GasValue()) + + return nil +} + +func (self *StateTransition) RefundGas() { + coinbase, sender := self.Coinbase(), self.Sender() + coinbase.RefundGas(self.gas, self.tx.GasPrice) + + // Return remaining gas + remaining := new(big.Int).Mul(self.gas, self.tx.GasPrice) + sender.AddAmount(remaining) +} + +func (self *StateTransition) preCheck() (err error) { + var ( + tx = self.tx + sender = self.Sender() + ) + + // Make sure this transaction's nonce is correct + if sender.Nonce != tx.Nonce { + return NonceError(tx.Nonce, sender.Nonce) + } + + // Pre-pay gas / Buy gas of the coinbase account + if err = self.BuyGas(); err != nil { + return err + } + + return nil +} + +func (self *StateTransition) TransitionState() (err error) { + statelogger.Debugf("(~) %x\n", self.tx.Hash()) + + // XXX Transactions after this point are considered valid. + if err = self.preCheck(); err != nil { + return + } + + var ( + tx = self.tx + sender = self.Sender() + receiver *state.StateObject + ) + + defer self.RefundGas() + + // Increment the nonce for the next transaction + sender.Nonce += 1 + + // Transaction gas + if err = self.UseGas(vm.GasTx); err != nil { + return + } + + // Pay data gas + var dgas int64 + for _, byt := range self.data { + if byt != 0 { + dgas += vm.GasData.Int64() + } else { + dgas += 1 // This is 1/5. If GasData changes this fails + } + } + if err = self.UseGas(big.NewInt(dgas)); err != nil { + return + } + + var ret []byte + vmenv := NewEnv(self.state, self.tx, self.block) + var ref vm.ClosureRef + if tx.CreatesContract() { + self.rec = MakeContract(tx, self.state) + + ret, err, ref = vmenv.Create(sender, receiver.Address(), self.tx.Data, self.gas, self.gasPrice, self.value) + ref.SetCode(ret) + } else { + ret, err = vmenv.Call(self.Sender(), self.Receiver().Address(), self.tx.Data, self.gas, self.gasPrice, self.value) + } + if err != nil { + statelogger.Debugln(err) + } + + return +} + +// Converts an transaction in to a state object +func MakeContract(tx *types.Transaction, state *state.State) *state.StateObject { + addr := tx.CreationAddress(state) + + contract := state.GetOrNewStateObject(addr) + contract.InitCode = tx.Data + + return contract +} diff --git a/core/transaction_pool.go b/core/transaction_pool.go new file mode 100644 index 000000000..2a14e48b2 --- /dev/null +++ b/core/transaction_pool.go @@ -0,0 +1,236 @@ +package core + +import ( + "bytes" + "container/list" + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/state" + "github.com/ethereum/go-ethereum/wire" +) + +var txplogger = logger.NewLogger("TXP") + +const txPoolQueueSize = 50 + +type TxPoolHook chan *types.Transaction +type TxMsgTy byte + +const ( + minGasPrice = 1000000 +) + +var MinGasPrice = big.NewInt(10000000000000) + +type TxMsg struct { + Tx *types.Transaction + Type TxMsgTy +} + +func EachTx(pool *list.List, it func(*types.Transaction, *list.Element) bool) { + for e := pool.Front(); e != nil; e = e.Next() { + if it(e.Value.(*types.Transaction), e) { + break + } + } +} + +func FindTx(pool *list.List, finder func(*types.Transaction, *list.Element) bool) *types.Transaction { + for e := pool.Front(); e != nil; e = e.Next() { + if tx, ok := e.Value.(*types.Transaction); ok { + if finder(tx, e) { + return tx + } + } + } + + return nil +} + +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. If the +// pool is being drained or synced for whatever reason the transactions +// will simple queue up and handled when the mutex is freed. +type TxPool struct { + Ethereum EthManager + // The mutex for accessing the Tx pool. + mutex sync.Mutex + // Queueing channel for reading and writing incoming + // transactions to + queueChan chan *types.Transaction + // Quiting channel + quit chan bool + // The actual pool + pool *list.List + + SecondaryProcessor TxProcessor + + subscribers []chan TxMsg +} + +func NewTxPool(ethereum EthManager) *TxPool { + return &TxPool{ + pool: list.New(), + queueChan: make(chan *types.Transaction, txPoolQueueSize), + quit: make(chan bool), + Ethereum: ethereum, + } +} + +// Blocking function. Don't use directly. Use QueueTransaction instead +func (pool *TxPool) addTransaction(tx *types.Transaction) { + pool.mutex.Lock() + defer pool.mutex.Unlock() + + pool.pool.PushBack(tx) + + // Broadcast the transaction to the rest of the peers + pool.Ethereum.Broadcast(wire.MsgTxTy, []interface{}{tx.RlpData()}) +} + +func (pool *TxPool) ValidateTransaction(tx *types.Transaction) error { + // Get the last block so we can retrieve the sender and receiver from + // the merkle trie + block := pool.Ethereum.ChainManager().CurrentBlock + // Something has gone horribly wrong if this happens + if block == nil { + return fmt.Errorf("No last block on the block chain") + } + + if len(tx.Recipient) != 0 && len(tx.Recipient) != 20 { + return fmt.Errorf("Invalid recipient. len = %d", len(tx.Recipient)) + } + + v, _, _ := tx.Curve() + if v > 28 || v < 27 { + return fmt.Errorf("tx.v != (28 || 27)") + } + + if tx.GasPrice.Cmp(MinGasPrice) < 0 { + return fmt.Errorf("Gas price to low. Require %v > Got %v", MinGasPrice, tx.GasPrice) + } + + // Get the sender + sender := pool.Ethereum.BlockManager().CurrentState().GetAccount(tx.Sender()) + + 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.Sender()) + } + + if tx.IsContract() { + if tx.GasPrice.Cmp(big.NewInt(minGasPrice)) < 0 { + return fmt.Errorf("Gasprice too low, %s given should be at least %d.", tx.GasPrice, minGasPrice) + } + } + + // Increment the nonce making each tx valid only once to prevent replay + // attacks + + return nil +} + +func (self *TxPool) Add(tx *types.Transaction) error { + hash := tx.Hash() + foundTx := FindTx(self.pool, func(tx *types.Transaction, e *list.Element) bool { + return bytes.Compare(tx.Hash(), hash) == 0 + }) + + if foundTx != nil { + return fmt.Errorf("Known transaction (%x)", hash[0:4]) + } + + err := self.ValidateTransaction(tx) + if err != nil { + return err + } + + self.addTransaction(tx) + + tmp := make([]byte, 4) + copy(tmp, tx.Recipient) + + txplogger.Debugf("(t) %x => %x (%v) %x\n", tx.Sender()[:4], tmp, tx.Value, tx.Hash()) + + // Notify the subscribers + self.Ethereum.EventMux().Post(TxPreEvent{tx}) + + return nil +} + +func (pool *TxPool) CurrentTransactions() []*types.Transaction { + pool.mutex.Lock() + defer pool.mutex.Unlock() + + txList := make([]*types.Transaction, pool.pool.Len()) + i := 0 + for e := pool.pool.Front(); e != nil; e = e.Next() { + tx := e.Value.(*types.Transaction) + + txList[i] = tx + + i++ + } + + return txList +} + +func (pool *TxPool) RemoveInvalid(state *state.State) { + pool.mutex.Lock() + defer pool.mutex.Unlock() + + for e := pool.pool.Front(); e != nil; e = e.Next() { + tx := e.Value.(*types.Transaction) + sender := state.GetAccount(tx.Sender()) + err := pool.ValidateTransaction(tx) + if err != nil || sender.Nonce >= tx.Nonce { + pool.pool.Remove(e) + } + } +} + +func (self *TxPool) RemoveSet(txs types.Transactions) { + self.mutex.Lock() + defer self.mutex.Unlock() + + for _, tx := range txs { + EachTx(self.pool, func(t *types.Transaction, element *list.Element) bool { + if t == tx { + self.pool.Remove(element) + return true // To stop the loop + } + return false + }) + } +} + +func (pool *TxPool) Flush() []*types.Transaction { + txList := pool.CurrentTransactions() + + // Recreate a new list all together + // XXX Is this the fastest way? + pool.pool = list.New() + + return txList +} + +func (pool *TxPool) Start() { + //go pool.queueHandler() +} + +func (pool *TxPool) Stop() { + pool.Flush() + + txplogger.Infoln("Stopped") +} diff --git a/core/types/block.go b/core/types/block.go new file mode 100644 index 000000000..feab7072f --- /dev/null +++ b/core/types/block.go @@ -0,0 +1,413 @@ +package types + +import ( + "bytes" + "fmt" + "math/big" + "sort" + "time" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/state" + "github.com/ethereum/go-ethereum/trie" +) + +type BlockInfo struct { + Number uint64 + Hash []byte + Parent []byte + TD *big.Int +} + +func (bi *BlockInfo) RlpDecode(data []byte) { + decoder := ethutil.NewValueFromBytes(data) + + bi.Number = decoder.Get(0).Uint() + bi.Hash = decoder.Get(1).Bytes() + bi.Parent = decoder.Get(2).Bytes() + bi.TD = decoder.Get(3).BigInt() +} + +func (bi *BlockInfo) RlpEncode() []byte { + return ethutil.Encode([]interface{}{bi.Number, bi.Hash, bi.Parent, bi.TD}) +} + +type Blocks []*Block + +func (self Blocks) AsSet() ethutil.UniqueSet { + set := make(ethutil.UniqueSet) + for _, block := range self { + set.Insert(block.Hash()) + } + + return set +} + +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.Number.Cmp(b2.Number) < 0 } + +type Block struct { + // Hash to the previous block + PrevHash ethutil.Bytes + // Uncles of this block + Uncles Blocks + UncleSha []byte + // The coin base address + Coinbase []byte + // Block Trie state + //state *ethutil.Trie + state *state.State + // Difficulty for the current block + Difficulty *big.Int + // Creation time + Time int64 + // The block number + Number *big.Int + // Gas limit + GasLimit *big.Int + // Gas used + GasUsed *big.Int + // Extra data + Extra string + // Block Nonce for verification + Nonce ethutil.Bytes + // List of transactions and/or contracts + transactions Transactions + receipts Receipts + TxSha, ReceiptSha []byte + LogsBloom []byte + + Reward *big.Int +} + +func NewBlockFromBytes(raw []byte) *Block { + block := &Block{} + block.RlpDecode(raw) + + return block +} + +// New block takes a raw encoded string +func NewBlockFromRlpValue(rlpValue *ethutil.Value) *Block { + block := &Block{} + block.RlpValueDecode(rlpValue) + + return block +} + +func CreateBlock(root interface{}, + prevHash []byte, + base []byte, + Difficulty *big.Int, + Nonce []byte, + extra string) *Block { + + block := &Block{ + PrevHash: prevHash, + Coinbase: base, + Difficulty: Difficulty, + Nonce: Nonce, + Time: time.Now().Unix(), + Extra: extra, + UncleSha: nil, + GasUsed: new(big.Int), + GasLimit: new(big.Int), + } + block.SetUncles([]*Block{}) + + block.state = state.New(trie.New(ethutil.Config.Db, root)) + + return block +} + +// Returns a hash of the block +func (block *Block) Hash() ethutil.Bytes { + return crypto.Sha3(ethutil.NewValue(block.header()).Encode()) + //return crypto.Sha3(block.Value().Encode()) +} + +func (block *Block) HashNoNonce() []byte { + return crypto.Sha3(ethutil.Encode(block.miningHeader())) +} + +func (block *Block) State() *state.State { + return block.state +} + +func (block *Block) Transactions() Transactions { + return block.transactions +} + +func (block *Block) CalcGasLimit(parent *Block) *big.Int { + if block.Number.Cmp(big.NewInt(0)) == 0 { + return ethutil.BigPow(10, 6) + } + + // ((1024-1) * parent.gasLimit + (gasUsed * 6 / 5)) / 1024 + + previous := new(big.Int).Mul(big.NewInt(1024-1), parent.GasLimit) + current := new(big.Rat).Mul(new(big.Rat).SetInt(parent.GasUsed), big.NewRat(6, 5)) + curInt := new(big.Int).Div(current.Num(), current.Denom()) + + result := new(big.Int).Add(previous, curInt) + result.Div(result, big.NewInt(1024)) + + min := big.NewInt(125000) + + return ethutil.BigMax(min, result) +} + +func (block *Block) BlockInfo() BlockInfo { + bi := BlockInfo{} + data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...)) + bi.RlpDecode(data) + + return bi +} + +func (self *Block) GetTransaction(hash []byte) *Transaction { + for _, tx := range self.transactions { + if bytes.Compare(tx.Hash(), hash) == 0 { + return tx + } + } + + return nil +} + +// Sync the block's state and contract respectively +func (block *Block) Sync() { + block.state.Sync() +} + +func (block *Block) Undo() { + // Sync the block state itself + block.state.Reset() +} + +/////// Block Encoding +func (block *Block) rlpReceipts() interface{} { + // Marshal the transactions of this block + encR := make([]interface{}, len(block.receipts)) + for i, r := range block.receipts { + // Cast it to a string (safe) + encR[i] = r.RlpData() + } + + return encR +} + +func (block *Block) rlpUncles() interface{} { + // Marshal the transactions of this block + uncles := make([]interface{}, len(block.Uncles)) + for i, uncle := range block.Uncles { + // Cast it to a string (safe) + uncles[i] = uncle.header() + } + + return uncles +} + +func (block *Block) SetUncles(uncles []*Block) { + block.Uncles = uncles + block.UncleSha = crypto.Sha3(ethutil.Encode(block.rlpUncles())) +} + +func (self *Block) SetReceipts(receipts Receipts) { + self.receipts = receipts + self.ReceiptSha = DeriveSha(receipts) + self.LogsBloom = CreateBloom(receipts) +} + +func (self *Block) SetTransactions(txs Transactions) { + self.transactions = txs + self.TxSha = DeriveSha(txs) +} + +func (block *Block) Value() *ethutil.Value { + return ethutil.NewValue([]interface{}{block.header(), block.transactions, block.rlpUncles()}) +} + +func (block *Block) RlpEncode() []byte { + // Encode a slice interface which contains the header and the list of + // transactions. + return block.Value().Encode() +} + +func (block *Block) RlpDecode(data []byte) { + rlpValue := ethutil.NewValueFromBytes(data) + block.RlpValueDecode(rlpValue) +} + +func (block *Block) RlpValueDecode(decoder *ethutil.Value) { + block.setHeader(decoder.Get(0)) + + // Tx list might be empty if this is an uncle. Uncles only have their + // header set. + if decoder.Get(1).IsNil() == false { // Yes explicitness + //receipts := decoder.Get(1) + //block.receipts = make([]*Receipt, receipts.Len()) + txs := decoder.Get(1) + block.transactions = make(Transactions, txs.Len()) + for i := 0; i < txs.Len(); i++ { + block.transactions[i] = NewTransactionFromValue(txs.Get(i)) + //receipt := NewRecieptFromValue(receipts.Get(i)) + //block.transactions[i] = receipt.Tx + //block.receipts[i] = receipt + } + + } + + if decoder.Get(2).IsNil() == false { // Yes explicitness + uncles := decoder.Get(2) + block.Uncles = make([]*Block, uncles.Len()) + for i := 0; i < uncles.Len(); i++ { + block.Uncles[i] = NewUncleBlockFromValue(uncles.Get(i)) + } + } + +} + +func (self *Block) setHeader(header *ethutil.Value) { + self.PrevHash = header.Get(0).Bytes() + self.UncleSha = header.Get(1).Bytes() + self.Coinbase = header.Get(2).Bytes() + self.state = state.New(trie.New(ethutil.Config.Db, header.Get(3).Val)) + self.TxSha = header.Get(4).Bytes() + self.ReceiptSha = header.Get(5).Bytes() + self.LogsBloom = header.Get(6).Bytes() + self.Difficulty = header.Get(7).BigInt() + self.Number = header.Get(8).BigInt() + self.GasLimit = header.Get(9).BigInt() + self.GasUsed = header.Get(10).BigInt() + self.Time = int64(header.Get(11).BigInt().Uint64()) + self.Extra = header.Get(12).Str() + self.Nonce = header.Get(13).Bytes() +} + +func NewUncleBlockFromValue(header *ethutil.Value) *Block { + block := &Block{} + block.setHeader(header) + + return block +} + +func (block *Block) Trie() *trie.Trie { + return block.state.Trie +} + +func (block *Block) Root() interface{} { + return block.state.Root() +} + +func (block *Block) Diff() *big.Int { + return block.Difficulty +} + +func (self *Block) Receipts() []*Receipt { + return self.receipts +} + +func (block *Block) miningHeader() []interface{} { + return []interface{}{ + // Sha of the previous block + block.PrevHash, + // Sha of uncles + block.UncleSha, + // Coinbase address + block.Coinbase, + // root state + block.Root(), + // tx root + block.TxSha, + // Sha of tx + block.ReceiptSha, + // Bloom + block.LogsBloom, + // Current block Difficulty + block.Difficulty, + // The block number + block.Number, + // Block upper gas bound + block.GasLimit, + // Block gas used + block.GasUsed, + // Time the block was found? + block.Time, + // Extra data + block.Extra, + } +} + +func (block *Block) header() []interface{} { + return append(block.miningHeader(), block.Nonce) +} + +func (block *Block) String() string { + return fmt.Sprintf(` + BLOCK(%x): Size: %v + PrevHash: %x + UncleSha: %x + Coinbase: %x + Root: %x + TxSha %x + ReceiptSha: %x + Bloom: %x + Difficulty: %v + Number: %v + MaxLimit: %v + GasUsed: %v + Time: %v + Extra: %v + Nonce: %x + NumTx: %v +`, + block.Hash(), + block.Size(), + block.PrevHash, + block.UncleSha, + block.Coinbase, + block.Root(), + block.TxSha, + block.ReceiptSha, + block.LogsBloom, + block.Difficulty, + block.Number, + block.GasLimit, + block.GasUsed, + block.Time, + block.Extra, + block.Nonce, + len(block.transactions), + ) +} + +func (self *Block) Size() ethutil.StorageSize { + return ethutil.StorageSize(len(self.RlpEncode())) +} + +// Implement RlpEncodable +func (self *Block) RlpData() interface{} { + return self.Value().Val +} diff --git a/core/types/bloom9.go b/core/types/bloom9.go new file mode 100644 index 000000000..77aa6eef5 --- /dev/null +++ b/core/types/bloom9.go @@ -0,0 +1,56 @@ +package types + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/state" +) + +func CreateBloom(receipts Receipts) []byte { + bin := new(big.Int) + for _, receipt := range receipts { + bin.Or(bin, LogsBloom(receipt.logs)) + } + + return ethutil.LeftPadBytes(bin.Bytes(), 64) +} + +func LogsBloom(logs state.Logs) *big.Int { + bin := new(big.Int) + for _, log := range logs { + data := [][]byte{log.Address} + for _, topic := range log.Topics { + data = append(data, topic) + } + + for _, b := range data { + bin.Or(bin, ethutil.BigD(bloom9(crypto.Sha3(b)).Bytes())) + } + + //if log.Data != nil { + // data = append(data, log.Data) + //} + } + + return bin +} + +func bloom9(b []byte) *big.Int { + r := new(big.Int) + for _, i := range []int{0, 2, 4} { + t := big.NewInt(1) + b := uint(b[i+1]) + 256*(uint(b[i])&1) + r.Or(r, t.Lsh(t, b)) + } + + return r +} + +func BloomLookup(bin, topic []byte) bool { + bloom := ethutil.BigD(bin) + cmp := bloom9(crypto.Sha3(topic)) + + return bloom.And(bloom, cmp).Cmp(cmp) == 0 +} diff --git a/core/types/bloom9_test.go b/core/types/bloom9_test.go new file mode 100644 index 000000000..74e00cac6 --- /dev/null +++ b/core/types/bloom9_test.go @@ -0,0 +1,31 @@ +package types + +/* +import ( + "testing" + + "github.com/ethereum/go-ethereum/state" +) + +func TestBloom9(t *testing.T) { + testCase := []byte("testtest") + bin := LogsBloom([]state.Log{ + {testCase, [][]byte{[]byte("hellohello")}, nil}, + }).Bytes() + res := BloomLookup(bin, testCase) + + if !res { + t.Errorf("Bloom lookup failed") + } +} + + +func TestAddress(t *testing.T) { + block := &Block{} + block.Coinbase = ethutil.Hex2Bytes("22341ae42d6dd7384bc8584e50419ea3ac75b83f") + fmt.Printf("%x\n", crypto.Sha3(block.Coinbase)) + + bin := CreateBloom(block) + fmt.Printf("bin = %x\n", ethutil.LeftPadBytes(bin, 64)) +} +*/ diff --git a/core/types/common.go b/core/types/common.go new file mode 100644 index 000000000..ba88b77e1 --- /dev/null +++ b/core/types/common.go @@ -0,0 +1,11 @@ +package types + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/state" +) + +type BlockProcessor interface { + Process(*Block) (*big.Int, state.Messages, error) +} diff --git a/core/types/derive_sha.go b/core/types/derive_sha.go new file mode 100644 index 000000000..1897ff198 --- /dev/null +++ b/core/types/derive_sha.go @@ -0,0 +1,20 @@ +package types + +import ( + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/trie" +) + +type DerivableList interface { + Len() int + GetRlp(i int) []byte +} + +func DeriveSha(list DerivableList) []byte { + trie := trie.New(ethutil.Config.Db, "") + for i := 0; i < list.Len(); i++ { + trie.Update(string(ethutil.NewValue(i).Encode()), string(list.GetRlp(i))) + } + + return trie.GetRoot() +} diff --git a/core/types/receipt.go b/core/types/receipt.go new file mode 100644 index 000000000..25fa8fb07 --- /dev/null +++ b/core/types/receipt.go @@ -0,0 +1,68 @@ +package types + +import ( + "bytes" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/state" +) + +type Receipt struct { + PostState []byte + CumulativeGasUsed *big.Int + Bloom []byte + logs state.Logs +} + +func NewReceipt(root []byte, cumalativeGasUsed *big.Int) *Receipt { + return &Receipt{PostState: ethutil.CopyBytes(root), CumulativeGasUsed: cumalativeGasUsed} +} + +func NewRecieptFromValue(val *ethutil.Value) *Receipt { + r := &Receipt{} + r.RlpValueDecode(val) + + return r +} + +func (self *Receipt) SetLogs(logs state.Logs) { + self.logs = logs +} + +func (self *Receipt) RlpValueDecode(decoder *ethutil.Value) { + self.PostState = decoder.Get(0).Bytes() + self.CumulativeGasUsed = decoder.Get(1).BigInt() + self.Bloom = decoder.Get(2).Bytes() + + it := decoder.Get(3).NewIterator() + for it.Next() { + self.logs = append(self.logs, state.NewLogFromValue(it.Value())) + } +} + +func (self *Receipt) RlpData() interface{} { + return []interface{}{self.PostState, self.CumulativeGasUsed, self.Bloom, self.logs.RlpData()} +} + +func (self *Receipt) RlpEncode() []byte { + return ethutil.Encode(self.RlpData()) +} + +func (self *Receipt) Cmp(other *Receipt) bool { + if bytes.Compare(self.PostState, other.PostState) != 0 { + return false + } + + return true +} + +func (self *Receipt) String() string { + return fmt.Sprintf("receipt{med=%x cgas=%v bloom=%x logs=%v}", self.PostState, self.CumulativeGasUsed, self.Bloom, self.logs) +} + +type Receipts []*Receipt + +func (self Receipts) Len() int { return len(self) } +func (self Receipts) GetRlp(i int) []byte { return ethutil.Rlp(self[i]) } diff --git a/core/types/transaction.go b/core/types/transaction.go new file mode 100644 index 000000000..efc90b6f2 --- /dev/null +++ b/core/types/transaction.go @@ -0,0 +1,225 @@ +package types + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/state" + "github.com/obscuren/secp256k1-go" +) + +var ContractAddr = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + +func IsContractAddr(addr []byte) bool { + return len(addr) == 0 + //return bytes.Compare(addr, ContractAddr) == 0 +} + +type Transaction struct { + Nonce uint64 + Recipient []byte + Value *big.Int + Gas *big.Int + GasPrice *big.Int + Data []byte + v byte + r, s []byte + + // Indicates whether this tx is a contract creation transaction + contractCreation bool +} + +func NewContractCreationTx(value, gas, gasPrice *big.Int, script []byte) *Transaction { + return &Transaction{Recipient: nil, Value: value, Gas: gas, GasPrice: gasPrice, Data: script, contractCreation: true} +} + +func NewTransactionMessage(to []byte, value, gas, gasPrice *big.Int, data []byte) *Transaction { + return &Transaction{Recipient: to, Value: value, GasPrice: gasPrice, Gas: gas, Data: data, contractCreation: IsContractAddr(to)} +} + +func NewTransactionFromBytes(data []byte) *Transaction { + tx := &Transaction{} + tx.RlpDecode(data) + + return tx +} + +func NewTransactionFromValue(val *ethutil.Value) *Transaction { + tx := &Transaction{} + tx.RlpValueDecode(val) + + return tx +} + +func (self *Transaction) GasValue() *big.Int { + return new(big.Int).Mul(self.Gas, self.GasPrice) +} + +func (self *Transaction) TotalValue() *big.Int { + v := self.GasValue() + return v.Add(v, self.Value) +} + +func (tx *Transaction) Hash() []byte { + data := []interface{}{tx.Nonce, tx.GasPrice, tx.Gas, tx.Recipient, tx.Value, tx.Data} + + return crypto.Sha3(ethutil.NewValue(data).Encode()) +} + +func (tx *Transaction) CreatesContract() bool { + return tx.contractCreation +} + +/* Deprecated */ +func (tx *Transaction) IsContract() bool { + return tx.CreatesContract() +} + +func (tx *Transaction) CreationAddress(state *state.State) []byte { + // Generate a new address + return crypto.Sha3(ethutil.NewValue([]interface{}{tx.Sender(), tx.Nonce}).Encode())[12:] +} + +func (tx *Transaction) Curve() (v byte, r []byte, s []byte) { + v = tx.v + r = ethutil.LeftPadBytes(tx.r, 32) + s = ethutil.LeftPadBytes(tx.s, 32) + + return +} + +func (tx *Transaction) Signature(key []byte) []byte { + hash := tx.Hash() + + sig, _ := secp256k1.Sign(hash, key) + + return sig +} + +func (tx *Transaction) PublicKey() []byte { + hash := tx.Hash() + + v, r, s := tx.Curve() + + sig := append(r, s...) + sig = append(sig, v-27) + + pubkey := crypto.Ecrecover(append(hash, sig...)) + //pubkey, _ := secp256k1.RecoverPubkey(hash, sig) + + return pubkey +} + +func (tx *Transaction) Sender() []byte { + pubkey := tx.PublicKey() + + // Validate the returned key. + // Return nil if public key isn't in full format + if len(pubkey) != 0 && pubkey[0] != 4 { + return nil + } + + return crypto.Sha3(pubkey[1:])[12:] +} + +func (tx *Transaction) Sign(privk []byte) error { + + sig := tx.Signature(privk) + + tx.r = sig[:32] + tx.s = sig[32:64] + tx.v = sig[64] + 27 + + return nil +} + +func (tx *Transaction) RlpData() interface{} { + data := []interface{}{tx.Nonce, tx.GasPrice, tx.Gas, tx.Recipient, tx.Value, tx.Data} + + // TODO Remove prefixing zero's + + return append(data, tx.v, new(big.Int).SetBytes(tx.r).Bytes(), new(big.Int).SetBytes(tx.s).Bytes()) +} + +func (tx *Transaction) RlpValue() *ethutil.Value { + return ethutil.NewValue(tx.RlpData()) +} + +func (tx *Transaction) RlpEncode() []byte { + return tx.RlpValue().Encode() +} + +func (tx *Transaction) RlpDecode(data []byte) { + tx.RlpValueDecode(ethutil.NewValueFromBytes(data)) +} + +func (tx *Transaction) RlpValueDecode(decoder *ethutil.Value) { + tx.Nonce = decoder.Get(0).Uint() + tx.GasPrice = decoder.Get(1).BigInt() + tx.Gas = decoder.Get(2).BigInt() + tx.Recipient = decoder.Get(3).Bytes() + tx.Value = decoder.Get(4).BigInt() + tx.Data = decoder.Get(5).Bytes() + tx.v = byte(decoder.Get(6).Uint()) + + tx.r = decoder.Get(7).Bytes() + tx.s = decoder.Get(8).Bytes() + + if IsContractAddr(tx.Recipient) { + tx.contractCreation = true + } +} + +func (tx *Transaction) String() string { + return fmt.Sprintf(` + TX(%x) + Contract: %v + From: %x + To: %x + Nonce: %v + GasPrice: %v + Gas: %v + Value: %v + Data: 0x%x + V: 0x%x + R: 0x%x + S: 0x%x + `, + tx.Hash(), + len(tx.Recipient) == 0, + tx.Sender(), + tx.Recipient, + tx.Nonce, + tx.GasPrice, + tx.Gas, + tx.Value, + tx.Data, + tx.v, + tx.r, + tx.s) +} + +// Transaction slice type for basic sorting +type Transactions []*Transaction + +func (self Transactions) RlpData() interface{} { + // Marshal the transactions of this block + enc := make([]interface{}, len(self)) + for i, tx := range self { + // Cast it to a string (safe) + enc[i] = tx.RlpData() + } + + return enc +} +func (s Transactions) Len() int { return len(s) } +func (s Transactions) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s Transactions) GetRlp(i int) []byte { return ethutil.Rlp(s[i]) } + +type TxByNonce struct{ Transactions } + +func (s TxByNonce) Less(i, j int) bool { + return s.Transactions[i].Nonce < s.Transactions[j].Nonce +} 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..17686b28f --- /dev/null +++ b/core/vm_env.go @@ -0,0 +1,63 @@ +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.State + block *types.Block + tx *types.Transaction + depth int +} + +func NewEnv(state *state.State, tx *types.Transaction, block *types.Block) *VMEnv { + return &VMEnv{ + state: state, + block: block, + tx: tx, + } +} + +func (self *VMEnv) Origin() []byte { return self.tx.Sender() } +func (self *VMEnv) BlockNumber() *big.Int { return self.block.Number } +func (self *VMEnv) PrevHash() []byte { return self.block.PrevHash } +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) BlockHash() []byte { return self.block.Hash() } +func (self *VMEnv) Value() *big.Int { return self.tx.Value } +func (self *VMEnv) State() *state.State { return self.state } +func (self *VMEnv) GasLimit() *big.Int { return self.block.GasLimit } +func (self *VMEnv) Depth() int { return self.depth } +func (self *VMEnv) SetDepth(i int) { self.depth = i } +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 { + evm := vm.New(self, vm.DebugVmTy) + + return NewExecution(evm, addr, data, gas, price, value) +} + +func (self *VMEnv) Call(me vm.ClosureRef, 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.ClosureRef, 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.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error, vm.ClosureRef) { + exe := self.vm(addr, data, gas, price, value) + return exe.Create(me) +} |