core, core/vm: limited state writes and limited state copies

Limited state writes by tracking objects which have been changed. The
same principle is used during state forking by tracking which state fork
owns / changed what.

WIP: the name `ownedStateObjects` will change to `changedStateObjects`
This commit is contained in:
Jeffrey Wilcke 2016-09-18 23:14:55 +02:00
parent c6a0cb72bb
commit 0768a8bc6b
12 changed files with 166 additions and 53 deletions

View file

@ -44,6 +44,10 @@ var (
Name: "debug", Name: "debug",
Usage: "output full trace logs", Usage: "output full trace logs",
} }
VMStatsFlag = cli.BoolFlag{
Name: "vmstats",
Usage: "Display EVM statistics",
}
CodeFlag = cli.StringFlag{ CodeFlag = cli.StringFlag{
Name: "code", Name: "code",
Usage: "EVM code", Usage: "EVM code",
@ -113,14 +117,20 @@ var (
Name: "create", Name: "create",
Usage: "indicates the action should be create rather than call", Usage: "indicates the action should be create rather than call",
} }
NoOptimiseFlag = cli.BoolFlag{
Name: "nooptimise",
Usage: "disable program optimisations",
}
) )
func init() { func init() {
app.Flags = []cli.Flag{ app.Flags = []cli.Flag{
CreateFlag, CreateFlag,
NoOptimiseFlag,
DebugFlag, DebugFlag,
VerbosityFlag, VerbosityFlag,
SysStatFlag, SysStatFlag,
VMStatsFlag,
CodeFlag, CodeFlag,
GasFlag, GasFlag,
PriceFlag, PriceFlag,
@ -172,8 +182,9 @@ func run(ctx *cli.Context) error {
backend, backend,
ruleSet{}, ruleSet{},
vm.Config{ vm.Config{
Debug: ctx.GlobalBool(DebugFlag.Name), Debug: ctx.GlobalBool(DebugFlag.Name),
Tracer: logger, Tracer: logger,
NoOptimise: ctx.GlobalBool(NoOptimiseFlag.Name),
}, },
) )
@ -208,7 +219,10 @@ func run(ctx *cli.Context) error {
state.Commit(st) state.Commit(st)
fmt.Println(string(st.Dump())) fmt.Println(string(st.Dump()))
} }
vm.StdErrFormat(logger.StructLogs())
if ctx.GlobalBool(DebugFlag.Name) {
vm.StdErrFormat(logger.StructLogs())
}
if ctx.GlobalBool(SysStatFlag.Name) { if ctx.GlobalBool(SysStatFlag.Name) {
var mem runtime.MemStats var mem runtime.MemStats
@ -223,7 +237,13 @@ num gc: %d
`, mem.Alloc, mem.TotalAlloc, mem.Mallocs, mem.HeapAlloc, mem.HeapObjects, mem.NumGC) `, mem.Alloc, mem.TotalAlloc, mem.Mallocs, mem.HeapAlloc, mem.HeapObjects, mem.NumGC)
} }
fmt.Printf("OUT: 0x%x", ret) if ctx.GlobalBool(VMStatsFlag.Name) {
fmt.Printf(`VM Stats
Gas requirement: %v
`, vmenv.Gasser.HighwaterMark())
}
fmt.Printf("0x%x", ret)
if err != nil { if err != nil {
fmt.Printf(" error: %v", err) fmt.Printf(" error: %v", err)
} }

View file

@ -914,13 +914,16 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
reportBlock(block, err) reportBlock(block, err)
return i, err return i, err
} }
tstart := time.Now()
// flatten the state before committing. // flatten the state before committing.
st = state.Flatten(st) st = state.Flatten(st)
fmt.Println("reduce took", time.Since(tstart))
// Write state changes to database // Write state changes to database
_, err = state.Commit(st) _, err = state.Commit(st)
if err != nil { if err != nil {
return i, err return i, err
} }
glog.V(logger.Info).Infoln("state stats:", st)
// coalesce logs for later processing // coalesce logs for later processing
coalescedLogs = append(coalescedLogs, logs...) coalescedLogs = append(coalescedLogs, logs...)
@ -938,7 +941,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
switch status { switch status {
case CanonStatTy: case CanonStatTy:
if glog.V(logger.Debug) { if glog.V(logger.Info) {
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)) 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}) events = append(events, ChainEvent{block, block.Hash(), logs})

