Skip to content

Commit

Permalink
core, core/vm, crypto: fixes for homestead
Browse files Browse the repository at this point in the history
* Removed some strange code that didn't apply state reverting properly
* Refactored code setting from vm & state transition to the executioner
* Updated tests
  • Loading branch information
obscuren committed Feb 18, 2016
1 parent 4f4d2b6 commit b6d88a0
Show file tree
Hide file tree
Showing 24 changed files with 226 additions and 195 deletions.
5 changes: 4 additions & 1 deletion common/registrar/ethreg/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ import (
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"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/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/common/registrar"
)

// registryAPIBackend is a backend for an Ethereum Registry.
Expand Down Expand Up @@ -112,6 +112,9 @@ type callmsg struct {
func (m callmsg) From() (common.Address, error) {
return m.from.Address(), nil
}
func (m callmsg) FromFrontier() (common.Address, error) {
return m.from.Address(), nil
}
func (m callmsg) Nonce() uint64 {
return m.from.Nonce()
}
Expand Down
11 changes: 5 additions & 6 deletions core/block_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,7 @@ func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *stat
// For valid blocks this should always validate to true.
rbloom := types.CreateBloom(receipts)
if rbloom != header.Bloom {
//fmt.Printf("FUNKY: ValidateState: block number: %v\n", block.Number())
return fmt.Errorf("unable to replicate block's bloom=%x", rbloom)
return fmt.Errorf("unable to replicate block's bloom=%x vs calculated bloom=%x", header.Bloom, rbloom)
}
// Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, R1]]))
receiptSha := types.DeriveSha(receipts)
Expand Down Expand Up @@ -270,10 +269,6 @@ func calcDifficultyHomestead(time, parentTime uint64, parentNumber, parentDiff *
bigTime := new(big.Int).SetUint64(time)
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
x := new(big.Int)
y := new(big.Int)
Expand All @@ -298,6 +293,10 @@ func calcDifficultyHomestead(time, parentTime uint64, parentNumber, parentDiff *
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"
// diff = diff + 2^(periodCount - 2)
if periodCount.Cmp(common.Big1) > 0 {
Expand Down
14 changes: 0 additions & 14 deletions core/database_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,20 +95,6 @@ func GetHeadFastBlockHash(db ethdb.Database) common.Hash {
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
// if the header's not found.
func GetHeaderRLP(db ethdb.Database, hash common.Hash) rlp.RawValue {
Expand Down
4 changes: 2 additions & 2 deletions core/database_util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func (d *diffTest) UnmarshalJSON(b []byte) (err error) {
return nil
}

func TestDifficulty(t *testing.T) {
func TestDifficultyFrontier(t *testing.T) {
file, err := os.Open("../tests/files/BasicTests/difficulty.json")
if err != nil {
t.Fatal(err)
Expand All @@ -77,7 +77,7 @@ func TestDifficulty(t *testing.T) {

for name, test := range tests {
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 {
t.Error(name, "failed. Expected", test.CurrentDifficulty, "and calculated", diff)
}
Expand Down
60 changes: 34 additions & 26 deletions core/execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ func DelegateCall(env vm.Environment, caller vm.ContractRef, addr common.Address
originAddr := env.Origin()
callerValue := caller.Value()
ret, _, err = execDelegateCall(env, caller, &originAddr, &callerAddr, &addr, input, env.Db().GetCode(addr), gas, gasPrice, callerValue)
caller.SetAddress(callerAddr)
return ret, err
}

Expand Down Expand Up @@ -78,15 +77,15 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A

var createAccount bool
if address == nil {
// Generate a new address
// Create a new account on the state
nonce := env.Db().GetNonce(caller.Address())
env.Db().SetNonce(caller.Address(), nonce+1)
addr = crypto.CreateAddress(caller.Address(), nonce)
address = &addr
createAccount = true
}
snapshotPreTransfer := env.MakeSnapshot()

snapshotPreTransfer := env.MakeSnapshot()
var (
from = env.Db().GetAccount(caller.Address())
to vm.Account
Expand All @@ -101,24 +100,38 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
}
}
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.SetCallCode(codeAddr, code)
defer contract.Finalise()

ret, err = evm.Run(contract, input)

if err != nil {
if err == vm.CodeStoreOutOfGasError {
// TODO: this is rather hacky, restore all state changes
// except sender's account nonce increment
toNonce := env.Db().GetNonce(to.Address())
env.SetSnapshot(snapshotPostTransfer)
env.Db().SetNonce(to.Address(), toNonce)
// if the contract creation ran successfully and no errors were returned
// calculate the gas required to store the code. If the code could not
// be stored due to not enough gas set an error and let it be handled
// by the error checking condition below.
if err == nil && createAccount {
dataGas := big.NewInt(int64(len(ret)))
dataGas.Mul(dataGas, params.CreateDataGas)
if contract.UseGas(dataGas) {
env.Db().SetCode(*address, ret)
} 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
}

Expand All @@ -131,32 +144,27 @@ func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toA
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()

var (
//from = env.Db().GetAccount(*originAddr)
to vm.Account
)
var to vm.Account
if !env.Db().Exist(*toAddr) {
to = env.Db().CreateAccount(*toAddr)
} else {
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.DelegateCall = true
defer contract.Finalise()

ret, err = evm.Run(contract, input)

if err != nil {
env.SetSnapshot(snapshot) //env.Db().Set(snapshot)
contract.UseGas(contract.Gas)

env.SetSnapshot(snapshot)
}

return ret, addr, err
}

Expand Down
3 changes: 2 additions & 1 deletion core/state/dump.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type Account struct {
Nonce uint64 `json:"nonce"`
Root string `json:"root"`
CodeHash string `json:"codeHash"`
Code string `json:"code"`
Storage map[string]string `json:"storage"`
}

Expand All @@ -47,7 +48,7 @@ func (self *StateDB) RawDump() World {
addr := self.trie.GetKey(it.Key)
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)

storageIt := stateObject.trie.Iterator()
Expand Down
7 changes: 1 addition & 6 deletions core/state/state_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,11 +211,6 @@ func (c *StateObject) Address() common.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 {
return self.trie
}
Expand Down Expand Up @@ -247,7 +242,7 @@ func (self *StateObject) Nonce() uint64 {
// as a vm.Account interface that also satisfies the vm.ContractRef
// interface. Interfaces are awesome.
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)) {
Expand Down
17 changes: 0 additions & 17 deletions core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,18 +87,6 @@ func (self *StateDB) GetLogs(hash common.Hash) vm.Logs {
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 &copy
}

func (self *StateDB) SetAllLogs(logs *map[common.Hash]vm.Logs) {
self.logs = *logs
}

func (self *StateDB) Logs() vm.Logs {
var logs vm.Logs
for _, lgs := range self.logs {
Expand All @@ -107,11 +95,6 @@ func (self *StateDB) Logs() vm.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) {
self.refund.Add(self.refund, gas)
}
Expand Down
39 changes: 12 additions & 27 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
Expand Down Expand Up @@ -225,38 +224,24 @@ func (self *StateTransition) transitionDb() (ret []byte, usedGas *big.Int, err e
}

vmenv := self.env
snapshot := vmenv.MakeSnapshot()
var addr common.Address
//var addr common.Address
if contractCreation {
ret, addr, err = vmenv.Create(sender, self.data, self.gas, self.gasPrice, self.value)
if err == nil {
dataGas := big.NewInt(int64(len(ret)))
dataGas.Mul(dataGas, params.CreateDataGas)
if err := self.useGas(dataGas); err == nil {
self.state.SetCode(addr, ret)
} else {
if homestead {
// 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)
}
ret, _, err = vmenv.Create(sender, self.data, self.gas, self.gasPrice, self.value)
if homestead && err == vm.CodeStoreOutOfGasError {
self.gas = Big0
}

if err != nil {
ret = nil
glog.V(logger.Core).Infoln("VM create err:", err)
}
glog.V(logger.Core).Infoln("VM create err:", err)
} else {
// Increment the nonce for the next transaction
self.state.SetNonce(sender.Address(), self.state.GetNonce(sender.Address())+1)
ret, err = vmenv.Call(sender, self.to().Address(), self.data, self.gas, self.gasPrice, self.value)
glog.V(logger.Core).Infoln("VM call err:", err)
if err != nil {
glog.V(logger.Core).Infoln("VM call err:", err)
}
}

if err != nil && IsValueTransferErr(err) {
Expand Down
Loading

0 comments on commit b6d88a0

Please sign in to comment.