Skip to content

Commit

Permalink
allow 0 confirmation delays in VRF; use pending block for simulation …
Browse files Browse the repository at this point in the history
…in VRF
  • Loading branch information
jinhoonbang committed Feb 9, 2024
1 parent ac88b89 commit f053dba
Show file tree
Hide file tree
Showing 11 changed files with 289 additions and 66 deletions.
76 changes: 72 additions & 4 deletions core/chains/evm/client/simulated_backend_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ func (c *SimulatedBackendClient) CallContext(ctx context.Context, result interfa
return c.ethCall(ctx, result, args...)
case "eth_getHeaderByNumber":
return c.ethGetHeaderByNumber(ctx, result, args...)
case "eth_estimateGas":
return c.ethEstimateGas(ctx, result, args...)
default:
return fmt.Errorf("second arg to SimulatedBackendClient.Call is an RPC API method which has not yet been implemented: %s. Add processing for it here", method)
}
Expand Down Expand Up @@ -443,6 +445,8 @@ func (c *SimulatedBackendClient) BatchCallContext(ctx context.Context, b []rpc.B
b[i].Error = c.ethCall(ctx, b[i].Result, b[i].Args...)
case "eth_getHeaderByNumber":
b[i].Error = c.ethGetHeaderByNumber(ctx, b[i].Result, b[i].Args...)
case "eth_estimateGas":
b[i].Error = c.ethEstimateGas(ctx, b[i].Result, b[i].Args...)
default:
return fmt.Errorf("SimulatedBackendClient got unsupported method %s", elem.Method)
}
Expand Down Expand Up @@ -562,6 +566,37 @@ func (c *SimulatedBackendClient) ethGetBlockByNumber(ctx context.Context, result

return nil
}
func (c *SimulatedBackendClient) ethEstimateGas(ctx context.Context, result interface{}, args ...interface{}) error {
if len(args) != 2 {
return fmt.Errorf("SimulatedBackendClient expected 2 args, got %d for eth_call", len(args))
}

params, ok := args[0].(map[string]interface{})
if !ok {
return fmt.Errorf("SimulatedBackendClient expected first arg to be map[string]interface{} for eth_call, got: %T", args[0])
}

_, err := c.blockNumber(args[1])
if err != nil {
return fmt.Errorf("SimulatedBackendClient expected second arg to be the string 'latest' or a *big.Int for eth_call, got: %T", args[1])
}

resp, err := c.b.EstimateGas(ctx, toCallMsg(params))
if err != nil {
return err
}

switch typedResult := result.(type) {
case *uint64:
*typedResult = resp
case *hexutil.Uint64:
*typedResult = hexutil.Uint64(resp)
default:
return fmt.Errorf("SimulatedBackendClient unexpected type %T", result)
}

return nil
}

