Skip to content

Commit 37dd7ed

Browse files
fix(xrp): Fix Ripple.proto Kotlin compatibility (#4299)
Some parameters were changed from int64 to uint32 during the recent XRP Rust migration. However `protoc` generates Kotlin bindings with using signed integers only, so overflow happens on Kotlin side * Ripple.OperationPayment.destinationTag is now uint64 * Ripple.OperationEscrowCreate.destinationTag is now uint64 * Ripple.OperationEscrowCreate.cancelAfter is now uint64 * Ripple.OperationEscrowCreate.finishAfter is now uint64 * Ripple.SigningInput.flags is now uint64 * Ripple.SigningInput.sourceTag is now uint64
1 parent 7a6e727 commit 37dd7ed

File tree

3 files changed

+47
-17
lines changed

3 files changed

+47
-17
lines changed

rust/chains/tw_ripple/src/modules/protobuf_builder.rs

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,9 @@ impl<'a> ProtobufBuilder<'a> {
152152
.to_classic_address()
153153
.into_tw()
154154
.context("Error converting 'Payment.destination' to a Classic address")?;
155-
let destination_tag = payment.destination_tag.zero_or_some();
155+
let destination_tag = payment
156+
.destination_tag
157+
.try_into_u32_optional("destinationTag")?;
156158

157159
self.prepare_builder()?
158160
.payment(amount, destination, destination_tag)
@@ -200,9 +202,15 @@ impl<'a> ProtobufBuilder<'a> {
200202
.escrow_create(
201203
native_amount,
202204
destination,
203-
escrow_create.destination_tag.zero_or_some(),
204-
escrow_create.cancel_after.zero_or_some(),
205-
escrow_create.finish_after.zero_or_some(),
205+
escrow_create
206+
.destination_tag
207+
.try_into_u32_optional("destinationTag")?,
208+
escrow_create
209+
.cancel_after
210+
.try_into_u32_optional("cancelAfter")?,
211+
escrow_create
212+
.finish_after
213+
.try_into_u32_optional("finishAfter")?,
206214
condition,
207215
)
208216
.map(TransactionType::EscrowCreate)
@@ -323,13 +331,13 @@ impl<'a> ProtobufBuilder<'a> {
323331
let mut builder = TransactionBuilder::default();
324332
builder
325333
.fee(fee)
326-
.flags(self.input.flags)
334+
.flags(self.input.flags.try_into_u32("inputFlags")?)
327335
.sequence(self.input.sequence)
328336
.last_ledger_sequence(self.input.last_ledger_sequence)
329337
.account_str(self.input.account.as_ref())?
330338
.signing_pub_key(&signing_public_key);
331339
if self.input.source_tag != 0 {
332-
builder.source_tag(self.input.source_tag);
340+
builder.source_tag(self.input.source_tag.try_into_u32("sourceTag")?);
333341
}
334342
Ok(builder)
335343
}
@@ -369,3 +377,19 @@ impl<'a> ProtobufBuilder<'a> {
369377
})
370378
}
371379
}
380+
381+
trait AsUint32: TryInto<u32> {
382+
fn try_into_u32(self, param: &str) -> SigningResult<u32> {
383+
self.try_into()
384+
.map_err(|_| SigningError::new(SigningErrorType::Error_invalid_params))
385+
.with_context(|| format!("'{param}' must fit u32"))
386+
}
387+
388+
/// Tries to convert `self` as `u32`.
389+
/// Returns error if `self` doesn't fit `u32` type, or returns `None` if `self` is 0.
390+
fn try_into_u32_optional(self, param: &str) -> SigningResult<Option<u32>> {
391+
self.try_into_u32(param).map(u32::zero_or_some)
392+
}
393+
}
394+
395+
impl<T> AsUint32 for T where T: TryInto<u32> {}

rust/tw_tests/tests/chains/ripple/ripple_sign.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use tw_proto::Ripple::Proto;
1010
use tw_proto::Ripple::Proto::mod_OperationPayment::OneOfamount_oneof as AmountType;
1111
use tw_proto::Ripple::Proto::mod_SigningInput::OneOfoperation_oneof as OperationType;
1212

13-
const SELL_NFTOKEN_FLAG: u32 = 0x00000001;
13+
const SELL_NFTOKEN_FLAG: u64 = 0x00000001;
1414

1515
#[test]
1616
fn test_ripple_sign_xrp_payment_0() {

src/proto/Ripple.proto

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ message OperationPayment {
3737
string destination = 3;
3838

3939
// A Destination Tag
40-
uint32 destination_tag = 4;
40+
// It must fit uint32
41+
uint64 destination_tag = 4;
4142
}
4243

4344
// https://xrpl.org/escrowcreate.html
@@ -49,13 +50,16 @@ message OperationEscrowCreate {
4950
string destination = 2;
5051

5152
// Destination Tag
52-
uint32 destination_tag = 3;
53+
// It must fit uint32
54+
uint64 destination_tag = 3;
5355

5456
// Escrow expire time
55-
uint32 cancel_after = 4;
57+
// It must fit uint32
58+
uint64 cancel_after = 4;
5659

5760
// Escrow release time
58-
uint32 finish_after = 5;
61+
// It must fit uint32
62+
uint64 finish_after = 5;
5963

6064
// Hex-encoded crypto condition
6165
// https://datatracker.ietf.org/doc/html/draft-thomas-crypto-conditions-02#section-8.1
@@ -86,13 +90,13 @@ message OperationEscrowFinish {
8690
string fulfillment = 4;
8791
}
8892

89-
// https://xrpl.org/nftokenburn.html
93+
// https://xrpl.org/nftokenburn.html
9094
message OperationNFTokenBurn {
9195
// Hex-encoded H256 NFTokenId
9296
string nftoken_id = 1;
9397
}
9498

95-
// https://xrpl.org/nftokencreateoffer.html
99+
// https://xrpl.org/nftokencreateoffer.html
96100
message OperationNFTokenCreateOffer {
97101
// Hex-encoded Hash256 NFTokenId
98102
string nftoken_id = 1;
@@ -101,13 +105,13 @@ message OperationNFTokenCreateOffer {
101105
string destination = 2;
102106
}
103107

104-
// https://xrpl.org/nftokenacceptoffer.html
108+
// https://xrpl.org/nftokenacceptoffer.html
105109
message OperationNFTokenAcceptOffer {
106110
// Hex-encoded Hash256 NFTokenOffer
107111
string sell_offer = 1;
108112
}
109113

110-
// https://xrpl.org/nftokencanceloffer.html
114+
// https://xrpl.org/nftokencanceloffer.html
111115
message OperationNFTokenCancelOffer {
112116
// Hex-encoded Vector256 NFTokenOffers
113117
repeated string token_offers = 1;
@@ -128,7 +132,8 @@ message SigningInput {
128132
string account = 4;
129133

130134
// Transaction flags, optional
131-
uint32 flags = 5;
135+
// It must fit uint32
136+
uint64 flags = 5;
132137

133138
// The secret private key used for signing (32 bytes).
134139
bytes private_key = 6;
@@ -167,7 +172,8 @@ message SigningInput {
167172

168173
// Arbitrary integer used to identify the reason for this payment, or a sender on whose behalf this transaction is made.
169174
// Conventionally, a refund should specify the initial payment's SourceTag as the refund payment's DestinationTag.
170-
uint32 source_tag = 25;
175+
// It must fit uint32.
176+
uint64 source_tag = 25;
171177
}
172178

173179
// Result containing the signed and encoded transaction.

0 commit comments

Comments
 (0)