Skip to content

Commit

Permalink
feat: add gravity support for multi chain targets (#1174)
Browse files Browse the repository at this point in the history
# Related Github tickets

- VolumeFi#1636

# Background

This change adds Gravity support for more than one remote chain by
contextualising stored data on Paloma with a chain reference ID. It also
adds the needed changes to the genesis as well as CLI commands.

# Testing completed

- [x] test coverage exists or has been added/updated
- [x] tested in a private testnet

# Breaking changes

- [x] I have checked my code for breaking changes
- [x] If there are breaking changes, there is a supporting migration.
  • Loading branch information
byte-bandit authored Jun 3, 2024
1 parent d33b74c commit 9e70644
Show file tree
Hide file tree
Showing 37 changed files with 1,031 additions and 579 deletions.
19 changes: 9 additions & 10 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
###########################
#### Base image ####
###########################
FROM golang:1.21-buster AS base
FROM golang:1.22-buster AS base

# TODO add non-root user

MAINTAINER Matija Martinic <[email protected]>
LABEL org.opencontainers.image.authors="[email protected]"
WORKDIR /app

###########################
Expand All @@ -14,17 +13,17 @@ WORKDIR /app
FROM base AS builder
COPY . /app
RUN \
--mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
cd /app && go build -o /palomad ./cmd/palomad

--mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
cd /app && go build -o /palomad ./cmd/palomad
#################################
#### Local chain setup ####
#################################
FROM ubuntu AS setup-chain-locally
RUN apt-get update && \
apt-get install -y jq
COPY --from=builder /palomad /palomad
apt-get install -y jq
OPY --from=builder /palomad /palomad
COPY --from=builder /app/scripts/setup-chain-validator.sh /app/scripts/setup-chain-validator.sh
RUN PALOMA_CMD="/palomad" /app/scripts/setup-chain-validator.sh

Expand All @@ -46,7 +45,7 @@ CMD ["air"]
FROM ubuntu AS local-testnet
ENTRYPOINT ["/palomad"]
COPY --from=builder /palomad /palomad


###########################
#### Release ####
Expand Down
6 changes: 4 additions & 2 deletions proto/palomachain/paloma/gravity/genesis.proto
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ option go_package = "github.com/palomachain/paloma/x/gravity/types";
// module
message GenesisState {
Params params = 1;
GravityNonces gravity_nonces = 2 [ (gogoproto.nullable) = false ];
repeated GravityNonces gravity_nonces = 2 [ (gogoproto.nullable) = false ];
repeated OutgoingTxBatch batches = 3 [ (gogoproto.nullable) = false ];
repeated MsgConfirmBatch batch_confirms = 4 [ (gogoproto.nullable) = false ];
repeated Attestation attestations = 7 [ (gogoproto.nullable) = false ];
Expand All @@ -38,4 +38,6 @@ message GravityNonces {
// the last batch id from the Gravity batch pool, this prevents ID duplication
// during chain upgrades
uint64 last_batch_id = 4;
}
// the reference id of the remote chain this data applies to.
string chain_reference_id = 5;
}
34 changes: 20 additions & 14 deletions proto/palomachain/paloma/gravity/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,13 @@ message QueryBatchConfirmsResponse {
repeated MsgConfirmBatch confirms = 1 [ (gogoproto.nullable) = false ];
}

message QueryLastEventNonceRequest {}
message QueryLastEventNonceByAddrRequest { string address = 1; }
message QueryLastEventNonceRequest {
string chain_reference_id = 1;
}
message QueryLastEventNonceByAddrRequest {
string address = 1;
string chain_reference_id = 2;
}
message QueryLastEventNonceResponse { uint64 event_nonce = 1; }

message QueryERC20ToDenomRequest {
Expand All @@ -101,10 +106,10 @@ message QueryDenomToERC20Response { string erc20 = 1; }
// actual Ethereum block height significantly due to 1. Ethereum Finality and
// 2. Consensus mirroring the state on Ethereum
message QueryLastObservedEthBlockRequest {
// indicates whether to search for store data using the old Gravity v1 key
// "LastObservedEthereumBlockHeightKey" Note that queries before the Mercury
// upgrade at height 1282013 must set this to true
bool use_v1_key = 1;
reserved 1;
reserved "use_v1_key";

string chain_reference_id = 2;
}
message QueryLastObservedEthBlockResponse {
// a response of 0 indicates that no Ethereum events have been observed, and
Expand All @@ -117,10 +122,10 @@ message QueryLastObservedEthBlockResponse {
// likely to lag the last executed event a little due to 1. Ethereum Finality
// and 2. Consensus mirroring the Ethereum state
message QueryLastObservedEthNonceRequest {
// indicates whether to search for store data using the old Gravity v1 key
// "LastObservedEventNonceKey" Note that queries before the Mercury upgrade at
// height 1282013 must set this to true
bool use_v1_key = 1;
reserved 1;
reserved "use_v1_key";

string chain_reference_id = 2;
}
message QueryLastObservedEthNonceResponse {
// a response of 0 indicates that no Ethereum events have been observed, and
Expand All @@ -136,6 +141,9 @@ message QueryLastObservedEthNonceResponse {
// height. Note, that an attestation will be returned if it matches ANY of the
// filter query parameters provided.
message QueryAttestationsRequest {
reserved 6;
reserved "use_v1_key";

// limit defines how many attestations to limit in the response.
uint64 limit = 1;
// order_by provides ordering of atteststions by nonce in the response. Either
Expand All @@ -148,10 +156,8 @@ message QueryAttestationsRequest {
uint64 nonce = 4;
// height allows filtering attestations by Ethereum claim height.
uint64 height = 5;
// indicates whether to search for store data using the old Gravity v1 key
// "OracleAttestationKey" Note that queries before the Mercury upgrade at
// height 1282013 must set this to true
bool use_v1_key = 6;
// reference id of remote chain for which to query.
string chain_reference_id = 7;
}

message QueryAttestationsResponse {
Expand Down
2 changes: 1 addition & 1 deletion x/evm/keeper/attest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ var _ = g.Describe("attest router", func() {
consensukeeper = ms.ConsensusKeeper
q = consensusmocks.NewQueuer(t)
isGoodcase = true
ms.GravityKeeper.On("GetLastObservedEventNonce", mock.Anything).Return(uint64(100), nil).Maybe()
ms.GravityKeeper.On("GetLastObservedEventNonce", mock.Anything, mock.Anything).Return(uint64(100), nil).Maybe()
})

g.BeforeEach(func() {
Expand Down
4 changes: 2 additions & 2 deletions x/evm/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,12 @@ func buildKeeper(t *testing.T) (*Keeper, sdk.Context, mockedServices) {
// test-chain mocks
mockServices.ValsetKeeper.On("GetCurrentSnapshot", mock.Anything).Return(unpublishedSnapshot, nil)
mockServices.ConsensusKeeper.On("PutMessageInQueue", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(uint64(0), nil)
mockServices.GravityKeeper.On("GetLastObservedEventNonce", mock.Anything).Return(uint64(100), nil)
mockServices.GravityKeeper.On("GetLastObservedEventNonce", mock.Anything, mock.Anything).Return(uint64(100), nil)

// invalid-test-chain mocks
mockServices.ValsetKeeper.On("GetCurrentSnapshot", mock.Anything).Return(unpublishedSnapshot, nil)
mockServices.ConsensusKeeper.On("PutMessageInQueue", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(uint64(0), nil)
mockServices.GravityKeeper.On("GetLastObservedEventNonce", mock.Anything).Return(uint64(100), nil)
mockServices.GravityKeeper.On("GetLastObservedEventNonce", mock.Anything, mock.Anything).Return(uint64(100), nil)

// Add 2 new chains for our tests to use
err := k.AddSupportForNewChain(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func addDeploymentToKeeper(t *testing.T, ctx sdk.Context, k *Keeper, mockService
// test-chain mocks
mockServices.ValsetKeeper.On("GetCurrentSnapshot", mock.Anything).Return(unpublishedSnapshot, nil)
mockServices.ConsensusKeeper.On("PutMessageInQueue", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(uint64(10), nil)
mockServices.GravityKeeper.On("GetLastObservedEventNonce", mock.Anything).Return(uint64(100), nil)
mockServices.GravityKeeper.On("GetLastObservedEventNonce", mock.Anything, mock.Anything).Return(uint64(100), nil)

// Add a new chains for our test to use
err := k.AddSupportForNewChain(
Expand Down
2 changes: 1 addition & 1 deletion x/evm/keeper/smart_contract_deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ func (k Keeper) deploySmartContractToChain(ctx context.Context, chainInfo *types
}
uniqueID := generateSmartContractID(ctx)
k.createSmartContractDeployment(ctx, smartContract, chainInfo, uniqueID[:])
lastEventNonce, err := k.Gravity.GetLastObservedEventNonce(ctx)
lastEventNonce, err := k.Gravity.GetLastObservedEventNonce(ctx, chainInfo.GetChainReferenceID())
if err != nil {
return fmt.Errorf("failed to get last observed event nonce: %w", err)
}
Expand Down
2 changes: 1 addition & 1 deletion x/evm/types/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,6 @@ type ERC20Record interface {

//go:generate mockery --name=GravityKeeper
type GravityKeeper interface {
GetLastObservedEventNonce(ctx context.Context) (uint64, error)
GetLastObservedEventNonce(ctx context.Context, chainReferenceID string) (uint64, error)
CastAllERC20ToDenoms(ctx context.Context) ([]ERC20Record, error)
}
5 changes: 3 additions & 2 deletions x/evm/types/mocks/ConsensusKeeper.go

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

23 changes: 12 additions & 11 deletions x/evm/types/mocks/GravityKeeper.go

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

5 changes: 3 additions & 2 deletions x/evm/types/mocks/ValsetKeeper.go

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

41 changes: 26 additions & 15 deletions x/gravity/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,41 @@ import (

sdkerrors "cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/palomachain/paloma/util/liblog"
"github.com/palomachain/paloma/x/gravity/keeper"
log "github.com/sirupsen/logrus"
)

// EndBlocker is called at the end of every block
func EndBlocker(ctx context.Context, k keeper.Keeper) {
defer func() {
if r := recover(); r != nil {
liblog.FromSDKLogger(k.Logger(ctx)).
WithFields("original-error", r).
Warn("Recovered panic.")
}
}()
// slashing(ctx, k)
chains := k.GetChainsWithTokens(ctx)

err := createBatch(ctx, k)
if err != nil {
log.Error(err)
}

err = attestationTally(ctx, k)
if err != nil {
log.Error(err)
}
for _, v := range chains {
err = attestationTally(ctx, k, v)
if err != nil {
log.Error(err)
}

err = cleanupTimedOutBatches(ctx, k)
if err != nil {
log.Error(err)
err = pruneAttestations(ctx, k, v)
if err != nil {
log.Error(err)
}
}

err = pruneAttestations(ctx, k)
err = cleanupTimedOutBatches(ctx, k)
if err != nil {
log.Error(err)
}
Expand Down Expand Up @@ -60,8 +71,8 @@ func createBatch(ctx context.Context, k keeper.Keeper) error {
// Iterate over all attestations currently being voted on in order of nonce and
// "Observe" those who have passed the threshold. Break the loop once we see
// an attestation that has not passed the threshold
func attestationTally(ctx context.Context, k keeper.Keeper) error {
attmap, keys, err := k.GetAttestationMapping(ctx)
func attestationTally(ctx context.Context, k keeper.Keeper, chainReferenceID string) error {
attmap, keys, err := k.GetAttestationMapping(ctx, chainReferenceID)
if err != nil {
return err
}
Expand Down Expand Up @@ -90,7 +101,7 @@ func attestationTally(ctx context.Context, k keeper.Keeper) error {
// we skip the other attestations and move on to the next nonce again.
// If no attestation becomes observed, when we get to the next nonce, every attestation in
// it will be skipped. The same will happen for every nonce after that.
lastEventNonce, err := k.GetLastObservedEventNonce(ctx)
lastEventNonce, err := k.GetLastObservedEventNonce(ctx, chainReferenceID)
if err != nil {
return err
}
Expand Down Expand Up @@ -130,8 +141,8 @@ func cleanupTimedOutBatches(ctx context.Context, k keeper.Keeper) error {
// use. This could be combined with create attestation and save some computation
// but (A) pruning keeps the iteration small in the first place and (B) there is
// already enough nuance in the other handler that it's best not to complicate it further
func pruneAttestations(ctx context.Context, k keeper.Keeper) error {
attmap, keys, err := k.GetAttestationMapping(ctx)
func pruneAttestations(ctx context.Context, k keeper.Keeper, chainReferenceID string) error {
attmap, keys, err := k.GetAttestationMapping(ctx, chainReferenceID)
if err != nil {
return err
}
Expand All @@ -140,7 +151,7 @@ func pruneAttestations(ctx context.Context, k keeper.Keeper) error {
// minus some buffer value. This buffer value is purely to allow
// frontends and other UI components to view recent oracle history
const eventsToKeep = 1000
lastNonce, err := k.GetLastObservedEventNonce(ctx)
lastNonce, err := k.GetLastObservedEventNonce(ctx, chainReferenceID)
if err != nil {
return err
}
Expand All @@ -162,7 +173,7 @@ func pruneAttestations(ctx context.Context, k keeper.Keeper) error {
for _, att := range attmap[nonce] {
// delete all before the cutoff
if nonce < cutoff {
err := k.DeleteAttestation(ctx, att)
err := k.DeleteAttestation(ctx, chainReferenceID, att)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion x/gravity/abci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ func TestBatchTimeout(t *testing.T) {
require.NoError(t, err1)
require.Equal(t, b1.BatchTimeout, uint64(1693425290))

pk.SetLastObservedEthereumBlockHeight(ctx, 500)
pk.SetLastObservedEthereumBlockHeight(ctx, "test-chain", 500)

// create another batch
b2, err2 := pk.BuildOutgoingTXBatch(ctx, "test-chain", *tokenContract, 2)
Expand Down
Loading

0 comments on commit 9e70644

Please sign in to comment.