Open akileshtangella opened 2 years ago
Code examples would be useful here for parsing proposals and understanding how the verification will look from your perspective.
Another idea:
We can use your idea of signing the call and heavily simplify things in the following way.
The proposal data for all the proposals listed above is simply:
(r_id, nonce, call)
Total Bytes: 32 + 4 + (size of call) = 36 + (size of call)
The signature
is the signature of this proposal data.
execute_proposal
will then look like:
pub fn execute_proposal(
origin: OriginFor<T>,
src_id: T::ChainId,
call: Box<<T as Config<I>>::Proposal>,
proposal_data: Vec<u8>,
signature: Vec<u8>,
) -> DispatchResultWithPostInfo {
let _ = ensure_signed(origin)?;
let r_id = parse_r_id_from_proposal_data(proposal_data);
let nonce = parse_nonce_from_proposal_data(proposal_data);
let call = parse_call_from_proposal_data(proposal_data);
ensure!(
T::SignatureVerifier::verify(&Self::maintainer(), &proposal_data[..], &signature)
.unwrap_or(false),
Error::<T, I>::InvalidPermissions,
);
ensure!(Self::chain_whitelisted(src_id), Error::<T, I>::ChainNotWhitelisted);
ensure!(Self::resource_exists(r_id), Error::<T, I>::ResourceDoesNotExist);
// Ensure that the call and the r_id are consistent
let call_method = parse_method_name_from_call(call);
ensure!(call_method == Self::resources(r_id), Error::<T, I>::CallDoesNotMatchResourceId);
// Ensure this chain id matches the r_id
let execution_chain_id_type = parse_chain_id_type_from_r_id(r_id);
ensure!(execution_chain_id_type ==
T::ChainId::try_from(compute_chain_id_type(
T::ChainIdentifier::get(),
T::ChainType::get()
)), Error<T,I>::IncorrectExecutionChainIdType);
Self::finalize_execution(src_id, nonce, call)
}
The downside of this approach is it does not allow us to verify the data in the call against the proposal_data
.
The only thing left to decide would be the structure of the r_id
's.
I also mean we should sign the entire proposal data + call
(as in the proposal data contains the encoded call as well)
Nonetheless, that looks like a much better starting implementation for these updates. I would continue with an implementation of this firstly and then we can begin adding the relevant proposal tests.
Related task list: https://github.com/webb-tools/protocol-substrate/issues/164
Types
More on
r_id
'sr_id
's serve two purposes. Let's take the example of ananchor-update
.r_id
to method name mapping calledResources
on thesignature-bridge
.anchor-handler
pallet, there is a mapping calledAnchorList
that mapsr_id
's totree_id
's. This is how theupdate
method knows which anchor to update. Therefore, ther_id
should contain some uniquely identifiable information about thetree_id
(or thetree_id
itself). Don't think putting just thetree_id
will be enough since it just an integer...may not be unique enough. Might want to do something like the world 'ANCHOR" concatenated with thetree_id
.Overarching Schema of Proposal Data
Proposal Data:
r_id: ResourceId
(32 bytes)zeroes
:[u8, 4]
(4 bytes)nonce: T::ProposalNonce
(4 bytes)call
(the length of this is proposal specific)Terminology: We will call the
(r_id, zeroes, nonce)
as the proposal header. The proposal header is a total of 40 bytes. We add 4 bytes of zeroes padding to match the length of the EVM/Solidity proposal header. On the EVM/Solidity side, these 4 bytes are taken up by thefunctionSig
.See how
execute_proposal
works (including pseudocode) in the comment below.List of Proposals
1. Set Wrapping Fee Proposal Pallet Flow:
bridge/signature-bridge -> token-wrapper-handler -> token-wrapper
Proposal Data:(r_id, zeroes, nonce, call)
Total Bytes: 40 + (size of call) Call Structure:Call::TokenWrapperHandler(Call::execute_wrapping_fee_proposal { r_id, wrapping_fee_percent, into_pool_share_id })
r_id
is ther_id
wrapping_fee_percent
is the new token wrapping feeinto_pool_share_id
is theid
of the pool share we are setting the wrapping fee for (recall we have wrapping fees per pool share)2. Add Token to Pool Share Proposal Pallet Flow:
bridge/signature-bridge -> token-wrapper-handler -> token-wrapper -> asset-registry
Proposal Data:(r_id, zeroes, nonce, call)
Total Bytes: 40 + (size of call) Call Structure:Call::TokenWrapperHandler(Call::execute_add_token_to_pool_share { r_id, name, asset_id })
r_id
is ther_id
name
is the name of the pool shareasset_id
is theid
of the asset being added to the pool share.3. Remove Token from Pool Share Proposal Pallet Flow:
bridge/signature-bridge -> token-wrapper-handler -> token-wrapper -> asset-registry
Proposal Data:(r_id, zeroes, nonce, call)
Total Bytes: 40 + (size of call) Call Structure:Call::TokenWrapperHandler(Call::execute_remove_token_from_pool_share { r_id, name, asset_id })
r_id
is ther_id
name
is the name of the pool shareasset_id
is theid
of the asset being removed from the pool share.4. Anchor Create Proposal Pallet Flow:
bridge/signature-bridge -> anchor-handler -> anchor
Proposal Data:(r_id, zeroes, nonce, call)
Total Bytes: 40 + (size of call) Call Structure:Call::AnchorHandler(Call::execute_anchor_create_proposal { deposit_size, src_chain_id, r_id, max_edges, tree_depth, asset })
deposit_size
is the denomination size of the anchorsrc_chain_id
is theid
of the source chainr_id
isr_id
max_edges
is the maximum number of anchors the created anchor can have an edge totree_depth
is the depth of the Merkle tree maintained by the anchorasset
is the type of asset (CurrencyIdOf
) that can be deposited into the created anchor5. Anchor Update Proposal Pallet Flow:
bridge/signature-bridge -> anchor-handler -> anchor
Proposal Data:(r_id, zeroes, nonce, call)
Total Bytes: 40 + (size of call) Call Structure:Call::AnchorHandler(Call::execute_anchor_update_proposal { r_id, anchor_metadata: EdgeMetadata { src_chain_id, root, latest_leaf_index } })
r_id
is ther_id
src_chain_id
is theid
of the source chainroot
is the new root of the Merkle treelatest_leaf_index
is the index of the latest inserted leaf (corresponds to the updatedroot
above)6. Register
r_id
Proposal Pallet Flow:signature-bridge/bridge
Proposal Data:(r_id, zeroes, nonce, call)
Total Bytes: 40 + (size of call) Call Structure:7. Set fee recipient proposal Pallet Flow:
signature-bridge -> token-wrapper-handler -> token-wrapper
Proposal Data:(r_id, zeroes, nonce, call)
Total Bytes: 40 + (size of call) Call Structure:8. Rescue Tokens Proposal Pallet Flow:
signature-bridge -> token-wrapper-handler -> token-wrapper
Proposal Data:(r_id, zeroes, nonce, call)
Total Bytes: 40 + (size of call) Call Structure:Questions/Comments/Issues
T::AssetId
? It seems to just be an integer, so 4 bytes should be enough to cover the different asset types we will encounter.