core, core/vm, crypto: fixes for homestead
* Removed some strange code that didn't apply state reverting properly * Refactored code setting from vm & state transition to the executioner * Updated tests
This commit is contained in:
parent
4f4d2b6474
commit
b6d88a0e9f
@ -23,6 +23,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/compiler"
|
"github.com/ethereum/go-ethereum/common/compiler"
|
||||||
|
"github.com/ethereum/go-ethereum/common/registrar"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
@ -30,7 +31,6 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
"github.com/ethereum/go-ethereum/logger/glog"
|
||||||
"github.com/ethereum/go-ethereum/common/registrar"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// registryAPIBackend is a backend for an Ethereum Registry.
|
// registryAPIBackend is a backend for an Ethereum Registry.
|
||||||
@ -112,6 +112,9 @@ type callmsg struct {
|
|||||||
func (m callmsg) From() (common.Address, error) {
|
func (m callmsg) From() (common.Address, error) {
|
||||||
return m.from.Address(), nil
|
return m.from.Address(), nil
|
||||||
}
|
}
|
||||||
|
func (m callmsg) FromFrontier() (common.Address, error) {
|
||||||
|
return m.from.Address(), nil
|
||||||
|
}
|
||||||
func (m callmsg) Nonce() uint64 {
|
func (m callmsg) Nonce() uint64 {
|
||||||
return m.from.Nonce()
|
return m.from.Nonce()
|
||||||
}
|
}
|
||||||
|
@ -117,8 +117,7 @@ func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *stat
|
|||||||
// For valid blocks this should always validate to true.
|
// For valid blocks this should always validate to true.
|
||||||
rbloom := types.CreateBloom(receipts)
|
rbloom := types.CreateBloom(receipts)
|
||||||
if rbloom != header.Bloom {
|
if rbloom != header.Bloom {
|
||||||
//fmt.Printf("FUNKY: ValidateState: block number: %v\n", block.Number())
|
return fmt.Errorf("unable to replicate block's bloom=%x vs calculated bloom=%x", header.Bloom, rbloom)
|
||||||
return fmt.Errorf("unable to replicate block's bloom=%x", rbloom)
|
|
||||||
}
|
}
|
||||||
// Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, R1]]))
|
// Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, R1]]))
|
||||||
receiptSha := types.DeriveSha(receipts)
|
receiptSha := types.DeriveSha(receipts)
|
||||||
@ -270,10 +269,6 @@ func calcDifficultyHomestead(time, parentTime uint64, parentNumber, parentDiff *
|
|||||||
bigTime := new(big.Int).SetUint64(time)
|
bigTime := new(big.Int).SetUint64(time)
|
||||||
bigParentTime := new(big.Int).SetUint64(parentTime)
|
bigParentTime := new(big.Int).SetUint64(parentTime)
|
||||||
|
|
||||||
// for the exponential factor
|
|
||||||
periodCount := new(big.Int).Add(parentNumber, common.Big1)
|
|
||||||
periodCount.Div(periodCount, ExpDiffPeriod)
|
|
||||||
|
|
||||||
// holds intermediate values to make the algo easier to read & audit
|
// holds intermediate values to make the algo easier to read & audit
|
||||||
x := new(big.Int)
|
x := new(big.Int)
|
||||||
y := new(big.Int)
|
y := new(big.Int)
|
||||||
@ -298,6 +293,10 @@ func calcDifficultyHomestead(time, parentTime uint64, parentNumber, parentDiff *
|
|||||||
x = params.MinimumDifficulty
|
x = params.MinimumDifficulty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for the exponential factor
|
||||||
|
periodCount := new(big.Int).Add(parentNumber, common.Big1)
|
||||||
|
periodCount.Div(periodCount, ExpDiffPeriod)
|
||||||
|
|
||||||
// the exponential factor, commonly refered to as "the bomb"
|
// the exponential factor, commonly refered to as "the bomb"
|
||||||
// diff = diff + 2^(periodCount - 2)
|
// diff = diff + 2^(periodCount - 2)
|
||||||
if periodCount.Cmp(common.Big1) > 0 {
|
if periodCount.Cmp(common.Big1) > 0 {
|
||||||
|
@ -95,20 +95,6 @@ func GetHeadFastBlockHash(db ethdb.Database) common.Hash {
|
|||||||
return common.BytesToHash(data)
|
return common.BytesToHash(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetHeadBlockNum retrieves the block number of the current canonical head block.
|
|
||||||
func GetHeadBlockNum(db ethdb.Database) *big.Int {
|
|
||||||
data, _ := db.Get(headBlockKey)
|
|
||||||
if len(data) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
header := new(types.Header)
|
|
||||||
if err := rlp.Decode(bytes.NewReader(data), header); err != nil {
|
|
||||||
glog.V(logger.Error).Infof("invalid block header RLP for head block: %v", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return header.Number
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetHeaderRLP retrieves a block header in its raw RLP database encoding, or nil
|
// GetHeaderRLP retrieves a block header in its raw RLP database encoding, or nil
|
||||||
// if the header's not found.
|
// if the header's not found.
|
||||||
func GetHeaderRLP(db ethdb.Database, hash common.Hash) rlp.RawValue {
|
func GetHeaderRLP(db ethdb.Database, hash common.Hash) rlp.RawValue {
|
||||||
|
@ -62,7 +62,7 @@ func (d *diffTest) UnmarshalJSON(b []byte) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDifficulty(t *testing.T) {
|
func TestDifficultyFrontier(t *testing.T) {
|
||||||
file, err := os.Open("../tests/files/BasicTests/difficulty.json")
|
file, err := os.Open("../tests/files/BasicTests/difficulty.json")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -77,7 +77,7 @@ func TestDifficulty(t *testing.T) {
|
|||||||
|
|
||||||
for name, test := range tests {
|
for name, test := range tests {
|
||||||
number := new(big.Int).Sub(test.CurrentBlocknumber, big.NewInt(1))
|
number := new(big.Int).Sub(test.CurrentBlocknumber, big.NewInt(1))
|
||||||
diff := CalcDifficulty(test.CurrentTimestamp, test.ParentTimestamp, number, test.ParentDifficulty)
|
diff := calcDifficultyFrontier(test.CurrentTimestamp, test.ParentTimestamp, number, test.ParentDifficulty)
|
||||||
if diff.Cmp(test.CurrentDifficulty) != 0 {
|
if diff.Cmp(test.CurrentDifficulty) != 0 {
|
||||||
t.Error(name, "failed. Expected", test.CurrentDifficulty, "and calculated", diff)
|
t.Error(name, "failed. Expected", test.CurrentDifficulty, "and calculated", diff)
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,6 @@ func DelegateCall(env vm.Environment, caller vm.ContractRef, addr common.Address
|
|||||||
originAddr := env.Origin()
|
originAddr := env.Origin()
|
||||||
callerValue := caller.Value()
|
callerValue := caller.Value()
|
||||||
ret, _, err = execDelegateCall(env, caller, &originAddr, &callerAddr, &addr, input, env.Db().GetCode(addr), gas, gasPrice, callerValue)
|
ret, _, err = execDelegateCall(env, caller, &originAddr, &callerAddr, &addr, input, env.Db().GetCode(addr), gas, gasPrice, callerValue)
|
||||||
caller.SetAddress(callerAddr)
|
|
||||||
return ret, err
|
return ret, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,15 +77,15 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
|
|||||||
|
|
||||||
var createAccount bool
|
var createAccount bool
|
||||||
if address == nil {
|
if address == nil {
|
||||||
// Generate a new address
|
// Create a new account on the state
|
||||||
nonce := env.Db().GetNonce(caller.Address())
|
nonce := env.Db().GetNonce(caller.Address())
|
||||||
env.Db().SetNonce(caller.Address(), nonce+1)
|
env.Db().SetNonce(caller.Address(), nonce+1)
|
||||||
addr = crypto.CreateAddress(caller.Address(), nonce)
|
addr = crypto.CreateAddress(caller.Address(), nonce)
|
||||||
address = &addr
|
address = &addr
|
||||||
createAccount = true
|
createAccount = true
|
||||||
}
|
}
|
||||||
snapshotPreTransfer := env.MakeSnapshot()
|
|
||||||
|
|
||||||
|
snapshotPreTransfer := env.MakeSnapshot()
|
||||||
var (
|
var (
|
||||||
from = env.Db().GetAccount(caller.Address())
|
from = env.Db().GetAccount(caller.Address())
|
||||||
to vm.Account
|
to vm.Account
|
||||||
@ -101,24 +100,38 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
env.Transfer(from, to, value)
|
env.Transfer(from, to, value)
|
||||||
snapshotPostTransfer := env.MakeSnapshot()
|
|
||||||
|
|
||||||
|
// initialise a new contract and set the code that is to be used by the
|
||||||
|
// EVM. The contract is a scoped environment for this execution context
|
||||||
|
// only.
|
||||||
contract := vm.NewContract(caller, to, value, gas, gasPrice)
|
contract := vm.NewContract(caller, to, value, gas, gasPrice)
|
||||||
contract.SetCallCode(codeAddr, code)
|
contract.SetCallCode(codeAddr, code)
|
||||||
|
defer contract.Finalise()
|
||||||
|
|
||||||
ret, err = evm.Run(contract, input)
|
ret, err = evm.Run(contract, input)
|
||||||
|
// if the contract creation ran successfully and no errors were returned
|
||||||
if err != nil {
|
// calculate the gas required to store the code. If the code could not
|
||||||
if err == vm.CodeStoreOutOfGasError {
|
// be stored due to not enough gas set an error and let it be handled
|
||||||
// TODO: this is rather hacky, restore all state changes
|
// by the error checking condition below.
|
||||||
// except sender's account nonce increment
|
if err == nil && createAccount {
|
||||||
toNonce := env.Db().GetNonce(to.Address())
|
dataGas := big.NewInt(int64(len(ret)))
|
||||||
env.SetSnapshot(snapshotPostTransfer)
|
dataGas.Mul(dataGas, params.CreateDataGas)
|
||||||
env.Db().SetNonce(to.Address(), toNonce)
|
if contract.UseGas(dataGas) {
|
||||||
|
env.Db().SetCode(*address, ret)
|
||||||
} else {
|
} else {
|
||||||
env.SetSnapshot(snapshotPreTransfer) //env.Db().Set(snapshot)
|
err = vm.CodeStoreOutOfGasError
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When an error was returned by the EVM or when setting the creation code
|
||||||
|
// 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.
|
||||||
|
if err != nil && (params.IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError) {
|
||||||
|
contract.UseGas(contract.Gas)
|
||||||
|
|
||||||
|
env.SetSnapshot(snapshotPreTransfer)
|
||||||
|
}
|
||||||
|
|
||||||
return ret, addr, err
|
return ret, addr, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,32 +144,27 @@ func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toA
|
|||||||
return nil, common.Address{}, vm.DepthError
|
return nil, common.Address{}, vm.DepthError
|
||||||
}
|
}
|
||||||
|
|
||||||
if !env.CanTransfer(*originAddr, value) {
|
|
||||||
caller.ReturnGas(gas, gasPrice)
|
|
||||||
return nil, common.Address{}, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address()))
|
|
||||||
}
|
|
||||||
|
|
||||||
snapshot := env.MakeSnapshot()
|
snapshot := env.MakeSnapshot()
|
||||||
|
|
||||||
var (
|
var to vm.Account
|
||||||
//from = env.Db().GetAccount(*originAddr)
|
|
||||||
to vm.Account
|
|
||||||
)
|
|
||||||
if !env.Db().Exist(*toAddr) {
|
if !env.Db().Exist(*toAddr) {
|
||||||
to = env.Db().CreateAccount(*toAddr)
|
to = env.Db().CreateAccount(*toAddr)
|
||||||
} else {
|
} else {
|
||||||
to = env.Db().GetAccount(*toAddr)
|
to = env.Db().GetAccount(*toAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
contract := vm.NewContract(caller, to, value, gas, gasPrice)
|
// Iinitialise a new contract and make initialise the delegate values
|
||||||
|
contract := vm.NewContract(caller, to, value, gas, gasPrice).AsDelegate()
|
||||||
contract.SetCallCode(codeAddr, code)
|
contract.SetCallCode(codeAddr, code)
|
||||||
contract.DelegateCall = true
|
defer contract.Finalise()
|
||||||
|
|
||||||
ret, err = evm.Run(contract, input)
|
ret, err = evm.Run(contract, input)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
env.SetSnapshot(snapshot) //env.Db().Set(snapshot)
|
contract.UseGas(contract.Gas)
|
||||||
|
|
||||||
|
env.SetSnapshot(snapshot)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret, addr, err
|
return ret, addr, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ type Account struct {
|
|||||||
Nonce uint64 `json:"nonce"`
|
Nonce uint64 `json:"nonce"`
|
||||||
Root string `json:"root"`
|
Root string `json:"root"`
|
||||||
CodeHash string `json:"codeHash"`
|
CodeHash string `json:"codeHash"`
|
||||||
|
Code string `json:"code"`
|
||||||
Storage map[string]string `json:"storage"`
|
Storage map[string]string `json:"storage"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +48,7 @@ func (self *StateDB) RawDump() World {
|
|||||||
addr := self.trie.GetKey(it.Key)
|
addr := self.trie.GetKey(it.Key)
|
||||||
stateObject, _ := DecodeObject(common.BytesToAddress(addr), self.db, it.Value)
|
stateObject, _ := DecodeObject(common.BytesToAddress(addr), self.db, it.Value)
|
||||||
|
|
||||||
account := Account{Balance: stateObject.balance.String(), Nonce: stateObject.nonce, Root: common.Bytes2Hex(stateObject.Root()), CodeHash: common.Bytes2Hex(stateObject.codeHash)}
|
account := Account{Balance: stateObject.balance.String(), Nonce: stateObject.nonce, Root: common.Bytes2Hex(stateObject.Root()), CodeHash: common.Bytes2Hex(stateObject.codeHash), Code: common.Bytes2Hex(stateObject.Code())}
|
||||||
account.Storage = make(map[string]string)
|
account.Storage = make(map[string]string)
|
||||||
|
|
||||||
storageIt := stateObject.trie.Iterator()
|
storageIt := stateObject.trie.Iterator()
|
||||||
|
@ -211,11 +211,6 @@ func (c *StateObject) Address() common.Address {
|
|||||||
return c.address
|
return c.address
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets the address of the contract/account
|
|
||||||
func (c *StateObject) SetAddress(addr common.Address) {
|
|
||||||
c.address = addr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *StateObject) Trie() *trie.SecureTrie {
|
func (self *StateObject) Trie() *trie.SecureTrie {
|
||||||
return self.trie
|
return self.trie
|
||||||
}
|
}
|
||||||
@ -247,7 +242,7 @@ func (self *StateObject) Nonce() uint64 {
|
|||||||
// as a vm.Account interface that also satisfies the vm.ContractRef
|
// as a vm.Account interface that also satisfies the vm.ContractRef
|
||||||
// interface. Interfaces are awesome.
|
// interface. Interfaces are awesome.
|
||||||
func (self *StateObject) Value() *big.Int {
|
func (self *StateObject) Value() *big.Int {
|
||||||
return nil
|
panic("Value on StateObject should never be called")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateObject) EachStorage(cb func(key, value []byte)) {
|
func (self *StateObject) EachStorage(cb func(key, value []byte)) {
|
||||||
|
@ -87,18 +87,6 @@ func (self *StateDB) GetLogs(hash common.Hash) vm.Logs {
|
|||||||
return self.logs[hash]
|
return self.logs[hash]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) GetAllLogs() *map[common.Hash]vm.Logs {
|
|
||||||
copy := make(map[common.Hash]vm.Logs, len(self.logs))
|
|
||||||
for k, v := range self.logs {
|
|
||||||
copy[k] = v
|
|
||||||
}
|
|
||||||
return ©
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *StateDB) SetAllLogs(logs *map[common.Hash]vm.Logs) {
|
|
||||||
self.logs = *logs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *StateDB) Logs() vm.Logs {
|
func (self *StateDB) Logs() vm.Logs {
|
||||||
var logs vm.Logs
|
var logs vm.Logs
|
||||||
for _, lgs := range self.logs {
|
for _, lgs := range self.logs {
|
||||||
@ -107,11 +95,6 @@ func (self *StateDB) Logs() vm.Logs {
|
|||||||
return logs
|
return logs
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: this may not be the most proper thing
|
|
||||||
func (self *StateDB) GetDB() ethdb.Database {
|
|
||||||
return self.db
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *StateDB) AddRefund(gas *big.Int) {
|
func (self *StateDB) AddRefund(gas *big.Int) {
|
||||||
self.refund.Add(self.refund, gas)
|
self.refund.Add(self.refund, gas)
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,6 @@ import (
|
|||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"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/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
"github.com/ethereum/go-ethereum/logger/glog"
|
||||||
@ -225,38 +224,24 @@ func (self *StateTransition) transitionDb() (ret []byte, usedGas *big.Int, err e
|
|||||||
}
|
}
|
||||||
|
|
||||||
vmenv := self.env
|
vmenv := self.env
|
||||||
snapshot := vmenv.MakeSnapshot()
|
//var addr common.Address
|
||||||
var addr common.Address
|
|
||||||
if contractCreation {
|
if contractCreation {
|
||||||
ret, addr, err = vmenv.Create(sender, self.data, self.gas, self.gasPrice, self.value)
|
ret, _, err = vmenv.Create(sender, self.data, self.gas, self.gasPrice, self.value)
|
||||||
if err == nil {
|
if homestead && err == vm.CodeStoreOutOfGasError {
|
||||||
dataGas := big.NewInt(int64(len(ret)))
|
self.gas = Big0
|
||||||
dataGas.Mul(dataGas, params.CreateDataGas)
|
}
|
||||||
if err := self.useGas(dataGas); err == nil {
|
|
||||||
self.state.SetCode(addr, ret)
|
if err != nil {
|
||||||
} else {
|
ret = nil
|
||||||
if homestead {
|
glog.V(logger.Core).Infoln("VM create err:", err)
|
||||||
// rollback all contract creation changes except for
|
|
||||||
// sender's incremented account nonce and gas usage
|
|
||||||
// TODO: fucking gas hack... verify potential DoS vuln
|
|
||||||
accNonce := vmenv.Db().GetNonce(sender.Address())
|
|
||||||
logs := vmenv.Db().(*state.StateDB).GetAllLogs()
|
|
||||||
vmenv.SetSnapshot(snapshot)
|
|
||||||
vmenv.Db().SetNonce(sender.Address(), accNonce)
|
|
||||||
vmenv.Db().(*state.StateDB).SetAllLogs(logs)
|
|
||||||
self.gas = Big0
|
|
||||||
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
glog.V(logger.Core).Infoln("VM create err:", err)
|
|
||||||
} else {
|
} else {
|
||||||
// Increment the nonce for the next transaction
|
// Increment the nonce for the next transaction
|
||||||
self.state.SetNonce(sender.Address(), self.state.GetNonce(sender.Address())+1)
|
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)
|
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)
|
if err != nil {
|
||||||
|
glog.V(logger.Core).Infoln("VM call err:", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil && IsValueTransferErr(err) {
|
if err != nil && IsValueTransferErr(err) {
|
||||||
|
@ -71,6 +71,8 @@ type TxPool struct {
|
|||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
pending map[common.Hash]*types.Transaction // processable transactions
|
pending map[common.Hash]*types.Transaction // processable transactions
|
||||||
queue map[common.Address]map[common.Hash]*types.Transaction
|
queue map[common.Address]map[common.Hash]*types.Transaction
|
||||||
|
|
||||||
|
homestead bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTxPool(eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func() *big.Int) *TxPool {
|
func NewTxPool(eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func() *big.Int) *TxPool {
|
||||||
@ -86,6 +88,7 @@ func NewTxPool(eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func(
|
|||||||
localTx: newTxSet(),
|
localTx: newTxSet(),
|
||||||
events: eventMux.Subscribe(ChainHeadEvent{}, GasPriceChanged{}, RemovedTransactionEvent{}),
|
events: eventMux.Subscribe(ChainHeadEvent{}, GasPriceChanged{}, RemovedTransactionEvent{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
go pool.eventLoop()
|
go pool.eventLoop()
|
||||||
|
|
||||||
return pool
|
return pool
|
||||||
@ -99,6 +102,10 @@ func (pool *TxPool) eventLoop() {
|
|||||||
switch ev := ev.Data.(type) {
|
switch ev := ev.Data.(type) {
|
||||||
case ChainHeadEvent:
|
case ChainHeadEvent:
|
||||||
pool.mu.Lock()
|
pool.mu.Lock()
|
||||||
|
if ev.Block != nil && params.IsHomestead(ev.Block.Number()) {
|
||||||
|
pool.homestead = true
|
||||||
|
}
|
||||||
|
|
||||||
pool.resetState()
|
pool.resetState()
|
||||||
pool.mu.Unlock()
|
pool.mu.Unlock()
|
||||||
case GasPriceChanged:
|
case GasPriceChanged:
|
||||||
@ -211,12 +218,6 @@ func (pool *TxPool) SetLocal(tx *types.Transaction) {
|
|||||||
// validateTx checks whether a transaction is valid according
|
// validateTx checks whether a transaction is valid according
|
||||||
// to the consensus rules.
|
// to the consensus rules.
|
||||||
func (pool *TxPool) validateTx(tx *types.Transaction) error {
|
func (pool *TxPool) validateTx(tx *types.Transaction) error {
|
||||||
// Validate sender
|
|
||||||
var (
|
|
||||||
from common.Address
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
local := pool.localTx.contains(tx.Hash())
|
local := pool.localTx.contains(tx.Hash())
|
||||||
// Drop transactions under our own minimal accepted gas price
|
// Drop transactions under our own minimal accepted gas price
|
||||||
if !local && pool.minGasPrice.Cmp(tx.GasPrice()) > 0 {
|
if !local && pool.minGasPrice.Cmp(tx.GasPrice()) > 0 {
|
||||||
@ -228,15 +229,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
homestead := params.IsHomestead(GetHeadBlockNum(currentState.GetDB()))
|
from, err := tx.From()
|
||||||
|
|
||||||
// Validate the transaction sender and it's sig. Throw
|
|
||||||
// if the from fields is invalid.
|
|
||||||
if homestead {
|
|
||||||
from, err = tx.From()
|
|
||||||
} else {
|
|
||||||
from, err = tx.FromFrontier()
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ErrInvalidSender
|
return ErrInvalidSender
|
||||||
}
|
}
|
||||||
@ -271,8 +264,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error {
|
|||||||
return ErrInsufficientFunds
|
return ErrInsufficientFunds
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should supply enough intrinsic gas
|
intrGas := IntrinsicGas(tx.Data(), MessageCreatesContract(tx), pool.homestead)
|
||||||
intrGas := IntrinsicGas(tx.Data(), MessageCreatesContract(tx), homestead)
|
|
||||||
if tx.Gas().Cmp(intrGas) < 0 {
|
if tx.Gas().Cmp(intrGas) < 0 {
|
||||||
return ErrIntrinsicGas
|
return ErrIntrinsicGas
|
||||||
}
|
}
|
@ -157,7 +157,14 @@ func (tx *Transaction) Size() common.StorageSize {
|
|||||||
return common.StorageSize(c)
|
return common.StorageSize(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// From() caches the address, allowing it to be used regardless of
|
// From returns the address derived from the signature (V, R, S) using secp256k1
|
||||||
|
// eliptic curve and an error if it failed deriving or upon an incorrect
|
||||||
|
// signature.
|
||||||
|
//
|
||||||
|
// From Uses the homestead consensus rules to determine whether the signature is
|
||||||
|
// valid.
|
||||||
|
//
|
||||||
|
// From caches the address, allowing it to be used regardless of
|
||||||
// Frontier / Homestead. however, the first time called it runs
|
// Frontier / Homestead. however, the first time called it runs
|
||||||
// signature validations, so we need two versions. This makes it
|
// signature validations, so we need two versions. This makes it
|
||||||
// easier to ensure backwards compatibility of things like package rpc
|
// easier to ensure backwards compatibility of things like package rpc
|
||||||
@ -168,6 +175,20 @@ func (tx *Transaction) From() (common.Address, error) {
|
|||||||
return doFrom(tx, true)
|
return doFrom(tx, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FromFrontier returns the address derived from the signature (V, R, S) using
|
||||||
|
// secp256k1 eliptic curve and an error if it failed deriving or upon an
|
||||||
|
// incorrect signature.
|
||||||
|
//
|
||||||
|
// FromFrantier uses the frontier consensus rules to determine whether the
|
||||||
|
// signature is valid.
|
||||||
|
//
|
||||||
|
// FromFrontier caches the address, allowing it to be used regardless of
|
||||||
|
// Frontier / Homestead. however, the first time called it runs
|
||||||
|
// signature validations, so we need two versions. This makes it
|
||||||
|
// easier to ensure backwards compatibility of things like package rpc
|
||||||
|
// where eth_getblockbynumber uses tx.From() and needs to work for
|
||||||
|
// both txs before and after the first homestead block. Signatures
|
||||||
|
// valid in homestead are a subset of valid ones in Frontier)
|
||||||
func (tx *Transaction) FromFrontier() (common.Address, error) {
|
func (tx *Transaction) FromFrontier() (common.Address, error) {
|
||||||
return doFrom(tx, false)
|
return doFrom(tx, false)
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,6 @@ import (
|
|||||||
type ContractRef interface {
|
type ContractRef interface {
|
||||||
ReturnGas(*big.Int, *big.Int)
|
ReturnGas(*big.Int, *big.Int)
|
||||||
Address() common.Address
|
Address() common.Address
|
||||||
SetAddress(common.Address)
|
|
||||||
Value() *big.Int
|
Value() *big.Int
|
||||||
SetCode([]byte)
|
SetCode([]byte)
|
||||||
EachStorage(cb func(key, value []byte))
|
EachStorage(cb func(key, value []byte))
|
||||||
@ -35,8 +34,12 @@ type ContractRef interface {
|
|||||||
// Contract represents an ethereum contract in the state database. It contains
|
// Contract represents an ethereum contract in the state database. It contains
|
||||||
// the the contract code, calling arguments. Contract implements ContractRef
|
// the the contract code, calling arguments. Contract implements ContractRef
|
||||||
type Contract struct {
|
type Contract struct {
|
||||||
caller ContractRef
|
// CallerAddress is the result of the caller which initialised this
|
||||||
self ContractRef
|
// contract. However when the "call method" is delegated this value
|
||||||
|
// needs to be initialised to that of the caller's caller.
|
||||||
|
CallerAddress common.Address
|
||||||
|
caller ContractRef
|
||||||
|
self ContractRef
|
||||||
|
|
||||||
jumpdests destinations // result of JUMPDEST analysis.
|
jumpdests destinations // result of JUMPDEST analysis.
|
||||||
|
|
||||||
@ -51,9 +54,9 @@ type Contract struct {
|
|||||||
DelegateCall bool
|
DelegateCall bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new context for the given data items.
|
// NewContract returns a new contract environment for the execution of EVM.
|
||||||
func NewContract(caller ContractRef, object ContractRef, value, gas, price *big.Int) *Contract {
|
func NewContract(caller ContractRef, object ContractRef, value, gas, price *big.Int) *Contract {
|
||||||
c := &Contract{caller: caller, self: object, Args: nil}
|
c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object, Args: nil}
|
||||||
|
|
||||||
if parent, ok := caller.(*Contract); ok {
|
if parent, ok := caller.(*Contract); ok {
|
||||||
// Reuse JUMPDEST analysis from parent context if available.
|
// Reuse JUMPDEST analysis from parent context if available.
|
||||||
@ -74,6 +77,16 @@ func NewContract(caller ContractRef, object ContractRef, value, gas, price *big.
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AsDelegate sets the contract to be a delegate call and returns the current
|
||||||
|
// contract (for chaining calls)
|
||||||
|
func (c *Contract) AsDelegate() *Contract {
|
||||||
|
c.DelegateCall = true
|
||||||
|
// NOTE: caller must, at all times be a contract. It should never happen
|
||||||
|
// that caller is something other than a Contract.
|
||||||
|
c.CallerAddress = c.caller.(*Contract).CallerAddress
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
// GetOp returns the n'th element in the contract's byte array
|
// GetOp returns the n'th element in the contract's byte array
|
||||||
func (c *Contract) GetOp(n uint64) OpCode {
|
func (c *Contract) GetOp(n uint64) OpCode {
|
||||||
return OpCode(c.GetByte(n))
|
return OpCode(c.GetByte(n))
|
||||||
@ -88,13 +101,19 @@ func (c *Contract) GetByte(n uint64) byte {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return returns the given ret argument and returns any remaining gas to the
|
// Caller returns the caller of the contract.
|
||||||
// caller
|
//
|
||||||
func (c *Contract) Return(ret []byte) []byte {
|
// Caller will recursively call caller when the contract is a delegate
|
||||||
|
// call, including that of caller's caller.
|
||||||
|
func (c *Contract) Caller() common.Address {
|
||||||
|
return c.CallerAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finalise finalises the contract and returning any remaining gas to the original
|
||||||
|
// caller.
|
||||||
|
func (c *Contract) Finalise() {
|
||||||
// Return the remaining gas to the caller
|
// Return the remaining gas to the caller
|
||||||
c.caller.ReturnGas(c.Gas, c.Price)
|
c.caller.ReturnGas(c.Gas, c.Price)
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
@ -118,11 +137,6 @@ func (c *Contract) Address() common.Address {
|
|||||||
return c.self.Address()
|
return c.self.Address()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetAddress sets the contracts address
|
|
||||||
func (c *Contract) SetAddress(addr common.Address) {
|
|
||||||
c.self.SetAddress(addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value returns the contracts value (sent to it from it's caller)
|
// Value returns the contracts value (sent to it from it's caller)
|
||||||
func (c *Contract) Value() *big.Int {
|
func (c *Contract) Value() *big.Int {
|
||||||
return c.value
|
return c.value
|
||||||
|
@ -121,7 +121,6 @@ type Account interface {
|
|||||||
SetNonce(uint64)
|
SetNonce(uint64)
|
||||||
Balance() *big.Int
|
Balance() *big.Int
|
||||||
Address() common.Address
|
Address() common.Address
|
||||||
SetAddress(common.Address)
|
|
||||||
ReturnGas(*big.Int, *big.Int)
|
ReturnGas(*big.Int, *big.Int)
|
||||||
SetCode([]byte)
|
SetCode([]byte)
|
||||||
EachStorage(cb func(key, value []byte))
|
EachStorage(cb func(key, value []byte))
|
||||||
|
@ -337,13 +337,7 @@ func opOrigin(instr instruction, pc *uint64, env Environment, contract *Contract
|
|||||||
}
|
}
|
||||||
|
|
||||||
func opCaller(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
|
func opCaller(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
|
||||||
var bigAddr *big.Int
|
stack.push(contract.Caller().Big())
|
||||||
if contract.DelegateCall {
|
|
||||||
bigAddr = env.Origin().Big()
|
|
||||||
} else {
|
|
||||||
bigAddr = contract.caller.Address().Big()
|
|
||||||
}
|
|
||||||
stack.push(bigAddr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func opCallValue(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
|
func opCallValue(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
|
||||||
@ -514,6 +508,25 @@ func opGas(instr instruction, pc *uint64, env Environment, contract *Contract, m
|
|||||||
}
|
}
|
||||||
|
|
||||||
func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract, 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(contract.Gas)
|
||||||
|
)
|
||||||
|
contract.UseGas(contract.Gas)
|
||||||
|
_, addr, suberr := env.Create(contract, input, gas, contract.Price, value)
|
||||||
|
// Push item on the stack based on the returned error. If the ruleset is
|
||||||
|
// homestead we must check for CodeStoreOutOfGasError (homestead only
|
||||||
|
// rule) and treat as an error, if the ruleset is frontier we must
|
||||||
|
// ignore this error and pretend the operation was successful.
|
||||||
|
if params.IsHomestead(env.BlockNumber()) && suberr == CodeStoreOutOfGasError {
|
||||||
|
stack.push(new(big.Int))
|
||||||
|
} else if suberr != nil && suberr != CodeStoreOutOfGasError {
|
||||||
|
stack.push(new(big.Int))
|
||||||
|
} else {
|
||||||
|
stack.push(addr.Big())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
|
@ -275,6 +275,11 @@ func CompileProgram(program *Program) (err error) {
|
|||||||
program.addInstr(op, pc, opGas, nil)
|
program.addInstr(op, pc, opGas, nil)
|
||||||
case CREATE:
|
case CREATE:
|
||||||
program.addInstr(op, pc, opCreate, nil)
|
program.addInstr(op, pc, opCreate, nil)
|
||||||
|
case DELEGATECALL:
|
||||||
|
// Instruction added regardless of homestead phase.
|
||||||
|
// Homestead (and execution of the opcode) is checked during
|
||||||
|
// runtime.
|
||||||
|
program.addInstr(op, pc, opDelegateCall, nil)
|
||||||
case CALL:
|
case CALL:
|
||||||
program.addInstr(op, pc, opCall, nil)
|
program.addInstr(op, pc, opCall, nil)
|
||||||
case CALLCODE:
|
case CALLCODE:
|
||||||
@ -317,10 +322,14 @@ func runProgram(program *Program, pcstart uint64, mem *Memory, stack *stack, env
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
homestead := params.IsHomestead(env.BlockNumber())
|
||||||
for pc < uint64(len(program.instructions)) {
|
for pc < uint64(len(program.instructions)) {
|
||||||
instrCount++
|
instrCount++
|
||||||
|
|
||||||
instr := program.instructions[pc]
|
instr := program.instructions[pc]
|
||||||
|
if instr.Op() == DELEGATECALL && !homestead {
|
||||||
|
return nil, fmt.Errorf("Invalid opcode 0x%x", instr.Op())
|
||||||
|
}
|
||||||
|
|
||||||
ret, err := instr.do(program, &pc, env, contract, mem, stack)
|
ret, err := instr.do(program, &pc, env, contract, mem, stack)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -328,13 +337,13 @@ func runProgram(program *Program, pcstart uint64, mem *Memory, stack *stack, env
|
|||||||
}
|
}
|
||||||
|
|
||||||
if instr.halts() {
|
if instr.halts() {
|
||||||
return contract.Return(ret), nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
contract.Input = nil
|
contract.Input = nil
|
||||||
|
|
||||||
return contract.Return(nil), nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// validDest checks if the given distination is a valid one given the
|
// validDest checks if the given distination is a valid one given the
|
||||||
@ -457,7 +466,6 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi
|
|||||||
gas.Add(gas, stack.data[stack.len()-1])
|
gas.Add(gas, stack.data[stack.len()-1])
|
||||||
|
|
||||||
if op == CALL {
|
if op == CALL {
|
||||||
//if env.Db().GetStateObject(common.BigToAddress(stack.data[stack.len()-2])) == nil {
|
|
||||||
if !env.Db().Exist(common.BigToAddress(stack.data[stack.len()-2])) {
|
if !env.Db().Exist(common.BigToAddress(stack.data[stack.len()-2])) {
|
||||||
gas.Add(gas, params.CallNewAccountGas)
|
gas.Add(gas, params.CallNewAccountGas)
|
||||||
}
|
}
|
||||||
@ -470,6 +478,13 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi
|
|||||||
x := calcMemSize(stack.data[stack.len()-6], stack.data[stack.len()-7])
|
x := calcMemSize(stack.data[stack.len()-6], stack.data[stack.len()-7])
|
||||||
y := calcMemSize(stack.data[stack.len()-4], stack.data[stack.len()-5])
|
y := calcMemSize(stack.data[stack.len()-4], stack.data[stack.len()-5])
|
||||||
|
|
||||||
|
newMemSize = common.BigMax(x, y)
|
||||||
|
case DELEGATECALL:
|
||||||
|
gas.Add(gas, stack.data[stack.len()-1])
|
||||||
|
|
||||||
|
x := calcMemSize(stack.data[stack.len()-5], stack.data[stack.len()-6])
|
||||||
|
y := calcMemSize(stack.data[stack.len()-3], stack.data[stack.len()-4])
|
||||||
|
|
||||||
newMemSize = common.BigMax(x, y)
|
newMemSize = common.BigMax(x, y)
|
||||||
}
|
}
|
||||||
quadMemGas(mem, newMemSize, gas)
|
quadMemGas(mem, newMemSize, gas)
|
||||||
|
@ -127,6 +127,8 @@ type account struct{}
|
|||||||
|
|
||||||
func (account) SubBalance(amount *big.Int) {}
|
func (account) SubBalance(amount *big.Int) {}
|
||||||
func (account) AddBalance(amount *big.Int) {}
|
func (account) AddBalance(amount *big.Int) {}
|
||||||
|
func (account) SetAddress(common.Address) {}
|
||||||
|
func (account) Value() *big.Int { return nil }
|
||||||
func (account) SetBalance(*big.Int) {}
|
func (account) SetBalance(*big.Int) {}
|
||||||
func (account) SetNonce(uint64) {}
|
func (account) SetNonce(uint64) {}
|
||||||
func (account) Balance() *big.Int { return nil }
|
func (account) Balance() *big.Int { return nil }
|
||||||
@ -206,3 +208,6 @@ func (self *Env) CallCode(caller ContractRef, addr common.Address, data []byte,
|
|||||||
func (self *Env) Create(caller ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
|
func (self *Env) Create(caller ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
|
||||||
return nil, common.Address{}, nil
|
return nil, common.Address{}, nil
|
||||||
}
|
}
|
||||||
|
func (self *Env) DelegateCall(me ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
@ -1,13 +1,29 @@
|
|||||||
package vm
|
package vm
|
||||||
|
|
||||||
import "math/big"
|
import (
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
)
|
||||||
|
|
||||||
type jumpPtr struct {
|
type jumpPtr struct {
|
||||||
fn instrFn
|
fn instrFn
|
||||||
valid bool
|
valid bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var jumpTable [256]jumpPtr
|
type vmJumpTable [256]jumpPtr
|
||||||
|
|
||||||
|
func (jt vmJumpTable) init(blockNumber *big.Int) {
|
||||||
|
// when initialising a new VM execution we must first check the homestead
|
||||||
|
// changes.
|
||||||
|
if params.IsHomestead(blockNumber) {
|
||||||
|
jumpTable[DELEGATECALL] = jumpPtr{opDelegateCall, true}
|
||||||
|
} else {
|
||||||
|
jumpTable[DELEGATECALL] = jumpPtr{nil, false}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var jumpTable vmJumpTable
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
jumpTable[ADD] = jumpPtr{opAdd, true}
|
jumpTable[ADD] = jumpPtr{opAdd, true}
|
||||||
@ -62,10 +78,9 @@ func init() {
|
|||||||
jumpTable[PC] = jumpPtr{nil, true}
|
jumpTable[PC] = jumpPtr{nil, true}
|
||||||
jumpTable[MSIZE] = jumpPtr{opMsize, true}
|
jumpTable[MSIZE] = jumpPtr{opMsize, true}
|
||||||
jumpTable[GAS] = jumpPtr{opGas, true}
|
jumpTable[GAS] = jumpPtr{opGas, true}
|
||||||
jumpTable[CREATE] = jumpPtr{nil, true}
|
jumpTable[CREATE] = jumpPtr{opCreate, true}
|
||||||
jumpTable[CALL] = jumpPtr{opCall, true}
|
jumpTable[CALL] = jumpPtr{opCall, true}
|
||||||
jumpTable[CALLCODE] = jumpPtr{opCallCode, true}
|
jumpTable[CALLCODE] = jumpPtr{opCallCode, true}
|
||||||
jumpTable[DELEGATECALL] = jumpPtr{opDelegateCall, true}
|
|
||||||
jumpTable[LOG0] = jumpPtr{makeLog(0), true}
|
jumpTable[LOG0] = jumpPtr{makeLog(0), true}
|
||||||
jumpTable[LOG1] = jumpPtr{makeLog(1), true}
|
jumpTable[LOG1] = jumpPtr{makeLog(1), true}
|
||||||
jumpTable[LOG2] = jumpPtr{makeLog(2), true}
|
jumpTable[LOG2] = jumpPtr{makeLog(2), true}
|
||||||
|
24
core/vm/jump_table_test.go
Normal file
24
core/vm/jump_table_test.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package vm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestInit(t *testing.T) {
|
||||||
|
params.HomesteadBlock = big.NewInt(1)
|
||||||
|
|
||||||
|
jumpTable.init(big.NewInt(0))
|
||||||
|
if jumpTable[DELEGATECALL].valid {
|
||||||
|
t.Error("Expected DELEGATECALL not to be present")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, n := range []int64{1, 2, 100} {
|
||||||
|
jumpTable.init(big.NewInt(n))
|
||||||
|
if !jumpTable[DELEGATECALL].valid {
|
||||||
|
t.Error("Expected DELEGATECALL to be present for block", n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -404,6 +404,7 @@ var stringToOp = map[string]OpCode{
|
|||||||
"CALLDATALOAD": CALLDATALOAD,
|
"CALLDATALOAD": CALLDATALOAD,
|
||||||
"CALLDATASIZE": CALLDATASIZE,
|
"CALLDATASIZE": CALLDATASIZE,
|
||||||
"CALLDATACOPY": CALLDATACOPY,
|
"CALLDATACOPY": CALLDATACOPY,
|
||||||
|
"DELEGATECALL": DELEGATECALL,
|
||||||
"CODESIZE": CODESIZE,
|
"CODESIZE": CODESIZE,
|
||||||
"CODECOPY": CODECOPY,
|
"CODECOPY": CODECOPY,
|
||||||
"GASPRICE": GASPRICE,
|
"GASPRICE": GASPRICE,
|
||||||
|
@ -35,6 +35,9 @@ type Vm struct {
|
|||||||
|
|
||||||
// New returns a new Vm
|
// New returns a new Vm
|
||||||
func New(env Environment) *Vm {
|
func New(env Environment) *Vm {
|
||||||
|
// init the jump table. Also prepares the homestead changes
|
||||||
|
jumpTable.init(env.BlockNumber())
|
||||||
|
|
||||||
return &Vm{env: env}
|
return &Vm{env: env}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,16 +46,6 @@ func (self *Vm) Run(contract *Contract, input []byte) (ret []byte, err error) {
|
|||||||
self.env.SetDepth(self.env.Depth() + 1)
|
self.env.SetDepth(self.env.Depth() + 1)
|
||||||
defer 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 contract.CodeAddr != nil {
|
||||||
if p := Precompiled[contract.CodeAddr.Str()]; p != nil {
|
if p := Precompiled[contract.CodeAddr.Str()]; p != nil {
|
||||||
return self.RunPrecompiled(p, input, contract)
|
return self.RunPrecompiled(p, input, contract)
|
||||||
@ -61,7 +54,7 @@ func (self *Vm) Run(contract *Contract, input []byte) (ret []byte, err error) {
|
|||||||
|
|
||||||
// Don't bother with the execution if there's no code.
|
// Don't bother with the execution if there's no code.
|
||||||
if len(contract.Code) == 0 {
|
if len(contract.Code) == 0 {
|
||||||
return contract.Return(nil), nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -199,46 +192,17 @@ func (self *Vm) Run(contract *Contract, input []byte) (ret []byte, err error) {
|
|||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
case CREATE:
|
|
||||||
var (
|
|
||||||
value = stack.pop()
|
|
||||||
offset, size = stack.pop(), stack.pop()
|
|
||||||
input = mem.Get(offset.Int64(), size.Int64())
|
|
||||||
gas = new(big.Int).Set(contract.Gas)
|
|
||||||
addr common.Address
|
|
||||||
ret []byte
|
|
||||||
suberr error
|
|
||||||
)
|
|
||||||
contract.UseGas(contract.Gas)
|
|
||||||
ret, addr, suberr = self.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 contract.UseGas(dataGas) {
|
|
||||||
self.env.Db().SetCode(addr, ret)
|
|
||||||
} else {
|
|
||||||
if params.IsHomestead(self.env.BlockNumber()) {
|
|
||||||
stack.push(new(big.Int))
|
|
||||||
return nil, CodeStoreOutOfGasError
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stack.push(addr.Big())
|
|
||||||
}
|
|
||||||
|
|
||||||
case RETURN:
|
case RETURN:
|
||||||
offset, size := stack.pop(), stack.pop()
|
offset, size := stack.pop(), stack.pop()
|
||||||
ret := mem.GetPtr(offset.Int64(), size.Int64())
|
ret := mem.GetPtr(offset.Int64(), size.Int64())
|
||||||
|
|
||||||
return contract.Return(ret), nil
|
return ret, nil
|
||||||
case SUICIDE:
|
case SUICIDE:
|
||||||
opSuicide(instruction{}, nil, self.env, contract, mem, stack)
|
opSuicide(instruction{}, nil, self.env, contract, mem, stack)
|
||||||
|
|
||||||
fallthrough
|
fallthrough
|
||||||
case STOP: // Stop the contract
|
case STOP: // Stop the contract
|
||||||
return contract.Return(nil), nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -359,7 +323,6 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef
|
|||||||
gas.Add(gas, stack.data[stack.len()-1])
|
gas.Add(gas, stack.data[stack.len()-1])
|
||||||
|
|
||||||
if op == CALL {
|
if op == CALL {
|
||||||
//if env.Db().GetStateObject(common.BigToAddress(stack.data[stack.len()-2])) == nil {
|
|
||||||
if !env.Db().Exist(common.BigToAddress(stack.data[stack.len()-2])) {
|
if !env.Db().Exist(common.BigToAddress(stack.data[stack.len()-2])) {
|
||||||
gas.Add(gas, params.CallNewAccountGas)
|
gas.Add(gas, params.CallNewAccountGas)
|
||||||
}
|
}
|
||||||
@ -392,7 +355,7 @@ func (self *Vm) RunPrecompiled(p *PrecompiledAccount, input []byte, contract *Co
|
|||||||
if contract.UseGas(gas) {
|
if contract.UseGas(gas) {
|
||||||
ret = p.Call(input)
|
ret = p.Call(input)
|
||||||
|
|
||||||
return contract.Return(ret), nil
|
return ret, nil
|
||||||
} else {
|
} else {
|
||||||
return nil, OutOfGasError
|
return nil, OutOfGasError
|
||||||
}
|
}
|
||||||
|
@ -593,7 +593,8 @@ func (env *Work) commitTransactions(mux *event.TypeMux, transactions types.Trans
|
|||||||
|
|
||||||
var coalescedLogs vm.Logs
|
var coalescedLogs vm.Logs
|
||||||
for _, tx := range transactions {
|
for _, tx := range transactions {
|
||||||
// We can skip err. It has already been validated in the tx pool
|
// Error may be ignored here. The error has already been checked
|
||||||
|
// during transaction acceptance is the transaction pool.
|
||||||
from, _ := tx.From()
|
from, _ := tx.From()
|
||||||
|
|
||||||
// Check if it falls within margin. Txs from owned accounts are always processed.
|
// Check if it falls within margin. Txs from owned accounts are always processed.
|
||||||
|
@ -18,8 +18,7 @@ package params
|
|||||||
|
|
||||||
import "math/big"
|
import "math/big"
|
||||||
|
|
||||||
// TODO: set exact block number after community consensus is reached.
|
var HomesteadBlock *big.Int = big.NewInt(2000000)
|
||||||
var HomesteadBlock *big.Int = big.NewInt(0)
|
|
||||||
|
|
||||||
func IsHomestead(blockNumber *big.Int) bool {
|
func IsHomestead(blockNumber *big.Int) bool {
|
||||||
// for unit tests TODO: flip to true after homestead is live
|
// for unit tests TODO: flip to true after homestead is live
|
||||||
|
@ -130,6 +130,15 @@ func TestStateTransaction(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStateTransition(t *testing.T) {
|
||||||
|
params.HomesteadBlock = big.NewInt(1000000)
|
||||||
|
|
||||||
|
fn := filepath.Join(stateTestDir, "stTransitionTest.json")
|
||||||
|
if err := RunStateTest(fn, StateSkipTests); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCallCreateCallCode(t *testing.T) {
|
func TestCallCreateCallCode(t *testing.T) {
|
||||||
params.HomesteadBlock = big.NewInt(1000000)
|
params.HomesteadBlock = big.NewInt(1000000)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user