From 6a9a51969736c6fb138f9fa928a97159cb8ae0d5 Mon Sep 17 00:00:00 2001 From: Feliciss <10203-feliciss@users.noreply.0xacab.org> Date: Mon, 29 Jul 2024 23:37:44 +0900 Subject: [PATCH] [gh-2295] add derive_multisig_xonly_pubkey_from_public_keys and derive_bitcoin_taproot_address_from_multisig_xonly_pubkey. --- Cargo.lock | 28 ++++++ Cargo.toml | 1 + frameworks/rooch-framework/Cargo.toml | 1 + .../rooch-framework/doc/bitcoin_address.md | 20 ++++- .../sources/address_type/bitcoin_address.move | 40 ++++++++- .../natives/gas_parameter/bitcoin_address.rs | 6 +- .../rooch_framework/bitcoin_address.rs | 90 ++++++++++++++++--- 7 files changed, 168 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 12915e6c2c..28e6ff803c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7128,6 +7128,21 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" +[[package]] +name = "musig2" +version = "0.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bed08befaac75bfb31ca5e87678c4e8490bcd21d0c98ccb4f12f4065a7567e83" +dependencies = [ + "base16ct 0.2.0", + "hmac", + "once_cell", + "secp", + "secp256k1 0.28.2", + "sha2 0.10.8", + "subtle", +] + [[package]] name = "named-lock" version = "0.2.0" @@ -9543,6 +9558,7 @@ dependencies = [ "moveos-stdlib", "moveos-types", "moveos-verifier", + "musig2", "once_cell", "rooch-types", "serde 1.0.204", @@ -10753,6 +10769,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "secp" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c4754628ff9006f80c6abd1cd1e88c5ca6f5a60eab151ad2e16268aab3514d0" +dependencies = [ + "base16ct 0.2.0", + "once_cell", + "secp256k1 0.28.2", + "subtle", +] + [[package]] name = "secp256k1" version = "0.27.0" diff --git a/Cargo.toml b/Cargo.toml index b0bfe0f16a..d6ea2f43e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -318,6 +318,7 @@ fastcrypto-zkp = { version = "0.1.3" } function_name = { version = "0.3.0" } rustc-hash = { version = "2.0.0" } xorf = { version = "0.11.0" } +musig2 = { version = "0.0.11" } # Note: the BEGIN and END comments below are required for external tooling. Do not remove. # BEGIN MOVE DEPENDENCIES diff --git a/frameworks/rooch-framework/Cargo.toml b/frameworks/rooch-framework/Cargo.toml index 37cd9eeedb..f0f0327b3d 100644 --- a/frameworks/rooch-framework/Cargo.toml +++ b/frameworks/rooch-framework/Cargo.toml @@ -28,6 +28,7 @@ smallvec = { workspace = true } hex = { workspace = true } tracing = { workspace = true } bitcoin = { workspace = true } +musig2 = { workspace = true } move-binary-format = { workspace = true } move-bytecode-utils = { workspace = true } diff --git a/frameworks/rooch-framework/doc/bitcoin_address.md b/frameworks/rooch-framework/doc/bitcoin_address.md index f303c14901..d788176c9c 100644 --- a/frameworks/rooch-framework/doc/bitcoin_address.md +++ b/frameworks/rooch-framework/doc/bitcoin_address.md @@ -21,7 +21,8 @@ - [Function `verify_with_public_key`](#0x3_bitcoin_address_verify_with_public_key) - [Function `to_rooch_address`](#0x3_bitcoin_address_to_rooch_address) - [Function `verify_bitcoin_address_with_public_key`](#0x3_bitcoin_address_verify_bitcoin_address_with_public_key) -- [Function `derive_multi_sign_address`](#0x3_bitcoin_address_derive_multi_sign_address) +- [Function `derive_multisig_xonly_pubkey_from_public_keys`](#0x3_bitcoin_address_derive_multisig_xonly_pubkey_from_public_keys) +- [Function `derive_bitcoin_taproot_address_from_multisig_xonly_pubkey`](#0x3_bitcoin_address_derive_bitcoin_taproot_address_from_multisig_xonly_pubkey)
use 0x1::string;
@@ -287,11 +288,22 @@ Empty address is a special address that is used to if we parse address failed fr
 
 
 
-
+
 
-## Function `derive_multi_sign_address`
+## Function `derive_multisig_xonly_pubkey_from_public_keys`
 
 
 
-
public fun derive_multi_sign_address(public_keys: &vector<vector<u8>>, threshold: u64): bitcoin_address::BitcoinAddress
+
public fun derive_multisig_xonly_pubkey_from_public_keys(public_keys: &vector<vector<u8>>, threshold: u64): vector<u8>
+
+ + + + + +## Function `derive_bitcoin_taproot_address_from_multisig_xonly_pubkey` + + + +
public fun derive_bitcoin_taproot_address_from_multisig_xonly_pubkey(xonly_pubkey: &vector<u8>): bitcoin_address::BitcoinAddress
 
