Skip to content

Commit

Permalink
fix: Move tx signing logic to the substrate transaction queue (#525)
Browse files Browse the repository at this point in the history
* Add logging to the create proposal handler

* Make substrate chain id generic

* Move the signing logic to the tx queue

* Update the logging format

* Add Typed Erased Static Transaction Payload for Substrate

* cargo fmt

* Use `TypeErasedStaticTxPayload` for Signature Bridge Watcher

* fix substrate failing tests
  • Loading branch information
shekohex authored May 29, 2023
1 parent e29222c commit 06fa971
Show file tree
Hide file tree
Showing 23 changed files with 301 additions and 133 deletions.
5 changes: 5 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ libsecp256k1 = "0.7.1"
serde = { version = "^1", default-features = false, features = ["derive"] }
glob = "^0.3"
serde_path_to_error = "0.1.9"
serde_bytes = "0.11"

# eth2 light client crates
eth-rpc-client = { package = "eth_rpc_client", git = "https://github.com/webb-tools/pallet-eth2-light-client" }
Expand Down
5 changes: 2 additions & 3 deletions crates/event-watcher-traits/src/substrate/bridge_watcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,8 @@ where
webb_proposals::TypedChainId::Substrate(chain_id);
let bridge_key = BridgeKey::new(typed_chain_id);
let key = SledQueueKey::from_bridge_key(bridge_key);
let client = ctx
.substrate_provider::<RuntimeConfig>(&chain_id.to_string())
.await?;
let client =
ctx.substrate_provider::<RuntimeConfig, _>(chain_id).await?;
let client = Arc::new(client);

loop {
Expand Down
5 changes: 2 additions & 3 deletions crates/event-watcher-traits/src/substrate/event_watcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,8 @@ where
};
let metrics_clone = metrics.clone();
let task = || async {
let maybe_client = ctx
.substrate_provider::<RuntimeConfig>(&chain_id.to_string())
.await;
let maybe_client =
ctx.substrate_provider::<RuntimeConfig, _>(chain_id).await;
let client = match maybe_client {
Ok(client) => client,
Err(err) => {
Expand Down
36 changes: 7 additions & 29 deletions crates/proposal-signing-backends/src/dkg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use webb::substrate::tangle_runtime::api::runtime_types::bounded_collections::bo
use webb::substrate::tangle_runtime::api::runtime_types::webb_proposals::header::{TypedChainId, ResourceId};
use webb::substrate::tangle_runtime::api::runtime_types::webb_proposals::nonce::Nonce;
use webb::substrate::subxt::{OnlineClient, PolkadotConfig};
use sp_core::sr25519::Pair as Sr25519Pair;
use webb::evm::ethers::utils;
use webb::substrate::tangle_runtime::api::runtime_types::webb_proposals::proposal::{Proposal, ProposalKind};
use webb_proposals::ProposalTrait;
Expand All @@ -13,7 +12,7 @@ use webb_relayer_utils::metric;
use webb::substrate::tangle_runtime::api as RuntimeApi;
use webb_relayer_store::{QueueStore, SledStore};
use webb_relayer_store::sled::SledQueueKey;
use webb::substrate::subxt::tx::PairSigner;
use webb_relayer_utils::static_tx_payload::TypeErasedStaticTxPayload;

type DkgConfig = PolkadotConfig;
type DkgClient = OnlineClient<DkgConfig>;
Expand All @@ -22,7 +21,6 @@ type DkgClient = OnlineClient<DkgConfig>;
pub struct DkgProposalSigningBackend {
#[builder(setter(into))]
pub client: DkgClient,
pub pair: PairSigner<PolkadotConfig, Sr25519Pair>,
/// Something that implements the QueueStore trait.
#[builder(setter(into))]
store: Arc<SledStore>,
Expand Down Expand Up @@ -102,10 +100,10 @@ impl super::ProposalSigningBackend for DkgProposalSigningBackend {
let src_chain_id =
webb_proposals_typed_chain_converter(self.src_chain_id);
tracing::debug!(
?nonce,
resource_id = %hex::encode(resource_id.into_bytes()),
nonce = nonce.0,
resource_id = hex::encode(resource_id.into_bytes()),
src_chain_id = ?self.src_chain_id,
proposal = %hex::encode(proposal.to_vec()),
proposal = hex::encode(proposal.to_vec()),
"sending proposal to DKG runtime"
);

Expand All @@ -115,41 +113,21 @@ impl super::ProposalSigningBackend for DkgProposalSigningBackend {
data: BoundedVec(proposal.to_vec()),
};
let acknowledge_proposal_tx = tx_api.acknowledge_proposal(
nonce.clone(),
nonce,
src_chain_id,
ResourceId(resource_id.into_bytes()),
unsigned_proposal,
);

let signer = &self.pair;
let maybe_signed_acknowledge_proposal_tx = self
.client
.tx()
.create_signed(&acknowledge_proposal_tx, signer, Default::default())
.await;
let signed_acknowledge_proposal_tx =
match maybe_signed_acknowledge_proposal_tx {
Ok(tx) => tx,
Err(e) => {
tracing::error!(?e, "failed to sign tx");
return Err(webb_relayer_utils::Error::Generic(
"failed to sign tx",
));
}
};
let data_hash =
utils::keccak256(acknowledge_proposal_tx.call_data().encode());
let tx_key = SledQueueKey::from_substrate_with_custom_key(
my_chain_id,
make_acknowledge_proposal_key(data_hash),
);
let tx = TypeErasedStaticTxPayload::try_from(acknowledge_proposal_tx)?;
// Enqueue transaction in protocol-substrate transaction queue
QueueStore::<Vec<u8>>::enqueue_item(
&self.store,
tx_key,
signed_acknowledge_proposal_tx.into_encoded(),
)?;

QueueStore::enqueue_item(&self.store, tx_key, tx)?;
Ok(())
}
}
Expand Down
16 changes: 13 additions & 3 deletions crates/proposal-signing-backends/src/proposal_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,15 @@ where
Ok(())
}

// create anchor update proposal for Evm target system
/// create anchor update proposal for Evm target system
#[tracing::instrument(
skip_all,
fields(
proposal_type = "AnchorUpdateProposal",
from = ?src_resource_id.typed_chain_id(),
to = ?target_resource_id.typed_chain_id(),
leaf_index,
merkle_root = ?hex::encode(merkle_root),
nonce = leaf_index,
merkle_root = hex::encode(merkle_root),
)
)]
pub fn evm_anchor_update_proposal(
Expand Down Expand Up @@ -82,6 +82,16 @@ pub fn evm_anchor_update_proposal(
}

// create anchor update proposal for substrate system
#[tracing::instrument(
skip_all,
fields(
proposal_type = "AnchorUpdateProposal",
from = ?src_resource_id.typed_chain_id(),
to = ?target_resource_id.typed_chain_id(),
nonce = leaf_index,
merkle_root = hex::encode(merkle_root),
)
)]
pub fn substrate_anchor_update_proposal(
merkle_root: [u8; 32],
leaf_index: u32,
Expand Down
28 changes: 15 additions & 13 deletions crates/relayer-context/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,12 +202,14 @@ impl RelayerContext {
///
/// * `chain_id` - A string representing the chain ID.
#[cfg(feature = "substrate")]
pub async fn substrate_provider<C: subxt::Config>(
pub async fn substrate_provider<C: subxt::Config, I: Into<types::U256>>(
&self,
chain_id: &str,
chain_id: I,
) -> webb_relayer_utils::Result<subxt::OnlineClient<C>> {
let chain_id: types::U256 = chain_id.into();
let chain_name = chain_id.to_string();
let node_config =
self.config.substrate.get(chain_id).ok_or_else(|| {
self.config.substrate.get(&chain_name).ok_or_else(|| {
webb_relayer_utils::Error::NodeNotFound {
chain_id: chain_id.to_string(),
}
Expand All @@ -224,18 +226,18 @@ impl RelayerContext {
///
/// * `chain_id` - A string representing the chain ID.
#[cfg(feature = "substrate")]
pub async fn substrate_wallet(
pub async fn substrate_wallet<I: Into<types::U256>>(
&self,
chain_id: &str,
chain_id: I,
) -> webb_relayer_utils::Result<Sr25519Pair> {
let node_config = self
.config
.substrate
.get(chain_id)
.cloned()
.ok_or_else(|| webb_relayer_utils::Error::NodeNotFound {
chain_id: chain_id.to_string(),
})?;
let chain_id: types::U256 = chain_id.into();
let chain_name = chain_id.to_string();
let node_config =
self.config.substrate.get(&chain_name).cloned().ok_or_else(
|| webb_relayer_utils::Error::NodeNotFound {
chain_id: chain_id.to_string(),
},
)?;
let suri_key = node_config
.suri
.ok_or(webb_relayer_utils::Error::MissingSecrets)?;
Expand Down
2 changes: 1 addition & 1 deletion crates/relayer-types/src/suri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use sp_core::Pair;

/// [`Substrate Uri`](https://polkadot.js.org/docs/keyring/start/suri/)
#[derive(Clone)]
pub struct Suri(Sr25519Pair);
pub struct Suri(pub Sr25519Pair);

impl std::fmt::Debug for Suri {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Expand Down
2 changes: 2 additions & 0 deletions crates/relayer-utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ backoff = { workspace = true }
serde_path_to_error = { workspace = true }
webb-proposals = { workspace = true }
webb = { workspace = true }
serde = { workspace = true }
serde_bytes = { workspace = true }
# Used by ethers (but we need it to be vendored with the lib).
native-tls = { workspace = true, optional = true }
glob = { workspace = true }
Expand Down
7 changes: 7 additions & 0 deletions crates/relayer-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ pub mod metric;
pub mod probe;
/// Retry functionality
pub mod retry;
/// type-erased StaticTxPayload for Substrate Transaction queue.
pub mod static_tx_payload;

/// An enum of all possible errors that could be encountered during the execution of the Webb
/// Relayer.
Expand Down Expand Up @@ -202,6 +204,11 @@ pub enum Error {
/// Invalid Merkle root
#[error("Invalid Merkle root at index {}", _0)]
InvalidMerkleRootError(u32),
/// Missing Static Transaction Validation Details
/// This error is raised when the static transaction validation details
/// are missing.
#[error("Missing Substrate Static Transaction Validation Details")]
MissingValidationDetails,
}

/// A type alias for the result for webb relayer, that uses the `Error` enum.
Expand Down
72 changes: 72 additions & 0 deletions crates/relayer-utils/src/static_tx_payload.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use core::fmt;

use webb::substrate::{
scale::Encode,
subxt::tx::{StaticTxPayload, TxPayload},
};

#[derive(Clone, serde::Serialize, serde::Deserialize)]
pub struct TypeErasedStaticTxPayload {
pub pallet_name: String,
pub call_name: String,
#[serde(with = "serde_bytes")]
pub call_data: Vec<u8>,
}

impl std::fmt::Debug for TypeErasedStaticTxPayload {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TypeErasedStaticTxPayload")
.field("pallet_name", &self.pallet_name)
.field("call_name", &self.call_name)
.field("call_data", &hex::encode(&self.call_data))
.finish()
}
}

impl fmt::Display for TypeErasedStaticTxPayload {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}.{}({})",
self.pallet_name,
self.call_name,
hex::encode(&self.call_data)
)
}
}

impl<CallData: Encode> TryFrom<StaticTxPayload<CallData>>
for TypeErasedStaticTxPayload
{
type Error = super::Error;
fn try_from(
payload: StaticTxPayload<CallData>,
) -> Result<Self, Self::Error> {
let details = payload
.validation_details()
.ok_or_else(|| Self::Error::MissingValidationDetails)?;
let call_data = payload.call_data().encode();
Ok(Self {
pallet_name: details.pallet_name.to_owned(),
call_name: details.call_name.to_owned(),
call_data,
})
}
}

impl TxPayload for TypeErasedStaticTxPayload {
fn encode_call_data_to(
&self,
metadata: &webb::substrate::subxt::Metadata,
out: &mut Vec<u8>,
) -> Result<(), webb::substrate::subxt::Error> {
let pallet = metadata.pallet(&self.pallet_name)?;
let pallet_index = pallet.index();
let call_index = pallet.call_index(&self.call_name)?;

pallet_index.encode_to(out);
call_index.encode_to(out);
out.extend_from_slice(&self.call_data);
Ok(())
}
}
5 changes: 5 additions & 0 deletions crates/tx-queue/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,8 @@ default = ["std", "evm", "substrate"]
std = []
evm = ["webb-relayer-context/evm"]
substrate = ["webb-relayer-context/substrate", "sp-core", "sp-runtime"]

[dev-dependencies]
webb-relayer-config = { workspace = true }
url = { workspace = true }
tracing-subscriber = { workspace = true }
2 changes: 1 addition & 1 deletion crates/tx-queue/src/evm/evm_tx_queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@ where
let metrics_clone = self.ctx.metrics.clone();
let task = || async {
loop {
// tracing::trace!("Checking for any txs in the queue ...");
let maybe_tx = store
.dequeue_item(SledQueueKey::from_evm_chain_id(chain_id))?;
let maybe_explorer = &chain_config.explorer;
Expand All @@ -115,6 +114,7 @@ where
raw_tx.set_chain_id(U64::from(chain_id)).clone();
let my_tx_hash = raw_tx.sighash();
tx_hash = my_tx_hash;
tracing::debug!(?tx_hash, tx = ?raw_tx, "Found tx in queue");
// dry run test
let dry_run_outcome =
client.call(&raw_tx.clone(), None).await;
Expand Down
Loading

0 comments on commit 06fa971

Please sign in to comment.