Skip to content
This repository was archived by the owner on Sep 6, 2022. It is now read-only.

Commit 5064bb2

Browse files
committed
add features
1 parent ec93a8f commit 5064bb2

File tree

1 file changed

+154
-0
lines changed

1 file changed

+154
-0
lines changed

internal/ethapi/api.go

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1483,6 +1483,74 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
14831483
}
14841484
}
14851485

1486+
// Creates an access list on top of given state
1487+
// identical to AccessList, with the exception of state is loaded in arguments
1488+
func AccessListOnState(ctx context.Context, b Backend, header *types.Header, db *state.StateDB, args TransactionArgs) (acl types.AccessList, gasUsed uint64, vmErr error, err error) {
1489+
// If the gas amount is not set, extract this as it will depend on access
1490+
// lists and we'll need to reestimate every time
1491+
nogas := args.Gas == nil
1492+
1493+
// Ensure any missing fields are filled, extract the recipient and input data
1494+
if err := args.setDefaults(ctx, b); err != nil {
1495+
return nil, 0, nil, err
1496+
}
1497+
var to common.Address
1498+
if args.To != nil {
1499+
to = *args.To
1500+
} else {
1501+
to = crypto.CreateAddress(args.from(), uint64(*args.Nonce))
1502+
}
1503+
// Retrieve the precompiles since they don't need to be added to the access list
1504+
isPostMerge := header.Difficulty.Cmp(common.Big0) == 0
1505+
// Retrieve the precompiles since they don't need to be added to the access list
1506+
precompiles := vm.ActivePrecompiles(b.ChainConfig().Rules(header.Number, isPostMerge))
1507+
1508+
// Create an initial tracer
1509+
prevTracer := logger.NewAccessListTracer(nil, args.from(), to, precompiles)
1510+
if args.AccessList != nil {
1511+
prevTracer = logger.NewAccessListTracer(*args.AccessList, args.from(), to, precompiles)
1512+
}
1513+
for {
1514+
// Retrieve the current access list to expand
1515+
accessList := prevTracer.AccessList()
1516+
log.Trace("Creating access list", "input", accessList)
1517+
1518+
// If no gas amount was specified, each unique access list needs it's own
1519+
// gas calculation. This is quite expensive, but we need to be accurate
1520+
// and it's convered by the sender only anyway.
1521+
if nogas {
1522+
args.Gas = nil
1523+
if err := args.setDefaults(ctx, b); err != nil {
1524+
return nil, 0, nil, err // shouldn't happen, just in case
1525+
}
1526+
}
1527+
1528+
statedb := db.Copy() // woops shouldn't have removed this lol
1529+
// Set the accesslist to the last al
1530+
args.AccessList = &accessList
1531+
msg, err := args.ToMessage(b.RPCGasCap(), header.BaseFee)
1532+
if err != nil {
1533+
return nil, 0, nil, err
1534+
}
1535+
1536+
// Apply the transaction with the access list tracer
1537+
tracer := logger.NewAccessListTracer(accessList, args.from(), to, precompiles)
1538+
config := vm.Config{Tracer: tracer, Debug: true, NoBaseFee: true}
1539+
vmenv, _, err := b.GetEVM(ctx, msg, statedb, header, &config)
1540+
if err != nil {
1541+
return nil, 0, nil, err
1542+
}
1543+
res, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()))
1544+
if err != nil {
1545+
return nil, 0, nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.toTransaction().Hash(), err)
1546+
}
1547+
if tracer.Equal(prevTracer) {
1548+
return accessList, res.UsedGas, res.Err, nil
1549+
}
1550+
prevTracer = tracer
1551+
}
1552+
}
1553+
14861554
// PublicTransactionPoolAPI exposes methods for the RPC interface
14871555
type PublicTransactionPoolAPI struct {
14881556
b Backend
@@ -2246,6 +2314,8 @@ type CallBundleArgs struct {
22462314
GasLimit *uint64 `json:"gasLimit"`
22472315
Difficulty *big.Int `json:"difficulty"`
22482316
BaseFee *big.Int `json:"baseFee"`
2317+
SimulationLogs bool `json:"simulationLogs"`
2318+
CreateAccessList bool `json:"createAccessList"`
22492319
}
22502320

22512321
// CallBundle will simulate a bundle of transactions at the top of a given block
@@ -2345,6 +2415,8 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st
23452415
coinbaseBalanceBeforeTx := state.GetBalance(coinbase)
23462416
state.Prepare(tx.Hash(), i)
23472417

2418+
accessListState := state.Copy() // create a copy just in case we use it later for access list creation
2419+
23482420
receipt, result, err := core.ApplyTransactionWithResult(s.b.ChainConfig(), s.chain, &coinbase, gp, state, header, tx, &header.GasUsed, vmconfig)
23492421
if err != nil {
23502422
return nil, fmt.Errorf("err: %w; txhash %s", err, tx.Hash())
@@ -2384,6 +2456,46 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st
23842456
hex.Encode(dst, result.Return())
23852457
jsonResult["value"] = "0x" + string(dst)
23862458
}
2459+
// if simulation logs are requested append it to logs
2460+
if args.SimulationLogs {
2461+
jsonResult["logs"] = receipt.Logs
2462+
}
2463+
// if an access list is requested create and append
2464+
if args.CreateAccessList {
2465+
// ifdk another way to fill all values so this will have to do - x2
2466+
txArgGas := hexutil.Uint64(tx.Gas())
2467+
txArgNonce := hexutil.Uint64(tx.Nonce())
2468+
txArgData := hexutil.Bytes(tx.Data())
2469+
txargs := TransactionArgs{
2470+
From: &from,
2471+
To: tx.To(),
2472+
Gas: &txArgGas,
2473+
Nonce: &txArgNonce,
2474+
Data: &txArgData,
2475+
Value: (*hexutil.Big)(tx.Value()),
2476+
ChainID: (*hexutil.Big)(tx.ChainId()),
2477+
}
2478+
if tx.GasFeeCap().Cmp(big.NewInt(0)) == 0 { // no maxbasefee, set gasprice instead
2479+
txargs.GasPrice = (*hexutil.Big)(tx.GasPrice())
2480+
} else { // otherwise set base and priority fee
2481+
txargs.MaxFeePerGas = (*hexutil.Big)(tx.GasFeeCap())
2482+
txargs.MaxPriorityFeePerGas = (*hexutil.Big)(tx.GasTipCap())
2483+
}
2484+
acl, gasUsed, vmerr, err := AccessListOnState(ctx, s.b, header, accessListState, txargs)
2485+
if err == nil {
2486+
if gasUsed != receipt.GasUsed {
2487+
log.Info("Gas used in receipt differ from accesslist", "receipt", receipt.GasUsed, "acl", gasUsed) // weird bug but it works
2488+
}
2489+
if vmerr != nil {
2490+
log.Info("CallBundle accesslist creation encountered vmerr", "vmerr", vmerr)
2491+
}
2492+
jsonResult["accessList"] = acl
2493+
2494+
} else {
2495+
log.Info("CallBundle accesslist creation encountered err", "err", err)
2496+
jsonResult["accessList"] = acl //
2497+
} // return the empty accesslist either way
2498+
}
23872499
coinbaseDiffTx := new(big.Int).Sub(state.GetBalance(coinbase), coinbaseBalanceBeforeTx)
23882500
jsonResult["coinbaseDiff"] = coinbaseDiffTx.String()
23892501
jsonResult["gasFees"] = gasFeesTx.String()
@@ -2415,8 +2527,11 @@ type EstimateGasBundleArgs struct {
24152527
Coinbase *string `json:"coinbase"`
24162528
Timestamp *uint64 `json:"timestamp"`
24172529
Timeout *int64 `json:"timeout"`
2530+
//SimulationLogs bool `json:"simulationLogs"`
2531+
CreateAccessList bool `json:"createAccessList"`
24182532
}
24192533

2534+
// im lazy to create a few extra core functions to return logs here, maybe sometime later or if someone else does it
24202535
func (s *BundleAPI) EstimateGasBundle(ctx context.Context, args EstimateGasBundleArgs) (map[string]interface{}, error) {
24212536
if len(args.Txs) == 0 {
24222537
return nil, errors.New("bundle missing txs")
@@ -2495,6 +2610,8 @@ func (s *BundleAPI) EstimateGasBundle(ctx context.Context, args EstimateGasBundl
24952610
// New random hash since its a call
24962611
statedb.Prepare(randomHash, i)
24972612

2613+
accessListState := statedb.Copy() // create a copy just in case we use it later for access list creation
2614+
24982615
// Convert tx args to msg to apply state transition
24992616
msg, err := txArgs.ToMessage(globalGasCap, header.BaseFee)
25002617
if err != nil {
@@ -2521,6 +2638,43 @@ func (s *BundleAPI) EstimateGasBundle(ctx context.Context, args EstimateGasBundl
25212638
jsonResult := map[string]interface{}{
25222639
"gasUsed": result.UsedGas,
25232640
}
2641+
2642+
// if simulation logs are requested append it to logs
2643+
// if an access list is requested create and append
2644+
if args.CreateAccessList {
2645+
// welp guess we're copying these again sigh
2646+
txArgFrom := msg.From()
2647+
txArgGas := hexutil.Uint64(msg.Gas())
2648+
txArgNonce := hexutil.Uint64(msg.Nonce())
2649+
txArgData := hexutil.Bytes(msg.Data())
2650+
txargs := TransactionArgs{
2651+
From: &txArgFrom,
2652+
To: msg.To(),
2653+
Gas: &txArgGas,
2654+
Nonce: &txArgNonce,
2655+
Data: &txArgData,
2656+
ChainID: (*hexutil.Big)(s.chain.Config().ChainID),
2657+
Value: (*hexutil.Big)(msg.Value()),
2658+
}
2659+
if msg.GasFeeCap().Cmp(big.NewInt(0)) == 0 { // no maxbasefee, set gasprice instead
2660+
txargs.GasPrice = (*hexutil.Big)(msg.GasPrice())
2661+
} else { // otherwise set base and priority fee
2662+
txargs.MaxFeePerGas = (*hexutil.Big)(msg.GasFeeCap())
2663+
txargs.MaxPriorityFeePerGas = (*hexutil.Big)(msg.GasTipCap())
2664+
}
2665+
acl, _, vmerr, err := AccessListOnState(ctx, s.b, header, accessListState, txargs)
2666+
if err == nil {
2667+
if vmerr != nil {
2668+
log.Info("CallBundle accesslist creation encountered vmerr", "vmerr", vmerr)
2669+
}
2670+
jsonResult["accessList"] = acl
2671+
2672+
} else {
2673+
log.Info("CallBundle accesslist creation encountered err", "err", err)
2674+
jsonResult["accessList"] = acl //
2675+
} // return the empty accesslist either way
2676+
}
2677+
25242678
results = append(results, jsonResult)
25252679
}
25262680

0 commit comments

Comments
 (0)