Skip to content

Commit 587bafa

Browse files
committed
[release/1.3.4] 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 Conflicts: common/registrar/ethreg/api.go core/tx_pool.go core/vm/jit_test.go
1 parent 7bb496f commit 587bafa

23 files changed

+233
-205
lines changed

core/block_validator.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,7 @@ func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *stat
117117
// For valid blocks this should always validate to true.
118118
rbloom := types.CreateBloom(receipts)
119119
if rbloom != header.Bloom {
120-
//fmt.Printf("FUNKY: ValidateState: block number: %v\n", block.Number())
121-
return fmt.Errorf("unable to replicate block's bloom=%x", rbloom)
120+
return fmt.Errorf("unable to replicate block's bloom=%x vs calculated bloom=%x", header.Bloom, rbloom)
122121
}
123122
// Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, R1]]))
124123
receiptSha := types.DeriveSha(receipts)
@@ -270,10 +269,6 @@ func calcDifficultyHomestead(time, parentTime uint64, parentNumber, parentDiff *
270269
bigTime := new(big.Int).SetUint64(time)
271270
bigParentTime := new(big.Int).SetUint64(parentTime)
272271

273-
// for the exponential factor
274-
periodCount := new(big.Int).Add(parentNumber, common.Big1)
275-
periodCount.Div(periodCount, ExpDiffPeriod)
276-
277272
// holds intermediate values to make the algo easier to read & audit
278273
x := new(big.Int)
279274
y := new(big.Int)
@@ -298,6 +293,10 @@ func calcDifficultyHomestead(time, parentTime uint64, parentNumber, parentDiff *
298293
x = params.MinimumDifficulty
299294
}
300295

296+
// for the exponential factor
297+
periodCount := new(big.Int).Add(parentNumber, common.Big1)
298+
periodCount.Div(periodCount, ExpDiffPeriod)
299+
301300
// the exponential factor, commonly refered to as "the bomb"
302301
// diff = diff + 2^(periodCount - 2)
303302
if periodCount.Cmp(common.Big1) > 0 {

core/database_util.go

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -95,20 +95,6 @@ func GetHeadFastBlockHash(db ethdb.Database) common.Hash {
9595
return common.BytesToHash(data)
9696
}
9797

98-
// GetHeadBlockNum retrieves the block number of the current canonical head block.
99-
func GetHeadBlockNum(db ethdb.Database) *big.Int {
100-
data, _ := db.Get(headBlockKey)
101-
if len(data) == 0 {
102-
return nil
103-
}
104-
header := new(types.Header)
105-
if err := rlp.Decode(bytes.NewReader(data), header); err != nil {
106-
glog.V(logger.Error).Infof("invalid block header RLP for head block: %v", err)
107-
return nil
108-
}
109-
return header.Number
110-
}
111-
11298
// GetHeaderRLP retrieves a block header in its raw RLP database encoding, or nil
11399
// if the header's not found.
114100
func GetHeaderRLP(db ethdb.Database, hash common.Hash) rlp.RawValue {

core/database_util_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ func (d *diffTest) UnmarshalJSON(b []byte) (err error) {
6262
return nil
6363
}
6464

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

7878
for name, test := range tests {
7979
number := new(big.Int).Sub(test.CurrentBlocknumber, big.NewInt(1))
80-
diff := CalcDifficulty(test.CurrentTimestamp, test.ParentTimestamp, number, test.ParentDifficulty)
80+
diff := calcDifficultyFrontier(test.CurrentTimestamp, test.ParentTimestamp, number, test.ParentDifficulty)
8181
if diff.Cmp(test.CurrentDifficulty) != 0 {
8282
t.Error(name, "failed. Expected", test.CurrentDifficulty, "and calculated", diff)
8383
}

core/execution.go

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ func DelegateCall(env vm.Environment, caller vm.ContractRef, addr common.Address
4444
originAddr := env.Origin()
4545
callerValue := caller.Value()
4646
ret, _, err = execDelegateCall(env, caller, &originAddr, &callerAddr, &addr, input, env.Db().GetCode(addr), gas, gasPrice, callerValue)
47-
caller.SetAddress(callerAddr)
4847
return ret, err
4948
}
5049

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

7978
var createAccount bool
8079
if address == nil {
81-
// Generate a new address
80+
// Create a new account on the state
8281
nonce := env.Db().GetNonce(caller.Address())
8382
env.Db().SetNonce(caller.Address(), nonce+1)
8483
addr = crypto.CreateAddress(caller.Address(), nonce)
8584
address = &addr
8685
createAccount = true
8786
}
88-
snapshotPreTransfer := env.MakeSnapshot()
8987

88+
snapshotPreTransfer := env.MakeSnapshot()
9089
var (
9190
from = env.Db().GetAccount(caller.Address())
9291
to vm.Account
@@ -101,24 +100,38 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
101100
}
102101
}
103102
env.Transfer(from, to, value)
104-
snapshotPostTransfer := env.MakeSnapshot()
105103

104+
// initialise a new contract and set the code that is to be used by the
105+
// EVM. The contract is a scoped environment for this execution context
106+
// only.
106107
contract := vm.NewContract(caller, to, value, gas, gasPrice)
107108
contract.SetCallCode(codeAddr, code)
109+
defer contract.Finalise()
108110

109111
ret, err = evm.Run(contract, input)
110-
111-
if err != nil {
112-
if err == vm.CodeStoreOutOfGasError {
113-
// TODO: this is rather hacky, restore all state changes
114-
// except sender's account nonce increment
115-
toNonce := env.Db().GetNonce(to.Address())
116-
env.SetSnapshot(snapshotPostTransfer)
117-
env.Db().SetNonce(to.Address(), toNonce)
112+
// if the contract creation ran successfully and no errors were returned
113+
// calculate the gas required to store the code. If the code could not
114+
// be stored due to not enough gas set an error and let it be handled
115+
// by the error checking condition below.
116+
if err == nil && createAccount {
117+
dataGas := big.NewInt(int64(len(ret)))
118+
dataGas.Mul(dataGas, params.CreateDataGas)
119+
if contract.UseGas(dataGas) {
120+
env.Db().SetCode(*address, ret)
118121
} else {
119-
env.SetSnapshot(snapshotPreTransfer) //env.Db().Set(snapshot)
122+
err = vm.CodeStoreOutOfGasError
120123
}
121124
}
125+
126+
// When an error was returned by the EVM or when setting the creation code
127+
// above we revert to the snapshot and consume any gas remaining. Additionally
128+
// when we're in homestead this also counts for code storage gas errors.
129+
if err != nil && (params.IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError) {
130+
contract.UseGas(contract.Gas)
131+
132+
env.SetSnapshot(snapshotPreTransfer)
133+
}
134+
122135
return ret, addr, err
123136
}
124137

@@ -131,32 +144,27 @@ func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toA
131144
return nil, common.Address{}, vm.DepthError
132145
}
133146

134-
if !env.CanTransfer(*originAddr, value) {
135-
caller.ReturnGas(gas, gasPrice)
136-
return nil, common.Address{}, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address()))
137-
}
138-
139147
snapshot := env.MakeSnapshot()
140148

141-
var (
142-
//from = env.Db().GetAccount(*originAddr)
143-
to vm.Account
144-
)
149+
var to vm.Account
145150
if !env.Db().Exist(*toAddr) {
146151
to = env.Db().CreateAccount(*toAddr)
147152
} else {
148153
to = env.Db().GetAccount(*toAddr)
149154
}
150155

151-
contract := vm.NewContract(caller, to, value, gas, gasPrice)
156+
// Iinitialise a new contract and make initialise the delegate values
157+
contract := vm.NewContract(caller, to, value, gas, gasPrice).AsDelegate()
152158
contract.SetCallCode(codeAddr, code)
153-
contract.DelegateCall = true
159+
defer contract.Finalise()
154160

155161
ret, err = evm.Run(contract, input)
156-
157162
if err != nil {
158-
env.SetSnapshot(snapshot) //env.Db().Set(snapshot)
163+
contract.UseGas(contract.Gas)
164+
165+
env.SetSnapshot(snapshot)
159166
}
167+
160168
return ret, addr, err
161169
}
162170