func (c *SimulatedBackendClient) ethCall(ctx context.Context, result interface{}, args ...interface{}) error {
if len(args) != 2 {
Expand Down Expand Up @@ -625,7 +660,6 @@ func (c *SimulatedBackendClient) ethGetHeaderByNumber(ctx context.Context, resul

func toCallMsg(params map[string]interface{}) ethereum.CallMsg {
var callMsg ethereum.CallMsg

toAddr, err := interfaceToAddress(params["to"])
if err != nil {
panic(fmt.Errorf("unexpected 'to' parameter: %s", err))
Expand All @@ -645,6 +679,10 @@ func toCallMsg(params map[string]interface{}) ethereum.CallMsg {
callMsg.From = common.HexToAddress("0x")
}

if params["data"] != nil && params["input"] != nil {
panic("cannot have both 'data' and 'input' parameters")
}

switch data := params["data"].(type) {
case nil:
// This parameter is not required so nil is acceptable
Expand All @@ -656,16 +694,41 @@ func toCallMsg(params map[string]interface{}) ethereum.CallMsg {
panic("unexpected type of 'data' parameter; try hexutil.Bytes, []byte, or nil")
}

switch input := params["input"].(type) {
case nil:
// This parameter is not required so nil is acceptable
case hexutil.Bytes:
callMsg.Data = input
case []byte:
callMsg.Data = input
default:
panic("unexpected type of 'input' parameter; try hexutil.Bytes, []byte, or nil")
}

if value, ok := params["value"].(*big.Int); ok {
callMsg.Value = value
}

if gas, ok := params["gas"].(uint64); ok {
switch gas := params["gas"].(type) {
case nil:
// This parameter is not required so nil is acceptable
case uint64:
callMsg.Gas = gas
case hexutil.Uint64:
callMsg.Gas = uint64(gas)
default:
panic("unexpected type of 'gas' parameter; try hexutil.Uint64, or uint64")
}

if gasPrice, ok := params["gasPrice"].(*big.Int); ok {
switch gasPrice := params["gasPrice"].(type) {
case nil:
// This parameter is not required so nil is acceptable
case *big.Int:
callMsg.GasPrice = gasPrice
case *hexutil.Big:
callMsg.GasPrice = gasPrice.ToInt()
default:
panic("unexpected type of 'gasPrice' parameter; try *big.Int, or *hexutil.Big")
}

return callMsg
Expand All @@ -675,6 +738,11 @@ func interfaceToAddress(value interface{}) (common.Address, error) {
switch v := value.(type) {
case common.Address:
return v, nil
case *common.Address:
if v == nil {
return common.Address{}, nil
}
return *v, nil
case string:
if ok := common.IsHexAddress(v); !ok {
return common.Address{}, fmt.Errorf("string not formatted as a hex encoded evm address")
Expand All @@ -688,6 +756,6 @@ func interfaceToAddress(value interface{}) (common.Address, error) {

return common.BigToAddress(v), nil
default:
return common.Address{}, fmt.Errorf("unrecognized value type for converting value to common.Address; use hex encoded string, *big.Int, or common.Address")
return common.Address{}, fmt.Errorf("unrecognized value type: %T for converting value to common.Address; use hex encoded string, *big.Int, or common.Address", v)
}
}
27 changes: 20 additions & 7 deletions core/scripts/vrfv2plus/testnet/v2plusscripts/super_scripts.go
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,7 @@ func DeployUniverseViaCLI(e helpers.Environment) {
deployCmd := flag.NewFlagSet("deploy-universe", flag.ExitOnError)

// required flags
nativeOnly := deployCmd.Bool("native-only", false, "if true, link and link feed are not set up")
linkAddress := deployCmd.String("link-address", "", "address of link token")
linkEthAddress := deployCmd.String("link-eth-feed", "", "address of link eth feed")
bhsContractAddressString := deployCmd.String("bhs-address", "", "address of BHS contract")
Expand Down Expand Up @@ -507,6 +508,15 @@ func DeployUniverseViaCLI(e helpers.Environment) {
deployCmd, os.Args[2:],
)

if *nativeOnly {
if *linkAddress != "" || *linkEthAddress != "" {
panic("native-only flag is set, but link address or link eth address is provided")
}
if *subscriptionBalanceJuelsString != "0" {
panic("native-only flag is set, but link subscription balance is provided")
}
}

fallbackWeiPerUnitLink := decimal.RequireFromString(*fallbackWeiPerUnitLinkString).BigInt()
subscriptionBalanceJuels := decimal.RequireFromString(*subscriptionBalanceJuelsString).BigInt()
subscriptionBalanceNativeWei := decimal.RequireFromString(*subscriptionBalanceNativeWeiString).BigInt()
Expand Down Expand Up @@ -569,6 +579,8 @@ func DeployUniverseViaCLI(e helpers.Environment) {
vrfKeyRegistrationConfig,
contractAddresses,
coordinatorConfig,
*batchFulfillmentEnabled,
*nativeOnly,
nodesMap,
uint64(*gasLaneMaxGas),
coordinatorJobSpecConfig,
Expand All @@ -587,6 +599,8 @@ func VRFV2PlusDeployUniverse(e helpers.Environment,
vrfKeyRegistrationConfig model.VRFKeyRegistrationConfig,
contractAddresses model.ContractAddresses,
coordinatorConfig CoordinatorConfigV2Plus,
batchFulfillmentEnabled bool,
nativeOnly bool,
nodesMap map[string]model.Node,
gasLaneMaxGas uint64,
coordinatorJobSpecConfig model.CoordinatorJobSpecConfig,
Expand Down Expand Up @@ -618,12 +632,12 @@ func VRFV2PlusDeployUniverse(e helpers.Environment,
helpers.PanicErr(err)
}

if len(contractAddresses.LinkAddress) == 0 {
if !nativeOnly && len(contractAddresses.LinkAddress) == 0 {
fmt.Println("\nDeploying LINK Token...")
contractAddresses.LinkAddress = helpers.DeployLinkToken(e).String()
}

if len(contractAddresses.LinkEthAddress) == 0 {
if !nativeOnly && len(contractAddresses.LinkEthAddress) == 0 {
fmt.Println("\nDeploying LINK/ETH Feed...")
contractAddresses.LinkEthAddress = helpers.DeployLinkEthFeed(e, contractAddresses.LinkAddress, coordinatorConfig.FallbackWeiPerUnitLink).String()
}
Expand Down Expand Up @@ -715,11 +729,10 @@ func VRFV2PlusDeployUniverse(e helpers.Environment,

formattedVrfV2PlusPrimaryJobSpec := fmt.Sprintf(
jobs.VRFV2PlusJobFormatted,
contractAddresses.CoordinatorAddress, //coordinatorAddress
contractAddresses.BatchCoordinatorAddress, //batchCoordinatorAddress
coordinatorJobSpecConfig.BatchFulfillmentEnabled, //batchFulfillmentEnabled
coordinatorJobSpecConfig.BatchFulfillmentGasMultiplier, //batchFulfillmentGasMultiplier
strings.Join(util.MapToAddressArr(nodesMap[model.VRFPrimaryNodeName].SendingKeys), "\",\""), //fromAddresses
contractAddresses.CoordinatorAddress, //coordinatorAddress
contractAddresses.BatchCoordinatorAddress, //batchCoordinatorAddress
coordinatorJobSpecConfig.BatchFulfillmentEnabled, //batchFulfillmentEnabled
coordinatorJobSpecConfig.BatchFulfillmentGasMultiplier, //batchFulfillmentGasMultiplier
compressedPkHex, //publicKey
coordinatorConfig.MinConfs, //minIncomingConfirmations
e.ChainID, //evmChainID
Expand Down
10 changes: 6 additions & 4 deletions core/scripts/vrfv2plus/testnet/v2plusscripts/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,12 @@ func DeployCoordinator(
coordinator, err := vrf_coordinator_v2_5.NewVRFCoordinatorV25(coordinatorAddress, e.Ec)
helpers.PanicErr(err)

linkTx, err := coordinator.SetLINKAndLINKNativeFeed(e.Owner,
common.HexToAddress(linkAddress), common.HexToAddress(linkEthAddress))
helpers.PanicErr(err)
helpers.ConfirmTXMined(context.Background(), e.Ec, linkTx, e.ChainID)
if linkAddress != "" && linkEthAddress != "" {
linkTx, err := coordinator.SetLINKAndLINKNativeFeed(e.Owner,
common.HexToAddress(linkAddress), common.HexToAddress(linkEthAddress))
helpers.PanicErr(err)
helpers.ConfirmTXMined(context.Background(), e.Ec, linkTx, e.ChainID)
}
return coordinatorAddress
}

Expand Down
11 changes: 11 additions & 0 deletions core/services/pipeline/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -671,3 +671,14 @@ func getJsonNumberValue(value json.Number) (interface{}, error) {

return result, nil
}

func selectBlock(block string) (string, error) {
if block == "" {
return "latest", nil
}
block = strings.ToLower(block)
if block == "pending" || block == "latest" {
return block, nil
}
return "", pkgerrors.Errorf("unsupported block param: %s", block)
}
30 changes: 24 additions & 6 deletions core/services/pipeline/task.estimategas.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/pkg/errors"
"github.com/shopspring/decimal"
"go.uber.org/multierr"
Expand All @@ -28,6 +29,7 @@ type EstimateGasLimitTask struct {
Multiplier string `json:"multiplier"`
Data string `json:"data"`
EVMChainID string `json:"evmChainID" mapstructure:"evmChainID"`
Block string `json:"block"`

specGasLimit *uint32
legacyChains legacyevm.LegacyChainContainer
Expand Down Expand Up @@ -61,6 +63,7 @@ func (t *EstimateGasLimitTask) Run(ctx context.Context, lggr logger.Logger, vars
data BytesParam
multiplier DecimalParam
chainID StringParam
block StringParam
)
err := multierr.Combine(
errors.Wrap(ResolveParam(&fromAddr, From(VarExpr(t.From, vars), utils.ZeroAddress)), "from"),
Expand All @@ -69,6 +72,7 @@ func (t *EstimateGasLimitTask) Run(ctx context.Context, lggr logger.Logger, vars
// Default to 1, i.e. exactly what estimateGas suggests
errors.Wrap(ResolveParam(&multiplier, From(VarExpr(t.Multiplier, vars), NonemptyString(t.Multiplier), decimal.New(1, 0))), "multiplier"),
errors.Wrap(ResolveParam(&chainID, From(VarExpr(t.getEvmChainID(), vars), NonemptyString(t.getEvmChainID()), "")), "evmChainID"),
errors.Wrap(ResolveParam(&block, From(VarExpr(t.Block, vars), t.Block)), "block"),
)
if err != nil {
return Result{Error: err}, runInfo
Expand All @@ -82,18 +86,32 @@ func (t *EstimateGasLimitTask) Run(ctx context.Context, lggr logger.Logger, vars

maximumGasLimit := SelectGasLimit(chain.Config().EVM().GasEstimator(), t.jobType, t.specGasLimit)
to := common.Address(toAddr)
gasLimit, err := chain.Client().EstimateGas(ctx, ethereum.CallMsg{
From: common.Address(fromAddr),
To: &to,
Data: data,
})
var gasLimit hexutil.Uint64
args := map[string]interface{}{
"from": common.Address(fromAddr),
"to": &to,
"input": hexutil.Bytes([]byte(data)),
}

selectedBlock, err := selectBlock(string(block))
if err != nil {
return Result{Error: err}, runInfo
}
err = chain.Client().CallContext(ctx,
&gasLimit,
"eth_estimateGas",
args,
selectedBlock,
)

if err != nil {
// Fallback to the maximum conceivable gas limit
// if we're unable to call estimate gas for whatever reason.
lggr.Warnw("EstimateGas: unable to estimate, fallback to configured limit", "err", err, "fallback", maximumGasLimit)
return Result{Value: maximumGasLimit}, runInfo
}
gasLimitDecimal, err := decimal.NewFromString(strconv.FormatUint(gasLimit, 10))

gasLimitDecimal, err := decimal.NewFromString(strconv.FormatUint(uint64(gasLimit), 10))
if err != nil {
return Result{Error: err}, retryableRunInfo()
}
Expand Down
40 changes: 24 additions & 16 deletions core/services/pipeline/task.eth_call.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import (
"fmt"
"time"

"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
Expand All @@ -33,6 +33,7 @@ type ETHCallTask struct {
GasUnlimited string `json:"gasUnlimited"`
ExtractRevertReason bool `json:"extractRevertReason"`
EVMChainID string `json:"evmChainID" mapstructure:"evmChainID"`
Block string `json:"block"`

specGasLimit *uint32
legacyChains legacyevm.LegacyChainContainer
Expand Down Expand Up @@ -78,6 +79,7 @@ func (t *ETHCallTask) Run(ctx context.Context, lggr logger.Logger, vars Vars, in
gasFeeCap MaybeBigIntParam
gasUnlimited BoolParam
chainID StringParam
block StringParam
)
err = multierr.Combine(
errors.Wrap(ResolveParam(&contractAddr, From(VarExpr(t.Contract, vars), NonemptyString(t.Contract))), "contract"),
Expand All @@ -89,6 +91,7 @@ func (t *ETHCallTask) Run(ctx context.Context, lggr logger.Logger, vars Vars, in
errors.Wrap(ResolveParam(&gasFeeCap, From(VarExpr(t.GasFeeCap, vars), t.GasFeeCap)), "gasFeeCap"),
errors.Wrap(ResolveParam(&chainID, From(VarExpr(t.getEvmChainID(), vars), NonemptyString(t.getEvmChainID()), "")), "evmChainID"),
errors.Wrap(ResolveParam(&gasUnlimited, From(VarExpr(t.GasUnlimited, vars), NonemptyString(t.GasUnlimited), false)), "gasUnlimited"),
errors.Wrap(ResolveParam(&block, From(VarExpr(t.Block, vars), t.Block)), "block"),
)
if err != nil {
return Result{Error: err}, runInfo
Expand All @@ -115,23 +118,29 @@ func (t *ETHCallTask) Run(ctx context.Context, lggr logger.Logger, vars Vars, in
}
}

call := ethereum.CallMsg{
To: (*common.Address)(&contractAddr),
From: (common.Address)(from),
Data: []byte(data),
Gas: uint64(selectedGas),
GasPrice: gasPrice.BigInt(),
GasTipCap: gasTipCap.BigInt(),
GasFeeCap: gasFeeCap.BigInt(),
args := map[string]interface{}{
"from": common.Address(from),
"to": (*common.Address)(&contractAddr),
"input": hexutil.Bytes([]byte(data)),
"gas": hexutil.Uint64(uint64(selectedGas)),
"gasPrice": (*hexutil.Big)(gasPrice.BigInt()),
"maxFeePerGas": (*hexutil.Big)(gasFeeCap.BigInt()),
"maxPriorityFeePerGas": (*hexutil.Big)(gasTipCap.BigInt()),
}

lggr = lggr.With("gas", call.Gas).
With("gasPrice", call.GasPrice).
With("gasTipCap", call.GasTipCap).
With("gasFeeCap", call.GasFeeCap)
selectedBlock, err := selectBlock(string(block))
if err != nil {
return Result{Error: err}, runInfo
}

lggr = lggr.With("gas", uint64(selectedGas)).
With("gasPrice", gasPrice.BigInt()).
With("gasTipCap", gasTipCap.BigInt()).
With("gasFeeCap", gasFeeCap.BigInt())

start := time.Now()
resp, err := chain.Client().CallContract(ctx, call, nil)
var resp hexutil.Bytes
err = chain.Client().CallContext(ctx, &resp, "eth_call", args, selectedBlock)
elapsed := time.Since(start)
if err != nil {
if t.ExtractRevertReason {
Expand All @@ -149,6 +158,5 @@ func (t *ETHCallTask) Run(ctx context.Context, lggr logger.Logger, vars Vars, in
}

promETHCallTime.WithLabelValues(t.DotID()).Set(float64(elapsed))

return Result{Value: resp}, runInfo
return Result{Value: []byte(resp)}, runInfo
}
Loading

0 comments on commit f053dba

Please sign in to comment.