Skip to content

Commit 0f7aee4

Browse files
Add estimate gas to s3 (#2214)
1 parent 0c9b696 commit 0f7aee4

File tree

2 files changed

+128
-0
lines changed

2 files changed

+128
-0
lines changed

Diff for: internal/hmyapi/apiv1/blockchain.go

+64
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"github.com/harmony-one/harmony/core/types"
1818
"github.com/harmony-one/harmony/core/vm"
1919
internal_common "github.com/harmony-one/harmony/internal/common"
20+
"github.com/harmony-one/harmony/internal/params"
2021
"github.com/harmony-one/harmony/internal/utils"
2122
)
2223

@@ -269,3 +270,66 @@ func (s *PublicBlockChainAPI) LatestHeader(ctx context.Context) *HeaderInformati
269270
header, _ := s.b.HeaderByNumber(context.Background(), rpc.LatestBlockNumber) // latest header should always be available
270271
return newHeaderInformation(header)
271272
}
273+
274+
// doEstimateGas ..
275+
func doEstimateGas(ctx context.Context, b Backend, args CallArgs, gasCap *big.Int) (hexutil.Uint64, error) {
276+
// Binary search the gas requirement, as it may be higher than the amount used
277+
var (
278+
lo uint64 = params.TxGas - 1
279+
hi uint64
280+
cap uint64
281+
)
282+
blockNum := rpc.LatestBlockNumber
283+
if args.Gas != nil && uint64(*args.Gas) >= params.TxGas {
284+
hi = uint64(*args.Gas)
285+
} else {
286+
// Retrieve the block to act as the gas ceiling
287+
block, err := b.BlockByNumber(ctx, blockNum)
288+
if err != nil {
289+
return 0, err
290+
}
291+
hi = block.GasLimit()
292+
}
293+
if gasCap != nil && hi > gasCap.Uint64() {
294+
// log.Warn("Caller gas above allowance, capping", "requested", hi, "cap", gasCap)
295+
hi = gasCap.Uint64()
296+
}
297+
cap = hi
298+
299+
// Use zero-address if none other is available
300+
if args.From == nil {
301+
args.From = &common.Address{}
302+
}
303+
// Create a helper to check if a gas allowance results in an executable transaction
304+
executable := func(gas uint64) bool {
305+
args.Gas = (*hexutil.Uint64)(&gas)
306+
307+
_, _, failed, err := doCall(ctx, b, args, blockNum, vm.Config{}, 0, big.NewInt(int64(cap)))
308+
if err != nil || failed {
309+
return false
310+
}
311+
return true
312+
}
313+
// Execute the binary search and hone in on an executable gas limit
314+
for lo+1 < hi {
315+
mid := (hi + lo) / 2
316+
if !executable(mid) {
317+
lo = mid
318+
} else {
319+
hi = mid
320+
}
321+
}
322+
// Reject the transaction as invalid if it still fails at the highest allowance
323+
if hi == cap {
324+
if !executable(hi) {
325+
return 0, fmt.Errorf("gas required exceeds allowance (%d) or always failing transaction", cap)
326+
}
327+
}
328+
return hexutil.Uint64(hi), nil
329+
}
330+
331+
// EstimateGas returns an estimate of the amount of gas needed to execute the
332+
// given transaction against the current pending block.
333+
func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (hexutil.Uint64, error) {
334+
return doEstimateGas(ctx, s.b, args, nil)
335+
}

Diff for: internal/hmyapi/apiv2/blockchain.go

+64
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"github.com/harmony-one/harmony/core/types"
1818
"github.com/harmony-one/harmony/core/vm"
1919
internal_common "github.com/harmony-one/harmony/internal/common"
20+
"github.com/harmony-one/harmony/internal/params"
2021
"github.com/harmony-one/harmony/internal/utils"
2122
)
2223

@@ -274,3 +275,66 @@ func (s *PublicBlockChainAPI) LatestHeader(ctx context.Context) *HeaderInformati
274275
header, _ := s.b.HeaderByNumber(context.Background(), rpc.LatestBlockNumber) // latest header should always be available
275276
return newHeaderInformation(header)
276277
}
278+
279+
// doEstimateGas ..
280+
func doEstimateGas(ctx context.Context, b Backend, args CallArgs, gasCap *big.Int) (hexutil.Uint64, error) {
281+
// Binary search the gas requirement, as it may be higher than the amount used
282+
var (
283+
lo uint64 = params.TxGas - 1
284+
hi uint64
285+
cap uint64
286+
)
287+
blockNum := rpc.LatestBlockNumber
288+
if args.Gas != nil && uint64(*args.Gas) >= params.TxGas {
289+
hi = uint64(*args.Gas)
290+
} else {
291+
// Retrieve the block to act as the gas ceiling
292+
block, err := b.BlockByNumber(ctx, blockNum)
293+
if err != nil {
294+
return 0, err
295+
}
296+
hi = block.GasLimit()
297+
}
298+
if gasCap != nil && hi > gasCap.Uint64() {
299+
// log.Warn("Caller gas above allowance, capping", "requested", hi, "cap", gasCap)
300+
hi = gasCap.Uint64()
301+
}
302+
cap = hi
303+
304+
// Use zero-address if none other is available
305+
if args.From == nil {
306+
args.From = &common.Address{}
307+
}
308+
// Create a helper to check if a gas allowance results in an executable transaction
309+
executable := func(gas uint64) bool {
310+
args.Gas = (*hexutil.Uint64)(&gas)
311+
312+
_, _, failed, err := doCall(ctx, b, args, blockNum, vm.Config{}, 0, big.NewInt(int64(cap)))
313+
if err != nil || failed {
314+
return false
315+
}
316+
return true
317+
}
318+
// Execute the binary search and hone in on an executable gas limit
319+
for lo+1 < hi {
320+
mid := (hi + lo) / 2
321+
if !executable(mid) {
322+
lo = mid
323+
} else {
324+
hi = mid
325+
}
326+
}
327+
// Reject the transaction as invalid if it still fails at the highest allowance
328+
if hi == cap {
329+
if !executable(hi) {
330+
return 0, fmt.Errorf("gas required exceeds allowance (%d) or always failing transaction", cap)
331+
}
332+
}
333+
return hexutil.Uint64(hi), nil
334+
}
335+
336+
// EstimateGas returns an estimate of the amount of gas needed to execute the
337+
// given transaction against the current pending block.
338+
func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (hexutil.Uint64, error) {
339+
return doEstimateGas(ctx, s.b, args, nil)
340+
}

0 commit comments

Comments
 (0)