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: initialize cctx gateway interface #2291

Merged
merged 27 commits into from
Jun 3, 2024
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/ante/vesting_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ func TestVesting_AnteHandle(t *testing.T) {

if tt.wantHasErr {
require.Error(t, err)
skosito marked this conversation as resolved.
Show resolved Hide resolved
require.Contains(t, err.Error(), tt.wantErr)
require.ErrorContains(t, err, tt.wantErr)
} else {
require.NoError(t, err)
}
Expand Down
9 changes: 9 additions & 0 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@

"github.com/zeta-chain/zetacore/app/ante"
"github.com/zeta-chain/zetacore/docs/openapi"
"github.com/zeta-chain/zetacore/pkg/chains"
zetamempool "github.com/zeta-chain/zetacore/pkg/mempool"
srvflags "github.com/zeta-chain/zetacore/server/flags"
authoritymodule "github.com/zeta-chain/zetacore/x/authority"
Expand Down Expand Up @@ -597,6 +598,14 @@
app.LightclientKeeper,
)

// initializing map of cctx gateways so crosschain module can decide which one to use
// based on chain info of destination chain
cctxGateways := map[chains.CCTXGateway]crosschainkeeper.CCTXGateway{
skosito marked this conversation as resolved.
Show resolved Hide resolved
chains.CCTXGateway_observers: crosschainkeeper.NewCCTXGatewayObservers(app.CrosschainKeeper),
chains.CCTXGateway_zevm: crosschainkeeper.NewCCTXGatewayZEVM(app.CrosschainKeeper),

Check warning on line 605 in app/app.go

View check run for this annotation

Codecov / codecov/patch

app/app.go#L603-L605

Added lines #L603 - L605 were not covered by tests
}
app.CrosschainKeeper.SetCCTXGateways(cctxGateways)

Check warning on line 607 in app/app.go

View check run for this annotation

Codecov / codecov/patch

app/app.go#L607

Added line #L607 was not covered by tests

// initialize ibccrosschain keeper and set it to the crosschain keeper
// there is a circular dependency between the two keepers, crosschain keeper must be initialized first