core/state/dump.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ type Account struct {
2828
Nonce uint64 `json:"nonce"`
2929
Root string `json:"root"`
3030
CodeHash string `json:"codeHash"`
31+
Code string `json:"code"`
3132
Storage map[string]string `json:"storage"`
3233
}
3334

@@ -47,7 +48,7 @@ func (self *StateDB) RawDump() World {
4748
addr := self.trie.GetKey(it.Key)
4849
stateObject := NewStateObjectFromBytes(common.BytesToAddress(addr), it.Value, self.db)
4950

50-
account := Account{Balance: stateObject.balance.String(), Nonce: stateObject.nonce, Root: common.Bytes2Hex(stateObject.Root()), CodeHash: common.Bytes2Hex(stateObject.codeHash)}
51+
account := Account{Balance: stateObject.balance.String(), Nonce: stateObject.nonce, Root: common.Bytes2Hex(stateObject.Root()), CodeHash: common.Bytes2Hex(stateObject.codeHash), Code: common.Bytes2Hex(stateObject.Code())}
5152
account.Storage = make(map[string]string)
5253

5354
storageIt := stateObject.trie.Iterator()

core/state/state_object.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -234,11 +234,6 @@ func (c *StateObject) Address() common.Address {
234234
return c.address
235235
}
236236

237-
// Sets the address of the contract/account
238-
func (c *StateObject) SetAddress(addr common.Address) {
239-
c.address = addr
240-
}
241-
242237
func (self *StateObject) Trie() *trie.SecureTrie {
243238
return self.trie
244239
}
@@ -269,7 +264,7 @@ func (self *StateObject) Nonce() uint64 {
269264
// as a vm.Account interface that also satisfies the vm.ContractRef
270265
// interface. Interfaces are awesome.
271266
func (self *StateObject) Value() *big.Int {
272-
return nil
267+
panic("Value on StateObject should never be called")
273268
}
274269

275270
func (self *StateObject) EachStorage(cb func(key, value []byte)) {

core/state/statedb.go

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -85,18 +85,6 @@ func (self *StateDB) GetLogs(hash common.Hash) vm.Logs {
8585
return self.logs[hash]
8686
}
8787

88-
func (self *StateDB) GetAllLogs() *map[common.Hash]vm.Logs {
89-
copy := make(map[common.Hash]vm.Logs, len(self.logs))
90-
for k, v := range self.logs {
91-
copy[k] = v
92-
}
93-
return &copy
94-
}
95-
96-
func (self *StateDB) SetAllLogs(logs *map[common.Hash]vm.Logs) {
97-
self.logs = *logs
98-
}
99-
10088
func (self *StateDB) Logs() vm.Logs {
10189
var logs vm.Logs
10290
for _, lgs := range self.logs {
@@ -105,11 +93,6 @@ func (self *StateDB) Logs() vm.Logs {
10593
return logs
10694
}
10795

108-
// TODO: this may not be the most proper thing
109-
func (self *StateDB) GetDB() ethdb.Database {
110-
return self.db
111-
}
112-
11396
func (self *StateDB) AddRefund(gas *big.Int) {
11497
self.refund.Add(self.refund, gas)
11598
}

core/state_transition.go

Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"math/big"
2222

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

227226
vmenv := self.env
228-
snapshot := vmenv.MakeSnapshot()
229-
var addr common.Address
227+
//var addr common.Address
230228
if contractCreation {
231-
ret, addr, err = vmenv.Create(sender, self.data, self.gas, self.gasPrice, self.value)
232-
if err == nil {
233-
dataGas := big.NewInt(int64(len(ret)))
234-
dataGas.Mul(dataGas, params.CreateDataGas)
235-
if err := self.useGas(dataGas); err == nil {
236-
self.state.SetCode(addr, ret)
237-
} else {
238-
if homestead {
239-
// rollback all contract creation changes except for
240-
// sender's incremented account nonce and gas usage
241-
// TODO: fucking gas hack... verify potential DoS vuln
242-
accNonce := vmenv.Db().GetNonce(sender.Address())
243-
logs := vmenv.Db().(*state.StateDB).GetAllLogs()
244-
vmenv.SetSnapshot(snapshot)
245-
vmenv.Db().SetNonce(sender.Address(), accNonce)
246-
vmenv.Db().(*state.StateDB).SetAllLogs(logs)
247-
self.gas = Big0
248-
249-
}
250-
ret = nil // does not affect consensus but useful for StateTests validations
251-
glog.V(logger.Core).Infoln("Insufficient gas for creating code. Require", dataGas, "and have", self.gas)
252-
}
229+
ret, _, err = vmenv.Create(sender, self.data, self.gas, self.gasPrice, self.value)
230+
if homestead && err == vm.CodeStoreOutOfGasError {
231+
self.gas = Big0
232+
}
233+
234+
if err != nil {
235+
ret = nil
236+
glog.V(logger.Core).Infoln("VM create err:", err)
253237
}
254-
glog.V(logger.Core).Infoln("VM create err:", err)
255238
} else {
256239
// Increment the nonce for the next transaction
257240
self.state.SetNonce(sender.Address(), self.state.GetNonce(sender.Address())+1)
258241
ret, err = vmenv.Call(sender, self.to().Address(), self.data, self.gas, self.gasPrice, self.value)
259-
glog.V(logger.Core).Infoln("VM call err:", err)
242+
if err != nil {
243+
glog.V(logger.Core).Infoln("VM call err:", err)
244+
}
260245
}
261246

262247
if err != nil && IsValueTransferErr(err) {

core/transaction_pool.go renamed to core/tx_pool.go

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,11 @@ type TxPool struct {
6666
minGasPrice *big.Int
6767
eventMux *event.TypeMux
6868
events event.Subscription
69+
mu sync.RWMutex
70+
pending map[common.Hash]*types.Transaction // processable transactions
71+
queue map[common.Address]map[common.Hash]*types.Transaction
6972

70-
mu sync.RWMutex
71-
pending map[common.Hash]*types.Transaction // processable transactions
72-
queue map[common.Address]map[common.Hash]*types.Transaction
73+
homestead bool
7374
}
7475

7576
func NewTxPool(eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func() *big.Int) *TxPool {
@@ -84,6 +85,7 @@ func NewTxPool(eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func(
8485
pendingState: nil,
8586
events: eventMux.Subscribe(ChainHeadEvent{}, GasPriceChanged{}, RemovedTransactionEvent{}),
8687
}
88+
8789
go pool.eventLoop()
8890

8991
return pool
@@ -97,6 +99,10 @@ func (pool *TxPool) eventLoop() {
9799
switch ev := ev.Data.(type) {
98100
case ChainHeadEvent:
99101
pool.mu.Lock()
102+
if ev.Block != nil && params.IsHomestead(ev.Block.Number()) {
103+
pool.homestead = true
104+
}
105+
100106
pool.resetState()
101107
pool.mu.Unlock()
102108
case GasPriceChanged:
@@ -171,12 +177,6 @@ func (pool *TxPool) Stats() (pending int, queued int) {
171177
// validateTx checks whether a transaction is valid according
172178
// to the consensus rules.
173179
func (pool *TxPool) validateTx(tx *types.Transaction) error {
174-
// Validate sender
175-
var (
176-
from common.Address
177-
err error
178-
)
179-
180180
// Drop transactions under our own minimal accepted gas price
181181
if pool.minGasPrice.Cmp(tx.GasPrice()) > 0 {
182182
return ErrCheap
@@ -187,15 +187,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error {
187187
return err
188188
}
189189

190-
homestead := params.IsHomestead(GetHeadBlockNum(currentState.GetDB()))
191-
192-
// Validate the transaction sender and it's sig. Throw
193-
// if the from fields is invalid.
194-
if homestead {
195-
from, err = tx.From()
196-
} else {
197-
from, err = tx.FromFrontier()
198-
}
190+
from, err := tx.From()
199191
if err != nil {
200192
return ErrInvalidSender
201193
}
@@ -230,8 +222,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error {
230222
return ErrInsufficientFunds
231223
}
232224

233-
// Should supply enough intrinsic gas
234-
intrGas := IntrinsicGas(tx.Data(), MessageCreatesContract(tx), homestead)
225+
intrGas := IntrinsicGas(tx.Data(), MessageCreatesContract(tx), pool.homestead)
235226
if tx.Gas().Cmp(intrGas) < 0 {
236227
return ErrIntrinsicGas
237228
}
File renamed without changes.

0 commit comments

Comments
 (0)