Skip to content

Commit

Permalink
fix: adds an index to keep track of finalized inbounds and prevent du…
Browse files Browse the repository at this point in the history
…plicate (#1536)

* add finalized inbound

* fix lint

* fix unit test

* changelog entry

* add function to generate key for finalized inbound

* add proto files

* add simple test for evm deposit

* add todo for improving unit tests

* generate files

* add nosec

* add genesis inmport export form Finalzied inbounds

* add log lines

* avoid creation of ballots during double spend

* add changelog entry

* remove changelog entry

---------

Co-authored-by: Lucas Bertrand <[email protected]>
  • Loading branch information
kingpinXD and lumtis authored Jan 11, 2024
1 parent fde9cab commit 736d616
Show file tree
Hide file tree
Showing 26 changed files with 684 additions and 188 deletions.
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
* [1525](https://github.com/zeta-chain/node/pull/1525) - relax EVM chain block header length check 1024->4096
* [1522](https://github.com/zeta-chain/node/pull/1522/files) - block `distribution` module account from receiving zeta
* [1528](https://github.com/zeta-chain/node/pull/1528) - fix panic caused on decoding malformed BTC addresses
* [1536](https://github.com/zeta-chain/node/pull/1536) - add index to check previously finalized inbounds
* [1556](https://github.com/zeta-chain/node/pull/1556) - add emptiness check for topic array in event parsing
* [1555](https://github.com/zeta-chain/node/pull/1555) - Reduce websocket message limit to 10MB

Expand Down
15 changes: 15 additions & 0 deletions docs/openapi/openapi.swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50813,6 +50813,8 @@ definitions:
inbound_tx_finalized_zeta_height:
type: string
format: uint64
tx_finalization_status:
$ref: '#/definitions/crosschainTxFinalizationStatus'
crosschainLastBlockHeight:
type: object
properties:
Expand Down Expand Up @@ -50914,6 +50916,8 @@ definitions:
format: uint64
tss_pubkey:
type: string
tx_finalization_status:
$ref: '#/definitions/crosschainTxFinalizationStatus'
crosschainQueryAllCctxResponse:
type: object
properties:
Expand Down Expand Up @@ -51064,6 +51068,17 @@ definitions:
properties:
aborted_zeta_amount:
type: string
crosschainTxFinalizationStatus:
type: string
enum:
- NotFinalized
- Finalized
- Executed
default: NotFinalized
title: |-
- NotFinalized: the corresponding tx is not finalized
- Finalized: the corresponding tx is finalized but not executed yet
- Executed: the corresponding tx is executed
crosschainTxHashList:
type: object
properties:
Expand Down
8 changes: 8 additions & 0 deletions proto/crosschain/cross_chain_tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ enum CctxStatus {
Aborted = 6; // inbound tx error or invalid paramters and cannot revert; just abort
}

enum TxFinalizationStatus {
option (gogoproto.goproto_enum_stringer) = true;
NotFinalized = 0; // the corresponding tx is not finalized
Finalized = 1; // the corresponding tx is finalized but not executed yet
Executed = 2; // the corresponding tx is executed
}
message InboundTxParams {
string sender = 1; // this address is the immediate contract/EOA that calls the Connector.send()
int64 sender_chain_id = 2;
Expand All @@ -30,6 +36,7 @@ message InboundTxParams {
uint64 inbound_tx_observed_external_height = 8;
string inbound_tx_ballot_index = 9;
uint64 inbound_tx_finalized_zeta_height = 10;
TxFinalizationStatus tx_finalization_status = 11;
}

message ZetaAccounting {
Expand Down Expand Up @@ -63,6 +70,7 @@ message OutboundTxParams {
];
uint64 outbound_tx_effective_gas_limit = 22;
string tss_pubkey = 11;
TxFinalizationStatus tx_finalization_status = 12;
}

message Status {
Expand Down
1 change: 1 addition & 0 deletions proto/crosschain/genesis.proto
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ message GenesisState {
repeated InTxHashToCctx inTxHashToCctxList = 9 [(gogoproto.nullable) = false];
repeated InTxTracker in_tx_tracker_list = 11 [(gogoproto.nullable) = false];
ZetaAccounting zeta_accounting = 12 [(gogoproto.nullable) = false];
repeated string FinalizedInbounds = 16;
}
2 changes: 1 addition & 1 deletion standalone-network/change-observer-params.sh
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
zetacored tx gov submit-proposal param-change proposal.json --from zeta --keyring-backend=test -b block --chain-id=localnet_101-1 --gas=auto --gas-prices=0.1azeta --gas-adjustment=1.5 --yes
zetacored tx gov submit-proposal proposal_feemarket.json --from zeta --keyring-backend=test -b block --chain-id=localnet_101-1 --gas=auto --gas-prices=0.1azeta --gas-adjustment=1.5 --yes
zetacored tx gov vote 1 yes --from zeta --keyring-backend test --chain-id localnet_101-1 --yes --gas=auto --gas-prices=0.1azeta --gas-adjustment=1.5
15 changes: 15 additions & 0 deletions standalone-network/proposal_feemarket.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"messages": [
{
"@type": "/ethermint.feemarket.v1.MsgUpdateParams",
"authority": "zeta10d07y265gmmuvt4z0w9aw880jnsr700jvxasvr",
"params": {
"base_fee": "7",
"min_gas_price" : "100000000000000",
"base_fee_change_denominator": "8"
}
}
],
"metadata" : "some metadata",
"deposit": "10000000azeta"
}
5 changes: 5 additions & 0 deletions testutil/sample/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,8 @@ func Proof() (txIndex int64, block *ethtypes.Block, header ethtypes.Header, head
tx = block.Transactions()[txIndex]
return
}

func EventIndex() uint64 {
r := newRandFromSeed(1)
return r.Uint64()
}
19 changes: 19 additions & 0 deletions testutil/sample/crosschain.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,22 @@ func ZetaAccounting(t *testing.T, index string) types.ZetaAccounting {
AbortedZetaAmount: math.NewUint(uint64(r.Int63())),
}
}

func InboundVote(coinType common.CoinType, from, to int64) types.MsgVoteOnObservedInboundTx {
return types.MsgVoteOnObservedInboundTx{
Creator: "",
Sender: EthAddress().String(),
SenderChainId: Chain(from).GetChainId(),
Receiver: EthAddress().String(),
ReceiverChain: Chain(to).GetChainId(),
Amount: UintInRange(10000000, 1000000000),
Message: String(),
InBlockHeight: Uint64InRange(1, 10000),
GasLimit: 1000000000,
InTxHash: Hash().String(),
CoinType: coinType,
TxOrigin: EthAddress().String(),
Asset: "",
EventIndex: EventIndex(),
}
}
12 changes: 6 additions & 6 deletions testutil/sample/observer.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,15 @@ func ChainParams(chainID int64) *types.ChainParams {
ChainId: chainID,
ConfirmationCount: r.Uint64(),

GasPriceTicker: Uint64InRange(r, 1, 300),
InTxTicker: Uint64InRange(r, 1, 300),
OutTxTicker: Uint64InRange(r, 1, 300),
WatchUtxoTicker: Uint64InRange(r, 1, 300),
GasPriceTicker: Uint64InRange(1, 300),
InTxTicker: Uint64InRange(1, 300),
OutTxTicker: Uint64InRange(1, 300),
WatchUtxoTicker: Uint64InRange(1, 300),
ZetaTokenContractAddress: EthAddress().String(),
ConnectorContractAddress: EthAddress().String(),
Erc20CustodyContractAddress: EthAddress().String(),
OutboundTxScheduleInterval: Int64InRange(r, 1, 100),
OutboundTxScheduleLookahead: Int64InRange(r, 1, 500),
OutboundTxScheduleInterval: Int64InRange(1, 100),
OutboundTxScheduleLookahead: Int64InRange(1, 500),
BallotThreshold: fiftyPercent,
MinObserverDelegation: sdk.NewDec(r.Int63()),
IsSupported: false,
Expand Down
12 changes: 10 additions & 2 deletions testutil/sample/sample.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"strconv"
"testing"

sdkmath "cosmossdk.io/math"
"github.com/zeta-chain/zetacore/cmd/zetacored/config"

"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
Expand Down Expand Up @@ -131,11 +132,18 @@ func Coins() sdk.Coins {
}

// Uint64InRange returns a sample uint64 in the given ranges
func Uint64InRange(r *rand.Rand, low, high uint64) uint64 {
func Uint64InRange(low, high uint64) uint64 {
r := newRandFromSeed(int64(low))
return r.Uint64()%(high-low) + low
}

// Int64InRange returns a sample int64 in the given ranges
func Int64InRange(r *rand.Rand, low, high int64) int64 {
func Int64InRange(low, high int64) int64 {
r := newRandFromSeed(low)
return r.Int63()%(high-low) + low
}

func UintInRange(low, high uint64) sdkmath.Uint {
u := Uint64InRange(low, high)
return sdkmath.NewUint(u)
}
36 changes: 36 additions & 0 deletions typescript/crosschain/cross_chain_tx_pb.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,32 @@ export declare enum CctxStatus {
Aborted = 6,
}

/**
* @generated from enum zetachain.zetacore.crosschain.TxFinalizationStatus
*/
export declare enum TxFinalizationStatus {
/**
* the corresponding tx is not finalized
*
* @generated from enum value: NotFinalized = 0;
*/
NotFinalized = 0,

/**
* the corresponding tx is finalized but not executed yet
*
* @generated from enum value: Finalized = 1;
*/
Finalized = 1,

/**
* the corresponding tx is executed
*
* @generated from enum value: Executed = 2;
*/
Executed = 2,
}

/**
* @generated from message zetachain.zetacore.crosschain.InboundTxParams
*/
Expand Down Expand Up @@ -114,6 +140,11 @@ export declare class InboundTxParams extends Message<InboundTxParams> {
*/
inboundTxFinalizedZetaHeight: bigint;

/**
* @generated from field: zetachain.zetacore.crosschain.TxFinalizationStatus tx_finalization_status = 11;
*/
txFinalizationStatus: TxFinalizationStatus;

constructor(data?: PartialMessage<InboundTxParams>);

static readonly runtime: typeof proto3;
Expand Down Expand Up @@ -232,6 +263,11 @@ export declare class OutboundTxParams extends Message<OutboundTxParams> {
*/
tssPubkey: string;

/**
* @generated from field: zetachain.zetacore.crosschain.TxFinalizationStatus tx_finalization_status = 12;
*/
txFinalizationStatus: TxFinalizationStatus;

constructor(data?: PartialMessage<OutboundTxParams>);

static readonly runtime: typeof proto3;
Expand Down
5 changes: 5 additions & 0 deletions typescript/crosschain/genesis_pb.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ export declare class GenesisState extends Message<GenesisState> {
*/
zetaAccounting?: ZetaAccounting;

/**
* @generated from field: repeated string FinalizedInbounds = 16;
*/
FinalizedInbounds: string[];

constructor(data?: PartialMessage<GenesisState>);

static readonly runtime: typeof proto3;
Expand Down
9 changes: 5 additions & 4 deletions x/crosschain/client/integrationtests/cli_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ func BuildSignedOutboundVote(
return WriteToNewTempFile(t, res.String())
}

func BuildSignedInboundVote(t testing.TB, val *network.Validator, denom string, account authtypes.AccountI, message string) *os.File {
func BuildSignedInboundVote(t testing.TB, val *network.Validator, denom string, account authtypes.AccountI, message string, eventIndex int) *os.File {
cmd := cli.CmdCCTXInboundVoter()
inboundVoterArgs := []string{
"0x96B05C238b99768F349135de0653b687f9c13fEE",
Expand All @@ -256,7 +256,7 @@ func BuildSignedInboundVote(t testing.TB, val *network.Validator, denom string,
"100",
"Zeta",
"",
"0",
strconv.Itoa(eventIndex),
}
txArgs := []string{
fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address),
Expand All @@ -274,7 +274,7 @@ func BuildSignedInboundVote(t testing.TB, val *network.Validator, denom string,
return WriteToNewTempFile(t, res.String())
}

func GetBallotIdentifier(message string) string {
func GetBallotIdentifier(message string, eventIndex int) string {
msg := types.NewMsgVoteOnObservedInboundTx(
"",
"0x96B05C238b99768F349135de0653b687f9c13fEE",
Expand All @@ -289,7 +289,8 @@ func GetBallotIdentifier(message string) string {
250_000,
common.CoinType_Zeta,
"",
0,
// #nosec G701 always positive
uint(eventIndex),
)
return msg.Digest()
}
Expand Down
10 changes: 5 additions & 5 deletions x/crosschain/client/integrationtests/inbound_voter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ func (s *IntegrationTestSuite) TestCCTXInboundVoter() {
},
ballotResult: observerTypes.BallotStatus_BallotInProgress,
cctxStatus: crosschaintypes.CctxStatus_PendingOutbound,
falseBallotIdentifier: GetBallotIdentifier("majority wrong votes incorrect ballot finalized / correct ballot still in progress" + "falseVote"),
falseBallotIdentifier: "majority wrong votes incorrect ballot finalized / correct ballot still in progress" + "falseVote",
},
{
name: "7 votes only just crossed threshold",
Expand All @@ -223,7 +223,7 @@ func (s *IntegrationTestSuite) TestCCTXInboundVoter() {
cctxStatus: crosschaintypes.CctxStatus_PendingOutbound,
},
}
for _, test := range tt {
for i, test := range tt {
test := test
s.Run(test.name, func() {
// Vote the gas price
Expand Down Expand Up @@ -266,14 +266,14 @@ func (s *IntegrationTestSuite) TestCCTXInboundVoter() {
if vote == observerTypes.VoteType_FailureObservation {
message = message + "falseVote"
}
signedTx := BuildSignedInboundVote(s.T(), val, s.cfg.BondDenom, account, message)
signedTx := BuildSignedInboundVote(s.T(), val, s.cfg.BondDenom, account, message, i)
out, err = clitestutil.ExecTestCLICmd(broadcaster.ClientCtx, authcli.GetBroadcastCommand(), []string{signedTx.Name(), "--broadcast-mode", "sync"})
s.Require().NoError(err)
}
s.Require().NoError(s.network.WaitForNBlocks(2))

// Get the ballot
ballotIdentifier := GetBallotIdentifier(test.name)
ballotIdentifier := GetBallotIdentifier(test.name, i)
out, err := clitestutil.ExecTestCLICmd(broadcaster.ClientCtx, observercli.CmdBallotByIdentifier(), []string{ballotIdentifier, "--output", "json"})
s.Require().NoError(err)
ballot := observerTypes.QueryBallotByIdentifierResponse{}
Expand All @@ -293,7 +293,7 @@ func (s *IntegrationTestSuite) TestCCTXInboundVoter() {
// Get the cctx and check its status
cctxIdentifier := ballotIdentifier
if test.falseBallotIdentifier != "" {
cctxIdentifier = test.falseBallotIdentifier
cctxIdentifier = GetBallotIdentifier(test.falseBallotIdentifier, i)
}
out, err = clitestutil.ExecTestCLICmd(broadcaster.ClientCtx, crosschaincli.CmdShowSend(), []string{cctxIdentifier, "--output", "json"})
cctx := crosschaintypes.QueryGetCctxResponse{}
Expand Down
8 changes: 5 additions & 3 deletions x/crosschain/client/integrationtests/outbound_voter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,9 @@ func (s *IntegrationTestSuite) TestCCTXOutBoundVoter() {
valueReceived: "7991636132140714751",
},
}
for _, test := range tt {
for i, test := range tt {
// Buffer event index so that it does not clash with the inbound voter test
eventIndex := i + 100
test := test
s.Run(test.name, func() {
broadcaster := s.network.Validators[0]
Expand Down Expand Up @@ -148,14 +150,14 @@ func (s *IntegrationTestSuite) TestCCTXOutBoundVoter() {
var account authtypes.AccountI
s.NoError(val.ClientCtx.Codec.UnmarshalInterfaceJSON(out.Bytes(), &account))
message := test.name
signedTx := BuildSignedInboundVote(s.T(), val, s.cfg.BondDenom, account, message)
signedTx := BuildSignedInboundVote(s.T(), val, s.cfg.BondDenom, account, message, eventIndex)
out, err = clitestutil.ExecTestCLICmd(broadcaster.ClientCtx, authcli.GetBroadcastCommand(), []string{signedTx.Name(), "--broadcast-mode", "sync"})
s.Require().NoError(err)
}
s.Require().NoError(s.network.WaitForNBlocks(2))

// Get the ballot
cctxIdentifier := GetBallotIdentifier(test.name)
cctxIdentifier := GetBallotIdentifier(test.name, eventIndex)
out, err := clitestutil.ExecTestCLICmd(broadcaster.ClientCtx, crosschaincli.CmdShowSend(), []string{cctxIdentifier, "--output", "json"})
cctx := crosschaintypes.QueryGetCctxResponse{}
s.NoError(broadcaster.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &cctx))
Expand Down
5 changes: 4 additions & 1 deletion x/crosschain/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState)
k.SetCctxAndNonceToCctxAndInTxHashToCctx(ctx, *elem)
}
}

for _, elem := range genState.FinalizedInbounds {
k.SetFinalizedInbound(ctx, elem)
}
}

// ExportGenesis returns the crosschain module's exported genesis.
Expand Down Expand Up @@ -87,6 +89,7 @@ func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState {
if found {
genesis.ZetaAccounting = amount
}
genesis.FinalizedInbounds = k.GetAllFinalizedInbound(ctx)

return &genesis
}
Loading

0 comments on commit 736d616

Please sign in to comment.