Skip to content

Instantly share code, notes, and snippets.

@norganna
Created April 23, 2018 02:30
Show Gist options
  • Save norganna/5c82da8dc4b663a05130463f8a93bddc to your computer and use it in GitHub Desktop.
Save norganna/5c82da8dc4b663a05130463f8a93bddc to your computer and use it in GitHub Desktop.
Add rejection reason to go-ethereum EstimateGas
--- 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