Created
April 23, 2018 02:30
-
-
Save norganna/5c82da8dc4b663a05130463f8a93bddc to your computer and use it in GitHub Desktop.
Add rejection reason to go-ethereum EstimateGas
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- core/vm/interpreter.go (revision 744428cb03ea8de8f219708f57d2e197acb6689b) | |
+++ core/vm/interpreter.go (date 1524449729000) | |
@@ -102,7 +102,7 @@ | |
// | |
// It's important to note that any errors returned by the interpreter should be | |
// considered a revert-and-consume-all-gas operation except for | |
-// errExecutionReverted which means revert-and-keep-gas-left. | |
+// ErrExecutionReverted which means revert-and-keep-gas-left. | |
func (in *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err error) { | |
// Increment the call depth which is restricted to 1024 | |
in.evm.depth++ | |
@@ -215,7 +215,7 @@ | |
case err != nil: | |
return nil, err | |
case operation.reverts: | |
- return res, errExecutionReverted | |
+ return res, ErrExecutionReverted | |
case operation.halts: | |
return res, nil | |
case !operation.jumps: | |
--- core/vm/evm.go (revision 744428cb03ea8de8f219708f57d2e197acb6689b) | |
+++ core/vm/evm.go (date 1524449729000) | |
@@ -188,7 +188,7 @@ | |
// when we're in homestead this also counts for code storage gas errors. | |
if err != nil { | |
evm.StateDB.RevertToSnapshot(snapshot) | |
- if err != errExecutionReverted { | |
+ if err != ErrExecutionReverted { | |
contract.UseGas(contract.Gas) | |
} | |
} | |
@@ -229,7 +229,7 @@ | |
ret, err = run(evm, contract, input) | |
if err != nil { | |
evm.StateDB.RevertToSnapshot(snapshot) | |
- if err != errExecutionReverted { | |
+ if err != ErrExecutionReverted { | |
contract.UseGas(contract.Gas) | |
} | |
} | |
@@ -262,7 +262,7 @@ | |
ret, err = run(evm, contract, input) | |
if err != nil { | |
evm.StateDB.RevertToSnapshot(snapshot) | |
- if err != errExecutionReverted { | |
+ if err != ErrExecutionReverted { | |
contract.UseGas(contract.Gas) | |
} | |
} | |
@@ -305,7 +305,7 @@ | |
ret, err = run(evm, contract, input) | |
if err != nil { | |
evm.StateDB.RevertToSnapshot(snapshot) | |
- if err != errExecutionReverted { | |
+ if err != ErrExecutionReverted { | |
contract.UseGas(contract.Gas) | |
} | |
} | |
@@ -377,7 +377,7 @@ | |
// when we're in homestead this also counts for code storage gas errors. | |
if maxCodeSizeExceeded || (err != nil && (evm.ChainConfig().IsHomestead(evm.BlockNumber) || err != ErrCodeStoreOutOfGas)) { | |
evm.StateDB.RevertToSnapshot(snapshot) | |
- if err != errExecutionReverted { | |
+ if err != ErrExecutionReverted { | |
contract.UseGas(contract.Gas) | |
} | |
} | |
--- core/state_transition.go (revision 744428cb03ea8de8f219708f57d2e197acb6689b) | |
+++ core/state_transition.go (date 1524449729000) | |
@@ -220,6 +220,10 @@ | |
if vmerr == vm.ErrInsufficientBalance { | |
return nil, 0, false, vmerr | |
} | |
+ | |
+ if vmerr == vm.ErrExecutionReverted { | |
+ err = vmerr | |
+ } | |
} | |
st.refundGas() | |
st.state.AddBalance(st.evm.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice)) | |
--- accounts/abi/bind/backends/simulated.go (revision 744428cb03ea8de8f219708f57d2e197acb6689b) | |
+++ accounts/abi/bind/backends/simulated.go (date 1524449729000) | |
@@ -223,6 +223,8 @@ | |
lo uint64 = params.TxGas - 1 | |
hi uint64 | |
cap uint64 | |
+ | |
+ revert []byte | |
) | |
if call.Gas >= params.TxGas { | |
hi = call.Gas | |
@@ -236,14 +238,28 @@ | |
call.Gas = gas | |
snapshot := b.pendingState.Snapshot() | |
- _, _, failed, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState) | |
+ res, _, failed, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState) | |
b.pendingState.RevertToSnapshot(snapshot) | |
if err != nil || failed { | |
+ if err == vm.ErrExecutionReverted { | |
+ revert = res | |
+ } | |
return false | |
} | |
return true | |
} | |
+ | |
+ // Test the cap first to avoid searching if the maximum won't work | |
+ if !executable(cap) { | |
+ if len(revert) > 0 { | |
+ return 0, vm.ErrRevertedTransaction{ | |
+ Ret: revert, | |
+ } | |
+ } | |
+ return 0, errGasEstimationFailed | |
+ } | |
+ | |
// Execute the binary search and hone in on an executable gas limit | |
for lo+1 < hi { | |
mid := (hi + lo) / 2 | |
@@ -253,12 +269,7 @@ | |
hi = mid | |
} | |
} | |
- // Reject the transaction as invalid if it still fails at the highest allowance | |
- if hi == cap { | |
- if !executable(hi) { | |
- return 0, errGasEstimationFailed | |
- } | |
- } | |
+ | |
return hi, nil | |
} | |
--- core/vm/instructions.go (revision 744428cb03ea8de8f219708f57d2e197acb6689b) | |
+++ core/vm/instructions.go (date 1524449729000) | |
@@ -33,7 +33,6 @@ | |
tt255 = math.BigPow(2, 255) | |
errWriteProtection = errors.New("evm: write protection") | |
errReturnDataOutOfBounds = errors.New("evm: return data out of bounds") | |
- errExecutionReverted = errors.New("evm: execution reverted") | |
errMaxCodeSizeExceeded = errors.New("evm: max code size exceeded") | |
) | |
@@ -659,7 +658,7 @@ | |
contract.Gas += returnGas | |
evm.interpreter.intPool.put(value, offset, size) | |
- if suberr == errExecutionReverted { | |
+ if suberr == ErrExecutionReverted { | |
return res, nil | |
} | |
return nil, nil | |
@@ -685,7 +684,7 @@ | |
} else { | |
stack.push(evm.interpreter.intPool.get().SetUint64(1)) | |
} | |
- if err == nil || err == errExecutionReverted { | |
+ if err == nil || err == ErrExecutionReverted { | |
memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) | |
} | |
contract.Gas += returnGas | |
@@ -714,7 +713,7 @@ | |
} else { | |
stack.push(evm.interpreter.intPool.get().SetUint64(1)) | |
} | |
- if err == nil || err == errExecutionReverted { | |
+ if err == nil || err == ErrExecutionReverted { | |
memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) | |
} | |
contract.Gas += returnGas | |
@@ -739,7 +738,7 @@ | |
} else { | |
stack.push(evm.interpreter.intPool.get().SetUint64(1)) | |
} | |
- if err == nil || err == errExecutionReverted { | |
+ if err == nil || err == ErrExecutionReverted { | |
memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) | |
} | |
contract.Gas += returnGas | |
@@ -764,7 +763,7 @@ | |
} else { | |
stack.push(evm.interpreter.intPool.get().SetUint64(1)) | |
} | |
- if err == nil || err == errExecutionReverted { | |
+ if err == nil || err == ErrExecutionReverted { | |
memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) | |
} | |
contract.Gas += returnGas | |
--- core/vm/errors.go (revision 744428cb03ea8de8f219708f57d2e197acb6689b) | |
+++ core/vm/errors.go (date 1524449729000) | |
@@ -16,7 +16,11 @@ | |
package vm | |
-import "errors" | |
+import ( | |
+ "encoding/binary" | |
+ "errors" | |
+ "fmt" | |
+) | |
var ( | |
ErrOutOfGas = errors.New("out of gas") | |
@@ -25,4 +29,33 @@ | |
ErrTraceLimitReached = errors.New("the number of logs reached the specified limit") | |
ErrInsufficientBalance = errors.New("insufficient balance for transfer") | |
ErrContractAddressCollision = errors.New("contract address collision") | |
+ ErrExecutionReverted = errors.New("evm: execution reverted") | |
) | |
+ | |
+// ErrRevertedTransaction represents an error caused by a reverted execution at the highest gas level. | |
+type ErrRevertedTransaction struct { | |
+ Ret []byte | |
+} | |
+ | |
+// Error returns a structured error message. | |
+func (e ErrRevertedTransaction) Error() string { | |
+ if msg := e.Message(); msg != "" { | |
+ return fmt.Sprintf("transaction reverted with message \"%s\"", msg) | |
+ } | |
+ return "transaction reverted" | |
+} | |
+ | |
+// Message returns the string "message" portion of the revert operation (if supplied). | |
+func (e ErrRevertedTransaction) Message() string { | |
+ // Not sure what the previous 68 bytes represent, but they may also need a method to extract data? | |
+ // 4 bytes = unknown uint32? | |
+ // 16 bytes = empty? | |
+ // 16 bytes = " "? | |
+ // 4 bytes = uint32 message length | |
+ // *16 bytes = message | |
+ if len(e.Ret) > 68 { | |
+ n := binary.BigEndian.Uint32(e.Ret[64:68]) | |
+ return string(e.Ret[68:68+n]) | |
+ } | |
+ return "" | |
+} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment