diff options
author | Jeffrey Wilcke <jeffrey@ethereum.org> | 2015-10-05 22:14:01 +0800 |
---|---|---|
committer | Jeffrey Wilcke <jeffrey@ethereum.org> | 2015-10-05 22:14:01 +0800 |
commit | 5b34fa538e54320778314081df97da561f7c6314 (patch) | |
tree | dca15c858b8ac4cd79ed7b7733260ab3021bbd0c /core | |
parent | 8b865fa9bf75e728d5d76f5a1460e0c37d8b5f9e (diff) | |
parent | 7c7692933c21b77328a94eed714f66c276776197 (diff) | |
download | dexon-5b34fa538e54320778314081df97da561f7c6314.tar dexon-5b34fa538e54320778314081df97da561f7c6314.tar.gz dexon-5b34fa538e54320778314081df97da561f7c6314.tar.bz2 dexon-5b34fa538e54320778314081df97da561f7c6314.tar.lz dexon-5b34fa538e54320778314081df97da561f7c6314.tar.xz dexon-5b34fa538e54320778314081df97da561f7c6314.tar.zst dexon-5b34fa538e54320778314081df97da561f7c6314.zip |
Merge pull request #1756 from obscuren/core-refactor
core, core/vm: refactor
Diffstat (limited to 'core')
36 files changed, 938 insertions, 1301 deletions
diff --git a/core/bench_test.go b/core/bench_test.go index def4f0d2a..27f3e3158 100644 --- a/core/bench_test.go +++ b/core/bench_test.go @@ -168,7 +168,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) { // Time the insertion of the new chain. // State and blocks are stored in the same DB. evmux := new(event.TypeMux) - chainman, _ := NewChainManager(db, FakePow{}, evmux) + chainman, _ := NewBlockChain(db, FakePow{}, evmux) chainman.SetProcessor(NewBlockProcessor(db, FakePow{}, chainman, evmux)) defer chainman.Stop() b.ReportAllocs() diff --git a/core/block_processor.go b/core/block_processor.go index 40590bdc5..783e15687 100644 --- a/core/block_processor.go +++ b/core/block_processor.go @@ -25,6 +25,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" @@ -46,7 +47,7 @@ type BlockProcessor struct { // Mutex for locking the block processor. Blocks can only be handled one at a time mutex sync.Mutex // Canonical block chain - bc *ChainManager + bc *BlockChain // non-persistent key/value memory storage mem map[string]*big.Int // Proof of work used for validating @@ -69,12 +70,12 @@ type GasPool interface { SubGas(gas, price *big.Int) error } -func NewBlockProcessor(db ethdb.Database, pow pow.PoW, chainManager *ChainManager, eventMux *event.TypeMux) *BlockProcessor { +func NewBlockProcessor(db ethdb.Database, pow pow.PoW, blockchain *BlockChain, eventMux *event.TypeMux) *BlockProcessor { sm := &BlockProcessor{ chainDb: db, mem: make(map[string]*big.Int), Pow: pow, - bc: chainManager, + bc: blockchain, eventMux: eventMux, } return sm @@ -123,7 +124,7 @@ func (self *BlockProcessor) ApplyTransaction(gp GasPool, statedb *state.StateDB, return receipt, gas, err } -func (self *BlockProcessor) ChainManager() *ChainManager { +func (self *BlockProcessor) BlockChain() *BlockChain { return self.bc } @@ -163,7 +164,7 @@ func (self *BlockProcessor) ApplyTransactions(gp GasPool, statedb *state.StateDB return receipts, err } -func (sm *BlockProcessor) RetryProcess(block *types.Block) (logs state.Logs, err error) { +func (sm *BlockProcessor) RetryProcess(block *types.Block) (logs vm.Logs, err error) { // Processing a blocks may never happen simultaneously sm.mutex.Lock() defer sm.mutex.Unlock() @@ -188,7 +189,7 @@ func (sm *BlockProcessor) RetryProcess(block *types.Block) (logs state.Logs, 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) (logs state.Logs, receipts types.Receipts, err error) { +func (sm *BlockProcessor) Process(block *types.Block) (logs vm.Logs, receipts types.Receipts, err error) { // Processing a blocks may never happen simultaneously sm.mutex.Lock() defer sm.mutex.Unlock() @@ -204,7 +205,7 @@ func (sm *BlockProcessor) Process(block *types.Block) (logs state.Logs, receipts return sm.processWithParent(block, parent) } -func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs state.Logs, receipts types.Receipts, err error) { +func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs vm.Logs, receipts types.Receipts, err error) { // Create a new state based on the parent's root (e.g., create copy) state := state.New(parent.Root(), sm.chainDb) header := block.Header() @@ -346,7 +347,7 @@ func (sm *BlockProcessor) VerifyUncles(statedb *state.StateDB, block, parent *ty // GetBlockReceipts returns the receipts beloniging to the block hash func (sm *BlockProcessor) GetBlockReceipts(bhash common.Hash) types.Receipts { - if block := sm.ChainManager().GetBlock(bhash); block != nil { + if block := sm.BlockChain().GetBlock(bhash); block != nil { return GetBlockReceipts(sm.chainDb, block.Hash()) } @@ -356,7 +357,7 @@ func (sm *BlockProcessor) GetBlockReceipts(bhash common.Hash) types.Receipts { // GetLogs returns the logs of the given block. This method is using a two step approach // where it tries to get it from the (updated) method which gets them from the receipts or // the depricated way by re-processing the block. -func (sm *BlockProcessor) GetLogs(block *types.Block) (logs state.Logs, err error) { +func (sm *BlockProcessor) GetLogs(block *types.Block) (logs vm.Logs, err error) { receipts := GetBlockReceipts(sm.chainDb, block.Hash()) // coalesce logs for _, receipt := range receipts { diff --git a/core/block_processor_test.go b/core/block_processor_test.go index 538cf4ee5..ba8bd7bcd 100644 --- a/core/block_processor_test.go +++ b/core/block_processor_test.go @@ -24,21 +24,22 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/pow/ezp" ) -func proc() (*BlockProcessor, *ChainManager) { +func proc() (*BlockProcessor, *BlockChain) { db, _ := ethdb.NewMemDatabase() var mux event.TypeMux WriteTestNetGenesisBlock(db, 0) - chainMan, err := NewChainManager(db, thePow(), &mux) + blockchain, err := NewBlockChain(db, thePow(), &mux) if err != nil { fmt.Println(err) } - return NewBlockProcessor(db, ezp.New(), chainMan, &mux), chainMan + return NewBlockProcessor(db, ezp.New(), blockchain, &mux), blockchain } func TestNumber(t *testing.T) { @@ -69,7 +70,7 @@ func TestPutReceipt(t *testing.T) { hash[0] = 2 receipt := new(types.Receipt) - receipt.SetLogs(state.Logs{&state.Log{ + receipt.SetLogs(vm.Logs{&vm.Log{ Address: addr, Topics: []common.Hash{hash}, Data: []byte("hi"), diff --git a/core/chain_manager.go b/core/blockchain.go index 49f831a59..e8209f8e3 100644 --- a/core/chain_manager.go +++ b/core/blockchain.go @@ -55,11 +55,9 @@ const ( blockCacheLimit = 256 maxFutureBlocks = 256 maxTimeFutureBlocks = 30 - checkpointLimit = 200 ) -type ChainManager struct { - //eth EthManager +type BlockChain struct { chainDb ethdb.Database processor types.BlockProcessor eventMux *event.TypeMux @@ -69,7 +67,6 @@ type ChainManager struct { chainmu sync.RWMutex tsmu sync.RWMutex - checkpoint int // checkpoint counts towards the new checkpoint td *big.Int currentBlock *types.Block currentGasLimit *big.Int @@ -90,7 +87,7 @@ type ChainManager struct { pow pow.PoW } -func NewChainManager(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*ChainManager, error) { +func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*BlockChain, error) { headerCache, _ := lru.New(headerCacheLimit) bodyCache, _ := lru.New(bodyCacheLimit) bodyRLPCache, _ := lru.New(bodyCacheLimit) @@ -98,7 +95,7 @@ func NewChainManager(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (* blockCache, _ := lru.New(blockCacheLimit) futureBlocks, _ := lru.New(maxFutureBlocks) - bc := &ChainManager{ + bc := &BlockChain{ chainDb: chainDb, eventMux: mux, quit: make(chan struct{}), @@ -144,7 +141,7 @@ func NewChainManager(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (* return bc, nil } -func (bc *ChainManager) SetHead(head *types.Block) { +func (bc *BlockChain) SetHead(head *types.Block) { bc.mu.Lock() defer bc.mu.Unlock() @@ -163,80 +160,55 @@ func (bc *ChainManager) SetHead(head *types.Block) { bc.setLastState() } -func (self *ChainManager) Td() *big.Int { +func (self *BlockChain) Td() *big.Int { self.mu.RLock() defer self.mu.RUnlock() return new(big.Int).Set(self.td) } -func (self *ChainManager) GasLimit() *big.Int { +func (self *BlockChain) GasLimit() *big.Int { self.mu.RLock() defer self.mu.RUnlock() return self.currentBlock.GasLimit() } -func (self *ChainManager) LastBlockHash() common.Hash { +func (self *BlockChain) LastBlockHash() common.Hash { self.mu.RLock() defer self.mu.RUnlock() return self.currentBlock.Hash() } -func (self *ChainManager) CurrentBlock() *types.Block { +func (self *BlockChain) CurrentBlock() *types.Block { self.mu.RLock() defer self.mu.RUnlock() return self.currentBlock } -func (self *ChainManager) Status() (td *big.Int, currentBlock common.Hash, genesisBlock common.Hash) { +func (self *BlockChain) Status() (td *big.Int, currentBlock common.Hash, genesisBlock common.Hash) { self.mu.RLock() defer self.mu.RUnlock() return new(big.Int).Set(self.td), self.currentBlock.Hash(), self.genesisBlock.Hash() } -func (self *ChainManager) SetProcessor(proc types.BlockProcessor) { +func (self *BlockChain) SetProcessor(proc types.BlockProcessor) { self.processor = proc } -func (self *ChainManager) State() *state.StateDB { +func (self *BlockChain) State() *state.StateDB { return state.New(self.CurrentBlock().Root(), self.chainDb) } -func (bc *ChainManager) recover() bool { - data, _ := bc.chainDb.Get([]byte("checkpoint")) - if len(data) != 0 { - block := bc.GetBlock(common.BytesToHash(data)) - if block != nil { - if err := WriteCanonicalHash(bc.chainDb, block.Hash(), block.NumberU64()); err != nil { - glog.Fatalf("failed to write database head number: %v", err) - } - if err := WriteHeadBlockHash(bc.chainDb, block.Hash()); err != nil { - glog.Fatalf("failed to write database head hash: %v", err) - } - bc.currentBlock = block - return true - } - } - return false -} - -func (bc *ChainManager) setLastState() error { +func (bc *BlockChain) setLastState() error { head := GetHeadBlockHash(bc.chainDb) if head != (common.Hash{}) { block := bc.GetBlock(head) if block != nil { bc.currentBlock = block - } else { - glog.Infof("LastBlock (%x) not found. Recovering...\n", head) - if bc.recover() { - glog.Infof("Recover successful") - } else { - glog.Fatalf("Recover failed. Please report") - } } } else { bc.Reset() @@ -252,13 +224,13 @@ func (bc *ChainManager) setLastState() error { } // Reset purges the entire blockchain, restoring it to its genesis state. -func (bc *ChainManager) Reset() { +func (bc *BlockChain) Reset() { bc.ResetWithGenesisBlock(bc.genesisBlock) } // ResetWithGenesisBlock purges the entire blockchain, restoring it to the // specified genesis state. -func (bc *ChainManager) ResetWithGenesisBlock(genesis *types.Block) { +func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) { bc.mu.Lock() defer bc.mu.Unlock() @@ -286,7 +258,7 @@ func (bc *ChainManager) ResetWithGenesisBlock(genesis *types.Block) { } // Export writes the active chain to the given writer. -func (self *ChainManager) Export(w io.Writer) error { +func (self *BlockChain) Export(w io.Writer) error { if err := self.ExportN(w, uint64(0), self.currentBlock.NumberU64()); err != nil { return err } @@ -294,7 +266,7 @@ func (self *ChainManager) Export(w io.Writer) error { } // ExportN writes a subset of the active chain to the given writer. -func (self *ChainManager) ExportN(w io.Writer, first uint64, last uint64) error { +func (self *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error { self.mu.RLock() defer self.mu.RUnlock() @@ -320,40 +292,28 @@ func (self *ChainManager) ExportN(w io.Writer, first uint64, last uint64) error // insert injects a block into the current chain block chain. Note, this function // assumes that the `mu` mutex is held! -func (bc *ChainManager) insert(block *types.Block) { +func (bc *BlockChain) insert(block *types.Block) { // Add the block to the canonical chain number scheme and mark as the head if err := WriteCanonicalHash(bc.chainDb, block.Hash(), block.NumberU64()); err != nil { glog.Fatalf("failed to insert block number: %v", err) } - if err := WriteHeadBlockHash(bc.chainDb, block.Hash()); err != nil { - glog.Fatalf("failed to insert block number: %v", err) - } - // Add a new restore point if we reached some limit - bc.checkpoint++ - if bc.checkpoint > checkpointLimit { - if err := bc.chainDb.Put([]byte("checkpoint"), block.Hash().Bytes()); err != nil { - glog.Fatalf("failed to create checkpoint: %v", err) - } - bc.checkpoint = 0 - } - // Update the internal internal state with the head block bc.currentBlock = block } // Accessors -func (bc *ChainManager) Genesis() *types.Block { +func (bc *BlockChain) Genesis() *types.Block { return bc.genesisBlock } // HasHeader checks if a block header is present in the database or not, caching // it if present. -func (bc *ChainManager) HasHeader(hash common.Hash) bool { +func (bc *BlockChain) HasHeader(hash common.Hash) bool { return bc.GetHeader(hash) != nil } // GetHeader retrieves a block header from the database by hash, caching it if // found. -func (self *ChainManager) GetHeader(hash common.Hash) *types.Header { +func (self *BlockChain) GetHeader(hash common.Hash) *types.Header { // Short circuit if the header's already in the cache, retrieve otherwise if header, ok := self.headerCache.Get(hash); ok { return header.(*types.Header) @@ -369,7 +329,7 @@ func (self *ChainManager) GetHeader(hash common.Hash) *types.Header { // GetHeaderByNumber retrieves a block header from the database by number, // caching it (associated with its hash) if found. -func (self *ChainManager) GetHeaderByNumber(number uint64) *types.Header { +func (self *BlockChain) GetHeaderByNumber(number uint64) *types.Header { hash := GetCanonicalHash(self.chainDb, number) if hash == (common.Hash{}) { return nil @@ -379,7 +339,7 @@ func (self *ChainManager) GetHeaderByNumber(number uint64) *types.Header { // GetBody retrieves a block body (transactions and uncles) from the database by // hash, caching it if found. -func (self *ChainManager) GetBody(hash common.Hash) *types.Body { +func (self *BlockChain) GetBody(hash common.Hash) *types.Body { // Short circuit if the body's already in the cache, retrieve otherwise if cached, ok := self.bodyCache.Get(hash); ok { body := cached.(*types.Body) @@ -396,7 +356,7 @@ func (self *ChainManager) GetBody(hash common.Hash) *types.Body { // GetBodyRLP retrieves a block body in RLP encoding from the database by hash, // caching it if found. -func (self *ChainManager) GetBodyRLP(hash common.Hash) rlp.RawValue { +func (self *BlockChain) GetBodyRLP(hash common.Hash) rlp.RawValue { // Short circuit if the body's already in the cache, retrieve otherwise if cached, ok := self.bodyRLPCache.Get(hash); ok { return cached.(rlp.RawValue) @@ -412,7 +372,7 @@ func (self *ChainManager) GetBodyRLP(hash common.Hash) rlp.RawValue { // GetTd retrieves a block's total difficulty in the canonical chain from the // database by hash, caching it if found. -func (self *ChainManager) GetTd(hash common.Hash) *big.Int { +func (self *BlockChain) GetTd(hash common.Hash) *big.Int { // Short circuit if the td's already in the cache, retrieve otherwise if cached, ok := self.tdCache.Get(hash); ok { return cached.(*big.Int) @@ -428,12 +388,12 @@ func (self *ChainManager) GetTd(hash common.Hash) *big.Int { // HasBlock checks if a block is fully present in the database or not, caching // it if present. -func (bc *ChainManager) HasBlock(hash common.Hash) bool { +func (bc *BlockChain) HasBlock(hash common.Hash) bool { return bc.GetBlock(hash) != nil } // GetBlock retrieves a block from the database by hash, caching it if found. -func (self *ChainManager) GetBlock(hash common.Hash) *types.Block { +func (self *BlockChain) GetBlock(hash common.Hash) *types.Block { // Short circuit if the block's already in the cache, retrieve otherwise if block, ok := self.blockCache.Get(hash); ok { return block.(*types.Block) @@ -449,7 +409,7 @@ func (self *ChainManager) GetBlock(hash common.Hash) *types.Block { // GetBlockByNumber retrieves a block from the database by number, caching it // (associated with its hash) if found. -func (self *ChainManager) GetBlockByNumber(number uint64) *types.Block { +func (self *BlockChain) GetBlockByNumber(number uint64) *types.Block { hash := GetCanonicalHash(self.chainDb, number) if hash == (common.Hash{}) { return nil @@ -459,7 +419,7 @@ func (self *ChainManager) GetBlockByNumber(number uint64) *types.Block { // GetBlockHashesFromHash retrieves a number of block hashes starting at a given // hash, fetching towards the genesis block. -func (self *ChainManager) GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash { +func (self *BlockChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash { // Get the origin header from which to fetch header := self.GetHeader(hash) if header == nil { @@ -481,7 +441,7 @@ func (self *ChainManager) GetBlockHashesFromHash(hash common.Hash, max uint64) [ // [deprecated by eth/62] // GetBlocksFromHash returns the block corresponding to hash and up to n-1 ancestors. -func (self *ChainManager) GetBlocksFromHash(hash common.Hash, n int) (blocks []*types.Block) { +func (self *BlockChain) GetBlocksFromHash(hash common.Hash, n int) (blocks []*types.Block) { for i := 0; i < n; i++ { block := self.GetBlock(hash) if block == nil { @@ -493,7 +453,7 @@ func (self *ChainManager) GetBlocksFromHash(hash common.Hash, n int) (blocks []* return } -func (self *ChainManager) GetUnclesInChain(block *types.Block, length int) (uncles []*types.Header) { +func (self *BlockChain) 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()) @@ -504,11 +464,11 @@ func (self *ChainManager) GetUnclesInChain(block *types.Block, length int) (uncl // setTotalDifficulty updates the TD of the chain manager. Note, this function // assumes that the `mu` mutex is held! -func (bc *ChainManager) setTotalDifficulty(td *big.Int) { +func (bc *BlockChain) setTotalDifficulty(td *big.Int) { bc.td = new(big.Int).Set(td) } -func (bc *ChainManager) Stop() { +func (bc *BlockChain) Stop() { if !atomic.CompareAndSwapInt32(&bc.running, 0, 1) { return } @@ -527,7 +487,7 @@ type queueEvent struct { splitCount int } -func (self *ChainManager) procFutureBlocks() { +func (self *BlockChain) procFutureBlocks() { blocks := make([]*types.Block, self.futureBlocks.Len()) for i, hash := range self.futureBlocks.Keys() { block, _ := self.futureBlocks.Get(hash) @@ -549,7 +509,7 @@ const ( ) // WriteBlock writes the block to the chain. -func (self *ChainManager) WriteBlock(block *types.Block) (status writeStatus, err error) { +func (self *BlockChain) WriteBlock(block *types.Block) (status writeStatus, err error) { self.wg.Add(1) defer self.wg.Done() @@ -599,7 +559,7 @@ func (self *ChainManager) WriteBlock(block *types.Block) (status writeStatus, er // InsertChain will attempt to insert the given chain in to the canonical chain or, otherwise, create a fork. It an error is returned // it will return the index number of the failing block as well an error describing what went wrong (for possible errors see core/errors.go). -func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) { +func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) { self.wg.Add(1) defer self.wg.Done() @@ -730,7 +690,7 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) { // reorgs takes two blocks, an old chain and a new chain and will reconstruct the blocks and inserts them // to be part of the new canonical chain and accumulates potential missing transactions and post an // event about them -func (self *ChainManager) reorg(oldBlock, newBlock *types.Block) error { +func (self *BlockChain) reorg(oldBlock, newBlock *types.Block) error { self.mu.Lock() defer self.mu.Unlock() @@ -811,7 +771,7 @@ func (self *ChainManager) reorg(oldBlock, newBlock *types.Block) error { return nil } -func (self *ChainManager) update() { +func (self *BlockChain) update() { events := self.eventMux.Subscribe(queueEvent{}) futureTimer := time.Tick(5 * time.Second) out: diff --git a/core/chain_manager_test.go b/core/blockchain_test.go index 6cfafb8c0..e034417ce 100644 --- a/core/chain_manager_test.go +++ b/core/blockchain_test.go @@ -28,8 +28,8 @@ import ( "github.com/ethereum/ethash" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" @@ -48,19 +48,19 @@ func thePow() pow.PoW { return pow } -func theChainManager(db ethdb.Database, t *testing.T) *ChainManager { +func theBlockChain(db ethdb.Database, t *testing.T) *BlockChain { var eventMux event.TypeMux WriteTestNetGenesisBlock(db, 0) - chainMan, err := NewChainManager(db, thePow(), &eventMux) + blockchain, err := NewBlockChain(db, thePow(), &eventMux) if err != nil { t.Error("failed creating chainmanager:", err) t.FailNow() return nil } - blockMan := NewBlockProcessor(db, nil, chainMan, &eventMux) - chainMan.SetProcessor(blockMan) + blockMan := NewBlockProcessor(db, nil, blockchain, &eventMux) + blockchain.SetProcessor(blockMan) - return chainMan + return blockchain } // Test fork of length N starting from block i @@ -104,7 +104,7 @@ func testFork(t *testing.T, bman *BlockProcessor, i, N int, f func(td1, td2 *big // Loop over parents making sure reconstruction is done properly } -func printChain(bc *ChainManager) { +func printChain(bc *BlockChain) { for i := bc.CurrentBlock().Number().Uint64(); i > 0; i-- { b := bc.GetBlockByNumber(uint64(i)) fmt.Printf("\t%x %v\n", b.Hash(), b.Difficulty()) @@ -144,8 +144,8 @@ func loadChain(fn string, t *testing.T) (types.Blocks, error) { return chain, nil } -func insertChain(done chan bool, chainMan *ChainManager, chain types.Blocks, t *testing.T) { - _, err := chainMan.InsertChain(chain) +func insertChain(done chan bool, blockchain *BlockChain, chain types.Blocks, t *testing.T) { + _, err := blockchain.InsertChain(chain) if err != nil { fmt.Println(err) t.FailNow() @@ -294,23 +294,23 @@ func TestChainInsertions(t *testing.T) { t.FailNow() } - chainMan := theChainManager(db, t) + blockchain := theBlockChain(db, t) const max = 2 done := make(chan bool, max) - go insertChain(done, chainMan, chain1, t) - go insertChain(done, chainMan, chain2, t) + go insertChain(done, blockchain, chain1, t) + go insertChain(done, blockchain, chain2, t) for i := 0; i < max; i++ { <-done } - if chain2[len(chain2)-1].Hash() != chainMan.CurrentBlock().Hash() { + if chain2[len(chain2)-1].Hash() != blockchain.CurrentBlock().Hash() { t.Error("chain2 is canonical and shouldn't be") } - if chain1[len(chain1)-1].Hash() != chainMan.CurrentBlock().Hash() { + if chain1[len(chain1)-1].Hash() != blockchain.CurrentBlock().Hash() { t.Error("chain1 isn't canonical and should be") } } @@ -337,7 +337,7 @@ func TestChainMultipleInsertions(t *testing.T) { } } - chainMan := theChainManager(db, t) + blockchain := theBlockChain(db, t) done := make(chan bool, max) for i, chain := range chains { @@ -345,7 +345,7 @@ func TestChainMultipleInsertions(t *testing.T) { i := i chain := chain go func() { - insertChain(done, chainMan, chain, t) + insertChain(done, blockchain, chain, t) fmt.Println(i, "done") }() } @@ -354,14 +354,14 @@ func TestChainMultipleInsertions(t *testing.T) { <-done } - if chains[longest][len(chains[longest])-1].Hash() != chainMan.CurrentBlock().Hash() { + if chains[longest][len(chains[longest])-1].Hash() != blockchain.CurrentBlock().Hash() { t.Error("Invalid canonical chain") } } type bproc struct{} -func (bproc) Process(*types.Block) (state.Logs, types.Receipts, error) { return nil, nil, nil } +func (bproc) Process(*types.Block) (vm.Logs, types.Receipts, error) { return nil, nil, nil } func makeChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Block { var chain []*types.Block @@ -382,9 +382,9 @@ func makeChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Block return chain } -func chm(genesis *types.Block, db ethdb.Database) *ChainManager { +func chm(genesis *types.Block, db ethdb.Database) *BlockChain { var eventMux event.TypeMux - bc := &ChainManager{chainDb: db, genesisBlock: genesis, eventMux: &eventMux, pow: FakePow{}} + bc := &BlockChain{chainDb: db, genesisBlock: genesis, eventMux: &eventMux, pow: FakePow{}} bc.headerCache, _ = lru.New(100) bc.bodyCache, _ = lru.New(100) bc.bodyRLPCache, _ = lru.New(100) @@ -459,7 +459,7 @@ func TestReorgBadHashes(t *testing.T) { BadHashes[chain[3].Header().Hash()] = true var eventMux event.TypeMux - ncm, err := NewChainManager(db, FakePow{}, &eventMux) + ncm, err := NewBlockChain(db, FakePow{}, &eventMux) if err != nil { t.Errorf("NewChainManager err: %s", err) } @@ -593,7 +593,7 @@ func TestChainTxReorgs(t *testing.T) { }) // Import the chain. This runs all block validation rules. evmux := &event.TypeMux{} - chainman, _ := NewChainManager(db, FakePow{}, evmux) + chainman, _ := NewBlockChain(db, FakePow{}, evmux) chainman.SetProcessor(NewBlockProcessor(db, FakePow{}, chainman, evmux)) if i, err := chainman.InsertChain(chain); err != nil { t.Fatalf("failed to insert original chain[%d]: %v", i, err) diff --git a/core/chain_makers.go b/core/chain_makers.go index 3af9b0b89..ba09b3029 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -153,7 +153,7 @@ func (b *BlockGen) OffsetTime(seconds int64) { // and their coinbase will be the zero address. // // Blocks created by GenerateChain do not contain valid proof of work -// values. Inserting them into ChainManager requires use of FakePow or +// values. Inserting them into BlockChain requires use of FakePow or // a similar non-validating proof of work implementation. func GenerateChain(parent *types.Block, db ethdb.Database, n int, gen func(int, *BlockGen)) []*types.Block { statedb := state.New(parent.Root(), db) @@ -205,7 +205,7 @@ func newCanonical(n int, db ethdb.Database) (*BlockProcessor, error) { evmux := &event.TypeMux{} WriteTestNetGenesisBlock(db, 0) - chainman, _ := NewChainManager(db, FakePow{}, evmux) + chainman, _ := NewBlockChain(db, FakePow{}, evmux) bman := NewBlockProcessor(db, FakePow{}, chainman, evmux) bman.bc.SetProcessor(bman) parent := bman.bc.CurrentBlock() diff --git a/core/chain_makers_test.go b/core/chain_makers_test.go index ac18e5e0b..b33af8d87 100644 --- a/core/chain_makers_test.go +++ b/core/chain_makers_test.go @@ -77,7 +77,7 @@ func ExampleGenerateChain() { // Import the chain. This runs all block validation rules. evmux := &event.TypeMux{} - chainman, _ := NewChainManager(db, FakePow{}, evmux) + chainman, _ := NewBlockChain(db, FakePow{}, evmux) chainman.SetProcessor(NewBlockProcessor(db, FakePow{}, chainman, evmux)) if i, err := chainman.InsertChain(chain); err != nil { fmt.Printf("insert error (block %d): %v\n", i, err) diff --git a/core/events.go b/core/events.go index e142b6dba..8cf230dda 100644 --- a/core/events.go +++ b/core/events.go @@ -20,8 +20,8 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" ) // TxPreEvent is posted when a transaction enters the transaction pool. @@ -42,23 +42,23 @@ type RemovedTransactionEvent struct{ Txs types.Transactions } // ChainSplit is posted when a new head is detected type ChainSplitEvent struct { Block *types.Block - Logs state.Logs + Logs vm.Logs } type ChainEvent struct { Block *types.Block Hash common.Hash - Logs state.Logs + Logs vm.Logs } type ChainSideEvent struct { Block *types.Block - Logs state.Logs + Logs vm.Logs } type PendingBlockEvent struct { Block *types.Block - Logs state.Logs + Logs vm.Logs } type ChainUncleEvent struct { diff --git a/core/execution.go b/core/execution.go index 3a136515d..e3c00a2ea 100644 --- a/core/execution.go +++ b/core/execution.go @@ -17,108 +17,104 @@ package core import ( + "errors" "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" ) -// Execution is the execution environment for the given call or create action. -type Execution struct { - env vm.Environment - address *common.Address - input []byte - evm vm.VirtualMachine - - Gas, price, value *big.Int +// Call executes within the given contract +func Call(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) { + ret, _, err = exec(env, caller, &addr, &addr, input, env.Db().GetCode(addr), gas, gasPrice, value) + return ret, err } -// NewExecution returns a new execution environment that handles all calling -// and creation logic defined by the YP. -func NewExecution(env vm.Environment, address *common.Address, input []byte, gas, gasPrice, value *big.Int) *Execution { - exe := &Execution{env: env, address: address, input: input, Gas: gas, price: gasPrice, value: value} - exe.evm = vm.NewVm(env) - return exe +// CallCode executes the given address' code as the given contract address +func CallCode(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) { + prev := caller.Address() + ret, _, err = exec(env, caller, &prev, &addr, input, env.Db().GetCode(addr), gas, gasPrice, value) + return ret, err } -// Call executes within the given context -func (self *Execution) Call(codeAddr common.Address, caller vm.ContextRef) ([]byte, error) { - // Retrieve the executing code - code := self.env.State().GetCode(codeAddr) - - return self.exec(&codeAddr, code, caller) -} - -// Create creates a new contract and runs the initialisation procedure of the -// contract. This returns the returned code for the contract and is stored -// elsewhere. -func (self *Execution) Create(caller vm.ContextRef) (ret []byte, err error, account *state.StateObject) { - // Input must be nil for create - code := self.input - self.input = nil - ret, err = self.exec(nil, code, caller) +// Create creates a new contract with the given code +func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, gasPrice, value *big.Int) (ret []byte, address common.Address, err error) { + ret, address, err = exec(env, caller, nil, nil, nil, code, gas, gasPrice, value) // Here we get an error if we run into maximum stack depth, // See: https://github.com/ethereum/yellowpaper/pull/131 // and YP definitions for CREATE instruction if err != nil { - return nil, err, nil + return nil, address, err } - account = self.env.State().GetStateObject(*self.address) - return + return ret, address, err } -// exec executes the given code and executes within the contextAddr context. -func (self *Execution) exec(contextAddr *common.Address, code []byte, caller vm.ContextRef) (ret []byte, err error) { - env := self.env - evm := self.evm +func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.Address, input, code []byte, gas, gasPrice, value *big.Int) (ret []byte, addr common.Address, err error) { + evm := vm.NewVm(env) + // Depth check execution. Fail if we're trying to execute above the // limit. if env.Depth() > int(params.CallCreateDepth.Int64()) { - caller.ReturnGas(self.Gas, self.price) + caller.ReturnGas(gas, gasPrice) - return nil, vm.DepthError + return nil, common.Address{}, vm.DepthError } - if !env.CanTransfer(env.State().GetStateObject(caller.Address()), self.value) { - caller.ReturnGas(self.Gas, self.price) + if !env.CanTransfer(caller.Address(), value) { + caller.ReturnGas(gas, gasPrice) - return nil, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", self.value, env.State().GetBalance(caller.Address())) + return nil, common.Address{}, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address())) } var createAccount bool - if self.address == nil { + if address == nil { // Generate a new address - nonce := env.State().GetNonce(caller.Address()) - env.State().SetNonce(caller.Address(), nonce+1) + nonce := env.Db().GetNonce(caller.Address()) + env.Db().SetNonce(caller.Address(), nonce+1) - addr := crypto.CreateAddress(caller.Address(), nonce) + addr = crypto.CreateAddress(caller.Address(), nonce) - self.address = &addr + address = &addr createAccount = true } - snapshot := env.State().Copy() + snapshot := env.MakeSnapshot() var ( - from = env.State().GetStateObject(caller.Address()) - to *state.StateObject + from = env.Db().GetAccount(caller.Address()) + to vm.Account ) if createAccount { - to = env.State().CreateAccount(*self.address) + to = env.Db().CreateAccount(*address) } else { - to = env.State().GetOrNewStateObject(*self.address) + if !env.Db().Exist(*address) { + to = env.Db().CreateAccount(*address) + } else { + to = env.Db().GetAccount(*address) + } } - vm.Transfer(from, to, self.value) + env.Transfer(from, to, value) - context := vm.NewContext(caller, to, self.value, self.Gas, self.price) - context.SetCallCode(contextAddr, code) + contract := vm.NewContract(caller, to, value, gas, gasPrice) + contract.SetCallCode(codeAddr, code) - ret, err = evm.Run(context, self.input) + ret, err = evm.Run(contract, input) if err != nil { - env.State().Set(snapshot) + env.SetSnapshot(snapshot) //env.Db().Set(snapshot) } - return + return ret, addr, err +} + +// generic transfer method +func Transfer(from, to vm.Account, amount *big.Int) error { + if from.Balance().Cmp(amount) < 0 { + return errors.New("Insufficient balance in account") + } + + from.SubBalance(amount) + to.AddBalance(amount) + + return nil } diff --git a/core/filter.go b/core/filter.go deleted file mode 100644 index b328ffff3..000000000 --- a/core/filter.go +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright 2014 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package core - -import ( - "math" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/logger/glog" -) - -type AccountChange struct { - Address, StateAddress []byte -} - -// Filtering interface -type Filter struct { - eth Backend - earliest int64 - latest int64 - skip int - address []common.Address - max int - topics [][]common.Hash - - BlockCallback func(*types.Block, state.Logs) - TransactionCallback func(*types.Transaction) - 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} -} - -// 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 []common.Address) { - self.address = addr -} - -func (self *Filter) SetTopics(topics [][]common.Hash) { - 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) - ) - -done: - for i := 0; block != nil; i++ { - // Quit on latest - switch { - case block.NumberU64() == 0: - break done - case block.NumberU64() < earliestBlockNo: - break done - case self.max <= len(logs): - break done - } - - // 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 { - glog.V(logger.Warn).Infoln("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 []common.Address, a common.Address) bool { - for _, addr := range addresses { - if addr == a { - return true - } - } - - return false -} - -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([]common.Hash, len(self.topics)) - copy(logTopics, log.Topics) - - // If the to filtered topics is greater than the amount of topics in - // logs, skip. - if len(self.topics) > len(log.Topics) { - continue Logs - } - - for i, topics := range self.topics { - var match bool - for _, topic := range topics { - // common.Hash{} is a match all (wildcard) - if (topic == common.Hash{}) || log.Topics[i] == topic { - match = true - break - } - } - - 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 (topic == common.Hash{}) || types.BloomLookup(block.Bloom(), topic) { - included = true - break - } - } - if !included { - return false - } - } - - return true -} diff --git a/core/helper_test.go b/core/helper_test.go index 81ea6fc22..fd6a5491c 100644 --- a/core/helper_test.go +++ b/core/helper_test.go @@ -34,7 +34,7 @@ type TestManager struct { db ethdb.Database txPool *TxPool - blockChain *ChainManager + blockChain *BlockChain Blocks []*types.Block } @@ -54,7 +54,7 @@ func (s *TestManager) Peers() *list.List { return list.New() } -func (s *TestManager) ChainManager() *ChainManager { +func (s *TestManager) BlockChain() *BlockChain { return s.blockChain } @@ -89,7 +89,7 @@ func NewTestManager() *TestManager { testManager.eventMux = new(event.TypeMux) testManager.db = db // testManager.txPool = NewTxPool(testManager) - // testManager.blockChain = NewChainManager(testManager) + // testManager.blockChain = NewBlockChain(testManager) // testManager.stateManager = NewStateManager(testManager) return testManager diff --git a/core/manager.go b/core/manager.go index 0f108a6de..289c87c11 100644 --- a/core/manager.go +++ b/core/manager.go @@ -26,7 +26,7 @@ import ( type Backend interface { AccountManager() *accounts.Manager BlockProcessor() *BlockProcessor - ChainManager() *ChainManager + BlockChain() *BlockChain TxPool() *TxPool ChainDb() ethdb.Database DappDb() ethdb.Database diff --git a/core/state/statedb.go b/core/state/statedb.go index 4233c763b..499ea5f52 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -21,6 +21,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" @@ -42,7 +43,7 @@ type StateDB struct { thash, bhash common.Hash txIndex int - logs map[common.Hash]Logs + logs map[common.Hash]vm.Logs logSize uint } @@ -59,7 +60,7 @@ func New(root common.Hash, db ethdb.Database) *StateDB { trie: tr, stateObjects: make(map[string]*StateObject), refund: new(big.Int), - logs: make(map[common.Hash]Logs), + logs: make(map[common.Hash]vm.Logs), } } @@ -69,7 +70,7 @@ func (self *StateDB) StartRecord(thash, bhash common.Hash, ti int) { self.txIndex = ti } -func (self *StateDB) AddLog(log *Log) { +func (self *StateDB) AddLog(log *vm.Log) { log.TxHash = self.thash log.BlockHash = self.bhash log.TxIndex = uint(self.txIndex) @@ -78,30 +79,34 @@ func (self *StateDB) AddLog(log *Log) { self.logSize++ } -func (self *StateDB) GetLogs(hash common.Hash) Logs { +func (self *StateDB) GetLogs(hash common.Hash) vm.Logs { return self.logs[hash] } -func (self *StateDB) Logs() Logs { - var logs Logs +func (self *StateDB) Logs() vm.Logs { + var logs vm.Logs for _, lgs := range self.logs { logs = append(logs, lgs...) } return logs } -func (self *StateDB) Refund(gas *big.Int) { +func (self *StateDB) AddRefund(gas *big.Int) { self.refund.Add(self.refund, gas) } -/* - * GETTERS - */ - func (self *StateDB) HasAccount(addr common.Address) bool { return self.GetStateObject(addr) != nil } +func (self *StateDB) Exist(addr common.Address) bool { + return self.GetStateObject(addr) != nil +} + +func (self *StateDB) GetAccount(addr common.Address) vm.Account { + return self.GetStateObject(addr) +} + // Retrieve the balance from the given address or 0 if object not found func (self *StateDB) GetBalance(addr common.Address) *big.Int { stateObject := self.GetStateObject(addr) @@ -245,7 +250,7 @@ func (self *StateDB) SetStateObject(object *StateObject) { func (self *StateDB) GetOrNewStateObject(addr common.Address) *StateObject { stateObject := self.GetStateObject(addr) if stateObject == nil || stateObject.deleted { - stateObject = self.CreateAccount(addr) + stateObject = self.CreateStateObject(addr) } return stateObject @@ -264,7 +269,7 @@ func (self *StateDB) newStateObject(addr common.Address) *StateObject { } // Creates creates a new state object and takes ownership. This is different from "NewStateObject" -func (self *StateDB) CreateAccount(addr common.Address) *StateObject { +func (self *StateDB) CreateStateObject(addr common.Address) *StateObject { // Get previous (if any) so := self.GetStateObject(addr) // Create a new one @@ -278,6 +283,10 @@ func (self *StateDB) CreateAccount(addr common.Address) *StateObject { return newSo } +func (self *StateDB) CreateAccount(addr common.Address) vm.Account { + return self.CreateStateObject(addr) +} + // // Setting, copying of the state methods // @@ -292,7 +301,7 @@ func (self *StateDB) Copy() *StateDB { state.refund.Set(self.refund) for hash, logs := range self.logs { - state.logs[hash] = make(Logs, len(logs)) + state.logs[hash] = make(vm.Logs, len(logs)) copy(state.logs[hash], logs) } state.logSize = self.logSize @@ -309,6 +318,10 @@ func (self *StateDB) Set(state *StateDB) { self.logSize = state.logSize } +func (self *StateDB) GetRefund() *big.Int { + return self.refund +} + // IntermediateRoot computes the current root hash of the state trie. // It is called in between transactions to get the root hash that // goes into transaction receipts. diff --git a/core/state_transition.go b/core/state_transition.go index 6ff7fa1ff..e83019229 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -51,7 +51,7 @@ type StateTransition struct { initialGas *big.Int value *big.Int data []byte - state *state.StateDB + state vm.Database env vm.Environment } @@ -95,11 +95,7 @@ func IntrinsicGas(data []byte) *big.Int { } func ApplyMessage(env vm.Environment, msg Message, gp GasPool) ([]byte, *big.Int, error) { - return NewStateTransition(env, msg, gp).transitionState() -} - -func NewStateTransition(env vm.Environment, msg Message, gp GasPool) *StateTransition { - return &StateTransition{ + var st = StateTransition{ gp: gp, env: env, msg: msg, @@ -108,18 +104,22 @@ func NewStateTransition(env vm.Environment, msg Message, gp GasPool) *StateTrans initialGas: new(big.Int), value: msg.Value(), data: msg.Data(), - state: env.State(), + state: env.Db(), } + return st.transitionDb() } -func (self *StateTransition) From() (*state.StateObject, error) { +func (self *StateTransition) from() (vm.Account, error) { f, err := self.msg.From() if err != nil { return nil, err } - return self.state.GetOrNewStateObject(f), nil + if !self.state.Exist(f) { + return self.state.CreateAccount(f), nil + } + return self.state.GetAccount(f), nil } -func (self *StateTransition) To() *state.StateObject { +func (self *StateTransition) to() vm.Account { if self.msg == nil { return nil } @@ -127,10 +127,14 @@ func (self *StateTransition) To() *state.StateObject { if to == nil { return nil // contract creation } - return self.state.GetOrNewStateObject(*to) + + if !self.state.Exist(*to) { + return self.state.CreateAccount(*to) + } + return self.state.GetAccount(*to) } -func (self *StateTransition) UseGas(amount *big.Int) error { +func (self *StateTransition) useGas(amount *big.Int) error { if self.gas.Cmp(amount) < 0 { return vm.OutOfGasError } @@ -139,15 +143,15 @@ func (self *StateTransition) UseGas(amount *big.Int) error { return nil } -func (self *StateTransition) AddGas(amount *big.Int) { +func (self *StateTransition) addGas(amount *big.Int) { self.gas.Add(self.gas, amount) } -func (self *StateTransition) BuyGas() error { +func (self *StateTransition) buyGas() error { mgas := self.msg.Gas() mgval := new(big.Int).Mul(mgas, self.gasPrice) - sender, err := self.From() + sender, err := self.from() if err != nil { return err } @@ -157,7 +161,7 @@ func (self *StateTransition) BuyGas() error { if err = self.gp.SubGas(mgas, self.gasPrice); err != nil { return err } - self.AddGas(mgas) + self.addGas(mgas) self.initialGas.Set(mgas) sender.SubBalance(mgval) return nil @@ -165,18 +169,19 @@ func (self *StateTransition) BuyGas() error { func (self *StateTransition) preCheck() (err error) { msg := self.msg - sender, err := self.From() + sender, err := self.from() if err != nil { return err } // Make sure this transaction's nonce is correct - if sender.Nonce() != msg.Nonce() { - return NonceError(msg.Nonce(), sender.Nonce()) + //if sender.Nonce() != msg.Nonce() { + if n := self.state.GetNonce(sender.Address()); n != msg.Nonce() { + return NonceError(msg.Nonce(), n) } // Pre-pay gas / Buy gas of the coinbase account - if err = self.BuyGas(); err != nil { + if err = self.buyGas(); err != nil { if state.IsGasLimitErr(err) { return err } @@ -186,28 +191,28 @@ func (self *StateTransition) preCheck() (err error) { return nil } -func (self *StateTransition) transitionState() (ret []byte, usedGas *big.Int, err error) { +func (self *StateTransition) transitionDb() (ret []byte, usedGas *big.Int, err error) { if err = self.preCheck(); err != nil { return } msg := self.msg - sender, _ := self.From() // err checked in preCheck + sender, _ := self.from() // err checked in preCheck // Pay intrinsic gas - if err = self.UseGas(IntrinsicGas(self.data)); err != nil { + if err = self.useGas(IntrinsicGas(self.data)); err != nil { return nil, nil, InvalidTxError(err) } vmenv := self.env - var ref vm.ContextRef + var addr common.Address if MessageCreatesContract(msg) { - ret, err, ref = vmenv.Create(sender, self.data, self.gas, self.gasPrice, self.value) + ret, addr, err = vmenv.Create(sender, self.data, self.gas, self.gasPrice, self.value) if err == nil { dataGas := big.NewInt(int64(len(ret))) dataGas.Mul(dataGas, params.CreateDataGas) - if err := self.UseGas(dataGas); err == nil { - ref.SetCode(ret) + if err := self.useGas(dataGas); err == nil { + self.state.SetCode(addr, ret) } else { ret = nil // does not affect consensus but useful for StateTests validations glog.V(logger.Core).Infoln("Insufficient gas for creating code. Require", dataGas, "and have", self.gas) @@ -216,8 +221,8 @@ func (self *StateTransition) transitionState() (ret []byte, usedGas *big.Int, er glog.V(logger.Core).Infoln("VM create err:", err) } else { // Increment the nonce for the next transaction - self.state.SetNonce(sender.Address(), sender.Nonce()+1) - ret, err = vmenv.Call(sender, self.To().Address(), self.data, self.gas, self.gasPrice, self.value) + self.state.SetNonce(sender.Address(), self.state.GetNonce(sender.Address())+1) + ret, err = vmenv.Call(sender, self.to().Address(), self.data, self.gas, self.gasPrice, self.value) glog.V(logger.Core).Infoln("VM call err:", err) } @@ -241,13 +246,13 @@ func (self *StateTransition) transitionState() (ret []byte, usedGas *big.Int, er } func (self *StateTransition) refundGas() { - sender, _ := self.From() // err already checked + sender, _ := self.from() // err already checked // Return remaining gas remaining := new(big.Int).Mul(self.gas, self.gasPrice) sender.AddBalance(remaining) uhalf := remaining.Div(self.gasUsed(), common.Big2) - refund := common.BigMin(uhalf, self.state.Refunds()) + refund := common.BigMin(uhalf, self.state.GetRefund()) self.gas.Add(self.gas, refund) self.state.AddBalance(sender.Address(), refund.Mul(refund, self.gasPrice)) diff --git a/core/types/bloom9.go b/core/types/bloom9.go index 0629b31d4..f87ae58e6 100644 --- a/core/types/bloom9.go +++ b/core/types/bloom9.go @@ -20,7 +20,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" ) @@ -37,7 +37,7 @@ func CreateBloom(receipts Receipts) Bloom { return BytesToBloom(bin.Bytes()) } -func LogsBloom(logs state.Logs) *big.Int { +func LogsBloom(logs vm.Logs) *big.Int { bin := new(big.Int) for _, log := range logs { data := make([]common.Hash, len(log.Topics)) diff --git a/core/types/common.go b/core/types/common.go index de6efcd86..dc428c00c 100644 --- a/core/types/common.go +++ b/core/types/common.go @@ -19,14 +19,14 @@ package types import ( "math/big" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" - "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" ) type BlockProcessor interface { - Process(*Block) (state.Logs, Receipts, error) + Process(*Block) (vm.Logs, Receipts, error) } const bloomLength = 256 diff --git a/core/types/receipt.go b/core/types/receipt.go index e01d69005..bcb4bd8a5 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -23,7 +23,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/rlp" ) @@ -33,7 +33,7 @@ type Receipt struct { Bloom Bloom TxHash common.Hash ContractAddress common.Address - logs state.Logs + logs vm.Logs GasUsed *big.Int } @@ -41,11 +41,11 @@ func NewReceipt(root []byte, cumalativeGasUsed *big.Int) *Receipt { return &Receipt{PostState: common.CopyBytes(root), CumulativeGasUsed: new(big.Int).Set(cumalativeGasUsed)} } -func (self *Receipt) SetLogs(logs state.Logs) { +func (self *Receipt) SetLogs(logs vm.Logs) { self.logs = logs } -func (self *Receipt) Logs() state.Logs { +func (self *Receipt) Logs() vm.Logs { return self.logs } @@ -60,7 +60,7 @@ func (self *Receipt) DecodeRLP(s *rlp.Stream) error { Bloom Bloom TxHash common.Hash ContractAddress common.Address - Logs state.Logs + Logs vm.Logs GasUsed *big.Int } if err := s.Decode(&r); err != nil { @@ -74,9 +74,9 @@ func (self *Receipt) DecodeRLP(s *rlp.Stream) error { type ReceiptForStorage Receipt func (self *ReceiptForStorage) EncodeRLP(w io.Writer) error { - storageLogs := make([]*state.LogForStorage, len(self.logs)) + storageLogs := make([]*vm.LogForStorage, len(self.logs)) for i, log := range self.logs { - storageLogs[i] = (*state.LogForStorage)(log) + storageLogs[i] = (*vm.LogForStorage)(log) } return rlp.Encode(w, []interface{}{self.PostState, self.CumulativeGasUsed, self.Bloom, self.TxHash, self.ContractAddress, storageLogs, self.GasUsed}) } diff --git a/core/vm/asm.go b/core/vm/asm.go index 639201e50..065d3eb97 100644 --- a/core/vm/asm.go +++ b/core/vm/asm.go @@ -23,6 +23,8 @@ import ( "github.com/ethereum/go-ethereum/common" ) +// Dissassemble dissassembles the byte code and returns the string +// representation (human readable opcodes). func Disassemble(script []byte) (asm []string) { pc := new(big.Int) for { diff --git a/core/vm/common.go b/core/vm/common.go index 2e03ec80b..2d1aa9332 100644 --- a/core/vm/common.go +++ b/core/vm/common.go @@ -22,34 +22,34 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/params" ) // Global Debug flag indicating Debug VM (full logging) var Debug bool +// Type is the VM type accepted by **NewVm** type Type byte const ( - StdVmTy Type = iota - JitVmTy + StdVmTy Type = iota // Default standard VM + JitVmTy // LLVM JIT VM MaxVmTy - - LogTyPretty byte = 0x1 - LogTyDiff byte = 0x2 ) var ( - Pow256 = common.BigPow(2, 256) + Pow256 = common.BigPow(2, 256) // Pow256 is 2**256 - U256 = common.U256 - S256 = common.S256 + U256 = common.U256 // Shortcut to common.U256 + S256 = common.S256 // Shortcut to common.S256 - Zero = common.Big0 - One = common.Big1 + Zero = common.Big0 // Shortcut to common.Big0 + One = common.Big1 // Shortcut to common.Big1 - max = big.NewInt(math.MaxInt64) + max = big.NewInt(math.MaxInt64) // Maximum 64 bit integer ) +// NewVm returns a new VM based on the Environment func NewVm(env Environment) VirtualMachine { switch env.VmType() { case JitVmTy: @@ -62,6 +62,7 @@ func NewVm(env Environment) VirtualMachine { } } +// calculates the memory size required for a step func calcMemSize(off, l *big.Int) *big.Int { if l.Cmp(common.Big0) == 0 { return common.Big0 @@ -70,6 +71,32 @@ func calcMemSize(off, l *big.Int) *big.Int { return new(big.Int).Add(off, l) } +// calculates the quadratic gas +func quadMemGas(mem *Memory, newMemSize, gas *big.Int) { + if newMemSize.Cmp(common.Big0) > 0 { + newMemSizeWords := toWordSize(newMemSize) + newMemSize.Mul(newMemSizeWords, u256(32)) + + if newMemSize.Cmp(u256(int64(mem.Len()))) > 0 { + // be careful reusing variables here when changing. + // The order has been optimised to reduce allocation + oldSize := toWordSize(big.NewInt(int64(mem.Len()))) + pow := new(big.Int).Exp(oldSize, common.Big2, Zero) + linCoef := oldSize.Mul(oldSize, params.MemoryGas) + quadCoef := new(big.Int).Div(pow, params.QuadCoeffDiv) + oldTotalFee := new(big.Int).Add(linCoef, quadCoef) + + pow.Exp(newMemSizeWords, common.Big2, Zero) + linCoef = linCoef.Mul(newMemSizeWords, params.MemoryGas) + quadCoef = quadCoef.Div(pow, params.QuadCoeffDiv) + newTotalFee := linCoef.Add(linCoef, quadCoef) + + fee := newTotalFee.Sub(newTotalFee, oldTotalFee) + gas.Add(gas, fee) + } + } +} + // Simple helper func u256(n int64) *big.Int { return big.NewInt(n) @@ -86,6 +113,8 @@ func toValue(val *big.Int) interface{} { return val } +// getData returns a slice from the data based on the start and size and pads +// up to size with zero's. This function is overflow safe. func getData(data []byte, start, size *big.Int) []byte { dlen := big.NewInt(int64(len(data))) @@ -94,7 +123,9 @@ func getData(data []byte, start, size *big.Int) []byte { return common.RightPadBytes(data[s.Uint64():e.Uint64()], int(size.Uint64())) } -func UseGas(gas, amount *big.Int) bool { +// useGas attempts to subtract the amount of gas and returns whether it was +// successful +func useGas(gas, amount *big.Int) bool { if gas.Cmp(amount) < 0 { return false } diff --git a/core/vm/context.go b/core/vm/contract.go index d17934ba5..95417e747 100644 --- a/core/vm/context.go +++ b/core/vm/contract.go @@ -22,15 +22,18 @@ import ( "github.com/ethereum/go-ethereum/common" ) -type ContextRef interface { +// ContractRef is a reference to the contract's backing object +type ContractRef interface { ReturnGas(*big.Int, *big.Int) Address() common.Address SetCode([]byte) } -type Context struct { - caller ContextRef - self ContextRef +// Contract represents an ethereum contract in the state database. It contains +// the the contract code, calling arguments. Contract implements ContractReg +type Contract struct { + caller ContractRef + self ContractRef jumpdests destinations // result of JUMPDEST analysis. @@ -44,10 +47,10 @@ type Context struct { } // Create a new context for the given data items. -func NewContext(caller ContextRef, object ContextRef, value, gas, price *big.Int) *Context { - c := &Context{caller: caller, self: object, Args: nil} +func NewContract(caller ContractRef, object ContractRef, value, gas, price *big.Int) *Contract { + c := &Contract{caller: caller, self: object, Args: nil} - if parent, ok := caller.(*Context); ok { + if parent, ok := caller.(*Contract); ok { // Reuse JUMPDEST analysis from parent context if available. c.jumpdests = parent.jumpdests } else { @@ -66,11 +69,13 @@ func NewContext(caller ContextRef, object ContextRef, value, gas, price *big.Int return c } -func (c *Context) GetOp(n uint64) OpCode { +// GetOp returns the n'th element in the contract's byte array +func (c *Contract) GetOp(n uint64) OpCode { return OpCode(c.GetByte(n)) } -func (c *Context) GetByte(n uint64) byte { +// GetByte returns the n'th byte in the contract's byte array +func (c *Contract) GetByte(n uint64) byte { if n < uint64(len(c.Code)) { return c.Code[n] } @@ -78,43 +83,44 @@ func (c *Context) GetByte(n uint64) byte { return 0 } -func (c *Context) Return(ret []byte) []byte { +// Return returns the given ret argument and returns any remaining gas to the +// caller +func (c *Contract) Return(ret []byte) []byte { // Return the remaining gas to the caller c.caller.ReturnGas(c.Gas, c.Price) return ret } -/* - * Gas functions - */ -func (c *Context) UseGas(gas *big.Int) (ok bool) { - ok = UseGas(c.Gas, gas) +// UseGas attempts the use gas and subtracts it and returns true on success +func (c *Contract) UseGas(gas *big.Int) (ok bool) { + ok = useGas(c.Gas, gas) if ok { c.UsedGas.Add(c.UsedGas, gas) } return } -// Implement the caller interface -func (c *Context) ReturnGas(gas, price *big.Int) { +// ReturnGas adds the given gas back to itself. +func (c *Contract) ReturnGas(gas, price *big.Int) { // Return the gas to the context c.Gas.Add(c.Gas, gas) c.UsedGas.Sub(c.UsedGas, gas) } -/* - * Set / Get - */ -func (c *Context) Address() common.Address { +// Address returns the contracts address +func (c *Contract) Address() common.Address { return c.self.Address() } -func (self *Context) SetCode(code []byte) { +// SetCode sets the code to the contract +func (self *Contract) SetCode(code []byte) { self.Code = code } -func (self *Context) SetCallCode(addr *common.Address, code []byte) { +// SetCallCode sets the code of the contract and address of the backing data +// object +func (self *Contract) SetCallCode(addr *common.Address, code []byte) { self.Code = code self.CodeAddr = addr } diff --git a/core/vm/contracts.go b/core/vm/contracts.go index b965fa095..22cb9eab2 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -26,22 +26,22 @@ import ( "github.com/ethereum/go-ethereum/params" ) -type Address interface { - Call(in []byte) []byte -} - +// PrecompiledAccount represents a native ethereum contract type PrecompiledAccount struct { Gas func(l int) *big.Int fn func(in []byte) []byte } +// Call calls the native function func (self PrecompiledAccount) Call(in []byte) []byte { return self.fn(in) } +// Precompiled contains the default set of ethereum contracts var Precompiled = PrecompiledContracts() -// XXX Could set directly. Testing requires resetting and setting of pre compiled contracts. +// PrecompiledContracts returns the default set of precompiled ethereum +// contracts defined by the ethereum yellow paper. func PrecompiledContracts() map[string]*PrecompiledAccount { return map[string]*PrecompiledAccount{ // ECRECOVER diff --git a/core/vm/settings.go b/core/vm/doc.go index f9296f6c8..ab87bf934 100644 --- a/core/vm/settings.go +++ b/core/vm/doc.go @@ -14,12 +14,19 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. -package vm +/* +Package vm implements the Ethereum Virtual Machine. -var ( - EnableJit bool // Enables the JIT VM - ForceJit bool // Force the JIT, skip byte VM - MaxProgSize int // Max cache size for JIT Programs -) +The vm package implements two EVMs, a byte code VM and a JIT VM. The BC +(Byte Code) VM loops over a set of bytes and executes them according to the set +of rules defined in the Ethereum yellow paper. When the BC VM is invoked it +invokes the JIT VM in a seperate goroutine and compiles the byte code in JIT +instructions. -const defaultJitMaxCache int = 64 +The JIT VM, when invoked, loops around a set of pre-defined instructions until +it either runs of gas, causes an internal error, returns or stops. At a later +stage the JIT VM will see some additional features that will cause sets of +instructions to be compiled down to segments. Segments are sets of instructions +that can be run in one go saving precious time during execution. +*/ +package vm diff --git a/core/vm/environment.go b/core/vm/environment.go index 916081f51..f8e19baea 100644 --- a/core/vm/environment.go +++ b/core/vm/environment.go @@ -17,39 +17,86 @@ package vm import ( - "errors" "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" ) // Environment is is required by the virtual machine to get information from -// it's own isolated environment. For an example see `core.VMEnv` -type Environment interface { - State() *state.StateDB +// it's own isolated environment. +// Environment is an EVM requirement and helper which allows access to outside +// information such as states. +type Environment interface { + // The state database + Db() Database + // Creates a restorable snapshot + MakeSnapshot() Database + // Set database to previous snapshot + SetSnapshot(Database) + // Address of the original invoker (first occurance of the VM invoker) Origin() common.Address + // The block number this VM is invoken on BlockNumber() *big.Int + // The n'th hash ago from this block number GetHash(n uint64) common.Hash + // The handler's address Coinbase() common.Address + // The current time (block time) Time() *big.Int + // Difficulty set on the current block Difficulty() *big.Int + // The gas limit of the block GasLimit() *big.Int - CanTransfer(from Account, balance *big.Int) bool + // Determines whether it's possible to transact + CanTransfer(from common.Address, balance *big.Int) bool + // Transfers amount from one account to the other Transfer(from, to Account, amount *big.Int) error - AddLog(*state.Log) + // Adds a LOG to the state + AddLog(*Log) + // Adds a structured log to the env AddStructLog(StructLog) + // Returns all coalesced structured logs StructLogs() []StructLog + // Type of the VM VmType() Type + // Current calling depth Depth() int SetDepth(i int) - Call(me ContextRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) - CallCode(me ContextRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) - Create(me ContextRef, data []byte, gas, price, value *big.Int) ([]byte, error, ContextRef) + // Call another contract + Call(me ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) + // Take another's contract code and execute within our own context + CallCode(me ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) + // Create a new contract + Create(me ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) +} + +// Database is a EVM database for full state querying +type Database interface { + GetAccount(common.Address) Account + CreateAccount(common.Address) Account + + AddBalance(common.Address, *big.Int) + GetBalance(common.Address) *big.Int + + GetNonce(common.Address) uint64 + SetNonce(common.Address, uint64) + + GetCode(common.Address) []byte + SetCode(common.Address, []byte) + + AddRefund(*big.Int) + GetRefund() *big.Int + + GetState(common.Address, common.Hash) common.Hash + SetState(common.Address, common.Hash, common.Hash) + + Delete(common.Address) bool + Exist(common.Address) bool + IsDeleted(common.Address) bool } // StructLog is emited to the Environment each cycle and lists information about the curent internal state @@ -68,18 +115,10 @@ type StructLog struct { type Account interface { SubBalance(amount *big.Int) AddBalance(amount *big.Int) + SetBalance(*big.Int) + SetNonce(uint64) Balance() *big.Int Address() common.Address -} - -// generic transfer method -func Transfer(from, to Account, amount *big.Int) error { - if from.Balance().Cmp(amount) < 0 { - return errors.New("Insufficient balance in account") - } - - from.SubBalance(amount) - to.AddBalance(amount) - - return nil + ReturnGas(*big.Int, *big.Int) + SetCode([]byte) } diff --git a/core/vm/gas.go b/core/vm/gas.go index b2f068e6e..bff0ac91b 100644 --- a/core/vm/gas.go +++ b/core/vm/gas.go @@ -37,6 +37,7 @@ var ( GasContractByte = big.NewInt(200) ) +// baseCheck checks for any stack error underflows func baseCheck(op OpCode, stack *stack, gas *big.Int) error { // PUSH and DUP are a bit special. They all cost the same but we do want to have checking on stack push limit // PUSH is also allowed to calculate the same price for all PUSHes @@ -63,6 +64,7 @@ func baseCheck(op OpCode, stack *stack, gas *big.Int) error { return nil } +// casts a arbitrary number to the amount of words (sets of 32 bytes) func toWordSize(size *big.Int) *big.Int { tmp := new(big.Int) tmp.Add(size, u256(31)) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index aa0117cc8..6c6039f74 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -20,46 +20,52 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" ) -type instrFn func(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) -type instrExFn func(instr instruction, ret *big.Int, env Environment, context *Context, memory *Memory, stack *stack) +type programInstruction interface { + Do(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) +} + +type instrFn func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) + +// Do executes the function. This implements programInstruction +func (fn instrFn) Do(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { + fn(instr, pc, env, contract, memory, stack) +} type instruction struct { - op OpCode - pc uint64 - fn instrFn - specFn instrExFn - data *big.Int + op OpCode + pc uint64 + fn instrFn + data *big.Int gas *big.Int spop int spush int } -func opStaticJump(instr instruction, ret *big.Int, env Environment, context *Context, memory *Memory, stack *stack) { +func opStaticJump(instr instruction, pc *uint64, ret *big.Int, env Environment, contract *Contract, memory *Memory, stack *stack) { ret.Set(instr.data) } -func opAdd(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opAdd(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { x, y := stack.pop(), stack.pop() stack.push(U256(x.Add(x, y))) } -func opSub(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opSub(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { x, y := stack.pop(), stack.pop() stack.push(U256(x.Sub(x, y))) } -func opMul(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opMul(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { x, y := stack.pop(), stack.pop() stack.push(U256(x.Mul(x, y))) } -func opDiv(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opDiv(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { x, y := stack.pop(), stack.pop() if y.Cmp(common.Big0) != 0 { stack.push(U256(x.Div(x, y))) @@ -68,7 +74,7 @@ func opDiv(instr instruction, env Environment, context *Context, memory *Memory, } } -func opSdiv(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opSdiv(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { x, y := S256(stack.pop()), S256(stack.pop()) if y.Cmp(common.Big0) == 0 { stack.push(new(big.Int)) @@ -88,7 +94,7 @@ func opSdiv(instr instruction, env Environment, context *Context, memory *Memory } } -func opMod(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opMod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { x, y := stack.pop(), stack.pop() if y.Cmp(common.Big0) == 0 { stack.push(new(big.Int)) @@ -97,7 +103,7 @@ func opMod(instr instruction, env Environment, context *Context, memory *Memory, } } -func opSmod(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opSmod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { x, y := S256(stack.pop()), S256(stack.pop()) if y.Cmp(common.Big0) == 0 { @@ -117,12 +123,12 @@ func opSmod(instr instruction, env Environment, context *Context, memory *Memory } } -func opExp(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opExp(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { x, y := stack.pop(), stack.pop() stack.push(U256(x.Exp(x, y, Pow256))) } -func opSignExtend(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opSignExtend(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { back := stack.pop() if back.Cmp(big.NewInt(31)) < 0 { bit := uint(back.Uint64()*8 + 7) @@ -139,12 +145,12 @@ func opSignExtend(instr instruction, env Environment, context *Context, memory * } } -func opNot(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opNot(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { x := stack.pop() stack.push(U256(x.Not(x))) } -func opLt(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opLt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { x, y := stack.pop(), stack.pop() if x.Cmp(y) < 0 { stack.push(big.NewInt(1)) @@ -153,7 +159,7 @@ func opLt(instr instruction, env Environment, context *Context, memory *Memory, } } -func opGt(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opGt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { x, y := stack.pop(), stack.pop() if x.Cmp(y) > 0 { stack.push(big.NewInt(1)) @@ -162,7 +168,7 @@ func opGt(instr instruction, env Environment, context *Context, memory *Memory, } } -func opSlt(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opSlt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { x, y := S256(stack.pop()), S256(stack.pop()) if x.Cmp(S256(y)) < 0 { stack.push(big.NewInt(1)) @@ -171,7 +177,7 @@ func opSlt(instr instruction, env Environment, context *Context, memory *Memory, } } -func opSgt(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opSgt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { x, y := S256(stack.pop()), S256(stack.pop()) if x.Cmp(y) > 0 { stack.push(big.NewInt(1)) @@ -180,7 +186,7 @@ func opSgt(instr instruction, env Environment, context *Context, memory *Memory, } } -func opEq(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opEq(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { x, y := stack.pop(), stack.pop() if x.Cmp(y) == 0 { stack.push(big.NewInt(1)) @@ -189,7 +195,7 @@ func opEq(instr instruction, env Environment, context *Context, memory *Memory, } } -func opIszero(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opIszero(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { x := stack.pop() if x.Cmp(common.Big0) > 0 { stack.push(new(big.Int)) @@ -198,19 +204,19 @@ func opIszero(instr instruction, env Environment, context *Context, memory *Memo } } -func opAnd(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opAnd(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { x, y := stack.pop(), stack.pop() stack.push(x.And(x, y)) } -func opOr(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opOr(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { x, y := stack.pop(), stack.pop() stack.push(x.Or(x, y)) } -func opXor(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opXor(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { x, y := stack.pop(), stack.pop() stack.push(x.Xor(x, y)) } -func opByte(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opByte(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { th, val := stack.pop(), stack.pop() if th.Cmp(big.NewInt(32)) < 0 { byte := big.NewInt(int64(common.LeftPadBytes(val.Bytes(), 32)[th.Int64()])) @@ -219,7 +225,7 @@ func opByte(instr instruction, env Environment, context *Context, memory *Memory stack.push(new(big.Int)) } } -func opAddmod(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opAddmod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { x, y, z := stack.pop(), stack.pop(), stack.pop() if z.Cmp(Zero) > 0 { add := x.Add(x, y) @@ -229,7 +235,7 @@ func opAddmod(instr instruction, env Environment, context *Context, memory *Memo stack.push(new(big.Int)) } } -func opMulmod(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opMulmod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { x, y, z := stack.pop(), stack.pop(), stack.pop() if z.Cmp(Zero) > 0 { mul := x.Mul(x, y) @@ -240,92 +246,92 @@ func opMulmod(instr instruction, env Environment, context *Context, memory *Memo } } -func opSha3(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opSha3(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { offset, size := stack.pop(), stack.pop() hash := crypto.Sha3(memory.Get(offset.Int64(), size.Int64())) stack.push(common.BytesToBig(hash)) } -func opAddress(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { - stack.push(common.Bytes2Big(context.Address().Bytes())) +func opAddress(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { + stack.push(common.Bytes2Big(contract.Address().Bytes())) } -func opBalance(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opBalance(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { addr := common.BigToAddress(stack.pop()) - balance := env.State().GetBalance(addr) + balance := env.Db().GetBalance(addr) stack.push(new(big.Int).Set(balance)) } -func opOrigin(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opOrigin(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { stack.push(env.Origin().Big()) } -func opCaller(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { - stack.push(common.Bytes2Big(context.caller.Address().Bytes())) +func opCaller(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { + stack.push(common.Bytes2Big(contract.caller.Address().Bytes())) } -func opCallValue(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { - stack.push(new(big.Int).Set(context.value)) +func opCallValue(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { + stack.push(new(big.Int).Set(contract.value)) } -func opCalldataLoad(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { - stack.push(common.Bytes2Big(getData(context.Input, stack.pop(), common.Big32))) +func opCalldataLoad(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { + stack.push(common.Bytes2Big(getData(contract.Input, stack.pop(), common.Big32))) } -func opCalldataSize(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { - stack.push(big.NewInt(int64(len(context.Input)))) +func opCalldataSize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { + stack.push(big.NewInt(int64(len(contract.Input)))) } -func opCalldataCopy(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opCalldataCopy(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { var ( mOff = stack.pop() cOff = stack.pop() l = stack.pop() ) - memory.Set(mOff.Uint64(), l.Uint64(), getData(context.Input, cOff, l)) + memory.Set(mOff.Uint64(), l.Uint64(), getData(contract.Input, cOff, l)) } -func opExtCodeSize(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opExtCodeSize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { addr := common.BigToAddress(stack.pop()) - l := big.NewInt(int64(len(env.State().GetCode(addr)))) + l := big.NewInt(int64(len(env.Db().GetCode(addr)))) stack.push(l) } -func opCodeSize(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { - l := big.NewInt(int64(len(context.Code))) +func opCodeSize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { + l := big.NewInt(int64(len(contract.Code))) stack.push(l) } -func opCodeCopy(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opCodeCopy(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { var ( mOff = stack.pop() cOff = stack.pop() l = stack.pop() ) - codeCopy := getData(context.Code, cOff, l) + codeCopy := getData(contract.Code, cOff, l) memory.Set(mOff.Uint64(), l.Uint64(), codeCopy) } -func opExtCodeCopy(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opExtCodeCopy(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { var ( addr = common.BigToAddress(stack.pop()) mOff = stack.pop() cOff = stack.pop() l = stack.pop() ) - codeCopy := getData(env.State().GetCode(addr), cOff, l) + codeCopy := getData(env.Db().GetCode(addr), cOff, l) memory.Set(mOff.Uint64(), l.Uint64(), codeCopy) } -func opGasprice(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { - stack.push(new(big.Int).Set(context.Price)) +func opGasprice(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { + stack.push(new(big.Int).Set(contract.Price)) } -func opBlockhash(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opBlockhash(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { num := stack.pop() n := new(big.Int).Sub(env.BlockNumber(), common.Big257) @@ -336,43 +342,43 @@ func opBlockhash(instr instruction, env Environment, context *Context, memory *M } } -func opCoinbase(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opCoinbase(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { stack.push(env.Coinbase().Big()) } -func opTimestamp(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opTimestamp(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { stack.push(U256(new(big.Int).Set(env.Time()))) } -func opNumber(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opNumber(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { stack.push(U256(new(big.Int).Set(env.BlockNumber()))) } -func opDifficulty(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opDifficulty(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { stack.push(U256(new(big.Int).Set(env.Difficulty()))) } -func opGasLimit(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opGasLimit(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { stack.push(U256(new(big.Int).Set(env.GasLimit()))) } -func opPop(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opPop(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { stack.pop() } -func opPush(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opPush(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { stack.push(new(big.Int).Set(instr.data)) } -func opDup(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opDup(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { stack.dup(int(instr.data.Int64())) } -func opSwap(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opSwap(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { stack.swap(int(instr.data.Int64())) } -func opLog(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opLog(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { n := int(instr.data.Int64()) topics := make([]common.Hash, n) mStart, mSize := stack.pop(), stack.pop() @@ -381,85 +387,88 @@ func opLog(instr instruction, env Environment, context *Context, memory *Memory, } d := memory.Get(mStart.Int64(), mSize.Int64()) - log := state.NewLog(context.Address(), topics, d, env.BlockNumber().Uint64()) + log := NewLog(contract.Address(), topics, d, env.BlockNumber().Uint64()) env.AddLog(log) } -func opMload(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opMload(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { offset := stack.pop() val := common.BigD(memory.Get(offset.Int64(), 32)) stack.push(val) } -func opMstore(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opMstore(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { // pop value of the stack mStart, val := stack.pop(), stack.pop() memory.Set(mStart.Uint64(), 32, common.BigToBytes(val, 256)) } -func opMstore8(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opMstore8(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { off, val := stack.pop().Int64(), stack.pop().Int64() memory.store[off] = byte(val & 0xff) } -func opSload(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opSload(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { loc := common.BigToHash(stack.pop()) - val := env.State().GetState(context.Address(), loc).Big() + val := env.Db().GetState(contract.Address(), loc).Big() stack.push(val) } -func opSstore(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opSstore(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { loc := common.BigToHash(stack.pop()) val := stack.pop() - env.State().SetState(context.Address(), loc, common.BigToHash(val)) + env.Db().SetState(contract.Address(), loc, common.BigToHash(val)) } -func opJump(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {} -func opJumpi(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {} -func opJumpdest(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {} +func opJump(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +} +func opJumpi(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +} +func opJumpdest(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +} -func opPc(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opPc(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { stack.push(new(big.Int).Set(instr.data)) } -func opMsize(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opMsize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { stack.push(big.NewInt(int64(memory.Len()))) } -func opGas(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { - stack.push(new(big.Int).Set(context.Gas)) +func opGas(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { + stack.push(new(big.Int).Set(contract.Gas)) } -func opCreate(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { var ( value = stack.pop() offset, size = stack.pop(), stack.pop() input = memory.Get(offset.Int64(), size.Int64()) - gas = new(big.Int).Set(context.Gas) + gas = new(big.Int).Set(contract.Gas) addr common.Address + ret []byte + suberr error ) - context.UseGas(context.Gas) - ret, suberr, ref := env.Create(context, input, gas, context.Price, value) + contract.UseGas(contract.Gas) + ret, addr, suberr = env.Create(contract, input, gas, contract.Price, value) if suberr != nil { stack.push(new(big.Int)) - } else { // gas < len(ret) * Createinstr.dataGas == NO_CODE dataGas := big.NewInt(int64(len(ret))) dataGas.Mul(dataGas, params.CreateDataGas) - if context.UseGas(dataGas) { - ref.SetCode(ret) + if contract.UseGas(dataGas) { + env.Db().SetCode(addr, ret) } - addr = ref.Address() stack.push(addr.Big()) } } -func opCall(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opCall(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { gas := stack.pop() // pop gas and value of the stack. addr, value := stack.pop(), stack.pop() @@ -478,7 +487,7 @@ func opCall(instr instruction, env Environment, context *Context, memory *Memory gas.Add(gas, params.CallStipend) } - ret, err := env.Call(context, address, args, gas, context.Price, value) + ret, err := env.Call(contract, address, args, gas, contract.Price, value) if err != nil { stack.push(new(big.Int)) @@ -490,7 +499,7 @@ func opCall(instr instruction, env Environment, context *Context, memory *Memory } } -func opCallCode(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { +func opCallCode(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { gas := stack.pop() // pop gas and value of the stack. addr, value := stack.pop(), stack.pop() @@ -509,7 +518,7 @@ func opCallCode(instr instruction, env Environment, context *Context, memory *Me gas.Add(gas, params.CallStipend) } - ret, err := env.CallCode(context, address, args, gas, context.Price, value) + ret, err := env.CallCode(contract, address, args, gas, contract.Price, value) if err != nil { stack.push(new(big.Int)) @@ -521,14 +530,58 @@ func opCallCode(instr instruction, env Environment, context *Context, memory *Me } } -func opReturn(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {} -func opStop(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {} +func opReturn(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +} +func opStop(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { +} + +func opSuicide(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { + //receiver := env.Db().GetOrNewStateObject(common.BigToAddress(stack.pop())) + //receiver.AddBalance(balance) + balance := env.Db().GetBalance(contract.Address()) + env.Db().AddBalance(common.BigToAddress(stack.pop()), balance) + + env.Db().Delete(contract.Address()) +} + +// following functions are used by the instruction jump table + +// make log instruction function +func makeLog(size int) instrFn { + return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { + topics := make([]common.Hash, size) + mStart, mSize := stack.pop(), stack.pop() + for i := 0; i < size; i++ { + topics[i] = common.BigToHash(stack.pop()) + } + + d := memory.Get(mStart.Int64(), mSize.Int64()) + log := NewLog(contract.Address(), topics, d, env.BlockNumber().Uint64()) + env.AddLog(log) + } +} -func opSuicide(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { - receiver := env.State().GetOrNewStateObject(common.BigToAddress(stack.pop())) - balance := env.State().GetBalance(context.Address()) +// make push instruction function +func makePush(size uint64, bsize *big.Int) instrFn { + return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { + byts := getData(contract.Code, new(big.Int).SetUint64(*pc+1), bsize) + stack.push(common.Bytes2Big(byts)) + *pc += size + } +} - receiver.AddBalance(balance) +// make push instruction function +func makeDup(size int64) instrFn { + return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { + stack.dup(int(size)) + } +} - env.State().Delete(context.Address()) +// make swap instruction function +func makeSwap(size int64) instrFn { + // switch n + 1 otherwise n would be swapped with n + size += 1 + return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { + stack.swap(int(size)) + } } diff --git a/core/vm/jit.go b/core/vm/jit.go index 084d2a3f3..6ad574917 100644 --- a/core/vm/jit.go +++ b/core/vm/jit.go @@ -20,10 +20,12 @@ import ( "fmt" "math/big" "sync/atomic" + "time" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/params" "github.com/hashicorp/golang-lru" ) @@ -35,6 +37,14 @@ const ( progCompile progReady progError + + defaultJitMaxCache int = 64 +) + +var ( + EnableJit bool // Enables the JIT VM + ForceJit bool // Force the JIT, skip byte VM + MaxProgSize int // Max cache size for JIT Programs ) var programs *lru.Cache @@ -74,7 +84,7 @@ type Program struct { Id common.Hash // Id of the program status int32 // status should be accessed atomically - context *Context + contract *Contract instructions []instruction // instruction set mapping map[uint64]int // real PC mapping to array indices @@ -108,7 +118,7 @@ func (p *Program) addInstr(op OpCode, pc uint64, fn instrFn, data *big.Int) { baseOp = DUP1 } base := _baseCheck[baseOp] - instr := instruction{op, pc, fn, nil, data, base.gas, base.stackPop, base.stackPush} + instr := instruction{op, pc, fn, data, base.gas, base.stackPop, base.stackPush} p.instructions = append(p.instructions, instr) p.mapping[pc] = len(p.instructions) - 1 @@ -127,6 +137,13 @@ func CompileProgram(program *Program) (err error) { atomic.StoreInt32(&program.status, int32(progReady)) } }() + if glog.V(logger.Debug) { + glog.Infof("compiling %x\n", program.Id[:4]) + tstart := time.Now() + defer func() { + glog.Infof("compiled %x instrc: %d time: %v\n", program.Id[:4], len(program.instructions), time.Since(tstart)) + }() + } // loop thru the opcodes and "compile" in to instructions for pc := uint64(0); pc < uint64(len(program.code)); pc++ { @@ -264,7 +281,7 @@ func CompileProgram(program *Program) (err error) { program.addInstr(op, pc, opReturn, nil) case SUICIDE: program.addInstr(op, pc, opSuicide, nil) - case STOP: // Stop the context + case STOP: // Stop the contract program.addInstr(op, pc, opStop, nil) default: program.addInstr(op, pc, nil, nil) @@ -274,23 +291,24 @@ func CompileProgram(program *Program) (err error) { return nil } -// RunProgram runs the program given the enviroment and context and returns an +// RunProgram runs the program given the enviroment and contract and returns an // error if the execution failed (non-consensus) -func RunProgram(program *Program, env Environment, context *Context, input []byte) ([]byte, error) { - return runProgram(program, 0, NewMemory(), newstack(), env, context, input) +func RunProgram(program *Program, env Environment, contract *Contract, input []byte) ([]byte, error) { + return runProgram(program, 0, NewMemory(), newstack(), env, contract, input) } -func runProgram(program *Program, pcstart uint64, mem *Memory, stack *stack, env Environment, context *Context, input []byte) ([]byte, error) { - context.Input = input +func runProgram(program *Program, pcstart uint64, mem *Memory, stack *stack, env Environment, contract *Contract, input []byte) ([]byte, error) { + contract.Input = input var ( - caller = context.caller - statedb = env.State() - pc int = program.mapping[pcstart] + caller = contract.caller + statedb = env.Db() + pc int = program.mapping[pcstart] + instrCount = 0 jump = func(to *big.Int) error { if !validDest(program.destinations, to) { - nop := context.GetOp(to.Uint64()) + nop := contract.GetOp(to.Uint64()) return fmt.Errorf("invalid jump destination (%v) %v", nop, to) } @@ -300,18 +318,28 @@ func runProgram(program *Program, pcstart uint64, mem *Memory, stack *stack, env } ) + if glog.V(logger.Debug) { + glog.Infof("running JIT program %x\n", program.Id[:4]) + tstart := time.Now() + defer func() { + glog.Infof("JIT program %x done. time: %v instrc: %v\n", program.Id[:4], time.Since(tstart), instrCount) + }() + } + for pc < len(program.instructions) { + instrCount++ + instr := program.instructions[pc] // calculate the new memory size and gas price for the current executing opcode - newMemSize, cost, err := jitCalculateGasAndSize(env, context, caller, instr, statedb, mem, stack) + newMemSize, cost, err := jitCalculateGasAndSize(env, contract, caller, instr, statedb, mem, stack) if err != nil { return nil, err } // Use the calculated gas. When insufficient gas is present, use all gas and return an // Out Of Gas error - if !context.UseGas(cost) { + if !contract.UseGas(cost) { return nil, OutOfGasError } // Resize the memory calculated previously @@ -338,27 +366,27 @@ func runProgram(program *Program, pcstart uint64, mem *Memory, stack *stack, env offset, size := stack.pop(), stack.pop() ret := mem.GetPtr(offset.Int64(), size.Int64()) - return context.Return(ret), nil + return contract.Return(ret), nil case SUICIDE: - instr.fn(instr, env, context, mem, stack) + instr.fn(instr, nil, env, contract, mem, stack) - return context.Return(nil), nil + return contract.Return(nil), nil case STOP: - return context.Return(nil), nil + return contract.Return(nil), nil default: if instr.fn == nil { return nil, fmt.Errorf("Invalid opcode %x", instr.op) } - instr.fn(instr, env, context, mem, stack) + instr.fn(instr, nil, env, contract, mem, stack) } pc++ } - context.Input = nil + contract.Input = nil - return context.Return(nil), nil + return contract.Return(nil), nil } // validDest checks if the given distination is a valid one given the @@ -375,7 +403,7 @@ func validDest(dests map[uint64]struct{}, dest *big.Int) bool { // jitCalculateGasAndSize calculates the required given the opcode and stack items calculates the new memorysize for // the operation. This does not reduce gas or resizes the memory. -func jitCalculateGasAndSize(env Environment, context *Context, caller ContextRef, instr instruction, statedb *state.StateDB, mem *Memory, stack *stack) (*big.Int, *big.Int, error) { +func jitCalculateGasAndSize(env Environment, contract *Contract, caller ContractRef, instr instruction, statedb Database, mem *Memory, stack *stack) (*big.Int, *big.Int, error) { var ( gas = new(big.Int) newMemSize *big.Int = new(big.Int) @@ -426,27 +454,25 @@ func jitCalculateGasAndSize(env Environment, context *Context, caller ContextRef var g *big.Int y, x := stack.data[stack.len()-2], stack.data[stack.len()-1] - val := statedb.GetState(context.Address(), common.BigToHash(x)) + val := statedb.GetState(contract.Address(), common.BigToHash(x)) // This checks for 3 scenario's and calculates gas accordingly // 1. From a zero-value address to a non-zero value (NEW VALUE) // 2. From a non-zero value address to a zero-value address (DELETE) // 3. From a nen-zero to a non-zero (CHANGE) if common.EmptyHash(val) && !common.EmptyHash(common.BigToHash(y)) { - // 0 => non 0 g = params.SstoreSetGas } else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) { - statedb.Refund(params.SstoreRefundGas) + statedb.AddRefund(params.SstoreRefundGas) g = params.SstoreClearGas } else { - // non 0 => non 0 (or 0 => 0) g = params.SstoreClearGas } gas.Set(g) case SUICIDE: - if !statedb.IsDeleted(context.Address()) { - statedb.Refund(params.SuicideRefundGas) + if !statedb.IsDeleted(contract.Address()) { + statedb.AddRefund(params.SuicideRefundGas) } case MLOAD: newMemSize = calcMemSize(stack.peek(), u256(32)) @@ -483,7 +509,8 @@ func jitCalculateGasAndSize(env Environment, context *Context, caller ContextRef gas.Add(gas, stack.data[stack.len()-1]) if op == CALL { - if env.State().GetStateObject(common.BigToAddress(stack.data[stack.len()-2])) == nil { + //if env.Db().GetStateObject(common.BigToAddress(stack.data[stack.len()-2])) == nil { + if !env.Db().Exist(common.BigToAddress(stack.data[stack.len()-2])) { gas.Add(gas, params.CallNewAccountGas) } } @@ -497,29 +524,7 @@ func jitCalculateGasAndSize(env Environment, context *Context, caller ContextRef newMemSize = common.BigMax(x, y) } - - if newMemSize.Cmp(common.Big0) > 0 { - newMemSizeWords := toWordSize(newMemSize) - newMemSize.Mul(newMemSizeWords, u256(32)) - - if newMemSize.Cmp(u256(int64(mem.Len()))) > 0 { - // be careful reusing variables here when changing. - // The order has been optimised to reduce allocation - oldSize := toWordSize(big.NewInt(int64(mem.Len()))) - pow := new(big.Int).Exp(oldSize, common.Big2, Zero) - linCoef := oldSize.Mul(oldSize, params.MemoryGas) - quadCoef := new(big.Int).Div(pow, params.QuadCoeffDiv) - oldTotalFee := new(big.Int).Add(linCoef, quadCoef) - - pow.Exp(newMemSizeWords, common.Big2, Zero) - linCoef = linCoef.Mul(newMemSizeWords, params.MemoryGas) - quadCoef = quadCoef.Div(pow, params.QuadCoeffDiv) - newTotalFee := linCoef.Add(linCoef, quadCoef) - - fee := newTotalFee.Sub(newTotalFee, oldTotalFee) - gas.Add(gas, fee) - } - } + quadMemGas(mem, newMemSize, gas) return newMemSize, gas, nil } diff --git a/core/vm/jit_test.go b/core/vm/jit_test.go index d8e442637..8c45f2ce7 100644 --- a/core/vm/jit_test.go +++ b/core/vm/jit_test.go @@ -21,13 +21,56 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" ) const maxRun = 1000 +func TestCompiling(t *testing.T) { + prog := NewProgram([]byte{0x60, 0x10}) + err := CompileProgram(prog) + if err != nil { + t.Error("didn't expect compile error") + } + + if len(prog.instructions) != 1 { + t.Error("exected 1 compiled instruction, got", len(prog.instructions)) + } +} + +func TestResetInput(t *testing.T) { + var sender account + + env := NewEnv() + contract := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000), big.NewInt(0)) + contract.CodeAddr = &common.Address{} + + program := NewProgram([]byte{}) + RunProgram(program, env, contract, []byte{0xbe, 0xef}) + if contract.Input != nil { + t.Errorf("expected input to be nil, got %x", contract.Input) + } +} + +func TestPcMappingToInstruction(t *testing.T) { + program := NewProgram([]byte{byte(PUSH2), 0xbe, 0xef, byte(ADD)}) + CompileProgram(program) + if program.mapping[3] != 1 { + t.Error("expected mapping PC 4 to me instr no. 2, got", program.mapping[4]) + } +} + +var benchmarks = map[string]vmBench{ + "pushes": vmBench{ + false, false, false, + common.Hex2Bytes("600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01"), nil, + }, +} + +func BenchmarkPushes(b *testing.B) { + runVmBench(benchmarks["pushes"], b) +} + type vmBench struct { precompile bool // compile prior to executing nojit bool // ignore jit (sets DisbaleJit = true @@ -37,9 +80,19 @@ type vmBench struct { input []byte } +type account struct{} + +func (account) SubBalance(amount *big.Int) {} +func (account) AddBalance(amount *big.Int) {} +func (account) SetBalance(*big.Int) {} +func (account) SetNonce(uint64) {} +func (account) Balance() *big.Int { return nil } +func (account) Address() common.Address { return common.Address{} } +func (account) ReturnGas(*big.Int, *big.Int) {} +func (account) SetCode([]byte) {} + func runVmBench(test vmBench, b *testing.B) { - db, _ := ethdb.NewMemDatabase() - sender := state.NewStateObject(common.Address{}, db) + var sender account if test.precompile && !test.forcejit { NewProgram(test.code) @@ -52,7 +105,7 @@ func runVmBench(test vmBench, b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - context := NewContext(sender, sender, big.NewInt(100), big.NewInt(10000), big.NewInt(0)) + context := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000), big.NewInt(0)) context.Code = test.code context.CodeAddr = &common.Address{} _, err := New(env).Run(context, test.input) @@ -63,17 +116,6 @@ func runVmBench(test vmBench, b *testing.B) { } } -var benchmarks = map[string]vmBench{ - "pushes": vmBench{ - false, false, false, - common.Hex2Bytes("600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01"), nil, - }, -} - -func BenchmarkPushes(b *testing.B) { - runVmBench(benchmarks["pushes"], b) -} - type Env struct { gasLimit *big.Int depth int @@ -93,30 +135,32 @@ func (self *Env) StructLogs() []StructLog { //func (self *Env) PrevHash() []byte { return self.parent } func (self *Env) Coinbase() common.Address { return common.Address{} } +func (self *Env) MakeSnapshot() Database { return nil } +func (self *Env) SetSnapshot(Database) {} func (self *Env) Time() *big.Int { return big.NewInt(time.Now().Unix()) } func (self *Env) Difficulty() *big.Int { return big.NewInt(0) } -func (self *Env) State() *state.StateDB { return nil } +func (self *Env) Db() Database { return nil } func (self *Env) GasLimit() *big.Int { return self.gasLimit } func (self *Env) VmType() Type { return StdVmTy } func (self *Env) GetHash(n uint64) common.Hash { return common.BytesToHash(crypto.Sha3([]byte(big.NewInt(int64(n)).String()))) } -func (self *Env) AddLog(log *state.Log) { +func (self *Env) AddLog(log *Log) { } func (self *Env) Depth() int { return self.depth } func (self *Env) SetDepth(i int) { self.depth = i } -func (self *Env) CanTransfer(from Account, balance *big.Int) bool { - return from.Balance().Cmp(balance) >= 0 +func (self *Env) CanTransfer(from common.Address, balance *big.Int) bool { + return true } func (self *Env) Transfer(from, to Account, amount *big.Int) error { return nil } -func (self *Env) Call(caller ContextRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { +func (self *Env) Call(caller ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { return nil, nil } -func (self *Env) CallCode(caller ContextRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { +func (self *Env) CallCode(caller ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { return nil, nil } -func (self *Env) Create(caller ContextRef, data []byte, gas, price, value *big.Int) ([]byte, error, ContextRef) { - return nil, nil, nil +func (self *Env) Create(caller ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) { + return nil, common.Address{}, nil } diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go new file mode 100644 index 000000000..ab899647f --- /dev/null +++ b/core/vm/jump_table.go @@ -0,0 +1,143 @@ +package vm + +import "math/big" + +type jumpPtr struct { + fn instrFn + valid bool +} + +var jumpTable [256]jumpPtr + +func init() { + jumpTable[ADD] = jumpPtr{opAdd, true} + jumpTable[SUB] = jumpPtr{opSub, true} + jumpTable[MUL] = jumpPtr{opMul, true} + jumpTable[DIV] = jumpPtr{opDiv, true} + jumpTable[SDIV] = jumpPtr{opSdiv, true} + jumpTable[MOD] = jumpPtr{opMod, true} + jumpTable[SMOD] = jumpPtr{opSmod, true} + jumpTable[EXP] = jumpPtr{opExp, true} + jumpTable[SIGNEXTEND] = jumpPtr{opSignExtend, true} + jumpTable[NOT] = jumpPtr{opNot, true} + jumpTable[LT] = jumpPtr{opLt, true} + jumpTable[GT] = jumpPtr{opGt, true} + jumpTable[SLT] = jumpPtr{opSlt, true} + jumpTable[SGT] = jumpPtr{opSgt, true} + jumpTable[EQ] = jumpPtr{opEq, true} + jumpTable[ISZERO] = jumpPtr{opIszero, true} + jumpTable[AND] = jumpPtr{opAnd, true} + jumpTable[OR] = jumpPtr{opOr, true} + jumpTable[XOR] = jumpPtr{opXor, true} + jumpTable[BYTE] = jumpPtr{opByte, true} + jumpTable[ADDMOD] = jumpPtr{opAddmod, true} + jumpTable[MULMOD] = jumpPtr{opMulmod, true} + jumpTable[SHA3] = jumpPtr{opSha3, true} + jumpTable[ADDRESS] = jumpPtr{opAddress, true} + jumpTable[BALANCE] = jumpPtr{opBalance, true} + jumpTable[ORIGIN] = jumpPtr{opOrigin, true} + jumpTable[CALLER] = jumpPtr{opCaller, true} + jumpTable[CALLVALUE] = jumpPtr{opCallValue, true} + jumpTable[CALLDATALOAD] = jumpPtr{opCalldataLoad, true} + jumpTable[CALLDATASIZE] = jumpPtr{opCalldataSize, true} + jumpTable[CALLDATACOPY] = jumpPtr{opCalldataCopy, true} + jumpTable[CODESIZE] = jumpPtr{opCodeSize, true} + jumpTable[EXTCODESIZE] = jumpPtr{opExtCodeSize, true} + jumpTable[CODECOPY] = jumpPtr{opCodeCopy, true} + jumpTable[EXTCODECOPY] = jumpPtr{opExtCodeCopy, true} + jumpTable[GASPRICE] = jumpPtr{opGasprice, true} + jumpTable[BLOCKHASH] = jumpPtr{opBlockhash, true} + jumpTable[COINBASE] = jumpPtr{opCoinbase, true} + jumpTable[TIMESTAMP] = jumpPtr{opTimestamp, true} + jumpTable[NUMBER] = jumpPtr{opNumber, true} + jumpTable[DIFFICULTY] = jumpPtr{opDifficulty, true} + jumpTable[GASLIMIT] = jumpPtr{opGasLimit, true} + jumpTable[POP] = jumpPtr{opPop, true} + jumpTable[MLOAD] = jumpPtr{opMload, true} + jumpTable[MSTORE] = jumpPtr{opMstore, true} + jumpTable[MSTORE8] = jumpPtr{opMstore8, true} + jumpTable[SLOAD] = jumpPtr{opSload, true} + jumpTable[SSTORE] = jumpPtr{opSstore, true} + jumpTable[JUMPDEST] = jumpPtr{opJumpdest, true} + jumpTable[PC] = jumpPtr{nil, true} + jumpTable[MSIZE] = jumpPtr{opMsize, true} + jumpTable[GAS] = jumpPtr{opGas, true} + jumpTable[CREATE] = jumpPtr{opCreate, true} + jumpTable[CALL] = jumpPtr{opCall, true} + jumpTable[CALLCODE] = jumpPtr{opCallCode, true} + jumpTable[LOG0] = jumpPtr{makeLog(0), true} + jumpTable[LOG1] = jumpPtr{makeLog(1), true} + jumpTable[LOG2] = jumpPtr{makeLog(2), true} + jumpTable[LOG3] = jumpPtr{makeLog(3), true} + jumpTable[LOG4] = jumpPtr{makeLog(4), true} + jumpTable[SWAP1] = jumpPtr{makeSwap(1), true} + jumpTable[SWAP2] = jumpPtr{makeSwap(2), true} + jumpTable[SWAP3] = jumpPtr{makeSwap(3), true} + jumpTable[SWAP4] = jumpPtr{makeSwap(4), true} + jumpTable[SWAP5] = jumpPtr{makeSwap(5), true} + jumpTable[SWAP6] = jumpPtr{makeSwap(6), true} + jumpTable[SWAP7] = jumpPtr{makeSwap(7), true} + jumpTable[SWAP8] = jumpPtr{makeSwap(8), true} + jumpTable[SWAP9] = jumpPtr{makeSwap(9), true} + jumpTable[SWAP10] = jumpPtr{makeSwap(10), true} + jumpTable[SWAP11] = jumpPtr{makeSwap(11), true} + jumpTable[SWAP12] = jumpPtr{makeSwap(12), true} + jumpTable[SWAP13] = jumpPtr{makeSwap(13), true} + jumpTable[SWAP14] = jumpPtr{makeSwap(14), true} + jumpTable[SWAP15] = jumpPtr{makeSwap(15), true} + jumpTable[SWAP16] = jumpPtr{makeSwap(16), true} + jumpTable[PUSH1] = jumpPtr{makePush(1, big.NewInt(1)), true} + jumpTable[PUSH2] = jumpPtr{makePush(2, big.NewInt(2)), true} + jumpTable[PUSH3] = jumpPtr{makePush(3, big.NewInt(3)), true} + jumpTable[PUSH4] = jumpPtr{makePush(4, big.NewInt(4)), true} + jumpTable[PUSH5] = jumpPtr{makePush(5, big.NewInt(5)), true} + jumpTable[PUSH6] = jumpPtr{makePush(6, big.NewInt(6)), true} + jumpTable[PUSH7] = jumpPtr{makePush(7, big.NewInt(7)), true} + jumpTable[PUSH8] = jumpPtr{makePush(8, big.NewInt(8)), true} + jumpTable[PUSH9] = jumpPtr{makePush(9, big.NewInt(9)), true} + jumpTable[PUSH10] = jumpPtr{makePush(10, big.NewInt(10)), true} + jumpTable[PUSH11] = jumpPtr{makePush(11, big.NewInt(11)), true} + jumpTable[PUSH12] = jumpPtr{makePush(12, big.NewInt(12)), true} + jumpTable[PUSH13] = jumpPtr{makePush(13, big.NewInt(13)), true} + jumpTable[PUSH14] = jumpPtr{makePush(14, big.NewInt(14)), true} + jumpTable[PUSH15] = jumpPtr{makePush(15, big.NewInt(15)), true} + jumpTable[PUSH16] = jumpPtr{makePush(16, big.NewInt(16)), true} + jumpTable[PUSH17] = jumpPtr{makePush(17, big.NewInt(17)), true} + jumpTable[PUSH18] = jumpPtr{makePush(18, big.NewInt(18)), true} + jumpTable[PUSH19] = jumpPtr{makePush(19, big.NewInt(19)), true} + jumpTable[PUSH20] = jumpPtr{makePush(20, big.NewInt(20)), true} + jumpTable[PUSH21] = jumpPtr{makePush(21, big.NewInt(21)), true} + jumpTable[PUSH22] = jumpPtr{makePush(22, big.NewInt(22)), true} + jumpTable[PUSH23] = jumpPtr{makePush(23, big.NewInt(23)), true} + jumpTable[PUSH24] = jumpPtr{makePush(24, big.NewInt(24)), true} + jumpTable[PUSH25] = jumpPtr{makePush(25, big.NewInt(25)), true} + jumpTable[PUSH26] = jumpPtr{makePush(26, big.NewInt(26)), true} + jumpTable[PUSH27] = jumpPtr{makePush(27, big.NewInt(27)), true} + jumpTable[PUSH28] = jumpPtr{makePush(28, big.NewInt(28)), true} + jumpTable[PUSH29] = jumpPtr{makePush(29, big.NewInt(29)), true} + jumpTable[PUSH30] = jumpPtr{makePush(30, big.NewInt(30)), true} + jumpTable[PUSH31] = jumpPtr{makePush(31, big.NewInt(31)), true} + jumpTable[PUSH32] = jumpPtr{makePush(32, big.NewInt(32)), true} + jumpTable[DUP1] = jumpPtr{makeDup(1), true} + jumpTable[DUP2] = jumpPtr{makeDup(2), true} + jumpTable[DUP3] = jumpPtr{makeDup(3), true} + jumpTable[DUP4] = jumpPtr{makeDup(4), true} + jumpTable[DUP5] = jumpPtr{makeDup(5), true} + jumpTable[DUP6] = jumpPtr{makeDup(6), true} + jumpTable[DUP7] = jumpPtr{makeDup(7), true} + jumpTable[DUP8] = jumpPtr{makeDup(8), true} + jumpTable[DUP9] = jumpPtr{makeDup(9), true} + jumpTable[DUP10] = jumpPtr{makeDup(10), true} + jumpTable[DUP11] = jumpPtr{makeDup(11), true} + jumpTable[DUP12] = jumpPtr{makeDup(12), true} + jumpTable[DUP13] = jumpPtr{makeDup(13), true} + jumpTable[DUP14] = jumpPtr{makeDup(14), true} + jumpTable[DUP15] = jumpPtr{makeDup(15), true} + jumpTable[DUP16] = jumpPtr{makeDup(16), true} + + jumpTable[RETURN] = jumpPtr{nil, true} + jumpTable[SUICIDE] = jumpPtr{nil, true} + jumpTable[JUMP] = jumpPtr{nil, true} + jumpTable[JUMPI] = jumpPtr{nil, true} + jumpTable[STOP] = jumpPtr{nil, true} +} diff --git a/core/state/log.go b/core/vm/log.go index 5d7d7357d..354f0ad35 100644 --- a/core/state/log.go +++ b/core/vm/log.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. -package state +package vm import ( "fmt" diff --git a/core/vm/logger.go b/core/vm/logger.go index 736f595f6..2bd02319f 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -24,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum/common" ) +// StdErrFormat formats a slice of StructLogs to human readable format func StdErrFormat(logs []StructLog) { fmt.Fprintf(os.Stderr, "VM STAT %d OPs\n", len(logs)) for _, log := range logs { diff --git a/core/vm/memory.go b/core/vm/memory.go index 0109050d7..d01188417 100644 --- a/core/vm/memory.go +++ b/core/vm/memory.go @@ -18,6 +18,7 @@ package vm import "fmt" +// Memory implements a simple memory model for the ethereum virtual machine. type Memory struct { store []byte } @@ -26,6 +27,7 @@ func NewMemory() *Memory { return &Memory{nil} } +// Set sets offset + size to value func (m *Memory) Set(offset, size uint64, value []byte) { // length of store may never be less than offset + size. // The store should be resized PRIOR to setting the memory @@ -40,12 +42,14 @@ func (m *Memory) Set(offset, size uint64, value []byte) { } } +// Resize resizes the memory to size func (m *Memory) Resize(size uint64) { if uint64(m.Len()) < size { m.store = append(m.store, make([]byte, size-uint64(m.Len()))...) } } +// Get returns offset + size as a new slice func (self *Memory) Get(offset, size int64) (cpy []byte) { if size == 0 { return nil @@ -61,6 +65,7 @@ func (self *Memory) Get(offset, size int64) (cpy []byte) { return } +// GetPtr returns the offset + size func (self *Memory) GetPtr(offset, size int64) []byte { if size == 0 { return nil @@ -73,10 +78,12 @@ func (self *Memory) GetPtr(offset, size int64) []byte { return nil } +// Len returns the length of the backing slice func (m *Memory) Len() int { return len(m.store) } +// Data returns the backing slice func (m *Memory) Data() []byte { return m.store } diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index ecced3650..986c35ef8 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -20,9 +20,9 @@ import ( "fmt" ) +// OpCode is an EVM opcode type OpCode byte -// Op codes const ( // 0x0 range - arithmetic ops STOP OpCode = iota diff --git a/core/vm/virtual_machine.go b/core/vm/virtual_machine.go index 047723744..9b3340bb2 100644 --- a/core/vm/virtual_machine.go +++ b/core/vm/virtual_machine.go @@ -16,7 +16,8 @@ package vm +// VirtualMachine is an EVM interface type VirtualMachine interface { Env() Environment - Run(context *Context, data []byte) ([]byte, error) + Run(*Contract, []byte) ([]byte, error) } diff --git a/core/vm/vm.go b/core/vm/vm.go index d9e1a0ce5..57dd4dac3 100644 --- a/core/vm/vm.go +++ b/core/vm/vm.go @@ -14,33 +14,32 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. -// Package vm implements the Ethereum Virtual Machine. package vm import ( "fmt" "math/big" + "time" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/params" ) -// Vm implements VirtualMachine +// Vm is an EVM and implements VirtualMachine type Vm struct { env Environment } -// New returns a new Virtual Machine +// New returns a new Vm func New(env Environment) *Vm { return &Vm{env: env} } // Run loops and evaluates the contract's code with the given input data -func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) { +func (self *Vm) Run(contract *Contract, input []byte) (ret []byte, err error) { self.env.SetDepth(self.env.Depth() + 1) defer self.env.SetDepth(self.env.Depth() - 1) @@ -48,42 +47,48 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) { defer func() { if err != nil { // In case of a VM exception (known exceptions) all gas consumed (panics NOT included). - context.UseGas(context.Gas) + contract.UseGas(contract.Gas) - ret = context.Return(nil) + ret = contract.Return(nil) } }() - if context.CodeAddr != nil { - if p := Precompiled[context.CodeAddr.Str()]; p != nil { - return self.RunPrecompiled(p, input, context) + if contract.CodeAddr != nil { + if p := Precompiled[contract.CodeAddr.Str()]; p != nil { + return self.RunPrecompiled(p, input, contract) } } + // Don't bother with the execution if there's no code. + if len(contract.Code) == 0 { + return contract.Return(nil), nil + } + var ( - codehash = crypto.Sha3Hash(context.Code) // codehash is used when doing jump dest caching + codehash = crypto.Sha3Hash(contract.Code) // codehash is used when doing jump dest caching program *Program ) if EnableJit { - // Fetch program status. - // * If ready run using JIT - // * If unknown, compile in a seperate goroutine - // * If forced wait for compilation and run once done - if status := GetProgramStatus(codehash); status == progReady { - return RunProgram(GetProgram(codehash), self.env, context, input) - } else if status == progUnknown { + // If the JIT is enabled check the status of the JIT program, + // if it doesn't exist compile a new program in a seperate + // goroutine or wait for compilation to finish if the JIT is + // forced. + switch GetProgramStatus(codehash) { + case progReady: + return RunProgram(GetProgram(codehash), self.env, contract, input) + case progUnknown: if ForceJit { // Create and compile program - program = NewProgram(context.Code) + program = NewProgram(contract.Code) perr := CompileProgram(program) if perr == nil { - return RunProgram(program, self.env, context, input) + return RunProgram(program, self.env, contract, input) } glog.V(logger.Info).Infoln("error compiling program", err) } else { // create and compile the program. Compilation // is done in a seperate goroutine - program = NewProgram(context.Code) + program = NewProgram(contract.Code) go func() { err := CompileProgram(program) if err != nil { @@ -96,15 +101,14 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) { } var ( - caller = context.caller - code = context.Code - value = context.value - price = context.Price - - op OpCode // current opcode - mem = NewMemory() // bound memory - stack = newstack() // local stack - statedb = self.env.State() // current state + caller = contract.caller + code = contract.Code + instrCount = 0 + + op OpCode // current opcode + mem = NewMemory() // bound memory + stack = newstack() // local stack + statedb = self.env.Db() // current state // For optimisation reason we're using uint64 as the program counter. // It's theoretically possible to go above 2^64. The YP defines the PC to be uint256. Pratically much less so feasible. pc = uint64(0) // program counter @@ -112,8 +116,8 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) { // jump evaluates and checks whether the given jump destination is a valid one // if valid move the `pc` otherwise return an error. jump = func(from uint64, to *big.Int) error { - if !context.jumpdests.has(codehash, code, to) { - nop := context.GetOp(to.Uint64()) + if !contract.jumpdests.has(codehash, code, to) { + nop := contract.GetOp(to.Uint64()) return fmt.Errorf("invalid jump destination (%v) %v", nop, to) } @@ -125,552 +129,92 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) { newMemSize *big.Int cost *big.Int ) + contract.Input = input // User defer pattern to check for an error and, based on the error being nil or not, use all gas and return. defer func() { if err != nil { - self.log(pc, op, context.Gas, cost, mem, stack, context, err) + self.log(pc, op, contract.Gas, cost, mem, stack, contract, err) } }() - // Don't bother with the execution if there's no code. - if len(code) == 0 { - return context.Return(nil), nil + if glog.V(logger.Debug) { + glog.Infof("running byte VM %x\n", codehash[:4]) + tstart := time.Now() + defer func() { + glog.Infof("byte VM %x done. time: %v instrc: %v\n", codehash[:4], time.Since(tstart), instrCount) + }() } - for { - // Overhead of the atomic read might not be worth it - /* TODO this still causes a few issues in the tests - if program != nil && progStatus(atomic.LoadInt32(&program.status)) == progReady { - // move execution - glog.V(logger.Info).Infoln("Moved execution to JIT") - return runProgram(program, pc, mem, stack, self.env, context, input) - } + for ; ; instrCount++ { + /* + if EnableJit && it%100 == 0 { + if program != nil && progStatus(atomic.LoadInt32(&program.status)) == progReady { + // move execution + fmt.Println("moved", it) + glog.V(logger.Info).Infoln("Moved execution to JIT") + return runProgram(program, pc, mem, stack, self.env, contract, input) + } + } */ - // The base for all big integer arithmetic - base := new(big.Int) // Get the memory location of pc - op = context.GetOp(pc) + op = contract.GetOp(pc) // calculate the new memory size and gas price for the current executing opcode - newMemSize, cost, err = calculateGasAndSize(self.env, context, caller, op, statedb, mem, stack) + newMemSize, cost, err = calculateGasAndSize(self.env, contract, caller, op, statedb, mem, stack) if err != nil { return nil, err } // Use the calculated gas. When insufficient gas is present, use all gas and return an // Out Of Gas error - if !context.UseGas(cost) { + if !contract.UseGas(cost) { return nil, OutOfGasError } // Resize the memory calculated previously mem.Resize(newMemSize.Uint64()) // Add a log message - self.log(pc, op, context.Gas, cost, mem, stack, context, nil) - - switch op { - case ADD: - x, y := stack.pop(), stack.pop() - - base.Add(x, y) - - U256(base) - - // pop result back on the stack - stack.push(base) - case SUB: - x, y := stack.pop(), stack.pop() - - base.Sub(x, y) - - U256(base) - - // pop result back on the stack - stack.push(base) - case MUL: - x, y := stack.pop(), stack.pop() - - base.Mul(x, y) - - U256(base) - - // pop result back on the stack - stack.push(base) - case DIV: - x, y := stack.pop(), stack.pop() - - if y.Cmp(common.Big0) != 0 { - base.Div(x, y) - } - - U256(base) - - // pop result back on the stack - stack.push(base) - case SDIV: - x, y := S256(stack.pop()), S256(stack.pop()) - - if y.Cmp(common.Big0) == 0 { - base.Set(common.Big0) - } else { - n := new(big.Int) - if new(big.Int).Mul(x, y).Cmp(common.Big0) < 0 { - n.SetInt64(-1) - } else { - n.SetInt64(1) - } - - base.Div(x.Abs(x), y.Abs(y)).Mul(base, n) - - U256(base) - } - - stack.push(base) - case MOD: - x, y := stack.pop(), stack.pop() - - if y.Cmp(common.Big0) == 0 { - base.Set(common.Big0) - } else { - base.Mod(x, y) - } - - U256(base) - - stack.push(base) - case SMOD: - x, y := S256(stack.pop()), S256(stack.pop()) - - if y.Cmp(common.Big0) == 0 { - base.Set(common.Big0) - } else { - n := new(big.Int) - if x.Cmp(common.Big0) < 0 { - n.SetInt64(-1) - } else { - n.SetInt64(1) - } - - base.Mod(x.Abs(x), y.Abs(y)).Mul(base, n) - - U256(base) - } - - stack.push(base) - - case EXP: - x, y := stack.pop(), stack.pop() - - base.Exp(x, y, Pow256) - - U256(base) - - stack.push(base) - case SIGNEXTEND: - back := stack.pop() - if back.Cmp(big.NewInt(31)) < 0 { - bit := uint(back.Uint64()*8 + 7) - num := stack.pop() - mask := new(big.Int).Lsh(common.Big1, bit) - mask.Sub(mask, common.Big1) - if common.BitTest(num, int(bit)) { - num.Or(num, mask.Not(mask)) - } else { - num.And(num, mask) - } - - num = U256(num) - - stack.push(num) - } - case NOT: - stack.push(U256(new(big.Int).Not(stack.pop()))) - case LT: - x, y := stack.pop(), stack.pop() - - // x < y - if x.Cmp(y) < 0 { - stack.push(common.BigTrue) - } else { - stack.push(common.BigFalse) - } - case GT: - x, y := stack.pop(), stack.pop() - - // x > y - if x.Cmp(y) > 0 { - stack.push(common.BigTrue) - } else { - stack.push(common.BigFalse) - } - - case SLT: - x, y := S256(stack.pop()), S256(stack.pop()) - - // x < y - if x.Cmp(S256(y)) < 0 { - stack.push(common.BigTrue) - } else { - stack.push(common.BigFalse) - } - case SGT: - x, y := S256(stack.pop()), S256(stack.pop()) - - // x > y - if x.Cmp(y) > 0 { - stack.push(common.BigTrue) - } else { - stack.push(common.BigFalse) - } - - case EQ: - x, y := stack.pop(), stack.pop() - - // x == y - if x.Cmp(y) == 0 { - stack.push(common.BigTrue) - } else { - stack.push(common.BigFalse) - } - case ISZERO: - x := stack.pop() - if x.Cmp(common.BigFalse) > 0 { - stack.push(common.BigFalse) - } else { - stack.push(common.BigTrue) - } - - case AND: - x, y := stack.pop(), stack.pop() - - stack.push(base.And(x, y)) - case OR: - x, y := stack.pop(), stack.pop() - - stack.push(base.Or(x, y)) - case XOR: - x, y := stack.pop(), stack.pop() - - stack.push(base.Xor(x, y)) - case BYTE: - th, val := stack.pop(), stack.pop() - - if th.Cmp(big.NewInt(32)) < 0 { - byt := big.NewInt(int64(common.LeftPadBytes(val.Bytes(), 32)[th.Int64()])) - - base.Set(byt) - } else { - base.Set(common.BigFalse) - } - - stack.push(base) - case ADDMOD: - x := stack.pop() - y := stack.pop() - z := stack.pop() - - if z.Cmp(Zero) > 0 { - add := new(big.Int).Add(x, y) - base.Mod(add, z) - - base = U256(base) - } - - stack.push(base) - case MULMOD: - x := stack.pop() - y := stack.pop() - z := stack.pop() - - if z.Cmp(Zero) > 0 { - mul := new(big.Int).Mul(x, y) - base.Mod(mul, z) - - U256(base) - } - - stack.push(base) - - case SHA3: - offset, size := stack.pop(), stack.pop() - data := crypto.Sha3(mem.Get(offset.Int64(), size.Int64())) - - stack.push(common.BigD(data)) + self.log(pc, op, contract.Gas, cost, mem, stack, contract, nil) - case ADDRESS: - stack.push(common.Bytes2Big(context.Address().Bytes())) - - case BALANCE: - addr := common.BigToAddress(stack.pop()) - balance := statedb.GetBalance(addr) - - stack.push(new(big.Int).Set(balance)) - - case ORIGIN: - origin := self.env.Origin() - - stack.push(origin.Big()) - - case CALLER: - caller := context.caller.Address() - stack.push(common.Bytes2Big(caller.Bytes())) - - case CALLVALUE: - stack.push(new(big.Int).Set(value)) - - case CALLDATALOAD: - data := getData(input, stack.pop(), common.Big32) - - stack.push(common.Bytes2Big(data)) - case CALLDATASIZE: - l := int64(len(input)) - stack.push(big.NewInt(l)) - - case CALLDATACOPY: - var ( - mOff = stack.pop() - cOff = stack.pop() - l = stack.pop() - ) - data := getData(input, cOff, l) - - mem.Set(mOff.Uint64(), l.Uint64(), data) - - case CODESIZE, EXTCODESIZE: - var code []byte - if op == EXTCODESIZE { - addr := common.BigToAddress(stack.pop()) - - code = statedb.GetCode(addr) - } else { - code = context.Code - } - - l := big.NewInt(int64(len(code))) - stack.push(l) - - case CODECOPY, EXTCODECOPY: - var code []byte - if op == EXTCODECOPY { - addr := common.BigToAddress(stack.pop()) - code = statedb.GetCode(addr) + if opPtr := jumpTable[op]; opPtr.valid { + if opPtr.fn != nil { + opPtr.fn(instruction{}, &pc, self.env, contract, mem, stack) } else { - code = context.Code - } - - var ( - mOff = stack.pop() - cOff = stack.pop() - l = stack.pop() - ) - - codeCopy := getData(code, cOff, l) - - mem.Set(mOff.Uint64(), l.Uint64(), codeCopy) - - case GASPRICE: - stack.push(new(big.Int).Set(context.Price)) - - case BLOCKHASH: - num := stack.pop() - - n := new(big.Int).Sub(self.env.BlockNumber(), common.Big257) - if num.Cmp(n) > 0 && num.Cmp(self.env.BlockNumber()) < 0 { - stack.push(self.env.GetHash(num.Uint64()).Big()) - } else { - stack.push(common.Big0) - } - - case COINBASE: - coinbase := self.env.Coinbase() - - stack.push(coinbase.Big()) - - case TIMESTAMP: - time := self.env.Time() - - stack.push(new(big.Int).Set(time)) - - case NUMBER: - number := self.env.BlockNumber() - - stack.push(U256(number)) - - case DIFFICULTY: - difficulty := self.env.Difficulty() - - stack.push(new(big.Int).Set(difficulty)) - - case GASLIMIT: - - stack.push(new(big.Int).Set(self.env.GasLimit())) - - case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32: - size := uint64(op - PUSH1 + 1) - byts := getData(code, new(big.Int).SetUint64(pc+1), new(big.Int).SetUint64(size)) - // push value to stack - stack.push(common.Bytes2Big(byts)) - pc += size - - case POP: - stack.pop() - case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16: - n := int(op - DUP1 + 1) - stack.dup(n) - - case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16: - n := int(op - SWAP1 + 2) - stack.swap(n) - - case LOG0, LOG1, LOG2, LOG3, LOG4: - n := int(op - LOG0) - topics := make([]common.Hash, n) - mStart, mSize := stack.pop(), stack.pop() - for i := 0; i < n; i++ { - topics[i] = common.BigToHash(stack.pop()) - } - - data := mem.Get(mStart.Int64(), mSize.Int64()) - log := state.NewLog(context.Address(), topics, data, self.env.BlockNumber().Uint64()) - self.env.AddLog(log) - - case MLOAD: - offset := stack.pop() - val := common.BigD(mem.Get(offset.Int64(), 32)) - stack.push(val) - - case MSTORE: - // pop value of the stack - mStart, val := stack.pop(), stack.pop() - mem.Set(mStart.Uint64(), 32, common.BigToBytes(val, 256)) - - case MSTORE8: - off, val := stack.pop().Int64(), stack.pop().Int64() - - mem.store[off] = byte(val & 0xff) - - case SLOAD: - loc := common.BigToHash(stack.pop()) - val := statedb.GetState(context.Address(), loc).Big() - stack.push(val) + switch op { + case PC: + opPc(instruction{data: new(big.Int).SetUint64(pc)}, &pc, self.env, contract, mem, stack) + case JUMP: + if err := jump(pc, stack.pop()); err != nil { + return nil, err + } - case SSTORE: - loc := common.BigToHash(stack.pop()) - val := stack.pop() + continue + case JUMPI: + pos, cond := stack.pop(), stack.pop() - statedb.SetState(context.Address(), loc, common.BigToHash(val)) + if cond.Cmp(common.BigTrue) >= 0 { + if err := jump(pc, pos); err != nil { + return nil, err + } - case JUMP: - if err := jump(pc, stack.pop()); err != nil { - return nil, err - } + continue + } + case RETURN: + offset, size := stack.pop(), stack.pop() + ret := mem.GetPtr(offset.Int64(), size.Int64()) - continue - case JUMPI: - pos, cond := stack.pop(), stack.pop() + return contract.Return(ret), nil + case SUICIDE: + opSuicide(instruction{}, nil, self.env, contract, mem, stack) - if cond.Cmp(common.BigTrue) >= 0 { - if err := jump(pc, pos); err != nil { - return nil, err + fallthrough + case STOP: // Stop the contract + return contract.Return(nil), nil } - - continue - } - - case JUMPDEST: - case PC: - stack.push(new(big.Int).SetUint64(pc)) - case MSIZE: - stack.push(big.NewInt(int64(mem.Len()))) - case GAS: - stack.push(new(big.Int).Set(context.Gas)) - case CREATE: - - var ( - value = stack.pop() - offset, size = stack.pop(), stack.pop() - input = mem.Get(offset.Int64(), size.Int64()) - gas = new(big.Int).Set(context.Gas) - addr common.Address - ) - - context.UseGas(context.Gas) - ret, suberr, ref := self.env.Create(context, input, gas, price, value) - if suberr != nil { - stack.push(common.BigFalse) - - } else { - // gas < len(ret) * CreateDataGas == NO_CODE - dataGas := big.NewInt(int64(len(ret))) - dataGas.Mul(dataGas, params.CreateDataGas) - if context.UseGas(dataGas) { - ref.SetCode(ret) - } - addr = ref.Address() - - stack.push(addr.Big()) - - } - - case CALL, CALLCODE: - gas := stack.pop() - // pop gas and value of the stack. - addr, value := stack.pop(), stack.pop() - value = U256(value) - // pop input size and offset - inOffset, inSize := stack.pop(), stack.pop() - // pop return size and offset - retOffset, retSize := stack.pop(), stack.pop() - - address := common.BigToAddress(addr) - - // Get the arguments from the memory - args := mem.Get(inOffset.Int64(), inSize.Int64()) - - if len(value.Bytes()) > 0 { - gas.Add(gas, params.CallStipend) - } - - var ( - ret []byte - err error - ) - if op == CALLCODE { - ret, err = self.env.CallCode(context, address, args, gas, price, value) - } else { - ret, err = self.env.Call(context, address, args, gas, price, value) - } - - if err != nil { - stack.push(common.BigFalse) - - } else { - stack.push(common.BigTrue) - - mem.Set(retOffset.Uint64(), retSize.Uint64(), ret) } - - case RETURN: - offset, size := stack.pop(), stack.pop() - ret := mem.GetPtr(offset.Int64(), size.Int64()) - - return context.Return(ret), nil - case SUICIDE: - receiver := statedb.GetOrNewStateObject(common.BigToAddress(stack.pop())) - balance := statedb.GetBalance(context.Address()) - - receiver.AddBalance(balance) - - statedb.Delete(context.Address()) - - fallthrough - case STOP: // Stop the context - - return context.Return(nil), nil - default: - + } else { return nil, fmt.Errorf("Invalid opcode %x", op) } @@ -681,7 +225,7 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) { // calculateGasAndSize calculates the required given the opcode and stack items calculates the new memorysize for // the operation. This does not reduce gas or resizes the memory. -func calculateGasAndSize(env Environment, context *Context, caller ContextRef, op OpCode, statedb *state.StateDB, mem *Memory, stack *stack) (*big.Int, *big.Int, error) { +func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef, op OpCode, statedb Database, mem *Memory, stack *stack) (*big.Int, *big.Int, error) { var ( gas = new(big.Int) newMemSize *big.Int = new(big.Int) @@ -731,7 +275,7 @@ func calculateGasAndSize(env Environment, context *Context, caller ContextRef, o var g *big.Int y, x := stack.data[stack.len()-2], stack.data[stack.len()-1] - val := statedb.GetState(context.Address(), common.BigToHash(x)) + val := statedb.GetState(contract.Address(), common.BigToHash(x)) // This checks for 3 scenario's and calculates gas accordingly // 1. From a zero-value address to a non-zero value (NEW VALUE) @@ -741,7 +285,7 @@ func calculateGasAndSize(env Environment, context *Context, caller ContextRef, o // 0 => non 0 g = params.SstoreSetGas } else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) { - statedb.Refund(params.SstoreRefundGas) + statedb.AddRefund(params.SstoreRefundGas) g = params.SstoreClearGas } else { @@ -750,8 +294,8 @@ func calculateGasAndSize(env Environment, context *Context, caller ContextRef, o } gas.Set(g) case SUICIDE: - if !statedb.IsDeleted(context.Address()) { - statedb.Refund(params.SuicideRefundGas) + if !statedb.IsDeleted(contract.Address()) { + statedb.AddRefund(params.SuicideRefundGas) } case MLOAD: newMemSize = calcMemSize(stack.peek(), u256(32)) @@ -788,7 +332,8 @@ func calculateGasAndSize(env Environment, context *Context, caller ContextRef, o gas.Add(gas, stack.data[stack.len()-1]) if op == CALL { - if env.State().GetStateObject(common.BigToAddress(stack.data[stack.len()-2])) == nil { + //if env.Db().GetStateObject(common.BigToAddress(stack.data[stack.len()-2])) == nil { + if !env.Db().Exist(common.BigToAddress(stack.data[stack.len()-2])) { gas.Add(gas, params.CallNewAccountGas) } } @@ -802,38 +347,18 @@ func calculateGasAndSize(env Environment, context *Context, caller ContextRef, o newMemSize = common.BigMax(x, y) } - - if newMemSize.Cmp(common.Big0) > 0 { - newMemSizeWords := toWordSize(newMemSize) - newMemSize.Mul(newMemSizeWords, u256(32)) - - if newMemSize.Cmp(u256(int64(mem.Len()))) > 0 { - oldSize := toWordSize(big.NewInt(int64(mem.Len()))) - pow := new(big.Int).Exp(oldSize, common.Big2, Zero) - linCoef := new(big.Int).Mul(oldSize, params.MemoryGas) - quadCoef := new(big.Int).Div(pow, params.QuadCoeffDiv) - oldTotalFee := new(big.Int).Add(linCoef, quadCoef) - - pow.Exp(newMemSizeWords, common.Big2, Zero) - linCoef = new(big.Int).Mul(newMemSizeWords, params.MemoryGas) - quadCoef = new(big.Int).Div(pow, params.QuadCoeffDiv) - newTotalFee := new(big.Int).Add(linCoef, quadCoef) - - fee := new(big.Int).Sub(newTotalFee, oldTotalFee) - gas.Add(gas, fee) - } - } + quadMemGas(mem, newMemSize, gas) return newMemSize, gas, nil } // RunPrecompile runs and evaluate the output of a precompiled contract defined in contracts.go -func (self *Vm) RunPrecompiled(p *PrecompiledAccount, input []byte, context *Context) (ret []byte, err error) { +func (self *Vm) RunPrecompiled(p *PrecompiledAccount, input []byte, contract *Contract) (ret []byte, err error) { gas := p.Gas(len(input)) - if context.UseGas(gas) { + if contract.UseGas(gas) { ret = p.Call(input) - return context.Return(ret), nil + return contract.Return(ret), nil } else { return nil, OutOfGasError } @@ -841,18 +366,20 @@ func (self *Vm) RunPrecompiled(p *PrecompiledAccount, input []byte, context *Con // log emits a log event to the environment for each opcode encountered. This is not to be confused with the // LOG* opcode. -func (self *Vm) log(pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *stack, context *Context, err error) { +func (self *Vm) log(pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *stack, contract *Contract, err error) { if Debug { mem := make([]byte, len(memory.Data())) copy(mem, memory.Data()) stck := make([]*big.Int, len(stack.Data())) copy(stck, stack.Data()) - object := context.self.(*state.StateObject) storage := make(map[common.Hash][]byte) - object.EachStorage(func(k, v []byte) { - storage[common.BytesToHash(k)] = v - }) + /* + object := contract.self.(*state.StateObject) + object.EachStorage(func(k, v []byte) { + storage[common.BytesToHash(k)] = v + }) + */ self.env.AddStructLog(StructLog{pc, op, new(big.Int).Set(gas), cost, mem, stck, storage, err}) } diff --git a/core/vm/vm_jit.go b/core/vm/vm_jit.go index 339cb8ea8..07cb52d4a 100644 --- a/core/vm/vm_jit.go +++ b/core/vm/vm_jit.go @@ -30,6 +30,7 @@ void evmjit_destroy(void* _jit); */ import "C" +/* import ( "bytes" "errors" @@ -385,4 +386,4 @@ func env_extcode(_vm unsafe.Pointer, _addr unsafe.Pointer, o_size *uint64) *byte code := vm.Env().State().GetCode(addr) *o_size = uint64(len(code)) return getDataPtr(code) -} +}*/ diff --git a/core/vm_env.go b/core/vm_env.go index a08f024fe..467e34c6b 100644 --- a/core/vm_env.go +++ b/core/vm_env.go @@ -30,13 +30,13 @@ type VMEnv struct { header *types.Header msg Message depth int - chain *ChainManager + chain *BlockChain typ vm.Type // structured logging logs []vm.StructLog } -func NewEnv(state *state.StateDB, chain *ChainManager, msg Message, header *types.Header) *VMEnv { +func NewEnv(state *state.StateDB, chain *BlockChain, msg Message, header *types.Header) *VMEnv { return &VMEnv{ chain: chain, state: state, @@ -53,7 +53,7 @@ func (self *VMEnv) Time() *big.Int { return self.header.Time } func (self *VMEnv) Difficulty() *big.Int { return self.header.Difficulty } func (self *VMEnv) GasLimit() *big.Int { return self.header.GasLimit } func (self *VMEnv) Value() *big.Int { return self.msg.Value() } -func (self *VMEnv) State() *state.StateDB { return self.state } +func (self *VMEnv) Db() vm.Database { 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 } @@ -66,30 +66,34 @@ func (self *VMEnv) GetHash(n uint64) common.Hash { return common.Hash{} } -func (self *VMEnv) AddLog(log *state.Log) { +func (self *VMEnv) AddLog(log *vm.Log) { self.state.AddLog(log) } -func (self *VMEnv) CanTransfer(from vm.Account, balance *big.Int) bool { - return from.Balance().Cmp(balance) >= 0 +func (self *VMEnv) CanTransfer(from common.Address, balance *big.Int) bool { + return self.state.GetBalance(from).Cmp(balance) >= 0 +} + +func (self *VMEnv) MakeSnapshot() vm.Database { + return self.state.Copy() +} + +func (self *VMEnv) SetSnapshot(copy vm.Database) { + self.state.Set(copy.(*state.StateDB)) } func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) error { - return vm.Transfer(from, to, amount) + return Transfer(from, to, amount) } -func (self *VMEnv) Call(me vm.ContextRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { - exe := NewExecution(self, &addr, data, gas, price, value) - return exe.Call(addr, me) +func (self *VMEnv) Call(me vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { + return Call(self, me, addr, data, gas, price, value) } -func (self *VMEnv) CallCode(me vm.ContextRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { - maddr := me.Address() - exe := NewExecution(self, &maddr, data, gas, price, value) - return exe.Call(addr, me) +func (self *VMEnv) CallCode(me vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { + return CallCode(self, me, addr, data, gas, price, value) } -func (self *VMEnv) Create(me vm.ContextRef, data []byte, gas, price, value *big.Int) ([]byte, error, vm.ContextRef) { - exe := NewExecution(self, nil, data, gas, price, value) - return exe.Create(me) +func (self *VMEnv) Create(me vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) { + return Create(self, me, data, gas, price, value) } func (self *VMEnv) StructLogs() []vm.StructLog { |