use desub_current::{
decoder::{self, SignedExtensionWithAdditional},
value, Metadata, Value, ValueDef,
};
static V14_METADATA_POLKADOT_SCALE: &[u8] = include_bytes!("data/v14_metadata_polkadot.scale");
fn metadata() -> Metadata {
Metadata::from_bytes(V14_METADATA_POLKADOT_SCALE).expect("valid metadata")
}
fn to_bytes(hex_str: &str) -> Vec<u8> {
let hex_str = hex_str.strip_prefix("0x").expect("0x should prefix hex encoded bytes");
hex::decode(hex_str).expect("valid bytes from hex")
}
fn empty_value() -> Value<()> {
Value::unnamed_composite(vec![])
}
fn singleton_value(x: Value<()>) -> Value<()> {
Value::unnamed_composite(vec![x])
}
fn hash_value(xs: Vec<u8>) -> Value<()> {
singleton_value(Value::unnamed_composite(xs.iter().map(|x| Value::u8(*x)).collect()))
}
fn assert_args_equal<T: Clone>(args: &[Value<T>], expected: Vec<Value<()>>) {
let args: Vec<_> = args.into_iter().map(|v| v.clone().without_context()).collect();
assert_eq!(&args, &expected);
}
#[test]
fn balance_transfer_signed() {
let meta = metadata();
let ext_bytes = &mut &*to_bytes("0x31028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d016ada9b477ef454972200e098f1186d4a2aeee776f1f6a68609797f5ba052906ad2427bdca865442158d118e2dfc82226077e4dfdff975d005685bab66eefa38a150200000500001cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07ce5c0");
let ext = decoder::decode_extrinsic(&meta, ext_bytes).expect("can decode extrinsic");
assert!(ext_bytes.is_empty(), "No more bytes expected");
assert_eq!(ext.call_data.pallet_name, "Balances");
assert_eq!(&*ext.call_data.ty.name(), "transfer");
assert_eq!(ext.call_data.arguments.len(), 2);
assert_eq!(ext.call_data.arguments[1].clone().without_context(), Value::u128(12345));
}
#[test]
fn balance_transfer_all_signed() {
let meta = metadata();
let ext_bytes = &mut &*to_bytes("0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01f0431ffe387134b4f84d92d3c3f1ac18c0f42237ad7dbd455bb0cf8a18efb1760528f052b2219ad1601d9a4719e1a446cf307bf6d7e9c56175bfe6e7bf8cbe81450304000504001cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c00");
let ext = decoder::decode_extrinsic(&meta, ext_bytes).expect("can decode extrinsic");
assert!(ext_bytes.is_empty(), "No more bytes expected");
assert_eq!(ext.call_data.pallet_name, "Balances");
assert_eq!(&*ext.call_data.ty.name(), "transfer_all");
assert_eq!(ext.call_data.arguments.len(), 2);
assert_eq!(ext.call_data.arguments[1].clone().without_context(), Value::bool(false));
}
#[test]
fn auctions_bid_unsigned() {
let meta = metadata();
let ext_bytes = &mut &*to_bytes("0x04480104080c1014");
let ext = decoder::decode_unwrapped_extrinsic(&meta, ext_bytes).expect("can decode extrinsic");
assert!(ext_bytes.is_empty(), "No more bytes expected");
assert_eq!(ext.call_data.pallet_name, "Auctions");
assert_eq!(&*ext.call_data.ty.name(), "bid");
assert_eq!(ext.call_data.arguments.len(), 5);
assert_args_equal(
&ext.call_data.arguments,
vec![singleton_value(Value::u32(1)), Value::u32(2), Value::u32(3), Value::u32(4), Value::u128(5)],
);
}
#[test]
fn auctions_bid_unsigned_excess_bytes_allowed() {
let meta = metadata();
let mut ext_bytes = to_bytes("0x04480104080c1014");
ext_bytes.extend(b"extra bytes!");
let ext_bytes_cursor = &mut &*ext_bytes;
decoder::decode_unwrapped_extrinsic(&meta, ext_bytes_cursor).expect("can decode extrinsic");
assert_eq!(ext_bytes_cursor, b"extra bytes!");
}
#[test]
fn system_fill_block_unsigned() {
let meta = metadata();
let ext_bytes = &mut &*to_bytes("0x040000d2040000");
let ext = decoder::decode_unwrapped_extrinsic(&meta, ext_bytes).expect("can decode extrinsic");
assert!(ext_bytes.is_empty(), "No more bytes expected");
assert_eq!(ext.call_data.pallet_name, "System");
assert_eq!(&*ext.call_data.ty.name(), "fill_block");
assert_eq!(ext.call_data.arguments.len(), 1);
assert_args_equal(&ext.call_data.arguments, vec![singleton_value(Value::u32(1234))]);
}
#[test]
fn technical_committee_execute_unsigned() {
let meta = metadata();
let ext_bytes =
&mut &*to_bytes("0x0410010500001cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07ce5c0d107");
let ext = decoder::decode_unwrapped_extrinsic(&meta, ext_bytes).expect("can decode extrinsic");
assert!(ext_bytes.is_empty(), "No more bytes expected");
assert_eq!(ext.call_data.pallet_name, "TechnicalCommittee");
assert_eq!(&*ext.call_data.ty.name(), "execute");
assert_eq!(ext.call_data.arguments.len(), 2);
assert!(matches!(&ext.call_data.arguments[0],
Value { value: ValueDef::Variant(value::Variant {
name,
values: value::Composite::Unnamed(args)
}), .. }
if &*name == "Balances"
&& matches!(&args[0], Value { value: ValueDef::Variant(value::Variant { name, ..}), .. } if &*name == "transfer")
));
assert_eq!(ext.call_data.arguments[1].clone().without_context(), Value::u32(500));
}
#[test]
fn tips_report_awesome_unsigned() {
let meta = metadata();
let ext_bytes = &mut &*to_bytes("0x042300485468697320706572736f6e20726f636b73211cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c");
let ext = decoder::decode_unwrapped_extrinsic(&meta, ext_bytes).expect("can decode extrinsic");
assert!(ext_bytes.is_empty(), "No more bytes expected");
assert_eq!(ext.call_data.pallet_name, "Tips");
assert_eq!(&*ext.call_data.ty.name(), "report_awesome");
assert_eq!(ext.call_data.arguments.len(), 2);
assert_eq!(
ext.call_data.arguments[0].clone().without_context(),
Value::unnamed_composite("This person rocks!".bytes().map(Value::u8).collect())
);
}
#[test]
fn vesting_force_vested_transfer_unsigned() {
let meta = metadata();
let ext_bytes = &mut &*to_bytes("0x04190300d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48010000000000000000000000000000000200000000000000000000000000000003000000");
let ext = decoder::decode_unwrapped_extrinsic(&meta, ext_bytes).expect("can decode extrinsic");
assert!(ext_bytes.is_empty(), "No more bytes expected");
assert_eq!(ext.call_data.pallet_name, "Vesting");
assert_eq!(&*ext.call_data.ty.name(), "force_vested_transfer");
assert_eq!(ext.call_data.arguments.len(), 3);
assert_eq!(
ext.call_data.arguments[2].clone().without_context(),
Value::named_composite(vec![
("locked".into(), Value::u128(1)),
("per_block".into(), Value::u128(2)),
("starting_block".into(), Value::u32(3)),
])
);
}
#[test]
fn can_decode_multiple_extrinsics_with_extra_bytes() {
let meta = metadata();
let extrinsics_hex = "0x0C2004480104080c10142004480104080c10142004480104080c1014";
let mut extrinsics_bytes = hex::decode(extrinsics_hex.strip_prefix("0x").unwrap()).unwrap();
extrinsics_bytes.extend(b"extra bytes!");
let extrinsics_cursor = &mut &*extrinsics_bytes;
let extrinsics = decoder::decode_extrinsics(&meta, extrinsics_cursor).unwrap();
assert_eq!(extrinsics_cursor, b"extra bytes!");
assert_eq!(extrinsics.len(), 3);
}
#[test]
fn can_decode_signer_payload() {
let meta = metadata();
let signer_payload = &mut &*to_bytes("0x0706b9340000962300000800000091b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c31c81d421f68281950ad2901291603b5e49fc5c872f129e75433f4b55f07ca072");
let r = decoder::decode_signer_payload(&meta, signer_payload).expect("can decode signer payload");
assert_eq!(signer_payload.len(), 0);
assert_eq!(r.call_data.pallet_name, "Staking");
assert_eq!(&*r.call_data.ty.name(), "chill");
assert_eq!(r.call_data.arguments, vec![]);
let expected = vec![
("CheckSpecVersion", empty_value(), Value::u32(9110)),
("CheckTxVersion", empty_value(), Value::u32(8)),
(
"CheckGenesis",
empty_value(),
hash_value(to_bytes("0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3")),
),
(
"CheckMortality",
singleton_value(Value::variant("Mortal185".to_string(), value::Composite::Unnamed(vec![Value::u8(52)]))),
hash_value(to_bytes("0x1c81d421f68281950ad2901291603b5e49fc5c872f129e75433f4b55f07ca072")),
),
("CheckNonce", singleton_value(Value::u32(0)), empty_value()),
("CheckWeight", empty_value(), empty_value()),
("ChargeTransactionPayment", singleton_value(Value::u128(0)), empty_value()),
("PrevalidateAttests", empty_value(), empty_value()),
];
for (actual, expected) in r.extensions.into_iter().zip(expected) {
let (name, SignedExtensionWithAdditional { extension, additional }) = actual;
let (expected_name, expected_extension, expected_additional) = expected;
assert_eq!(&*name, expected_name);
assert_eq!(extension.without_context(), expected_extension);
assert_eq!(additional.without_context(), expected_additional);
}
}