From 6a4284f192c8d4818fc214616b32ab8aced81f3c Mon Sep 17 00:00:00 2001 From: Dzmitry Hil Date: Mon, 6 Nov 2023 15:59:27 +0300 Subject: [PATCH 1/8] Implement TrustSet on the XRPL bridge account for the registered XRPL tokens. --- Makefile | 2 +- contract/src/contract.rs | 24 +- contract/src/error.rs | 23 +- contract/src/evidence.rs | 2 +- contract/src/operation.rs | 2 +- contract/src/state.rs | 10 +- contract/src/tests.rs | 4 +- contract/src/tickets.rs | 41 ++- integration-tests/contract.go | 3 +- .../coreum/contract_client_test.go | 264 +++++++++++++----- integration-tests/processes/env_test.go | 78 ++++-- .../send_from_xrpl_to_coreum_test.go | 180 +++++------- .../processes/ticket_allocation_test.go | 2 +- relayer/coreum/contract.go | 55 +++- relayer/processes/amount.go | 82 ++++-- relayer/processes/amount_test.go | 116 +++++++- relayer/processes/model.go | 1 + relayer/processes/model_mocks_test.go | 15 + relayer/processes/operation_tx.go | 33 ++- relayer/processes/xrpl_tx_observer.go | 127 ++++++--- relayer/processes/xrpl_tx_observer_test.go | 98 ++++++- relayer/processes/xrpl_tx_submitter.go | 4 + relayer/processes/xrpl_tx_submitter_test.go | 117 ++++++++ 23 files changed, 950 insertions(+), 333 deletions(-) diff --git a/Makefile b/Makefile index ff36d90c..9fc636a9 100644 --- a/Makefile +++ b/Makefile @@ -89,4 +89,4 @@ restart-dev-env: .PHONY: rebuild-dev-env rebuild-dev-env: - crust build images + crust build/crust images/cored diff --git a/contract/src/contract.rs b/contract/src/contract.rs index ec4c2037..fe833988 100644 --- a/contract/src/contract.rs +++ b/contract/src/contract.rs @@ -124,7 +124,7 @@ pub fn instantiate( sending_precision: XRP_DEFAULT_SENDING_PRECISION, max_holding_amount: Uint128::new(XRP_DEFAULT_MAX_HOLDING_AMOUNT), // The XRP active token will be active from the start because it doesn't need approval to be received by the multisig - state: TokenState::Active, + state: TokenState::Enabled, }; let key = build_xrpl_token_key(XRP_ISSUER.to_string(), XRP_CURRENCY.to_string()); @@ -336,7 +336,7 @@ fn register_xrpl_token( } fn save_evidence(deps: DepsMut, sender: Addr, evidence: Evidence) -> CoreumResult { - evidence.validate()?; + evidence.validate_basic()?; assert_relayer(deps.as_ref(), sender.clone())?; @@ -359,8 +359,8 @@ fn save_evidence(deps: DepsMut, sender: Addr, evidence: Evidence) -> CoreumResul .load(deps.storage, key) .map_err(|_| ContractError::TokenNotRegistered {})?; - if token.state.ne(&TokenState::Active) { - return Err(ContractError::XRPLTokenNotActive {}); + if token.state.ne(&TokenState::Enabled) { + return Err(ContractError::XRPLTokenNotEnabled {}); } let decimals = match is_token_xrp(token.issuer, token.currency) { @@ -409,6 +409,22 @@ fn save_evidence(deps: DepsMut, sender: Addr, evidence: Evidence) -> CoreumResul let operation_id = check_operation_exists(deps.storage, sequence_number, ticket_number)?; + // custom state validation of the transaction result + match operation_result.clone() { + OperationResult::TicketsAllocation { .. } => {} + OperationResult::TrustSet { issuer, currency } => { + let key = build_xrpl_token_key(issuer.clone(), currency.clone()); + // validate that we have received the trust set for the token we have and with the pending state + let token = XRPL_TOKENS + .load(deps.storage, key.clone()) + .map_err(|_| ContractError::TokenNotRegistered {})?; + // it is possible to + if token.state.ne(&TokenState::Processing) { + return Err(ContractError::XRPLTokenNotInProcessing {}); + } + } + } + if threshold_reached { match operation_result.clone() { OperationResult::TicketsAllocation { tickets } => { diff --git a/contract/src/error.rs b/contract/src/error.rs index 869f69dc..6ebb01b1 100644 --- a/contract/src/error.rs +++ b/contract/src/error.rs @@ -41,9 +41,9 @@ pub enum ContractError { CoreumTokenAlreadyRegistered { denom: String }, #[error( - "XRPLTokenAlreadyRegistered: Token with issuer: {} and currency: {} is already registered", - issuer, - currency + "XRPLTokenAlreadyRegistered: Token with issuer: {} and currency: {} is already registered", + issuer, + currency )] XRPLTokenAlreadyRegistered { issuer: String, currency: String }, @@ -63,7 +63,7 @@ pub enum ContractError { OperationAlreadyExecuted {}, #[error( - "EvidenceAlreadyProvided: The relayer already provided its evidence for the operation" + "EvidenceAlreadyProvided: The relayer already provided its evidence for the operation" )] EvidenceAlreadyProvided {}, @@ -83,7 +83,7 @@ pub enum ContractError { StillHaveAvailableTickets {}, #[error( - "PendingTicketUpdate: There is a pending ticket update operation already in the queue" + "PendingTicketUpdate: There is a pending ticket update operation already in the queue" )] PendingTicketUpdate {}, @@ -100,12 +100,12 @@ pub enum ContractError { InvalidTicketAllocationEvidence {}, #[error( - "PendingOperationNotFound: There is no pending operation with this ticket/sequence number" + "PendingOperationNotFound: There is no pending operation with this ticket/sequence number" )] PendingOperationNotFound {}, #[error( - "PendingOperationAlreadyExists: There is already a pending operation with this operation id" + "PendingOperationAlreadyExists: There is already a pending operation with this operation id" )] PendingOperationAlreadyExists {}, @@ -121,8 +121,11 @@ pub enum ContractError { #[error("InvalidXRPLCurrency: The currency must be a valid XRPL currency")] InvalidXRPLCurrency {}, - #[error("XRPLTokenNotActive: This token must be active to be bridged")] - XRPLTokenNotActive {}, + #[error("XRPLTokenNotEnabled: This token must be enabled to be bridged")] + XRPLTokenNotEnabled {}, + + #[error("XRPLTokenNotInProcessing: This token must be in processing state to be enabled")] + XRPLTokenNotInProcessing {}, #[error("AmountSentIsZeroAfterTruncation: Amount sent is zero after truncating to sending precision")] AmountSentIsZeroAfterTruncation {}, @@ -131,7 +134,7 @@ pub enum ContractError { MaximumBridgedAmountReached {}, #[error( - "InvalidSendingPrecision: The sending precision can't be more than the token decimals or less than the negative token decimals" + "InvalidSendingPrecision: The sending precision can't be more than the token decimals or less than the negative token decimals" )] InvalidSendingPrecision {}, } diff --git a/contract/src/evidence.rs b/contract/src/evidence.rs index ddc90d8d..0400e8d3 100644 --- a/contract/src/evidence.rs +++ b/contract/src/evidence.rs @@ -88,7 +88,7 @@ impl Evidence { } } // Function for basic validation of evidences in case relayers send something that is not valid - pub fn validate(&self) -> Result<(), ContractError> { + pub fn validate_basic(&self) -> Result<(), ContractError> { match self { Evidence::XRPLToCoreumTransfer { amount, .. } => { if amount.u128() == 0 { diff --git a/contract/src/operation.rs b/contract/src/operation.rs index 73fd690a..ff66d740 100644 --- a/contract/src/operation.rs +++ b/contract/src/operation.rs @@ -71,7 +71,7 @@ pub fn handle_trust_set_confirmation( // Set token to active if TrustSet operation was successful if transaction_result.eq(&TransactionResult::Accepted) { - token.state = TokenState::Active; + token.state = TokenState::Enabled; } else { token.state = TokenState::Inactive; } diff --git a/contract/src/state.rs b/contract/src/state.rs index 353febe7..71abd806 100644 --- a/contract/src/state.rs +++ b/contract/src/state.rs @@ -52,14 +52,14 @@ pub struct XRPLToken { #[cw_serde] pub enum TokenState { - // Active tokens are tokens that can be bridged - Active, - // Inactive tokens are tokens that can't be bridged because the trust set registration failed so it must be triggered again. - Inactive, + // Enabled tokens are tokens that can be bridged + Enabled, // Disabled tokens are tokens that can be bridged but have been disabled by the admin and they must be activated again to be bridged Disabled, - // Processing are tokens that have a TrustSet operation pending to be completed. If this operation succeeds they will be Active, if it fails they will be Inactive + // Processing are tokens that have a TrustSet operation pending to be completed. If this operation succeeds they will be Enabled, if it fails they will be Inactive Processing, + // Inactive tokens are tokens that can't be bridged because the trust set registration failed so it must be triggered again. + Inactive, } #[cw_serde] diff --git a/contract/src/tests.rs b/contract/src/tests.rs index 6ad6f782..241cd958 100644 --- a/contract/src/tests.rs +++ b/contract/src/tests.rs @@ -511,7 +511,7 @@ mod tests { coreum_denom: format!("{}-{}", XRP_SUBUNIT, contract_addr).to_lowercase(), sending_precision: XRP_DEFAULT_SENDING_PRECISION, max_holding_amount: Uint128::new(XRP_DEFAULT_MAX_HOLDING_AMOUNT), - state: TokenState::Active, + state: TokenState::Enabled, } ); } @@ -1079,7 +1079,7 @@ mod tests { assert!(not_active_error .to_string() - .contains(ContractError::XRPLTokenNotActive {}.to_string().as_str())); + .contains(ContractError::XRPLTokenNotEnabled {}.to_string().as_str())); // Activate the token let query_pending_operations = wasm diff --git a/contract/src/tickets.rs b/contract/src/tickets.rs index 26cd1cde..c3f8872c 100644 --- a/contract/src/tickets.rs +++ b/contract/src/tickets.rs @@ -38,21 +38,28 @@ pub fn register_used_ticket(storage: &mut dyn Storage) -> Result<(), ContractErr // If we reach the max allowed tickets to be used, we need to create an operation to allocate new ones if used_tickets + 1 >= config.used_tickets_threshold && !PENDING_TICKET_UPDATE.load(storage)? { - let ticket_to_update = reserve_ticket(storage)?; - - PENDING_OPERATIONS.save( - storage, - ticket_to_update, - &Operation { - ticket_number: Some(ticket_to_update), - sequence_number: None, - signatures: vec![], - operation_type: OperationType::AllocateTickets { - number: config.used_tickets_threshold, - }, - }, - )?; - PENDING_TICKET_UPDATE.save(storage, &true)?; + match reserve_ticket(storage) { + Ok(ticket_to_update) => { + PENDING_OPERATIONS.save( + storage, + ticket_to_update, + &Operation { + ticket_number: Some(ticket_to_update), + sequence_number: None, + signatures: vec![], + operation_type: OperationType::AllocateTickets { + number: config.used_tickets_threshold, + }, + }, + )?; + PENDING_TICKET_UPDATE.save(storage, &true)? + } + // TODO(keyne) add specific flag to the resp + // TODO(keyne) handle one expected error that way (not tickets to allocate) + // in case we don't have free tickets anymore we should still accept the tx + // otherwise the contract will stuck + Err(_) => {}, + }; } Ok(()) @@ -87,6 +94,10 @@ pub fn handle_ticket_allocation_confirmation( // Extract a ticket from the available tickets fn reserve_ticket(storage: &mut dyn Storage) -> Result { let mut available_tickets = AVAILABLE_TICKETS.load(storage)?; + if available_tickets.is_empty() { + return Err(ContractError::NoAvailableTickets {}); + } + let ticket_to_update = available_tickets.pop_front().unwrap(); AVAILABLE_TICKETS.save(storage, &available_tickets)?; Ok(ticket_to_update) diff --git a/integration-tests/contract.go b/integration-tests/contract.go index b5cd3d7f..a0d88290 100644 --- a/integration-tests/contract.go +++ b/integration-tests/contract.go @@ -5,6 +5,7 @@ import ( "os" "testing" + sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" @@ -22,7 +23,7 @@ func DeployAndInstantiateContract( relayers []coreum.Relayer, evidenceThreshold int, usedTicketsThreshold int, - trustSetLimitAmount string, + trustSetLimitAmount sdkmath.Int, ) (sdk.AccAddress, *coreum.ContractClient) { t.Helper() diff --git a/integration-tests/coreum/contract_client_test.go b/integration-tests/coreum/contract_client_test.go index 108f31eb..493e259a 100644 --- a/integration-tests/coreum/contract_client_test.go +++ b/integration-tests/coreum/contract_client_test.go @@ -7,6 +7,7 @@ import ( "context" "crypto/rand" "fmt" + "strconv" "strings" "testing" @@ -35,9 +36,10 @@ const ( xrpCurrency = "XRP" eventAttributeThresholdReached = "threshold_reached" - trustSetLimitAmount = "10000000000000000" ) +var defaultTrustSetLimitAmount = sdkmath.NewInt(10000000000000000) + func TestDeployAndInstantiateContract(t *testing.T) { t.Parallel() @@ -54,7 +56,7 @@ func TestDeployAndInstantiateContract(t *testing.T) { relayers, len(relayers), usedTicketsThreshold, - trustSetLimitAmount, + defaultTrustSetLimitAmount, ) contractCfg, err := contractClient.GetContractConfig(ctx) @@ -64,7 +66,7 @@ func TestDeployAndInstantiateContract(t *testing.T) { Relayers: relayers, EvidenceThreshold: len(relayers), UsedTicketsThreshold: usedTicketsThreshold, - TrustSetLimitAmount: trustSetLimitAmount, + TrustSetLimitAmount: defaultTrustSetLimitAmount, }, contractCfg) contractOwnership, err := contractClient.GetContractOwnership(ctx) @@ -106,7 +108,7 @@ func TestDeployAndInstantiateContract(t *testing.T) { Issuer: xrpIssuer, Currency: xrpCurrency, CoreumDenom: coreumDenom, - State: coreum.TokenStateActive, + State: coreum.TokenStateEnabled, }, xrplTokens[0]) } @@ -125,7 +127,7 @@ func TestChangeContractOwnership(t *testing.T) { relayers, len(relayers), usedTicketsThreshold, - trustSetLimitAmount, + defaultTrustSetLimitAmount, ) contractOwnership, err := contractClient.GetContractOwnership(ctx) @@ -186,7 +188,7 @@ func TestRegisterCoreumToken(t *testing.T) { relayers, len(relayers), usedTicketsThreshold, - trustSetLimitAmount, + defaultTrustSetLimitAmount, ) denom1 := "denom1" @@ -261,9 +263,11 @@ func TestRegisterXRPLToken(t *testing.T) { ctx, chains := integrationtests.NewTestingContext(t) assetftClient := assetfttypes.NewQueryClient(chains.Coreum.ClientContext) + bankClient := banktypes.NewQueryClient(chains.Coreum.ClientContext) - relayers := genRelayers(ctx, t, chains, 1) + relayers := genRelayers(ctx, t, chains, 2) usedTicketsThreshold := 3 + coreumRecipient := chains.Coreum.GenAccount() notOwner := chains.Coreum.GenAccount() @@ -280,7 +284,7 @@ func TestRegisterXRPLToken(t *testing.T) { relayers, len(relayers), usedTicketsThreshold, - trustSetLimitAmount, + defaultTrustSetLimitAmount, ) // fund owner to cover registration fees twice @@ -290,7 +294,8 @@ func TestRegisterXRPLToken(t *testing.T) { issuerAcc := chains.XRPL.GenAccount(ctx, t, 0) issuer := issuerAcc.String() - currency := "CRA" + invactiveCurrency := "INA" + activeCurrency := "ACT" sendingPrecision := int32(15) maxHoldingAmount := sdk.NewIntFromUint64(10000) @@ -298,15 +303,15 @@ func TestRegisterXRPLToken(t *testing.T) { allocateInitialTickets(ctx, t, contractClient, owner, relayers) // try to register from not owner - _, err := contractClient.RegisterXRPLToken(ctx, notOwner, issuer, currency, sendingPrecision, maxHoldingAmount) + _, err := contractClient.RegisterXRPLToken(ctx, notOwner, issuer, invactiveCurrency, sendingPrecision, maxHoldingAmount) require.True(t, coreum.IsNotOwnerError(err), err) // register from the owner - _, err = contractClient.RegisterXRPLToken(ctx, owner, issuer, currency, sendingPrecision, maxHoldingAmount) + _, err = contractClient.RegisterXRPLToken(ctx, owner, issuer, invactiveCurrency, sendingPrecision, maxHoldingAmount) require.NoError(t, err) // try to register the same denom one more time - _, err = contractClient.RegisterXRPLToken(ctx, owner, issuer, currency, sendingPrecision, maxHoldingAmount) + _, err = contractClient.RegisterXRPLToken(ctx, owner, issuer, invactiveCurrency, sendingPrecision, maxHoldingAmount) require.True(t, coreum.IsXRPLTokenAlreadyRegisteredError(err), err) xrplTokens, err := contractClient.GetXRPLTokens(ctx) @@ -314,32 +319,31 @@ func TestRegisterXRPLToken(t *testing.T) { // one XRP token and registered require.Len(t, xrplTokens, 2) - var registeredToken coreum.XRPLToken - for _, token := range xrplTokens { - if token.Issuer == issuer && token.Currency == currency { - registeredToken = token - break - } - } - require.Equal(t, issuer, registeredToken.Issuer) - require.Equal(t, currency, registeredToken.Currency) - require.Equal(t, registeredToken.State, coreum.TokenStateProcessing) - require.NotEmpty(t, registeredToken.CoreumDenom) + registeredInactiveToken, err := contractClient.GetXRPLToken(ctx, issuer, invactiveCurrency) + require.NoError(t, err) + require.NotNil(t, registeredInactiveToken) + + require.Equal(t, coreum.XRPLToken{ + Issuer: issuer, + Currency: invactiveCurrency, + CoreumDenom: registeredInactiveToken.CoreumDenom, + State: coreum.TokenStateProcessing, + }, *registeredInactiveToken) // check that corresponding token is issued contractAddress := contractClient.GetContractAddress() tokenRes, err := assetftClient.Token(ctx, &assetfttypes.QueryTokenRequest{ - Denom: registeredToken.CoreumDenom, + Denom: registeredInactiveToken.CoreumDenom, }) require.NoError(t, err) // deconstruct the denom to get prefix used for the symbol and subunit - prefix, _, err := assetfttypes.DeconstructDenom(registeredToken.CoreumDenom) + prefix, _, err := assetfttypes.DeconstructDenom(registeredInactiveToken.CoreumDenom) require.NoError(t, err) require.Equal(t, assetfttypes.Token{ - Denom: registeredToken.CoreumDenom, + Denom: registeredInactiveToken.CoreumDenom, Issuer: contractAddress.String(), Symbol: strings.ToUpper(prefix), Subunit: prefix, @@ -351,6 +355,104 @@ func TestRegisterXRPLToken(t *testing.T) { SendCommissionRate: sdk.ZeroDec(), Version: assetfttypes.CurrentTokenVersion, }, tokenRes.Token) + + // go through the trust set evidence use cases + + pendingOperations, err := contractClient.GetPendingOperations(ctx) + require.NoError(t, err) + require.Len(t, pendingOperations, 1) + operation := pendingOperations[0] + require.NotNil(t, operation.OperationType.TrustSet) + + rejectTxEvidenceTrustSet := coreum.XRPLTransactionResultTrustSetEvidence{ + XRPLTransactionResultEvidence: coreum.XRPLTransactionResultEvidence{ + TxHash: genXRPLTxHash(t), + TicketNumber: &operation.TicketNumber, + TransactionResult: coreum.TransactionResultRejected, + }, + Issuer: issuer, + Currency: invactiveCurrency, + } + + // try to register not existing operation + invalidEvidenceTrustSetWithInvalidTicket := rejectTxEvidenceTrustSet + invalidEvidenceTrustSetWithInvalidTicket.TicketNumber = lo.ToPtr(uint32(99)) + _, err = contractClient.SendXRPLTrustSetTransactionResultEvidence(ctx, relayers[0].CoreumAddress, invalidEvidenceTrustSetWithInvalidTicket) + require.True(t, coreum.IsPendingOperationNotFoundError(err), err) + + // try to register with not existing currency + invalidEvidenceNotExistingIssuer := rejectTxEvidenceTrustSet + invalidEvidenceNotExistingIssuer.Issuer = xrpl.GenPrivKeyTxSigner().Account().String() + _, err = contractClient.SendXRPLTrustSetTransactionResultEvidence(ctx, relayers[0].CoreumAddress, invalidEvidenceNotExistingIssuer) + require.True(t, coreum.IsTokenNotRegisteredError(err), err) + + // send valid rejected evidence from first relayer + txResTrustSet, err := contractClient.SendXRPLTrustSetTransactionResultEvidence(ctx, relayers[0].CoreumAddress, rejectTxEvidenceTrustSet) + require.NoError(t, err) + thresholdReachedTrustSet, err := event.FindStringEventAttribute(txResTrustSet.Events, wasmtypes.ModuleName, eventAttributeThresholdReached) + require.NoError(t, err) + require.Equal(t, strconv.FormatBool(false), thresholdReachedTrustSet) + // send valid rejected evidence from second relayer + txResTrustSet, err = contractClient.SendXRPLTrustSetTransactionResultEvidence(ctx, relayers[1].CoreumAddress, rejectTxEvidenceTrustSet) + require.NoError(t, err) + thresholdReachedTrustSet, err = event.FindStringEventAttribute(txResTrustSet.Events, wasmtypes.ModuleName, eventAttributeThresholdReached) + require.NoError(t, err) + require.Equal(t, strconv.FormatBool(true), thresholdReachedTrustSet) + + registeredInactiveToken, err = contractClient.GetXRPLToken(ctx, issuer, invactiveCurrency) + require.NoError(t, err) + require.NotNil(t, registeredInactiveToken) + + require.Equal(t, coreum.XRPLToken{ + Issuer: issuer, + Currency: invactiveCurrency, + CoreumDenom: registeredInactiveToken.CoreumDenom, + State: coreum.TokenStateInactive, + }, *registeredInactiveToken) + + // try to send evidence one more time + _, err = contractClient.SendXRPLTrustSetTransactionResultEvidence(ctx, relayers[1].CoreumAddress, rejectTxEvidenceTrustSet) + require.True(t, coreum.IsOperationAlreadyExecutedError(err), err) + + // try to register the sending from the XRPL to coreum evidence with inactive token + xrplToCoreumInactiveTokenTransferEvidence := coreum.XRPLToCoreumTransferEvidence{ + TxHash: genXRPLTxHash(t), + Issuer: issuerAcc.String(), + Currency: invactiveCurrency, + Amount: sdkmath.NewInt(10), + Recipient: coreumRecipient, + } + _, err = contractClient.SendXRPLToCoreumTransferEvidence(ctx, relayers[1].CoreumAddress, xrplToCoreumInactiveTokenTransferEvidence) + require.True(t, coreum.IsXRPLTokenNotEnabledError(err), err) + + _ = activeCurrency + + // register one more token and activate it + _, err = contractClient.RegisterXRPLToken(ctx, owner, issuer, activeCurrency, sendingPrecision, maxHoldingAmount) + require.NoError(t, err) + + registeredActiveToken, err := contractClient.GetXRPLToken(ctx, issuer, activeCurrency) + require.NoError(t, err) + require.NotNil(t, registeredInactiveToken) + + require.Equal(t, coreum.XRPLToken{ + Issuer: issuer, + Currency: activeCurrency, + CoreumDenom: registeredActiveToken.CoreumDenom, + State: coreum.TokenStateProcessing, + }, *registeredActiveToken) + + activateXRPLToken(ctx, t, contractClient, relayers, issuer, activeCurrency) + + amountToSend := sdkmath.NewInt(99) + sendFromXRPLToCoreum(ctx, t, contractClient, relayers, issuer, activeCurrency, amountToSend, coreumRecipient) + + balanceRes, err := bankClient.Balance(ctx, &banktypes.QueryBalanceRequest{ + Address: coreumRecipient.String(), + Denom: registeredActiveToken.CoreumDenom, + }) + require.NoError(t, err) + require.Equal(t, amountToSend.String(), balanceRes.Balance.Amount.String()) } func TestSendFromXRPLToCoreumXRPLNativeToken(t *testing.T) { @@ -377,7 +479,7 @@ func TestSendFromXRPLToCoreumXRPLNativeToken(t *testing.T) { relayers, len(relayers), usedTicketsThreshold, - trustSetLimitAmount, + defaultTrustSetLimitAmount, ) issueFee := chains.Coreum.QueryAssetFTParams(ctx, t).IssueFee // fund owner to cover registration fees twice @@ -412,36 +514,8 @@ func TestSendFromXRPLToCoreumXRPLNativeToken(t *testing.T) { require.Equal(t, currency, registeredToken.Currency) require.NotEmpty(t, registeredToken.CoreumDenom) - pendingOperations, err := contractClient.GetPendingOperations(ctx) - require.NoError(t, err) - require.Len(t, pendingOperations, 1) - ticketAllocated := pendingOperations[0].TicketNumber - // activate token - acceptedTxHashTrustSet := genXRPLTxHash(t) - acceptedTxEvidenceTrustSet := coreum.XRPLTransactionResultTrustSetEvidence{ - XRPLTransactionResultEvidence: coreum.XRPLTransactionResultEvidence{ - TxHash: acceptedTxHashTrustSet, - TicketNumber: &ticketAllocated, - TransactionResult: coreum.TransactionResultAccepted, - }, - Issuer: issuer, - Currency: currency, - } - - // send evidence from first relayer - txResTrustSet, err := contractClient.SendXRPLTrustSetTransactionResultEvidence(ctx, relayers[0].CoreumAddress, acceptedTxEvidenceTrustSet) - require.NoError(t, err) - thresholdReachedTrustSet, err := event.FindStringEventAttribute(txResTrustSet.Events, wasmtypes.ModuleName, eventAttributeThresholdReached) - require.NoError(t, err) - require.Equal(t, "false", thresholdReachedTrustSet) - - // send evidence from second relayer - txResTrustSet, err = contractClient.SendXRPLTrustSetTransactionResultEvidence(ctx, relayers[1].CoreumAddress, acceptedTxEvidenceTrustSet) - require.NoError(t, err) - thresholdReachedTrustSet, err = event.FindStringEventAttribute(txResTrustSet.Events, wasmtypes.ModuleName, eventAttributeThresholdReached) - require.NoError(t, err) - require.Equal(t, "true", thresholdReachedTrustSet) + activateXRPLToken(ctx, t, contractClient, relayers, issuer, currency) // create an evidence to transfer tokens from XRPL to Coreum xrplToCoreumTransferEvidence := coreum.XRPLToCoreumTransferEvidence{ @@ -473,7 +547,7 @@ func TestSendFromXRPLToCoreumXRPLNativeToken(t *testing.T) { require.True(t, recipientBalanceRes.Balance.IsZero()) thresholdReached, err := event.FindStringEventAttribute(txRes.Events, wasmtypes.ModuleName, eventAttributeThresholdReached) require.NoError(t, err) - require.Equal(t, "false", thresholdReached) + require.Equal(t, strconv.FormatBool(false), thresholdReached) // call from first relayer one more time _, err = contractClient.SendXRPLToCoreumTransferEvidence(ctx, relayers[0].CoreumAddress, xrplToCoreumTransferEvidence) @@ -489,7 +563,7 @@ func TestSendFromXRPLToCoreumXRPLNativeToken(t *testing.T) { require.NoError(t, err) thresholdReached, err = event.FindStringEventAttribute(txRes.Events, wasmtypes.ModuleName, eventAttributeThresholdReached) require.NoError(t, err) - require.Equal(t, "true", thresholdReached) + require.Equal(t, strconv.FormatBool(true), thresholdReached) require.NoError(t, err) // expect new token on the recipient balance @@ -521,7 +595,7 @@ func TestSendFromXRPLToCoreumXRPLNativeTokenWithDifferentSendingPrecision(t *tes relayers, len(relayers), usedTicketsThreshold, - trustSetLimitAmount, + defaultTrustSetLimitAmount, ) // register tickets allocateInitialTickets(ctx, t, contractClient, owner, relayers) @@ -635,7 +709,7 @@ func TestSendFromXRPLToCoreumXRPLNativeTokenWithDifferentSendingPrecision(t *tes } // activate token - activateToken(ctx, t, contractClient, relayers, issuerAcc.String(), currency) + activateXRPLToken(ctx, t, contractClient, relayers, issuerAcc.String(), currency) // call from all relayers for _, relayer := range relayers { @@ -678,7 +752,7 @@ func TestRecoverTickets(t *testing.T) { relayers, 2, usedTicketsThreshold, - trustSetLimitAmount, + defaultTrustSetLimitAmount, ) // ********** Ticket allocation / Recovery ********** @@ -821,7 +895,7 @@ func TestRecoverTickets(t *testing.T) { require.NoError(t, err) thresholdReached, err := event.FindStringEventAttribute(txRes.Events, wasmtypes.ModuleName, eventAttributeThresholdReached) require.NoError(t, err) - require.Equal(t, "false", thresholdReached) + require.Equal(t, strconv.FormatBool(false), thresholdReached) // try to send evidence from second relayer one more time _, err = contractClient.SendXRPLTicketsAllocationTransactionResultEvidence(ctx, relayers[0].CoreumAddress, rejectedTxEvidence) @@ -832,7 +906,7 @@ func TestRecoverTickets(t *testing.T) { require.NoError(t, err) thresholdReached, err = event.FindStringEventAttribute(txRes.Events, wasmtypes.ModuleName, eventAttributeThresholdReached) require.NoError(t, err) - require.Equal(t, "true", thresholdReached) + require.Equal(t, strconv.FormatBool(true), thresholdReached) // try to send the evidence one more time _, err = contractClient.SendXRPLTicketsAllocationTransactionResultEvidence(ctx, relayers[0].CoreumAddress, rejectedTxEvidence) @@ -897,10 +971,9 @@ func TestRecoverTickets(t *testing.T) { // ********** TransactionResultEvidence / Transaction accepted ********** // we can omit the signing here since it is required only for the tx submission. - acceptedTxHash := genXRPLTxHash(t) acceptedTxEvidence := coreum.XRPLTransactionResultTicketsAllocationEvidence{ XRPLTransactionResultEvidence: coreum.XRPLTransactionResultEvidence{ - TxHash: acceptedTxHash, + TxHash: genXRPLTxHash(t), SequenceNumber: &secondBridgeAccountSeqNumber, TransactionResult: coreum.TransactionResultAccepted, }, @@ -918,14 +991,14 @@ func TestRecoverTickets(t *testing.T) { require.NoError(t, err) thresholdReached, err = event.FindStringEventAttribute(txRes.Events, wasmtypes.ModuleName, eventAttributeThresholdReached) require.NoError(t, err) - require.Equal(t, "false", thresholdReached) + require.Equal(t, strconv.FormatBool(false), thresholdReached) // send evidence from second relayer txRes, err = contractClient.SendXRPLTicketsAllocationTransactionResultEvidence(ctx, relayers[1].CoreumAddress, acceptedTxEvidence) require.NoError(t, err) thresholdReached, err = event.FindStringEventAttribute(txRes.Events, wasmtypes.ModuleName, eventAttributeThresholdReached) require.NoError(t, err) - require.Equal(t, "true", thresholdReached) + require.Equal(t, strconv.FormatBool(true), thresholdReached) pendingOperations, err = contractClient.GetPendingOperations(ctx) require.NoError(t, err) @@ -975,10 +1048,9 @@ func allocateInitialTickets( _, err := contractClient.RecoverTickets(ctx, owner, firstBridgeAccountSeqNumber, &numberOfTicketsToInit) require.NoError(t, err) - acceptedTxHash := genXRPLTxHash(t) acceptedTxEvidence := coreum.XRPLTransactionResultTicketsAllocationEvidence{ XRPLTransactionResultEvidence: coreum.XRPLTransactionResultEvidence{ - TxHash: acceptedTxHash, + TxHash: genXRPLTxHash(t), SequenceNumber: &firstBridgeAccountSeqNumber, TransactionResult: coreum.TransactionResultAccepted, }, @@ -992,13 +1064,13 @@ func allocateInitialTickets( require.NoError(t, err) thresholdReached, err := event.FindStringEventAttribute(txRes.Events, wasmtypes.ModuleName, eventAttributeThresholdReached) require.NoError(t, err) - if thresholdReached == "true" { + if thresholdReached == strconv.FormatBool(true) { break } } } -func activateToken( +func activateXRPLToken( ctx context.Context, t *testing.T, contractClient *coreum.ContractClient, @@ -1010,12 +1082,13 @@ func activateToken( pendingOperations, err := contractClient.GetPendingOperations(ctx) require.NoError(t, err) require.Len(t, pendingOperations, 1) - ticketAllocated := pendingOperations[0].TicketNumber + operation := pendingOperations[0] + require.NotNil(t, operation.OperationType.TrustSet) acceptedTxEvidenceTrustSet := coreum.XRPLTransactionResultTrustSetEvidence{ XRPLTransactionResultEvidence: coreum.XRPLTransactionResultEvidence{ TxHash: genXRPLTxHash(t), - TicketNumber: &ticketAllocated, + TicketNumber: &operation.TicketNumber, TransactionResult: coreum.TransactionResultAccepted, }, Issuer: issuer, @@ -1028,7 +1101,50 @@ func activateToken( require.NoError(t, err) thresholdReached, err := event.FindStringEventAttribute(txRes.Events, wasmtypes.ModuleName, eventAttributeThresholdReached) require.NoError(t, err) - if thresholdReached == "true" { + if thresholdReached == strconv.FormatBool(true) { + break + } + } + + // asset token state + registeredToken, err := contractClient.GetXRPLToken(ctx, issuer, currency) + require.NoError(t, err) + require.NotNil(t, registeredToken) + + require.Equal(t, coreum.XRPLToken{ + Issuer: issuer, + Currency: currency, + CoreumDenom: registeredToken.CoreumDenom, + State: coreum.TokenStateEnabled, + }, *registeredToken) +} + +func sendFromXRPLToCoreum( + ctx context.Context, + t *testing.T, + contractClient *coreum.ContractClient, + relayers []coreum.Relayer, + issuer, currency string, + amount sdkmath.Int, + coreumRecipient sdk.AccAddress, +) { + t.Helper() + + xrplToCoreumTransferEvidence := coreum.XRPLToCoreumTransferEvidence{ + TxHash: genXRPLTxHash(t), + Issuer: issuer, + Currency: currency, + Amount: amount, + Recipient: coreumRecipient, + } + + // send evidences from relayers + for _, relayer := range relayers { + txRes, err := contractClient.SendXRPLToCoreumTransferEvidence(ctx, relayer.CoreumAddress, xrplToCoreumTransferEvidence) + require.NoError(t, err) + thresholdReached, err := event.FindStringEventAttribute(txRes.Events, wasmtypes.ModuleName, eventAttributeThresholdReached) + require.NoError(t, err) + if thresholdReached == strconv.FormatBool(true) { break } } diff --git a/integration-tests/processes/env_test.go b/integration-tests/processes/env_test.go index 3bbc941f..00ba42e8 100644 --- a/integration-tests/processes/env_test.go +++ b/integration-tests/processes/env_test.go @@ -25,6 +25,7 @@ import ( integrationtests "github.com/CoreumFoundation/coreumbridge-xrpl/integration-tests" "github.com/CoreumFoundation/coreumbridge-xrpl/relayer/coreum" "github.com/CoreumFoundation/coreumbridge-xrpl/relayer/runner" + "github.com/CoreumFoundation/coreumbridge-xrpl/relayer/xrpl" ) const XRPLTokenDecimals = 15 @@ -37,7 +38,7 @@ type RunnerEnvConfig struct { MaliciousRelayerNumber int DisableMasterKey bool UsedTicketsThreshold int - TrustSetLimitAmount string + TrustSetLimitAmount sdkmath.Int } // DefaultRunnerEnvConfig returns default runner environment config. @@ -49,7 +50,7 @@ func DefaultRunnerEnvConfig() RunnerEnvConfig { MaliciousRelayerNumber: 0, DisableMasterKey: true, UsedTicketsThreshold: 150, - TrustSetLimitAmount: "10000000000000000", + TrustSetLimitAmount: sdkmath.NewIntWithDecimal(1, 30), } } @@ -60,6 +61,7 @@ type RunnerEnv struct { // TODO(dzmitryhil) replace with the relayer logic RelayerAddresses []sdk.AccAddress ContractClient *coreum.ContractClient + Chains integrationtests.Chains ContractOwner sdk.AccAddress Runners []*runner.Runner ProcessErrorsMu sync.RWMutex @@ -139,6 +141,7 @@ func NewRunnerEnv(ctx context.Context, t *testing.T, cfg RunnerEnvConfig, chains XRPLBridgeAccount: xrplBridgeAccount, RelayerAddresses: coreumRelayerAddresses, ContractClient: contractClient, + Chains: chains, ContractOwner: contractOwner, Runners: runners, ProcessErrorsMu: sync.RWMutex{}, @@ -232,40 +235,58 @@ func (r *RunnerEnv) AwaitState(ctx context.Context, t *testing.T, stateChecker f require.NoError(t, err) } -func (r *RunnerEnv) RequireNoErrors(t *testing.T) { - r.ProcessErrorsMu.RLock() - defer r.ProcessErrorsMu.RUnlock() - require.Empty(t, r.ProcessErrors, "Found unexpected process errors after the execution") +// AllocateTickets allocate initial tickets amount. +func (r *RunnerEnv) AllocateTickets( + ctx context.Context, + t *testing.T, + numberOfTicketsToAllocate uint32, +) { + xrplBridgeAccountInfo, err := r.Chains.XRPL.RPCClient().AccountInfo(ctx, r.XRPLBridgeAccount) + require.NoError(t, err) + + r.Chains.XRPL.FundAccountForTicketAllocation(ctx, t, r.XRPLBridgeAccount, numberOfTicketsToAllocate) + _, err = r.ContractClient.RecoverTickets(ctx, r.ContractOwner, *xrplBridgeAccountInfo.AccountData.Sequence, &numberOfTicketsToAllocate) + require.NoError(t, err) + + require.NoError(t, err) + r.AwaitNoPendingOperations(ctx, t) + availableTickets, err := r.ContractClient.GetAvailableTickets(ctx) + require.NoError(t, err) + require.Len(t, availableTickets, int(numberOfTicketsToAllocate)) } -// SendTrustSet sends TrustSet transaction. -func SendTrustSet( +// RegisterXRPLTokenAndAwaitTrustSet registers XRPL currency and awaits for the trust set ot be set. +func (r *RunnerEnv) RegisterXRPLTokenAndAwaitTrustSet( ctx context.Context, t *testing.T, - xrplChain integrationtests.XRPLChain, - issuer, sender rippledata.Account, + issuer rippledata.Account, currency rippledata.Currency, -) { - trustSetValue, err := rippledata.NewValue("10e20", false) + sendingPrecision int32, + maxHoldingAmount sdkmath.Int, +) coreum.XRPLToken { + _, err := r.ContractClient.RegisterXRPLToken(ctx, r.ContractOwner, issuer.String(), xrpl.ConvertCurrencyToString(currency), sendingPrecision, maxHoldingAmount) require.NoError(t, err) - senderCurrencyTrustSetTx := rippledata.TrustSet{ - LimitAmount: rippledata.Amount{ - Value: trustSetValue, - Currency: currency, - Issuer: issuer, - }, - TxBase: rippledata.TxBase{ - TransactionType: rippledata.TRUST_SET, - }, - } - require.NoError(t, xrplChain.AutoFillSignAndSubmitTx(ctx, t, &senderCurrencyTrustSetTx, sender)) + // await for the trust set + r.AwaitNoPendingOperations(ctx, t) + xrplRegisteredToken, err := r.ContractClient.GetXRPLToken(ctx, issuer.String(), xrpl.ConvertCurrencyToString(currency)) + require.NoError(t, err) + require.NotNil(t, xrplRegisteredToken) + require.Equal(t, coreum.TokenStateEnabled, xrplRegisteredToken.State) + + return *xrplRegisteredToken +} + +// RequireNoErrors check whether the runner err received runner errors. +func (r *RunnerEnv) RequireNoErrors(t *testing.T) { + r.ProcessErrorsMu.RLock() + defer r.ProcessErrorsMu.RUnlock() + require.Empty(t, r.ProcessErrors, "Found unexpected process errors after the execution") } // SendXRPLPaymentTx sends Payment transaction. -func SendXRPLPaymentTx( +func (r *RunnerEnv) SendXRPLPaymentTx( ctx context.Context, t *testing.T, - xrplChain integrationtests.XRPLChain, senderAcc, recipientAcc rippledata.Account, amount rippledata.Amount, memo rippledata.Memo, @@ -280,14 +301,13 @@ func SendXRPLPaymentTx( }, }, } - require.NoError(t, xrplChain.AutoFillSignAndSubmitTx(ctx, t, &xrpPaymentTx, senderAcc)) + require.NoError(t, r.Chains.XRPL.AutoFillSignAndSubmitTx(ctx, t, &xrpPaymentTx, senderAcc)) } // SendXRPLPartialPaymentTx sends Payment transaction with partial payment. -func SendXRPLPartialPaymentTx( +func (r *RunnerEnv) SendXRPLPartialPaymentTx( ctx context.Context, t *testing.T, - xrplChain integrationtests.XRPLChain, senderAcc, recipientAcc rippledata.Account, amount rippledata.Amount, maxAmount rippledata.Amount, @@ -305,7 +325,7 @@ func SendXRPLPartialPaymentTx( Flags: lo.ToPtr(rippledata.TxPartialPayment), }, } - require.NoError(t, xrplChain.AutoFillSignAndSubmitTx(ctx, t, &xrpPaymentTx, senderAcc)) + require.NoError(t, r.Chains.XRPL.AutoFillSignAndSubmitTx(ctx, t, &xrpPaymentTx, senderAcc)) } func genCoreumRelayers( diff --git a/integration-tests/processes/send_from_xrpl_to_coreum_test.go b/integration-tests/processes/send_from_xrpl_to_coreum_test.go index bcb2ac1c..05190fdc 100644 --- a/integration-tests/processes/send_from_xrpl_to_coreum_test.go +++ b/integration-tests/processes/send_from_xrpl_to_coreum_test.go @@ -4,8 +4,6 @@ package processes_test import ( - "context" - "crypto/rand" "encoding/hex" "strings" "testing" @@ -20,7 +18,7 @@ import ( "github.com/CoreumFoundation/coreumbridge-xrpl/relayer/xrpl" ) -func TestSendFromXRPLToCoreumWithManualTrustSet(t *testing.T) { +func TestRegisterXRPLTokensAndSendFromXRPLToCoreum(t *testing.T) { t.Parallel() ctx, chains := integrationtests.NewTestingContext(t) @@ -35,8 +33,6 @@ func TestSendFromXRPLToCoreumWithManualTrustSet(t *testing.T) { maxHoldingAmount := integrationtests.ConvertStringWithDecimalsToSDKInt(t, "1", 30) envCfg := DefaultRunnerEnvConfig() - // we need it to manually do the TrustSet - envCfg.DisableMasterKey = false runnerEnv := NewRunnerEnv(ctx, t, envCfg, chains) xrplRegisteredCurrency, err := rippledata.NewCurrency("RCP") @@ -47,82 +43,49 @@ func TestSendFromXRPLToCoreumWithManualTrustSet(t *testing.T) { xrplRegisteredHexCurrency, err := rippledata.NewCurrency(currencyHexSymbol) require.NoError(t, err) - xrplNotRegisterCurrency, err := rippledata.NewCurrency("NRG") - require.NoError(t, err) - - // set trust for all tokens - SendTrustSet(ctx, t, chains.XRPL, xrplCurrencyIssuerAcc, runnerEnv.XRPLBridgeAccount, xrplRegisteredCurrency) - SendTrustSet(ctx, t, chains.XRPL, xrplCurrencyIssuerAcc, runnerEnv.XRPLBridgeAccount, xrplRegisteredHexCurrency) - SendTrustSet(ctx, t, chains.XRPL, xrplCurrencyIssuerAcc, runnerEnv.XRPLBridgeAccount, xrplNotRegisterCurrency) - // fund owner to cover registration fees chains.Coreum.FundAccountWithOptions(ctx, t, runnerEnv.ContractOwner, coreumintegration.BalancesOptions{ Amount: chains.Coreum.QueryAssetFTParams(ctx, t).IssueFee.Amount.MulRaw(2), }) - // recover tickets so that we can register tokens - numberOfTicketsToInit := uint32(200) - firstBridgeAccountSeqNumber := uint32(1) - _, err = runnerEnv.ContractClient.RecoverTickets(ctx, runnerEnv.ContractOwner, firstBridgeAccountSeqNumber, &numberOfTicketsToInit) + xrplBridgeAccountInfo, err := chains.XRPL.RPCClient().AccountInfo(ctx, runnerEnv.XRPLBridgeAccount) require.NoError(t, err) - // Send evidences from relayers - acceptedTxHash := "D5F78F452DFFBE239EFF668E4B34B1AF66CD2F4D5C5D9E54A5AF34121B5862C5" - tickets := make([]uint32, 200) - for i := range tickets { - tickets[i] = uint32(i) - } + // recover tickets so we can register tokens + numberOfTicketsToAllocate := uint32(200) + chains.XRPL.FundAccountForTicketAllocation(ctx, t, runnerEnv.XRPLBridgeAccount, numberOfTicketsToAllocate) + _, err = runnerEnv.ContractClient.RecoverTickets(ctx, runnerEnv.ContractOwner, *xrplBridgeAccountInfo.AccountData.Sequence, &numberOfTicketsToAllocate) + require.NoError(t, err) - acceptedTxEvidence := coreum.XRPLTransactionResultTicketsAllocationEvidence{ - XRPLTransactionResultEvidence: coreum.XRPLTransactionResultEvidence{ - TxHash: acceptedTxHash, - SequenceNumber: &firstBridgeAccountSeqNumber, - TransactionResult: coreum.TransactionResultAccepted, - }, - Tickets: tickets, - } + // start relayers + runnerEnv.StartAllRunnerProcesses(ctx, t) - // send evidences from relayers - for i := 0; i < runnerEnv.Cfg.SigningThreshold; i++ { - _, err = runnerEnv.ContractClient.SendXRPLTicketsAllocationTransactionResultEvidence(ctx, runnerEnv.RelayerAddresses[i], acceptedTxEvidence) - require.NoError(t, err) - } + require.NoError(t, err) + runnerEnv.AwaitNoPendingOperations(ctx, t) + availableTickets, err := runnerEnv.ContractClient.GetAvailableTickets(ctx) + require.NoError(t, err) + require.Len(t, availableTickets, int(numberOfTicketsToAllocate)) // register XRPL native token with 3 chars _, err = runnerEnv.ContractClient.RegisterXRPLToken(ctx, runnerEnv.ContractOwner, xrplCurrencyIssuerAcc.String(), xrpl.ConvertCurrencyToString(xrplRegisteredCurrency), sendingPrecision, maxHoldingAmount) require.NoError(t, err) - // activate the token - activateToken(ctx, t, runnerEnv, xrplCurrencyIssuerAcc.String(), xrpl.ConvertCurrencyToString(xrplRegisteredCurrency)) - // register XRPL native token with 20 chars _, err = runnerEnv.ContractClient.RegisterXRPLToken(ctx, runnerEnv.ContractOwner, xrplCurrencyIssuerAcc.String(), xrpl.ConvertCurrencyToString(xrplRegisteredHexCurrency), sendingPrecision, maxHoldingAmount) require.NoError(t, err) - // activate the token - activateToken(ctx, t, runnerEnv, xrplCurrencyIssuerAcc.String(), xrpl.ConvertCurrencyToString(xrplRegisteredHexCurrency)) - - registeredXRPLTokens, err := runnerEnv.ContractClient.GetXRPLTokens(ctx) - require.NoError(t, err) - // take the tokens with the generated denom - var ( - registeredXRPLToken coreum.XRPLToken - registeredXRPLTokenHexCurrency coreum.XRPLToken - ) - for _, token := range registeredXRPLTokens { - if token.Issuer == xrplCurrencyIssuerAcc.String() && token.Currency == xrplRegisteredCurrency.String() { - registeredXRPLToken = token - continue - } - if token.Issuer == xrplCurrencyIssuerAcc.String() && token.Currency == xrpl.ConvertCurrencyToString(xrplRegisteredHexCurrency) { - registeredXRPLTokenHexCurrency = token - continue - } - } - require.NotEmpty(t, registeredXRPLToken.CoreumDenom) - require.NotEmpty(t, registeredXRPLTokenHexCurrency.CoreumDenom) + // await for the trust set + runnerEnv.AwaitNoPendingOperations(ctx, t) - runnerEnv.StartAllRunnerProcesses(ctx, t) + xrplRegisteredToken, err := runnerEnv.ContractClient.GetXRPLToken(ctx, xrplCurrencyIssuerAcc.String(), xrpl.ConvertCurrencyToString(xrplRegisteredCurrency)) + require.NoError(t, err) + require.NotNil(t, xrplRegisteredToken) + require.Equal(t, coreum.TokenStateEnabled, xrplRegisteredToken.State) + + xrplRegisteredHexCurrencyToken, err := runnerEnv.ContractClient.GetXRPLToken(ctx, xrplCurrencyIssuerAcc.String(), xrpl.ConvertCurrencyToString(xrplRegisteredHexCurrency)) + require.NoError(t, err) + require.NotNil(t, xrplRegisteredHexCurrencyToken) + require.Equal(t, coreum.TokenStateEnabled, xrplRegisteredHexCurrencyToken.State) lowValue, err := rippledata.NewValue("1.00000111", false) require.NoError(t, err) @@ -151,66 +114,71 @@ func TestSendFromXRPLToCoreumWithManualTrustSet(t *testing.T) { memo, err := xrpl.EncodeCoreumRecipientToMemo(coreumRecipient) require.NoError(t, err) - // send incorrect transactions - - // currency is not registered - xrplNotRegisterCurrencyAmount := rippledata.Amount{ - Value: lowValue, - Currency: xrplNotRegisterCurrency, - Issuer: xrplCurrencyIssuerAcc, - } - SendXRPLPaymentTx(ctx, t, chains.XRPL, xrplCurrencyIssuerAcc, runnerEnv.XRPLBridgeAccount, xrplNotRegisterCurrencyAmount, memo) - // incorrect memo - SendXRPLPaymentTx(ctx, t, chains.XRPL, xrplCurrencyIssuerAcc, runnerEnv.XRPLBridgeAccount, maxDecimalsRegisterCurrencyAmount, rippledata.Memo{}) + runnerEnv.SendXRPLPaymentTx(ctx, t, xrplCurrencyIssuerAcc, runnerEnv.XRPLBridgeAccount, maxDecimalsRegisterCurrencyAmount, rippledata.Memo{}) // send correct transactions // send tx with partial payment - SendXRPLPartialPaymentTx(ctx, t, chains.XRPL, xrplCurrencyIssuerAcc, runnerEnv.XRPLBridgeAccount, highValueRegisteredCurrencyAmount, maxDecimalsRegisterCurrencyAmount, memo) + runnerEnv.SendXRPLPartialPaymentTx(ctx, t, xrplCurrencyIssuerAcc, runnerEnv.XRPLBridgeAccount, highValueRegisteredCurrencyAmount, maxDecimalsRegisterCurrencyAmount, memo) // send tx with high amount - SendXRPLPaymentTx(ctx, t, chains.XRPL, xrplCurrencyIssuerAcc, runnerEnv.XRPLBridgeAccount, highValueRegisteredCurrencyAmount, memo) + runnerEnv.SendXRPLPaymentTx(ctx, t, xrplCurrencyIssuerAcc, runnerEnv.XRPLBridgeAccount, highValueRegisteredCurrencyAmount, memo) // send tx with hex currency - SendXRPLPaymentTx(ctx, t, chains.XRPL, xrplCurrencyIssuerAcc, runnerEnv.XRPLBridgeAccount, registeredHexCurrencyAmount, memo) + runnerEnv.SendXRPLPaymentTx(ctx, t, xrplCurrencyIssuerAcc, runnerEnv.XRPLBridgeAccount, registeredHexCurrencyAmount, memo) - runnerEnv.AwaitCoreumBalance(ctx, t, chains.Coreum, coreumRecipient, sdk.NewCoin(registeredXRPLToken.CoreumDenom, integrationtests.ConvertStringWithDecimalsToSDKInt(t, "100001.000001", XRPLTokenDecimals))) - runnerEnv.AwaitCoreumBalance(ctx, t, chains.Coreum, coreumRecipient, sdk.NewCoin(registeredXRPLTokenHexCurrency.CoreumDenom, integrationtests.ConvertStringWithDecimalsToSDKInt(t, "9.9", XRPLTokenDecimals))) + runnerEnv.AwaitCoreumBalance(ctx, t, chains.Coreum, coreumRecipient, sdk.NewCoin(xrplRegisteredToken.CoreumDenom, integrationtests.ConvertStringWithDecimalsToSDKInt(t, "100001.000001", XRPLTokenDecimals))) + runnerEnv.AwaitCoreumBalance(ctx, t, chains.Coreum, coreumRecipient, sdk.NewCoin(xrplRegisteredHexCurrencyToken.CoreumDenom, integrationtests.ConvertStringWithDecimalsToSDKInt(t, "9.9", XRPLTokenDecimals))) } -// TODO(dzmitryhil) remove the manual activation and use automatic VIA relayer. -func activateToken(ctx context.Context, t *testing.T, runnerEnv *RunnerEnv, issuer, currency string) { - t.Helper() +func TestSendFromXRPLToCoreumWithMaliciousRelayer(t *testing.T) { + t.Parallel() - pendingOperations, err := runnerEnv.ContractClient.GetPendingOperations(ctx) - require.NoError(t, err) - require.Len(t, pendingOperations, 1) - ticketAllocated := pendingOperations[0].TicketNumber + ctx, chains := integrationtests.NewTestingContext(t) - acceptedTxHashTrustSet, err := randomTxHash(40) + xrplCurrencyIssuerAcc := chains.XRPL.GenAccount(ctx, t, 100) + t.Logf("XRPL currency issuer account: %s", xrplCurrencyIssuerAcc) + + coreumRecipient := chains.Coreum.GenAccount() + t.Logf("Coreum recipient: %s", coreumRecipient.String()) + + sendingPrecision := int32(6) + maxHoldingAmount := integrationtests.ConvertStringWithDecimalsToSDKInt(t, "1", 30) + + envCfg := DefaultRunnerEnvConfig() + // add malicious relayers to the config + envCfg.RelayerNumber = 5 + envCfg.MaliciousRelayerNumber = 2 + envCfg.SigningThreshold = 3 + runnerEnv := NewRunnerEnv(ctx, t, envCfg, chains) + + xrplRegisteredCurrency, err := rippledata.NewCurrency("CRC") require.NoError(t, err) - acceptedTxEvidenceTrustSet := coreum.XRPLTransactionResultTrustSetEvidence{ - XRPLTransactionResultEvidence: coreum.XRPLTransactionResultEvidence{ - TxHash: acceptedTxHashTrustSet, - TicketNumber: &ticketAllocated, - TransactionResult: coreum.TransactionResultAccepted, - }, - Issuer: issuer, - Currency: currency, - } - // send evidences from relayers - for i := 0; i < runnerEnv.Cfg.SigningThreshold; i++ { - _, err = runnerEnv.ContractClient.SendXRPLTrustSetTransactionResultEvidence(ctx, runnerEnv.RelayerAddresses[i], acceptedTxEvidenceTrustSet) - require.NoError(t, err) - } -} + // fund owner to cover registration fees + chains.Coreum.FundAccountWithOptions(ctx, t, runnerEnv.ContractOwner, coreumintegration.BalancesOptions{ + Amount: chains.Coreum.QueryAssetFTParams(ctx, t).IssueFee.Amount, + }) + + // start relayers + runnerEnv.StartAllRunnerProcesses(ctx, t) + runnerEnv.AllocateTickets(ctx, t, 200) + + // register XRPL token + xrplRegisteredToken := runnerEnv.RegisterXRPLTokenAndAwaitTrustSet(ctx, t, xrplCurrencyIssuerAcc, xrplRegisteredCurrency, sendingPrecision, maxHoldingAmount) -func randomTxHash(n int) (string, error) { - bytes := make([]byte, n) - if _, err := rand.Read(bytes); err != nil { - return "", err + // send + value, err := rippledata.NewValue("9999999999999.1111", false) + require.NoError(t, err) + registerCurrencyAmount := rippledata.Amount{ + Value: value, + Currency: xrplRegisteredCurrency, + Issuer: xrplCurrencyIssuerAcc, } - return hex.EncodeToString(bytes), nil + memo, err := xrpl.EncodeCoreumRecipientToMemo(coreumRecipient) + require.NoError(t, err) + + runnerEnv.SendXRPLPaymentTx(ctx, t, xrplCurrencyIssuerAcc, runnerEnv.XRPLBridgeAccount, registerCurrencyAmount, memo) + runnerEnv.AwaitCoreumBalance(ctx, t, chains.Coreum, coreumRecipient, sdk.NewCoin(xrplRegisteredToken.CoreumDenom, integrationtests.ConvertStringWithDecimalsToSDKInt(t, "9999999999999.111", XRPLTokenDecimals))) } diff --git a/integration-tests/processes/ticket_allocation_test.go b/integration-tests/processes/ticket_allocation_test.go index ca7326fc..3e8944dc 100644 --- a/integration-tests/processes/ticket_allocation_test.go +++ b/integration-tests/processes/ticket_allocation_test.go @@ -11,7 +11,7 @@ import ( integrationtests "github.com/CoreumFoundation/coreumbridge-xrpl/integration-tests" ) -// TODO(dzmitryhil) add the additional test for the re-allocation of the tickets without provided number once we have more operations +// TODO(dzmitryhil) add the additional test for each operation which might cause the re-allocation func TestTicketsAllocationRecoveryWithSequenceNumber(t *testing.T) { t.Parallel() diff --git a/relayer/coreum/contract.go b/relayer/coreum/contract.go index 49cc9ca8..9f5ee4ce 100644 --- a/relayer/coreum/contract.go +++ b/relayer/coreum/contract.go @@ -51,10 +51,10 @@ type TokenState string // TokenState values. const ( - TokenStateActive TokenState = "active" - TokenStateInactive TokenState = "inactive" + TokenStateEnabled TokenState = "enabled" TokenStateDisabled TokenState = "disabled" TokenStateProcessing TokenState = "processing" + TokenStateInactive TokenState = "inactive" ) // QueryMethod is contract query method. @@ -85,6 +85,7 @@ const ( amountSentIsZeroAfterTruncatingErrorString = "AmountSentIsZeroAfterTruncation" maximumBridgedAmountReachedErrorString = "MaximumBridgedAmountReached" stillHaveAvailableTicketsErrorString = "StillHaveAvailableTickets" + xrplTokenNotEnabledErrorString = "XRPLTokenNotEnabled" ) // Relayer is the relayer information in the contract config. @@ -101,15 +102,15 @@ type InstantiationConfig struct { Relayers []Relayer EvidenceThreshold int UsedTicketsThreshold int - TrustSetLimitAmount string + TrustSetLimitAmount sdkmath.Int } // ContractConfig is contract config. type ContractConfig struct { - Relayers []Relayer `json:"relayers"` - EvidenceThreshold int `json:"evidence_threshold"` - UsedTicketsThreshold int `json:"used_tickets_threshold"` - TrustSetLimitAmount string `json:"trust_set_limit_amount"` + Relayers []Relayer `json:"relayers"` + EvidenceThreshold int `json:"evidence_threshold"` + UsedTicketsThreshold int `json:"used_tickets_threshold"` + TrustSetLimitAmount sdkmath.Int `json:"trust_set_limit_amount"` } // ContractOwnership is owner contract config. @@ -178,9 +179,17 @@ type OperationTypeAllocateTickets struct { Number uint32 `json:"number"` } +// OperationTypeTrustSet is trust set operation type. +type OperationTypeTrustSet struct { + Issuer string `json:"issuer"` + Currency string `json:"currency"` + TrustSetLimitAmount sdkmath.Int `json:"trust_set_limit_amount"` +} + // OperationType is operation type. type OperationType struct { AllocateTickets *OperationTypeAllocateTickets `json:"allocate_tickets,omitempty"` + TrustSet *OperationTypeTrustSet `json:"trust_set,omitempty"` } // Operation is contract operation which should be signed and executed. @@ -207,7 +216,7 @@ type instantiateRequest struct { Relayers []Relayer `json:"relayers"` EvidenceThreshold int `json:"evidence_threshold"` UsedTicketsThreshold int `json:"used_tickets_threshold"` - TrustSetLimitAmount string `json:"trust_set_limit_amount"` + TrustSetLimitAmount sdkmath.Int `json:"trust_set_limit_amount"` } type transferOwnershipRequest struct { @@ -222,10 +231,10 @@ type registerCoreumTokenRequest struct { } type registerXRPLTokenRequest struct { - Issuer string `json:"issuer"` - Currency string `json:"currency"` - SendingPrecision int32 `json:"sending_precision"` - MaxHoldingAmount string `json:"max_holding_amount"` + Issuer string `json:"issuer"` + Currency string `json:"currency"` + SendingPrecision int32 `json:"sending_precision"` + MaxHoldingAmount sdkmath.Int `json:"max_holding_amount"` } type saveEvidenceRequest struct { @@ -488,7 +497,7 @@ func (c *ContractClient) RegisterXRPLToken(ctx context.Context, sender sdk.AccAd Issuer: issuer, Currency: currency, SendingPrecision: sendingPrecision, - MaxHoldingAmount: maxHoldingAmount.String(), + MaxHoldingAmount: maxHoldingAmount, }, }, Funds: sdk.NewCoins(fee), @@ -634,6 +643,21 @@ func (c *ContractClient) GetContractOwnership(ctx context.Context) (ContractOwne return response, nil } +// GetXRPLToken returns an XRPL registered token or nil. +func (c *ContractClient) GetXRPLToken(ctx context.Context, issuer, currency string) (*XRPLToken, error) { + tokens, err := c.GetXRPLTokens(ctx) + if err != nil { + return nil, err + } + for _, token := range tokens { + if token.Issuer == issuer && token.Currency == currency { + return &token, nil + } + } + + return nil, nil //nolint:nilnil // is token not found we return nil instead of an error +} + // GetXRPLTokens returns a list of all XRPL tokens. func (c *ContractClient) GetXRPLTokens(ctx context.Context) ([]XRPLToken, error) { tokens := make([]XRPLToken, 0) @@ -875,6 +899,11 @@ func IsStillHaveAvailableTicketsError(err error) bool { return isError(err, stillHaveAvailableTicketsErrorString) } +// IsXRPLTokenNotEnabledError returns true if error is `XRPLTokenNotEnabled` error. +func IsXRPLTokenNotEnabledError(err error) bool { + return isError(err, xrplTokenNotEnabledErrorString) +} + func isError(err error, errorString string) bool { return err != nil && strings.Contains(err.Error(), errorString) } diff --git a/relayer/processes/amount.go b/relayer/processes/amount.go index c449f690..2ee0aba9 100644 --- a/relayer/processes/amount.go +++ b/relayer/processes/amount.go @@ -1,37 +1,87 @@ package processes import ( + "fmt" + "math/big" + sdkmath "cosmossdk.io/math" "github.com/pkg/errors" rippledata "github.com/rubblelabs/ripple/data" ) const ( - xrplCurrencyDecimals = 15 - xrplXRPDecimals = 6 + // XRPLAmountPrec is precision we use to covert float to float string for the amount representation. + // That value is value which corelates with the min/max sending precision. + XRPLAmountPrec = 16 + // XRPLIssuedCurrencyDecimals is XRPL decimals used the on coreum. + XRPLIssuedCurrencyDecimals = 15 + // XRPIssuer is XRP issuer name used the on coreum. + XRPIssuer = "rrrrrrrrrrrrrrrrrrrrrho" + // XRPCurrency is XRP currency name used the on coreum. + XRPCurrency = "XRP" ) -// ConvertXRPLNativeTokenAmountToCoreum converts the XRPL native token amount to coreum based on the currency type. -func ConvertXRPLNativeTokenAmountToCoreum(xrplAmount rippledata.Amount) (sdkmath.Int, error) { +// ConvertXRPLNativeTokenXRPLAmountToCoreumAmount converts the XRPL native token amount from XRPL to coreum amount +// based on the currency type. +func ConvertXRPLNativeTokenXRPLAmountToCoreumAmount(xrplAmount rippledata.Amount) (sdkmath.Int, error) { if xrplAmount.Value == nil { return sdkmath.ZeroInt(), nil } - prec := xrplCurrencyDecimals - // is token is XRP + xrplRatAmount := xrplAmount.Value.Rat() + // native amount is represented as int value if xrplAmount.IsNative() { - prec = xrplXRPDecimals + return sdkmath.NewIntFromBigInt(xrplRatAmount.Num()), nil + } + // not XRP value is repressed as value multiplied by 10^15 + tenPowerDec := big.NewInt(0).Exp(big.NewInt(10), big.NewInt(int64(XRPLIssuedCurrencyDecimals)), nil) + binIntAmount := big.NewInt(0).Quo(big.NewInt(0).Mul(tenPowerDec, xrplRatAmount.Num()), xrplRatAmount.Denom()) + if binIntAmount.BitLen() > sdkmath.MaxBitLen { + return sdkmath.Int{}, errors.New("failed to convert big.Int to sdkmath.Int, out of bound") + } + + return sdkmath.NewIntFromBigInt(binIntAmount), nil +} + +// ConvertXRPLNativeTokenCoreumAmountToXRPLAmount converts the XRPL native token amount from coreum to XRPL amount +// based on the currency type. +func ConvertXRPLNativeTokenCoreumAmountToXRPLAmount(coreumAmount sdkmath.Int, issuerString, currencyString string) (rippledata.Amount, error) { + if isXRPToken(issuerString, currencyString) { + // format with exponent + amountString := big.NewFloat(0).SetInt(coreumAmount.BigInt()).Text('g', XRPLAmountPrec) + // we don't use the decimals for the XRP values since the `NewValue` function will do it automatically + xrplValue, err := rippledata.NewValue(amountString, true) + if err != nil { + return rippledata.Amount{}, errors.Wrapf(err, "failed to convert amount stringy to ripple.Value, amount stirng: %s", amountString) + } + return rippledata.Amount{ + Value: xrplValue, + }, nil } - // by default the sdkmath.Dec uses 18 decimals as max, so if you plan to use more that logic must be changed - floatString := xrplAmount.Value.Rat().FloatString(prec) - rawAmount, err := sdkmath.LegacyNewDecFromStr(floatString) + tenPowerDec := big.NewInt(0).Exp(big.NewInt(10), big.NewInt(int64(XRPLIssuedCurrencyDecimals)), nil) + floatAmount := big.NewFloat(0).SetRat(big.NewRat(0, 1).SetFrac(coreumAmount.BigInt(), tenPowerDec)) + // format with exponent + amountString := fmt.Sprintf("%s/%s/%s", floatAmount.Text('g', XRPLAmountPrec), currencyString, issuerString) + xrplValue, err := rippledata.NewValue(amountString, false) if err != nil { - return sdkmath.Int{}, errors.Wrapf(err, "failed to convert XRPL amount to sdkmath.Dec") + return rippledata.Amount{}, errors.Wrapf(err, "failed to convert amount string to ripple.Value, amount stirng: %s", amountString) } - // native amount is represented as int value - if xrplAmount.IsNative() { - return rawAmount.TruncateInt(), nil + currency, err := rippledata.NewCurrency(currencyString) + if err != nil { + return rippledata.Amount{}, errors.Wrapf(err, "failed to convert currency to ripple.Currency, currency: %s", currencyString) + } + issuer, err := rippledata.NewAccountFromAddress(issuerString) + if err != nil { + return rippledata.Amount{}, errors.Wrapf(err, "failed to convert issuer to ripple.Account, issuer: %s", issuerString) } - // not native value is repressed as value multiby by -10^15 - return rawAmount.MulInt(sdkmath.NewIntWithDecimal(1, prec)).TruncateInt(), nil + + return rippledata.Amount{ + Value: xrplValue, + Currency: currency, + Issuer: *issuer, + }, nil +} + +func isXRPToken(issuer, currency string) bool { + return issuer == XRPIssuer && currency == XRPCurrency } diff --git a/relayer/processes/amount_test.go b/relayer/processes/amount_test.go index 90ad5051..5abe9931 100644 --- a/relayer/processes/amount_test.go +++ b/relayer/processes/amount_test.go @@ -1,6 +1,7 @@ package processes_test import ( + "fmt" "testing" sdkmath "cosmossdk.io/math" @@ -8,11 +9,17 @@ import ( "github.com/stretchr/testify/require" "github.com/CoreumFoundation/coreumbridge-xrpl/relayer/processes" + "github.com/CoreumFoundation/coreumbridge-xrpl/relayer/xrpl" ) -func TestConvertXRPLNativeTokenAmountToCoreum(t *testing.T) { +func TestConvertXRPLNativeTokenXRPLAmountToCoreumAmount(t *testing.T) { t.Parallel() + var ( + fooIssuer = xrpl.GenPrivKeyTxSigner().Account().String() + fooCurrency = "FOO" + ) + tests := []struct { name string xrplAmount rippledata.Amount @@ -46,27 +53,27 @@ func TestConvertXRPLNativeTokenAmountToCoreum(t *testing.T) { }, { name: "one_XRPL_FOO_to_coreum_FOO", - xrplAmount: amountStringToXRPLAmount(t, "1.0/FOO/rE6BWGaND13tXp8kzBVNhgcu1remuhmXk6"), + xrplAmount: amountStringToXRPLAmount(t, fmt.Sprintf("1.0/%s/%s", fooCurrency, fooIssuer)), want: stringToSDKInt(t, "1000000000000000"), }, { name: "one_with_some_decimals_XRPL_FOO_to_coreum_FOO", - xrplAmount: amountStringToXRPLAmount(t, "1.0000000001/FOO/rE6BWGaND13tXp8kzBVNhgcu1remuhmXk6"), + xrplAmount: amountStringToXRPLAmount(t, fmt.Sprintf("1.0000000001/%s/%s", fooCurrency, fooIssuer)), want: sdkmath.NewIntFromUint64(1000000000100000), }, { name: "min_decimals_XRPL_FOO_to_coreum_FOO", - xrplAmount: amountStringToXRPLAmount(t, "0.000000000000001/FOO/rE6BWGaND13tXp8kzBVNhgcu1remuhmXk6"), + xrplAmount: amountStringToXRPLAmount(t, fmt.Sprintf("0.000000000000001/%s/%s", fooCurrency, fooIssuer)), want: sdkmath.NewIntFromUint64(1), }, { name: "high_value_XRPL_FOO_to_coreum_FOO", - xrplAmount: amountStringToXRPLAmount(t, "1.1e10/FOO/rE6BWGaND13tXp8kzBVNhgcu1remuhmXk6"), + xrplAmount: amountStringToXRPLAmount(t, fmt.Sprintf("1.1e10/%s/%s", fooCurrency, fooIssuer)), want: stringToSDKInt(t, "11000000000000000000000000"), }, { name: "invalid_foo_amount", - xrplAmount: amountStringToXRPLAmount(t, "1e92/FOO/rE6BWGaND13tXp8kzBVNhgcu1remuhmXk6"), + xrplAmount: amountStringToXRPLAmount(t, fmt.Sprintf("1e92/%s/%s", fooCurrency, fooIssuer)), wantErr: true, }, } @@ -74,7 +81,102 @@ func TestConvertXRPLNativeTokenAmountToCoreum(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() - got, err := processes.ConvertXRPLNativeTokenAmountToCoreum(tt.xrplAmount) + got, err := processes.ConvertXRPLNativeTokenXRPLAmountToCoreumAmount(tt.xrplAmount) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + require.Equal(t, tt.want.String(), got.String()) + }) + } +} + +func TestConvertXRPLNativeTokenCoreumAmountToXRPLAmount(t *testing.T) { + t.Parallel() + + var ( + fooIssuer = xrpl.GenPrivKeyTxSigner().Account().String() + fooCurrency = "FOO" + ) + + tests := []struct { + name string + coreumAmount sdkmath.Int + issuer string + currency string + want rippledata.Amount + wantErr bool + }{ + { + name: "one_coreum_XRP_to_XRPL_XRP", + coreumAmount: sdkmath.NewIntFromUint64(1_000_000), + issuer: processes.XRPIssuer, + currency: processes.XRPCurrency, + want: amountStringToXRPLAmount(t, "1.0XRP"), + }, + { + name: "one_with_some_decimals_coreum_XRP_to_XRPL_XRP", + coreumAmount: sdkmath.NewIntFromUint64(1000100), + issuer: processes.XRPIssuer, + currency: processes.XRPCurrency, + want: amountStringToXRPLAmount(t, "1.0001XRP"), + }, + { + name: "min_decimals_coreum_XRP_to_XRPL_XRP", + coreumAmount: sdkmath.NewIntFromUint64(999000001), + issuer: processes.XRPIssuer, + currency: processes.XRPCurrency, + want: amountStringToXRPLAmount(t, "999.000001XRP"), + }, + { + name: "high_value_coreum_XRP_to_XRPL_XRP", + coreumAmount: sdkmath.NewIntFromUint64(1000000000000001), + issuer: processes.XRPIssuer, + currency: processes.XRPCurrency, + want: amountStringToXRPLAmount(t, "1000000000.000001XRP"), + }, + { + name: "one_coreum_FOO_to_XRPL_FOO", + coreumAmount: sdkmath.NewIntFromUint64(1000000000000000), + issuer: fooIssuer, + currency: fooCurrency, + want: amountStringToXRPLAmount(t, fmt.Sprintf("1.0/%s/%s", fooCurrency, fooIssuer)), + }, + { + name: "one_with_some_decimals_FOO_to_XRPL_FOO", + coreumAmount: sdkmath.NewIntFromUint64(1000000000100000), + issuer: fooIssuer, + currency: fooCurrency, + want: amountStringToXRPLAmount(t, fmt.Sprintf("1.0000000001/%s/%s", fooCurrency, fooIssuer)), + }, + { + name: "min_decimals_FOO_to_XRPL_FOO", + coreumAmount: sdkmath.NewIntFromUint64(1), + issuer: fooIssuer, + currency: fooCurrency, + want: amountStringToXRPLAmount(t, fmt.Sprintf("0.000000000000001/%s/%s", fooCurrency, fooIssuer)), + }, + { + name: "high_value_FOO_to_XRPL_FOO", + coreumAmount: stringToSDKInt(t, "100000000000000000000000000000000000"), + issuer: fooIssuer, + currency: fooCurrency, + want: amountStringToXRPLAmount(t, fmt.Sprintf("1e20/%s/%s", fooCurrency, fooIssuer)), + }, + { + name: "max_high_value_with_some_decimals_FOO_to_XRPL_FOO", + coreumAmount: stringToSDKInt(t, "1000000000000001"), + issuer: fooIssuer, + currency: fooCurrency, + want: amountStringToXRPLAmount(t, fmt.Sprintf("1.000000000000001/%s/%s", fooCurrency, fooIssuer)), + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + got, err := processes.ConvertXRPLNativeTokenCoreumAmountToXRPLAmount(tt.coreumAmount, tt.issuer, tt.currency) if tt.wantErr { require.Error(t, err) } else { diff --git a/relayer/processes/model.go b/relayer/processes/model.go index 6713a0b5..3b3d4320 100644 --- a/relayer/processes/model.go +++ b/relayer/processes/model.go @@ -17,6 +17,7 @@ type ContractClient interface { IsInitialized() bool SendXRPLToCoreumTransferEvidence(ctx context.Context, sender sdk.AccAddress, evidence coreum.XRPLToCoreumTransferEvidence) (*sdk.TxResponse, error) SendXRPLTicketsAllocationTransactionResultEvidence(ctx context.Context, sender sdk.AccAddress, evidence coreum.XRPLTransactionResultTicketsAllocationEvidence) (*sdk.TxResponse, error) + SendXRPLTrustSetTransactionResultEvidence(ctx context.Context, sender sdk.AccAddress, evd coreum.XRPLTransactionResultTrustSetEvidence) (*sdk.TxResponse, error) RegisterSignature(ctx context.Context, sender sdk.AccAddress, operationID uint32, signature string) (*sdk.TxResponse, error) GetPendingOperations(ctx context.Context) ([]coreum.Operation, error) GetContractConfig(ctx context.Context) (coreum.ContractConfig, error) diff --git a/relayer/processes/model_mocks_test.go b/relayer/processes/model_mocks_test.go index a90f78d9..50fecb87 100644 --- a/relayer/processes/model_mocks_test.go +++ b/relayer/processes/model_mocks_test.go @@ -128,6 +128,21 @@ func (mr *MockContractClientMockRecorder) SendXRPLToCoreumTransferEvidence(arg0, return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendXRPLToCoreumTransferEvidence", reflect.TypeOf((*MockContractClient)(nil).SendXRPLToCoreumTransferEvidence), arg0, arg1, arg2) } +// SendXRPLTrustSetTransactionResultEvidence mocks base method. +func (m *MockContractClient) SendXRPLTrustSetTransactionResultEvidence(arg0 context.Context, arg1 types.AccAddress, arg2 coreum.XRPLTransactionResultTrustSetEvidence) (*types.TxResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendXRPLTrustSetTransactionResultEvidence", arg0, arg1, arg2) + ret0, _ := ret[0].(*types.TxResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SendXRPLTrustSetTransactionResultEvidence indicates an expected call of SendXRPLTrustSetTransactionResultEvidence. +func (mr *MockContractClientMockRecorder) SendXRPLTrustSetTransactionResultEvidence(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendXRPLTrustSetTransactionResultEvidence", reflect.TypeOf((*MockContractClient)(nil).SendXRPLTrustSetTransactionResultEvidence), arg0, arg1, arg2) +} + // MockXRPLAccountTxScanner is a mock of XRPLAccountTxScanner interface. type MockXRPLAccountTxScanner struct { ctrl *gomock.Controller diff --git a/relayer/processes/operation_tx.go b/relayer/processes/operation_tx.go index 72590d67..2fd60063 100644 --- a/relayer/processes/operation_tx.go +++ b/relayer/processes/operation_tx.go @@ -16,7 +16,7 @@ func BuildTicketCreateTxForMultiSigning(bridgeAccount rippledata.Account, operat TicketCount: &operation.OperationType.AllocateTickets.Number, } if operation.TicketNumber != 0 { - tx.TxBase.Sequence = operation.TicketNumber + tx.TicketSequence = &operation.TicketNumber } else { tx.TxBase.Sequence = operation.SequenceNumber } @@ -31,3 +31,34 @@ func BuildTicketCreateTxForMultiSigning(bridgeAccount rippledata.Account, operat return &tx, nil } + +// BuildTrustSetTxForMultiSigning builds TrustSet transaction operation from the contract operation. +func BuildTrustSetTxForMultiSigning(bridgeAccount rippledata.Account, operation coreum.Operation) (*rippledata.TrustSet, error) { + trustSetType := operation.OperationType.TrustSet + value, err := ConvertXRPLNativeTokenCoreumAmountToXRPLAmount( + trustSetType.TrustSetLimitAmount, + trustSetType.Issuer, + trustSetType.Currency, + ) + if err != nil { + return nil, err + } + tx := rippledata.TrustSet{ + TxBase: rippledata.TxBase{ + Account: bridgeAccount, + TransactionType: rippledata.TRUST_SET, + }, + LimitAmount: value, + } + tx.TicketSequence = &operation.TicketNumber + // important for the multi-signing + tx.TxBase.SigningPubKey = &rippledata.PublicKey{} + + fee, err := GetTxFee(&tx) + if err != nil { + return nil, err + } + tx.TxBase.Fee = fee + + return &tx, nil +} diff --git a/relayer/processes/xrpl_tx_observer.go b/relayer/processes/xrpl_tx_observer.go index 823b99d2..9fd0565c 100644 --- a/relayer/processes/xrpl_tx_observer.go +++ b/relayer/processes/xrpl_tx_observer.go @@ -114,7 +114,7 @@ func (o *XRPLTxObserver) processIncomingTx(ctx context.Context, tx rippledata.Tr } deliveredXRPLAmount := tx.MetaData.DeliveredAmount - coreumAmount, err := ConvertXRPLNativeTokenAmountToCoreum(*deliveredXRPLAmount) + coreumAmount, err := ConvertXRPLNativeTokenXRPLAmountToCoreumAmount(*deliveredXRPLAmount) if err != nil { return err } @@ -157,47 +157,9 @@ func (o *XRPLTxObserver) processOutgoingTx(ctx context.Context, tx rippledata.Tr switch txType { case rippledata.TICKET_CREATE.String(): - tickets := extractTicketSequencesFromMetaData(tx.MetaData) - txResult := coreum.TransactionResultAccepted - if !tx.MetaData.TransactionResult.Success() { - txResult = coreum.TransactionResultRejected - tickets = nil - } - evidence := coreum.XRPLTransactionResultTicketsAllocationEvidence{ - XRPLTransactionResultEvidence: coreum.XRPLTransactionResultEvidence{ - TxHash: tx.GetHash().String(), - TransactionResult: txResult, - }, - Tickets: tickets, - } - ticketCreateTx, ok := tx.Transaction.(*rippledata.TicketCreate) - if !ok { - return errors.Errorf("failed to cast tx to TicketCreate, data:%+v", tx) - } - if ticketCreateTx.Sequence != 0 { - evidence.SequenceNumber = lo.ToPtr(ticketCreateTx.Sequence) - } - if ticketCreateTx.TicketSequence != nil && *ticketCreateTx.TicketSequence != 0 { - evidence.TicketNumber = lo.ToPtr(*ticketCreateTx.TicketSequence) - } - _, err := o.contractClient.SendXRPLTicketsAllocationTransactionResultEvidence( - ctx, - o.cfg.RelayerAddress, - evidence, - ) - if err == nil { - if evidence.TransactionResult != coreum.TransactionResultAccepted { - o.log.Warn(ctx, "Transaction was rejected", logger.StringField("txResult", tx.MetaData.TransactionResult.String())) - } - return nil - } - if IsExpectedSendEvidenceError(err) { - o.log.Debug(ctx, "Received expected send evidence error") - return nil - } - - return err - + return o.sendXRPLTicketsAllocationTransactionResultEvidence(ctx, tx) + case rippledata.TRUST_SET.String(): + return o.sendXRPLTrustSetTransactionResultEvidence(ctx, tx) default: // TODO(dzmitryhil) replace with the error once we integrate all supported types o.log.Warn(ctx, "Found unsupported transaction type", logger.AnyField("tx", tx)) @@ -212,6 +174,87 @@ func IsExpectedSendEvidenceError(err error) bool { coreum.IsPendingOperationNotFoundError(err) } +func (o *XRPLTxObserver) sendXRPLTicketsAllocationTransactionResultEvidence(ctx context.Context, tx rippledata.TransactionWithMetaData) error { + tickets := extractTicketSequencesFromMetaData(tx.MetaData) + txResult := coreum.TransactionResultAccepted + if !tx.MetaData.TransactionResult.Success() { + txResult = coreum.TransactionResultRejected + tickets = nil + } + evidence := coreum.XRPLTransactionResultTicketsAllocationEvidence{ + XRPLTransactionResultEvidence: coreum.XRPLTransactionResultEvidence{ + TxHash: tx.GetHash().String(), + TransactionResult: txResult, + }, + Tickets: tickets, + } + ticketCreateTx, ok := tx.Transaction.(*rippledata.TicketCreate) + if !ok { + return errors.Errorf("failed to cast tx to TicketCreate, data:%+v", tx) + } + if ticketCreateTx.Sequence != 0 { + evidence.SequenceNumber = lo.ToPtr(ticketCreateTx.Sequence) + } + if ticketCreateTx.TicketSequence != nil && *ticketCreateTx.TicketSequence != 0 { + evidence.TicketNumber = lo.ToPtr(*ticketCreateTx.TicketSequence) + } + _, err := o.contractClient.SendXRPLTicketsAllocationTransactionResultEvidence( + ctx, + o.cfg.RelayerAddress, + evidence, + ) + if err == nil { + if evidence.TransactionResult != coreum.TransactionResultAccepted { + o.log.Warn(ctx, "Transaction was rejected", logger.StringField("txResult", tx.MetaData.TransactionResult.String())) + } + return nil + } + if IsExpectedSendEvidenceError(err) { + o.log.Debug(ctx, "Received expected send evidence error") + return nil + } + + return err +} + +func (o *XRPLTxObserver) sendXRPLTrustSetTransactionResultEvidence(ctx context.Context, tx rippledata.TransactionWithMetaData) error { + txResult := coreum.TransactionResultAccepted + if !tx.MetaData.TransactionResult.Success() { + txResult = coreum.TransactionResultRejected + } + trustSetTx, ok := tx.Transaction.(*rippledata.TrustSet) + if !ok { + return errors.Errorf("failed to cast tx to TrustSet, data:%+v", tx) + } + evidence := coreum.XRPLTransactionResultTrustSetEvidence{ + XRPLTransactionResultEvidence: coreum.XRPLTransactionResultEvidence{ + TxHash: tx.GetHash().String(), + TransactionResult: txResult, + TicketNumber: trustSetTx.TicketSequence, + }, + Issuer: trustSetTx.LimitAmount.Issuer.String(), + Currency: xrpl.ConvertCurrencyToString(trustSetTx.LimitAmount.Currency), + } + + _, err := o.contractClient.SendXRPLTrustSetTransactionResultEvidence( + ctx, + o.cfg.RelayerAddress, + evidence, + ) + if err == nil { + if evidence.TransactionResult != coreum.TransactionResultAccepted { + o.log.Warn(ctx, "Transaction was rejected", logger.StringField("txResult", tx.MetaData.TransactionResult.String())) + } + return nil + } + if IsExpectedSendEvidenceError(err) { + o.log.Debug(ctx, "Received expected send evidence error") + return nil + } + + return err +} + func extractTicketSequencesFromMetaData(metaData rippledata.MetaData) []uint32 { ticketSequences := make([]uint32, 0) for _, node := range metaData.AffectedNodes { diff --git a/relayer/processes/xrpl_tx_observer_test.go b/relayer/processes/xrpl_tx_observer_test.go index ac8b6dc8..717301ce 100644 --- a/relayer/processes/xrpl_tx_observer_test.go +++ b/relayer/processes/xrpl_tx_observer_test.go @@ -22,6 +22,8 @@ func TestXRPLTxObserver_Start(t *testing.T) { bridgeAccount := xrpl.GenPrivKeyTxSigner().Account() issuerAccount := xrpl.GenPrivKeyTxSigner().Account() + failTxResult := rippledata.TransactionResult(111) + relayerAddress := coreum.GenAccount() coreumRecipientAddress := coreum.GenAccount() memo, err := xrpl.EncodeCoreumRecipientToMemo(coreumRecipientAddress) @@ -111,8 +113,7 @@ func TestXRPLTxObserver_Start(t *testing.T) { ch <- rippledata.TransactionWithMetaData{ Transaction: &rippledata.Payment{}, MetaData: rippledata.MetaData{ - // if code not 0 - not success - TransactionResult: rippledata.TransactionResult(111), + TransactionResult: failTxResult, }, } cancel() @@ -245,8 +246,7 @@ func TestXRPLTxObserver_Start(t *testing.T) { }, }, MetaData: rippledata.MetaData{ - // if code not 0 - not success - TransactionResult: rippledata.TransactionResult(111), + TransactionResult: failTxResult, }, } cancel() @@ -274,6 +274,96 @@ func TestXRPLTxObserver_Start(t *testing.T) { return contractClientMock }, }, + { + name: "outgoing_trust_set_tx", + txScannerBuilder: func(ctrl *gomock.Controller, cancel func()) processes.XRPLAccountTxScanner { + xrplAccountTxScannerMock := NewMockXRPLAccountTxScanner(ctrl) + xrplAccountTxScannerMock.EXPECT().ScanTxs(gomock.Any(), gomock.Any()).DoAndReturn( + func(ctx context.Context, ch chan<- rippledata.TransactionWithMetaData) error { + go func() { + ch <- rippledata.TransactionWithMetaData{ + Transaction: &rippledata.TrustSet{ + TxBase: rippledata.TxBase{ + Account: bridgeAccount, + TransactionType: rippledata.TRUST_SET, + }, + LimitAmount: xrplCurrencyAmount, + TicketSequence: lo.ToPtr(uint32(11)), + }, + } + cancel() + }() + return nil + }) + + return xrplAccountTxScannerMock + }, + contractClientBuilder: func(ctrl *gomock.Controller) processes.ContractClient { + contractClientMock := NewMockContractClient(ctrl) + contractClientMock.EXPECT().SendXRPLTrustSetTransactionResultEvidence( + gomock.Any(), + relayerAddress, + coreum.XRPLTransactionResultTrustSetEvidence{ + XRPLTransactionResultEvidence: coreum.XRPLTransactionResultEvidence{ + TxHash: rippledata.Hash256{}.String(), + TicketNumber: lo.ToPtr(uint32(11)), + TransactionResult: coreum.TransactionResultAccepted, + }, + Issuer: xrplCurrencyAmount.Issuer.String(), + Currency: xrpl.ConvertCurrencyToString(xrplCurrencyAmount.Currency), + }, + ).Return(nil, nil) + + return contractClientMock + }, + }, + { + name: "outgoing_trust_set_tx_with_failure", + txScannerBuilder: func(ctrl *gomock.Controller, cancel func()) processes.XRPLAccountTxScanner { + xrplAccountTxScannerMock := NewMockXRPLAccountTxScanner(ctrl) + xrplAccountTxScannerMock.EXPECT().ScanTxs(gomock.Any(), gomock.Any()).DoAndReturn( + func(ctx context.Context, ch chan<- rippledata.TransactionWithMetaData) error { + go func() { + ch <- rippledata.TransactionWithMetaData{ + Transaction: &rippledata.TrustSet{ + TxBase: rippledata.TxBase{ + Account: bridgeAccount, + TransactionType: rippledata.TRUST_SET, + }, + LimitAmount: xrplCurrencyAmount, + TicketSequence: lo.ToPtr(uint32(11)), + }, + MetaData: rippledata.MetaData{ + TransactionResult: failTxResult, + }, + } + cancel() + }() + return nil + }) + + return xrplAccountTxScannerMock + }, + contractClientBuilder: func(ctrl *gomock.Controller) processes.ContractClient { + contractClientMock := NewMockContractClient(ctrl) + contractClientMock.EXPECT().SendXRPLTrustSetTransactionResultEvidence( + gomock.Any(), + relayerAddress, + coreum.XRPLTransactionResultTrustSetEvidence{ + XRPLTransactionResultEvidence: coreum.XRPLTransactionResultEvidence{ + TxHash: rippledata.Hash256{}.String(), + TicketNumber: lo.ToPtr(uint32(11)), + TransactionResult: coreum.TransactionResultRejected, + }, + Issuer: xrplCurrencyAmount.Issuer.String(), + Currency: xrpl.ConvertCurrencyToString(xrplCurrencyAmount.Currency), + }, + ).Return(nil, nil) + + return contractClientMock + }, + }, + { name: "outgoing_not_expected_tx", txScannerBuilder: func(ctrl *gomock.Controller, cancel func()) processes.XRPLAccountTxScanner { diff --git a/relayer/processes/xrpl_tx_submitter.go b/relayer/processes/xrpl_tx_submitter.go index ab937476..173f9d8f 100644 --- a/relayer/processes/xrpl_tx_submitter.go +++ b/relayer/processes/xrpl_tx_submitter.go @@ -401,6 +401,10 @@ func (s *XRPLTxSubmitter) buildXRPLTxFromOperation(operation coreum.Operation) ( switch { case operation.OperationType.AllocateTickets != nil && operation.OperationType.AllocateTickets.Number > 0: return BuildTicketCreateTxForMultiSigning(s.cfg.BridgeAccount, operation) + case operation.OperationType.TrustSet != nil && + operation.OperationType.TrustSet.Issuer != "" && + operation.OperationType.TrustSet.Currency != "": + return BuildTrustSetTxForMultiSigning(s.cfg.BridgeAccount, operation) default: return nil, errors.Errorf("failed to process operation, unable to determin operation type, operation:%+v", operation) } diff --git a/relayer/processes/xrpl_tx_submitter_test.go b/relayer/processes/xrpl_tx_submitter_test.go index 6d903f19..05128501 100644 --- a/relayer/processes/xrpl_tx_submitter_test.go +++ b/relayer/processes/xrpl_tx_submitter_test.go @@ -4,6 +4,7 @@ import ( "context" "testing" + sdkmath "cosmossdk.io/math" "github.com/golang/mock/gomock" rippledata "github.com/rubblelabs/ripple/data" "github.com/samber/lo" @@ -77,6 +78,8 @@ func TestXRPLTxSubmitter_Start(t *testing.T) { }, } + // ********** Allocate ticket ********** + allocateTicketOperationWithoutSignatures := coreum.Operation{ SequenceNumber: 1, Signatures: nil, @@ -130,6 +133,53 @@ func TestXRPLTxSubmitter_Start(t *testing.T) { allocateTicketOperationSigner2, } + // ********** TrustSet ********** + + trustSetOperationWithoutSignatures := coreum.Operation{ + SequenceNumber: 1, + Signatures: nil, + OperationType: coreum.OperationType{ + TrustSet: &coreum.OperationTypeTrustSet{ + Issuer: xrpl.GenPrivKeyTxSigner().Account().String(), + Currency: "TRC", + TrustSetLimitAmount: sdkmath.NewInt(1000000000000), + }, + }, + } + + trustSetOperationWithSignatures := trustSetOperationWithoutSignatures + trustSetOperationSigner1 := multiSignTrustSetsOperation( + t, + xrplRelayer1Signer, + xrplBridgeAccount, + trustSetOperationWithSignatures, + ) + trustSetOperationSigner2 := multiSignTrustSetsOperation( + t, + xrplRelayer2Signer, + xrplBridgeAccount, + trustSetOperationWithSignatures, + ) + trustSetOperationWithSignatures.Signatures = []coreum.Signature{ + { + Relayer: coreumRelayer1Address, + Signature: trustSetOperationSigner1.Signer.TxnSignature.String(), + }, + { + Relayer: coreumRelayer2Address, + Signature: trustSetOperationSigner2.Signer.TxnSignature.String(), + }, + { + Relayer: coreumRelayer3Address, + // the signature is taken from the first signer, so it is invalid + Signature: trustSetOperationSigner1.Signer.TxnSignature.String(), + }, + } + trustSetOperationWithSignaturesSigners := []rippledata.Signer{ + trustSetOperationSigner1, + trustSetOperationSigner2, + } + tests := []struct { name string contractClientBuilder func(ctrl *gomock.Controller) processes.ContractClient @@ -221,6 +271,59 @@ func TestXRPLTxSubmitter_Start(t *testing.T) { return xrplRPCClientMock }, }, + { + name: "resister_signature_for_trust_set_tx", + contractClientBuilder: func(ctrl *gomock.Controller) processes.ContractClient { + contractClientMock := NewMockContractClient(ctrl) + contractClientMock.EXPECT().GetPendingOperations(gomock.Any()).Return([]coreum.Operation{trustSetOperationWithoutSignatures}, nil) + contractClientMock.EXPECT().GetContractConfig(gomock.Any()).Return(coreum.ContractConfig{ + Relayers: contractRelayers, + }, nil) + contractClientMock.EXPECT().RegisterSignature(gomock.Any(), coreumRelayer1Address, trustSetOperationWithoutSignatures.SequenceNumber, trustSetOperationSigner1.Signer.TxnSignature.String()) + return contractClientMock + }, + xrplRPCClientBuilder: func(ctrl *gomock.Controller) processes.XRPLRPCClient { + xrplRPCClientMock := NewMockXRPLRPCClient(ctrl) + xrplRPCClientMock.EXPECT().AccountInfo(gomock.Any(), xrplBridgeAccount).Return(xrplBridgeSignerAccountWithSigners, nil) + return xrplRPCClientMock + }, + xrplTxSignerBuilder: func(ctrl *gomock.Controller) processes.XRPLTxSigner { + xrplTxSignerMock := NewMockXRPLTxSigner(ctrl) + tx, err := processes.BuildTrustSetTxForMultiSigning(xrplBridgeAccount, trustSetOperationWithoutSignatures) + require.NoError(t, err) + xrplTxSignerMock.EXPECT().MultiSign(tx, xrplTxSignerKeyName).Return(trustSetOperationSigner1, nil) + + return xrplTxSignerMock + }, + }, + { + name: "submit_trust_set_tx_with_filtered_signature", + contractClientBuilder: func(ctrl *gomock.Controller) processes.ContractClient { + contractClientMock := NewMockContractClient(ctrl) + contractClientMock.EXPECT().GetPendingOperations(gomock.Any()).Return([]coreum.Operation{trustSetOperationWithSignatures}, nil) + contractClientMock.EXPECT().GetContractConfig(gomock.Any()).Return(coreum.ContractConfig{ + Relayers: contractRelayers, + }, nil) + return contractClientMock + }, + xrplRPCClientBuilder: func(ctrl *gomock.Controller) processes.XRPLRPCClient { + xrplRPCClientMock := NewMockXRPLRPCClient(ctrl) + xrplRPCClientMock.EXPECT().AccountInfo(gomock.Any(), xrplBridgeAccount).Return(xrplBridgeSignerAccountWithSigners, nil) + expectedTx, err := processes.BuildTrustSetTxForMultiSigning(xrplBridgeAccount, trustSetOperationWithSignatures) + require.NoError(t, err) + require.NoError(t, rippledata.SetSigners(expectedTx, trustSetOperationWithSignaturesSigners...)) + xrplRPCClientMock.EXPECT().Submit(gomock.Any(), gomock.Any()).Do(func(ctx context.Context, tx rippledata.Transaction) (xrpl.SubmitResult, error) { + _, expectedTxRaw, err := rippledata.Raw(expectedTx) + require.NoError(t, err) + _, txRaw, err := rippledata.Raw(tx) + require.NoError(t, err) + require.Equal(t, expectedTxRaw, txRaw) + return xrpl.SubmitResult{}, nil + }) + + return xrplRPCClientMock + }, + }, } for _, tt := range tests { tt := tt @@ -276,3 +379,17 @@ func multiSignAllocateTicketsOperation( return signer } + +func multiSignTrustSetsOperation( + t *testing.T, + xrplRelayerSigner *xrpl.PrivKeyTxSigner, + xrplBridgeAcc rippledata.Account, + operation coreum.Operation, +) rippledata.Signer { + tx, err := processes.BuildTrustSetTxForMultiSigning(xrplBridgeAcc, operation) + require.NoError(t, err) + signer, err := xrplRelayerSigner.MultiSign(tx) + require.NoError(t, err) + + return signer +} From 0ffe93b859b5ccf690c8aee4d3d3e3d80717c9a0 Mon Sep 17 00:00:00 2001 From: Dzmitry Hil Date: Mon, 6 Nov 2023 16:10:04 +0300 Subject: [PATCH 2/8] Fix contract linter --- contract/src/contract.rs | 4 ++-- contract/src/tickets.rs | 37 +++++++++++++++++-------------------- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/contract/src/contract.rs b/contract/src/contract.rs index fe833988..eeb0fcc3 100644 --- a/contract/src/contract.rs +++ b/contract/src/contract.rs @@ -413,10 +413,10 @@ fn save_evidence(deps: DepsMut, sender: Addr, evidence: Evidence) -> CoreumResul match operation_result.clone() { OperationResult::TicketsAllocation { .. } => {} OperationResult::TrustSet { issuer, currency } => { - let key = build_xrpl_token_key(issuer.clone(), currency.clone()); + let key = build_xrpl_token_key(issuer, currency); // validate that we have received the trust set for the token we have and with the pending state let token = XRPL_TOKENS - .load(deps.storage, key.clone()) + .load(deps.storage, key) .map_err(|_| ContractError::TokenNotRegistered {})?; // it is possible to if token.state.ne(&TokenState::Processing) { diff --git a/contract/src/tickets.rs b/contract/src/tickets.rs index c3f8872c..9a8ce8f1 100644 --- a/contract/src/tickets.rs +++ b/contract/src/tickets.rs @@ -38,27 +38,24 @@ pub fn register_used_ticket(storage: &mut dyn Storage) -> Result<(), ContractErr // If we reach the max allowed tickets to be used, we need to create an operation to allocate new ones if used_tickets + 1 >= config.used_tickets_threshold && !PENDING_TICKET_UPDATE.load(storage)? { - match reserve_ticket(storage) { - Ok(ticket_to_update) => { - PENDING_OPERATIONS.save( - storage, - ticket_to_update, - &Operation { - ticket_number: Some(ticket_to_update), - sequence_number: None, - signatures: vec![], - operation_type: OperationType::AllocateTickets { - number: config.used_tickets_threshold, - }, + // TODO(keyne) add specific flag to the resp + // TODO(keyne) handle one expected error that way (not tickets to allocate) + // in case we don't have free tickets anymore we should still accept the tx + // otherwise the contract will stuck + if let Ok(ticket_to_update) = reserve_ticket(storage) { + PENDING_OPERATIONS.save( + storage, + ticket_to_update, + &Operation { + ticket_number: Some(ticket_to_update), + sequence_number: None, + signatures: vec![], + operation_type: OperationType::AllocateTickets { + number: config.used_tickets_threshold, }, - )?; - PENDING_TICKET_UPDATE.save(storage, &true)? - } - // TODO(keyne) add specific flag to the resp - // TODO(keyne) handle one expected error that way (not tickets to allocate) - // in case we don't have free tickets anymore we should still accept the tx - // otherwise the contract will stuck - Err(_) => {}, + }, + )?; + PENDING_TICKET_UPDATE.save(storage, &true)? }; } From 987a3a021d2614197a2797451e29ebbbdca5a922 Mon Sep 17 00:00:00 2001 From: Dzmitry Hil Date: Tue, 7 Nov 2023 10:41:39 +0300 Subject: [PATCH 3/8] * Use min balances for integration tests * Add tickets re-allocation tests by the XRPL tokens registration --- integration-tests/processes/env_test.go | 10 ++-- .../processes/ticket_allocation_test.go | 56 +++++++++++++++++++ integration-tests/xrpl.go | 6 ++ 3 files changed, 68 insertions(+), 4 deletions(-) diff --git a/integration-tests/processes/env_test.go b/integration-tests/processes/env_test.go index 00ba42e8..43d621a7 100644 --- a/integration-tests/processes/env_test.go +++ b/integration-tests/processes/env_test.go @@ -122,7 +122,7 @@ func NewRunnerEnv(ctx context.Context, t *testing.T, cfg RunnerEnvConfig, chains // add malicious relayers // we keep the relayer indexes to make all config valid apart from the XRPL signing for i := cfg.RelayerNumber - cfg.MaliciousRelayerNumber; i < cfg.RelayerNumber; i++ { - maliciousSignerAcc := chains.XRPL.GenAccount(ctx, t, 10) + maliciousSignerAcc := chains.XRPL.GenAccount(ctx, t, 0) runners = append( runners, createDevRunner( @@ -357,14 +357,15 @@ func genXRPLBridgeAccountWithRelayers( disableMasterKey bool, ) (rippledata.Account, []rippledata.Account, []rippledata.PublicKey) { t.Helper() + // some fee to cover simple txs all extras must be allocated in the test + bridgeAcc := xrplChain.GenAccount(ctx, t, 0.5) - bridgeAcc := xrplChain.GenAccount(ctx, t, 10) t.Logf("Bridge account is generated, address:%s", bridgeAcc.String()) signerEntries := make([]rippledata.SignerEntry, 0, signersCount) signerAccounts := make([]rippledata.Account, 0, signersCount) signerPubKeys := make([]rippledata.PublicKey, 0, signersCount) for i := 0; i < signersCount; i++ { - signerAcc := xrplChain.GenAccount(ctx, t, 10) + signerAcc := xrplChain.GenAccount(ctx, t, 0) signerAccounts = append(signerAccounts, signerAcc) t.Logf("Signer %d is generated, address:%s", i+1, signerAcc.String()) signerEntries = append(signerEntries, rippledata.SignerEntry{ @@ -375,7 +376,8 @@ func genXRPLBridgeAccountWithRelayers( }) signerPubKeys = append(signerPubKeys, xrplChain.GetSignerPubKey(t, signerAcc)) } - + // fund for the signers SignerListSet + xrplChain.FundAccountForSignerListSet(ctx, t, bridgeAcc, signersCount) signerListSetTx := rippledata.SignerListSet{ SignerQuorum: signerQuorum, SignerEntries: signerEntries, diff --git a/integration-tests/processes/ticket_allocation_test.go b/integration-tests/processes/ticket_allocation_test.go index 3e8944dc..a90cbdc8 100644 --- a/integration-tests/processes/ticket_allocation_test.go +++ b/integration-tests/processes/ticket_allocation_test.go @@ -4,10 +4,13 @@ package processes_test import ( + "fmt" "testing" + rippledata "github.com/rubblelabs/ripple/data" "github.com/stretchr/testify/require" + coreumintegration "github.com/CoreumFoundation/coreum/v3/testutil/integration" integrationtests "github.com/CoreumFoundation/coreumbridge-xrpl/integration-tests" ) @@ -173,3 +176,56 @@ func TestTicketsAllocationRecoveryWithMaliciousRelayers(t *testing.T) { require.NoError(t, err) require.Len(t, availableTickets, int(numberOfTicketsToAllocate)) } + +func TestTicketsReAllocationByTheXRPLTokenRegistration(t *testing.T) { + t.Parallel() + + ctx, chains := integrationtests.NewTestingContext(t) + + envCfg := DefaultRunnerEnvConfig() + envCfg.UsedTicketsThreshold = 3 + runnerEnv := NewRunnerEnv(ctx, t, envCfg, chains) + + runnerEnv.StartAllRunnerProcesses(ctx, t) + + // allocate first five tickets + numberOfTicketsToAllocate := uint32(5) + chains.XRPL.FundAccountForTicketAllocation(ctx, t, runnerEnv.XRPLBridgeAccount, numberOfTicketsToAllocate) + runnerEnv.AllocateTickets(ctx, t, numberOfTicketsToAllocate) + initialAvailableTickets, err := runnerEnv.ContractClient.GetAvailableTickets(ctx) + require.NoError(t, err) + + xrplCurrencyIssuerAcc := chains.XRPL.GenAccount(ctx, t, 100) + + // register more than threshold to activate tickets re-allocation + numberOfXRPLTokensToRegister := int(numberOfTicketsToAllocate) - 1 + // fund owner to cover registration fees + chains.Coreum.FundAccountWithOptions(ctx, t, runnerEnv.ContractOwner, coreumintegration.BalancesOptions{ + Amount: chains.Coreum.QueryAssetFTParams(ctx, t).IssueFee.Amount.MulRaw(int64(numberOfXRPLTokensToRegister)).MulRaw(2), + }) + + for i := 0; i < numberOfXRPLTokensToRegister; i++ { + xrplRegisteredCurrency, err := rippledata.NewCurrency(fmt.Sprintf("CR%d", i)) + require.NoError(t, err) + runnerEnv.RegisterXRPLTokenAndAwaitTrustSet(ctx, t, xrplCurrencyIssuerAcc, xrplRegisteredCurrency, int32(6), integrationtests.ConvertStringWithDecimalsToSDKInt(t, "1", 30)) + } + runnerEnv.AwaitNoPendingOperations(ctx, t) + + availableTicketsAfterReAllocation, err := runnerEnv.ContractClient.GetAvailableTickets(ctx) + require.NoError(t, err) + require.Len(t, availableTicketsAfterReAllocation, envCfg.UsedTicketsThreshold) + // check that tickets are used + require.NotEqualValues(t, initialAvailableTickets, availableTicketsAfterReAllocation) + + // use re-allocated tickets + for i := 0; i < numberOfXRPLTokensToRegister; i++ { + xrplRegisteredCurrency, err := rippledata.NewCurrency(fmt.Sprintf("DR%d", i)) + require.NoError(t, err) + runnerEnv.RegisterXRPLTokenAndAwaitTrustSet(ctx, t, xrplCurrencyIssuerAcc, xrplRegisteredCurrency, int32(6), integrationtests.ConvertStringWithDecimalsToSDKInt(t, "1", 30)) + } + runnerEnv.AwaitNoPendingOperations(ctx, t) + availableTicketsAfterSecondReallocation, err := runnerEnv.ContractClient.GetAvailableTickets(ctx) + require.NoError(t, err) + require.NotEqualValues(t, initialAvailableTickets, availableTicketsAfterSecondReallocation) + require.NotEqualValues(t, availableTicketsAfterReAllocation, availableTicketsAfterSecondReallocation) +} diff --git a/integration-tests/xrpl.go b/integration-tests/xrpl.go index 58afa7da..158d0bc0 100644 --- a/integration-tests/xrpl.go +++ b/integration-tests/xrpl.go @@ -32,6 +32,7 @@ const ( xrplTxFee = "100" xrplReserveToActivateAccount = float64(10) xrplReservePerTicket = float64(2) + xrplReservePerSigner = float64(2) ecdsaKeyType = rippledata.ECDSA faucetKeyringKeyName = "faucet" @@ -146,6 +147,11 @@ func (c XRPLChain) FundAccountForTicketAllocation(ctx context.Context, t *testin c.FundAccount(ctx, t, acc, xrplReservePerTicket*float64(ticketsNumber)) } +// FundAccountForSignerListSet funds the provided account with the amount required for the multi-signing set. +func (c XRPLChain) FundAccountForSignerListSet(ctx context.Context, t *testing.T, acc rippledata.Account, singersCount int) { + c.FundAccount(ctx, t, acc, xrplReservePerTicket*float64(singersCount)) +} + // FundAccount funds the provided account with the provided amount. func (c XRPLChain) FundAccount(ctx context.Context, t *testing.T, acc rippledata.Account, amount float64) { t.Helper() From 80caa4dab95ec36b0cd7f264cb96f15a82bff9cf Mon Sep 17 00:00:00 2001 From: Dzmitry Hil Date: Tue, 7 Nov 2023 10:56:06 +0300 Subject: [PATCH 4/8] * Fix naming and text errors --- .../coreum/contract_client_test.go | 18 ++++++++---------- integration-tests/xrpl.go | 2 +- relayer/processes/amount.go | 8 ++++---- relayer/processes/xrpl_tx_observer.go | 12 ++++++------ relayer/processes/xrpl_tx_submitter.go | 2 +- relayer/processes/xrpl_tx_submitter_test.go | 6 +++--- 6 files changed, 23 insertions(+), 25 deletions(-) diff --git a/integration-tests/coreum/contract_client_test.go b/integration-tests/coreum/contract_client_test.go index 493e259a..520990ba 100644 --- a/integration-tests/coreum/contract_client_test.go +++ b/integration-tests/coreum/contract_client_test.go @@ -287,7 +287,7 @@ func TestRegisterXRPLToken(t *testing.T) { defaultTrustSetLimitAmount, ) - // fund owner to cover registration fees twice + // fund owner to cover issuance fees twice chains.Coreum.FundAccountWithOptions(ctx, t, owner, coreumintegration.BalancesOptions{ Amount: issueFee.Amount.Mul(sdkmath.NewIntFromUint64(2)), }) @@ -364,7 +364,7 @@ func TestRegisterXRPLToken(t *testing.T) { operation := pendingOperations[0] require.NotNil(t, operation.OperationType.TrustSet) - rejectTxEvidenceTrustSet := coreum.XRPLTransactionResultTrustSetEvidence{ + rejectedTxEvidenceTrustSet := coreum.XRPLTransactionResultTrustSetEvidence{ XRPLTransactionResultEvidence: coreum.XRPLTransactionResultEvidence{ TxHash: genXRPLTxHash(t), TicketNumber: &operation.TicketNumber, @@ -375,25 +375,25 @@ func TestRegisterXRPLToken(t *testing.T) { } // try to register not existing operation - invalidEvidenceTrustSetWithInvalidTicket := rejectTxEvidenceTrustSet + invalidEvidenceTrustSetWithInvalidTicket := rejectedTxEvidenceTrustSet invalidEvidenceTrustSetWithInvalidTicket.TicketNumber = lo.ToPtr(uint32(99)) _, err = contractClient.SendXRPLTrustSetTransactionResultEvidence(ctx, relayers[0].CoreumAddress, invalidEvidenceTrustSetWithInvalidTicket) require.True(t, coreum.IsPendingOperationNotFoundError(err), err) // try to register with not existing currency - invalidEvidenceNotExistingIssuer := rejectTxEvidenceTrustSet + invalidEvidenceNotExistingIssuer := rejectedTxEvidenceTrustSet invalidEvidenceNotExistingIssuer.Issuer = xrpl.GenPrivKeyTxSigner().Account().String() _, err = contractClient.SendXRPLTrustSetTransactionResultEvidence(ctx, relayers[0].CoreumAddress, invalidEvidenceNotExistingIssuer) require.True(t, coreum.IsTokenNotRegisteredError(err), err) // send valid rejected evidence from first relayer - txResTrustSet, err := contractClient.SendXRPLTrustSetTransactionResultEvidence(ctx, relayers[0].CoreumAddress, rejectTxEvidenceTrustSet) + txResTrustSet, err := contractClient.SendXRPLTrustSetTransactionResultEvidence(ctx, relayers[0].CoreumAddress, rejectedTxEvidenceTrustSet) require.NoError(t, err) thresholdReachedTrustSet, err := event.FindStringEventAttribute(txResTrustSet.Events, wasmtypes.ModuleName, eventAttributeThresholdReached) require.NoError(t, err) require.Equal(t, strconv.FormatBool(false), thresholdReachedTrustSet) // send valid rejected evidence from second relayer - txResTrustSet, err = contractClient.SendXRPLTrustSetTransactionResultEvidence(ctx, relayers[1].CoreumAddress, rejectTxEvidenceTrustSet) + txResTrustSet, err = contractClient.SendXRPLTrustSetTransactionResultEvidence(ctx, relayers[1].CoreumAddress, rejectedTxEvidenceTrustSet) require.NoError(t, err) thresholdReachedTrustSet, err = event.FindStringEventAttribute(txResTrustSet.Events, wasmtypes.ModuleName, eventAttributeThresholdReached) require.NoError(t, err) @@ -411,7 +411,7 @@ func TestRegisterXRPLToken(t *testing.T) { }, *registeredInactiveToken) // try to send evidence one more time - _, err = contractClient.SendXRPLTrustSetTransactionResultEvidence(ctx, relayers[1].CoreumAddress, rejectTxEvidenceTrustSet) + _, err = contractClient.SendXRPLTrustSetTransactionResultEvidence(ctx, relayers[1].CoreumAddress, rejectedTxEvidenceTrustSet) require.True(t, coreum.IsOperationAlreadyExecutedError(err), err) // try to register the sending from the XRPL to coreum evidence with inactive token @@ -425,8 +425,6 @@ func TestRegisterXRPLToken(t *testing.T) { _, err = contractClient.SendXRPLToCoreumTransferEvidence(ctx, relayers[1].CoreumAddress, xrplToCoreumInactiveTokenTransferEvidence) require.True(t, coreum.IsXRPLTokenNotEnabledError(err), err) - _ = activeCurrency - // register one more token and activate it _, err = contractClient.RegisterXRPLToken(ctx, owner, issuer, activeCurrency, sendingPrecision, maxHoldingAmount) require.NoError(t, err) @@ -482,7 +480,7 @@ func TestSendFromXRPLToCoreumXRPLNativeToken(t *testing.T) { defaultTrustSetLimitAmount, ) issueFee := chains.Coreum.QueryAssetFTParams(ctx, t).IssueFee - // fund owner to cover registration fees twice + // fund owner to cover issuance fees twice chains.Coreum.FundAccountWithOptions(ctx, t, owner, coreumintegration.BalancesOptions{ Amount: issueFee.Amount.Mul(sdkmath.NewIntFromUint64(2)), }) diff --git a/integration-tests/xrpl.go b/integration-tests/xrpl.go index 158d0bc0..1e89b2ff 100644 --- a/integration-tests/xrpl.go +++ b/integration-tests/xrpl.go @@ -149,7 +149,7 @@ func (c XRPLChain) FundAccountForTicketAllocation(ctx context.Context, t *testin // FundAccountForSignerListSet funds the provided account with the amount required for the multi-signing set. func (c XRPLChain) FundAccountForSignerListSet(ctx context.Context, t *testing.T, acc rippledata.Account, singersCount int) { - c.FundAccount(ctx, t, acc, xrplReservePerTicket*float64(singersCount)) + c.FundAccount(ctx, t, acc, xrplReservePerSigner*float64(singersCount)) } // FundAccount funds the provided account with the provided amount. diff --git a/relayer/processes/amount.go b/relayer/processes/amount.go index 2ee0aba9..bcf94d32 100644 --- a/relayer/processes/amount.go +++ b/relayer/processes/amount.go @@ -13,11 +13,11 @@ const ( // XRPLAmountPrec is precision we use to covert float to float string for the amount representation. // That value is value which corelates with the min/max sending precision. XRPLAmountPrec = 16 - // XRPLIssuedCurrencyDecimals is XRPL decimals used the on coreum. + // XRPLIssuedCurrencyDecimals is XRPL decimals used on the coreum. XRPLIssuedCurrencyDecimals = 15 - // XRPIssuer is XRP issuer name used the on coreum. + // XRPIssuer is XRP issuer name used on the coreum. XRPIssuer = "rrrrrrrrrrrrrrrrrrrrrho" - // XRPCurrency is XRP currency name used the on coreum. + // XRPCurrency is XRP currency name used on the coreum. XRPCurrency = "XRP" ) @@ -51,7 +51,7 @@ func ConvertXRPLNativeTokenCoreumAmountToXRPLAmount(coreumAmount sdkmath.Int, is // we don't use the decimals for the XRP values since the `NewValue` function will do it automatically xrplValue, err := rippledata.NewValue(amountString, true) if err != nil { - return rippledata.Amount{}, errors.Wrapf(err, "failed to convert amount stringy to ripple.Value, amount stirng: %s", amountString) + return rippledata.Amount{}, errors.Wrapf(err, "failed to convert amount string to ripple.Value, amount stirng: %s", amountString) } return rippledata.Amount{ Value: xrplValue, diff --git a/relayer/processes/xrpl_tx_observer.go b/relayer/processes/xrpl_tx_observer.go index 9fd0565c..055d50a2 100644 --- a/relayer/processes/xrpl_tx_observer.go +++ b/relayer/processes/xrpl_tx_observer.go @@ -141,8 +141,8 @@ func (o *XRPLTxObserver) processIncomingTx(ctx context.Context, tx rippledata.Tr return nil } - if IsExpectedSendEvidenceError(err) { - o.log.Debug(ctx, "Received expected send evidence error") + if IsEvidenceErrorCausedByResubmission(err) { + o.log.Debug(ctx, "Received expected send evidence error caused by re-submission") return nil } @@ -167,8 +167,8 @@ func (o *XRPLTxObserver) processOutgoingTx(ctx context.Context, tx rippledata.Tr } } -// IsExpectedSendEvidenceError returns true is error is cause of the re-submitting of the transaction. -func IsExpectedSendEvidenceError(err error) bool { +// IsEvidenceErrorCausedByResubmission returns true is error is cause of the re-submitting of the transaction. +func IsEvidenceErrorCausedByResubmission(err error) bool { return coreum.IsEvidenceAlreadyProvidedError(err) || coreum.IsOperationAlreadyExecutedError(err) || coreum.IsPendingOperationNotFoundError(err) @@ -209,7 +209,7 @@ func (o *XRPLTxObserver) sendXRPLTicketsAllocationTransactionResultEvidence(ctx } return nil } - if IsExpectedSendEvidenceError(err) { + if IsEvidenceErrorCausedByResubmission(err) { o.log.Debug(ctx, "Received expected send evidence error") return nil } @@ -247,7 +247,7 @@ func (o *XRPLTxObserver) sendXRPLTrustSetTransactionResultEvidence(ctx context.C } return nil } - if IsExpectedSendEvidenceError(err) { + if IsEvidenceErrorCausedByResubmission(err) { o.log.Debug(ctx, "Received expected send evidence error") return nil } diff --git a/relayer/processes/xrpl_tx_submitter.go b/relayer/processes/xrpl_tx_submitter.go index 173f9d8f..9cf22f10 100644 --- a/relayer/processes/xrpl_tx_submitter.go +++ b/relayer/processes/xrpl_tx_submitter.go @@ -364,7 +364,7 @@ func (s *XRPLTxSubmitter) preValidateOperation(ctx context.Context, operation co if err == nil { return false, nil } - if IsExpectedSendEvidenceError(err) { + if IsEvidenceErrorCausedByResubmission(err) { s.log.Debug(ctx, "Received expected send evidence error") return false, nil } diff --git a/relayer/processes/xrpl_tx_submitter_test.go b/relayer/processes/xrpl_tx_submitter_test.go index 05128501..104b68de 100644 --- a/relayer/processes/xrpl_tx_submitter_test.go +++ b/relayer/processes/xrpl_tx_submitter_test.go @@ -148,13 +148,13 @@ func TestXRPLTxSubmitter_Start(t *testing.T) { } trustSetOperationWithSignatures := trustSetOperationWithoutSignatures - trustSetOperationSigner1 := multiSignTrustSetsOperation( + trustSetOperationSigner1 := multiSignTrustSetOperation( t, xrplRelayer1Signer, xrplBridgeAccount, trustSetOperationWithSignatures, ) - trustSetOperationSigner2 := multiSignTrustSetsOperation( + trustSetOperationSigner2 := multiSignTrustSetOperation( t, xrplRelayer2Signer, xrplBridgeAccount, @@ -380,7 +380,7 @@ func multiSignAllocateTicketsOperation( return signer } -func multiSignTrustSetsOperation( +func multiSignTrustSetOperation( t *testing.T, xrplRelayerSigner *xrpl.PrivKeyTxSigner, xrplBridgeAcc rippledata.Account, From 1c7761858d5e5d0aaa6874f503516f8a7604fefd Mon Sep 17 00:00:00 2001 From: Dzmitry Hil Date: Tue, 7 Nov 2023 12:50:27 +0300 Subject: [PATCH 5/8] * Use scientific notation everywhere as a standard for 10 in power * Update some naming --- contract/src/contract.rs | 4 +-- integration-tests/conv.go | 2 +- .../coreum/contract_client_test.go | 20 ++++++------ .../processes/ticket_allocation_test.go | 2 +- relayer/coreum/contract.go | 32 +++++++++---------- relayer/fee/rounding_test.go | 4 +-- relayer/processes/amount.go | 2 +- spec/spec.md | 10 +++--- 8 files changed, 38 insertions(+), 38 deletions(-) diff --git a/contract/src/contract.rs b/contract/src/contract.rs index eeb0fcc3..2739b3c9 100644 --- a/contract/src/contract.rs +++ b/contract/src/contract.rs @@ -680,8 +680,8 @@ fn truncate_amount( amount: Uint128, ) -> Result { // To get exactly by how much we need to divide the original amount - // Example: if sending precision = -1. Exponent will be 15 - ( - 1) = 16 for XRPL tokens so we will divide the original amount by 10^16 - // Example: if sending precision = 14. Exponent will be 15 - 14 = 1 for XRPL tokens so we will divide the original amount by 10^1 + // Example: if sending precision = -1. Exponent will be 15 - ( - 1) = 16 for XRPL tokens so we will divide the original amount by 1e16 + // Example: if sending precision = 14. Exponent will be 15 - 14 = 1 for XRPL tokens so we will divide the original amount by 10 let exponent = decimals as i32 - sending_precision; let amount_to_send = amount.checked_div(Uint128::new(10u128.pow(exponent.unsigned_abs())))?; diff --git a/integration-tests/conv.go b/integration-tests/conv.go index 85206daa..65f7e2d1 100644 --- a/integration-tests/conv.go +++ b/integration-tests/conv.go @@ -8,7 +8,7 @@ import ( "github.com/stretchr/testify/require" ) -// ConvertStringWithDecimalsToSDKInt accepts the float string and returns the value equal to `value * 10^tokenDecimals` truncate to int. +// ConvertStringWithDecimalsToSDKInt accepts the float string and returns the value equal to `value * 1e(tokenDecimals)` truncate to int. func ConvertStringWithDecimalsToSDKInt(t *testing.T, stringValue string, tokenDecimals int64) sdkmath.Int { tenPowerDec := big.NewInt(0).Exp(big.NewInt(10), big.NewInt(tokenDecimals), nil) valueRat, ok := big.NewRat(0, 1).SetString(stringValue) diff --git a/integration-tests/coreum/contract_client_test.go b/integration-tests/coreum/contract_client_test.go index 520990ba..8cb5d93f 100644 --- a/integration-tests/coreum/contract_client_test.go +++ b/integration-tests/coreum/contract_client_test.go @@ -294,7 +294,7 @@ func TestRegisterXRPLToken(t *testing.T) { issuerAcc := chains.XRPL.GenAccount(ctx, t, 0) issuer := issuerAcc.String() - invactiveCurrency := "INA" + inactiveCurrency := "INA" activeCurrency := "ACT" sendingPrecision := int32(15) maxHoldingAmount := sdk.NewIntFromUint64(10000) @@ -303,15 +303,15 @@ func TestRegisterXRPLToken(t *testing.T) { allocateInitialTickets(ctx, t, contractClient, owner, relayers) // try to register from not owner - _, err := contractClient.RegisterXRPLToken(ctx, notOwner, issuer, invactiveCurrency, sendingPrecision, maxHoldingAmount) + _, err := contractClient.RegisterXRPLToken(ctx, notOwner, issuer, inactiveCurrency, sendingPrecision, maxHoldingAmount) require.True(t, coreum.IsNotOwnerError(err), err) // register from the owner - _, err = contractClient.RegisterXRPLToken(ctx, owner, issuer, invactiveCurrency, sendingPrecision, maxHoldingAmount) + _, err = contractClient.RegisterXRPLToken(ctx, owner, issuer, inactiveCurrency, sendingPrecision, maxHoldingAmount) require.NoError(t, err) // try to register the same denom one more time - _, err = contractClient.RegisterXRPLToken(ctx, owner, issuer, invactiveCurrency, sendingPrecision, maxHoldingAmount) + _, err = contractClient.RegisterXRPLToken(ctx, owner, issuer, inactiveCurrency, sendingPrecision, maxHoldingAmount) require.True(t, coreum.IsXRPLTokenAlreadyRegisteredError(err), err) xrplTokens, err := contractClient.GetXRPLTokens(ctx) @@ -319,13 +319,13 @@ func TestRegisterXRPLToken(t *testing.T) { // one XRP token and registered require.Len(t, xrplTokens, 2) - registeredInactiveToken, err := contractClient.GetXRPLToken(ctx, issuer, invactiveCurrency) + registeredInactiveToken, err := contractClient.GetXRPLToken(ctx, issuer, inactiveCurrency) require.NoError(t, err) require.NotNil(t, registeredInactiveToken) require.Equal(t, coreum.XRPLToken{ Issuer: issuer, - Currency: invactiveCurrency, + Currency: inactiveCurrency, CoreumDenom: registeredInactiveToken.CoreumDenom, State: coreum.TokenStateProcessing, }, *registeredInactiveToken) @@ -371,7 +371,7 @@ func TestRegisterXRPLToken(t *testing.T) { TransactionResult: coreum.TransactionResultRejected, }, Issuer: issuer, - Currency: invactiveCurrency, + Currency: inactiveCurrency, } // try to register not existing operation @@ -399,13 +399,13 @@ func TestRegisterXRPLToken(t *testing.T) { require.NoError(t, err) require.Equal(t, strconv.FormatBool(true), thresholdReachedTrustSet) - registeredInactiveToken, err = contractClient.GetXRPLToken(ctx, issuer, invactiveCurrency) + registeredInactiveToken, err = contractClient.GetXRPLToken(ctx, issuer, inactiveCurrency) require.NoError(t, err) require.NotNil(t, registeredInactiveToken) require.Equal(t, coreum.XRPLToken{ Issuer: issuer, - Currency: invactiveCurrency, + Currency: inactiveCurrency, CoreumDenom: registeredInactiveToken.CoreumDenom, State: coreum.TokenStateInactive, }, *registeredInactiveToken) @@ -418,7 +418,7 @@ func TestRegisterXRPLToken(t *testing.T) { xrplToCoreumInactiveTokenTransferEvidence := coreum.XRPLToCoreumTransferEvidence{ TxHash: genXRPLTxHash(t), Issuer: issuerAcc.String(), - Currency: invactiveCurrency, + Currency: inactiveCurrency, Amount: sdkmath.NewInt(10), Recipient: coreumRecipient, } diff --git a/integration-tests/processes/ticket_allocation_test.go b/integration-tests/processes/ticket_allocation_test.go index a90cbdc8..676c8d2c 100644 --- a/integration-tests/processes/ticket_allocation_test.go +++ b/integration-tests/processes/ticket_allocation_test.go @@ -198,7 +198,7 @@ func TestTicketsReAllocationByTheXRPLTokenRegistration(t *testing.T) { xrplCurrencyIssuerAcc := chains.XRPL.GenAccount(ctx, t, 100) // register more than threshold to activate tickets re-allocation - numberOfXRPLTokensToRegister := int(numberOfTicketsToAllocate) - 1 + numberOfXRPLTokensToRegister := envCfg.UsedTicketsThreshold + 1 // fund owner to cover registration fees chains.Coreum.FundAccountWithOptions(ctx, t, runnerEnv.ContractOwner, coreumintegration.BalancesOptions{ Amount: chains.Coreum.QueryAssetFTParams(ctx, t).IssueFee.Amount.MulRaw(int64(numberOfXRPLTokensToRegister)).MulRaw(2), diff --git a/relayer/coreum/contract.go b/relayer/coreum/contract.go index 9f5ee4ce..38ef630b 100644 --- a/relayer/coreum/contract.go +++ b/relayer/coreum/contract.go @@ -655,7 +655,7 @@ func (c *ContractClient) GetXRPLToken(ctx context.Context, issuer, currency stri } } - return nil, nil //nolint:nilnil // is token not found we return nil instead of an error + return nil, nil //nolint:nilnil // if token not found we return nil instead of an error } // GetXRPLTokens returns a list of all XRPL tokens. @@ -829,77 +829,77 @@ func (c *ContractClient) getTxFactory() client.Factory { // ******************** Error func ******************** -// IsNotOwnerError returns true if error is `not owner` error. +// IsNotOwnerError returns true if error is `not owner`. func IsNotOwnerError(err error) bool { return isError(err, notOwnerErrorString) } -// IsCoreumTokenAlreadyRegisteredError returns true if error is `CoreumTokenAlreadyRegistered` error. +// IsCoreumTokenAlreadyRegisteredError returns true if error is `CoreumTokenAlreadyRegistered`. func IsCoreumTokenAlreadyRegisteredError(err error) bool { return isError(err, coreumTokenAlreadyRegisteredErrorString) } -// IsXRPLTokenAlreadyRegisteredError returns true if error is `XRPLTokenAlreadyRegistered` error. +// IsXRPLTokenAlreadyRegisteredError returns true if error is `XRPLTokenAlreadyRegistered`. func IsXRPLTokenAlreadyRegisteredError(err error) bool { return isError(err, xrplTokenAlreadyRegisteredErrorString) } -// IsUnauthorizedSenderError returns true if error is `UnauthorizedSender` error. +// IsUnauthorizedSenderError returns true if error is `UnauthorizedSender`. func IsUnauthorizedSenderError(err error) bool { return isError(err, unauthorizedSenderErrorString) } -// IsOperationAlreadyExecutedError returns true if error is `OperationAlreadyExecuted` error. +// IsOperationAlreadyExecutedError returns true if error is `OperationAlreadyExecuted`. func IsOperationAlreadyExecutedError(err error) bool { return isError(err, operationAlreadyExecutedErrorString) } -// IsTokenNotRegisteredError returns true if error is `TokenNotRegistered` error. +// IsTokenNotRegisteredError returns true if error is `TokenNotRegistered`. func IsTokenNotRegisteredError(err error) bool { return isError(err, tokenNotRegisteredErrorString) } -// IsEvidenceAlreadyProvidedError returns true if error is `EvidenceAlreadyProvided` error. +// IsEvidenceAlreadyProvidedError returns true if error is `EvidenceAlreadyProvided`. func IsEvidenceAlreadyProvidedError(err error) bool { return isError(err, evidenceAlreadyProvidedErrorString) } -// IsPendingTicketUpdateError returns true if error is `PendingTicketUpdate` error. +// IsPendingTicketUpdateError returns true if error is `PendingTicketUpdate`. func IsPendingTicketUpdateError(err error) bool { return isError(err, pendingTicketUpdateErrorString) } -// IsInvalidTicketNumberToAllocateError returns true if error is `InvalidTicketNumberToAllocate` error. +// IsInvalidTicketNumberToAllocateError returns true if error is `InvalidTicketNumberToAllocate`. func IsInvalidTicketNumberToAllocateError(err error) bool { return isError(err, invalidTicketNumberToAllocateErrorString) } -// IsSignatureAlreadyProvidedError returns true if error is `SignatureAlreadyProvided` error. +// IsSignatureAlreadyProvidedError returns true if error is `SignatureAlreadyProvided`. func IsSignatureAlreadyProvidedError(err error) bool { return isError(err, signatureAlreadyProvidedErrorString) } -// IsPendingOperationNotFoundError returns true if error is `PendingOperationNotFound` error. +// IsPendingOperationNotFoundError returns true if error is `PendingOperationNotFound`. func IsPendingOperationNotFoundError(err error) bool { return isError(err, pendingOperationNotFoundErrorString) } -// IsAmountSentIsZeroAfterTruncationError returns true if error is `AmountSentIsZeroAfterTruncation` error. +// IsAmountSentIsZeroAfterTruncationError returns true if error is `AmountSentIsZeroAfterTruncation`. func IsAmountSentIsZeroAfterTruncationError(err error) bool { return isError(err, amountSentIsZeroAfterTruncatingErrorString) } -// IsMaximumBridgedAmountReachedError returns true if error is `MaximumBridgedAmountReached` error. +// IsMaximumBridgedAmountReachedError returns true if error is `MaximumBridgedAmountReached`. func IsMaximumBridgedAmountReachedError(err error) bool { return isError(err, maximumBridgedAmountReachedErrorString) } -// IsStillHaveAvailableTicketsError returns true if error is `StillHaveAvailableTickets` error. +// IsStillHaveAvailableTicketsError returns true if error is `StillHaveAvailableTickets`. func IsStillHaveAvailableTicketsError(err error) bool { return isError(err, stillHaveAvailableTicketsErrorString) } -// IsXRPLTokenNotEnabledError returns true if error is `XRPLTokenNotEnabled` error. +// IsXRPLTokenNotEnabledError returns true if error is `XRPLTokenNotEnabled`. func IsXRPLTokenNotEnabledError(err error) bool { return isError(err, xrplTokenNotEnabledErrorString) } diff --git a/relayer/fee/rounding_test.go b/relayer/fee/rounding_test.go index 3db77218..469917ff 100644 --- a/relayer/fee/rounding_test.go +++ b/relayer/fee/rounding_test.go @@ -285,7 +285,7 @@ func truncateRatBySendingPrecision(ratValue *big.Rat, sendingPrecision int) *big switch { case sendingPrecision > 0: tenPowerDec := big.NewInt(0).Exp(big.NewInt(10), big.NewInt(int64(sendingPrecision)), nil) - // (nominator / (denominator / 10^sendingPrecision) * (denominator / 10^sendingPrecision) with denominator equal original + // (nominator / (denominator / 1e(sendingPrecision)) * (denominator / 1e(sendingPrecision)) with denominator equal original subDenominator := big.NewInt(0).Quo(denominator, tenPowerDec) if subDenominator.Cmp(big.NewInt(0)) == 1 { updatedNominator := big.NewInt(0).Mul( @@ -306,7 +306,7 @@ func truncateRatBySendingPrecision(ratValue *big.Rat, sendingPrecision int) *big // nominator > denominator if nominator.Cmp(denominator) == 1 { tenPowerDec := big.NewInt(0).Exp(big.NewInt(10), big.NewInt(int64(-sendingPrecision)), nil) - // (nominator / denominator / 10^(-1*sendingPrecision) * 10^(-1*sendingPrecision) with denominator equal 1 + // (nominator / denominator / 1e(-1*sendingPrecision) * 1e(-1*sendingPrecision) with denominator equal 1 updatedNominator := big.NewInt(0).Mul( big.NewInt(0).Quo( big.NewInt(0).Quo(nominator, denominator), tenPowerDec), diff --git a/relayer/processes/amount.go b/relayer/processes/amount.go index bcf94d32..beb7f441 100644 --- a/relayer/processes/amount.go +++ b/relayer/processes/amount.go @@ -32,7 +32,7 @@ func ConvertXRPLNativeTokenXRPLAmountToCoreumAmount(xrplAmount rippledata.Amount if xrplAmount.IsNative() { return sdkmath.NewIntFromBigInt(xrplRatAmount.Num()), nil } - // not XRP value is repressed as value multiplied by 10^15 + // not XRP value is repressed as value multiplied by 1e15 tenPowerDec := big.NewInt(0).Exp(big.NewInt(10), big.NewInt(int64(XRPLIssuedCurrencyDecimals)), nil) binIntAmount := big.NewInt(0).Quo(big.NewInt(0).Mul(tenPowerDec, xrplRatAmount.Num()), xrplRatAmount.Denom()) if binIntAmount.BitLen() > sdkmath.MaxBitLen { diff --git a/spec/spec.md b/spec/spec.md index 599a332e..f91d6850 100644 --- a/spec/spec.md +++ b/spec/spec.md @@ -147,7 +147,7 @@ When a user transfers a token from the XRPL to coreum we can compute the expecte ```text roundedRatAmount := roundWithSendingPrecision(sendingRatAmount) -receivedIntAmount = roundedRatAmount * 10^tokenDecimals - bridgingIntFee +receivedIntAmount = roundedRatAmount * 1e(tokenDecimals) - bridgingIntFee ``` The `roundWithSendingPrecision` is described [here](#amount-rounding-handling) . @@ -165,9 +165,9 @@ When a user transfers a token from the coreum to XRPL account we can compute the formula: ```text -sendingRatValue = Rat{sendingIntAmount / 10^tokenDecimals} +sendingRatValue = Rat{sendingIntAmount / 1e(tokenDecimals)} sendingRatValueWithoutTransferFee = sendingRatValue - (transferRatFeeRate * sendingRatValue - sendingRatValue) -bridgingRatFee = Rat{bridgingIntFee / 10^tokenDecimals} +bridgingRatFee = Rat{bridgingIntFee / 1e(tokenDecimals)} sendingRatValueWithoutAllFees = sendingRatValueWithoutTransferFee - bridgingRatFee roundedRatAmount := roundWithSendingPrecision(sendingRatValueWithoutAllFees) ``` @@ -289,12 +289,12 @@ func roundWithSendingPrecision (ratValue Rat, sendingPrecision int) Rat{ denominator := ratValue.Denominator case: sendingPrecision > 0: - nominator = (nominator / (denominator / 10^sendingPrecision) * (denominator / 10^sendingPrecision) + nominator = (nominator / (denominator / 1e(sendingPrecision)) * (denominator / 1esendingPrecision) case: sendingPrecision == 0: nominator = nominator / denominator denominator = 1 case sendingPrecision < 0: - nominator = (nominator / denominator / 10^(-1*sendingPrecision) * 10^(-1*sendingPrecision) + nominator = (nominator / denominator / 1e(-1*sendingPrecision) * 1e(-1*sendingPrecision) denominator = 1 return Rat{nominator, denominator} From b9fb691f9804927077c501bed48576737bfee61d Mon Sep 17 00:00:00 2001 From: Dzmitry Hil Date: Tue, 7 Nov 2023 15:12:00 +0300 Subject: [PATCH 6/8] * Update comments and variable names --- integration-tests/processes/ticket_allocation_test.go | 8 ++++---- relayer/processes/amount.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/integration-tests/processes/ticket_allocation_test.go b/integration-tests/processes/ticket_allocation_test.go index 676c8d2c..ca927590 100644 --- a/integration-tests/processes/ticket_allocation_test.go +++ b/integration-tests/processes/ticket_allocation_test.go @@ -211,11 +211,11 @@ func TestTicketsReAllocationByTheXRPLTokenRegistration(t *testing.T) { } runnerEnv.AwaitNoPendingOperations(ctx, t) - availableTicketsAfterReAllocation, err := runnerEnv.ContractClient.GetAvailableTickets(ctx) + availableTicketsAfterReallocation, err := runnerEnv.ContractClient.GetAvailableTickets(ctx) require.NoError(t, err) - require.Len(t, availableTicketsAfterReAllocation, envCfg.UsedTicketsThreshold) + require.Len(t, availableTicketsAfterReallocation, envCfg.UsedTicketsThreshold) // check that tickets are used - require.NotEqualValues(t, initialAvailableTickets, availableTicketsAfterReAllocation) + require.NotEqualValues(t, initialAvailableTickets, availableTicketsAfterReallocation) // use re-allocated tickets for i := 0; i < numberOfXRPLTokensToRegister; i++ { @@ -227,5 +227,5 @@ func TestTicketsReAllocationByTheXRPLTokenRegistration(t *testing.T) { availableTicketsAfterSecondReallocation, err := runnerEnv.ContractClient.GetAvailableTickets(ctx) require.NoError(t, err) require.NotEqualValues(t, initialAvailableTickets, availableTicketsAfterSecondReallocation) - require.NotEqualValues(t, availableTicketsAfterReAllocation, availableTicketsAfterSecondReallocation) + require.NotEqualValues(t, availableTicketsAfterReallocation, availableTicketsAfterSecondReallocation) } diff --git a/relayer/processes/amount.go b/relayer/processes/amount.go index beb7f441..c59de9de 100644 --- a/relayer/processes/amount.go +++ b/relayer/processes/amount.go @@ -15,7 +15,7 @@ const ( XRPLAmountPrec = 16 // XRPLIssuedCurrencyDecimals is XRPL decimals used on the coreum. XRPLIssuedCurrencyDecimals = 15 - // XRPIssuer is XRP issuer name used on the coreum. + // XRPIssuer is XRP issuer address used on the coreum. XRPIssuer = "rrrrrrrrrrrrrrrrrrrrrho" // XRPCurrency is XRP currency name used on the coreum. XRPCurrency = "XRP" From e085b3cabf733e88698348cd078898a6087213ac Mon Sep 17 00:00:00 2001 From: Dzmitry Hil Date: Tue, 7 Nov 2023 15:46:11 +0300 Subject: [PATCH 7/8] * Update coreum version --- integration-tests/go.mod | 3 ++- integration-tests/go.sum | 4 ++-- relayer/go.mod | 26 ++++++++++++-------------- relayer/go.sum | 4 ++-- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 9773f6ba..d379a9d4 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -21,7 +21,7 @@ replace ( require ( github.com/CoreumFoundation/coreum-tools v0.4.1-0.20230920110418-b30366f1b19b - github.com/CoreumFoundation/coreum/v3 v3.0.0-20231003143732-da36cc6424f4 + github.com/CoreumFoundation/coreum/v3 v3.0.0-20231107070602-2ae43ed1f7cd github.com/CoreumFoundation/coreumbridge-xrpl/relayer v1.0.0 github.com/rubblelabs/ripple v0.0.0-20230908201244-7f73b1fe5e22 github.com/samber/lo v1.38.1 @@ -59,6 +59,7 @@ require ( github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chzyer/readline v1.5.1 // indirect + github.com/cockroachdb/apd/v2 v2.0.2 // indirect github.com/cockroachdb/errors v1.10.0 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect github.com/cockroachdb/redact v1.1.5 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 34fec158..9f1dc0a4 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -224,8 +224,8 @@ github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d h1:nalkkPQ github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4= github.com/CoreumFoundation/coreum-tools v0.4.1-0.20230920110418-b30366f1b19b h1:nSNvOe9oRVl0Ijph3u/e1nZi7j4vGhYyTI3NVbPLdXI= github.com/CoreumFoundation/coreum-tools v0.4.1-0.20230920110418-b30366f1b19b/go.mod h1:VD93vCHkxYaT/RhOesXTFgd/GQDW54tr0BqGi5JU1c0= -github.com/CoreumFoundation/coreum/v3 v3.0.0-20231003143732-da36cc6424f4 h1:APcwYmK6S6UTwcVm3dnsE3tV/IoVLGKrpwDcRKN1wCk= -github.com/CoreumFoundation/coreum/v3 v3.0.0-20231003143732-da36cc6424f4/go.mod h1:JD6ogmhLUI+v9QJfuLT/Qyyrfugo7Cg76Pys/KehbwY= +github.com/CoreumFoundation/coreum/v3 v3.0.0-20231107070602-2ae43ed1f7cd h1:NEwCGG9i6yjPY/avFTcrCDF16zvzIcvldnUR8AZtA7U= +github.com/CoreumFoundation/coreum/v3 v3.0.0-20231107070602-2ae43ed1f7cd/go.mod h1:XTqILFqH1e0GF1bYEnu/I0mElsfwH5OWfu2F5DACIjY= github.com/CosmWasm/wasmd v0.41.0 h1:fmwxSbwb50zZDcBaayYFRLIaSFca+EFld1WOaQi49jg= github.com/CosmWasm/wasmd v0.41.0/go.mod h1:0Sds1q2IsPaTN1gHa3BNOYcUFgtGvxH7CXEXPgoihns= github.com/CosmWasm/wasmvm v1.3.0 h1:x12X4bKlUPS7TT9QQP45+fJo2sp30GEbiSSgb9jsec8= diff --git a/relayer/go.mod b/relayer/go.mod index 92e2305d..7130d3b4 100644 --- a/relayer/go.mod +++ b/relayer/go.mod @@ -21,7 +21,7 @@ replace ( require ( cosmossdk.io/math v1.1.2 github.com/CoreumFoundation/coreum-tools v0.4.1-0.20230920110418-b30366f1b19b - github.com/CoreumFoundation/coreum/v3 v3.0.0-20231003143732-da36cc6424f4 + github.com/CoreumFoundation/coreum/v3 v3.0.0-20231107070602-2ae43ed1f7cd github.com/CosmWasm/wasmd v0.41.0 github.com/cosmos/cosmos-sdk v0.47.5 github.com/golang/mock v1.6.0 @@ -35,19 +35,6 @@ require ( gopkg.in/yaml.v3 v3.0.1 ) -require ( - github.com/bits-and-blooms/bitset v1.2.1 // indirect - github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/pkg/errors v0.9.1 - github.com/rogpeppe/go-internal v1.11.0 // indirect - go.uber.org/atomic v1.10.0 // indirect - go.uber.org/multierr v1.8.0 // indirect - golang.org/x/crypto v0.11.0 // indirect - golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb // indirect -) - require ( cloud.google.com/go v0.110.4 // indirect cloud.google.com/go/compute v1.20.1 // indirect @@ -70,10 +57,13 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect + github.com/bits-and-blooms/bitset v1.2.1 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/cenkalti/backoff/v4 v4.1.3 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chzyer/readline v1.5.1 // indirect + github.com/cockroachdb/apd/v2 v2.0.2 // indirect github.com/cockroachdb/errors v1.10.0 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect github.com/cockroachdb/redact v1.1.5 // indirect @@ -94,6 +84,7 @@ require ( github.com/creachadair/taskgroup v0.4.2 // indirect github.com/danieljoos/wincred v1.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect github.com/dgraph-io/badger/v2 v2.2007.4 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect @@ -143,6 +134,7 @@ require ( github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect github.com/klauspost/compress v1.16.3 // indirect + github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/lib/pq v1.10.7 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect @@ -161,6 +153,7 @@ require ( github.com/opencontainers/go-digest v1.0.0 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect + github.com/pkg/errors v0.9.1 github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.16.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect @@ -168,6 +161,7 @@ require ( github.com/prometheus/procfs v0.10.1 // indirect github.com/rakyll/statik v0.1.7 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect + github.com/rogpeppe/go-internal v1.11.0 // indirect github.com/rs/cors v1.8.3 // indirect github.com/rs/zerolog v1.30.0 // indirect github.com/sasha-s/go-deadlock v0.3.1 // indirect @@ -185,6 +179,10 @@ require ( github.com/zondax/ledger-go v0.14.1 // indirect go.etcd.io/bbolt v1.3.7 // indirect go.opencensus.io v0.24.0 // indirect + go.uber.org/atomic v1.10.0 // indirect + go.uber.org/multierr v1.8.0 // indirect + golang.org/x/crypto v0.11.0 // indirect + golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb // indirect golang.org/x/net v0.12.0 // indirect golang.org/x/oauth2 v0.10.0 // indirect golang.org/x/sync v0.2.0 // indirect diff --git a/relayer/go.sum b/relayer/go.sum index 34fec158..9f1dc0a4 100644 --- a/relayer/go.sum +++ b/relayer/go.sum @@ -224,8 +224,8 @@ github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d h1:nalkkPQ github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4= github.com/CoreumFoundation/coreum-tools v0.4.1-0.20230920110418-b30366f1b19b h1:nSNvOe9oRVl0Ijph3u/e1nZi7j4vGhYyTI3NVbPLdXI= github.com/CoreumFoundation/coreum-tools v0.4.1-0.20230920110418-b30366f1b19b/go.mod h1:VD93vCHkxYaT/RhOesXTFgd/GQDW54tr0BqGi5JU1c0= -github.com/CoreumFoundation/coreum/v3 v3.0.0-20231003143732-da36cc6424f4 h1:APcwYmK6S6UTwcVm3dnsE3tV/IoVLGKrpwDcRKN1wCk= -github.com/CoreumFoundation/coreum/v3 v3.0.0-20231003143732-da36cc6424f4/go.mod h1:JD6ogmhLUI+v9QJfuLT/Qyyrfugo7Cg76Pys/KehbwY= +github.com/CoreumFoundation/coreum/v3 v3.0.0-20231107070602-2ae43ed1f7cd h1:NEwCGG9i6yjPY/avFTcrCDF16zvzIcvldnUR8AZtA7U= +github.com/CoreumFoundation/coreum/v3 v3.0.0-20231107070602-2ae43ed1f7cd/go.mod h1:XTqILFqH1e0GF1bYEnu/I0mElsfwH5OWfu2F5DACIjY= github.com/CosmWasm/wasmd v0.41.0 h1:fmwxSbwb50zZDcBaayYFRLIaSFca+EFld1WOaQi49jg= github.com/CosmWasm/wasmd v0.41.0/go.mod h1:0Sds1q2IsPaTN1gHa3BNOYcUFgtGvxH7CXEXPgoihns= github.com/CosmWasm/wasmvm v1.3.0 h1:x12X4bKlUPS7TT9QQP45+fJo2sp30GEbiSSgb9jsec8= From 1fa4f9d7bac00b679f9357d2d179e7f966b33a41 Mon Sep 17 00:00:00 2001 From: Dzmitry Hil Date: Tue, 7 Nov 2023 17:42:52 +0300 Subject: [PATCH 8/8] Improve XRPIssuer comment. --- relayer/processes/amount.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/relayer/processes/amount.go b/relayer/processes/amount.go index c59de9de..39d17408 100644 --- a/relayer/processes/amount.go +++ b/relayer/processes/amount.go @@ -15,7 +15,8 @@ const ( XRPLAmountPrec = 16 // XRPLIssuedCurrencyDecimals is XRPL decimals used on the coreum. XRPLIssuedCurrencyDecimals = 15 - // XRPIssuer is XRP issuer address used on the coreum. + // XRPIssuer is XRP issuer address used to generate XRP token representation on coreum side. This is done to unify + // representation for all XRPL originated tokens. XRPIssuer = "rrrrrrrrrrrrrrrrrrrrrho" // XRPCurrency is XRP currency name used on the coreum. XRPCurrency = "XRP"