Skip to content

Commit

Permalink
Refactor WriteTarget to use ChainWriter
Browse files Browse the repository at this point in the history
  • Loading branch information
archseer committed May 29, 2024
1 parent 641cd7a commit 86ed2b2
Showing 1 changed file with 42 additions and 80 deletions.
122 changes: 42 additions & 80 deletions core/capabilities/targets/write_target.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,21 @@ import (
"context"
"encoding/json"
"fmt"
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/google/uuid"

chainselectors "github.com/smartcontractkit/chain-selectors"

"github.com/smartcontractkit/chainlink-common/pkg/capabilities"
"github.com/smartcontractkit/chainlink-common/pkg/loop"
commontypes "github.com/smartcontractkit/chainlink-common/pkg/types"
"github.com/smartcontractkit/chainlink-common/pkg/values"
txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr"
evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr"
evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types"
"github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/forwarder"
"github.com/smartcontractkit/chainlink/v2/core/logger"
"github.com/smartcontractkit/chainlink/v2/core/services/relay/evm"
relayevmtypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types"
)

Expand All @@ -28,15 +27,15 @@ var (
)

type WriteTarget struct {
relayer loop.Relayer
config evmconfig.ChainScopedConfig
cr commontypes.ContractReader
cw EvmChainWriter
relayer loop.Relayer
cr commontypes.ContractReader
cw commontypes.ChainWriter
forwarderAddress string
capabilities.CapabilityInfo
lggr logger.Logger
}