Expand Down
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
* [2287](https://github.com/zeta-chain/node/pull/2287) - implement `MsgUpdateChainInfo` message
* [2279](https://github.com/zeta-chain/node/pull/2279) - add a CCTXGateway field to chain static data
* [2275](https://github.com/zeta-chain/node/pull/2275) - add ChainInfo singleton state variable in authority
* [2291](https://github.com/zeta-chain/node/pull/2291) - initialize cctx gateway interface
* [2289](https://github.com/zeta-chain/node/pull/2289) - add an authorization list to keep track of all authorizations on the chain

### Refactor
Expand Down
14 changes: 7 additions & 7 deletions pkg/chains/address_taproot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ func TestAddressTaproot(t *testing.T) {
// should parse mainnet taproot address
addrStr := "bc1p4ur084x8y63mj5hj7eydscuc4awals7ly749x8vhyquc0twcmvhquspa5c"
addr, err := DecodeTaprootAddress(addrStr)
require.Nil(t, err)
require.NoError(t, err)
require.Equal(t, addrStr, addr.String())
require.Equal(t, addrStr, addr.EncodeAddress())
require.True(t, addr.IsForNet(&chaincfg.MainNetParams))
Expand All @@ -22,7 +22,7 @@ func TestAddressTaproot(t *testing.T) {
// should parse testnet taproot address
addrStr := "tb1pzeclkt6upu8xwuksjcz36y4q56dd6jw5r543eu8j8238yaxpvcvq7t8f33"
addr, err := DecodeTaprootAddress(addrStr)
require.Nil(t, err)
require.NoError(t, err)
require.Equal(t, addrStr, addr.String())
require.Equal(t, addrStr, addr.EncodeAddress())
require.True(t, addr.IsForNet(&chaincfg.TestNet3Params))
Expand All @@ -31,7 +31,7 @@ func TestAddressTaproot(t *testing.T) {
// should parse regtest taproot address
addrStr := "bcrt1pqqqsyqcyq5rqwzqfpg9scrgwpugpzysnzs23v9ccrydpk8qarc0sj9hjuh"
addr, err := DecodeTaprootAddress(addrStr)
require.Nil(t, err)
require.NoError(t, err)
require.Equal(t, addrStr, addr.String())
require.Equal(t, addrStr, addr.EncodeAddress())
require.True(t, addr.IsForNet(&chaincfg.RegressionNetParams))
Expand All @@ -50,17 +50,17 @@ func TestAddressTaproot(t *testing.T) {
witnessProg[i] = byte(i)
}
_, err := newAddressTaproot("bcrt", witnessProg[:])
require.Nil(t, err)
require.NoError(t, err)
//t.Logf("addr: %v", addr)
}
{
// should create correct taproot address from given witness program
// these hex string comes from link
// https://mempool.space/tx/41f7cbaaf9a8d378d09ee86de32eebef455225520cb71015cc9a7318fb42e326
witnessProg, err := hex.DecodeString("af06f3d4c726a3b952f2f648d86398af5ddfc3df27aa531d97203987add8db2e")
require.Nil(t, err)
require.NoError(t, err)
addr, err := NewAddressTaproot(witnessProg[:], &chaincfg.MainNetParams)
require.Nil(t, err)
require.NoError(t, err)
require.Equal(t, addr.EncodeAddress(), "bc1p4ur084x8y63mj5hj7eydscuc4awals7ly749x8vhyquc0twcmvhquspa5c")
}
{
Expand All @@ -69,7 +69,7 @@ func TestAddressTaproot(t *testing.T) {
// https://blockstream.info/tx/09298a2f32f5267f419aeaf8a58c4807dcf6cac3edb59815a3b129cd8f1219b0?expand
addrStr := "bc1p6pls9gpm24g8ntl37pajpjtuhd3y08hs5rnf9a4n0wq595hwdh9suw7m2h"
addr, err := DecodeTaprootAddress(addrStr)
require.Nil(t, err)
require.NoError(t, err)
require.Equal(
t,
"d07f02a03b555079aff1f07b20c97cbb62479ef0a0e692f6b37b8142d2ee6dcb",
Expand Down
16 changes: 8 additions & 8 deletions pkg/crypto/pubkey_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,15 +207,15 @@ func TestNewPubKey(t *testing.T) {
t.Run("should create new pub key from string", func(t *testing.T) {
_, pubKey, _ := testdata.KeyTestPubAddr()
spk, err := cosmos.Bech32ifyPubKey(cosmos.Bech32PubKeyTypeAccPub, pubKey)
require.Nil(t, err)
require.NoError(t, err)
pk, err := NewPubKey(spk)
require.Nil(t, err)
require.NoError(t, err)
require.Equal(t, PubKey(spk), pk)
})

t.Run("should return empty pub key from empty string", func(t *testing.T) {
pk, err := NewPubKey("")
require.Nil(t, err)
require.NoError(t, err)
require.Equal(t, EmptyPubKey, pk)
})

Expand All @@ -230,9 +230,9 @@ func TestGetAddressFromPubkeyString(t *testing.T) {
t.Run("should get address from pubkey string", func(t *testing.T) {
_, pubKey, _ := testdata.KeyTestPubAddr()
spk, err := cosmos.Bech32ifyPubKey(cosmos.Bech32PubKeyTypeAccPub, pubKey)
require.Nil(t, err)
require.NoError(t, err)
_, err = GetAddressFromPubkeyString(spk)
require.Nil(t, err)
require.NoError(t, err)
})

t.Run("should get address from nonbech32 string", func(t *testing.T) {
Expand Down Expand Up @@ -349,7 +349,7 @@ func TestGetEVMAddress(t *testing.T) {
t.Run("should return empty if pubkey is empty", func(t *testing.T) {
pubKey := PubKey("")
e, err := pubKey.GetEVMAddress()
require.Nil(t, err)
require.NoError(t, err)
require.Equal(t, chains.NoAddress, e)
})

Expand All @@ -359,13 +359,13 @@ func TestGetEVMAddress(t *testing.T) {
pk, _ := NewPubKey(spk)

_, err := pk.GetEVMAddress()
require.Nil(t, err)
require.NoError(t, err)
})

t.Run("should error if non bech32", func(t *testing.T) {
pk := PubKey("invalid")
e, err := pk.GetEVMAddress()
require.NotNil(t, err)
require.Error(t, err)
skosito marked this conversation as resolved.
Show resolved Hide resolved
require.Equal(t, chains.NoAddress, e)
})
}
7 changes: 7 additions & 0 deletions testutil/keeper/crosschain.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,13 @@ func CrosschainKeeperWithMocks(
lightclientKeeper,
)

cctxGateways := map[chains.CCTXGateway]keeper.CCTXGateway{
skosito marked this conversation as resolved.
Show resolved Hide resolved
chains.CCTXGateway_observers: keeper.NewCCTXGatewayObservers(*k),
chains.CCTXGateway_zevm: keeper.NewCCTXGatewayZEVM(*k),
}

k.SetCCTXGateways(cctxGateways)

// initialize ibccrosschain keeper and set it to the crosschain keeper
// there is a circular dependency between the two keepers, crosschain keeper must be initialized first

Expand Down
2 changes: 1 addition & 1 deletion x/authority/types/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func TestGenesisState_Validate(t *testing.T) {
err := tt.gs.Validate()
if tt.errContains != "" {
require.Error(t, err)
require.Contains(t, err.Error(), tt.errContains)
require.ErrorContains(t, err, tt.errContains)
} else {
require.NoError(t, err)
}
Expand Down
60 changes: 60 additions & 0 deletions x/crosschain/keeper/cctx_gateway_observers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package keeper

import (
sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/zeta-chain/zetacore/x/crosschain/types"
)

// CCTXGatewayObservers is implementation of CCTXGateway interface for observers
type CCTXGatewayObservers struct {
skosito marked this conversation as resolved.
Show resolved Hide resolved
crosschainKeeper Keeper
}

// NewCCTXGatewayObservers returns new instance of CCTXGatewayObservers
func NewCCTXGatewayObservers(crosschainKeeper Keeper) CCTXGatewayObservers {
return CCTXGatewayObservers{
crosschainKeeper: crosschainKeeper,
}
}

/*
InitiateOutbound updates the store so observers can use the PendingCCTX query:

- If preprocessing of outbound is successful, the CCTX status is changed to PendingOutbound.

- if preprocessing of outbound, such as paying the gas fee for the destination fails, the state is reverted to aborted

We do not return an error from this function, as all changes need to be persisted to the state.
skosito marked this conversation as resolved.
Show resolved Hide resolved

Instead, we use a temporary context to make changes and then commit the context on for the happy path, i.e cctx is set to PendingOutbound.
New CCTX status after preprocessing is returned.
*/
func (c CCTXGatewayObservers) InitiateOutbound(
ctx sdk.Context,
cctx *types.CrossChainTx,
) (newCCTXStatus types.CctxStatus) {
tmpCtx, commit := ctx.CacheContext()
lumtis marked this conversation as resolved.
Show resolved Hide resolved
outboundReceiverChainID := cctx.GetCurrentOutboundParam().ReceiverChainId
err := func() error {
err := c.crosschainKeeper.PayGasAndUpdateCctx(
tmpCtx,
outboundReceiverChainID,
cctx,
cctx.InboundParams.Amount,
false,
)
if err != nil {
return err
}
return c.crosschainKeeper.UpdateNonce(tmpCtx, outboundReceiverChainID, cctx)
}()
if err != nil {
// do not commit anything here as the CCTX should be aborted
cctx.SetAbort(err.Error())
return types.CctxStatus_Aborted
}
commit()
cctx.SetPendingOutbound("")
return types.CctxStatus_PendingOutbound
}
100 changes: 100 additions & 0 deletions x/crosschain/keeper/cctx_gateway_zevm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package keeper

import (
"fmt"

sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/zeta-chain/zetacore/x/crosschain/types"
)

// CCTXGatewayZEVM is implementation of CCTXGateway interface for ZEVM
type CCTXGatewayZEVM struct {
skosito marked this conversation as resolved.
Show resolved Hide resolved
crosschainKeeper Keeper
}

// NewCCTXGatewayZEVM returns new instance of CCTXGatewayZEVM
func NewCCTXGatewayZEVM(crosschainKeeper Keeper) CCTXGatewayZEVM {
return CCTXGatewayZEVM{
crosschainKeeper: crosschainKeeper,
}
}

/*
InitiateOutbound handles evm deposit and call ValidateOutbound.
TODO (https://github.com/zeta-chain/node/issues/2278): move remaining of this comment to ValidateOutbound once it's added.

- If the deposit is successful, the CCTX status is changed to OutboundMined.

- If the deposit returns an internal error i.e if HandleEVMDeposit() returns an error, but isContractReverted is false, the CCTX status is changed to Aborted.

- If the deposit is reverted, the function tries to create a revert cctx with status PendingRevert.

- If the creation of revert tx also fails it changes the status to Aborted.

Note : Aborted CCTXs are not refunded in this function. The refund is done using a separate refunding mechanism.
We do not return an error from this function , as all changes need to be persisted to the state.
skosito marked this conversation as resolved.
Show resolved Hide resolved
Instead we use a temporary context to make changes and then commit the context on for the happy path ,i.e cctx is set to OutboundMined.
New CCTX status after preprocessing is returned.
*/
func (c CCTXGatewayZEVM) InitiateOutbound(ctx sdk.Context, cctx *types.CrossChainTx) (newCCTXStatus types.CctxStatus) {
tmpCtx, commit := ctx.CacheContext()
isContractReverted, err := c.crosschainKeeper.HandleEVMDeposit(tmpCtx, cctx)

// TODO (https://github.com/zeta-chain/node/issues/2278): further processing will be in validateOutbound(...), for now keeping it here
if err != nil && !isContractReverted {
// exceptional case; internal error; should abort CCTX
cctx.SetAbort(err.Error())
return types.CctxStatus_Aborted
} else if err != nil && isContractReverted {
skosito marked this conversation as resolved.
Show resolved Hide resolved
// contract call reverted; should refund via a revert tx
revertMessage := err.Error()
senderChain := c.crosschainKeeper.zetaObserverKeeper.GetSupportedChainFromChainID(ctx, cctx.InboundParams.SenderChainId)
if senderChain == nil {
cctx.SetAbort(fmt.Sprintf("invalid sender chain id %d", cctx.InboundParams.SenderChainId))
return types.CctxStatus_Aborted
}
gasLimit, err := c.crosschainKeeper.GetRevertGasLimit(ctx, *cctx)
if err != nil {
cctx.SetAbort(fmt.Sprintf("revert gas limit error: %s", err.Error()))
return types.CctxStatus_Aborted
}
if gasLimit == 0 {
// use same gas limit of outbound as a fallback -- should not be required
gasLimit = cctx.GetCurrentOutboundParam().GasLimit
}

err = cctx.AddRevertOutbound(gasLimit)
if err != nil {
cctx.SetAbort(fmt.Sprintf("revert outbound error: %s", err.Error()))
return types.CctxStatus_Aborted
}
// we create a new cached context, and we don't commit the previous one with EVM deposit
tmpCtxRevert, commitRevert := ctx.CacheContext()
err = func() error {
err := c.crosschainKeeper.PayGasAndUpdateCctx(
tmpCtxRevert,
senderChain.ChainId,
cctx,
cctx.InboundParams.Amount,
false,
)
if err != nil {
return err
}
// Update nonce using senderchain id as this is a revert tx and would go back to the original sender
return c.crosschainKeeper.UpdateNonce(tmpCtxRevert, senderChain.ChainId, cctx)
}()
if err != nil {
cctx.SetAbort(fmt.Sprintf("deposit revert message: %s err : %s", revertMessage, err.Error()))
return types.CctxStatus_Aborted
}
commitRevert()
cctx.SetPendingRevert(revertMessage)
return types.CctxStatus_PendingRevert
}
// successful HandleEVMDeposit;
lumtis marked this conversation as resolved.
Show resolved Hide resolved
commit()
cctx.SetOutBoundMined("Remote omnichain contract call completed")
return types.CctxStatus_OutboundMined
}
28 changes: 28 additions & 0 deletions x/crosschain/keeper/initiate_outbound.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package keeper

import (
"fmt"

sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/zeta-chain/zetacore/pkg/chains"
"github.com/zeta-chain/zetacore/x/crosschain/types"
)

// InitiateOutbound initiates the outbound for the CCTX depending on the CCTX gateway.
// It does a conditional dispatch to correct CCTX gateway based on the receiver chain
// which handle the state changes and error handling.
skosito marked this conversation as resolved.
Show resolved Hide resolved
func (k Keeper) InitiateOutbound(ctx sdk.Context, cctx *types.CrossChainTx) (types.CctxStatus, error) {
receiverChainID := cctx.GetCurrentOutboundParam().ReceiverChainId
chainInfo := chains.GetChainFromChainID(receiverChainID)
if chainInfo == nil {
return cctx.CctxStatus.Status, fmt.Errorf("chain info not found for %d", receiverChainID)
}

cctxGateway, ok := k.cctxGateways[chainInfo.CctxGateway]
if !ok {
return cctx.CctxStatus.Status, fmt.Errorf("CCTXGateway not defined for receiver chain %d", receiverChainID)
}
skosito marked this conversation as resolved.
Show resolved Hide resolved

return cctxGateway.InitiateOutbound(ctx, cctx), nil
}
Loading
Loading