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