Skip to content

Commit

Permalink
Test for RWA
Browse files Browse the repository at this point in the history
  • Loading branch information
samsondav committed Dec 4, 2024
1 parent 48f3df2 commit d074b92
Show file tree
Hide file tree
Showing 3 changed files with 236 additions and 23 deletions.
17 changes: 13 additions & 4 deletions core/services/llo/evm/report_codec_evm_abi_encode_unpacked.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ func (a ABIEncoder) Encode(value llo.StreamValue) ([]byte, error) {
// TODO: How to handle overflow?

// packBigInt encodes a *big.Int as a byte slice according to the given ABI type
func packBigInt(val *big.Int, t string) ([]byte, error) {
func packBigInt(val *big.Int, t string) (b []byte, err error) {
abiType, err := abi.NewType(t, "", []abi.ArgumentMarshaling{})
if err != nil {
return nil, fmt.Errorf("invalid ABI type %q; %w", abiType, err)
Expand All @@ -253,10 +253,19 @@ func packBigInt(val *big.Int, t string) ([]byte, error) {
},
}

packedData, err := arguments.Pack(val)
switch t {
case "uint32":
// packing uint32 expects uint32 as argument
if val.BitLen() > 32 {
return nil, fmt.Errorf("value %v is too large for uint32", val)
}
b, err = arguments.Pack(uint32(val.Uint64()))
default:
b, err = arguments.Pack(val)
}
if err != nil {
return nil, fmt.Errorf("failed to pack value %v as %q: %w", val, abiType, err)
return nil, fmt.Errorf("failed to pack value %v as %q: %w", val, t, err)
}

return packedData, nil
return b, nil
}
156 changes: 137 additions & 19 deletions core/services/llo/evm/report_codec_evm_abi_encode_unpacked_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,25 @@ func TestReportCodecEVMABIEncodeUnpacked_Encode_properties(t *testing.T) {

properties := gopter.NewProperties(nil)

linkQuoteStreamID := llotypes.StreamID(rand.Uint32())
ethQuoteStreamID := llotypes.StreamID(rand.Uint32())
dexBasedAssetDecimalStreamID := llotypes.StreamID(rand.Uint32())
baseMarketDepthStreamID := llotypes.StreamID(rand.Uint32())
quoteMarketDepthStreamID := llotypes.StreamID(rand.Uint32())
marketStatusStreamID := llotypes.StreamID(rand.Uint32())

t.Run("DEX-based asset schema example", func(t *testing.T) {
expectedDEXBasedAssetSchema := abi.Arguments([]abi.Argument{
{Name: "feedId", Type: mustNewABIType("bytes32")},
{Name: "validFromTimestamp", Type: mustNewABIType("uint32")},
{Name: "observationsTimestamp", Type: mustNewABIType("uint32")},
{Name: "nativeFee", Type: mustNewABIType("uint192")},
{Name: "linkFee", Type: mustNewABIType("uint192")},
{Name: "expiresAt", Type: mustNewABIType("uint32")},
{Name: "price", Type: mustNewABIType("int192")},
{Name: "baseMarketDepth", Type: mustNewABIType("int192")},
{Name: "quoteMarketDepth", Type: mustNewABIType("int192")},
})
runTest := func(sampleFeedID common.Hash, sampleObservationsTimestamp, sampleValidAfterSeconds, sampleExpirationWindow uint32, priceMultiplier, marketDepthMultiplier *ubig.Big, sampleBaseUSDFee, sampleLinkBenchmarkPrice, sampleNativeBenchmarkPrice, sampleDexBasedAssetPrice, sampleBaseMarketDepth, sampleQuoteMarketDepth decimal.Decimal) bool {
report := llo.Report{
ConfigDigest: types.ConfigDigest{0x01},
Expand All @@ -96,12 +114,6 @@ func TestReportCodecEVMABIEncodeUnpacked_Encode_properties(t *testing.T) {
Specimen: false,
}

linkQuoteStreamID := llotypes.StreamID(rand.Uint32())
ethQuoteStreamID := llotypes.StreamID(rand.Uint32())
dexBasedAssetDecimalStreamID := llotypes.StreamID(rand.Uint32())
baseMarketDepthStreamID := llotypes.StreamID(rand.Uint32())
quoteMarketDepthStreamID := llotypes.StreamID(rand.Uint32())

opts := ReportFormatEVMABIEncodeOpts{
BaseUSDFee: sampleBaseUSDFee,
ExpirationWindow: sampleExpirationWindow,
Expand Down Expand Up @@ -161,22 +173,10 @@ func TestReportCodecEVMABIEncodeUnpacked_Encode_properties(t *testing.T) {
encoded, err := codec.Encode(ctx, report, cd)
require.NoError(t, err)

expectedDEXBasedAssetSchema := abi.Arguments([]abi.Argument{
{Name: "feedId", Type: mustNewABIType("bytes32")},
{Name: "validFromTimestamp", Type: mustNewABIType("uint32")},
{Name: "observationsTimestamp", Type: mustNewABIType("uint32")},
{Name: "nativeFee", Type: mustNewABIType("uint192")},
{Name: "linkFee", Type: mustNewABIType("uint192")},
{Name: "expiresAt", Type: mustNewABIType("uint32")},
{Name: "price", Type: mustNewABIType("int192")},
{Name: "baseMarketDepth", Type: mustNewABIType("int192")},
{Name: "quoteMarketDepth", Type: mustNewABIType("int192")},
})

values, err := expectedDEXBasedAssetSchema.Unpack(encoded)
require.NoError(t, err)

require.Len(t, values, 9)
require.Len(t, values, len(expectedDEXBasedAssetSchema))

expectedLinkFee := CalculateFee(sampleLinkBenchmarkPrice, sampleBaseUSDFee)
expectedNativeFee := CalculateFee(sampleNativeBenchmarkPrice, sampleBaseUSDFee)
Expand Down Expand Up @@ -212,6 +212,118 @@ func TestReportCodecEVMABIEncodeUnpacked_Encode_properties(t *testing.T) {

properties.TestingRun(t)
})

t.Run("Market status schema", func(t *testing.T) {
expectedRWASchema := abi.Arguments([]abi.Argument{
{Name: "feedId", Type: mustNewABIType("bytes32")},
{Name: "validFromTimestamp", Type: mustNewABIType("uint32")},
{Name: "observationsTimestamp", Type: mustNewABIType("uint32")},
{Name: "nativeFee", Type: mustNewABIType("uint192")},
{Name: "linkFee", Type: mustNewABIType("uint192")},
{Name: "expiresAt", Type: mustNewABIType("uint32")},
{Name: "marketStatus", Type: mustNewABIType("uint32")},
})

runTest := func(sampleFeedID common.Hash, sampleObservationsTimestamp, sampleValidAfterSeconds, sampleExpirationWindow uint32, sampleBaseUSDFee, sampleLinkBenchmarkPrice, sampleNativeBenchmarkPrice, sampleMarketStatus decimal.Decimal) bool {
report := llo.Report{
ConfigDigest: types.ConfigDigest{0x01},
SeqNr: 0x02,
ChannelID: llotypes.ChannelID(0x03),
ValidAfterSeconds: sampleValidAfterSeconds,
ObservationTimestampSeconds: sampleObservationsTimestamp,
Values: []llo.StreamValue{
&llo.Quote{Bid: decimal.NewFromFloat(6.1), Benchmark: sampleLinkBenchmarkPrice, Ask: decimal.NewFromFloat(8.2332)}, // Link price
&llo.Quote{Bid: decimal.NewFromFloat(9.4), Benchmark: sampleNativeBenchmarkPrice, Ask: decimal.NewFromFloat(11.33)}, // Native price
llo.ToDecimal(sampleMarketStatus), // DEX-based asset price
},
Specimen: false,
}

linkQuoteStreamID := llotypes.StreamID(rand.Uint32())
ethQuoteStreamID := llotypes.StreamID(rand.Uint32())
dexBasedAssetDecimalStreamID := llotypes.StreamID(rand.Uint32())
baseMarketDepthStreamID := llotypes.StreamID(rand.Uint32())
quoteMarketDepthStreamID := llotypes.StreamID(rand.Uint32())

opts := ReportFormatEVMABIEncodeOpts{
BaseUSDFee: sampleBaseUSDFee,
ExpirationWindow: sampleExpirationWindow,
FeedID: sampleFeedID,
ABI: []ABIEncoder{
// market status
ABIEncoder{
StreamID: marketStatusStreamID,
Type: "uint32",
},
},
}
serializedOpts, err := opts.Encode()
require.NoError(t, err)

cd := llotypes.ChannelDefinition{
// ReportFormat: llotypes.ReportFormatEVMABIEncodeUnpacked,
ReportFormat: llotypes.ReportFormat(4), // FIXME: When chainlink-common is fixed
Streams: []llotypes.Stream{
{
StreamID: linkQuoteStreamID,
Aggregator: llotypes.AggregatorMedian,
},
{
StreamID: ethQuoteStreamID,
Aggregator: llotypes.AggregatorMedian,
},
{
StreamID: dexBasedAssetDecimalStreamID,
Aggregator: llotypes.AggregatorQuote,
},
{
StreamID: baseMarketDepthStreamID,
Aggregator: llotypes.AggregatorMedian,
},
{
StreamID: quoteMarketDepthStreamID,
Aggregator: llotypes.AggregatorMedian,
},
},
Opts: serializedOpts,
}

encoded, err := codec.Encode(ctx, report, cd)
require.NoError(t, err)

values, err := expectedRWASchema.Unpack(encoded)
require.NoError(t, err)

require.Len(t, values, len(expectedRWASchema))

expectedLinkFee := CalculateFee(sampleLinkBenchmarkPrice, sampleBaseUSDFee)
expectedNativeFee := CalculateFee(sampleNativeBenchmarkPrice, sampleBaseUSDFee)

return AllTrue([]bool{
assert.Equal(t, sampleFeedID, (common.Hash)(values[0].([32]byte))), // feedId
assert.Equal(t, uint32(sampleValidAfterSeconds+1), values[1].(uint32)), // validFromTimestamp
assert.Equal(t, sampleObservationsTimestamp, values[2].(uint32)), // observationsTimestamp
assert.Equal(t, expectedLinkFee.String(), values[3].(*big.Int).String()), // linkFee
assert.Equal(t, expectedNativeFee.String(), values[4].(*big.Int).String()), // nativeFee
assert.Equal(t, uint32(sampleObservationsTimestamp+sampleExpirationWindow), values[5].(uint32)), // expiresAt
assert.Equal(t, uint32(sampleMarketStatus.BigInt().Int64()), values[6].(uint32)), // market status
})
}

properties.Property("Encodes values", prop.ForAll(
runTest,
genFeedID(),
genObservationsTimestamp(),
genValidAfterSeconds(),
genExpirationWindow(),
genBaseUSDFee(),
genLinkBenchmarkPrice(),
genNativeBenchmarkPrice(),
genMarketStatus(),
))

properties.TestingRun(t)
})
}

func genFeedID() gopter.Gen {
Expand Down Expand Up @@ -276,6 +388,12 @@ func genQuoteMarketDepth() gopter.Gen {
return genDecimal()
}

func genMarketStatus() gopter.Gen {
return gen.UInt32().Map(func(i uint32) decimal.Decimal {
return decimal.NewFromInt(int64(i))
})
}

func mustNewABIType(t string) abi.Type {
result, err := abi.NewType(t, "", []abi.ArgumentMarshaling{})
if err != nil {
Expand Down
86 changes: 86 additions & 0 deletions core/services/ocr2/plugins/llo/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,8 @@ func TestIntegration_LLO(t *testing.T) {
bootstrapNode := Node{App: appBootstrap, KeyBundle: bootstrapKb}

t.Run("using legacy verifier configuration contract, produces reports in v0.3 format", func(t *testing.T) {
t.Parallel()

reqs := make(chan request, 100000)
serverKey := csakey.MustNewV2XXXTestingOnly(big.NewInt(-1))
serverPubKey := serverKey.PublicKey
Expand Down Expand Up @@ -549,7 +551,91 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi
})
})

t.Run("generates reports using ReportFormatEVMAbiEncodeUnpacked format", func(t *testing.T) {
t.Parallel()

t.Skip()
reqs := make(chan request, 100000)
serverKey := csakey.MustNewV2XXXTestingOnly(big.NewInt(-2))
serverPubKey := serverKey.PublicKey
srv := NewMercuryServer(t, ed25519.PrivateKey(serverKey.Raw()), reqs)

serverURL := startMercuryServer(t, srv, clientPubKeys)

donID := uint32(888333)
streams := []Stream{ethStream, linkStream}
streamMap := make(map[uint32]Stream)
for _, strm := range streams {
streamMap[strm.id] = strm
}

// Setup oracle nodes
oracles, nodes := setupNodes(t, nNodes, backend, clientCSAKeys, streams)

chainID := testutils.SimulatedChainID
relayType := "evm"
relayConfig := fmt.Sprintf(`
chainID = "%s"
fromBlock = %d
lloDonID = %d
lloConfigMode = "bluegreen"
`, chainID, fromBlock, donID)
addBootstrapJob(t, bootstrapNode, configuratorAddress, "job-3", relayType, relayConfig)

// Channel definitions
channelDefinitions := llotypes.ChannelDefinitions{
1: {
ReportFormat: llotypes.ReportFormatJSON,
Streams: []llotypes.Stream{
{
StreamID: ethStreamID,
Aggregator: llotypes.AggregatorMedian,
},
},
},
}
url, sha := newChannelDefinitionsServer(t, channelDefinitions)

// Set channel definitions
_, err := configStore.SetChannelDefinitions(steve, donID, url, sha)
require.NoError(t, err)
backend.Commit()

pluginConfig := fmt.Sprintf(`servers = { "%s" = "%x" }
donID = %d
channelDefinitionsContractAddress = "0x%x"
channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, configStoreAddress, fromBlock)
addOCRJobsEVMPremiumLegacy(t, streams, serverPubKey, serverURL, configuratorAddress, bootstrapPeerID, bootstrapNodePort, nodes, configStoreAddress, clientPubKeys, pluginConfig, relayType, relayConfig)

var blueDigest ocr2types.ConfigDigest

allReports := make(map[types.ConfigDigest][]datastreamsllo.Report)
{
// Set config on configurator
blueDigest = setProductionConfig(
t, donID, steve, backend, configurator, configuratorAddress, nodes, oracles,
)

// NOTE: Wait until blue produces a report

for req := range reqs {
_, _, r, _, err := (datastreamsllo.JSONReportCodec{}).UnpackDecode(req.req.Payload)
require.NoError(t, err)

allReports[r.ConfigDigest] = append(allReports[r.ConfigDigest], r)

assert.Equal(t, blueDigest, r.ConfigDigest)
assert.False(t, r.Specimen)
assert.Len(t, r.Values, 1)
assert.Equal(t, "2976.39", r.Values[0].(*datastreamsllo.Decimal).String())
break
}
}
})

t.Run("Blue/Green lifecycle (using JSON report format)", func(t *testing.T) {
t.Parallel()

reqs := make(chan request, 100000)
serverKey := csakey.MustNewV2XXXTestingOnly(big.NewInt(-2))
serverPubKey := serverKey.PublicKey
Expand Down

0 comments on commit d074b92

Please sign in to comment.