Skip to content

Commit

Permalink
feat: optimistic polkadot rotation (#4182)
Browse files Browse the repository at this point in the history
  • Loading branch information
dandanlen authored Nov 2, 2023
1 parent 3fafc45 commit b080527
Show file tree
Hide file tree
Showing 12 changed files with 105 additions and 138 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,7 @@ where
// Deposit witnessing
.dot_deposits(witness_call.clone())
// Proxy added witnessing
.then({
let witness_call = witness_call.clone();
move |epoch, header| proxy_added_witnessing(epoch, header, witness_call.clone())
})
.then(proxy_added_witnessing)
// Broadcast success
.egress_items(scope, state_chain_stream.clone(), state_chain_client.clone())
.await
Expand Down
45 changes: 8 additions & 37 deletions engine/src/witness/dot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use cf_chains::dot::{
PolkadotAccountId, PolkadotBalance, PolkadotExtrinsicIndex, PolkadotHash, PolkadotSignature,
PolkadotUncheckedExtrinsic,
};
use cf_primitives::{EpochIndex, PolkadotBlockNumber, TxId};
use cf_primitives::{EpochIndex, PolkadotBlockNumber};
use futures_core::Future;
use state_chain_runtime::PolkadotInstance;
use subxt::{
Expand Down Expand Up @@ -92,27 +92,12 @@ pub fn filter_map_events(
}
}

pub async fn proxy_added_witnessing<ProcessCall, ProcessingFut>(
pub async fn proxy_added_witnessing(
epoch: Vault<cf_chains::Polkadot, PolkadotAccountId, ()>,
header: Header<PolkadotBlockNumber, PolkadotHash, Vec<(Phase, EventWrapper)>>,
process_call: ProcessCall,
) -> (Vec<(Phase, EventWrapper)>, BTreeSet<u32>)
where
ProcessCall: Fn(state_chain_runtime::RuntimeCall, EpochIndex) -> ProcessingFut
+ Send
+ Sync
+ Clone
+ 'static,
ProcessingFut: Future<Output = ()> + Send + 'static,
{
) -> (Vec<(Phase, EventWrapper)>, BTreeSet<u32>) {
let events = header.data;

let (vault_key_rotated_calls, proxy_added_broadcasts) =
proxy_addeds(header.index, &events, &epoch.info.1);

for call in vault_key_rotated_calls {
process_call(call, epoch.index).await;
}
let proxy_added_broadcasts = proxy_addeds(header.index, &events, &epoch.info.1);

(events, proxy_added_broadcasts)
}
Expand Down Expand Up @@ -262,10 +247,7 @@ where
// Deposit witnessing
.dot_deposits(process_call.clone())
// Proxy added witnessing
.then({
let process_call = process_call.clone();
move |epoch, header| proxy_added_witnessing(epoch, header, process_call.clone())
})
.then(proxy_added_witnessing)
// Broadcast success
.egress_items(scope, state_chain_stream.clone(), state_chain_client.clone())
.await
Expand Down Expand Up @@ -314,8 +296,7 @@ fn proxy_addeds(
block_number: PolkadotBlockNumber,
events: &Vec<(Phase, EventWrapper)>,
our_vault: &PolkadotAccountId,
) -> (Vec<state_chain_runtime::RuntimeCall>, BTreeSet<PolkadotExtrinsicIndex>) {
let mut vault_key_rotated_calls = vec![];
) -> BTreeSet<PolkadotExtrinsicIndex> {
let mut extrinsic_indices = BTreeSet::new();
for (phase, wrapped_event) in events {
if let Phase::ApplyExtrinsic(extrinsic_index) = *phase {
Expand All @@ -326,19 +307,11 @@ fn proxy_addeds(

tracing::info!("Witnessing ProxyAdded. new delegatee: {delegatee:?} at block number {block_number} and extrinsic_index; {extrinsic_index}");

vault_key_rotated_calls.push(
pallet_cf_vaults::Call::<_, PolkadotInstance>::vault_key_rotated {
block_number,
tx_id: TxId { block_number, extrinsic_index },
}
.into(),
);

extrinsic_indices.insert(extrinsic_index);
}
}
}
(vault_key_rotated_calls, extrinsic_indices)
extrinsic_indices
}

#[cfg(test)]
Expand Down Expand Up @@ -383,10 +356,8 @@ pub mod test {
(3u32, mock_tx_fee_paid(20000)),
]);

let (vault_key_rotated_calls, extrinsic_indices) =
proxy_addeds(20, &block_event_details, &our_vault);
let extrinsic_indices = proxy_addeds(20, &block_event_details, &our_vault);

assert_eq!(vault_key_rotated_calls.len(), 1);
assert_eq!(extrinsic_indices.len(), 1);
assert!(extrinsic_indices.contains(&our_proxy_added_index));
}
Expand Down
53 changes: 19 additions & 34 deletions state-chain/cf-integration-tests/src/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,43 +300,28 @@ impl Engine {
}),
RuntimeOrigin::signed(self.node_id.clone())
);
}

