Skip to content

Commit 52558e7

Browse files
authored
Relayer: Sending of the coreum originated token from the coreum to XRPL. (#50)
Relayer: Sending of the coreum originated token from the coreum to XRPL. * Add contract client integration tests * Implement relayer part for the sending
1 parent 7701c01 commit 52558e7

24 files changed

+1167
-391
lines changed

.github/workflows/relayer-ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232
linter-cache: false
3333
docker-cache: false
3434
- ci_step: "integration tests"
35-
command: "make build-contract && make rebuild-dev-env && make restart-dev-env && make test-integration"
35+
command: "make build-contract && make build-dev-env && make restart-dev-env && make test-integration"
3636
go-cache: true
3737
wasm-cache: true
3838
linter-cache: false

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,8 @@ test-contract:
8888
restart-dev-env:
8989
crust znet remove && crust znet start --profiles=1cored,xrpl --timeout-commit 0.5s
9090

91-
.PHONY: rebuild-dev-env
92-
rebuild-dev-env:
91+
.PHONY: build-dev-env
92+
build-dev-env:
9393
crust build/crust images/cored
9494

9595
.PHONY: smoke

integration-tests/coreum/contract_client_test.go

Lines changed: 427 additions & 52 deletions
Large diffs are not rendered by default.

integration-tests/init.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ func init() {
4242
// parse additional flags
4343
flag.Parse()
4444

45-
zapDevLogger, err := zap.NewDevelopment()
45+
zapDevConfig := zap.NewDevelopmentConfig()
46+
zapDevConfig.Level = zap.NewAtomicLevelAt(zap.InfoLevel)
47+
zapDevLogger, err := zapDevConfig.Build()
4648
if err != nil {
4749
panic(errors.WithStack(err))
4850
}

integration-tests/processes/env_test.go

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,9 @@ func NewRunnerEnv(ctx context.Context, t *testing.T, cfg RunnerEnvConfig, chains
109109
runners = append(
110110
runners,
111111
createDevRunner(
112+
ctx,
112113
t,
113114
chains,
114-
bridgeXRPLAddress,
115115
relayerXRPLAddresses[i],
116116
contractClient.GetContractAddress(),
117117
relayerCoreumAddresses[i],
@@ -125,9 +125,9 @@ func NewRunnerEnv(ctx context.Context, t *testing.T, cfg RunnerEnvConfig, chains
125125
runners = append(
126126
runners,
127127
createDevRunner(
128+
ctx,
128129
t,
129130
chains,
130-
bridgeXRPLAddress,
131131
maliciousXRPLAddress,
132132
contractClient.GetContractAddress(),
133133
relayerCoreumAddresses[i],
@@ -269,12 +269,11 @@ func (r *RunnerEnv) RegisterXRPLOriginatedToken(
269269
require.NoError(t, err)
270270
// await for the trust set
271271
r.AwaitNoPendingOperations(ctx, t)
272-
registeredXRPLToken, err := r.ContractClient.GetXRPLToken(ctx, issuer.String(), xrpl.ConvertCurrencyToString(currency))
272+
registeredXRPLToken, err := r.ContractClient.GetXRPLTokenByIssuerAndCurrency(ctx, issuer.String(), xrpl.ConvertCurrencyToString(currency))
273273
require.NoError(t, err)
274-
require.NotNil(t, registeredXRPLToken)
275274
require.Equal(t, coreum.TokenStateEnabled, registeredXRPLToken.State)
276275

277-
return *registeredXRPLToken
276+
return registeredXRPLToken
278277
}
279278

280279
// RequireNoErrors check whether the runner err received runner errors.
@@ -347,7 +346,7 @@ func (r *RunnerEnv) SendXRPLMaxTrustSetTx(
347346
issuer rippledata.Account,
348347
currency rippledata.Currency,
349348
) {
350-
value, err := rippledata.NewValue("1000000000000", false)
349+
value, err := rippledata.NewValue("1e80", false)
351350
require.NoError(t, err)
352351
trustSetTx := rippledata.TrustSet{
353352
LimitAmount: rippledata.Amount{
@@ -439,12 +438,12 @@ func genBridgeXRPLAccountWithRelayers(
439438
}
440439

441440
func createDevRunner(
441+
ctx context.Context,
442442
t *testing.T,
443443
chains integrationtests.Chains,
444-
bridgeXRPLAddress rippledata.Account,
445444
xrplRelayerAcc rippledata.Account,
446445
contractAddress sdk.AccAddress,
447-
coreumRelayerAddress sdk.AccAddress,
446+
relayerCoreumAddress sdk.AccAddress,
448447
) *runner.Runner {
449448
t.Helper()
450449

@@ -458,7 +457,7 @@ func createDevRunner(
458457

459458
// reimport coreum key
460459
coreumKr := chains.Coreum.ClientContext.Keyring()
461-
keyInfo, err := coreumKr.KeyByAddress(coreumRelayerAddress)
460+
keyInfo, err := coreumKr.KeyByAddress(relayerCoreumAddress)
462461
require.NoError(t, err)
463462
pass := uuid.NewString()
464463
armor, err := coreumKr.ExportPrivKeyArmor(keyInfo.Name, pass)
@@ -474,9 +473,8 @@ func createDevRunner(
474473
require.NoError(t, kr.ImportPrivKey(relayerXRPLKeyName, armor, pass))
475474

476475
relayerRunnerCfg := runner.DefaultConfig()
477-
relayerRunnerCfg.LoggingConfig.Level = "debug"
476+
relayerRunnerCfg.LoggingConfig.Level = "info"
478477

479-
relayerRunnerCfg.XRPL.BridgeAccount = bridgeXRPLAddress.String()
480478
relayerRunnerCfg.XRPL.MultiSignerKeyName = relayerXRPLKeyName
481479
relayerRunnerCfg.XRPL.RPC.URL = chains.XRPL.Config().RPCAddress
482480
// make the scanner fast
@@ -493,7 +491,7 @@ func createDevRunner(
493491
// make operation fetcher fast
494492
relayerRunnerCfg.Processes.XRPLTxSubmitter.RepeatDelay = 500 * time.Millisecond
495493

496-
relayerRunner, err := runner.NewRunner(relayerRunnerCfg, kr)
494+
relayerRunner, err := runner.NewRunner(ctx, relayerRunnerCfg, kr)
497495
require.NoError(t, err)
498496
return relayerRunner
499497
}

integration-tests/processes/send_from_coreum_to_xrpl_test.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ import (
1414
rippledata "github.com/rubblelabs/ripple/data"
1515
"github.com/stretchr/testify/require"
1616

17+
"github.com/CoreumFoundation/coreum/v3/pkg/client"
1718
coreumintegration "github.com/CoreumFoundation/coreum/v3/testutil/integration"
19+
assetfttypes "github.com/CoreumFoundation/coreum/v3/x/asset/ft/types"
1820
integrationtests "github.com/CoreumFoundation/coreumbridge-xrpl/integration-tests"
1921
"github.com/CoreumFoundation/coreumbridge-xrpl/relayer/coreum"
2022
"github.com/CoreumFoundation/coreumbridge-xrpl/relayer/xrpl"
@@ -203,3 +205,93 @@ func TestSendFromXRPLToCoreumWithTicketsReallocation(t *testing.T) {
203205
balance := runnerEnv.Chains.XRPL.GetAccountBalance(ctx, t, xrplRecipientAddress, xrplIssuerAddress, registeredXRPLCurrency)
204206
require.Equal(t, totalSent.Quo(sdkmath.NewIntWithDecimal(1, XRPLTokenDecimals)).String(), balance.Value.String())
205207
}
208+
209+
func TestRegisterCoreumOriginatedTokenAndSendFromXRPLToCoreum(t *testing.T) {
210+
t.Parallel()
211+
212+
ctx, chains := integrationtests.NewTestingContext(t)
213+
214+
xrplRecipientAddress := chains.XRPL.GenAccount(ctx, t, 0)
215+
t.Logf("XRPL recipient address: %s", xrplRecipientAddress)
216+
217+
coreumSenderAddress := chains.Coreum.GenAccount()
218+
issueFee := chains.Coreum.QueryAssetFTParams(ctx, t).IssueFee
219+
chains.Coreum.FundAccountWithOptions(ctx, t, coreumSenderAddress, coreumintegration.BalancesOptions{
220+
Amount: issueFee.Amount.Add(sdkmath.NewInt(10_000_000)),
221+
})
222+
223+
// issue asset ft and register it
224+
sendingPrecision := int32(2)
225+
tokenDecimals := uint32(4)
226+
maxHoldingAmount, ok := sdk.NewIntFromString("10000000000000000")
227+
require.True(t, ok)
228+
issueMsg := &assetfttypes.MsgIssue{
229+
Issuer: coreumSenderAddress.String(),
230+
Symbol: "denom",
231+
Subunit: "denom",
232+
Precision: tokenDecimals, // token decimals in terms of the contract
233+
InitialAmount: maxHoldingAmount,
234+
}
235+
_, err := client.BroadcastTx(
236+
ctx,
237+
chains.Coreum.ClientContext.WithFromAddress(coreumSenderAddress),
238+
chains.Coreum.TxFactory().WithSimulateAndExecute(true),
239+
issueMsg,
240+
)
241+
require.NoError(t, err)
242+
243+
envCfg := DefaultRunnerEnvConfig()
244+
runnerEnv := NewRunnerEnv(ctx, t, envCfg, chains)
245+
246+
bridgeXRPLAccountInfo, err := chains.XRPL.RPCClient().AccountInfo(ctx, runnerEnv.bridgeXRPLAddress)
247+
require.NoError(t, err)
248+
249+
// recover tickets so we can register tokens
250+
numberOfTicketsToAllocate := uint32(200)
251+
chains.XRPL.FundAccountForTicketAllocation(ctx, t, runnerEnv.bridgeXRPLAddress, numberOfTicketsToAllocate)
252+
_, err = runnerEnv.ContractClient.RecoverTickets(ctx, runnerEnv.ContractOwner, *bridgeXRPLAccountInfo.AccountData.Sequence, &numberOfTicketsToAllocate)
253+
require.NoError(t, err)
254+
255+
// start relayers
256+
runnerEnv.StartAllRunnerProcesses(ctx, t)
257+
runnerEnv.AwaitNoPendingOperations(ctx, t)
258+
availableTickets, err := runnerEnv.ContractClient.GetAvailableTickets(ctx)
259+
require.NoError(t, err)
260+
require.Len(t, availableTickets, int(numberOfTicketsToAllocate))
261+
262+
// register Coreum originated token
263+
require.NoError(t, err)
264+
denom := assetfttypes.BuildDenom(issueMsg.Subunit, coreumSenderAddress)
265+
_, err = runnerEnv.ContractClient.RegisterCoreumToken(ctx, runnerEnv.ContractOwner, denom, tokenDecimals, sendingPrecision, maxHoldingAmount)
266+
require.NoError(t, err)
267+
registeredCoreumOriginatedToken, err := runnerEnv.ContractClient.GetCoreumTokenByDenom(ctx, denom)
268+
require.NoError(t, err)
269+
270+
// send TrustSet to be able to receive coins from the bridge
271+
xrplCurrency, err := rippledata.NewCurrency(registeredCoreumOriginatedToken.XRPLCurrency)
272+
require.NoError(t, err)
273+
runnerEnv.SendXRPLMaxTrustSetTx(ctx, t, xrplRecipientAddress, runnerEnv.bridgeXRPLAddress, xrplCurrency)
274+
275+
// equal to 11.1111 on XRPL, but with the sending prec 2 we expect 11.11 to be received
276+
amountToSend1 := sdkmath.NewInt(111111)
277+
// TODO(dzmitryhil) update assertion once we add the final tx revert/recovery
278+
_, err = runnerEnv.ContractClient.SendToXRPL(ctx, coreumSenderAddress, xrplRecipientAddress.String(), sdk.NewCoin(registeredCoreumOriginatedToken.Denom, amountToSend1))
279+
require.NoError(t, err)
280+
281+
runnerEnv.AwaitNoPendingOperations(ctx, t)
282+
283+
// check the XRPL recipient balance
284+
balance := runnerEnv.Chains.XRPL.GetAccountBalance(ctx, t, xrplRecipientAddress, runnerEnv.bridgeXRPLAddress, xrplCurrency)
285+
require.Equal(t, "11.11", balance.Value.String())
286+
287+
amountToSend2 := maxHoldingAmount.QuoRaw(2)
288+
require.NoError(t, err)
289+
_, err = runnerEnv.ContractClient.SendToXRPL(ctx, coreumSenderAddress, xrplRecipientAddress.String(), sdk.NewCoin(registeredCoreumOriginatedToken.Denom, amountToSend2))
290+
require.NoError(t, err)
291+
292+
runnerEnv.AwaitNoPendingOperations(ctx, t)
293+
294+
// check the XRPL recipient balance
295+
balance = runnerEnv.Chains.XRPL.GetAccountBalance(ctx, t, xrplRecipientAddress, runnerEnv.bridgeXRPLAddress, xrplCurrency)
296+
require.Equal(t, "50000000001111e-2", balance.Value.String())
297+
}

integration-tests/processes/send_from_xrpl_to_coreum_test.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,12 @@ func TestRegisterXRPLOriginatedTokensAndSendFromXRPLToCoreum(t *testing.T) {
7575
// await for the trust set
7676
runnerEnv.AwaitNoPendingOperations(ctx, t)
7777

78-
registeredXRPLToken, err := runnerEnv.ContractClient.GetXRPLToken(ctx, xrplIssuerAddress.String(), xrpl.ConvertCurrencyToString(registeredXRPLCurrency))
78+
registeredXRPLToken, err := runnerEnv.ContractClient.GetXRPLTokenByIssuerAndCurrency(ctx, xrplIssuerAddress.String(), xrpl.ConvertCurrencyToString(registeredXRPLCurrency))
7979
require.NoError(t, err)
80-
require.NotNil(t, registeredXRPLToken)
8180
require.Equal(t, coreum.TokenStateEnabled, registeredXRPLToken.State)
8281

83-
registeredXRPLHexCurrencyToken, err := runnerEnv.ContractClient.GetXRPLToken(ctx, xrplIssuerAddress.String(), xrpl.ConvertCurrencyToString(registeredXRPLHexCurrency))
82+
registeredXRPLHexCurrencyToken, err := runnerEnv.ContractClient.GetXRPLTokenByIssuerAndCurrency(ctx, xrplIssuerAddress.String(), xrpl.ConvertCurrencyToString(registeredXRPLHexCurrency))
8483
require.NoError(t, err)
85-
require.NotNil(t, registeredXRPLHexCurrencyToken)
8684
require.Equal(t, coreum.TokenStateEnabled, registeredXRPLHexCurrencyToken.State)
8785

8886
lowValue, err := rippledata.NewValue("1.00000111", false)

integration-tests/xrpl.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,16 @@ func (c XRPLChain) SubmitTx(ctx context.Context, t *testing.T, tx rippledata.Tra
254254

255255
// GetAccountBalance returns account balance for the provided issuer and currency.
256256
func (c XRPLChain) GetAccountBalance(ctx context.Context, t *testing.T, account, issuer rippledata.Account, currency rippledata.Currency) rippledata.Amount {
257-
return c.GetAccountBalances(ctx, t, account)[fmt.Sprintf("%s/%s", currency.String(), issuer.String())]
257+
balance, ok := c.GetAccountBalances(ctx, t, account)[fmt.Sprintf("%s/%s", currency.String(), issuer.String())]
258+
if !ok {
259+
// equal to zero
260+
return rippledata.Amount{
261+
Value: &rippledata.Value{},
262+
Currency: currency,
263+
Issuer: issuer,
264+
}
265+
}
266+
return balance
258267
}
259268

260269
// GetAccountBalances returns account balances.

relayer/coreum/contract.go

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -127,19 +127,24 @@ type ContractOwnership struct {
127127

128128
// XRPLToken is XRPL token representation on coreum.
129129
type XRPLToken struct {
130-
Issuer string `json:"issuer"`
131-
Currency string `json:"currency"`
132-
CoreumDenom string `json:"coreum_denom"`
133-
State TokenState `json:"state"`
130+
Issuer string `json:"issuer"`
131+
Currency string `json:"currency"`
132+
CoreumDenom string `json:"coreum_denom"`
133+
SendingPrecision int32 `json:"sending_precision"`
134+
MaxHoldingAmount sdkmath.Int `json:"max_holding_amount"`
135+
State TokenState `json:"state"`
134136
}
135137

136138
// CoreumToken is coreum token registered on the contract.
137139
//
138140
//nolint:revive //kept for the better naming convention.
139141
type CoreumToken struct {
140-
Denom string `json:"denom"`
141-
Decimals uint32 `json:"decimals"`
142-
XRPLCurrency string `json:"xrpl_currency"`
142+
Denom string `json:"denom"`
143+
Decimals uint32 `json:"decimals"`
144+
XRPLCurrency string `json:"xrpl_currency"`
145+
SendingPrecision int32 `json:"sending_precision"`
146+
MaxHoldingAmount sdkmath.Int `json:"max_holding_amount"`
147+
State TokenState `json:"state"`
143148
}
144149

145150
// XRPLToCoreumTransferEvidence is evidence with values represented sending from XRPL to coreum.
@@ -717,19 +722,19 @@ func (c *ContractClient) GetContractOwnership(ctx context.Context) (ContractOwne
717722
return response, nil
718723
}
719724

720-
// GetXRPLToken returns an XRPL registered token or nil.
721-
func (c *ContractClient) GetXRPLToken(ctx context.Context, issuer, currency string) (*XRPLToken, error) {
725+
// GetXRPLTokenByIssuerAndCurrency returns a XRPL registered token by issuer and currency or error.
726+
func (c *ContractClient) GetXRPLTokenByIssuerAndCurrency(ctx context.Context, issuer, currency string) (XRPLToken, error) {
722727
tokens, err := c.GetXRPLTokens(ctx)
723728
if err != nil {
724-
return nil, err
729+
return XRPLToken{}, err
725730
}
726731
for _, token := range tokens {
727732
if token.Issuer == issuer && token.Currency == currency {
728-
return &token, nil
733+
return token, nil
729734
}
730735
}
731736

732-
return nil, nil //nolint:nilnil // if token not found we return nil instead of an error
737+
return XRPLToken{}, errors.Errorf("token not found in the registered tokens list, issuer:%s, currency:%s", issuer, currency)
733738
}
734739

735740
// GetXRPLTokens returns a list of all XRPL tokens.
@@ -751,6 +756,37 @@ func (c *ContractClient) GetXRPLTokens(ctx context.Context) ([]XRPLToken, error)
751756
return tokens, nil
752757
}
753758

759+
// GetCoreumTokenByDenom returns a coreum registered token or nil by the provided denom.
760+
func (c *ContractClient) GetCoreumTokenByDenom(ctx context.Context, denom string) (CoreumToken, error) {
761+
tokens, err := c.GetCoreumTokens(ctx)
762+
if err != nil {
763+
return CoreumToken{}, err
764+
}
765+
for _, token := range tokens {
766+
if token.Denom == denom {
767+
return token, nil
768+
}
769+
}
770+
771+
return CoreumToken{}, errors.Errorf("token not found in the registered tokens list, denom:%s", denom)
772+
}
773+
774+
// GetCoreumTokenByXRPLCurrency returns a coreum registered token or nil by the provided xrpl currency.
775+
func (c *ContractClient) GetCoreumTokenByXRPLCurrency(ctx context.Context, xrplCurrency string) (CoreumToken, error) {
776+
// TODO(dzmitryhil) use new query function from the contract once we create it
777+
tokens, err := c.GetCoreumTokens(ctx)
778+
if err != nil {
779+
return CoreumToken{}, err
780+
}
781+
for _, token := range tokens {
782+
if token.XRPLCurrency == xrplCurrency {
783+
return token, nil
784+
}
785+
}
786+
787+
return CoreumToken{}, errors.Errorf("token not found in the registered tokens list, xrplCurrency:%s", xrplCurrency)
788+
}
789+
754790
// GetCoreumTokens returns a list of all coreum tokens.
755791
func (c *ContractClient) GetCoreumTokens(ctx context.Context) ([]CoreumToken, error) {
756792
tokens := make([]CoreumToken, 0)

relayer/logger/zap.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ func NewZapLogger(cfg ZapLoggerConfig) (*ZapLogger, error) {
6767

6868
zapLogger, err := zapCfg.Build(zap.AddCaller(), zap.AddCallerSkip(1), zap.AddStacktrace(zapcore.ErrorLevel))
6969
if err != nil {
70-
return nil, errors.Wrapf(err, "failed to build zap logger form the config, config:%+v", zapCfg)
70+
return nil, errors.Wrapf(err, "failed to build zap logger from the config, config:%+v", zapCfg)
7171
}
7272

7373
return &ZapLogger{

0 commit comments

Comments
 (0)