Skip to content

Commit

Permalink
Add report methods
Browse files Browse the repository at this point in the history
  • Loading branch information
rstout committed Jul 16, 2024
1 parent d925d47 commit d62f0b3
Show file tree
Hide file tree
Showing 5 changed files with 220 additions and 75 deletions.
121 changes: 66 additions & 55 deletions commit_rmn_ocb/outcome.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,9 @@ func (p *Plugin) ReportRangesOutcome(
"offRampMaxSeqNum", offRampMaxSeqNum,
"onRampMaxSeqNum", onRampMaxSeqNum,
"chainSelector", chainSel)
continue
} else if offRampMaxSeqNum == onRampMaxSeqNum {
continue
} else {
}

if offRampMaxSeqNum < onRampMaxSeqNum {
chainRange := ChainRange{
ChainSel: chainSel,
SeqNumRange: [2]cciptypes.SeqNum{offRampMaxSeqNum, onRampMaxSeqNum},
Expand All @@ -85,6 +84,7 @@ func (p *Plugin) ReportRangesOutcome(
}
}

// TODO: explain this
sort.Slice(rangesToReport, func(i, j int) bool { return rangesToReport[i].ChainSel < rangesToReport[j].ChainSel })

outcome := CommitPluginOutcome{
Expand All @@ -95,20 +95,55 @@ func (p *Plugin) ReportRangesOutcome(
return outcome.Encode()
}

// TODO: doc
// TODO: doc, break apart
func (p *Plugin) buildReport(
query CommitQuery,
consensusObservation ConsensusObservation,
) (ocr3types.Outcome, error) {
if query.SignedMerkleRoots == nil || len(query.SignedMerkleRoots) == 0 {
// TODO: metrics
return ocr3types.Outcome{}, fmt.Errorf("buildReport: query.SignedMerkleRoots is empty")
verifiedSignedRoots := p.verifySignedRoots(query.SignedMerkleRoots, consensusObservation.MerkleRoots)

chainsToExclude := make([]cciptypes.ChainSelector, 0)
for chain := range verifiedSignedRoots {
if _, exists := consensusObservation.GasPrices[chain]; !exists {
// TODO: metrics
p.log.Warnw(
"did not find a consensus gas price for chain %d, excluding it from the report", chain)
chainsToExclude = append(chainsToExclude, chain)
}
}

observedMerkleRoots := consensusObservation.MerkleRoots
for _, chainSelector := range chainsToExclude {
delete(verifiedSignedRoots, chainSelector)
}

// TODO: token prices validation
// exclude merkle roots if expected token prices don't exist

verifiedSignedRootsArray := make([]SignedMerkleRoot, 0, len(verifiedSignedRoots))
for _, signedRoot := range verifiedSignedRoots {
verifiedSignedRootsArray = append(verifiedSignedRootsArray, signedRoot)
}

// TODO: explain this
sort.Slice(verifiedSignedRootsArray, func(i, j int) bool {
return verifiedSignedRootsArray[i].chain() < verifiedSignedRootsArray[j].chain()
})

return CommitPluginOutcome{
OutcomeType: ReportGenerated,
SignedRootsToReport: verifiedSignedRootsArray,
GasPrices: consensusObservation.GasPricesSortedArray(),
TokenPrices: consensusObservation.TokenPricesSortedArray(),
}.Encode()
}

// TODO: doc, break apart
func (p *Plugin) verifySignedRoots(
rmnSignedRoots []SignedMerkleRoot,
observedRoots map[cciptypes.ChainSelector]MerkleRoot,
) map[cciptypes.ChainSelector]SignedMerkleRoot {
verifiedSignedRoots := make(map[cciptypes.ChainSelector]SignedMerkleRoot)
for _, signedRoot := range query.SignedMerkleRoots {
for _, signedRoot := range rmnSignedRoots {
if err := p.rmn.VerifySignedMerkleRoot(signedRoot); err != nil {
// TODO: metrics
p.log.Warnw("failed to verify signed merkle root",
Expand All @@ -117,30 +152,26 @@ func (p *Plugin) buildReport(
continue
}

if observedMerkleRoot, exists := observedMerkleRoots[signedRoot.chain()]; exists {
// check merkle root equality
if observedMerkleRoot != signedRoot.MerkleRoot {
if observedMerkleRoot, exists := observedRoots[signedRoot.chain()]; exists {
if observedMerkleRoot == signedRoot.MerkleRoot {
verifiedSignedRoots[signedRoot.chain()] = signedRoot
} else {
// TODO: metrics
p.log.Warnw("observed merkle root does not match merkle root received from RMN",
"rmnSignedRoot", signedRoot,
"observedMerkleRoot", observedMerkleRoot)
continue
} else {

}
} else {
// TODO: metrics
p.log.Warnw(
"received a signed merkle root from RMN for a chain, but did not observe a merkle root for "+
"this chain",
"rmnSignedRoot", signedRoot)
continue
}

verifiedSignedRoots[signedRoot.chain()] = signedRoot
}

for chain, observedMerkleRoot := range observedMerkleRoots {
// TODO: explain this
for chain, observedMerkleRoot := range observedRoots {
if _, exists := verifiedSignedRoots[chain]; !exists {
if p.rmn.ChainThreshold(chain) == 0 {
verifiedSignedRoots[chain] = SignedMerkleRoot{
Expand All @@ -156,23 +187,7 @@ func (p *Plugin) buildReport(
}
}

chainsToExclude := make([]cciptypes.ChainSelector, 0)
for chain, _ := range verifiedSignedRoots {
if _, exists := consensusObservation.GasPrices[chain]; !exists {
// TODO: metrics
p.log.Warnw(
"did not find a consensus gas price for chain %d, excluding it from the report", chain)
chainsToExclude = append(chainsToExclude, chain)
}
}

for _, chainSelector := range chainsToExclude {
delete(verifiedSignedRoots, chainSelector)
}

// TODO: token prices validation

return nil, nil
return verifiedSignedRoots
}

// TODO: doc
Expand All @@ -193,21 +208,21 @@ func (p *Plugin) checkForReportTransmission(

if offRampUpdated {
return CommitPluginOutcome{
OutcomeType: CommitPluginOutcomeType(ReportGenerated),
OutcomeType: ReportGenerated,
}.Encode()
} else {
if previousOutcome.ReportTransmissionCheckAttempts+1 >= p.cfg.MaxReportTransmissionCheckAttempts {
return CommitPluginOutcome{
OutcomeType: CommitPluginOutcomeType(ReportNotTransmitted),
}.Encode()
} else {
return CommitPluginOutcome{
OutcomeType: CommitPluginOutcomeType(ReportNotYetTransmitted),
OffRampMaxSeqNums: previousOutcome.OffRampMaxSeqNums,
ReportTransmissionCheckAttempts: previousOutcome.ReportTransmissionCheckAttempts + 1,
}.Encode()
}
}

if previousOutcome.ReportTransmissionCheckAttempts+1 >= p.cfg.MaxReportTransmissionCheckAttempts {
return CommitPluginOutcome{
OutcomeType: ReportNotTransmitted,
}.Encode()
}

return CommitPluginOutcome{
OutcomeType: ReportNotYetTransmitted,
OffRampMaxSeqNums: previousOutcome.OffRampMaxSeqNums,
ReportTransmissionCheckAttempts: previousOutcome.ReportTransmissionCheckAttempts + 1,
}.Encode()
}

// getConsensusObservation TODO: doc
Expand Down Expand Up @@ -401,11 +416,7 @@ func mostFrequentElem[T comparable](elems []T) (T, int) {
func counts[T comparable](elems []T) map[T]int {
m := make(map[T]int)
for _, elem := range elems {
if _, exists := m[elem]; exists {
m[elem]++
} else {
m[elem] = 1
}
m[elem]++
}

return m
Expand Down
2 changes: 1 addition & 1 deletion commit_rmn_ocb/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func NewPlugin(
// SelectingRangesForReport doesn't depend on the previous outcome, explain how this is resilient (to being unable
// to parse previous outcome)
func (p *Plugin) decodeOutcome(outcome ocr3types.Outcome) (CommitPluginOutcome, CommitPluginState) {
if outcome == nil || len(outcome) == 0 {
if len(outcome) == 0 {
return CommitPluginOutcome{}, SelectingRangesForReport
}

Expand Down
82 changes: 82 additions & 0 deletions commit_rmn_ocb/report.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package commitrmnocb

import (
"context"
"fmt"

"github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types"
)

// Reports TODO: doc, metrics
func (p *Plugin) Reports(seqNr uint64, outcomeBytes ocr3types.Outcome) ([]ocr3types.ReportWithInfo[[]byte], error) {
outcome, err := DecodeCommitPluginOutcome(outcomeBytes)
if err != nil {
// TODO: metrics
p.log.Errorw("failed to decode CommitPluginOutcome", "outcomeBytes", outcomeBytes, "err", err)
return nil, fmt.Errorf("failed to decode CommitPluginOutcome: %w", err)
}

report := CommitPluginReport{
SignedRoots: outcome.SignedRootsToReport,
GasPrices: outcome.GasPrices,
TokenPrices: outcome.TokenPrices,
}

// TODO: log, metrics

encodedReport, err := report.Encode()
if err != nil {
return nil, fmt.Errorf("encode commit plugin report: %w", err)
}

return []ocr3types.ReportWithInfo[[]byte]{{Report: encodedReport, Info: nil}}, nil
}

func (p *Plugin) ShouldAcceptAttestedReport(
_ context.Context, _ uint64, r ocr3types.ReportWithInfo[[]byte],
) (bool, error) {
decodedReport, err := DecodeCommitPluginReport(r.Report)
if err != nil {
// TODO: metrics
p.log.Errorw("failed to decode CommitPluginOutcome", "outcomeBytes", r.Report, "err", err)
return false, err
}

if decodedReport.IsEmpty() {
// TODO: metrics
p.log.Warnf("found an empty report")
return false, nil
}

return true, nil
}

func (p *Plugin) ShouldTransmitAcceptedReport(
_ context.Context, _ uint64, r ocr3types.ReportWithInfo[[]byte],
) (bool, error) {
destChainSupported, err := p.supportsDestChain(p.nodeID)
if err != nil {
return false, fmt.Errorf("call to supportsDestChain failed: %w", err)
}
if !destChainSupported {
p.log.Debugw("oracle does not support dest chain, skipping report transmission")
return false, nil
}

decodedReport, err := DecodeCommitPluginReport(r.Report)
if err != nil {
return false, fmt.Errorf("decode commit plugin report: %w", err)
}

// TODO: metrics
p.log.Debugw("transmitting report",
"signedRoots", len(decodedReport.SignedRoots),
"tokenPrices", len(decodedReport.TokenPrices),
"gasPriceUpdates", len(decodedReport.GasPrices),
)
return true, nil
}

func (p *Plugin) Close() error {
return nil
}
Loading

0 comments on commit d62f0b3

Please sign in to comment.