Skip to content

Commit

Permalink
[gh-2295] add functions of multisig xonly pubkeys.
Browse files Browse the repository at this point in the history
  • Loading branch information
Feliciss committed Jul 29, 2024
1 parent af705a4 commit 667f511
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 50 deletions.
57 changes: 51 additions & 6 deletions frameworks/rooch-framework/doc/bitcoin_address.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
- [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_multisig_xonly_pubkey_from_public_keys`](#0x3_bitcoin_address_derive_multisig_xonly_pubkey_from_public_keys)
- [Function `derive_multisig_xonly_pubkey_from_xonly_pubkeys`](#0x3_bitcoin_address_derive_multisig_xonly_pubkey_from_xonly_pubkeys)
- [Function `derive_bitcoin_taproot_address_from_multisig_xonly_pubkey`](#0x3_bitcoin_address_derive_bitcoin_taproot_address_from_multisig_xonly_pubkey)


Expand Down Expand Up @@ -52,11 +52,56 @@ We just keep the raw bytes of the address and do care about the network.
## Constants


<a name="0x3_bitcoin_address_ErrorAddressBytesLen"></a>
<a name="0x3_bitcoin_address_E_ARG_NOT_VECTOR_U8"></a>



<pre><code><b>const</b> <a href="bitcoin_address.md#0x3_bitcoin_address_ErrorAddressBytesLen">ErrorAddressBytesLen</a>: u64 = 1;
<pre><code><b>const</b> <a href="bitcoin_address.md#0x3_bitcoin_address_E_ARG_NOT_VECTOR_U8">E_ARG_NOT_VECTOR_U8</a>: u64 = 2;
</code></pre>



<a name="0x3_bitcoin_address_E_INVALID_ADDRESS"></a>



<pre><code><b>const</b> <a href="bitcoin_address.md#0x3_bitcoin_address_E_INVALID_ADDRESS">E_INVALID_ADDRESS</a>: u64 = 1;
</code></pre>



<a name="0x3_bitcoin_address_E_INVALID_KEY_EGG_CONTEXT"></a>



<pre><code><b>const</b> <a href="bitcoin_address.md#0x3_bitcoin_address_E_INVALID_KEY_EGG_CONTEXT">E_INVALID_KEY_EGG_CONTEXT</a>: u64 = 5;
</code></pre>



<a name="0x3_bitcoin_address_E_INVALID_PUBLIC_KEY"></a>



<pre><code><b>const</b> <a href="bitcoin_address.md#0x3_bitcoin_address_E_INVALID_PUBLIC_KEY">E_INVALID_PUBLIC_KEY</a>: u64 = 3;
</code></pre>



<a name="0x3_bitcoin_address_E_INVALID_THRESHOLD"></a>



<pre><code><b>const</b> <a href="bitcoin_address.md#0x3_bitcoin_address_E_INVALID_THRESHOLD">E_INVALID_THRESHOLD</a>: u64 = 4;
</code></pre>



<a name="0x3_bitcoin_address_E_INVALID_XONLY_PUBKEY"></a>



<pre><code><b>const</b> <a href="bitcoin_address.md#0x3_bitcoin_address_E_INVALID_XONLY_PUBKEY">E_INVALID_XONLY_PUBKEY</a>: u64 = 6;
</code></pre>


Expand Down Expand Up @@ -288,13 +333,13 @@ Empty address is a special address that is used to if we parse address failed fr



<a name="0x3_bitcoin_address_derive_multisig_xonly_pubkey_from_public_keys"></a>
<a name="0x3_bitcoin_address_derive_multisig_xonly_pubkey_from_xonly_pubkeys"></a>

## Function `derive_multisig_xonly_pubkey_from_public_keys`
## Function `derive_multisig_xonly_pubkey_from_xonly_pubkeys`



<pre><code><b>public</b> <b>fun</b> <a href="bitcoin_address.md#0x3_bitcoin_address_derive_multisig_xonly_pubkey_from_public_keys">derive_multisig_xonly_pubkey_from_public_keys</a>(public_keys: &<a href="">vector</a>&lt;<a href="">vector</a>&lt;u8&gt;&gt;, threshold: u64): <a href="">vector</a>&lt;u8&gt;
<pre><code><b>public</b> <b>fun</b> <a href="bitcoin_address.md#0x3_bitcoin_address_derive_multisig_xonly_pubkey_from_xonly_pubkeys">derive_multisig_xonly_pubkey_from_xonly_pubkeys</a>(public_keys: &<a href="">vector</a>&lt;<a href="">vector</a>&lt;u8&gt;&gt;, threshold: u64): <a href="">vector</a>&lt;u8&gt;
</code></pre>


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ module rooch_framework::bitcoin_address {
const P2SH_ADDR_BYTE_LEN: u64 = 21;

// error code
const ErrorAddressBytesLen: u64 = 1;
const E_INVALID_ADDRESS: u64 = 1;
const E_ARG_NOT_VECTOR_U8: u64 = 2;
const E_INVALID_PUBLIC_KEY: u64 = 3;
const E_INVALID_THRESHOLD: u64 = 4;
const E_INVALID_KEY_EGG_CONTEXT: u64 = 5;
const E_INVALID_XONLY_PUBKEY: u64 = 6;

// P2PKH address decimal prefix
const P2PKH_ADDR_DECIMAL_PREFIX_MAIN: u8 = 0; // 0x00
Expand All @@ -32,7 +37,7 @@ module rooch_framework::bitcoin_address {
}

public fun new_p2pkh(pubkey_hash: vector<u8>): BitcoinAddress{
assert!(vector::length(&pubkey_hash) == PUBKEY_HASH_LEN, ErrorAddressBytesLen);
assert!(vector::length(&pubkey_hash) == PUBKEY_HASH_LEN, E_INVALID_ADDRESS);
//we do not distinguish between mainnet and testnet in Move
let bytes = vector::singleton<u8>(P2PKH_ADDR_DECIMAL_PREFIX_MAIN);
vector::append(&mut bytes, pubkey_hash);
Expand All @@ -42,7 +47,7 @@ module rooch_framework::bitcoin_address {
}

public fun new_p2sh(script_hash: vector<u8>): BitcoinAddress{
assert!(vector::length(&script_hash) == SCRIPT_HASH_LEN, ErrorAddressBytesLen);
assert!(vector::length(&script_hash) == SCRIPT_HASH_LEN, E_INVALID_ADDRESS);
let bytes = vector::singleton<u8>(P2SH_ADDR_DECIMAL_PREFIX_MAIN);
vector::append(&mut bytes, script_hash);
BitcoinAddress {
Expand Down Expand Up @@ -109,7 +114,7 @@ module rooch_framework::bitcoin_address {
public native fun verify_bitcoin_address_with_public_key(bitcoin_addr: &BitcoinAddress, pk: &vector<u8>): bool;

// derive multisig xonly public key from public keys
public native fun derive_multisig_xonly_pubkey_from_public_keys(public_keys: &vector<vector<u8>>, threshold: u64): vector<u8>;
public native fun derive_multisig_xonly_pubkey_from_xonly_pubkeys(public_keys: &vector<vector<u8>>, threshold: u64): vector<u8>;

// derive bitcoin taproot address from the multisig xonly public key
public native fun derive_bitcoin_taproot_address_from_multisig_xonly_pubkey(xonly_pubkey: &vector<u8>): BitcoinAddress;
Expand Down Expand Up @@ -155,35 +160,37 @@ module rooch_framework::bitcoin_address {
}

#[test]
fun test_derive_multisig_xonly_pubkey_from_public_keys_success() {
fun test_derive_multisig_xonly_pubkey_from_xonly_pubkeys_success() {
let expected_xonly_pubkey = x"ffa540e2d3df158dfb202fc1a2cbb20c4920ba35e8f75bb11101bfa47d71449a";
let pk_list = vector::empty<vector<u8>>();

let pk_1 = x"038e3d29b653e40f5b620f9443ee05222d1e40be58f544b6fed3d464edd54db883";
let pk_2 = x"02481521eb57656db4bc9ec81857e105cc7853fe8cad61be23667bb401840fc7f8";
let pk_3 = x"02c3bc6ff4dec7f43dd4f587d4dc227fb171755779425ca032e0fcb2f0bb639cc2";
let pk_4 = x"02ebdc1107552f81d188a2c63806cb6fa5d734eaa7316a85dc1f608fcaee412b72";
let pk_1 = x"03935f972da013f80ae011890fa89b67a27b7be6ccb24d3274d18b2d4067f261a9";
let pk_2 = x"02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9";
let pk_3 = x"02dff1d77f2a671c5f36183726db2341be58feae1da2deced843240f7b502ba659";

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);
let xonly_pubkey = derive_multisig_xonly_pubkey_from_xonly_pubkeys(&pk_list, 3);

assert!(expected_xonly_pubkey == xonly_pubkey, 1005);
std::debug::print(&xonly_pubkey);
std::debug::print(&expected_xonly_pubkey);

assert!(expected_xonly_pubkey == xonly_pubkey, E_INVALID_KEY_EGG_CONTEXT);
}

// test covered from https://slowli.github.io/bech32-buffer/ using script version 1
#[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",
bytes: x"02010ca8363b3e074be0697eb46b5ac5a291d06ecbeb95cf16f685735147753f8b45",
};

assert!(expected_bitcoin_addr.bytes == bitcoin_addr.bytes, 1006);
assert!(expected_bitcoin_addr.bytes == bitcoin_addr.bytes, E_INVALID_XONLY_PUBKEY);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +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_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_multisig_xonly_pubkey_from_xonly_pubkeys.base, "derive_multisig_xonly_pubkey_from_xonly_pubkeys.base", 1000 * MUL],
[.derive_multisig_xonly_pubkey_from_xonly_pubkeys.per_byte, "derive_multisig_xonly_pubkey_from_xonly_pubkeys.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],
]);
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

use bitcoin::{
address::{Address, AddressType},
hex::DisplayHex,
secp256k1::Secp256k1,
PublicKey, XOnlyPublicKey,
};
Expand All @@ -18,14 +19,22 @@ use move_vm_types::{
pop_arg,
values::{StructRef, Value, VectorRef},
};
use moveos_stdlib::natives::helpers::{make_module_natives, make_native};
use moveos_stdlib::natives::{
helpers::{make_module_natives, make_native},
moveos_stdlib::move_module::ConstantOps,
};
use moveos_types::state::{MoveState, MoveStructState};
use musig2::{secp::Point, secp256k1, KeyAggContext};
use musig2::{secp::Point, KeyAggContext};
use rooch_types::address::BitcoinAddress;
use smallvec::smallvec;
use std::{collections::VecDeque, str::FromStr};

pub const E_INVALID_ADDRESS: u64 = 1;
pub const E_ARG_NOT_VECTOR_U8: u64 = 2;
pub const E_INVALID_PUBLIC_KEY: u64 = 3;
pub const E_INVALID_THRESHOLD: u64 = 4;
pub const E_INVALID_KEY_EGG_CONTEXT: u64 = 5;
pub const E_INVALID_XONLY_PUBKEY: u64 = 6;

pub fn parse(
gas_params: &FromBytesGasParameters,
Expand Down Expand Up @@ -102,35 +111,47 @@ pub fn verify_bitcoin_address_with_public_key(
Ok(NativeResult::ok(cost, smallvec![Value::bool(is_ok)]))
}

pub fn derive_multisig_xonly_pubkey_from_public_keys(
pub fn derive_multisig_xonly_pubkey_from_xonly_pubkeys(
gas_params: &FromBytesGasParameters,
_context: &mut NativeContext,
_ty_args: Vec<Type>,
mut args: VecDeque<Value>,
) -> PartialVMResult<NativeResult> {
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 pk_list = pop_arg!(args, Vec<Value>);

let cost = gas_params.base
+ gas_params.per_byte * NumBytes::new(threshold_bytes + pk_vec_ref.len() as u64);
let cost = gas_params.base + gas_params.per_byte * NumBytes::new(threshold_bytes);

println!("{:?}", pk_vec_ref);

if pk_vec_ref.len() >= threshold_bytes as usize {
return Ok(NativeResult::ok(cost, smallvec![Value::bool(false)]));
if pk_list.len() < threshold_bytes as usize {
return Ok(NativeResult::err(cost, E_INVALID_THRESHOLD));
}

let pk = secp256k1::PublicKey::from_slice(&pk_vec_ref).expect("msg");

println!("{:?}", pk);

let pks = vec![pk];

println!("{:?}", pks);
let mut pubkeys = Vec::new();
for arg_value in pk_list.iter() {
let value = arg_value.copy_value()?;
match value.value_as::<Vec<u8>>() {
Ok(v) => {
match Point::lift_x_hex(&v.as_hex().to_string()) {
Ok(pk_args) => {
pubkeys.push(pk_args);
}
Err(_) => {
return Ok(NativeResult::err(cost, E_INVALID_PUBLIC_KEY));
}
};
}
Err(_) => {
return Ok(NativeResult::err(cost, E_ARG_NOT_VECTOR_U8));
}
}
}

let key_agg_ctx = KeyAggContext::new(pks).unwrap();
let key_agg_ctx = match KeyAggContext::new(pubkeys) {
Ok(key_agg_ctx) => key_agg_ctx,
Err(_) => {
return Ok(NativeResult::err(cost, E_INVALID_KEY_EGG_CONTEXT));
}
};

let aggregated_pubkey: Point = key_agg_ctx.aggregated_pubkey();

Expand All @@ -154,7 +175,13 @@ pub fn derive_bitcoin_taproot_address_from_multisig_xonly_pubkey(

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 internal_key = match XOnlyPublicKey::from_slice(&xonly_pubkey_ref) {
Ok(xonly_pubkey) => xonly_pubkey,
Err(_) => {
return Ok(NativeResult::err(cost, E_INVALID_XONLY_PUBKEY));
}
};

let secp = bitcoin::secp256k1::Secp256k1::verification_only();
let bitcoin_addr = BitcoinAddress::from(bitcoin::Address::p2tr(
&secp,
Expand All @@ -163,8 +190,6 @@ pub fn derive_bitcoin_taproot_address_from_multisig_xonly_pubkey(
bitcoin::Network::Bitcoin,
));

println!("{:?}", bitcoin_addr);

Ok(NativeResult::ok(
cost,
smallvec![Value::struct_(bitcoin_addr.to_runtime_value_struct())],
Expand Down Expand Up @@ -194,7 +219,7 @@ impl FromBytesGasParameters {
pub struct GasParameters {
pub parse: FromBytesGasParameters,
pub verify_bitcoin_address_with_public_key: FromBytesGasParameters,
pub derive_multisig_xonly_pubkey_from_public_keys: FromBytesGasParameters,
pub derive_multisig_xonly_pubkey_from_xonly_pubkeys: FromBytesGasParameters,
pub derive_bitcoin_taproot_address_from_multisig_xonly_pubkey: FromBytesGasParameters,
}

Expand All @@ -203,7 +228,7 @@ impl GasParameters {
Self {
parse: FromBytesGasParameters::zeros(),
verify_bitcoin_address_with_public_key: FromBytesGasParameters::zeros(),
derive_multisig_xonly_pubkey_from_public_keys: FromBytesGasParameters::zeros(),
derive_multisig_xonly_pubkey_from_xonly_pubkeys: FromBytesGasParameters::zeros(),
derive_bitcoin_taproot_address_from_multisig_xonly_pubkey:
FromBytesGasParameters::zeros(),
}
Expand All @@ -221,10 +246,10 @@ pub fn make_all(gas_params: GasParameters) -> impl Iterator<Item = (String, Nati
),
),
(
"derive_multisig_xonly_pubkey_from_public_keys",
"derive_multisig_xonly_pubkey_from_xonly_pubkeys",
make_native(
gas_params.derive_multisig_xonly_pubkey_from_public_keys,
derive_multisig_xonly_pubkey_from_public_keys,
gas_params.derive_multisig_xonly_pubkey_from_xonly_pubkeys,
derive_multisig_xonly_pubkey_from_xonly_pubkeys,
),
),
(
Expand Down

0 comments on commit 667f511

Please sign in to comment.