Skip to content

Commit

Permalink
Blue/green deployments for LLO; code cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
samsondav committed Oct 10, 2024
1 parent 8ddab7e commit 42005e4
Show file tree
Hide file tree
Showing 29 changed files with 886 additions and 316 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ toolchain go1.22.5
require (
github.com/hashicorp/go-plugin v1.6.2-0.20240829161738-06afb6d7ae99
github.com/shopspring/decimal v1.4.0
github.com/smartcontractkit/chainlink-common v0.3.0
github.com/smartcontractkit/chainlink-common v0.3.1-0.20241010121129-90d73545782e
github.com/smartcontractkit/libocr v0.0.0-20241007185508-adbe57025f12
github.com/stretchr/testify v1.9.0
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,8 @@ github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6Ng
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/smartcontractkit/chainlink-common v0.3.0 h1:mUXHBzzw2qPKyw6gPAC8JhO+ryT8maY+rBi9NFtqEy0=
github.com/smartcontractkit/chainlink-common v0.3.0/go.mod h1:tsGgeEJc5SUSlfVGSX0wR0EkRU3pM58D6SKF97V68ko=
github.com/smartcontractkit/chainlink-common v0.3.1-0.20241010121129-90d73545782e h1:6D1uTPgyTAaXGeO7Owu96cuOzQB8xnCMkjFKrjtHhY0=
github.com/smartcontractkit/chainlink-common v0.3.1-0.20241010121129-90d73545782e/go.mod h1:tsGgeEJc5SUSlfVGSX0wR0EkRU3pM58D6SKF97V68ko=
github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 h1:12ijqMM9tvYVEm+nR826WsrNi6zCKpwBhuApq127wHs=
github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7/go.mod h1:FX7/bVdoep147QQhsOPkYsPEXhGZjeYx6lBSaSXtZOA=
github.com/smartcontractkit/libocr v0.0.0-20241007185508-adbe57025f12 h1:NzZGjaqez21I3DU7objl3xExTH4fxYvzTqar8DC6360=
Expand Down
2 changes: 1 addition & 1 deletion llo/channel_definitions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func Test_VerifyChannelDefinitions(t *testing.T) {
channelDefs[i] = llotypes.ChannelDefinition{}
}
err := VerifyChannelDefinitions(channelDefs)
assert.EqualError(t, err, "too many channels, got: 10001/10000")
assert.EqualError(t, err, "too many channels, got: 2001/2000")
})

t.Run("fails for channel with no streams", func(t *testing.T) {
Expand Down
60 changes: 60 additions & 0 deletions llo/json_report_codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"

"github.com/smartcontractkit/libocr/offchainreporting2/types"
ocr2types "github.com/smartcontractkit/libocr/offchainreporting2/types"

llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo"
)
Expand Down Expand Up @@ -110,6 +111,10 @@ func (cdc JSONReportCodec) Decode(b []byte) (r Report, err error) {
return r, fmt.Errorf("failed to decode StreamValue: %w", err)
}
}
if d.SeqNr == 0 {
// catch obviously bad inputs, since a valid report can never have SeqNr == 0
return r, fmt.Errorf("missing SeqNr")
}

return Report{
ConfigDigest: cd,
Expand All @@ -121,3 +126,58 @@ func (cdc JSONReportCodec) Decode(b []byte) (r Report, err error) {
Specimen: d.Specimen,
}, err
}

// TODO: Needs tests, MERC-3524
func (cdc JSONReportCodec) Pack(digest types.ConfigDigest, seqNr uint64, report ocr2types.Report, sigs []types.AttributedOnchainSignature) ([]byte, error) {
type packed struct {
ConfigDigest types.ConfigDigest `json:"configDigest"`
SeqNr uint64 `json:"seqNr"`
Report json.RawMessage `json:"report"`
Sigs []types.AttributedOnchainSignature `json:"sigs"`
}
p := packed{
ConfigDigest: digest,
SeqNr: seqNr,
Report: json.RawMessage(report), // TODO: check if its valid JSON
Sigs: sigs,
}
return json.Marshal(p)
}

