Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: multichain ocr3 contract transmitter #11672

Closed
wants to merge 16 commits into from
110 changes: 110 additions & 0 deletions core/services/relay/evm/contract_transmitter_ocr3.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package evm

import (
"context"
"encoding/binary"
"encoding/hex"
"fmt"

"github.com/ethereum/go-ethereum/accounts/abi"
gethcommon "github.com/ethereum/go-ethereum/common"
"github.com/pkg/errors"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller"
"github.com/smartcontractkit/chainlink/v2/core/logger"
"github.com/smartcontractkit/libocr/offchainreporting2plus/chains/evmutil"
"github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types"
"github.com/smartcontractkit/libocr/offchainreporting2plus/types"
)

var _ ocr3types.ContractTransmitter[any] = &contractTransmitterOCR3[any]{}

type contractTransmitterOCR3[RI any] struct {
contractAddress gethcommon.Address
contractABI abi.ABI
transmitter Transmitter
transmittedEventSig gethcommon.Hash
lp logpoller.LogPoller
lggr logger.Logger
reportToEvmTxMeta ReportToEthMetadata
}

func NewOCR3ContractTransmitter[RI any](
address gethcommon.Address,
contractABI abi.ABI,
transmitter Transmitter,
lp logpoller.LogPoller,
lggr logger.Logger,
reportToEvmTxMeta ReportToEthMetadata,
) (*contractTransmitterOCR3[RI], error) {
transmitted, ok := contractABI.Events["Transmitted"]
if !ok {
return nil, fmt.Errorf("abi missing Transmitted event")
}

err := lp.RegisterFilter(logpoller.Filter{
Copy link
Contributor

@dimkouv dimkouv Jan 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will never be Unregistered?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do unregisters need to happen?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Name: transmitterFilterName(address),
EventSigs: []gethcommon.Hash{transmitted.ID},
Addresses: []gethcommon.Address{address},
})
if err != nil {
return nil, err
makramkd marked this conversation as resolved.
Show resolved Hide resolved
}
if reportToEvmTxMeta == nil {
reportToEvmTxMeta = reportToEvmTxMetaNoop
}
return &contractTransmitterOCR3[RI]{
contractAddress: address,
contractABI: contractABI,
transmitter: transmitter,
transmittedEventSig: transmitted.ID,
lp: lp,
lggr: lggr,
reportToEvmTxMeta: reportToEvmTxMeta,
}, nil
}

// FromAccount implements ocr3types.ContractTransmitter.
func (c *contractTransmitterOCR3[RI]) FromAccount() (types.Account, error) {
return types.Account(c.transmitter.FromAddress().Hex()), nil
}

// Transmit implements ocr3types.ContractTransmitter.
func (c *contractTransmitterOCR3[RI]) Transmit(ctx context.Context, configDigest types.ConfigDigest, seqNum uint64, rwi ocr3types.ReportWithInfo[RI], sigs []types.AttributedOnchainSignature) error {
var rs [][32]byte
var ss [][32]byte
var vs [32]byte
if len(sigs) > 32 {
return errors.New("too many signatures, maximum is 32")
}
for i, as := range sigs {
r, s, v, err := evmutil.SplitSignature(as.Signature)
if err != nil {
panic("eventTransmit(ev): error in SplitSignature")
dimkouv marked this conversation as resolved.
Show resolved Hide resolved
}
rs = append(rs, r)
ss = append(ss, s)
vs[i] = v
}

// report ctx for OCR3 consists of the following
// reportContext[0]: ConfigDigest
// reportContext[1]: 24 byte padding, 8 byte sequence number
// reportContext[2]: unused
var rawReportCtx [3][32]byte
copy(rawReportCtx[0][:], configDigest[:])
binary.BigEndian.PutUint64(rawReportCtx[1][24:], seqNum)
makramkd marked this conversation as resolved.
Show resolved Hide resolved

txMeta, err := c.reportToEvmTxMeta(rwi.Report)
if err != nil {
c.lggr.Warnw("failed to generate tx metadata for report", "err", err)
}

c.lggr.Debugw("Transmitting report", "report", hex.EncodeToString(rwi.Report), "rawReportCtx", rawReportCtx, "contractAddress", c.contractAddress, "txMeta", txMeta)

payload, err := c.contractABI.Pack("transmit", rawReportCtx, []byte(rwi.Report), rs, ss, vs)
if err != nil {
return errors.Wrapf(err, "abi.Pack failed with args: (%v, %s, %v, %v, %v)", rawReportCtx, hex.EncodeToString(rwi.Report), rs, ss, vs)
makramkd marked this conversation as resolved.
Show resolved Hide resolved
}

return errors.Wrap(c.transmitter.CreateEthTransaction(ctx, c.contractAddress, payload, txMeta), "failed to send Eth transaction")
}
56 changes: 56 additions & 0 deletions core/services/relay/evm/multichain_transmitter_ocr3.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package evm

import (
"context"
"fmt"
"strings"

"github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller"
"github.com/smartcontractkit/chainlink/v2/core/logger"
"github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types"
"github.com/smartcontractkit/libocr/offchainreporting2plus/types"
)

type MultichainMeta interface {
GetDestinationChainID() string
}

type multichainTransmitterOCR3[RI MultichainMeta] struct {
transmitters map[string]ocr3types.ContractTransmitter[RI]
lp logpoller.LogPoller
lggr logger.Logger
}

func NewMultichainTransmitterOCR3[RI MultichainMeta](
transmitters map[string]ocr3types.ContractTransmitter[RI],
lp logpoller.LogPoller,
lggr logger.Logger,
) (*multichainTransmitterOCR3[RI], error) {
return &multichainTransmitterOCR3[RI]{
transmitters: transmitters,
lp: lp,
lggr: lggr,
}, nil
}

// FromAccount implements ocr3types.ContractTransmitter.
func (m *multichainTransmitterOCR3[RI]) FromAccount() (types.Account, error) {
makramkd marked this conversation as resolved.
Show resolved Hide resolved
var accounts []string
for _, t := range m.transmitters {
account, err := t.FromAccount()
if err != nil {
return "", err
}
accounts = append(accounts, string(account))
}
return types.Account(strings.Join(accounts, ",")), nil
}

// Transmit implements ocr3types.ContractTransmitter.
func (m *multichainTransmitterOCR3[RI]) Transmit(ctx context.Context, configDigest types.ConfigDigest, seqNr uint64, rwi ocr3types.ReportWithInfo[RI], sigs []types.AttributedOnchainSignature) error {
transmitter, ok := m.transmitters[rwi.Info.GetDestinationChainID()]
if !ok {
return fmt.Errorf("no transmitter for chain %s", rwi.Info.GetDestinationChainID())
}
return transmitter.Transmit(ctx, configDigest, seqNr, rwi, sigs)
}
Loading