diff --git a/contracts/multisig-prover/src/encoding/bcs.rs b/contracts/multisig-prover/src/encoding/bcs.rs index b8bccd9a9..7605da1d6 100644 --- a/contracts/multisig-prover/src/encoding/bcs.rs +++ b/contracts/multisig-prover/src/encoding/bcs.rs @@ -1,14 +1,50 @@ use bcs::to_bytes; use cosmwasm_std::{HexBinary, Uint256}; use itertools::Itertools; +use multisig::{key::Signature, msg::Signer}; -use crate::error::ContractError; +use crate::{error::ContractError, types::Operator}; use super::Data; // TODO: all of the public functions in this file should be moved to a trait, // that has an abi and bcs implementation (and possibly others) +#[allow(dead_code)] +fn encode_proof( + quorum: Uint256, + signers: Vec<(Signer, Option)>, +) -> Result { + let mut operators = make_operators_with_sigs(signers); + operators.sort(); // gateway requires operators to be sorted + + let (addresses, weights, signatures): (Vec<_>, Vec<_>, Vec<_>) = operators + .iter() + .map(|op| { + ( + op.address.to_vec(), + u256_to_u128(op.weight), + op.signature.as_ref().map(|sig| sig.as_ref().to_vec()), + ) + }) + .multiunzip(); + + let signatures: Vec> = signatures.into_iter().flatten().collect(); + let quorum = u256_to_u128(quorum); + Ok(to_bytes(&(addresses, weights, quorum, signatures))?.into()) +} + +fn make_operators_with_sigs(signers_with_sigs: Vec<(Signer, Option)>) -> Vec { + signers_with_sigs + .into_iter() + .map(|(signer, sig)| Operator { + address: signer.pub_key.into(), + weight: signer.weight, + signature: sig, + }) + .collect() +} + pub fn command_params( source_chain: String, source_address: String, @@ -41,10 +77,6 @@ pub fn command_params( .into()) } -fn u256_to_u64(chain_id: Uint256) -> u64 { - chain_id.to_string().parse().expect("chain_id is invalid") -} - fn make_command_id(command_id: &HexBinary) -> [u8; 32] { // command-ids are fixed length sequences command_id @@ -79,28 +111,118 @@ pub fn encode(data: &Data) -> HexBinary { .into() } +fn u256_to_u128(val: Uint256) -> u128 { + val.to_string().parse().expect("value is larger than u128") +} + +fn u256_to_u64(chain_id: Uint256) -> u64 { + chain_id + .to_string() + .parse() + .expect("value is larger than u64") +} + #[cfg(test)] mod test { use std::vec; use bcs::from_bytes; - use cosmwasm_std::{HexBinary, Uint256}; + use cosmwasm_std::{Addr, HexBinary, Uint256}; + use multisig::{ + key::{PublicKey, Signature}, + msg::Signer, + }; use crate::{ encoding::{ - bcs::{command_params, encode, make_command_id, u256_to_u64}, + bcs::{ + command_params, encode, encode_proof, make_command_id, u256_to_u128, u256_to_u64, + }, Data, }, types::Command, }; + #[test] + fn test_u256_to_u128() { + let val = u128::MAX; + assert_eq!(val, u256_to_u128(Uint256::from(val))); + } + #[test] fn test_chain_id_as_u64() { let chain_id = 1u64; assert_eq!(chain_id, u256_to_u64(Uint256::from(chain_id as u128))); } + #[test] + #[should_panic] + fn test_u256_to_u128_fails() { + let _ = u256_to_u128(Uint256::MAX); + } + + #[test] + fn test_encode_proof() { + let signers = vec![ + (Signer { + address: Addr::unchecked("axelarvaloper1ff675m593vve8yh82lzhdnqfpu7m23cxstr6h4"), + weight: Uint256::from(10u128), + pub_key: PublicKey::Ecdsa( + HexBinary::from_hex( + "03c6ddb0fcee7b528da1ef3c9eed8d51eeacd7cc28a8baa25c33037c5562faa6e4", + ) + .unwrap(), + ), + }, + Some(Signature::Ecdsa( + HexBinary::from_hex("283786d844a7c4d1d424837074d0c8ec71becdcba4dd42b5307cb543a0e2c8b81c10ad541defd5ce84d2a608fc454827d0b65b4865c8192a2ea1736a5c4b72021b").unwrap()))), + (Signer { + address: Addr::unchecked("axelarvaloper1x86a8prx97ekkqej2x636utrdu23y8wupp9gk5"), + weight: Uint256::from(10u128), + pub_key: PublicKey::Ecdsa( + HexBinary::from_hex( + "03d123ce370b163acd576be0e32e436bb7e63262769881d35fa3573943bf6c6f81", + ) + .unwrap(), + ), + }, + Some(Signature::Ecdsa( + HexBinary::from_hex("283786d844a7c4d1d424837074d0c8ec71becdcba4dd42b5307cb543a0e2c8b81c10ad541defd5ce84d2a608fc454827d0b65b4865c8192a2ea1736a5c4b72021b").unwrap())))]; + + let quorum = Uint256::from(10u128); + let proof = encode_proof(quorum, signers.clone()); + + assert!(proof.is_ok()); + let proof = proof.unwrap(); + let decoded_proof: Result<(Vec>, Vec, u128, Vec>), _> = + from_bytes(&proof); + assert!(decoded_proof.is_ok()); + let (operators, weights, quorum_decoded, signatures): ( + Vec>, + Vec, + u128, + Vec>, + ) = decoded_proof.unwrap(); + + assert_eq!(operators.len(), signers.len()); + assert_eq!(weights.len(), signers.len()); + assert_eq!(signatures.len(), signers.len()); + assert_eq!(quorum_decoded, 10u128); + + for i in 0..signers.len() { + assert_eq!( + operators[i], + HexBinary::from(signers[i].0.pub_key.clone()).to_vec() + ); + assert_eq!(weights[i], 10u128); + assert_eq!( + signatures[i], + HexBinary::from(signers[i].1.clone().unwrap()).to_vec() + ); + } + } + #[test] #[should_panic] fn test_chain_id_as_u64_fails() {