func NewWriteTarget(ctx context.Context, relayer loop.Relayer, chain legacyevm.Chain, lggr logger.Logger) (*WriteTarget, error) {
func NewEvmWriteTarget(ctx context.Context, relayer loop.Relayer, chain legacyevm.Chain, lggr logger.Logger) (*WriteTarget, error) {
// generate ID based on chain selector
name := fmt.Sprintf("write_%v", chain.ID())
chainName, err := chainselectors.NameFromChainId(chain.ID().Uint64())
Expand All @@ -53,7 +52,7 @@ func NewWriteTarget(ctx context.Context, relayer loop.Relayer, chain legacyevm.C
)

// EVM-specific init
config := chain.Config()
config := chain.Config().EVM().ChainWriter()

// Initialize a reader to check whether a value was already transmitted on chain
contractReaderConfigEncoded, err := json.Marshal(relayevmtypes.ChainReaderConfig{
Expand All @@ -75,18 +74,37 @@ func NewWriteTarget(ctx context.Context, relayer loop.Relayer, chain legacyevm.C
if err != nil {
return nil, err
}
cr.Bind(ctx, []commontypes.BoundContract{{
Address: config.ForwarderAddress().String(),
Name: "forwarder",
}})

cw := EvmChainWriter{
chain,
logger := lggr.Named("WriteTarget")

chainWriterConfig := relayevmtypes.ChainWriterConfig{
Contracts: map[string]relayevmtypes.ChainWriter{
"forwarder": {
ContractABI: forwarder.KeystoneForwarderABI,
Configs: map[string]*relayevmtypes.ChainWriterDefinition{
"report": {
ChainSpecificName: "report",
Checker: "simulate",
FromAddress: config.FromAddress().Address(),
GasLimit: 200_000,
},
},
},
},
}
cw := evm.NewChainWriterService(logger, chain.Client(), chain.TxManager(), chainWriterConfig)

return &WriteTarget{
relayer,
config,
cr,
cw,
config.ForwarderAddress().String(),
info,
lggr.Named("WriteTarget"),
logger,
}, nil
}

Expand Down Expand Up @@ -118,45 +136,8 @@ func success() <-chan capabilities.CapabilityResponse {
return callback
}

type EvmChainWriter struct {
chain legacyevm.Chain
}

func (cw *EvmChainWriter) CreateTransaction(ctx context.Context, reqConfig EvmConfig) (tx txmgr.Tx, err error) {
txm := cw.chain.TxManager()
config := cw.chain.Config().EVM().ChainWriter()

// construct forwarder payload
calldata, err := forwardABI.Pack("report", common.HexToAddress(reqConfig.Address), inputs.Report, inputs.Signatures)
if err != nil {
return tx, err
}

txMeta := &txmgr.TxMeta{
// FwdrDestAddress could also be set for better logging but it's used for various purposes around Operator Forwarders
// WorkflowExecutionID: &request.Metadata.WorkflowExecutionID, // TODO: remove?
}
req := txmgr.TxRequest{
FromAddress: config.FromAddress().Address(),
ToAddress: config.ForwarderAddress().Address(),
EncodedPayload: calldata,
FeeLimit: uint64(defaultGasLimit),
Meta: txMeta,
Strategy: txmgrcommon.NewSendEveryStrategy(),
Checker: txmgr.TransmitCheckerSpec{
CheckerType: txmgr.TransmitCheckerTypeSimulate,
},
}
return txm.CreateTransaction(ctx, req)
}

var forwardABI = evmtypes.MustGetABI(forwarder.KeystoneForwarderMetaData.ABI)

const defaultGasLimit = 200000

func (cap *WriteTarget) Execute(ctx context.Context, request capabilities.CapabilityRequest) (<-chan capabilities.CapabilityResponse, error) {
cap.lggr.Debugw("Execute", "request", request)
// TODO: idempotency

reqConfig, err := parseConfig(request.Config)
if err != nil {
Expand All @@ -169,27 +150,12 @@ func (cap *WriteTarget) Execute(ctx context.Context, request capabilities.Capabi
if inputs.Report == nil {
// We received any empty report -- this means we should skip transmission.
cap.lggr.Debugw("Skipping empty report", "request", request)
callback := make(chan capabilities.CapabilityResponse)
go func() {
// TODO: cast tx.Error to Err (or Value to Value?)
callback <- capabilities.CapabilityResponse{
Value: nil,
Err: nil,
}
close(callback)
}()
return callback, nil
return success(), nil
}

// TODO: validate encoded report is prefixed with workflowID and executionID that match the request meta

config := cap.config.EVM().ChainWriter()

// Check whether value was already transmitted on chain
cap.cr.Bind(ctx, []commontypes.BoundContract{{
Address: config.ForwarderAddress().String(),
Name: "forwarder",
}})
queryInputs := struct {
Receiver string
WorkflowExecutionID []byte
Expand All @@ -206,22 +172,18 @@ func (cap *WriteTarget) Execute(ctx context.Context, request capabilities.Capabi
return success(), nil
}

tx, err := cap.cw.CreateTransaction(ctx, reqConfig)
txID, err := uuid.NewUUID() // TODO(archseer): it seems odd that CW expects us to generate an ID, rather than return one
if err != nil {
return nil, err
}
cap.lggr.Debugw("Transaction submitted", "request", request, "transaction", tx)

callback := make(chan capabilities.CapabilityResponse)
go func() {
// TODO: cast tx.Error to Err (or Value to Value?)
callback <- capabilities.CapabilityResponse{
Value: nil,
Err: nil,
}
close(callback)
}()
return callback, nil
args := []any{common.HexToAddress(reqConfig.Address), inputs.Report, inputs.Signatures}
meta := commontypes.TxMeta{WorkflowExecutionID: &request.Metadata.WorkflowExecutionID}
value := big.NewInt(0)
if err := cap.cw.SubmitTransaction(ctx, "forwarder", "report", args, txID, cap.forwarderAddress, &meta, *value); err != nil {
return nil, err
}
cap.lggr.Debugw("Transaction submitted", "request", request, "transaction", txID)
return success(), nil
}

func (cap *WriteTarget) RegisterToWorkflow(ctx context.Context, request capabilities.RegisterToWorkflowRequest) error {
Expand Down

0 comments on commit 86ed2b2

Please sign in to comment.