aboutsummaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/.gitignore12
-rw-r--r--core/asm.go50
-rw-r--r--core/block_processor.go362
-rw-r--r--core/block_processor_test.go35
-rw-r--r--core/chain_makers.go142
-rw-r--r--core/chain_manager.go513
-rw-r--r--core/chain_manager_test.go346
-rw-r--r--core/error.go164
-rw-r--r--core/events.go28
-rw-r--r--core/execution.go80
-rw-r--r--core/fees.go7
-rw-r--r--core/filter.go204
-rw-r--r--core/filter_test.go1
-rw-r--r--core/genesis.go75
-rw-r--r--core/helper_test.go83
-rw-r--r--core/manager.go19
-rw-r--r--core/state_transition.go251
-rw-r--r--core/transaction_pool.go206
-rw-r--r--core/transaction_pool_test.go97
-rw-r--r--core/types/block.go308
-rw-r--r--core/types/block_test.go1
-rw-r--r--core/types/bloom9.go55
-rw-r--r--core/types/bloom9_test.go31
-rw-r--r--core/types/common.go7
-rw-r--r--core/types/derive_sha.go22
-rw-r--r--core/types/receipt.go81
-rw-r--r--core/types/transaction.go234
-rw-r--r--core/types/transaction_test.go1
-rw-r--r--core/vm_env.go72
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)
+}