Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement TrustSet on the XRPL bridge account for the registered XRPL tokens. #41

Merged
merged 8 commits into from
Nov 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,4 @@ restart-dev-env:

.PHONY: rebuild-dev-env
rebuild-dev-env:
crust build images
crust build/crust images/cored
28 changes: 22 additions & 6 deletions contract/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down Expand Up @@ -336,7 +336,7 @@ fn register_xrpl_token(
}

fn save_evidence(deps: DepsMut, sender: Addr, evidence: Evidence) -> CoreumResult<ContractError> {
evidence.validate()?;
evidence.validate_basic()?;

assert_relayer(deps.as_ref(), sender.clone())?;

Expand All @@ -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) {
Expand Down Expand Up @@ -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, 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)
.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 } => {
Expand Down Expand Up @@ -664,8 +680,8 @@ fn truncate_amount(
amount: Uint128,
) -> Result<Uint128, ContractError> {
// 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())))?;
Expand Down
23 changes: 13 additions & 10 deletions contract/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 },

Expand All @@ -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 {},

Expand All @@ -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 {},

Expand All @@ -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 {},

Expand All @@ -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 {},
Expand All @@ -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 {},
}
2 changes: 1 addition & 1 deletion contract/src/evidence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion contract/src/operation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
10 changes: 5 additions & 5 deletions contract/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
4 changes: 2 additions & 2 deletions contract/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
);
}
Expand Down Expand Up @@ -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
Expand Down
36 changes: 22 additions & 14 deletions contract/src/tickets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,25 @@ 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,
// 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)?;
)?;
PENDING_TICKET_UPDATE.save(storage, &true)?
};
}

Ok(())
Expand Down Expand Up @@ -87,6 +91,10 @@ pub fn handle_ticket_allocation_confirmation(
// Extract a ticket from the available tickets
fn reserve_ticket(storage: &mut dyn Storage) -> Result<u64, ContractError> {
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)
Expand Down
3 changes: 2 additions & 1 deletion integration-tests/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"os"
"testing"

sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"

Expand All @@ -22,7 +23,7 @@ func DeployAndInstantiateContract(
relayers []coreum.Relayer,
evidenceThreshold int,
usedTicketsThreshold int,
trustSetLimitAmount string,
trustSetLimitAmount sdkmath.Int,
) (sdk.AccAddress, *coreum.ContractClient) {
t.Helper()

Expand Down
2 changes: 1 addition & 1 deletion integration-tests/conv.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading
Loading