RuntimeEvent::PolkadotVault(pallet_cf_vaults::Event::<_, PolkadotInstance>::AwaitingGovernanceActivation { .. }) => {
queue_dispatch_extrinsic(
RuntimeCall::Witnesser(
pallet_cf_witnesser::Call::witness_at_epoch {
call: Box::new(pallet_cf_vaults::Call::<_, BitcoinInstance>::vault_key_rotated {
block_number: 100,
tx_id: [2u8; 32],
}.into()),
epoch_index: Validator::epoch_index()
}),
RuntimeOrigin::signed(self.node_id.clone())
RuntimeCall::Environment(pallet_cf_environment::Call::witness_polkadot_vault_creation {
dot_pure_proxy_vault_key: Default::default(),
tx_id: TxId {
block_number: 1,
extrinsic_index: 0,
},
}),
pallet_cf_governance::RawOrigin::GovernanceApproval.into()
);
}
RuntimeEvent::BitcoinVault(pallet_cf_vaults::Event::<_, BitcoinInstance>::AwaitingGovernanceActivation { new_public_key }) => {
queue_dispatch_extrinsic(
RuntimeCall::Environment(pallet_cf_environment::Call::witness_current_bitcoin_block_number_for_key {
block_number: 0,
new_public_key: *new_public_key,
}),
pallet_cf_governance::RawOrigin::GovernanceApproval.into()
);

if Validator::epoch_index() == GENESIS_EPOCH {
queue_dispatch_extrinsic(
RuntimeCall::Environment(pallet_cf_environment::Call::witness_polkadot_vault_creation {
dot_pure_proxy_vault_key: Default::default(),
tx_id: TxId {
block_number: 1,
extrinsic_index: 0,
},
}
), pallet_cf_governance::RawOrigin::GovernanceApproval.into());
} else {
queue_dispatch_extrinsic(RuntimeCall::Witnesser(
pallet_cf_witnesser::Call::witness_at_epoch {
call: Box::new(pallet_cf_vaults::Call::<_, PolkadotInstance>::vault_key_rotated {
block_number: 100,
tx_id: TxId {
block_number: 2,
extrinsic_index: 1,
},
}.into()),
epoch_index: Validator::epoch_index()
}
), RuntimeOrigin::signed(self.node_id.clone()));
}
}
};
}
Expand Down
43 changes: 29 additions & 14 deletions state-chain/chains/src/dot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,17 @@ impl ChainCrypto for PolkadotCrypto {
fn agg_key_to_payload(agg_key: Self::AggKey, _for_handover: bool) -> Self::Payload {
EncodedPolkadotPayload(Blake2_256::hash(&agg_key.aliased_ref()[..]).to_vec())
}

/// Once authored, polkadot extrinsic must be signed by the key whose nonce was incorporated
/// into the extrinsic.
fn sign_with_specific_key() -> bool {
true
}

/// We sign with a specific key, so we can optimistically activate the next one.
fn optimistic_activation() -> bool {
true
}
}