View file

@ -69,13 +69,13 @@ func (c EVMCallContext) exec(env *vm.Environment, caller vm.ContractRef, address
// Depth check execution. Fail if we're trying to execute above the // Depth check execution. Fail if we're trying to execute above the
// limit. // limit.
if env.Depth > int(params.CallCreateDepth.Int64()) { if env.Depth > int(params.CallCreateDepth.Int64()) {
caller.ReturnGas(gas.Uint64()) env.Gasser.ReturnGas(caller, gas.Uint64())
return nil, common.Address{}, vm.DepthError return nil, common.Address{}, vm.DepthError
} }
if !c.CanTransfer(env.Db(), caller.Address(), value) { if !c.CanTransfer(env.Db(), caller.Address(), value) {
caller.ReturnGas(gas.Uint64()) env.Gasser.ReturnGas(caller, gas.Uint64())
return nil, common.Address{}, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address())) return nil, common.Address{}, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address()))
} }
@ -113,7 +113,9 @@ func (c EVMCallContext) exec(env *vm.Environment, caller vm.ContractRef, address
// only. // only.
contract := vm.NewContract(caller, to, value, gas) contract := vm.NewContract(caller, to, value, gas)
contract.SetCallCode(codeAddr, code) contract.SetCallCode(codeAddr, code)
defer contract.Finalise() defer func() {
contract.Finalise(env.Gasser)
}()
ret, err = evm.Run(contract, input) ret, err = evm.Run(contract, input)
// if the contract creation ran successfully and no errors were returned // if the contract creation ran successfully and no errors were returned
@ -123,7 +125,7 @@ func (c EVMCallContext) exec(env *vm.Environment, caller vm.ContractRef, address
if err == nil && createAccount { if err == nil && createAccount {
dataGas := big.NewInt(int64(len(ret))) dataGas := big.NewInt(int64(len(ret)))
dataGas.Mul(dataGas, params.CreateDataGas) dataGas.Mul(dataGas, params.CreateDataGas)
if contract.UseGas(dataGas.Uint64()) { if env.Gasser.UseGas(contract, dataGas.Uint64()) { //contract.UseGas(dataGas.Uint64()) {
env.Db().SetCode(*address, ret) env.Db().SetCode(*address, ret)
} else { } else {
err = vm.CodeStoreOutOfGasError err = vm.CodeStoreOutOfGasError
@ -134,7 +136,7 @@ func (c EVMCallContext) exec(env *vm.Environment, caller vm.ContractRef, address
// above we revert to the snapshot and consume any gas remaining. Additionally // above we revert to the snapshot and consume any gas remaining. Additionally
// when we're in homestead this also counts for code storage gas errors. // when we're in homestead this also counts for code storage gas errors.
if err != nil && (env.RuleSet().IsHomestead(env.BlockNumber) || err != vm.CodeStoreOutOfGasError) { if err != nil && (env.RuleSet().IsHomestead(env.BlockNumber) || err != vm.CodeStoreOutOfGasError) {
contract.UseGas(contract.Gas()) env.Gasser.UseGas(contract, contract.Gas()) //contract.UseGas(contract.Gas())
env.Backend.Set(snapshotPreTransfer) env.Backend.Set(snapshotPreTransfer)
} }
@ -147,7 +149,8 @@ func (c EVMCallContext) execDelegateCall(env *vm.Environment, caller vm.Contract
// Depth check execution. Fail if we're trying to execute above the // Depth check execution. Fail if we're trying to execute above the
// limit. // limit.
if env.Depth > int(params.CallCreateDepth.Int64()) { if env.Depth > int(params.CallCreateDepth.Int64()) {
caller.ReturnGas(gas.Uint64()) env.Gasser.ReturnGas(caller, gas.Uint64())
return nil, common.Address{}, vm.DepthError return nil, common.Address{}, vm.DepthError
} }
@ -164,11 +167,13 @@ func (c EVMCallContext) execDelegateCall(env *vm.Environment, caller vm.Contract
// Iinitialise a new contract and make initialise the delegate values // Iinitialise a new contract and make initialise the delegate values
contract := vm.NewContract(caller, to, value, gas).AsDelegate() contract := vm.NewContract(caller, to, value, gas).AsDelegate()
contract.SetCallCode(codeAddr, code) contract.SetCallCode(codeAddr, code)
defer contract.Finalise() defer func() {
contract.Finalise(env.Gasser)
}()
ret, err = evm.Run(contract, input) ret, err = evm.Run(contract, input)
if err != nil { if err != nil {
contract.UseGas(contract.Gas()) env.Gasser.UseGas(contract, contract.Gas()) //contract.UseGas(contract.Gas())
env.Backend.Set(snapshot) env.Backend.Set(snapshot)
} }

View file

@ -21,8 +21,9 @@ type State struct {
parent *State parent *State
StateObjects map[common.Address]*StateObject StateObjects map[common.Address]*StateObject
refund *big.Int ownedStateObjects map[common.Address]bool
refund *big.Int
logIdx uint logIdx uint
logs []*vm.Log logs []*vm.Log
@ -44,10 +45,11 @@ func New(root common.Hash, db ethdb.Database) (*State, error) {
} }
return &State{ return &State{
Db: db, Db: db,
Trie: tr, Trie: tr,
StateObjects: make(map[common.Address]*StateObject), StateObjects: make(map[common.Address]*StateObject),
refund: new(big.Int), ownedStateObjects: make(map[common.Address]bool),
refund: new(big.Int),
}, nil }, nil
} }
@ -90,9 +92,32 @@ func (s *State) Read(address common.Address) (object *StateObject, inCache bool)
glog.Errorf("can't decode object at %x: %v", address[:], err) glog.Errorf("can't decode object at %x: %v", address[:], err)
return nil, false return nil, false
} }
return stateObject, false return stateObject, false
} }
func (s *State) GetOrNewStateObject(address common.Address) *StateObject {
stateObject, inCache := s.Read(address)
if stateObject != nil {
if !inCache || !s.ownedStateObjects[address] {
stateObject = stateObject.Copy()
s.StateObjects[address] = stateObject
s.ownedStateObjects[address] = true
}
return stateObject
}
if stateObject == nil || stateObject.deleted {
stateObject = NewStateObject(address, s.Db)
stateObject.SetNonce(StartingNonce)
s.StateObjects[address] = stateObject
s.ownedStateObjects[address] = true
}
return stateObject
}
/*
func (s *State) GetOrNewStateObject(address common.Address) *StateObject { func (s *State) GetOrNewStateObject(address common.Address) *StateObject {
stateObject := s.GetStateObject(address) stateObject := s.GetStateObject(address)
if stateObject == nil || stateObject.deleted { if stateObject == nil || stateObject.deleted {
@ -104,14 +129,15 @@ func (s *State) GetOrNewStateObject(address common.Address) *StateObject {
return stateObject return stateObject
} }
*/
func (s *State) GetStateObject(address common.Address) *StateObject { func (s *State) GetStateObject(address common.Address) *StateObject {
account, inCache := s.Read(address) account, inCache := s.Read(address)
if account != nil { if account != nil {
if !inCache { if !inCache {
s.StateObjects[address] = account.Copy() s.StateObjects[address] = account
} }
return s.StateObjects[address] return account
} }
return nil return nil
} }
@ -294,6 +320,7 @@ func (s *State) UpdateStateObject(stateObject *StateObject) {
} }
func (s *State) Reset(root common.Hash) error { func (s *State) Reset(root common.Hash) error {
fmt.Println("reset")
var ( var (
err error err error
tr = s.Trie tr = s.Trie
@ -304,10 +331,13 @@ func (s *State) Reset(root common.Hash) error {
} }
} }
*s = State{ *s = State{
Db: s.Db, Db: s.Db,
Trie: tr, Trie: tr,
StateObjects: make(map[common.Address]*StateObject), StateObjects: s.StateObjects,
refund: new(big.Int), ownedStateObjects: s.ownedStateObjects,
//StateObjects: make(map[common.Address]*StateObject),
//ownedStateObjects: make(map[common.Address]bool),
refund: new(big.Int),
} }
return nil return nil
} }
@ -345,11 +375,12 @@ func Fork(parent *State) *State {
Db: parent.Db, Db: parent.Db,
Trie: parent.Trie, Trie: parent.Trie,
parent: parent, parent: parent,
StateObjects: make(map[common.Address]*StateObject), StateObjects: make(map[common.Address]*StateObject),
refund: new(big.Int), ownedStateObjects: make(map[common.Address]bool),
logIdx: parent.logIdx, refund: new(big.Int),
logs: nil, logIdx: parent.logIdx,
logs: nil,
} }
} }
@ -366,15 +397,19 @@ func Flatten(s *State) *State {
flattenedState = Flatten(s.parent) flattenedState = Flatten(s.parent)
} else { } else {
flattenedState = &State{ flattenedState = &State{
Db: s.Db, Db: s.Db,
Trie: s.Trie, Trie: s.Trie,
refund: new(big.Int), refund: new(big.Int),
StateObjects: make(map[common.Address]*StateObject), StateObjects: make(map[common.Address]*StateObject),
ownedStateObjects: make(map[common.Address]bool),
} }
} }
for address, object := range s.StateObjects { for address, object := range s.StateObjects {
flattenedState.StateObjects[address] = object flattenedState.StateObjects[address] = object
if s.ownedStateObjects[address] {
flattenedState.ownedStateObjects[address] = true
}
} }
flattenedState.logs = append(flattenedState.logs, s.logs...) flattenedState.logs = append(flattenedState.logs, s.logs...)
@ -383,6 +418,10 @@ func Flatten(s *State) *State {
return flattenedState return flattenedState
} }
func (s *State) String() string {
return fmt.Sprintf("objects: %d owned: %d", len(s.StateObjects), len(s.ownedStateObjects))
}
func IntermediateRoot(state *State) common.Hash { func IntermediateRoot(state *State) common.Hash {
s := Flatten(state) s := Flatten(state)

View file

@ -27,13 +27,13 @@ func CommitBatch(state *State) (common.Hash, ethdb.Batch) {
func stateCommit(state *State, db trie.DatabaseWriter) (common.Hash, error) { func stateCommit(state *State, db trie.DatabaseWriter) (common.Hash, error) {
// make sure the state is flattened before committing // make sure the state is flattened before committing
state = Flatten(state) state = Flatten(state)
for _, stateObject := range state.StateObjects { for address, stateObject := range state.StateObjects {
if stateObject.remove { if stateObject.remove {
// If the object has been removed, don't bother syncing it // If the object has been removed, don't bother syncing it
// and just mark it for deletion in the trie. // and just mark it for deletion in the trie.
stateObject.deleted = true stateObject.deleted = true
state.Trie.Delete(stateObject.Address().Bytes()[:]) state.Trie.Delete(stateObject.Address().Bytes()[:])
} else { } else if state.ownedStateObjects[address] {
// Write any contract code associated with the state object // Write any contract code associated with the state object
if len(stateObject.code) > 0 { if len(stateObject.code) > 0 {
if err := db.Put(stateObject.codeHash, stateObject.code); err != nil { if err := db.Put(stateObject.codeHash, stateObject.code); err != nil {

View file

@ -114,14 +114,14 @@ func (c *Contract) Caller() common.Address {
// Finalise finalises the contract and returning any remaining gas to the original // Finalise finalises the contract and returning any remaining gas to the original
// caller. // caller.
func (c *Contract) Finalise() { func (c *Contract) Finalise(gasser *Gasser) {
c.gas.SetUint64(c.gas64) c.gas.SetUint64(c.gas64)
// Return the remaining gas to the caller // Return the remaining gas to the caller
c.caller.ReturnGas(c.gas64) gasser.ReturnGas(c.caller, c.gas64)
} }
// UseGas attempts the use gas and subtracts it and returns true on success // UseGas attempts the use gas and subtracts it and returns true on success
func (c *Contract) UseGas(gas uint64) (ok bool) { func (c *Contract) useGas(gas uint64) (ok bool) {
if c.gas64 < gas { if c.gas64 < gas {
return false return false
} }

View file

@ -45,7 +45,7 @@ var PrecompiledContracts = map[common.Address]PrecompiledContract{
// RunPrecompile runs and evaluate the output of a precompiled contract defined in contracts.go // RunPrecompile runs and evaluate the output of a precompiled contract defined in contracts.go
func RunPrecompiled(p PrecompiledContract, input []byte, contract *Contract) (ret []byte, err error) { func RunPrecompiled(p PrecompiledContract, input []byte, contract *Contract) (ret []byte, err error) {
gas := p.RequiredGas(len(input)) gas := p.RequiredGas(len(input))
if contract.UseGas(gas.Uint64()) { if contract.useGas(gas.Uint64()) {
ret = p.Run(input) ret = p.Run(input)
return ret, nil return ret, nil

View file

@ -69,9 +69,14 @@ func (instr instruction) do(program *Program, pc *uint64, env *Environment, cont
// Use the calculated gas. When insufficient gas is present, use all gas and return an // Use the calculated gas. When insufficient gas is present, use all gas and return an
// Out Of Gas error // Out Of Gas error
if !contract.UseGas(cost) { if !env.Gasser.UseGas(contract, cost) {
return nil, OutOfGasError return nil, OutOfGasError
} }
/*
if !contract.UseGas(cost) {
return nil, OutOfGasError
}
*/
// Resize the memory calculated previously // Resize the memory calculated previously
memory.Resize(newMemSize) memory.Resize(newMemSize)
@ -515,10 +520,11 @@ func opCreate(instr instruction, pc *uint64, env *Environment, contract *Contrac
value = stack.pop() value = stack.pop()
offset, size = stack.pop(), stack.pop() offset, size = stack.pop(), stack.pop()
input = memory.Get(offset.Int64(), size.Int64()) input = memory.Get(offset.Int64(), size.Int64())
gas = new(big.Int).SetUint64(contract.gas64) gas = contract.gas64
) )
contract.UseGas(contract.gas64)
_, addr, suberr := env.Create(contract, input, gas, value) contract.useGas(gas)
_, addr, suberr := env.Create(contract, input, new(big.Int).SetUint64(gas), value)
// Push item on the stack based on the returned error. If the ruleset is // Push item on the stack based on the returned error. If the ruleset is
// homestead we must check for CodeStoreOutOfGasError (homestead only // homestead we must check for CodeStoreOutOfGasError (homestead only
// rule) and treat as an error, if the ruleset is frontier we must // rule) and treat as an error, if the ruleset is frontier we must
@ -530,6 +536,8 @@ func opCreate(instr instruction, pc *uint64, env *Environment, contract *Contrac
} else { } else {
stack.push(addr.Big()) stack.push(addr.Big())
} }
env.Gasser.mark(gas - contract.Gas())
} }
func opCall(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { func opCall(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {

View file

@ -78,6 +78,36 @@ type Account interface {
Value() *big.Int Value() *big.Int
} }
type Gasser struct {
highwaterMark uint64
slider uint64
}
func (g *Gasser) mark(gas uint64) {
g.slider += gas
if g.slider > g.highwaterMark {
g.highwaterMark = g.slider
}
}
func (g *Gasser) UseGas(contract *Contract, gas uint64) bool {
ok := contract.useGas(gas)
if ok {
g.mark(gas)
}
return ok
}
func (g *Gasser) ReturnGas(contract ContractRef, gas uint64) {
g.slider -= gas
contract.ReturnGas(gas)
}
func (g *Gasser) HighwaterMark() uint64 {
return g.highwaterMark
}
// Context provides the EVM with auxilary information. Once provided it shouldn't be modified. // Context provides the EVM with auxilary information. Once provided it shouldn't be modified.
type Context struct { type Context struct {
CallContext CallContext
@ -121,6 +151,7 @@ type Environment struct {
Context Context
Backend Backend Backend Backend
Gasser *Gasser
ruleSet RuleSet ruleSet RuleSet
vmConfig Config vmConfig Config
@ -133,6 +164,7 @@ type Environment struct {
func NewEnvironment(context Context, backend Backend, ruleSet RuleSet, vmCfg Config) *Environment { func NewEnvironment(context Context, backend Backend, ruleSet RuleSet, vmCfg Config) *Environment {
env := &Environment{ env := &Environment{
Context: context, Context: context,
Gasser: new(Gasser),
Backend: backend, Backend: backend,
vmConfig: vmCfg, vmConfig: vmCfg,
ruleSet: ruleSet, ruleSet: ruleSet,
@ -143,7 +175,7 @@ func NewEnvironment(context Context, backend Backend, ruleSet RuleSet, vmCfg Con
func (env *Environment) Call(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) { func (env *Environment) Call(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
if env.vmConfig.Test && env.Depth > 0 { if env.vmConfig.Test && env.Depth > 0 {
caller.ReturnGas(gas.Uint64()) env.Gasser.ReturnGas(caller, gas.Uint64())
return nil, nil return nil, nil
} }
@ -154,7 +186,7 @@ func (env *Environment) Call(caller ContractRef, addr common.Address, data []byt
// Take another's contract code and execute within our own context // Take another's contract code and execute within our own context
func (env *Environment) CallCode(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) { func (env *Environment) CallCode(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
if env.vmConfig.Test && env.Depth > 0 { if env.vmConfig.Test && env.Depth > 0 {
caller.ReturnGas(gas.Uint64()) env.Gasser.ReturnGas(caller, gas.Uint64())
return nil, nil return nil, nil
} }
@ -165,7 +197,7 @@ func (env *Environment) CallCode(caller ContractRef, addr common.Address, data [
// Same as CallCode except sender and value is propagated from parent to child scope // Same as CallCode except sender and value is propagated from parent to child scope
func (env *Environment) DelegateCall(caller ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) { func (env *Environment) DelegateCall(caller ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) {
if env.vmConfig.Test && env.Depth > 0 { if env.vmConfig.Test && env.Depth > 0 {
caller.ReturnGas(gas.Uint64()) env.Gasser.ReturnGas(caller, gas.Uint64())
return nil, nil return nil, nil
} }
@ -176,7 +208,7 @@ func (env *Environment) DelegateCall(caller ContractRef, addr common.Address, da
// Create a new contract // Create a new contract
func (env *Environment) Create(caller ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) { func (env *Environment) Create(caller ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) {
if env.vmConfig.Test && env.Depth > 0 { if env.vmConfig.Test && env.Depth > 0 {
caller.ReturnGas(gas.Uint64()) env.Gasser.ReturnGas(caller, gas.Uint64())
return nil, common.Address{}, nil return nil, common.Address{}, nil
} }

View file

@ -25,7 +25,7 @@ type jumpSeg struct {
} }
func (j jumpSeg) do(program *Program, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { func (j jumpSeg) do(program *Program, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
if !contract.UseGas(j.gas) { if !env.Gasser.UseGas(contract, j.gas) { //!contract.UseGas(j.gas) {
return nil, OutOfGasError return nil, OutOfGasError
} }
if j.err != nil { if j.err != nil {
@ -45,7 +45,7 @@ type pushSeg struct {
func (s pushSeg) do(program *Program, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { func (s pushSeg) do(program *Program, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
// Use the calculated gas. When insufficient gas is present, use all gas and return an // Use the calculated gas. When insufficient gas is present, use all gas and return an
// Out Of Gas error // Out Of Gas error
if !contract.UseGas(s.gas) { if !env.Gasser.UseGas(contract, s.gas) { //!contract.UseGas(s.gas) {
return nil, OutOfGasError return nil, OutOfGasError
} }

View file

@ -61,7 +61,13 @@ func (evm *EVM) Run(contract *Contract, input []byte) ([]byte, error) {
if contract.CodeAddr != nil { if contract.CodeAddr != nil {
if p, exist := PrecompiledContracts[*contract.CodeAddr]; exist { if p, exist := PrecompiledContracts[*contract.CodeAddr]; exist {
return RunPrecompiled(p, input, contract) gas := p.RequiredGas(len(input))
if evm.env.Gasser.UseGas(contract, gas.Uint64()) { //contract.UseGas(gas.Uint64()) {
return p.Run(input), nil
} else {
return nil, OutOfGasError
}
//return RunPrecompiled(p, input, contract)
} }
} }

View file

@ -1280,8 +1280,8 @@ func (api *PrivateDebugAPI) ChaindbProperty(property string) (string, error) {
} }
// SetHead rewinds the head of the blockchain to a previous block. // SetHead rewinds the head of the blockchain to a previous block.
func (api *PrivateDebugAPI) SetHead(number uint64) { func (api *PrivateDebugAPI) SetHead(number string) {
api.b.SetHead(number) api.b.SetHead(common.String2Big(number).Uint64())
} }
// PublicNetAPI offers network related RPC methods // PublicNetAPI offers network related RPC methods