diff --git a/frameworks/rooch-framework/sources/address_type/bitcoin_address.move b/frameworks/rooch-framework/sources/address_type/bitcoin_address.move index 0331ab6d03..7d48431b6d 100644 --- a/frameworks/rooch-framework/sources/address_type/bitcoin_address.move +++ b/frameworks/rooch-framework/sources/address_type/bitcoin_address.move @@ -108,8 +108,11 @@ module rooch_framework::bitcoin_address { // verify bitcoin address according to the pk bytes public native fun verify_bitcoin_address_with_public_key(bitcoin_addr: &BitcoinAddress, pk: &vector): bool; - // derive multi signature address from public keys - public native fun derive_multi_sign_address(public_keys: &vector>, threshold: u64): BitcoinAddress; + // derive multisig xonly public key from public keys + public native fun derive_multisig_xonly_pubkey_from_public_keys(public_keys: &vector>, threshold: u64): vector; + + // derive bitcoin taproot address from the multisig xonly public key + public native fun derive_bitcoin_taproot_address_from_multisig_xonly_pubkey(xonly_pubkey: &vector): BitcoinAddress; /// Parse the Bitcoin address string bytes to Move BitcoinAddress native fun parse(raw_addr: &vector): BitcoinAddress; @@ -150,4 +153,37 @@ module rooch_framework::bitcoin_address { let pk = x"038e3d29b653e40f5b620f9443ee05222d1e40be58f544b6fed3d464edd54db884"; assert!(!verify_with_public_key(&addr, &pk), 1004); } + + #[test] + fun test_derive_multisig_xonly_pubkey_from_public_keys_success() { + let expected_xonly_pubkey = x"ffa540e2d3df158dfb202fc1a2cbb20c4920ba35e8f75bb11101bfa47d71449a"; + let pk_list = vector::empty>(); + + let pk_1 = x"038e3d29b653e40f5b620f9443ee05222d1e40be58f544b6fed3d464edd54db883"; + let pk_2 = x"02481521eb57656db4bc9ec81857e105cc7853fe8cad61be23667bb401840fc7f8"; + let pk_3 = x"02c3bc6ff4dec7f43dd4f587d4dc227fb171755779425ca032e0fcb2f0bb639cc2"; + let pk_4 = x"02ebdc1107552f81d188a2c63806cb6fa5d734eaa7316a85dc1f608fcaee412b72"; + + vector::push_back(&mut pk_list, pk_1); + vector::push_back(&mut pk_list, pk_2); + vector::push_back(&mut pk_list, pk_3); + vector::push_back(&mut pk_list, pk_4); + + let xonly_pubkey = derive_multisig_xonly_pubkey_from_public_keys(&pk_list, 4); + + assert!(expected_xonly_pubkey == xonly_pubkey, 1005); + } + + #[test] + fun test_derive_bitcoin_taproot_address_from_multisig_xonly_pubkey_success() { + let xonly_pubkey = x"ffa540e2d3df158dfb202fc1a2cbb20c4920ba35e8f75bb11101bfa47d71449a"; + + let bitcoin_addr = derive_bitcoin_taproot_address_from_multisig_xonly_pubkey(&xonly_pubkey); + + let expected_bitcoin_addr = BitcoinAddress { + bytes: b"bc1p8xpjpkc9uzj2dexcxjg9sw8lxje85xa4070zpcys589e3rf6k20qm6gjrt", + }; + + assert!(expected_bitcoin_addr.bytes == bitcoin_addr.bytes, 1006); + } } \ No newline at end of file diff --git a/frameworks/rooch-framework/src/natives/gas_parameter/bitcoin_address.rs b/frameworks/rooch-framework/src/natives/gas_parameter/bitcoin_address.rs index c8623b984c..5625e53665 100644 --- a/frameworks/rooch-framework/src/natives/gas_parameter/bitcoin_address.rs +++ b/frameworks/rooch-framework/src/natives/gas_parameter/bitcoin_address.rs @@ -9,6 +9,8 @@ crate::natives::gas_parameter::native::define_gas_parameters_for_natives!(GasPar [.parse.per_byte, "parse.per_byte", 30 * MUL], [.verify_bitcoin_address_with_public_key.base, "verify_bitcoin_address_with_public_key.base", 1000 * MUL], [.verify_bitcoin_address_with_public_key.per_byte, "verify_bitcoin_address_with_public_key.per_byte", 30 * MUL], - [.derive_multi_sign_address.base, "parse.base", 1000 * MUL], - [.derive_multi_sign_address.per_byte, "parse.per_byte", 30 * MUL], + [.derive_multisig_xonly_pubkey_from_public_keys.base, "derive_multisig_xonly_pubkey_from_public_keys.base", 1000 * MUL], + [.derive_multisig_xonly_pubkey_from_public_keys.per_byte, "derive_multisig_xonly_pubkey_from_public_keys.per_byte", 30 * MUL], + [.derive_bitcoin_taproot_address_from_multisig_xonly_pubkey.base, "derive_bitcoin_taproot_address_from_multisig_xonly_pubkey.base", 1000 * MUL], + [.derive_bitcoin_taproot_address_from_multisig_xonly_pubkey.per_byte, "derive_bitcoin_taproot_address_from_multisig_xonly_pubkey.per_byte", 30 * MUL], ]); diff --git a/frameworks/rooch-framework/src/natives/rooch_framework/bitcoin_address.rs b/frameworks/rooch-framework/src/natives/rooch_framework/bitcoin_address.rs index 073dddccb1..915dd28e48 100644 --- a/frameworks/rooch-framework/src/natives/rooch_framework/bitcoin_address.rs +++ b/frameworks/rooch-framework/src/natives/rooch_framework/bitcoin_address.rs @@ -20,6 +20,7 @@ use move_vm_types::{ }; use moveos_stdlib::natives::helpers::{make_module_natives, make_native}; use moveos_types::state::{MoveState, MoveStructState}; +use musig2::{secp::Point, secp256k1, KeyAggContext}; use rooch_types::address::BitcoinAddress; use smallvec::smallvec; use std::{collections::VecDeque, str::FromStr}; @@ -101,14 +102,73 @@ pub fn verify_bitcoin_address_with_public_key( Ok(NativeResult::ok(cost, smallvec![Value::bool(is_ok)])) } -// TODO: derive_multi_sign_address -pub fn derive_multi_sign_address( - _gas_params: &FromBytesGasParameters, +pub fn derive_multisig_xonly_pubkey_from_public_keys( + gas_params: &FromBytesGasParameters, _context: &mut NativeContext, _ty_args: Vec, - mut _args: VecDeque, + mut args: VecDeque, ) -> PartialVMResult { - todo!(); + let threshold_bytes = pop_arg!(args, u64); + let pk_vec_bytes = pop_arg!(args, VectorRef); + + let pk_vec_ref = pk_vec_bytes.as_bytes_ref(); + + let cost = gas_params.base + + gas_params.per_byte * NumBytes::new(threshold_bytes + pk_vec_ref.len() as u64); + + println!("{:?}", pk_vec_ref); + + if pk_vec_ref.len() >= threshold_bytes as usize { + return Ok(NativeResult::ok(cost, smallvec![Value::bool(false)])); + } + + let pk = secp256k1::PublicKey::from_slice(&pk_vec_ref).expect("msg"); + + println!("{:?}", pk); + + let pks = vec![pk]; + + println!("{:?}", pks); + + let key_agg_ctx = KeyAggContext::new(pks).unwrap(); + + let aggregated_pubkey: Point = key_agg_ctx.aggregated_pubkey(); + + let xonly_pubkey = aggregated_pubkey.serialize_xonly(); + + Ok(NativeResult::ok( + cost, + smallvec![Value::vector_u8(xonly_pubkey)], + )) +} + +pub fn derive_bitcoin_taproot_address_from_multisig_xonly_pubkey( + gas_params: &FromBytesGasParameters, + _context: &mut NativeContext, + _ty_args: Vec, + mut args: VecDeque, +) -> PartialVMResult { + let xonly_pubkey_bytes = pop_arg!(args, VectorRef); + + let xonly_pubkey_ref = xonly_pubkey_bytes.as_bytes_ref(); + + let cost = gas_params.base + gas_params.per_byte * NumBytes::new(xonly_pubkey_ref.len() as u64); + + let internal_key = XOnlyPublicKey::from_slice(&xonly_pubkey_ref).unwrap(); + let secp = bitcoin::secp256k1::Secp256k1::verification_only(); + let bitcoin_addr = BitcoinAddress::from(bitcoin::Address::p2tr( + &secp, + internal_key, + None, + bitcoin::Network::Bitcoin, + )); + + println!("{:?}", bitcoin_addr); + + Ok(NativeResult::ok( + cost, + smallvec![Value::struct_(bitcoin_addr.to_runtime_value_struct())], + )) } #[derive(Debug, Clone)] @@ -134,7 +194,8 @@ impl FromBytesGasParameters { pub struct GasParameters { pub parse: FromBytesGasParameters, pub verify_bitcoin_address_with_public_key: FromBytesGasParameters, - pub derive_multi_sign_address: FromBytesGasParameters, + pub derive_multisig_xonly_pubkey_from_public_keys: FromBytesGasParameters, + pub derive_bitcoin_taproot_address_from_multisig_xonly_pubkey: FromBytesGasParameters, } impl GasParameters { @@ -142,7 +203,9 @@ impl GasParameters { Self { parse: FromBytesGasParameters::zeros(), verify_bitcoin_address_with_public_key: FromBytesGasParameters::zeros(), - derive_multi_sign_address: FromBytesGasParameters::zeros(), + derive_multisig_xonly_pubkey_from_public_keys: FromBytesGasParameters::zeros(), + derive_bitcoin_taproot_address_from_multisig_xonly_pubkey: + FromBytesGasParameters::zeros(), } } } @@ -158,10 +221,17 @@ pub fn make_all(gas_params: GasParameters) -> impl Iterator