#[derive(Encode, Decode, TypeInfo, Clone, RuntimeDebug, Default, PartialEq, Eq)]
Expand All @@ -333,19 +344,19 @@ pub struct CurrentVaultAndProxy {
pub struct PolkadotExtrinsicBuilder {
extrinsic_call: PolkadotRuntimeCall,
replay_protection: PolkadotReplayProtection,
signer_and_signature: Option<(PolkadotAccountId, PolkadotSignature)>,
signature: Option<PolkadotSignature>,
}

impl PolkadotExtrinsicBuilder {
pub fn new(
replay_protection: PolkadotReplayProtection,
extrinsic_call: PolkadotRuntimeCall,
) -> Self {
Self { extrinsic_call, replay_protection, signer_and_signature: None }
Self { extrinsic_call, replay_protection, signature: None }
}

pub fn signature(&self) -> Option<PolkadotSignature> {
self.signer_and_signature.as_ref().map(|(_, sig)| sig.clone())
self.signature.clone()
}

fn extra(&self) -> PolkadotSignedExtra {
Expand Down Expand Up @@ -389,23 +400,23 @@ impl PolkadotExtrinsicBuilder {
)
}

pub fn insert_signature(&mut self, signer: PolkadotAccountId, signature: PolkadotSignature) {
self.signer_and_signature.replace((signer, signature));
pub fn insert_signature(&mut self, signature: PolkadotSignature) {
self.signature.replace(signature);
}

pub fn get_signed_unchecked_extrinsic(&self) -> Option<PolkadotUncheckedExtrinsic> {
self.signer_and_signature.as_ref().map(|(signer, signature)| {
self.signature.as_ref().map(|signature| {
PolkadotUncheckedExtrinsic::new_signed(
self.extrinsic_call.clone(),
*signer,
self.replay_protection.signer,
signature.clone(),
self.extra(),
)
})
}

pub fn is_signed(&self) -> bool {
self.signer_and_signature.is_some()
self.signature.is_some()
}
}

Expand Down Expand Up @@ -912,6 +923,7 @@ pub type PolkadotPublicKey = PolkadotAccountId;
#[derive(Debug, Encode, Decode, TypeInfo, Eq, PartialEq, Clone)]
pub struct PolkadotReplayProtection {
pub genesis_hash: PolkadotHash,
pub signer: PolkadotAccountId,
pub nonce: PolkadotIndex,
}

Expand Down Expand Up @@ -1038,16 +1050,19 @@ mod test_polkadot_extrinsics {
println!("Encoded Call: 0x{}", hex::encode(test_runtime_call.encode()));

let mut extrinsic_builder = PolkadotExtrinsicBuilder::new(
PolkadotReplayProtection { nonce: 12, genesis_hash: Default::default() },
PolkadotReplayProtection {
nonce: 12,
signer: account_id_1,
genesis_hash: Default::default(),
},
test_runtime_call,
);
extrinsic_builder.insert_signature(
account_id_1,
keypair_1.sign(&extrinsic_builder.get_signature_payload(
extrinsic_builder.insert_signature(keypair_1.sign(
&extrinsic_builder.get_signature_payload(
TEST_RUNTIME_VERSION.spec_version,
TEST_RUNTIME_VERSION.transaction_version,
)),
);
),
));

assert!(extrinsic_builder.is_signed());

Expand Down
27 changes: 7 additions & 20 deletions state-chain/chains/src/dot/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,35 +27,23 @@ pub trait PolkadotEnvironment {
Self::try_vault_account().expect("Vault account must be set")
}

fn try_proxy_account() -> Option<PolkadotAccountId>;
fn proxy_account() -> PolkadotAccountId {
Self::try_proxy_account().expect("Proxy account must be set")
}

fn runtime_version() -> RuntimeVersion;
}

impl<T: ChainEnvironment<SystemAccounts, PolkadotAccountId> + Get<RuntimeVersion>>
PolkadotEnvironment for T
impl<T: ChainEnvironment<VaultAccount, PolkadotAccountId> + Get<RuntimeVersion>> PolkadotEnvironment
for T
{
fn try_vault_account() -> Option<PolkadotAccountId> {
Self::lookup(SystemAccounts::Vault)
}

fn try_proxy_account() -> Option<PolkadotAccountId> {
Self::lookup(SystemAccounts::Proxy)
Self::lookup(VaultAccount)
}

fn runtime_version() -> RuntimeVersion {
Self::get()
}
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Encode, Decode, TypeInfo, MaxEncodedLen)]
pub enum SystemAccounts {
Proxy,
Vault,
}
#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, TypeInfo)]
pub struct VaultAccount;

impl<E> AllBatch<Polkadot> for PolkadotApi<E>
where
Expand Down Expand Up @@ -155,11 +143,10 @@ impl<E: PolkadotEnvironment> ApiCall<PolkadotCrypto> for PolkadotApi<E> {
mut self,
threshold_signature: &<PolkadotCrypto as ChainCrypto>::ThresholdSignature,
) -> Self {
let proxy_account = E::proxy_account();
map_over_api_variants!(
self,
ref mut call,
call.insert_signature(proxy_account, threshold_signature.clone())
call.insert_signature(threshold_signature.clone())
);
self
}
Expand Down Expand Up @@ -219,7 +206,7 @@ impl<E: PolkadotEnvironment + 'static> ApiCall<PolkadotCrypto> for OpaqueApiCall
}

fn signed(mut self, signature: &<PolkadotCrypto as ChainCrypto>::ThresholdSignature) -> Self {
self.builder.insert_signature(E::proxy_account(), signature.clone());
self.builder.insert_signature(signature.clone());
self
}

Expand Down
8 changes: 6 additions & 2 deletions state-chain/chains/src/dot/api/batch_fetch_and_transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,11 @@ mod test_batch_fetch {
];

let mut builder = super::extrinsic_builder(
PolkadotReplayProtection { nonce: NONCE_1, genesis_hash: Default::default() },
PolkadotReplayProtection {
nonce: NONCE_1,
signer: keypair_proxy.public_key(),
genesis_hash: Default::default(),
},
dummy_fetch_params,
dummy_transfer_params,
account_id_vault,
Expand All @@ -117,7 +121,7 @@ mod test_batch_fetch {
hex::encode(&payload.0),
"6fdbf2de25ba016e2c8b4f8238d057066a6ea2a63770073c3b6dcee86b02aeff"
);
builder.insert_signature(keypair_proxy.public_key(), keypair_proxy.sign(&payload));
builder.insert_signature(keypair_proxy.sign(&payload));
assert!(builder.is_signed());
}

Expand Down
8 changes: 6 additions & 2 deletions state-chain/chains/src/dot/api/rotate_vault_proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,11 @@ mod test_rotate_vault_proxy {
let keypair_new_proxy = PolkadotPair::from_seed(&RAW_SEED_3);

let mut builder = super::extrinsic_builder(
PolkadotReplayProtection { nonce: NONCE_2, genesis_hash: Default::default() },
PolkadotReplayProtection {
nonce: NONCE_2,
signer: keypair_old_proxy.public_key(),
genesis_hash: Default::default(),
},
Some(keypair_old_proxy.public_key()),
keypair_new_proxy.public_key(),
PolkadotAccountId(hex_literal::hex!(
Expand All @@ -86,7 +90,7 @@ mod test_rotate_vault_proxy {
.split_whitespace()
.collect::<String>()
);
builder.insert_signature(keypair_old_proxy.public_key(), keypair_old_proxy.sign(&payload));
builder.insert_signature(keypair_old_proxy.sign(&payload));
assert!(builder.is_signed());
}
}
1 change: 1 addition & 0 deletions state-chain/chains/src/dot/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ impl<E> BenchmarkValue for PolkadotApi<E> {
PolkadotApi::RotateVaultProxy(rotate_vault_proxy::extrinsic_builder(
PolkadotReplayProtection {
genesis_hash: Default::default(),
signer: BenchmarkValue::benchmark_value(),
nonce: Default::default(),
},
Some(Default::default()),
Expand Down
2 changes: 1 addition & 1 deletion state-chain/chains/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ pub trait ChainCrypto {
/// Determines whether threshold signatures are made with a specific fixed key, or whether the
/// key is refreshed if we need to retry the signature.
///
/// By default, this is true for Utxo-based chains, true otherwise.
/// By default, this is true for Utxo-based chains, false otherwise.
fn sign_with_specific_key() -> bool {
Self::UtxoChain::get()
}
Expand Down
Loading

0 comments on commit b080527

Please sign in to comment.