// TODO: Needs tests, MERC-3524
func (cdc JSONReportCodec) Unpack(b []byte) (digest types.ConfigDigest, seqNr uint64, report ocr2types.Report, sigs []types.AttributedOnchainSignature, err error) {
type packed struct {
ConfigDigest string `json:"configDigest"`
SeqNr uint64 `json:"seqNr"`
Report json.RawMessage `json:"report"`
Sigs []types.AttributedOnchainSignature `json:"sigs"`
}
p := packed{}
err = json.Unmarshal(b, &p)
if err != nil {
return digest, seqNr, report, sigs, fmt.Errorf("failed to unpack report: expected JSON (got: %s); %w", b, err)
}
cdBytes, err := hex.DecodeString(p.ConfigDigest)
if err != nil {
return digest, seqNr, report, sigs, fmt.Errorf("invalid ConfigDigest; %w", err)
}
cd, err := types.BytesToConfigDigest(cdBytes)
if err != nil {
return digest, seqNr, report, sigs, fmt.Errorf("invalid ConfigDigest; %w", err)
}
return cd, p.SeqNr, ocr2types.Report(p.Report), p.Sigs, nil
}

// TODO: Needs tests, MERC-3524
func (cdc JSONReportCodec) UnpackDecode(b []byte) (digest types.ConfigDigest, seqNr uint64, report Report, sigs []types.AttributedOnchainSignature, err error) {
var encodedReport []byte
digest, seqNr, encodedReport, sigs, err = cdc.Unpack(b)
if err != nil {
return digest, seqNr, report, sigs, err
}
r, err := cdc.Decode(encodedReport)
if err != nil {
return digest, seqNr, report, sigs, err
}
return digest, seqNr, r, sigs, nil
}
7 changes: 7 additions & 0 deletions llo/json_report_codec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,11 @@ func Test_JSONCodec(t *testing.T) {

assert.Equal(t, r, decoded)
})
t.Run("invalid input fails decode", func(t *testing.T) {
cdc := JSONReportCodec{}
_, err := cdc.Decode([]byte(`{}`))
assert.EqualError(t, err, "invalid ConfigDigest; cannot convert bytes to ConfigDigest. bytes have wrong length 0")
_, err = cdc.Decode([]byte(`{"ConfigDigest":"0102030000000000000000000000000000000000000000000000000000000000"}`))
assert.EqualError(t, err, "missing SeqNr")
})
}
19 changes: 3 additions & 16 deletions llo/llo_offchain_config.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions llo/llo_offchain_config.proto
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,4 @@ syntax="proto3";
package v1;
option go_package = ".;llo";

message LLOOffchainConfigProto {
bytes predecessorConfigDigest = 1;
}
message LLOOffchainConfigProto {}
8 changes: 0 additions & 8 deletions llo/must.go

This file was deleted.

28 changes: 1 addition & 27 deletions llo/offchain_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,11 @@ package llo
import (
"fmt"

"github.com/smartcontractkit/libocr/offchainreporting2/types"
"google.golang.org/protobuf/proto"
)

type OffchainConfig struct {
// We use the offchainconfig of the plugin to tell the plugin the
// configdigest of its predecessor protocol instance.
//
// NOTE: Set here:
// https://github.com/smartcontractkit/mercury-v1-sketch/blob/f52c0f823788f86c1aeaa9ba1eee32a85b981535/onchain/src/ConfigurationStore.sol#L13
// TODO: This needs to be implemented alongside staging/production
// switchover support: https://smartcontract-it.atlassian.net/browse/MERC-3386
PredecessorConfigDigest *types.ConfigDigest
// TODO: Billing
// https://smartcontract-it.atlassian.net/browse/MERC-1189
// QUESTION: Previously we stored ExpiryWindow and BaseUSDFeeCents in offchain
// config, but those might be channel specific so need to move to
// channel definition
// ExpirationWindow uint32 `json:"expirationWindow"` // Integer number of seconds
// BaseUSDFee decimal.Decimal `json:"baseUSDFee"` // Base USD fee
// NOTE: Currently OffchainConfig does not contain anything, and is not used
}

