[Polkadot] [Wallet] add extrinsic events parsing routines (#34766)
Resolves brave/brave-browser#53707 To properly grab the status of a submitted extrinsic in Polkadot, we must slowly walk the chain within the given mortality window. Once an extrinsic has been located in the extrinsics array of a given block, we can use this index to probe the block's events for the extrinsic's status. This PR adds the parsing layer of this equation, constructing a search needle for the actual fee paid by the sender and then seraching for the status of the extrinsic itself. Note that even failed extrinsics still incur fee penalties. We depend on the memchr crate because events can be tens of KB and finding a substring can then be a non-trivial operation. We start searching from the right because that's typically where our submitted extrinsics tend to live. Typically, we could use events.window(needle.len()).position(|window| window == needle) to probe for our search string but rustc is insistent in lowering byte slice comparisons into a call bcmp, which would mean that if we slid the window over the entire string, there could easily be something like 40,000 call bcmps. The memchr crate seemed appropriate for this reason, as it's dramatically more performant.
This commit is contained in:
@@ -123,6 +123,7 @@ rust_static_library("polkadot_extrinsic_rs") {
|
||||
|
||||
deps = [
|
||||
"//brave/third_party/rust/blake2b_simd/v1:lib",
|
||||
"//brave/third_party/rust/memchr/v2:lib",
|
||||
"//brave/third_party/rust/parity_scale_codec/v3:lib",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -15,6 +15,17 @@ const MULTIADDRESS_TYPE: u8 = 0x00;
|
||||
const SR25519_SIGNATURE: u8 = 0x01;
|
||||
const PERIOD: u32 = 64;
|
||||
|
||||
const PHASE_APPLY_EXTRINSIC: u8 = 0;
|
||||
|
||||
const WITHDRAW_VARIANT_INDEX: u8 = 0x08;
|
||||
|
||||
// transactionpayment(TransactionFeePaid)
|
||||
const TRANSACTION_FEE_PAID_VARIANT_INDEX: u8 = 0x00;
|
||||
|
||||
// system(ExtrinsicSuccess | ExtrinsicFailed)
|
||||
const EXTRINSIC_SUCCESS_VARIANT_INDEX: u8 = 0x00;
|
||||
const EXTRINSIC_FAILED_VARIANT_INDEX: u8 = 0x01;
|
||||
|
||||
const UNSIGNED_TRANSFER_ALLOW_DEATH_MIN_LEN: usize = 1 /* extrinsic version */
|
||||
+ 1 /* pallet index */
|
||||
+ 1 /* call index */
|
||||
@@ -89,6 +100,14 @@ mod ffi {
|
||||
) -> Vec<u8>;
|
||||
|
||||
fn parse_fee_info(input: &[u8], fee_bytes: &mut [u8; 16]) -> bool;
|
||||
|
||||
fn was_extrinsic_successful(
|
||||
events: &[u8],
|
||||
extrinsic_idx: u32,
|
||||
sender: &[u8; 32],
|
||||
chain_metadata: &CxxPolkadotChainMetadata,
|
||||
actual_fee: &mut [u8; 16],
|
||||
) -> bool;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -446,3 +465,160 @@ fn parse_fee_info(input: &[u8], fee_bytes: &mut [u8; 16]) -> bool {
|
||||
fn compact_scale_encode_u32(x: u32) -> Vec<u8> {
|
||||
Compact(x).encode()
|
||||
}
|
||||
|
||||
fn was_extrinsic_successful(
|
||||
events: &[u8],
|
||||
extrinsic_idx: u32,
|
||||
sender: &[u8; 32],
|
||||
chain_metadata: &CxxPolkadotChainMetadata,
|
||||
actual_fee: &mut [u8; 16],
|
||||
) -> bool {
|
||||
/*
|
||||
For a send transaction, a simplified event flow looks roughly like this:
|
||||
|
||||
┌─────────────────────────────────┐
|
||||
│ balances(Withdraw) │
|
||||
└─────────────────────────────────┘
|
||||
│ │
|
||||
[success] [error]
|
||||
│ │
|
||||
▼ │
|
||||
┌──────────────────────┐ │
|
||||
│ balances(Transfer) │ │
|
||||
└──────────────────────┘ │
|
||||
│ │
|
||||
└──────┬───────┘
|
||||
│
|
||||
▼
|
||||
┌───────────────────────────────────┐
|
||||
│ balances(Deposit), ... │
|
||||
└───────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────┐
|
||||
│ transactionpayment(TransactionFeePaid) │
|
||||
└──────────────────────────────────────────┘
|
||||
│
|
||||
┌────────┴──────────────────────┐
|
||||
│ │
|
||||
▼ ▼
|
||||
┌──────────────────────────┐ ┌─────────────────────────┐
|
||||
│ system(ExtrinsicSuccess) │ │ system(ExtrinsicFailed) │
|
||||
└──────────────────────────┘ └─────────────────────────┘
|
||||
*/
|
||||
|
||||
// But in general, it seems like the events flow can become quite complex:
|
||||
// https://polkadot.subscan.io/extrinsic/30123219-2
|
||||
// The thing to note is that the extrinsic always ends with the same two
|
||||
// events, the fee was paid and the system gave the extrinsic a final status.
|
||||
//
|
||||
// In Polkadot, an event is defined as: {phase, event, topics}
|
||||
// https://github.com/polkadot-js/api/blob/eb34741c871ca8d029a9706ae989ba8ce865db0f/packages/types-support/src/metadata/v15/polkadot-types.json#L519-L542
|
||||
//
|
||||
// Because the events are a massive binary blob that rely on quite a bit of
|
||||
// Polkadot runtime metadata to fully parse, we just probe for the two events
|
||||
// for our extrinsic that we care about: the transaction fee paid and the final
|
||||
// status. We can theoretically probe for everything such as who the fee was
|
||||
// paid out to but it isn't strictly required for our current needs.
|
||||
|
||||
// We first probe for the balances(Withdraw) event, so that we can use the
|
||||
// withdrawn fee as a sanity check when we probe for our TransactionFeePaid
|
||||
// event later on.
|
||||
let mut withdraw_needle = [0_u8; 39];
|
||||
withdraw_needle[0] = PHASE_APPLY_EXTRINSIC;
|
||||
withdraw_needle[1..5].copy_from_slice(&extrinsic_idx.to_le_bytes());
|
||||
withdraw_needle[5] = chain_metadata.balances_pallet_index;
|
||||
withdraw_needle[6] = WITHDRAW_VARIANT_INDEX;
|
||||
withdraw_needle[7..39].copy_from_slice(sender);
|
||||
|
||||
// Use `rfind` here because extrinsic blobs can be huge, and our events are
|
||||
// typically found at the end of the events blob.
|
||||
let mut events = events;
|
||||
let Some(needle_idx) = memchr::memmem::rfind(events, &withdraw_needle) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
events = &events[needle_idx + withdraw_needle.len()..];
|
||||
|
||||
let Ok(withdrawn_fee) = next_n_bytes(&mut events, 16) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let Ok(topics) = next_n_bytes(&mut events, 1) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
if topics[0] != 0 {
|
||||
return false;
|
||||
};
|
||||
|
||||
// Look for the remainining two events we need,
|
||||
// transactionpayment(TransactionFeePaid) and system(ExtrinsicSuccess |
|
||||
// ExtrinsicFailed)
|
||||
let mut transaction_fee_paid_needle = [0_u8; 39];
|
||||
transaction_fee_paid_needle[0] = PHASE_APPLY_EXTRINSIC;
|
||||
transaction_fee_paid_needle[1..5].copy_from_slice(&extrinsic_idx.to_le_bytes());
|
||||
transaction_fee_paid_needle[5] = chain_metadata.transaction_payment_pallet_index;
|
||||
transaction_fee_paid_needle[6] = TRANSACTION_FEE_PAID_VARIANT_INDEX;
|
||||
transaction_fee_paid_needle[7..39].copy_from_slice(sender);
|
||||
|
||||
// Use `find` here because we've located the start of our event sequence above.
|
||||
let Some(needle_idx) = memchr::memmem::find(events, &transaction_fee_paid_needle) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
events = &events[needle_idx + transaction_fee_paid_needle.len()..];
|
||||
let Ok(fee) = next_n_bytes(&mut events, 16) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
// If our fees don't match here, we can consider the events blob invalid.
|
||||
if withdrawn_fee != fee {
|
||||
return false;
|
||||
}
|
||||
|
||||
let Ok(_tip) = next_n_bytes(&mut events, 16) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let Ok(topics) = next_n_bytes(&mut events, 1) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
if topics[0] != 0 {
|
||||
return false;
|
||||
};
|
||||
|
||||
let Ok(phase) = next_n_bytes(&mut events, 1) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
if phase[0] != PHASE_APPLY_EXTRINSIC {
|
||||
return false;
|
||||
}
|
||||
|
||||
let Ok(idx) = next_n_bytes(&mut events, 4) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
if idx != &extrinsic_idx.to_le_bytes() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let Ok(call_index) = next_n_bytes(&mut events, 2) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
if call_index[0] != chain_metadata.system_pallet_index {
|
||||
return false;
|
||||
};
|
||||
|
||||
if call_index[1] != EXTRINSIC_SUCCESS_VARIANT_INDEX
|
||||
&& call_index[1] != EXTRINSIC_FAILED_VARIANT_INDEX
|
||||
{
|
||||
return false;
|
||||
};
|
||||
|
||||
actual_fee.copy_from_slice(fee);
|
||||
call_index[1] == EXTRINSIC_SUCCESS_VARIANT_INDEX
|
||||
}
|
||||
|
||||
@@ -821,4 +821,491 @@ TEST(PolkadotExtrinsics, MetadataSerde) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(PolkadotExtrinsics, EventsParsing) {
|
||||
// This event comes from:
|
||||
// https://polkadot.subscan.io/extrinsic/30267458-2
|
||||
// Note that because the entire events blob for a block is ~12 kB, we choose
|
||||
// to only include a subset for this test.
|
||||
|
||||
std::array<uint8_t, kPolkadotSubstrateAccountIdSize> sender = {};
|
||||
const char sender_hex[] =
|
||||
"bf0be0352ca5bc12a8ac6cf0006e220e5c55bb03126890ad37ce9753f9b3e3db";
|
||||
ASSERT_TRUE(base::HexStringToSpan(sender_hex, sender));
|
||||
|
||||
auto chain_metadata =
|
||||
PolkadotChainMetadata::FromChainName("Polkadot").value();
|
||||
|
||||
const uint32_t extrinsic_idx = 2;
|
||||
|
||||
const char events_hex[] =
|
||||
// balances(Withdraw)
|
||||
"0002000000"
|
||||
"0508"
|
||||
"bf0be0352ca5bc12a8ac6cf0006e220e5c55bb03126890ad37ce9753f9b3e3db"
|
||||
"5f139909000000000000000000000000"
|
||||
"00"
|
||||
// balances(Transfer)
|
||||
"0002000000"
|
||||
"0502"
|
||||
"bf0be0352ca5bc12a8ac6cf0006e220e5c55bb03126890ad37ce9753f9b3e3db"
|
||||
"5d70f7105a51be4a5afd2f10377d9bec9b8cdb971d6e8c436630f236a805926e"
|
||||
"a1d0724a020000000000000000000000"
|
||||
"00"
|
||||
// balances(Deposit)
|
||||
"0002000000"
|
||||
"0507"
|
||||
"6d6f646c70792f74727372790000000000000000000000000000000000000000"
|
||||
"18a9ad07000000000000000000000000"
|
||||
"00"
|
||||
// transactionpayment(TransactionFeePaid)
|
||||
"0002000000"
|
||||
"2000"
|
||||
"bf0be0352ca5bc12a8ac6cf0006e220e5c55bb03126890ad37ce9753f9b3e3db"
|
||||
"5f139909000000000000000000000000"
|
||||
"00000000000000000000000000000000"
|
||||
"00"
|
||||
// system(ExtrinsicSuccess)
|
||||
"0002000000"
|
||||
"0000"
|
||||
"a2e910976da80000"
|
||||
"00";
|
||||
|
||||
std::vector<uint8_t> events;
|
||||
ASSERT_TRUE(base::HexStringToBytes(events_hex, &events));
|
||||
|
||||
std::array<uint8_t, 16> actual_fee_bytes = {};
|
||||
|
||||
EXPECT_TRUE(was_extrinsic_successful(rust::Slice<const uint8_t>(events),
|
||||
extrinsic_idx, sender, *chain_metadata,
|
||||
actual_fee_bytes));
|
||||
|
||||
EXPECT_EQ(base::bit_cast<uint128_t>(actual_fee_bytes), uint128_t{161026911});
|
||||
EXPECT_EQ(base::HexEncodeLower(actual_fee_bytes),
|
||||
"5f139909000000000000000000000000");
|
||||
}
|
||||
|
||||
TEST(PolkadotExtrinsics, EventsParsing_WithAccountCreation) {
|
||||
// This event comes from:
|
||||
// https://polkadot.subscan.io/extrinsic/30123219-2
|
||||
|
||||
std::array<uint8_t, kPolkadotSubstrateAccountIdSize> sender = {};
|
||||
const char sender_hex[] =
|
||||
"2a27dd26f5f3fe4f48fc67cddb54a8cdb0f3c6e4b9c8cf751a59466771dc6144";
|
||||
ASSERT_TRUE(base::HexStringToSpan(sender_hex, sender));
|
||||
|
||||
auto chain_metadata =
|
||||
PolkadotChainMetadata::FromChainName("Polkadot").value();
|
||||
|
||||
uint32_t extrinsic_idx = 2;
|
||||
|
||||
const char events_hex[] =
|
||||
// balances(Withdraw)
|
||||
"0002000000"
|
||||
"0508"
|
||||
"2a27dd26f5f3fe4f48fc67cddb54a8cdb0f3c6e4b9c8cf751a59466771dc6144"
|
||||
"5f139909000000000000000000000000"
|
||||
"00"
|
||||
// system(NewAccount)
|
||||
"0002000000"
|
||||
"0003"
|
||||
"70617261550d0000000000000000000000000000000000000000000000000000"
|
||||
"00"
|
||||
// balances(Endowed)
|
||||
"0002000000"
|
||||
"0500"
|
||||
"70617261550d0000000000000000000000000000000000000000000000000000"
|
||||
"00d8bc7ced0000000000000000000000"
|
||||
"00"
|
||||
// balances(Transfer)
|
||||
"0002000000"
|
||||
"0502"
|
||||
"2a27dd26f5f3fe4f48fc67cddb54a8cdb0f3c6e4b9c8cf751a59466771dc6144"
|
||||
"70617261550d0000000000000000000000000000000000000000000000000000"
|
||||
"00d8bc7ced0000000000000000000000"
|
||||
"00"
|
||||
// balances(Deposit)
|
||||
"0002000000"
|
||||
"0507"
|
||||
"6d6f646c70792f74727372790000000000000000000000000000000000000000"
|
||||
"18a9ad07000000000000000000000000"
|
||||
"00"
|
||||
// balances(Deposit)
|
||||
"0002000000"
|
||||
"0507"
|
||||
"d8837440d77698a5ac63985587594e45d0029538f9b413495621913a68f64941"
|
||||
"476aeb01000000000000000000000000"
|
||||
"00"
|
||||
// transactionpayment(TransactionFeePaid)
|
||||
"0002000000"
|
||||
"2000"
|
||||
"2a27dd26f5f3fe4f48fc67cddb54a8cdb0f3c6e4b9c8cf751a59466771dc6144"
|
||||
"5f139909000000000000000000000000"
|
||||
"00000000000000000000000000000000"
|
||||
"00"
|
||||
// system(ExtrinsicSuccess)
|
||||
"0002000000"
|
||||
"0000"
|
||||
"a2e910976da80000"
|
||||
"00";
|
||||
|
||||
std::vector<uint8_t> events;
|
||||
ASSERT_TRUE(base::HexStringToBytes(events_hex, &events));
|
||||
|
||||
std::array<uint8_t, 16> actual_fee_bytes = {};
|
||||
|
||||
EXPECT_TRUE(was_extrinsic_successful(rust::Slice<const uint8_t>(events),
|
||||
extrinsic_idx, sender, *chain_metadata,
|
||||
actual_fee_bytes));
|
||||
|
||||
EXPECT_EQ(base::bit_cast<uint128_t>(actual_fee_bytes), uint128_t{161026911});
|
||||
EXPECT_EQ(base::HexEncodeLower(actual_fee_bytes),
|
||||
"5f139909000000000000000000000000");
|
||||
}
|
||||
|
||||
TEST(PolkadotExtrinsics, EventsParsing_FailedExtrinsic_ArithmeticUnderflow) {
|
||||
// This event comes from:
|
||||
// https://polkadot.subscan.io/extrinsic/29943577-2
|
||||
|
||||
std::array<uint8_t, kPolkadotSubstrateAccountIdSize> sender = {};
|
||||
const char sender_hex[] =
|
||||
"d44c4639d57190aed08f053cac6db1c85221253e7353d484dba9caa663d86a5f";
|
||||
ASSERT_TRUE(base::HexStringToSpan(sender_hex, sender));
|
||||
|
||||
auto chain_metadata =
|
||||
PolkadotChainMetadata::FromChainName("Polkadot").value();
|
||||
|
||||
uint32_t extrinsic_idx = 2;
|
||||
|
||||
const char events_hex[] =
|
||||
// balances(Withdraw)
|
||||
"0002000000"
|
||||
"0508"
|
||||
"d44c4639d57190aed08f053cac6db1c85221253e7353d484dba9caa663d86a5f"
|
||||
"9f5ee509000000000000000000000000"
|
||||
"00"
|
||||
// balances(Deposit)
|
||||
"0002000000"
|
||||
"0507"
|
||||
"6d6f646c70792f74727372790000000000000000000000000000000000000000"
|
||||
"18b2ea07000000000000000000000000"
|
||||
"00"
|
||||
// balances(Deposit)
|
||||
"0002000000"
|
||||
"0507"
|
||||
"e08785d4123f656862a5fd4b2286ae30ab1ebf17f60538961d3f91f02c73ee91"
|
||||
"87acfa01000000000000000000000000"
|
||||
"00"
|
||||
// transactionpayment(TransactionFeePaid)
|
||||
"0002000000"
|
||||
"2000"
|
||||
"d44c4639d57190aed08f053cac6db1c85221253e7353d484dba9caa663d86a5f"
|
||||
"9f5ee509000000000000000000000000"
|
||||
"00000000000000000000000000000000"
|
||||
"00"
|
||||
// system(ExtrinsicFailed)
|
||||
"0002000000"
|
||||
"0001"
|
||||
"0800a2e910976da80000"
|
||||
"00";
|
||||
|
||||
std::vector<uint8_t> events;
|
||||
ASSERT_TRUE(base::HexStringToBytes(events_hex, &events));
|
||||
|
||||
std::array<uint8_t, 16> actual_fee_bytes = {};
|
||||
|
||||
EXPECT_FALSE(was_extrinsic_successful(rust::Slice<const uint8_t>(events),
|
||||
extrinsic_idx, sender, *chain_metadata,
|
||||
actual_fee_bytes));
|
||||
|
||||
EXPECT_EQ(base::bit_cast<uint128_t>(actual_fee_bytes), uint128_t{166026911});
|
||||
EXPECT_EQ(base::HexEncodeLower(actual_fee_bytes),
|
||||
"9f5ee509000000000000000000000000");
|
||||
}
|
||||
|
||||
TEST(PolkadotExtrinsics, EventsParsing_FailedExtrinsic_BelowMinimum) {
|
||||
// This event comes from:
|
||||
// https://polkadot.subscan.io/extrinsic/29509101-2
|
||||
|
||||
std::array<uint8_t, kPolkadotSubstrateAccountIdSize> sender = {};
|
||||
const char sender_hex[] =
|
||||
"3c67dd0ea1126b09609ac341b4417251457f0fad467b8e1d3004209d4756ea2e";
|
||||
ASSERT_TRUE(base::HexStringToSpan(sender_hex, sender));
|
||||
|
||||
auto chain_metadata =
|
||||
PolkadotChainMetadata::FromChainName("Polkadot").value();
|
||||
|
||||
uint32_t extrinsic_idx = 2;
|
||||
|
||||
const char events_hex[] =
|
||||
// balances(Withdraw)
|
||||
"0002000000"
|
||||
"0508"
|
||||
"3c67dd0ea1126b09609ac341b4417251457f0fad467b8e1d3004209d4756ea2e"
|
||||
"5f139909000000000000000000000000"
|
||||
"00"
|
||||
// balances(Deposit)
|
||||
"0002000000"
|
||||
"0507"
|
||||
"6d6f646c70792f74727372790000000000000000000000000000000000000000"
|
||||
"18a9ad07000000000000000000000000"
|
||||
"00"
|
||||
// balances(Deposit)
|
||||
"0002000000"
|
||||
"0507"
|
||||
"c5e80accf4092ea6f8ed087544576dddcfdd51366b492868f73b0c9ca19c5f31"
|
||||
"476aeb01000000000000000000000000"
|
||||
"00"
|
||||
// transactionpayment(TransactionFeePaid)
|
||||
"0002000000"
|
||||
"2000"
|
||||
"3c67dd0ea1126b09609ac341b4417251457f0fad467b8e1d3004209d4756ea2e"
|
||||
"5f139909000000000000000000000000"
|
||||
"00000000000000000000000000000000"
|
||||
"00"
|
||||
// system(ExtrinsicFailed)
|
||||
"0002000000"
|
||||
"0001"
|
||||
"0702a2e910976da80000"
|
||||
"00";
|
||||
|
||||
std::vector<uint8_t> events;
|
||||
ASSERT_TRUE(base::HexStringToBytes(events_hex, &events));
|
||||
|
||||
std::array<uint8_t, 16> actual_fee_bytes = {};
|
||||
|
||||
EXPECT_FALSE(was_extrinsic_successful(rust::Slice<const uint8_t>(events),
|
||||
extrinsic_idx, sender, *chain_metadata,
|
||||
actual_fee_bytes));
|
||||
|
||||
EXPECT_EQ(base::bit_cast<uint128_t>(actual_fee_bytes), uint128_t{161026911});
|
||||
EXPECT_EQ(base::HexEncodeLower(actual_fee_bytes),
|
||||
"5f139909000000000000000000000000");
|
||||
}
|
||||
|
||||
TEST(PolkadotExtrinsics, EventsParsing_Error) {
|
||||
const char sender_hex[] =
|
||||
"bf0be0352ca5bc12a8ac6cf0006e220e5c55bb03126890ad37ce9753f9b3e3db";
|
||||
|
||||
std::array<uint8_t, kPolkadotSubstrateAccountIdSize> sender = {};
|
||||
|
||||
ASSERT_TRUE(base::HexStringToSpan(sender_hex, sender));
|
||||
|
||||
auto chain_metadata =
|
||||
PolkadotChainMetadata::FromChainName("Polkadot").value();
|
||||
|
||||
uint32_t extrinsic_idx = 2;
|
||||
|
||||
const std::string valid_events =
|
||||
// balances(Withdraw)
|
||||
"0002000000"
|
||||
"0508"
|
||||
"bf0be0352ca5bc12a8ac6cf0006e220e5c55bb03126890ad37ce9753f9b3e3db"
|
||||
"5f139909000000000000000000000000"
|
||||
"00"
|
||||
// balances(Transfer)
|
||||
"0002000000"
|
||||
"0502"
|
||||
"bf0be0352ca5bc12a8ac6cf0006e220e5c55bb03126890ad37ce9753f9b3e3db"
|
||||
"5d70f7105a51be4a5afd2f10377d9bec9b8cdb971d6e8c436630f236a805926e"
|
||||
"a1d0724a020000000000000000000000"
|
||||
"00"
|
||||
// balances(Deposit)
|
||||
"0002000000"
|
||||
"0507"
|
||||
"6d6f646c70792f74727372790000000000000000000000000000000000000000"
|
||||
"18a9ad07000000000000000000000000"
|
||||
"00"
|
||||
// transactionpayment(TransactionFeePaid)
|
||||
"0002000000"
|
||||
"2000"
|
||||
"bf0be0352ca5bc12a8ac6cf0006e220e5c55bb03126890ad37ce9753f9b3e3db"
|
||||
"5f139909000000000000000000000000"
|
||||
"00000000000000000000000000000000"
|
||||
"00"
|
||||
// system(ExtrinsicSuccess)
|
||||
"0002000000"
|
||||
"0000"
|
||||
"a2e910976da80000"
|
||||
"00";
|
||||
|
||||
std::vector<std::string> inputs;
|
||||
|
||||
// Incorrect balances(Withdraw).
|
||||
{
|
||||
std::string bad_withdraw_event_prefix = valid_events;
|
||||
auto n = bad_withdraw_event_prefix.find("00020000000508");
|
||||
bad_withdraw_event_prefix[n] = '1';
|
||||
inputs.push_back(std::move(bad_withdraw_event_prefix));
|
||||
}
|
||||
{
|
||||
std::string bad_withdraw_event_prefix = valid_events;
|
||||
auto n = bad_withdraw_event_prefix.find("00020000000508");
|
||||
bad_withdraw_event_prefix.erase(n + 5, 2);
|
||||
inputs.push_back(std::move(bad_withdraw_event_prefix));
|
||||
}
|
||||
|
||||
// Incorrect transactionpayment(TransactionFeePaid).
|
||||
{
|
||||
std::string bad_fee_paid_event_prefix = valid_events;
|
||||
auto n = bad_fee_paid_event_prefix.find("00020000002000");
|
||||
bad_fee_paid_event_prefix[n] = '1';
|
||||
inputs.push_back(std::move(bad_fee_paid_event_prefix));
|
||||
}
|
||||
{
|
||||
std::string bad_fee_paid_event_prefix = valid_events;
|
||||
auto n = bad_fee_paid_event_prefix.find("00020000002000");
|
||||
bad_fee_paid_event_prefix.erase(n + 5, 2);
|
||||
inputs.push_back(std::move(bad_fee_paid_event_prefix));
|
||||
}
|
||||
|
||||
// Incorrect system(ExtrinsicSuccess).
|
||||
{
|
||||
std::string bad_extrinsic_success_event_prefix = valid_events;
|
||||
auto n = bad_extrinsic_success_event_prefix.find("00020000000000");
|
||||
bad_extrinsic_success_event_prefix[n] = '1';
|
||||
inputs.push_back(std::move(bad_extrinsic_success_event_prefix));
|
||||
}
|
||||
{
|
||||
std::string bad_extrinsic_success_event_prefix = valid_events;
|
||||
auto n = bad_extrinsic_success_event_prefix.find("00020000000000");
|
||||
bad_extrinsic_success_event_prefix.erase(n + 5, 2);
|
||||
inputs.push_back(std::move(bad_extrinsic_success_event_prefix));
|
||||
}
|
||||
|
||||
// Incorrect sender in balances withdraw.
|
||||
{
|
||||
std::string bad_sender_transfer = valid_events;
|
||||
auto n = bad_sender_transfer.find(sender_hex);
|
||||
bad_sender_transfer[n] = '0';
|
||||
inputs.push_back(std::move(bad_sender_transfer));
|
||||
}
|
||||
|
||||
// Incorrect sender in transaction fee paid.
|
||||
{
|
||||
std::string bad_sender_transfer = valid_events;
|
||||
auto n = bad_sender_transfer.find(sender_hex);
|
||||
n = bad_sender_transfer.find(sender_hex, n + 64);
|
||||
n = bad_sender_transfer.find(sender_hex, n + 64);
|
||||
bad_sender_transfer[n] = '0';
|
||||
inputs.push_back(std::move(bad_sender_transfer));
|
||||
}
|
||||
|
||||
// Incorrect topics for withdrawal.
|
||||
{
|
||||
std::string needle =
|
||||
"5f139909000000000000000000000000"
|
||||
"00"
|
||||
"0002000000";
|
||||
|
||||
std::string bad_fee_paid_topics = valid_events;
|
||||
auto n = bad_fee_paid_topics.find(needle);
|
||||
bad_fee_paid_topics[n + needle.size() -
|
||||
std::string_view("0002000000").size() - 2] = '1';
|
||||
|
||||
inputs.push_back(std::move(bad_fee_paid_topics));
|
||||
}
|
||||
|
||||
// Incorrect topics for fee paid.
|
||||
{
|
||||
std::string needle =
|
||||
"5f139909000000000000000000000000"
|
||||
"00000000000000000000000000000000"
|
||||
"00";
|
||||
|
||||
std::string bad_fee_paid_topics = valid_events;
|
||||
auto n = bad_fee_paid_topics.find(needle);
|
||||
bad_fee_paid_topics[n + needle.size() - 2] = '1';
|
||||
|
||||
inputs.push_back(std::move(bad_fee_paid_topics));
|
||||
}
|
||||
|
||||
// Extrinsic indexes don't match.
|
||||
{
|
||||
std::string invalid_event =
|
||||
// balances(Withdraw)
|
||||
"0003000000"
|
||||
"0508"
|
||||
"bf0be0352ca5bc12a8ac6cf0006e220e5c55bb03126890ad37ce9753f9b3e3db"
|
||||
"5f139909000000000000000000000000"
|
||||
"00"
|
||||
// balances(Transfer)
|
||||
"0003000000"
|
||||
"0502"
|
||||
"bf0be0352ca5bc12a8ac6cf0006e220e5c55bb03126890ad37ce9753f9b3e3db"
|
||||
"5d70f7105a51be4a5afd2f10377d9bec9b8cdb971d6e8c436630f236a805926e"
|
||||
"a1d0724a020000000000000000000000"
|
||||
"00"
|
||||
// balances(Deposit)
|
||||
"0003000000"
|
||||
"0507"
|
||||
"6d6f646c70792f74727372790000000000000000000000000000000000000000"
|
||||
"18a9ad07000000000000000000000000"
|
||||
"00"
|
||||
// transactionpayment(TransactionFeePaid)
|
||||
"0003000000"
|
||||
"2000"
|
||||
"bf0be0352ca5bc12a8ac6cf0006e220e5c55bb03126890ad37ce9753f9b3e3db"
|
||||
"5f139909000000000000000000000000"
|
||||
"00000000000000000000000000000000"
|
||||
"00"
|
||||
// system(ExtrinsicSuccess)
|
||||
"0003000000"
|
||||
"0000"
|
||||
"a2e910976da80000"
|
||||
"00";
|
||||
|
||||
inputs.push_back(std::move(invalid_event));
|
||||
}
|
||||
|
||||
// Empty string.
|
||||
{
|
||||
inputs.push_back("");
|
||||
}
|
||||
|
||||
// Truncated TransactionFeePaid.
|
||||
{
|
||||
std::string needle =
|
||||
"5f139909000000000000000000000000"
|
||||
"00000000000000000000000000000000"
|
||||
"00";
|
||||
|
||||
std::string truncated = valid_events;
|
||||
auto n = truncated.find(needle);
|
||||
truncated.erase(n);
|
||||
|
||||
inputs.push_back(std::move(truncated));
|
||||
}
|
||||
|
||||
// Withdrawal doesn't match transaction fee paid.
|
||||
{
|
||||
std::string needle =
|
||||
"0508"
|
||||
"bf0be0352ca5bc12a8ac6cf0006e220e5c55bb03126890ad37ce9753f9b3e3db"
|
||||
"5f139909000000000000000000000000";
|
||||
|
||||
std::string mismatched_withdrawal = valid_events;
|
||||
auto n = mismatched_withdrawal.find(needle);
|
||||
mismatched_withdrawal[n + 4 + 64 + 1] = '6';
|
||||
|
||||
inputs.push_back(std::move(mismatched_withdrawal));
|
||||
}
|
||||
|
||||
ASSERT_FALSE(inputs.empty());
|
||||
for (const auto& input : inputs) {
|
||||
std::vector<uint8_t> events;
|
||||
if (!input.empty()) {
|
||||
ASSERT_TRUE(base::HexStringToBytes(input, &events));
|
||||
}
|
||||
|
||||
std::array<uint8_t, 16> actual_fee_bytes = {};
|
||||
|
||||
EXPECT_FALSE(was_extrinsic_successful(rust::Slice<const uint8_t>(events),
|
||||
extrinsic_idx, sender,
|
||||
*chain_metadata, actual_fee_bytes))
|
||||
<< input;
|
||||
|
||||
EXPECT_EQ(base::bit_cast<uint128_t>(actual_fee_bytes), uint128_t{0});
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace brave_wallet
|
||||
|
||||
@@ -15,6 +15,7 @@ schnorrkel = "0.11.4"
|
||||
ciborium = "0.2.2"
|
||||
parity-scale-codec = "3.7.5"
|
||||
crc32fast = "1.5.0"
|
||||
memchr = "2.7.6"
|
||||
|
||||
[lib]
|
||||
name = "brave_wallet"
|
||||
|
||||
+1
@@ -268,6 +268,7 @@ dependencies = [
|
||||
"crc32fast",
|
||||
"curve25519-dalek",
|
||||
"cxx",
|
||||
"memchr",
|
||||
"parity-scale-codec",
|
||||
"schnorrkel",
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user