func runProgram(program *Program, pcstart uint64, mem *Memory, stack *stack, env Environment, contract *Contract, input []byte) ([]byte, error) { contract.Input = input var ( pc uint64 = program.mapping[pcstart] instrCount = 0 ) 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 < uint64(len(program.instructions)) { instrCount++ instr := program.instructions[pc] ret, err := instr.do(program, &pc, env, contract, mem, stack) if err != nil { return nil, err } if instr.halts() { return contract.Return(ret), nil } } contract.Input = nil return contract.Return(nil), nil }
// validatePool removes invalid and processed transactions from the main pool. // If a transaction is removed for being invalid (e.g. out of funds), all sub- // sequent (Still valid) transactions are moved back into the future queue. This // is important to prevent a drained account from DOSing the network with non // executable transactions. func (pool *TxPool) validatePool() { state, err := pool.currentState() if err != nil { glog.V(logger.Info).Infoln("failed to get current state: %v", err) return } balanceCache := make(map[common.Address]*big.Int) // Clean up the pending pool, accumulating invalid nonces gaps := make(map[common.Address]uint64) for hash, tx := range pool.pending { sender, _ := tx.From() // err already checked // Perform light nonce and balance validation balance := balanceCache[sender] if balance == nil { balance = state.GetBalance(sender) balanceCache[sender] = balance } if past := state.GetNonce(sender) > tx.Nonce(); past || balance.Cmp(tx.Cost()) < 0 { // Remove an already past it invalidated transaction if glog.V(logger.Core) { glog.Infof("removed tx (%v) from pool: low tx nonce or out of funds\n", tx) } delete(pool.pending, hash) // Track the smallest invalid nonce to postpone subsequent transactions if !past { if prev, ok := gaps[sender]; !ok || tx.Nonce() < prev { gaps[sender] = tx.Nonce() } } } } // Move all transactions after a gap back to the future queue if len(gaps) > 0 { for hash, tx := range pool.pending { sender, _ := tx.From() if gap, ok := gaps[sender]; ok && tx.Nonce() >= gap { if glog.V(logger.Core) { glog.Infof("postponed tx (%v) due to introduced gap\n", tx) } pool.queueTx(hash, tx) delete(pool.pending, hash) } } } }
// validate and queue transactions. func (self *TxPool) add(tx *types.Transaction) error { hash := tx.Hash() if self.pending[hash] != nil { return fmt.Errorf("Known transaction (%x)", hash[:4]) } err := self.validateTx(tx) if err != nil { return err } self.queueTx(hash, tx) if glog.V(logger.Debug) { var toname string if to := tx.To(); to != nil { toname = common.Bytes2Hex(to[:4]) } else { toname = "[NEW_CONTRACT]" } // we can ignore the error here because From is // verified in ValidateTransaction. f, _ := tx.From() from := common.Bytes2Hex(f[:4]) glog.Infof("(t) %x => %s (%v) %x\n", from, toname, tx.Value, hash) } return nil }
// enqueue schedules a new future import operation, if the block to be imported // has not yet been seen. func (f *Fetcher) enqueue(peer string, block *types.Block) { hash := block.Hash() // Ensure the peer isn't DOSing us count := f.queues[peer] + 1 if count > blockLimit { glog.V(logger.Debug).Infof("Peer %s: discarded block #%d [%x…], exceeded allowance (%d)", peer, block.NumberU64(), hash.Bytes()[:4], blockLimit) propBroadcastDOSMeter.Mark(1) f.forgetHash(hash) return } // Discard any past or too distant blocks if dist := int64(block.NumberU64()) - int64(f.chainHeight()); dist < -maxUncleDist || dist > maxQueueDist { glog.V(logger.Debug).Infof("Peer %s: discarded block #%d [%x…], distance %d", peer, block.NumberU64(), hash.Bytes()[:4], dist) propBroadcastDropMeter.Mark(1) f.forgetHash(hash) return } // Schedule the block for future importing if _, ok := f.queued[hash]; !ok { op := &inject{ origin: peer, block: block, } f.queues[peer] = count f.queued[hash] = op f.queue.Push(op, -float32(block.NumberU64())) if f.queueChangeHook != nil { f.queueChangeHook(op.block.Hash(), true) } if glog.V(logger.Debug) { glog.Infof("Peer %s: queued block #%d [%x…], total %v", peer, block.NumberU64(), hash.Bytes()[:4], f.queue.Size()) } } }
func (c *StateObject) SubBalance(amount *big.Int) { c.SetBalance(new(big.Int).Sub(c.balance, amount)) if glog.V(logger.Core) { glog.Infof("%x: #%d %v (- %v)\n", c.Address(), c.nonce, c.balance, amount) } }
func (self *StateObject) MarkForDeletion() { self.remove = true self.dirty = true if glog.V(logger.Core) { glog.Infof("%x: #%d %v X\n", self.Address(), self.nonce, self.balance) } }
func (env *Work) commitTransactions(transactions types.Transactions, gasPrice *big.Int, bc *core.BlockChain) { gp := new(core.GasPool).AddGas(env.header.GasLimit) for _, tx := range transactions { // We can skip err. It has already been validated in the tx pool from, _ := tx.From() // Check if it falls within margin. Txs from owned accounts are always processed. if tx.GasPrice().Cmp(gasPrice) < 0 && !env.ownedAccounts.Has(from) { // ignore the transaction and transactor. We ignore the transactor // because nonce will fail after ignoring this transaction so there's // no point env.lowGasTransactors.Add(from) glog.V(logger.Info).Infof("transaction(%x) below gas price (tx=%v ask=%v). All sequential txs from this address(%x) will be ignored\n", tx.Hash().Bytes()[:4], common.CurrencyToString(tx.GasPrice()), common.CurrencyToString(gasPrice), from[:4]) } // Continue with the next transaction if the transaction sender is included in // the low gas tx set. This will also remove the tx and all sequential transaction // from this transactor if env.lowGasTransactors.Has(from) { // add tx to the low gas set. This will be removed at the end of the run // owned accounts are ignored if !env.ownedAccounts.Has(from) { env.lowGasTxs = append(env.lowGasTxs, tx) } continue } // Move on to the next transaction when the transactor is in ignored transactions set // This may occur when a transaction hits the gas limit. When a gas limit is hit and // the transaction is processed (that could potentially be included in the block) it // will throw a nonce error because the previous transaction hasn't been processed. // Therefor we need to ignore any transaction after the ignored one. if env.ignoredTransactors.Has(from) { continue } env.state.StartRecord(tx.Hash(), common.Hash{}, 0) err := env.commitTransaction(tx, bc, gp) switch { case core.IsGasLimitErr(err): // ignore the transactor so no nonce errors will be thrown for this account // next time the worker is run, they'll be picked up again. env.ignoredTransactors.Add(from) glog.V(logger.Detail).Infof("Gas limit reached for (%x) in this block. Continue to try smaller txs\n", from[:4]) case err != nil: env.remove.Add(tx.Hash()) if glog.V(logger.Detail) { glog.Infof("TX (%x) failed, will be removed: %v\n", tx.Hash().Bytes()[:4], err) } default: env.tcount++ } } }
// GetWork returns a work package for external miner. The work package consists of 3 strings // result[0], 32 bytes hex encoded current block header pow-hash // result[1], 32 bytes hex encoded seed hash used for DAG // result[2], 32 bytes hex encoded boundary condition ("target"), 2^256/difficulty func (s *PublicMinerAPI) GetWork() ([]string, error) { if !s.Mining() { s.miner.Start(s.miner.coinbase, 0) } if work, err := s.agent.GetWork(); err == nil { return work[:], nil } else { glog.Infof("%v\n", err) } return nil, fmt.Errorf("mining not ready") }
// NewStateObject create a state object whether it exist in the trie or not func (self *StateDB) newStateObject(addr common.Address) *StateObject { if glog.V(logger.Core) { glog.Infof("(+) %x\n", addr) } stateObject := NewStateObject(addr, self.db) stateObject.SetNonce(StartingNonce) self.stateObjects[addr.Str()] = stateObject return stateObject }
// doRefresh performs a lookup for a random target to keep buckets // full. seed nodes are inserted if the table is empty (initial // bootstrap or discarded faulty peers). func (tab *Table) doRefresh(done chan struct{}) { defer close(done) // The Kademlia paper specifies that the bucket refresh should // perform a lookup in the least recently used bucket. We cannot // adhere to this because the findnode target is a 512bit value // (not hash-sized) and it is not easily possible to generate a // sha3 preimage that falls into a chosen bucket. // We perform a lookup with a random target instead. var target NodeID rand.Read(target[:]) result := tab.lookup(target, false) if len(result) > 0 { return } // The table is empty. Load nodes from the database and insert // them. This should yield a few previously seen nodes that are // (hopefully) still alive. seeds := tab.db.querySeeds(seedCount, seedMaxAge) seeds = tab.bondall(append(seeds, tab.nursery...)) if glog.V(logger.Debug) { if len(seeds) == 0 { glog.Infof("no seed nodes found") } for _, n := range seeds { age := time.Since(tab.db.lastPong(n.ID)) glog.Infof("seed node (age %v): %v", age, n) } } tab.mutex.Lock() tab.stuff(seeds) tab.mutex.Unlock() // Finally, do a self lookup to fill up the buckets. tab.lookup(tab.self.ID, false) }
// checkQueue moves transactions that have become processable to main pool. func (pool *TxPool) checkQueue() { // init delayed since tx pool could have been started before any state sync if pool.pendingState == nil { pool.resetState() } var promote txQueue for address, txs := range pool.queue { currentState, err := pool.currentState() if err != nil { glog.Errorf("could not get current state: %v", err) return } balance := currentState.GetBalance(address) var ( guessedNonce = pool.pendingState.GetNonce(address) // nonce currently kept by the tx pool (pending state) trueNonce = currentState.GetNonce(address) // nonce known by the last state ) promote = promote[:0] for hash, tx := range txs { // Drop processed or out of fund transactions if tx.Nonce() < trueNonce || balance.Cmp(tx.Cost()) < 0 { if glog.V(logger.Core) { glog.Infof("removed tx (%v) from pool queue: low tx nonce or out of funds\n", tx) } delete(txs, hash) continue } // Collect the remaining transactions for the next pass. promote = append(promote, txQueueEntry{hash, address, tx}) } // Find the next consecutive nonce range starting at the current account nonce, // pushing the guessed nonce forward if we add consecutive transactions. sort.Sort(promote) for i, entry := range promote { // If we reached a gap in the nonces, enforce transaction limit and stop if entry.Nonce() > guessedNonce { if len(promote)-i > maxQueued { if glog.V(logger.Debug) { glog.Infof("Queued tx limit exceeded for %s. Tx %s removed\n", common.PP(address[:]), common.PP(entry.hash[:])) } for _, drop := range promote[i+maxQueued:] { delete(txs, drop.hash) } } break } // Otherwise promote the transaction and move the guess nonce if needed pool.addTx(entry.hash, address, entry.Transaction) delete(txs, entry.hash) if entry.Nonce() == guessedNonce { guessedNonce++ } } // Delete the entire queue entry if it became empty. if len(txs) == 0 { delete(pool.queue, address) } } }
// CompileProgram compiles the given program and return an error when it fails func CompileProgram(program *Program) (err error) { if progStatus(atomic.LoadInt32(&program.status)) == progCompile { return nil } atomic.StoreInt32(&program.status, int32(progCompile)) defer func() { if err != nil { atomic.StoreInt32(&program.status, int32(progError)) } else { 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++ { switch op := OpCode(program.code[pc]); op { case ADD: program.addInstr(op, pc, opAdd, nil) case SUB: program.addInstr(op, pc, opSub, nil) case MUL: program.addInstr(op, pc, opMul, nil) case DIV: program.addInstr(op, pc, opDiv, nil) case SDIV: program.addInstr(op, pc, opSdiv, nil) case MOD: program.addInstr(op, pc, opMod, nil) case SMOD: program.addInstr(op, pc, opSmod, nil) case EXP: program.addInstr(op, pc, opExp, nil) case SIGNEXTEND: program.addInstr(op, pc, opSignExtend, nil) case NOT: program.addInstr(op, pc, opNot, nil) case LT: program.addInstr(op, pc, opLt, nil) case GT: program.addInstr(op, pc, opGt, nil) case SLT: program.addInstr(op, pc, opSlt, nil) case SGT: program.addInstr(op, pc, opSgt, nil) case EQ: program.addInstr(op, pc, opEq, nil) case ISZERO: program.addInstr(op, pc, opIszero, nil) case AND: program.addInstr(op, pc, opAnd, nil) case OR: program.addInstr(op, pc, opOr, nil) case XOR: program.addInstr(op, pc, opXor, nil) case BYTE: program.addInstr(op, pc, opByte, nil) case ADDMOD: program.addInstr(op, pc, opAddmod, nil) case MULMOD: program.addInstr(op, pc, opMulmod, nil) case SHA3: program.addInstr(op, pc, opSha3, nil) case ADDRESS: program.addInstr(op, pc, opAddress, nil) case BALANCE: program.addInstr(op, pc, opBalance, nil) case ORIGIN: program.addInstr(op, pc, opOrigin, nil) case CALLER: program.addInstr(op, pc, opCaller, nil) case CALLVALUE: program.addInstr(op, pc, opCallValue, nil) case CALLDATALOAD: program.addInstr(op, pc, opCalldataLoad, nil) case CALLDATASIZE: program.addInstr(op, pc, opCalldataSize, nil) case CALLDATACOPY: program.addInstr(op, pc, opCalldataCopy, nil) case CODESIZE: program.addInstr(op, pc, opCodeSize, nil) case EXTCODESIZE: program.addInstr(op, pc, opExtCodeSize, nil) case CODECOPY: program.addInstr(op, pc, opCodeCopy, nil) case EXTCODECOPY: program.addInstr(op, pc, opExtCodeCopy, nil) case GASPRICE: program.addInstr(op, pc, opGasprice, nil) case BLOCKHASH: program.addInstr(op, pc, opBlockhash, nil) case COINBASE: program.addInstr(op, pc, opCoinbase, nil) case TIMESTAMP: program.addInstr(op, pc, opTimestamp, nil) case NUMBER: program.addInstr(op, pc, opNumber, nil) case DIFFICULTY: program.addInstr(op, pc, opDifficulty, nil) case GASLIMIT: program.addInstr(op, pc, opGasLimit, nil) 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) bytes := getData([]byte(program.code), new(big.Int).SetUint64(pc+1), new(big.Int).SetUint64(size)) program.addInstr(op, pc, opPush, common.Bytes2Big(bytes)) pc += size case POP: program.addInstr(op, pc, opPop, nil) case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16: program.addInstr(op, pc, opDup, big.NewInt(int64(op-DUP1+1))) case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16: program.addInstr(op, pc, opSwap, big.NewInt(int64(op-SWAP1+2))) case LOG0, LOG1, LOG2, LOG3, LOG4: program.addInstr(op, pc, opLog, big.NewInt(int64(op-LOG0))) case MLOAD: program.addInstr(op, pc, opMload, nil) case MSTORE: program.addInstr(op, pc, opMstore, nil) case MSTORE8: program.addInstr(op, pc, opMstore8, nil) case SLOAD: program.addInstr(op, pc, opSload, nil) case SSTORE: program.addInstr(op, pc, opSstore, nil) case JUMP: program.addInstr(op, pc, opJump, nil) case JUMPI: program.addInstr(op, pc, opJumpi, nil) case JUMPDEST: program.addInstr(op, pc, opJumpdest, nil) program.destinations[pc] = struct{}{} case PC: program.addInstr(op, pc, opPc, big.NewInt(int64(pc))) case MSIZE: program.addInstr(op, pc, opMsize, nil) case GAS: program.addInstr(op, pc, opGas, nil) case CREATE: program.addInstr(op, pc, opCreate, nil) case CALL: program.addInstr(op, pc, opCall, nil) case CALLCODE: program.addInstr(op, pc, opCallCode, nil) case RETURN: program.addInstr(op, pc, opReturn, nil) case SUICIDE: program.addInstr(op, pc, opSuicide, nil) case STOP: // Stop the contract program.addInstr(op, pc, opStop, nil) default: program.addInstr(op, pc, nil, nil) } } optimiseProgram(program) return nil }
// 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 *BlockChain) reorg(oldBlock, newBlock *types.Block) error { var ( newChain types.Blocks commonBlock *types.Block oldStart = oldBlock newStart = newBlock deletedTxs types.Transactions deletedLogs vm.Logs // collectLogs collects the logs that were generated during the // processing of the block that corresponds with the given hash. // These logs are later announced as deleted. collectLogs = func(h common.Hash) { // Coalesce logs receipts := GetBlockReceipts(self.chainDb, h) for _, receipt := range receipts { deletedLogs = append(deletedLogs, receipt.Logs...) } } ) // first reduce whoever is higher bound if oldBlock.NumberU64() > newBlock.NumberU64() { // reduce old chain for oldBlock = oldBlock; oldBlock != nil && oldBlock.NumberU64() != newBlock.NumberU64(); oldBlock = self.GetBlock(oldBlock.ParentHash()) { deletedTxs = append(deletedTxs, oldBlock.Transactions()...) collectLogs(oldBlock.Hash()) } } else { // reduce new chain and append new chain blocks for inserting later on for newBlock = newBlock; newBlock != nil && newBlock.NumberU64() != oldBlock.NumberU64(); newBlock = self.GetBlock(newBlock.ParentHash()) { newChain = append(newChain, newBlock) } } if oldBlock == nil { return fmt.Errorf("Invalid old chain") } if newBlock == nil { return fmt.Errorf("Invalid new chain") } numSplit := newBlock.Number() for { if oldBlock.Hash() == newBlock.Hash() { commonBlock = oldBlock break } newChain = append(newChain, newBlock) deletedTxs = append(deletedTxs, oldBlock.Transactions()...) collectLogs(oldBlock.Hash()) oldBlock, newBlock = self.GetBlock(oldBlock.ParentHash()), self.GetBlock(newBlock.ParentHash()) if oldBlock == nil { return fmt.Errorf("Invalid old chain") } if newBlock == nil { return fmt.Errorf("Invalid new chain") } } if glog.V(logger.Debug) { commonHash := commonBlock.Hash() glog.Infof("Chain split detected @ %x. Reorganising chain from #%v %x to %x", commonHash[:4], numSplit, oldStart.Hash().Bytes()[:4], newStart.Hash().Bytes()[:4]) } var addedTxs types.Transactions // insert blocks. Order does not matter. Last block will be written in ImportChain itself which creates the new head properly for _, block := range newChain { // insert the block in the canonical way, re-writing history self.insert(block) // write canonical receipts and transactions if err := WriteTransactions(self.chainDb, block); err != nil { return err } receipts := GetBlockReceipts(self.chainDb, block.Hash()) // write receipts if err := WriteReceipts(self.chainDb, receipts); err != nil { return err } // Write map map bloom filters if err := WriteMipmapBloom(self.chainDb, block.NumberU64(), receipts); err != nil { return err } addedTxs = append(addedTxs, block.Transactions()...) } // calculate the difference between deleted and added transactions diff := types.TxDifference(deletedTxs, addedTxs) // When transactions get deleted from the database that means the // receipts that were created in the fork must also be deleted for _, tx := range diff { DeleteReceipt(self.chainDb, tx.Hash()) DeleteTransaction(self.chainDb, tx.Hash()) } // Must be posted in a goroutine because of the transaction pool trying // to acquire the chain manager lock if len(diff) > 0 { go self.eventMux.Post(RemovedTransactionEvent{diff}) } if len(deletedLogs) > 0 { go self.eventMux.Post(RemovedLogEvent{deletedLogs}) } return nil }
func ImportChain(chain *core.BlockChain, fn string) error { // Watch for Ctrl-C while the import is running. // If a signal is received, the import will stop at the next batch. interrupt := make(chan os.Signal, 1) stop := make(chan struct{}) signal.Notify(interrupt, os.Interrupt) defer signal.Stop(interrupt) defer close(interrupt) go func() { if _, ok := <-interrupt; ok { glog.Info("caught interrupt during import, will stop at next batch") } close(stop) }() checkInterrupt := func() bool { select { case <-stop: return true default: return false } } glog.Infoln("Importing blockchain", fn) fh, err := os.Open(fn) if err != nil { return err } defer fh.Close() stream := rlp.NewStream(fh, 0) // Run actual the import. blocks := make(types.Blocks, importBatchSize) n := 0 for batch := 0; ; batch++ { // Load a batch of RLP blocks. if checkInterrupt() { return fmt.Errorf("interrupted") } i := 0 for ; i < importBatchSize; i++ { var b types.Block if err := stream.Decode(&b); err == io.EOF { break } else if err != nil { return fmt.Errorf("at block %d: %v", n, err) } // don't import first block if b.NumberU64() == 0 { i-- continue } blocks[i] = &b n++ } if i == 0 { break } // Import the batch. if checkInterrupt() { return fmt.Errorf("interrupted") } if hasAllBlocks(chain, blocks[:i]) { glog.Infof("skipping batch %d, all blocks present [%x / %x]", batch, blocks[0].Hash().Bytes()[:4], blocks[i-1].Hash().Bytes()[:4]) continue } if _, err := chain.InsertChain(blocks[:i]); err != nil { return fmt.Errorf("invalid block %d: %v", n, err) } } return nil }
// optimeProgram optimises a JIT program creating segments out of program // instructions. Currently covered are multi-pushes and static jumps func optimiseProgram(program *Program) { var load []instruction var ( statsJump = 0 statsPush = 0 ) if glog.V(logger.Debug) { glog.Infof("optimising %x\n", program.Id[:4]) tstart := time.Now() defer func() { glog.Infof("optimised %x done in %v with JMP: %d PSH: %d\n", program.Id[:4], time.Since(tstart), statsJump, statsPush) }() } /* code := Parse(program.code) for _, test := range [][]OpCode{ []OpCode{PUSH, PUSH, ADD}, []OpCode{PUSH, PUSH, SUB}, []OpCode{PUSH, PUSH, MUL}, []OpCode{PUSH, PUSH, DIV}, } { matchCount := 0 MatchFn(code, test, func(i int) bool { matchCount++ return true }) fmt.Printf("found %d match count on: %v\n", matchCount, test) } */ for i := 0; i < len(program.instructions); i++ { instr := program.instructions[i].(instruction) switch { case instr.op.IsPush(): load = append(load, instr) case instr.op.IsStaticJump(): if len(load) == 0 { continue } // if the push load is greater than 1, finalise that // segment first if len(load) > 2 { seg, size := makePushSeg(load[:len(load)-1]) program.instructions[i-size-1] = seg statsPush++ } // create a segment consisting of a pre determined // jump, destination and validity. seg := makeStaticJumpSeg(load[len(load)-1].data, program) program.instructions[i-1] = seg statsJump++ load = nil default: // create a new N pushes segment if len(load) > 1 { seg, size := makePushSeg(load) program.instructions[i-size] = seg statsPush++ } load = nil } } }
// Run loops and evaluates the contract's code with the given input data 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) // 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 { // In case of a VM exception (known exceptions) all gas consumed (panics NOT included). contract.UseGas(contract.Gas) ret = contract.Return(nil) } }() 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(contract.Code) // codehash is used when doing jump dest caching program *Program ) if EnableJit { // 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(contract.Code) perr := CompileProgram(program) if perr == nil { 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(contract.Code) go func() { err := CompileProgram(program) if err != nil { glog.V(logger.Info).Infoln("error compiling program", err) return } }() } } } var ( 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 // 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 !contract.jumpdests.has(codehash, code, to) { nop := contract.GetOp(to.Uint64()) return fmt.Errorf("invalid jump destination (%v) %v", nop, to) } pc = to.Uint64() return nil } 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, contract.Gas, cost, mem, stack, contract, err) } }() 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 ; ; 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) } } */ // Get the memory location of pc op = contract.GetOp(pc) // calculate the new memory size and gas price for the current executing opcode 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 !contract.UseGas(cost) { return nil, OutOfGasError } // Resize the memory calculated previously mem.Resize(newMemSize.Uint64()) // Add a log message self.log(pc, op, contract.Gas, cost, mem, stack, contract, nil) if opPtr := jumpTable[op]; opPtr.valid { if opPtr.fn != nil { opPtr.fn(instruction{}, &pc, self.env, contract, mem, stack) } else { 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 } continue case JUMPI: pos, cond := stack.pop(), stack.pop() if cond.Cmp(common.BigTrue) >= 0 { if err := jump(pc, pos); err != nil { return nil, err } continue } case RETURN: offset, size := stack.pop(), stack.pop() ret := mem.GetPtr(offset.Int64(), size.Int64()) return contract.Return(ret), nil case SUICIDE: opSuicide(instruction{}, nil, self.env, contract, mem, stack) fallthrough case STOP: // Stop the contract return contract.Return(nil), nil } } } else { return nil, fmt.Errorf("Invalid opcode %x", op) } pc++ } }
// 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 *BlockChain) InsertChain(chain types.Blocks) (int, error) { self.wg.Add(1) defer self.wg.Done() self.chainmu.Lock() defer self.chainmu.Unlock() // A queued approach to delivering events. This is generally // faster than direct delivery and requires much less mutex // acquiring. var ( stats struct{ queued, processed, ignored int } events = make([]interface{}, 0, len(chain)) coalescedLogs vm.Logs tstart = time.Now() nonceChecked = make([]bool, len(chain)) ) // Start the parallel nonce verifier. nonceAbort, nonceResults := verifyNoncesFromBlocks(self.pow, chain) defer close(nonceAbort) txcount := 0 for i, block := range chain { if atomic.LoadInt32(&self.procInterrupt) == 1 { glog.V(logger.Debug).Infoln("Premature abort during block chain processing") break } bstart := time.Now() // Wait for block i's nonce to be verified before processing // its state transition. for !nonceChecked[i] { r := <-nonceResults nonceChecked[r.index] = true if !r.valid { block := chain[r.index] return r.index, &BlockNonceErr{Hash: block.Hash(), Number: block.Number(), Nonce: block.Nonce()} } } if BadHashes[block.Hash()] { err := BadHashError(block.Hash()) reportBlock(block, err) return i, err } // Stage 1 validation of the block using the chain's validator // interface. err := self.Validator().ValidateBlock(block) if err != nil { if IsKnownBlockErr(err) { stats.ignored++ continue } if err == BlockFutureErr { // Allow up to MaxFuture second in the future blocks. If this limit // is exceeded the chain is discarded and processed at a later time // if given. max := big.NewInt(time.Now().Unix() + maxTimeFutureBlocks) if block.Time().Cmp(max) == 1 { return i, fmt.Errorf("%v: BlockFutureErr, %v > %v", BlockFutureErr, block.Time(), max) } self.futureBlocks.Add(block.Hash(), block) stats.queued++ continue } if IsParentErr(err) && self.futureBlocks.Contains(block.ParentHash()) { self.futureBlocks.Add(block.Hash(), block) stats.queued++ continue } reportBlock(block, err) return i, err } // Create a new statedb using the parent block and report an // error if it fails. statedb, err := state.New(self.GetBlock(block.ParentHash()).Root(), self.chainDb) if err != nil { reportBlock(block, err) return i, err } // Process block using the parent state as reference point. receipts, logs, usedGas, err := self.processor.Process(block, statedb) if err != nil { reportBlock(block, err) return i, err } // Validate the state using the default validator err = self.Validator().ValidateState(block, self.GetBlock(block.ParentHash()), statedb, receipts, usedGas) if err != nil { reportBlock(block, err) return i, err } // Write state changes to database _, err = statedb.Commit() if err != nil { return i, err } // coalesce logs for later processing coalescedLogs = append(coalescedLogs, logs...) if err := WriteBlockReceipts(self.chainDb, block.Hash(), receipts); err != nil { return i, err } txcount += len(block.Transactions()) // write the block to the chain and get the status status, err := self.WriteBlock(block) if err != nil { return i, err } switch status { case CanonStatTy: if glog.V(logger.Debug) { glog.Infof("[%v] inserted block #%d (%d TXs %v G %d UNCs) (%x...). Took %v\n", time.Now().UnixNano(), block.Number(), len(block.Transactions()), block.GasUsed(), len(block.Uncles()), block.Hash().Bytes()[0:4], time.Since(bstart)) } events = append(events, ChainEvent{block, block.Hash(), logs}) // This puts transactions in a extra db for rpc if err := WriteTransactions(self.chainDb, block); err != nil { return i, err } // store the receipts if err := WriteReceipts(self.chainDb, receipts); err != nil { return i, err } // Write map map bloom filters if err := WriteMipmapBloom(self.chainDb, block.NumberU64(), receipts); err != nil { return i, err } case SideStatTy: if glog.V(logger.Detail) { glog.Infof("inserted forked block #%d (TD=%v) (%d TXs %d UNCs) (%x...). Took %v\n", block.Number(), block.Difficulty(), len(block.Transactions()), len(block.Uncles()), block.Hash().Bytes()[0:4], time.Since(bstart)) } events = append(events, ChainSideEvent{block, logs}) case SplitStatTy: events = append(events, ChainSplitEvent{block, logs}) } stats.processed++ } if (stats.queued > 0 || stats.processed > 0 || stats.ignored > 0) && bool(glog.V(logger.Info)) { tend := time.Since(tstart) start, end := chain[0], chain[len(chain)-1] glog.Infof("imported %d block(s) (%d queued %d ignored) including %d txs in %v. #%v [%x / %x]\n", stats.processed, stats.queued, stats.ignored, txcount, tend, end.Number(), start.Hash().Bytes()[:4], end.Hash().Bytes()[:4]) } go self.postChainEvents(events, coalescedLogs) return 0, nil }