func DecodeOffchainConfig(b []byte) (o OffchainConfig, err error) {
Expand All @@ -31,21 +16,10 @@ func DecodeOffchainConfig(b []byte) (o OffchainConfig, err error) {
if err != nil {
return o, fmt.Errorf("failed to decode offchain config: expected protobuf (got: 0x%x); %w", b, err)
}
if len(pbuf.PredecessorConfigDigest) > 0 {
var predecessorConfigDigest types.ConfigDigest
predecessorConfigDigest, err = types.BytesToConfigDigest(pbuf.PredecessorConfigDigest)
if err != nil {
return o, err
}
o.PredecessorConfigDigest = &predecessorConfigDigest
}
return
}

func (c OffchainConfig) Encode() ([]byte, error) {
pbuf := LLOOffchainConfigProto{}
if c.PredecessorConfigDigest != nil {
pbuf.PredecessorConfigDigest = c.PredecessorConfigDigest[:]
}
return proto.Marshal(&pbuf)
}
26 changes: 1 addition & 25 deletions llo/offchain_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,13 @@ package llo
import (
"testing"

"github.com/smartcontractkit/libocr/offchainreporting2/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func Test_OffchainConfig(t *testing.T) {
t.Run("garbage bytes", func(t *testing.T) {
_, err := DecodeOffchainConfig([]byte{1})
require.Error(t, err)

assert.Contains(t, err.Error(), "failed to decode offchain config: expected protobuf (got: 0x01); proto:")
})

t.Run("zero length for PredecessorConfigDigest is ok", func(t *testing.T) {
decoded, err := DecodeOffchainConfig([]byte{})
require.NoError(t, err)
assert.Equal(t, OffchainConfig{}, decoded)
})

t.Run("encoding nil PredecessorConfigDigest is ok", func(t *testing.T) {
cfg := OffchainConfig{nil}

b, err := cfg.Encode()
require.NoError(t, err)

assert.Len(t, b, 0)
})

t.Run("encode and decode", func(t *testing.T) {
cd := types.ConfigDigest([32]byte{1, 2, 3})
cfg := OffchainConfig{&cd}
cfg := OffchainConfig{}

b, err := cfg.Encode()
require.NoError(t, err)
Expand Down
80 changes: 80 additions & 0 deletions llo/onchain_config_codec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package llo

import (
"fmt"
"math/big"

"github.com/smartcontractkit/libocr/bigbigendian"
"github.com/smartcontractkit/libocr/offchainreporting2/types"
)

const onchainConfigVersion = 1

var onchainConfigVersionBig = big.NewInt(onchainConfigVersion)

const onchainConfigEncodedLength = 2 * 32 // 2x 32bit evm word: version and predecessorConfigDigest

type OnchainConfig struct {
Version uint8
PredecessorConfigDigest *types.ConfigDigest
}

type OnchainConfigCodec interface {
Decode(b []byte) (OnchainConfig, error)
Encode(OnchainConfig) ([]byte, error)
}

var _ OnchainConfigCodec = EVMOnchainConfigCodec{}

// EVMOnchainConfigCodec provides a llo-specific implementation of
// OnchainConfigCodec.
//
// An encoded onchain config is expected to be in the format
// <version><predecessorConfigDigest>
// where version is a uint8 and min and max are in the format
// returned by EncodeValueInt192.
type EVMOnchainConfigCodec struct{}

// TODO: Needs fuzz testing - MERC-3524
func (EVMOnchainConfigCodec) Decode(b []byte) (OnchainConfig, error) {
if len(b) != onchainConfigEncodedLength {
return OnchainConfig{}, fmt.Errorf("unexpected length of OnchainConfig, expected %v, got %v", onchainConfigEncodedLength, len(b))
}

v, err := bigbigendian.DeserializeSigned(32, b[:32])
if err != nil {
return OnchainConfig{}, err
}
if v.Cmp(onchainConfigVersionBig) != 0 {
return OnchainConfig{}, fmt.Errorf("unexpected version of OnchainConfig, expected %v, got %v", onchainConfigVersion, v)
}

o := OnchainConfig{
Version: uint8(v.Uint64()),
}

cd := types.ConfigDigest(b[32:64])
if (cd != types.ConfigDigest{}) {
o.PredecessorConfigDigest = &cd
}
return o, nil
}

// TODO: Needs fuzz testing - MERC-3524
func (EVMOnchainConfigCodec) Encode(c OnchainConfig) ([]byte, error) {
if c.Version != onchainConfigVersion {
return nil, fmt.Errorf("unexpected version of OnchainConfig, expected %v, got %v", onchainConfigVersion, c.Version)
}
verBytes, err := bigbigendian.SerializeSigned(32, onchainConfigVersionBig)
if err != nil {
return nil, err
}
cdBytes := make([]byte, 32)
if c.PredecessorConfigDigest != nil {
copy(cdBytes, c.PredecessorConfigDigest[:])
}
result := make([]byte, 0, onchainConfigEncodedLength)
result = append(result, verBytes...)
result = append(result, cdBytes...)
return result, nil
}
Loading

0 comments on commit 42005e4

Please sign in to comment.