Skip to content

Commit

Permalink
feat: nested polkadot fetch (#4006)
Browse files Browse the repository at this point in the history
  • Loading branch information
dandanlen authored Oct 24, 2023
1 parent 9cb4669 commit 35d8509
Show file tree
Hide file tree
Showing 2 changed files with 166 additions and 48 deletions.
87 changes: 77 additions & 10 deletions state-chain/chains/src/dot/api/batch_fetch_and_transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use crate::{
},
FetchAssetParams, TransferAssetParams,
};
use cf_primitives::ChannelId;
use cf_utilities::SliceToArray;
use sp_std::{boxed::Box, vec::Vec};

pub fn extrinsic_builder(
Expand All @@ -24,16 +26,7 @@ pub fn extrinsic_builder(
fetch_params
.into_iter()
.map(|fetch_param| {
PolkadotRuntimeCall::Utility(UtilityCall::as_derivative {
// TODO: refer to issue #2354
index: fetch_param.deposit_fetch_id as u16,
call: Box::new(PolkadotRuntimeCall::Balances(
BalancesCall::transfer_all {
dest: PolkadotAccountIdLookup::from(vault_account),
keep_alive: false,
},
)),
})
utility_fetch(fetch_param.deposit_fetch_id, vault_account)
})
.collect::<Vec<PolkadotRuntimeCall>>(),
transfer_params
Expand All @@ -52,6 +45,25 @@ pub fn extrinsic_builder(
)
}

fn utility_fetch(channel_id: ChannelId, vault_account: PolkadotAccountId) -> PolkadotRuntimeCall {
let layers = channel_id
.to_be_bytes()
.chunks(2)
.map(|chunk| u16::from_be_bytes(chunk.as_array::<2>()))
.skip_while(|layer| *layer == 0u16)
.collect::<Vec<u16>>();

layers.into_iter().fold(
PolkadotRuntimeCall::Balances(BalancesCall::transfer_all {
dest: PolkadotAccountIdLookup::from(vault_account),
keep_alive: false,
}),
|call, index| {
PolkadotRuntimeCall::Utility(UtilityCall::as_derivative { index, call: Box::new(call) })
},
)
}

#[cfg(test)]
mod test_batch_fetch {

Expand Down Expand Up @@ -108,4 +120,59 @@ mod test_batch_fetch {
builder.insert_signature(keypair_proxy.public_key(), keypair_proxy.sign(&payload));
assert!(builder.is_signed());
}

#[test]
fn nested_fetch() {
let channel_id = 0x0004_0003_0002_0001;
let vault_account = PolkadotAccountId::from_aliased([1u8; 32]);
let call = utility_fetch(channel_id, vault_account);

assert_eq!(
call,
PolkadotRuntimeCall::Utility(UtilityCall::as_derivative {
index: 0x0001,
call: Box::new(PolkadotRuntimeCall::Utility(UtilityCall::as_derivative {
index: 0x0002,
call: Box::new(PolkadotRuntimeCall::Utility(UtilityCall::as_derivative {
index: 0x0003,
call: Box::new(PolkadotRuntimeCall::Utility(UtilityCall::as_derivative {
index: 0x0004,
call: Box::new(PolkadotRuntimeCall::Balances(
BalancesCall::transfer_all {
dest: PolkadotAccountIdLookup::from(vault_account),
keep_alive: false,
}
)),
})),
})),
})),
})
);

let channel_id = 1;
let vault_account = PolkadotAccountId::from_aliased([1u8; 32]);
let call = utility_fetch(channel_id, vault_account);

assert_eq!(
call,
PolkadotRuntimeCall::Utility(UtilityCall::as_derivative {
index: 1,
call: Box::new(PolkadotRuntimeCall::Balances(BalancesCall::transfer_all {
dest: PolkadotAccountIdLookup::from(vault_account),
keep_alive: false,
})),
})
);
}

#[test]
fn fetch_equivalence() {
let channel_id_1 = 0x0000_0000_0000_0001;
let channel_id_2 = 0x0000_0000_0001_0000;
let vault_account = PolkadotAccountId::from_aliased([1u8; 32]);
let call_1 = utility_fetch(channel_id_1, vault_account);
let call_2 = utility_fetch(channel_id_2, vault_account);

assert_ne!(call_1, call_2);
}
}
127 changes: 89 additions & 38 deletions state-chain/runtime/src/chainflip/address_derivation/dot.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::Vec;
use cf_chains::{address::AddressDerivationApi, dot::PolkadotAccountId, Chain, Polkadot};
use cf_primitives::ChannelId;
use cf_utilities::SliceToArray;
use frame_support::sp_runtime::{
traits::{BlakeTwo256, Hash},
DispatchError,
Expand All @@ -23,25 +24,30 @@ impl AddressDerivationApi<Polkadot> for AddressDerivation {
let master_account = Environment::polkadot_vault_account()
.ok_or(DispatchError::Other("Vault Account does not exist."))?;

// Because we re-use addresses, we don't expect to hit this case in the wild.
if channel_id > u16::MAX.into() {
return Err(DispatchError::Other(
"Channel ID too large. Polkadot can only support up to u16::MAX addresses",
))
}
let mut layers = channel_id
.to_be_bytes()
.chunks(2)
.map(|chunk| u16::from_be_bytes(chunk.as_array::<2>()))
.skip_while(|layer| *layer == 0u16)
.collect::<Vec<u16>>();

let mut payload = Vec::with_capacity(PAYLOAD_LENGTH);
// Fill the first slots with the derivation prefix.
payload.extend(PREFIX);
// Then add the 32-byte public key.
payload.extend(master_account.aliased_ref());
// Finally, add the index to the end of the payload.
payload.extend(&(<u16>::try_from(channel_id).unwrap()).to_le_bytes());
layers.reverse();

// Hash the whole thing
let payload_hash = BlakeTwo256::hash(&payload);
let payload_hash =
layers.into_iter().fold(*master_account.aliased_ref(), |sub_account, salt| {
let mut payload = Vec::with_capacity(PAYLOAD_LENGTH);
// Fill the first slots with the derivation prefix.
payload.extend(PREFIX);
// Then add the 32-byte public key.
payload.extend(sub_account);
// Finally, add the index to the end of the payload.
payload.extend(&salt.to_le_bytes());

Ok(PolkadotAccountId::from_aliased(*payload_hash.as_fixed_bytes()))
// Hash the whole thing
BlakeTwo256::hash(&payload).to_fixed_bytes()
});

Ok(PolkadotAccountId::from_aliased(payload_hash))
}

fn generate_address_and_state(
Expand All @@ -58,34 +64,79 @@ impl AddressDerivationApi<Polkadot> for AddressDerivation {
}
}

#[test]
fn test_dot_derive() {
#[cfg(test)]
mod test {
use super::*;
use crate::Runtime;
use cf_primitives::chains::assets::dot;
use frame_support::sp_runtime::app_crypto::Ss58Codec;
use pallet_cf_environment::PolkadotVaultAccountId;

frame_support::sp_io::TestExternalities::new_empty().execute_with(|| {
let (account_id, address_format) = sp_runtime::AccountId32::from_ss58check_with_version(
"15uPkKV7SsNXxw5VCu3LgnuaR5uSZ4QMyzxnLfDFE9J5nni9",
)
.unwrap();
PolkadotVaultAccountId::<Runtime>::put(PolkadotAccountId::from_aliased(
*account_id.as_ref(),
));
#[test]
fn single_layer() {
frame_support::sp_io::TestExternalities::new_empty().execute_with(|| {
let (account_id, address_format) =
sp_runtime::AccountId32::from_ss58check_with_version(
"15uPkKV7SsNXxw5VCu3LgnuaR5uSZ4QMyzxnLfDFE9J5nni9",
)
.unwrap();
PolkadotVaultAccountId::<Runtime>::put(PolkadotAccountId::from_aliased(
*account_id.as_ref(),
));

assert_eq!(
"12AeXofJkQErqQuiJmJapqwS4KiAZXBhAYoj9HZ2sYo36mRg",
sp_runtime::AccountId32::new(
*<AddressDerivation as AddressDerivationApi<Polkadot>>::generate_address(
dot::Asset::Dot,
6259
assert_eq!(
"12AeXofJkQErqQuiJmJapqwS4KiAZXBhAYoj9HZ2sYo36mRg",
sp_runtime::AccountId32::new(
*<AddressDerivation as AddressDerivationApi<Polkadot>>::generate_address(
dot::Asset::Dot,
6259
)
.unwrap()
.aliased_ref()
)
.unwrap()
.aliased_ref()
.to_ss58check_with_version(address_format),
);
});
}

#[test]
fn four_layers() {
frame_support::sp_io::TestExternalities::new_empty().execute_with(|| {
let (alice, address_format) = sp_runtime::AccountId32::from_ss58check_with_version(
"15oF4uVJwmo4TdGW7VfQxNLavjCXviqxT9S1MgbjMNHr6Sp5",
)
.to_ss58check_with_version(address_format),
);
println!("Derivation worked for DOT! 🚀");
});
.unwrap();
PolkadotVaultAccountId::<Runtime>::put(PolkadotAccountId::from_aliased(
*alice.as_ref(),
));

assert_eq!(
// The following account was generated using nested utility.asDerivative calls in
// PolkaJS. The wrapped call was system.remarkWithEvent, which emits the generated
// address in its event.
//
// The call was dispatched from the Alice account.
//
// To see the call go to
// extrinsics/decode/0x1a0101001a0102001a0103001a01040000071448414c4c4f
// on any polkaJS instance connected to a polkadot node.
//
// Call details:
// 1a01 is the utility.asDerivative call index.
// 0007 is the system.remarkWithEvent call index.
// Encoded call: 0x1a01 0100 1a01 0200 1a01 0300 1a01 0400 0007 1448414c4c4f
// ---- -^-- ---- -^-- ---- -^-- ---- -^-- b"HALLO"
"1422Jc2BYRh5ENjxWJchoHPSC2Rd4jFs8PDWHqBJue4yskEt",
sp_runtime::AccountId32::new(
*<AddressDerivation as AddressDerivationApi<Polkadot>>::generate_address(
dot::Asset::Dot,
0x0004_0003_0002_0001
)
.unwrap()
.aliased_ref()
)
.to_ss58check_with_version(address_format),
);
});
}
}

0 comments on commit 35d8509

Please sign in to comment.