From 0c3c7cfb599b9565871261dc5d452daa0b424c26 Mon Sep 17 00:00:00 2001 From: eloylp Date: Mon, 10 Jun 2024 10:30:04 +0200 Subject: [PATCH 01/15] Add initial integration (POC) of the axelar-encoding crate --- Cargo.lock | 103 ++++++++++++++++ Cargo.toml | 1 + contracts/multisig-prover/Cargo.toml | 1 + contracts/multisig-prover/src/encoding/mod.rs | 2 + .../multisig-prover/src/encoding/sol/mod.rs | 115 ++++++++++++++++++ contracts/multisig-prover/src/error.rs | 5 + contracts/multisig-prover/src/payload.rs | 27 +++- 7 files changed, 253 insertions(+), 1 deletion(-) create mode 100644 contracts/multisig-prover/src/encoding/sol/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 86f4f019f..53fc5dab7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -746,6 +746,16 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "axelar-encoding" +version = "0.1.0" +source = "git+ssh://git@github.com/eigerco/solana-axelar-internal.git?branch=main#4321554e61d8f35fcb8b259b7c3be02ef01ec3b6" +dependencies = [ + "rkyv", + "sha3 0.10.8", + "thiserror", +] + [[package]] name = "axelar-wasm-std" version = "0.1.0" @@ -1289,6 +1299,28 @@ version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +dependencies = [ + "proc-macro2 1.0.67", + "quote 1.0.33", + "syn 1.0.109", +] + [[package]] name = "bytecount" version = "0.6.7" @@ -5117,6 +5149,7 @@ dependencies = [ "alloy-primitives", "alloy-sol-types", "anyhow", + "axelar-encoding", "axelar-wasm-std", "axelar-wasm-std-derive", "bcs", @@ -6442,6 +6475,26 @@ dependencies = [ "bytes", ] +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2 1.0.67", + "quote 1.0.33", + "syn 1.0.109", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -6736,6 +6789,15 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] + [[package]] name = "report" version = "0.1.0" @@ -6889,6 +6951,35 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "rkyv" +version = "0.7.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cba464629b3394fc4dbc6f940ff8f5b4ff5c7aef40f29166fd4ad12acbc99c0" +dependencies = [ + "bitvec 1.0.1", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid 1.4.1", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7dddfff8de25e6f62b9d64e6e432bf1c6736c57d20323e15ee10435fbda7c65" +dependencies = [ + "proc-macro2 1.0.67", + "quote 1.0.33", + "syn 1.0.109", +] + [[package]] name = "rlp" version = "0.5.2" @@ -7309,6 +7400,12 @@ dependencies = [ "untrusted", ] +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + [[package]] name = "sec1" version = "0.7.3" @@ -7737,6 +7834,12 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "simdutf8" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" + [[package]] name = "similar" version = "2.2.1" diff --git a/Cargo.toml b/Cargo.toml index 01387b218..ee9da4014 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,7 @@ serde_json = "1.0.89" schemars = "0.8.10" sha3 = { version = "0.10.8", default-features = false, features = [] } signature-verifier-api = { version = "^0.1.0", path = "packages/signature-verifier-api" } +axelar-encoding = { git = "ssh://git@github.com/eigerco/solana-axelar-internal.git", branch = "main"} [workspace.lints.clippy] arithmetic_side_effects = "deny" diff --git a/contracts/multisig-prover/Cargo.toml b/contracts/multisig-prover/Cargo.toml index e50323090..30713f7b5 100644 --- a/contracts/multisig-prover/Cargo.toml +++ b/contracts/multisig-prover/Cargo.toml @@ -59,6 +59,7 @@ service-registry = { workspace = true } sha3 = { workspace = true } thiserror = { workspace = true } voting-verifier = { workspace = true, features = ["library"] } +axelar-encoding = { workspace = true } [dev-dependencies] anyhow = "1.0" diff --git a/contracts/multisig-prover/src/encoding/mod.rs b/contracts/multisig-prover/src/encoding/mod.rs index 3ab989f42..6a7367f20 100644 --- a/contracts/multisig-prover/src/encoding/mod.rs +++ b/contracts/multisig-prover/src/encoding/mod.rs @@ -1,4 +1,5 @@ pub mod abi; +pub mod sol; use cosmwasm_schema::cw_serde; @@ -7,4 +8,5 @@ use cosmwasm_schema::cw_serde; pub enum Encoder { Abi, Bcs, + Solana } diff --git a/contracts/multisig-prover/src/encoding/sol/mod.rs b/contracts/multisig-prover/src/encoding/sol/mod.rs new file mode 100644 index 000000000..c40a9a2f5 --- /dev/null +++ b/contracts/multisig-prover/src/encoding/sol/mod.rs @@ -0,0 +1,115 @@ +use std::collections::BTreeMap; + +use axelar_encoding::types::{ + CrossChainId, Message, Payload, PublicKey, Signature, Signer, WeightedSignature, WorkerSet, + U256, +}; + +use itertools::Itertools; +use multisig::{msg::SignerWithSig, verifier_set::VerifierSet}; + +const ECDSA_COMPRESSED_PUBKEY_LEN: usize = 33; // this should be probably a public constant in axelar_encoding. +const ED25519_PUBKEY_LEN: usize = 32; // this should be probably a public constant in axelar_encoding. + +pub fn to_worker_set(vs: &VerifierSet) -> WorkerSet { + let signers: BTreeMap = vs + .signers + .iter() + .map(|(address, signer)| { + let enc_signer = signer.address.to_string(); + let enc_pubkey = to_pub_key(&signer.pub_key); + + let enc_weight = U256::from_be(to_u256_be(signer.weight.u128())); + + ( + address.clone(), + Signer::new(enc_signer, enc_pubkey, enc_weight), + ) + }) + .collect(); + + WorkerSet::new( + vs.created_at, + signers, + U256::from_be(to_u256_be(vs.threshold.u128())), + ) +} + +fn to_pub_key(pk: &multisig::key::PublicKey) -> PublicKey { + match pk { + multisig::key::PublicKey::Ecdsa(hb) => { + PublicKey::new_ecdsa(hb.to_array::().unwrap()) + } + multisig::key::PublicKey::Ed25519(hb) => { + PublicKey::new_ed25519(hb.to_array::().unwrap()) + } + } +} + +// Fits a u128 into a u256 in big endian representation. +fn to_u256_be(u: u128) -> [u8; 32] { + let mut uin256 = [0u8; 32]; + uin256[16..32].copy_from_slice(&u.to_be_bytes()); + uin256 +} + +impl From<&crate::payload::Payload> for Payload { + fn from(value: &crate::payload::Payload) -> Self { + match value { + crate::payload::Payload::Messages(msgs) => { + Payload::new_messages(msgs.iter().map(to_msg).collect_vec()) + } + crate::payload::Payload::VerifierSet(vs) => Payload::new_worker_set(to_worker_set(&vs)), + } + } +} + +fn to_msg(msg: &router_api::Message) -> Message { + let enc_cc_id = CrossChainId::new(msg.cc_id.chain.to_string(), msg.cc_id.id.to_string()); + + Message::new( + enc_cc_id, + msg.source_address.to_string(), + msg.destination_chain.to_string(), + msg.destination_address.to_string(), + msg.payload_hash, + ) +} + +pub fn to_weighted_signature(sig: &SignerWithSig) -> WeightedSignature { + let enc_pub_key = to_pub_key(&sig.signer.pub_key); + let enc_signature = to_signature(&sig.signature); + let enc_weight = U256::from_be(to_u256_be(sig.signer.weight.u128())); + + WeightedSignature::new(enc_pub_key, enc_signature, enc_weight) +} + +fn to_signature(sig: &multisig::key::Signature) -> Signature { + match sig { + multisig::key::Signature::Ecdsa(_) => unimplemented!(), // Todo: should we implement this in axelar_encoding ? + + // Following 2: We are just moving the bytes around, hoping this conversions match. Not sure if `HexBinary` + // representation will match here with the decoding part. + multisig::key::Signature::EcdsaRecoverable(r) => { + Signature::EcdsaRecoverable(r.as_ref().try_into().unwrap()) + } + multisig::key::Signature::Ed25519(ed) => { + Signature::new_ed25519(ed.as_slice().try_into().unwrap()) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn conversion_to_u256_be_works() { + let integer = to_u256_be(u128::MAX); + let expected = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + ]; + assert_eq!(expected, integer); + } +} diff --git a/contracts/multisig-prover/src/error.rs b/contracts/multisig-prover/src/error.rs index d95d0e1fd..6acde3bd2 100644 --- a/contracts/multisig-prover/src/error.rs +++ b/contracts/multisig-prover/src/error.rs @@ -41,6 +41,11 @@ pub enum ContractError { #[error(transparent)] BcsError(#[from] bcs::Error), + /// Todo, Below error throws: binary operation `==` cannot be applied to type `EncodingError<1024>` + /// this is a workaround. + #[error("encoding/decoding failure: [0]")] + SolEncodingError(String), + #[error("verifier set has not changed sufficiently since last update")] VerifierSetUnchanged, diff --git a/contracts/multisig-prover/src/payload.rs b/contracts/multisig-prover/src/payload.rs index 14082d89e..66f1c07e4 100644 --- a/contracts/multisig-prover/src/payload.rs +++ b/contracts/multisig-prover/src/payload.rs @@ -1,6 +1,7 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::{from_json, HexBinary, StdResult}; use cw_storage_plus::{Key, KeyDeserialize, PrimaryKey}; +use itertools::Itertools; use sha3::{Digest, Keccak256}; use axelar_wasm_std::hash::Hash; @@ -9,7 +10,11 @@ use multisig::verifier_set::VerifierSet; use router_api::{CrossChainId, Message}; use crate::{ - encoding::{abi, Encoder}, + encoding::{ + abi, + sol::{to_weighted_signature, to_worker_set}, + Encoder, + }, error::ContractError, }; @@ -46,6 +51,11 @@ impl Payload { match encoder { Encoder::Abi => abi::payload_hash_to_sign(domain_separator, cur_verifier_set, self), Encoder::Bcs => todo!(), + Encoder::Solana => Ok(axelar_encoding::hash_payload( + &domain_separator, + &to_worker_set(cur_verifier_set), + &axelar_encoding::types::Payload::from(self), + )), } } @@ -70,6 +80,21 @@ impl Payload { abi::execute_data::encode(verifier_set, signers_with_sigs, &payload_hash, payload) } Encoder::Bcs => todo!(), + Encoder::Solana => { + let enc_signatures = signers_with_sigs + .iter() + .map(to_weighted_signature) + .collect_vec(); + + let bytes = axelar_encoding::encode::<1024>( // Todo reason about this "1024" magic number. + &to_worker_set(&verifier_set), + enc_signatures, + axelar_encoding::types::Payload::from(payload), + ) + .map_err(|e| ContractError::SolEncodingError(e.to_string()))?; + + Ok(HexBinary::from(bytes)) // Todo, what kind of conversion if any comes from here. Can we expect hex repr directly ? + } } } } From 12c37e1843830d6283638076ecf75fae881231e8 Mon Sep 17 00:00:00 2001 From: eloylp Date: Mon, 10 Jun 2024 19:53:07 +0200 Subject: [PATCH 02/15] Propagate errors, do not panic --- .../multisig-prover/src/encoding/sol/mod.rs | 81 ++++++++++++------- contracts/multisig-prover/src/payload.rs | 20 ++--- 2 files changed, 62 insertions(+), 39 deletions(-) diff --git a/contracts/multisig-prover/src/encoding/sol/mod.rs b/contracts/multisig-prover/src/encoding/sol/mod.rs index c40a9a2f5..fc2644e08 100644 --- a/contracts/multisig-prover/src/encoding/sol/mod.rs +++ b/contracts/multisig-prover/src/encoding/sol/mod.rs @@ -1,4 +1,4 @@ -use std::collections::BTreeMap; +use std::{array::TryFromSliceError, collections::BTreeMap}; use axelar_encoding::types::{ CrossChainId, Message, Payload, PublicKey, Signature, Signer, WeightedSignature, WorkerSet, @@ -8,42 +8,47 @@ use axelar_encoding::types::{ use itertools::Itertools; use multisig::{msg::SignerWithSig, verifier_set::VerifierSet}; +use crate::error::ContractError; + const ECDSA_COMPRESSED_PUBKEY_LEN: usize = 33; // this should be probably a public constant in axelar_encoding. const ED25519_PUBKEY_LEN: usize = 32; // this should be probably a public constant in axelar_encoding. -pub fn to_worker_set(vs: &VerifierSet) -> WorkerSet { - let signers: BTreeMap = vs - .signers +type Result = core::result::Result; + +pub fn to_worker_set(vs: &VerifierSet) -> Result { + let mut signers: BTreeMap = BTreeMap::new(); + + vs.signers .iter() - .map(|(address, signer)| { + .try_for_each(|(address, signer)| -> Result<()> { let enc_signer = signer.address.to_string(); - let enc_pubkey = to_pub_key(&signer.pub_key); + let enc_pubkey = to_pub_key(&signer.pub_key)?; let enc_weight = U256::from_be(to_u256_be(signer.weight.u128())); - ( + signers.insert( address.clone(), Signer::new(enc_signer, enc_pubkey, enc_weight), - ) - }) - .collect(); + ); + Ok(()) + })?; - WorkerSet::new( + Ok(WorkerSet::new( vs.created_at, signers, U256::from_be(to_u256_be(vs.threshold.u128())), - ) + )) } -fn to_pub_key(pk: &multisig::key::PublicKey) -> PublicKey { - match pk { +fn to_pub_key(pk: &multisig::key::PublicKey) -> Result { + Ok(match pk { multisig::key::PublicKey::Ecdsa(hb) => { - PublicKey::new_ecdsa(hb.to_array::().unwrap()) + PublicKey::new_ecdsa(hb.to_array::()?) } multisig::key::PublicKey::Ed25519(hb) => { - PublicKey::new_ed25519(hb.to_array::().unwrap()) + PublicKey::new_ed25519(hb.to_array::()?) } - } + }) } // Fits a u128 into a u256 in big endian representation. @@ -53,14 +58,17 @@ fn to_u256_be(u: u128) -> [u8; 32] { uin256 } -impl From<&crate::payload::Payload> for Payload { - fn from(value: &crate::payload::Payload) -> Self { - match value { +impl TryFrom<&crate::payload::Payload> for Payload { + type Error = ContractError; + fn try_from(value: &crate::payload::Payload) -> std::result::Result { + Ok(match value { crate::payload::Payload::Messages(msgs) => { Payload::new_messages(msgs.iter().map(to_msg).collect_vec()) } - crate::payload::Payload::VerifierSet(vs) => Payload::new_worker_set(to_worker_set(&vs)), - } + crate::payload::Payload::VerifierSet(vs) => { + Payload::new_worker_set(to_worker_set(&vs)?) + } + }) } } @@ -76,25 +84,38 @@ fn to_msg(msg: &router_api::Message) -> Message { ) } -pub fn to_weighted_signature(sig: &SignerWithSig) -> WeightedSignature { - let enc_pub_key = to_pub_key(&sig.signer.pub_key); - let enc_signature = to_signature(&sig.signature); +pub fn to_weighted_signature(sig: &SignerWithSig) -> Result { + let enc_pub_key = to_pub_key(&sig.signer.pub_key)?; + let enc_signature = to_signature(&sig.signature)?; let enc_weight = U256::from_be(to_u256_be(sig.signer.weight.u128())); - WeightedSignature::new(enc_pub_key, enc_signature, enc_weight) + Ok(WeightedSignature::new( + enc_pub_key, + enc_signature, + enc_weight, + )) } -fn to_signature(sig: &multisig::key::Signature) -> Signature { +fn to_signature(sig: &multisig::key::Signature) -> Result { match sig { multisig::key::Signature::Ecdsa(_) => unimplemented!(), // Todo: should we implement this in axelar_encoding ? - + // Following 2: We are just moving the bytes around, hoping this conversions match. Not sure if `HexBinary` // representation will match here with the decoding part. multisig::key::Signature::EcdsaRecoverable(r) => { - Signature::EcdsaRecoverable(r.as_ref().try_into().unwrap()) + let data = r + .as_ref() + .try_into() + .map_err(|e: TryFromSliceError| ContractError::SolEncodingError(e.to_string()))?; + Ok(Signature::EcdsaRecoverable(data)) } multisig::key::Signature::Ed25519(ed) => { - Signature::new_ed25519(ed.as_slice().try_into().unwrap()) + let data = ed + .as_ref() + .try_into() + .map_err(|e: TryFromSliceError| ContractError::SolEncodingError(e.to_string()))?; + + Ok(Signature::new_ed25519(data)) } } } diff --git a/contracts/multisig-prover/src/payload.rs b/contracts/multisig-prover/src/payload.rs index 66f1c07e4..ed5dfd67b 100644 --- a/contracts/multisig-prover/src/payload.rs +++ b/contracts/multisig-prover/src/payload.rs @@ -53,8 +53,8 @@ impl Payload { Encoder::Bcs => todo!(), Encoder::Solana => Ok(axelar_encoding::hash_payload( &domain_separator, - &to_worker_set(cur_verifier_set), - &axelar_encoding::types::Payload::from(self), + &to_worker_set(cur_verifier_set)?, + &axelar_encoding::types::Payload::try_from(self)?, )), } } @@ -81,15 +81,17 @@ impl Payload { } Encoder::Bcs => todo!(), Encoder::Solana => { - let enc_signatures = signers_with_sigs - .iter() - .map(to_weighted_signature) - .collect_vec(); + let mut enc_signatures = Vec::new(); + + for s in signers_with_sigs { + enc_signatures.push(to_weighted_signature(&s)?) + } - let bytes = axelar_encoding::encode::<1024>( // Todo reason about this "1024" magic number. - &to_worker_set(&verifier_set), + let bytes = axelar_encoding::encode::<1024>( + // Todo reason about this "1024" magic number. + &to_worker_set(&verifier_set)?, enc_signatures, - axelar_encoding::types::Payload::from(payload), + axelar_encoding::types::Payload::try_from(payload)?, ) .map_err(|e| ContractError::SolEncodingError(e.to_string()))?; From 3812bbe263f5f7eb698e47756343d757602c72cf Mon Sep 17 00:00:00 2001 From: eloylp Date: Tue, 11 Jun 2024 17:59:47 +0200 Subject: [PATCH 03/15] Rename encoding method --- contracts/multisig-prover/src/encoding/mod.rs | 2 +- contracts/multisig-prover/src/payload.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/multisig-prover/src/encoding/mod.rs b/contracts/multisig-prover/src/encoding/mod.rs index 6a7367f20..bbcf9d34c 100644 --- a/contracts/multisig-prover/src/encoding/mod.rs +++ b/contracts/multisig-prover/src/encoding/mod.rs @@ -8,5 +8,5 @@ use cosmwasm_schema::cw_serde; pub enum Encoder { Abi, Bcs, - Solana + Rkyv, } diff --git a/contracts/multisig-prover/src/payload.rs b/contracts/multisig-prover/src/payload.rs index ed5dfd67b..406204994 100644 --- a/contracts/multisig-prover/src/payload.rs +++ b/contracts/multisig-prover/src/payload.rs @@ -51,7 +51,7 @@ impl Payload { match encoder { Encoder::Abi => abi::payload_hash_to_sign(domain_separator, cur_verifier_set, self), Encoder::Bcs => todo!(), - Encoder::Solana => Ok(axelar_encoding::hash_payload( + Encoder::Rkyv => Ok(axelar_encoding::hash_payload( &domain_separator, &to_worker_set(cur_verifier_set)?, &axelar_encoding::types::Payload::try_from(self)?, @@ -80,7 +80,7 @@ impl Payload { abi::execute_data::encode(verifier_set, signers_with_sigs, &payload_hash, payload) } Encoder::Bcs => todo!(), - Encoder::Solana => { + Encoder::Rkyv => { let mut enc_signatures = Vec::new(); for s in signers_with_sigs { From 366d837323c0f65f18c2812ab1f19293f436108b Mon Sep 17 00:00:00 2001 From: eloylp Date: Tue, 11 Jun 2024 18:12:29 +0200 Subject: [PATCH 04/15] Invert qualified names for Rkyv encoding types --- .../multisig-prover/src/encoding/sol/mod.rs | 67 +++++++++---------- 1 file changed, 32 insertions(+), 35 deletions(-) diff --git a/contracts/multisig-prover/src/encoding/sol/mod.rs b/contracts/multisig-prover/src/encoding/sol/mod.rs index fc2644e08..92cacd0a4 100644 --- a/contracts/multisig-prover/src/encoding/sol/mod.rs +++ b/contracts/multisig-prover/src/encoding/sol/mod.rs @@ -1,22 +1,19 @@ use std::{array::TryFromSliceError, collections::BTreeMap}; -use axelar_encoding::types::{ - CrossChainId, Message, Payload, PublicKey, Signature, Signer, WeightedSignature, WorkerSet, - U256, -}; use itertools::Itertools; -use multisig::{msg::SignerWithSig, verifier_set::VerifierSet}; +use multisig::{key::{PublicKey, Signature}, msg::SignerWithSig, verifier_set::VerifierSet}; +use router_api::Message; -use crate::error::ContractError; +use crate::{error::ContractError, payload::Payload}; const ECDSA_COMPRESSED_PUBKEY_LEN: usize = 33; // this should be probably a public constant in axelar_encoding. const ED25519_PUBKEY_LEN: usize = 32; // this should be probably a public constant in axelar_encoding. type Result = core::result::Result; -pub fn to_worker_set(vs: &VerifierSet) -> Result { - let mut signers: BTreeMap = BTreeMap::new(); +pub fn to_worker_set(vs: &VerifierSet) -> Result { + let mut signers: BTreeMap = BTreeMap::new(); vs.signers .iter() @@ -24,29 +21,29 @@ pub fn to_worker_set(vs: &VerifierSet) -> Result { let enc_signer = signer.address.to_string(); let enc_pubkey = to_pub_key(&signer.pub_key)?; - let enc_weight = U256::from_be(to_u256_be(signer.weight.u128())); + let enc_weight = axelar_encoding::types::U256::from_be(to_u256_be(signer.weight.u128())); signers.insert( address.clone(), - Signer::new(enc_signer, enc_pubkey, enc_weight), + axelar_encoding::types::Signer::new(enc_signer, enc_pubkey, enc_weight), ); Ok(()) })?; - Ok(WorkerSet::new( + Ok(axelar_encoding::types::WorkerSet::new( vs.created_at, signers, - U256::from_be(to_u256_be(vs.threshold.u128())), + axelar_encoding::types::U256::from_be(to_u256_be(vs.threshold.u128())), )) } -fn to_pub_key(pk: &multisig::key::PublicKey) -> Result { +fn to_pub_key(pk: &PublicKey) -> Result { Ok(match pk { - multisig::key::PublicKey::Ecdsa(hb) => { - PublicKey::new_ecdsa(hb.to_array::()?) + PublicKey::Ecdsa(hb) => { + axelar_encoding::types::PublicKey::new_ecdsa(hb.to_array::()?) } - multisig::key::PublicKey::Ed25519(hb) => { - PublicKey::new_ed25519(hb.to_array::()?) + PublicKey::Ed25519(hb) => { + axelar_encoding::types::PublicKey::new_ed25519(hb.to_array::()?) } }) } @@ -58,24 +55,24 @@ fn to_u256_be(u: u128) -> [u8; 32] { uin256 } -impl TryFrom<&crate::payload::Payload> for Payload { +impl TryFrom<&Payload> for axelar_encoding::types::Payload { type Error = ContractError; - fn try_from(value: &crate::payload::Payload) -> std::result::Result { + fn try_from(value: &Payload) -> std::result::Result { Ok(match value { - crate::payload::Payload::Messages(msgs) => { - Payload::new_messages(msgs.iter().map(to_msg).collect_vec()) + Payload::Messages(msgs) => { + axelar_encoding::types::Payload::new_messages(msgs.iter().map(to_msg).collect_vec()) } - crate::payload::Payload::VerifierSet(vs) => { - Payload::new_worker_set(to_worker_set(&vs)?) + Payload::VerifierSet(vs) => { + axelar_encoding::types::Payload::new_worker_set(to_worker_set(&vs)?) } }) } } -fn to_msg(msg: &router_api::Message) -> Message { - let enc_cc_id = CrossChainId::new(msg.cc_id.chain.to_string(), msg.cc_id.id.to_string()); +fn to_msg(msg: &Message) -> axelar_encoding::types::Message { + let enc_cc_id = axelar_encoding::types::CrossChainId::new(msg.cc_id.chain.to_string(), msg.cc_id.id.to_string()); - Message::new( + axelar_encoding::types::Message::new( enc_cc_id, msg.source_address.to_string(), msg.destination_chain.to_string(), @@ -84,38 +81,38 @@ fn to_msg(msg: &router_api::Message) -> Message { ) } -pub fn to_weighted_signature(sig: &SignerWithSig) -> Result { +pub fn to_weighted_signature(sig: &SignerWithSig) -> Result { let enc_pub_key = to_pub_key(&sig.signer.pub_key)?; let enc_signature = to_signature(&sig.signature)?; - let enc_weight = U256::from_be(to_u256_be(sig.signer.weight.u128())); + let enc_weight = axelar_encoding::types::U256::from_be(to_u256_be(sig.signer.weight.u128())); - Ok(WeightedSignature::new( + Ok(axelar_encoding::types::WeightedSignature::new( enc_pub_key, enc_signature, enc_weight, )) } -fn to_signature(sig: &multisig::key::Signature) -> Result { +fn to_signature(sig: &Signature) -> Result { match sig { - multisig::key::Signature::Ecdsa(_) => unimplemented!(), // Todo: should we implement this in axelar_encoding ? + Signature::Ecdsa(_) => unimplemented!(), // Todo: should we implement this in axelar_encoding ? // Following 2: We are just moving the bytes around, hoping this conversions match. Not sure if `HexBinary` // representation will match here with the decoding part. - multisig::key::Signature::EcdsaRecoverable(r) => { + Signature::EcdsaRecoverable(r) => { let data = r .as_ref() .try_into() .map_err(|e: TryFromSliceError| ContractError::SolEncodingError(e.to_string()))?; - Ok(Signature::EcdsaRecoverable(data)) + Ok(axelar_encoding::types::Signature::EcdsaRecoverable(data)) } - multisig::key::Signature::Ed25519(ed) => { + Signature::Ed25519(ed) => { let data = ed .as_ref() .try_into() .map_err(|e: TryFromSliceError| ContractError::SolEncodingError(e.to_string()))?; - Ok(Signature::new_ed25519(data)) + Ok(axelar_encoding::types::Signature::new_ed25519(data)) } } } From f7f42eee9e24d2283e63b255d3f5c086f89fa2e4 Mon Sep 17 00:00:00 2001 From: eloylp Date: Tue, 11 Jun 2024 18:32:02 +0200 Subject: [PATCH 05/15] Align new naming with rest of rkyv elements --- contracts/multisig-prover/src/encoding/mod.rs | 2 +- .../src/encoding/{sol => rkyv}/mod.rs | 29 ++++++++++++------- contracts/multisig-prover/src/error.rs | 2 +- contracts/multisig-prover/src/payload.rs | 4 +-- 4 files changed, 23 insertions(+), 14 deletions(-) rename contracts/multisig-prover/src/encoding/{sol => rkyv}/mod.rs (82%) diff --git a/contracts/multisig-prover/src/encoding/mod.rs b/contracts/multisig-prover/src/encoding/mod.rs index bbcf9d34c..68a0912c8 100644 --- a/contracts/multisig-prover/src/encoding/mod.rs +++ b/contracts/multisig-prover/src/encoding/mod.rs @@ -1,5 +1,5 @@ pub mod abi; -pub mod sol; +pub mod rkyv; use cosmwasm_schema::cw_serde; diff --git a/contracts/multisig-prover/src/encoding/sol/mod.rs b/contracts/multisig-prover/src/encoding/rkyv/mod.rs similarity index 82% rename from contracts/multisig-prover/src/encoding/sol/mod.rs rename to contracts/multisig-prover/src/encoding/rkyv/mod.rs index 92cacd0a4..e2b06eedf 100644 --- a/contracts/multisig-prover/src/encoding/sol/mod.rs +++ b/contracts/multisig-prover/src/encoding/rkyv/mod.rs @@ -1,8 +1,11 @@ use std::{array::TryFromSliceError, collections::BTreeMap}; - use itertools::Itertools; -use multisig::{key::{PublicKey, Signature}, msg::SignerWithSig, verifier_set::VerifierSet}; +use multisig::{ + key::{PublicKey, Signature}, + msg::SignerWithSig, + verifier_set::VerifierSet, +}; use router_api::Message; use crate::{error::ContractError, payload::Payload}; @@ -21,7 +24,8 @@ pub fn to_worker_set(vs: &VerifierSet) -> Result Result Result { Ok(match pk { - PublicKey::Ecdsa(hb) => { - axelar_encoding::types::PublicKey::new_ecdsa(hb.to_array::()?) - } + PublicKey::Ecdsa(hb) => axelar_encoding::types::PublicKey::new_ecdsa( + hb.to_array::()?, + ), PublicKey::Ed25519(hb) => { axelar_encoding::types::PublicKey::new_ed25519(hb.to_array::()?) } @@ -70,7 +74,10 @@ impl TryFrom<&Payload> for axelar_encoding::types::Payload { } fn to_msg(msg: &Message) -> axelar_encoding::types::Message { - let enc_cc_id = axelar_encoding::types::CrossChainId::new(msg.cc_id.chain.to_string(), msg.cc_id.id.to_string()); + let enc_cc_id = axelar_encoding::types::CrossChainId::new( + msg.cc_id.chain.to_string(), + msg.cc_id.id.to_string(), + ); axelar_encoding::types::Message::new( enc_cc_id, @@ -81,7 +88,9 @@ fn to_msg(msg: &Message) -> axelar_encoding::types::Message { ) } -pub fn to_weighted_signature(sig: &SignerWithSig) -> Result { +pub fn to_weighted_signature( + sig: &SignerWithSig, +) -> Result { let enc_pub_key = to_pub_key(&sig.signer.pub_key)?; let enc_signature = to_signature(&sig.signature)?; let enc_weight = axelar_encoding::types::U256::from_be(to_u256_be(sig.signer.weight.u128())); @@ -103,14 +112,14 @@ fn to_signature(sig: &Signature) -> Result { let data = r .as_ref() .try_into() - .map_err(|e: TryFromSliceError| ContractError::SolEncodingError(e.to_string()))?; + .map_err(|e: TryFromSliceError| ContractError::RkyvEncodingError(e.to_string()))?; Ok(axelar_encoding::types::Signature::EcdsaRecoverable(data)) } Signature::Ed25519(ed) => { let data = ed .as_ref() .try_into() - .map_err(|e: TryFromSliceError| ContractError::SolEncodingError(e.to_string()))?; + .map_err(|e: TryFromSliceError| ContractError::RkyvEncodingError(e.to_string()))?; Ok(axelar_encoding::types::Signature::new_ed25519(data)) } diff --git a/contracts/multisig-prover/src/error.rs b/contracts/multisig-prover/src/error.rs index 6acde3bd2..9327117e2 100644 --- a/contracts/multisig-prover/src/error.rs +++ b/contracts/multisig-prover/src/error.rs @@ -44,7 +44,7 @@ pub enum ContractError { /// Todo, Below error throws: binary operation `==` cannot be applied to type `EncodingError<1024>` /// this is a workaround. #[error("encoding/decoding failure: [0]")] - SolEncodingError(String), + RkyvEncodingError(String), #[error("verifier set has not changed sufficiently since last update")] VerifierSetUnchanged, diff --git a/contracts/multisig-prover/src/payload.rs b/contracts/multisig-prover/src/payload.rs index 406204994..7e05ac454 100644 --- a/contracts/multisig-prover/src/payload.rs +++ b/contracts/multisig-prover/src/payload.rs @@ -12,7 +12,7 @@ use router_api::{CrossChainId, Message}; use crate::{ encoding::{ abi, - sol::{to_weighted_signature, to_worker_set}, + rkyv::{to_weighted_signature, to_worker_set}, Encoder, }, error::ContractError, @@ -93,7 +93,7 @@ impl Payload { enc_signatures, axelar_encoding::types::Payload::try_from(payload)?, ) - .map_err(|e| ContractError::SolEncodingError(e.to_string()))?; + .map_err(|e| ContractError::RkyvEncodingError(e.to_string()))?; Ok(HexBinary::from(bytes)) // Todo, what kind of conversion if any comes from here. Can we expect hex repr directly ? } From 1766b3b2b52f4f9b3d0d40b7847629b78a02b760 Mon Sep 17 00:00:00 2001 From: eloylp Date: Tue, 11 Jun 2024 18:39:00 +0200 Subject: [PATCH 06/15] Remove confusing, wrong comment --- contracts/multisig-prover/src/payload.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/multisig-prover/src/payload.rs b/contracts/multisig-prover/src/payload.rs index 7e05ac454..ad5269134 100644 --- a/contracts/multisig-prover/src/payload.rs +++ b/contracts/multisig-prover/src/payload.rs @@ -95,7 +95,7 @@ impl Payload { ) .map_err(|e| ContractError::RkyvEncodingError(e.to_string()))?; - Ok(HexBinary::from(bytes)) // Todo, what kind of conversion if any comes from here. Can we expect hex repr directly ? + Ok(HexBinary::from(bytes)) } } } From 23be5851636afac76128f097f0353aa15bc3b7fe Mon Sep 17 00:00:00 2001 From: eloylp Date: Wed, 12 Jun 2024 12:38:40 +0200 Subject: [PATCH 07/15] Align encoding integration with latest changes in encoding crate - Use the new crate renaming `axlelar-rkyv-encoding` - Import the create directly from the development branch - Use now public constants - Change to little endian everywhere --- Cargo.lock | 6 +- Cargo.toml | 2 +- contracts/multisig-prover/Cargo.toml | 2 +- .../multisig-prover/src/encoding/rkyv/mod.rs | 65 ++++++++++--------- contracts/multisig-prover/src/payload.rs | 8 +-- 5 files changed, 42 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 53fc5dab7..1b9b5d98c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -747,9 +747,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] -name = "axelar-encoding" +name = "axelar-rkyv-encoding" version = "0.1.0" -source = "git+ssh://git@github.com/eigerco/solana-axelar-internal.git?branch=main#4321554e61d8f35fcb8b259b7c3be02ef01ec3b6" +source = "git+ssh://git@github.com/eigerco/solana-axelar-internal.git?branch=gateway/axelar-encoding#bd7ffa32020a52d5fc13398338cef7f208fb2845" dependencies = [ "rkyv", "sha3 0.10.8", @@ -5149,7 +5149,7 @@ dependencies = [ "alloy-primitives", "alloy-sol-types", "anyhow", - "axelar-encoding", + "axelar-rkyv-encoding", "axelar-wasm-std", "axelar-wasm-std-derive", "bcs", diff --git a/Cargo.toml b/Cargo.toml index ee9da4014..2bec5c33e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,7 @@ serde_json = "1.0.89" schemars = "0.8.10" sha3 = { version = "0.10.8", default-features = false, features = [] } signature-verifier-api = { version = "^0.1.0", path = "packages/signature-verifier-api" } -axelar-encoding = { git = "ssh://git@github.com/eigerco/solana-axelar-internal.git", branch = "main"} +axelar-rkyv-encoding = { git = "ssh://git@github.com/eigerco/solana-axelar-internal.git", branch = "gateway/axelar-encoding"} [workspace.lints.clippy] arithmetic_side_effects = "deny" diff --git a/contracts/multisig-prover/Cargo.toml b/contracts/multisig-prover/Cargo.toml index 30713f7b5..72ccb1100 100644 --- a/contracts/multisig-prover/Cargo.toml +++ b/contracts/multisig-prover/Cargo.toml @@ -59,7 +59,7 @@ service-registry = { workspace = true } sha3 = { workspace = true } thiserror = { workspace = true } voting-verifier = { workspace = true, features = ["library"] } -axelar-encoding = { workspace = true } +axelar-rkyv-encoding = { workspace = true } [dev-dependencies] anyhow = "1.0" diff --git a/contracts/multisig-prover/src/encoding/rkyv/mod.rs b/contracts/multisig-prover/src/encoding/rkyv/mod.rs index e2b06eedf..d1906b620 100644 --- a/contracts/multisig-prover/src/encoding/rkyv/mod.rs +++ b/contracts/multisig-prover/src/encoding/rkyv/mod.rs @@ -1,5 +1,6 @@ use std::{array::TryFromSliceError, collections::BTreeMap}; +use axelar_rkyv_encoding::types::{ECDSA_COMPRESSED_PUBKEY_LEN, ED25519_PUBKEY_LEN}; use itertools::Itertools; use multisig::{ key::{PublicKey, Signature}, @@ -10,13 +11,10 @@ use router_api::Message; use crate::{error::ContractError, payload::Payload}; -const ECDSA_COMPRESSED_PUBKEY_LEN: usize = 33; // this should be probably a public constant in axelar_encoding. -const ED25519_PUBKEY_LEN: usize = 32; // this should be probably a public constant in axelar_encoding. - type Result = core::result::Result; -pub fn to_worker_set(vs: &VerifierSet) -> Result { - let mut signers: BTreeMap = BTreeMap::new(); +pub fn to_worker_set(vs: &VerifierSet) -> Result { + let mut signers: BTreeMap = BTreeMap::new(); vs.signers .iter() @@ -25,61 +23,61 @@ pub fn to_worker_set(vs: &VerifierSet) -> Result Result { +fn to_pub_key(pk: &PublicKey) -> Result { Ok(match pk { - PublicKey::Ecdsa(hb) => axelar_encoding::types::PublicKey::new_ecdsa( + PublicKey::Ecdsa(hb) => axelar_rkyv_encoding::types::PublicKey::new_ecdsa( hb.to_array::()?, ), - PublicKey::Ed25519(hb) => { - axelar_encoding::types::PublicKey::new_ed25519(hb.to_array::()?) - } + PublicKey::Ed25519(hb) => axelar_rkyv_encoding::types::PublicKey::new_ed25519( + hb.to_array::()?, + ), }) } // Fits a u128 into a u256 in big endian representation. fn to_u256_be(u: u128) -> [u8; 32] { let mut uin256 = [0u8; 32]; - uin256[16..32].copy_from_slice(&u.to_be_bytes()); + uin256[0..16].copy_from_slice(&u.to_le_bytes()); uin256 } -impl TryFrom<&Payload> for axelar_encoding::types::Payload { +impl TryFrom<&Payload> for axelar_rkyv_encoding::types::Payload { type Error = ContractError; fn try_from(value: &Payload) -> std::result::Result { Ok(match value { - Payload::Messages(msgs) => { - axelar_encoding::types::Payload::new_messages(msgs.iter().map(to_msg).collect_vec()) - } + Payload::Messages(msgs) => axelar_rkyv_encoding::types::Payload::new_messages( + msgs.iter().map(to_msg).collect_vec(), + ), Payload::VerifierSet(vs) => { - axelar_encoding::types::Payload::new_worker_set(to_worker_set(&vs)?) + axelar_rkyv_encoding::types::Payload::new_worker_set(to_worker_set(&vs)?) } }) } } -fn to_msg(msg: &Message) -> axelar_encoding::types::Message { - let enc_cc_id = axelar_encoding::types::CrossChainId::new( +fn to_msg(msg: &Message) -> axelar_rkyv_encoding::types::Message { + let enc_cc_id = axelar_rkyv_encoding::types::CrossChainId::new( msg.cc_id.chain.to_string(), msg.cc_id.id.to_string(), ); - axelar_encoding::types::Message::new( + axelar_rkyv_encoding::types::Message::new( enc_cc_id, msg.source_address.to_string(), msg.destination_chain.to_string(), @@ -90,19 +88,20 @@ fn to_msg(msg: &Message) -> axelar_encoding::types::Message { pub fn to_weighted_signature( sig: &SignerWithSig, -) -> Result { +) -> Result { let enc_pub_key = to_pub_key(&sig.signer.pub_key)?; let enc_signature = to_signature(&sig.signature)?; - let enc_weight = axelar_encoding::types::U256::from_be(to_u256_be(sig.signer.weight.u128())); + let enc_weight = + axelar_rkyv_encoding::types::U256::from_le(to_u256_be(sig.signer.weight.u128())); - Ok(axelar_encoding::types::WeightedSignature::new( + Ok(axelar_rkyv_encoding::types::WeightedSignature::new( enc_pub_key, enc_signature, enc_weight, )) } -fn to_signature(sig: &Signature) -> Result { +fn to_signature(sig: &Signature) -> Result { match sig { Signature::Ecdsa(_) => unimplemented!(), // Todo: should we implement this in axelar_encoding ? @@ -113,7 +112,9 @@ fn to_signature(sig: &Signature) -> Result { .as_ref() .try_into() .map_err(|e: TryFromSliceError| ContractError::RkyvEncodingError(e.to_string()))?; - Ok(axelar_encoding::types::Signature::EcdsaRecoverable(data)) + Ok(axelar_rkyv_encoding::types::Signature::EcdsaRecoverable( + data, + )) } Signature::Ed25519(ed) => { let data = ed @@ -121,7 +122,7 @@ fn to_signature(sig: &Signature) -> Result { .try_into() .map_err(|e: TryFromSliceError| ContractError::RkyvEncodingError(e.to_string()))?; - Ok(axelar_encoding::types::Signature::new_ed25519(data)) + Ok(axelar_rkyv_encoding::types::Signature::new_ed25519(data)) } } } @@ -131,11 +132,11 @@ mod tests { use super::*; #[test] - fn conversion_to_u256_be_works() { + fn conversion_to_u256_le_works() { let integer = to_u256_be(u128::MAX); let expected = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]; assert_eq!(expected, integer); } diff --git a/contracts/multisig-prover/src/payload.rs b/contracts/multisig-prover/src/payload.rs index ad5269134..ab591f67a 100644 --- a/contracts/multisig-prover/src/payload.rs +++ b/contracts/multisig-prover/src/payload.rs @@ -51,10 +51,10 @@ impl Payload { match encoder { Encoder::Abi => abi::payload_hash_to_sign(domain_separator, cur_verifier_set, self), Encoder::Bcs => todo!(), - Encoder::Rkyv => Ok(axelar_encoding::hash_payload( + Encoder::Rkyv => Ok(axelar_rkyv_encoding::hash_payload( &domain_separator, &to_worker_set(cur_verifier_set)?, - &axelar_encoding::types::Payload::try_from(self)?, + &axelar_rkyv_encoding::types::Payload::try_from(self)?, )), } } @@ -87,11 +87,11 @@ impl Payload { enc_signatures.push(to_weighted_signature(&s)?) } - let bytes = axelar_encoding::encode::<1024>( + let bytes = axelar_rkyv_encoding::encode::<1024>( // Todo reason about this "1024" magic number. &to_worker_set(&verifier_set)?, enc_signatures, - axelar_encoding::types::Payload::try_from(payload)?, + axelar_rkyv_encoding::types::Payload::try_from(payload)?, ) .map_err(|e| ContractError::RkyvEncodingError(e.to_string()))?; From a30bd5645cb78d678c68b46e6f11e46263fbbf99 Mon Sep 17 00:00:00 2001 From: eloylp Date: Wed, 12 Jun 2024 13:13:12 +0200 Subject: [PATCH 08/15] Align function name with what it does --- contracts/multisig-prover/src/encoding/rkyv/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/contracts/multisig-prover/src/encoding/rkyv/mod.rs b/contracts/multisig-prover/src/encoding/rkyv/mod.rs index d1906b620..cca67b621 100644 --- a/contracts/multisig-prover/src/encoding/rkyv/mod.rs +++ b/contracts/multisig-prover/src/encoding/rkyv/mod.rs @@ -23,7 +23,7 @@ pub fn to_worker_set(vs: &VerifierSet) -> Result Result Result } // Fits a u128 into a u256 in big endian representation. -fn to_u256_be(u: u128) -> [u8; 32] { +fn to_u256_le(u: u128) -> [u8; 32] { let mut uin256 = [0u8; 32]; uin256[0..16].copy_from_slice(&u.to_le_bytes()); uin256 @@ -92,7 +92,7 @@ pub fn to_weighted_signature( let enc_pub_key = to_pub_key(&sig.signer.pub_key)?; let enc_signature = to_signature(&sig.signature)?; let enc_weight = - axelar_rkyv_encoding::types::U256::from_le(to_u256_be(sig.signer.weight.u128())); + axelar_rkyv_encoding::types::U256::from_le(to_u256_le(sig.signer.weight.u128())); Ok(axelar_rkyv_encoding::types::WeightedSignature::new( enc_pub_key, @@ -133,7 +133,7 @@ mod tests { #[test] fn conversion_to_u256_le_works() { - let integer = to_u256_be(u128::MAX); + let integer = to_u256_le(u128::MAX); let expected = [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, From c38f99730bac74d4671001b6c8e5c4336317ba8c Mon Sep 17 00:00:00 2001 From: eloylp Date: Wed, 12 Jun 2024 17:43:22 +0200 Subject: [PATCH 09/15] Fix code comment --- contracts/multisig-prover/src/encoding/rkyv/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/multisig-prover/src/encoding/rkyv/mod.rs b/contracts/multisig-prover/src/encoding/rkyv/mod.rs index cca67b621..bb8b356b6 100644 --- a/contracts/multisig-prover/src/encoding/rkyv/mod.rs +++ b/contracts/multisig-prover/src/encoding/rkyv/mod.rs @@ -50,7 +50,7 @@ fn to_pub_key(pk: &PublicKey) -> Result }) } -// Fits a u128 into a u256 in big endian representation. +// Fits a u128 into a u256 in little endian representation. fn to_u256_le(u: u128) -> [u8; 32] { let mut uin256 = [0u8; 32]; uin256[0..16].copy_from_slice(&u.to_le_bytes()); From 6ec5af14ef1526f57bb6e4fd333771840bcf2cec Mon Sep 17 00:00:00 2001 From: eloylp Date: Thu, 13 Jun 2024 18:38:35 +0200 Subject: [PATCH 10/15] Convert non recoverable keys into recoverable ones --- .../multisig-prover/src/encoding/rkyv/mod.rs | 32 ++++++++++++++++--- contracts/multisig-prover/src/payload.rs | 2 +- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/contracts/multisig-prover/src/encoding/rkyv/mod.rs b/contracts/multisig-prover/src/encoding/rkyv/mod.rs index bb8b356b6..fef406b75 100644 --- a/contracts/multisig-prover/src/encoding/rkyv/mod.rs +++ b/contracts/multisig-prover/src/encoding/rkyv/mod.rs @@ -1,5 +1,3 @@ -use std::{array::TryFromSliceError, collections::BTreeMap}; - use axelar_rkyv_encoding::types::{ECDSA_COMPRESSED_PUBKEY_LEN, ED25519_PUBKEY_LEN}; use itertools::Itertools; use multisig::{ @@ -8,6 +6,7 @@ use multisig::{ verifier_set::VerifierSet, }; use router_api::Message; +use std::{array::TryFromSliceError, collections::BTreeMap}; use crate::{error::ContractError, payload::Payload}; @@ -88,9 +87,10 @@ fn to_msg(msg: &Message) -> axelar_rkyv_encoding::types::Message { pub fn to_weighted_signature( sig: &SignerWithSig, + payload_hash: &[u8; 32], ) -> Result { let enc_pub_key = to_pub_key(&sig.signer.pub_key)?; - let enc_signature = to_signature(&sig.signature)?; + let enc_signature = to_signature(&sig.signature, &sig.signer.pub_key, payload_hash)?; let enc_weight = axelar_rkyv_encoding::types::U256::from_le(to_u256_le(sig.signer.weight.u128())); @@ -101,9 +101,24 @@ pub fn to_weighted_signature( )) } -fn to_signature(sig: &Signature) -> Result { +fn to_signature( + sig: &Signature, + pub_key: &PublicKey, + payload_hash: &[u8; 32], +) -> Result { match sig { - Signature::Ecdsa(_) => unimplemented!(), // Todo: should we implement this in axelar_encoding ? + Signature::Ecdsa(nonrec) => { + let recov = nonrec + .to_recoverable(payload_hash, pub_key, add27) + .map_err(|e| ContractError::RkyvEncodingError(e.to_string()))?; + let data = recov + .as_ref() + .try_into() + .map_err(|e: TryFromSliceError| ContractError::RkyvEncodingError(e.to_string()))?; + Ok(axelar_rkyv_encoding::types::Signature::EcdsaRecoverable( + data, + )) + } // Following 2: We are just moving the bytes around, hoping this conversions match. Not sure if `HexBinary` // representation will match here with the decoding part. @@ -127,6 +142,13 @@ fn to_signature(sig: &Signature) -> Result u8 { + recovery_byte + .to_byte() + .checked_add(27) + .expect("overflow when adding 27 to recovery byte") +} + #[cfg(test)] mod tests { use super::*; diff --git a/contracts/multisig-prover/src/payload.rs b/contracts/multisig-prover/src/payload.rs index ab591f67a..4511d3234 100644 --- a/contracts/multisig-prover/src/payload.rs +++ b/contracts/multisig-prover/src/payload.rs @@ -84,7 +84,7 @@ impl Payload { let mut enc_signatures = Vec::new(); for s in signers_with_sigs { - enc_signatures.push(to_weighted_signature(&s)?) + enc_signatures.push(to_weighted_signature(&s, &payload_hash)?) } let bytes = axelar_rkyv_encoding::encode::<1024>( From 236a43ea978107dc10850d09aed2b4e83c3da222 Mon Sep 17 00:00:00 2001 From: eloylp Date: Thu, 13 Jun 2024 19:09:39 +0200 Subject: [PATCH 11/15] refactor: Wrap common conversion logic --- .../multisig-prover/src/encoding/rkyv/mod.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/contracts/multisig-prover/src/encoding/rkyv/mod.rs b/contracts/multisig-prover/src/encoding/rkyv/mod.rs index fef406b75..3ef59ea5c 100644 --- a/contracts/multisig-prover/src/encoding/rkyv/mod.rs +++ b/contracts/multisig-prover/src/encoding/rkyv/mod.rs @@ -1,7 +1,7 @@ use axelar_rkyv_encoding::types::{ECDSA_COMPRESSED_PUBKEY_LEN, ED25519_PUBKEY_LEN}; use itertools::Itertools; use multisig::{ - key::{PublicKey, Signature}, + key::{PublicKey, Recoverable, Signature}, msg::SignerWithSig, verifier_set::VerifierSet, }; @@ -111,24 +111,16 @@ fn to_signature( let recov = nonrec .to_recoverable(payload_hash, pub_key, add27) .map_err(|e| ContractError::RkyvEncodingError(e.to_string()))?; - let data = recov - .as_ref() - .try_into() - .map_err(|e: TryFromSliceError| ContractError::RkyvEncodingError(e.to_string()))?; Ok(axelar_rkyv_encoding::types::Signature::EcdsaRecoverable( - data, + recoverable_ecdsa_to_array(&recov)?, )) } // Following 2: We are just moving the bytes around, hoping this conversions match. Not sure if `HexBinary` // representation will match here with the decoding part. Signature::EcdsaRecoverable(r) => { - let data = r - .as_ref() - .try_into() - .map_err(|e: TryFromSliceError| ContractError::RkyvEncodingError(e.to_string()))?; Ok(axelar_rkyv_encoding::types::Signature::EcdsaRecoverable( - data, + recoverable_ecdsa_to_array(r)?, )) } Signature::Ed25519(ed) => { @@ -149,6 +141,11 @@ fn add27(recovery_byte: k256::ecdsa::RecoveryId) -> u8 { .expect("overflow when adding 27 to recovery byte") } +fn recoverable_ecdsa_to_array(rec: &Recoverable) -> Result<[u8; 65]> { + rec.as_ref() + .try_into() + .map_err(|e: TryFromSliceError| ContractError::RkyvEncodingError(e.to_string())) +} #[cfg(test)] mod tests { use super::*; From 394afe73a6f1561a625abbfb4e044d7bf4b4b16c Mon Sep 17 00:00:00 2001 From: eloylp Date: Thu, 13 Jun 2024 19:29:51 +0200 Subject: [PATCH 12/15] Remove no longer needed comments --- contracts/multisig-prover/src/encoding/rkyv/mod.rs | 3 --- contracts/multisig-prover/src/error.rs | 2 -- 2 files changed, 5 deletions(-) diff --git a/contracts/multisig-prover/src/encoding/rkyv/mod.rs b/contracts/multisig-prover/src/encoding/rkyv/mod.rs index 3ef59ea5c..48ee57918 100644 --- a/contracts/multisig-prover/src/encoding/rkyv/mod.rs +++ b/contracts/multisig-prover/src/encoding/rkyv/mod.rs @@ -115,9 +115,6 @@ fn to_signature( recoverable_ecdsa_to_array(&recov)?, )) } - - // Following 2: We are just moving the bytes around, hoping this conversions match. Not sure if `HexBinary` - // representation will match here with the decoding part. Signature::EcdsaRecoverable(r) => { Ok(axelar_rkyv_encoding::types::Signature::EcdsaRecoverable( recoverable_ecdsa_to_array(r)?, diff --git a/contracts/multisig-prover/src/error.rs b/contracts/multisig-prover/src/error.rs index 9327117e2..cae0c6a47 100644 --- a/contracts/multisig-prover/src/error.rs +++ b/contracts/multisig-prover/src/error.rs @@ -41,8 +41,6 @@ pub enum ContractError { #[error(transparent)] BcsError(#[from] bcs::Error), - /// Todo, Below error throws: binary operation `==` cannot be applied to type `EncodingError<1024>` - /// this is a workaround. #[error("encoding/decoding failure: [0]")] RkyvEncodingError(String), From 12d3991763894dec2538ce96d67bc829975f9c88 Mon Sep 17 00:00:00 2001 From: eloylp Date: Thu, 13 Jun 2024 19:30:33 +0200 Subject: [PATCH 13/15] improve error message --- contracts/multisig-prover/src/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/multisig-prover/src/error.rs b/contracts/multisig-prover/src/error.rs index cae0c6a47..173c41302 100644 --- a/contracts/multisig-prover/src/error.rs +++ b/contracts/multisig-prover/src/error.rs @@ -41,7 +41,7 @@ pub enum ContractError { #[error(transparent)] BcsError(#[from] bcs::Error), - #[error("encoding/decoding failure: [0]")] + #[error("Rkyv encoding/decoding failure: [0]")] RkyvEncodingError(String), #[error("verifier set has not changed sufficiently since last update")] From 3659af1f7d21a8c76692f2ea2228019375f3356d Mon Sep 17 00:00:00 2001 From: eloylp Date: Tue, 2 Jul 2024 17:10:51 +0200 Subject: [PATCH 14/15] Use latest changes from rkyv encoding crate. --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- contracts/multisig-prover/src/encoding/rkyv/mod.rs | 14 ++++++-------- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7bbd3d22d..7a5a4afd2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -759,7 +759,7 @@ dependencies = [ [[package]] name = "axelar-rkyv-encoding" version = "0.1.0" -source = "git+ssh://git@github.com/eigerco/solana-axelar-internal.git?branch=main#eb9d5a7fcf2469256b275befa9ca4d4aaa21743c" +source = "git+https://git@github.com/eigerco/solana-axelar.git?branch=main#681ce307bfaa7b8649e05b1dabd1d4437b5ea4b2" dependencies = [ "bnum", "bs58 0.5.1", @@ -5342,7 +5342,7 @@ name = "multisig-prover" version = "0.5.0" dependencies = [ "anyhow", - "axelar-rkyv-encoding 0.1.0 (git+ssh://git@github.com/eigerco/solana-axelar-internal.git?branch=main)", + "axelar-rkyv-encoding 0.1.0 (git+https://git@github.com/eigerco/solana-axelar.git?branch=main)", "axelar-wasm-std", "axelar-wasm-std-derive", "bcs", diff --git a/Cargo.toml b/Cargo.toml index ea9f36cf2..ad27b7d9c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,7 @@ serde_json = "1.0.89" schemars = "0.8.10" sha3 = { version = "0.10.8", default-features = false, features = [] } signature-verifier-api = { version = "^0.1.0", path = "packages/signature-verifier-api" } -axelar-rkyv-encoding = { git = "ssh://git@github.com/eigerco/solana-axelar-internal.git", branch = "main"} +axelar-rkyv-encoding = { git = "https://git@github.com/eigerco/solana-axelar.git", branch = "main"} ethers-contract = { version = "2.0.14", default-features = false, features = ["abigen"] } ethers-core = "2.0.14" tokio = "1.38.0" diff --git a/contracts/multisig-prover/src/encoding/rkyv/mod.rs b/contracts/multisig-prover/src/encoding/rkyv/mod.rs index 43f8edd61..fe1d09556 100644 --- a/contracts/multisig-prover/src/encoding/rkyv/mod.rs +++ b/contracts/multisig-prover/src/encoding/rkyv/mod.rs @@ -13,21 +13,19 @@ use crate::{error::ContractError, payload::Payload}; type Result = core::result::Result; pub fn to_verifier_set(vs: &VerifierSet) -> Result { - let mut signers: BTreeMap = BTreeMap::new(); + let mut signers: BTreeMap< + axelar_rkyv_encoding::types::PublicKey, + axelar_rkyv_encoding::types::U256, + > = BTreeMap::new(); vs.signers .iter() - .try_for_each(|(address, signer)| -> Result<()> { - let enc_signer = signer.address.to_string(); + .try_for_each(|(_, signer)| -> Result<()> { let enc_pubkey = to_pub_key(&signer.pub_key)?; - let enc_weight = axelar_rkyv_encoding::types::U256::from_le(to_u256_le(signer.weight.u128())); - signers.insert( - address.clone(), - axelar_rkyv_encoding::types::Signer::new(enc_signer, enc_pubkey, enc_weight), - ); + signers.insert(enc_pubkey, enc_weight); Ok(()) })?; From 0814a1c8bac828cefa681d45799d6646b4099768 Mon Sep 17 00:00:00 2001 From: eloylp Date: Tue, 2 Jul 2024 17:11:05 +0200 Subject: [PATCH 15/15] remove unusued import --- contracts/multisig-prover/src/payload.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/contracts/multisig-prover/src/payload.rs b/contracts/multisig-prover/src/payload.rs index 25045f3b6..ced87fcb8 100644 --- a/contracts/multisig-prover/src/payload.rs +++ b/contracts/multisig-prover/src/payload.rs @@ -2,7 +2,6 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::{from_json, HexBinary, StdResult}; use cw_storage_plus::{Key, KeyDeserialize, PrimaryKey}; use error_stack::Result; -use itertools::Itertools; use sha3::{Digest, Keccak256}; use axelar_wasm_std::hash::Hash;