Skip to content

Commit

Permalink
[rooch-networkgh-2295] add tests and leave function unchanged.
Browse files Browse the repository at this point in the history
  • Loading branch information
Feliciss committed Jul 31, 2024
1 parent 80f89b7 commit 747ac4f
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 44 deletions.
32 changes: 16 additions & 16 deletions frameworks/rooch-framework/doc/bitcoin_address.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
- [Function `from_string`](#0x3_bitcoin_address_from_string)
- [Function `verify_with_public_key`](#0x3_bitcoin_address_verify_with_public_key)
- [Function `to_rooch_address`](#0x3_bitcoin_address_to_rooch_address)
- [Function `verify_with_pk`](#0x3_bitcoin_address_verify_with_pk)
- [Function `verify_bitcoin_address_with_public_key`](#0x3_bitcoin_address_verify_bitcoin_address_with_public_key)
- [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,56 +52,56 @@ We just keep the raw bytes of the address and do care about the network.
## Constants


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



<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;
<pre><code><b>const</b> <a href="bitcoin_address.md#0x3_bitcoin_address_ErrorArgNotVectorU8">ErrorArgNotVectorU8</a>: u64 = 2;
</code></pre>



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



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



<a name="0x3_bitcoin_address_E_INVALID_KEY_EGG_CONTEXT"></a>
<a name="0x3_bitcoin_address_ErrorInvalidKeyEggContext"></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;
<pre><code><b>const</b> <a href="bitcoin_address.md#0x3_bitcoin_address_ErrorInvalidKeyEggContext">ErrorInvalidKeyEggContext</a>: u64 = 5;
</code></pre>



<a name="0x3_bitcoin_address_E_INVALID_PUBLIC_KEY"></a>
<a name="0x3_bitcoin_address_ErrorInvalidPublicKey"></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;
<pre><code><b>const</b> <a href="bitcoin_address.md#0x3_bitcoin_address_ErrorInvalidPublicKey">ErrorInvalidPublicKey</a>: u64 = 3;
</code></pre>



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



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



<a name="0x3_bitcoin_address_E_INVALID_XONLY_PUBKEY"></a>
<a name="0x3_bitcoin_address_ErrorInvalidXOnlyPublicKey"></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;
<pre><code><b>const</b> <a href="bitcoin_address.md#0x3_bitcoin_address_ErrorInvalidXOnlyPublicKey">ErrorInvalidXOnlyPublicKey</a>: u64 = 6;
</code></pre>


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



<a name="0x3_bitcoin_address_verify_with_pk"></a>
<a name="0x3_bitcoin_address_verify_bitcoin_address_with_public_key"></a>

## Function `verify_with_pk`
## Function `verify_bitcoin_address_with_public_key`



<pre><code><b>public</b> <b>fun</b> <a href="bitcoin_address.md#0x3_bitcoin_address_verify_with_pk">verify_with_pk</a>(bitcoin_addr: &<a href="bitcoin_address.md#0x3_bitcoin_address_BitcoinAddress">bitcoin_address::BitcoinAddress</a>, pk: &<a href="">vector</a>&lt;u8&gt;): bool
<pre><code><b>public</b> <b>fun</b> <a href="bitcoin_address.md#0x3_bitcoin_address_verify_bitcoin_address_with_public_key">verify_bitcoin_address_with_public_key</a>(bitcoin_addr: &<a href="bitcoin_address.md#0x3_bitcoin_address_BitcoinAddress">bitcoin_address::BitcoinAddress</a>, pk: &<a href="">vector</a>&lt;u8&gt;): bool
</code></pre>


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

// error code
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;
const ErrorInvalidAddress: u64 = 1;
const ErrorArgNotVectorU8: u64 = 2;
const ErrorInvalidPublicKey: u64 = 3;
const ErrorInvalidThreshold: u64 = 4;
const ErrorInvalidKeyEggContext: u64 = 5;
const ErrorInvalidXOnlyPublicKey: u64 = 6;

// P2PKH address decimal prefix
const P2PKH_ADDR_DECIMAL_PREFIX_MAIN: u8 = 0; // 0x00
Expand All @@ -37,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, E_INVALID_ADDRESS);
assert!(vector::length(&pubkey_hash) == PUBKEY_HASH_LEN, ErrorInvalidAddress);
//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 @@ -47,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, E_INVALID_ADDRESS);
assert!(vector::length(&script_hash) == SCRIPT_HASH_LEN, ErrorInvalidAddress);
let bytes = vector::singleton<u8>(P2SH_ADDR_DECIMAL_PREFIX_MAIN);
vector::append(&mut bytes, script_hash);
BitcoinAddress {
Expand Down Expand Up @@ -102,7 +102,7 @@ module rooch_framework::bitcoin_address {

public fun verify_with_public_key(addr: &String, pk: &vector<u8>): bool {
let bitcoin_addr = from_string(addr);
verify_with_pk(&bitcoin_addr, pk)
verify_bitcoin_address_with_public_key(&bitcoin_addr, pk)
}

public fun to_rooch_address(addr: &BitcoinAddress): address{
Expand All @@ -111,7 +111,7 @@ module rooch_framework::bitcoin_address {
}

// verify bitcoin address according to the pk bytes
public native fun verify_with_pk(bitcoin_addr: &BitcoinAddress, pk: &vector<u8>): bool;
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_xonly_pubkeys(public_keys: vector<vector<u8>>, threshold: u64): vector<u8>;
Expand All @@ -121,6 +121,8 @@ module rooch_framework::bitcoin_address {

/// Parse the Bitcoin address string bytes to Move BitcoinAddress
native fun parse(raw_addr: &vector<u8>): BitcoinAddress;
// TODO: remove verify_with_pk after upgrade
native fun verify_with_pk(addr: &vector<u8>, pk: &vector<u8>): bool;

#[test_only]
public fun random_address_for_testing(): BitcoinAddress {
Expand Down Expand Up @@ -172,7 +174,35 @@ module rooch_framework::bitcoin_address {

let xonly_pubkey = derive_multisig_xonly_pubkey_from_xonly_pubkeys(pk_list, 2);

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

#[test]
#[expected_failure(location=Self, abort_code = ErrorInvalidThreshold)]
fun test_derive_multisig_xonly_pubkey_from_xonly_pubkeys_fail_invalid_threshold() {
let pk_list = vector::empty<vector<u8>>();

let pk_1 = x"f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9";
let pk_2 = x"ffa540e2d3df158dfb202fc1a2cbb20c4920ba35e8f75bb11101bfa47d71449a";

vector::push_back(&mut pk_list, pk_1);
vector::push_back(&mut pk_list, pk_2);

derive_multisig_xonly_pubkey_from_xonly_pubkeys(pk_list, 3);
}

#[test]
#[expected_failure(location=Self, abort_code = ErrorInvalidPublicKey)]
fun test_derive_multisig_xonly_pubkey_from_xonly_pubkeys_fail_invalid_public_key() {
let pk_list = vector::empty<vector<u8>>();

let pk_1 = x"f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9";
let pk_2 = x"02481521eb57656db4bc9ec81857e105cc7853fe8cad61be23667bb401840fc7f8";

vector::push_back(&mut pk_list, pk_1);
vector::push_back(&mut pk_list, pk_2);

derive_multisig_xonly_pubkey_from_xonly_pubkeys(pk_list, 2);
}

#[test]
Expand All @@ -185,6 +215,14 @@ module rooch_framework::bitcoin_address {
bytes: x"020102f97a0a664c8493bfa28cfcf3450628bdc0ba7b3b0af2b57d4d057f15cb41f9",
};

assert!(expected_bitcoin_addr.bytes == bitcoin_addr.bytes, E_INVALID_XONLY_PUBKEY);
assert!(expected_bitcoin_addr.bytes == bitcoin_addr.bytes, ErrorInvalidXOnlyPublicKey);
}

#[test]
#[expected_failure(location=Self, abort_code = ErrorInvalidXOnlyPublicKey)]
fun test_derive_bitcoin_taproot_address_from_multisig_xonly_pubkey_fail() {
let xonly_pubkey = x"038e3d29b653e40f5b620f9443ee05222d1e40be58f544b6fed3d464edd54db883";

derive_bitcoin_taproot_address_from_multisig_xonly_pubkey(&xonly_pubkey);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ use crate::natives::gas_parameter::native::MUL;
use crate::natives::rooch_framework::bitcoin_address::GasParameters;

crate::natives::gas_parameter::native::define_gas_parameters_for_natives!(GasParameters, "bitcoin_address", [
[.new.base, "parse.base", 1000 * MUL],
[.new.per_byte, "parse.per_byte", 30 * MUL],
[.verify_with_pk.base, "verify_with_pk.base", 1000 * MUL],
[.verify_with_pk.per_byte, "verify_with_pk.per_byte", 30 * MUL],
[.new.base, "parse.base", 1000 * MUL],
[.new.per_byte, "parse.per_byte", 30 * MUL],
[.verify_bitcoin_address_with_public_key.base, optional "verify_bitcoin_address_with_public_key.base", 1000 * MUL],
[.verify_bitcoin_address_with_public_key.per_byte, optional "verify_bitcoin_address_with_public_key.per_byte", 30 * MUL],
[.derive_multisig_xonly_pubkey_from_xonly_pubkeys.base, optional "derive_multisig_xonly_pubkey_from_xonly_pubkeys.base", 1000 * MUL],
[.derive_multisig_xonly_pubkey_from_xonly_pubkeys.per_byte, optional "derive_multisig_xonly_pubkey_from_xonly_pubkeys.per_byte", 30 * MUL],
[.derive_bitcoin_taproot_address_from_multisig_xonly_pubkey.base, optional "derive_bitcoin_taproot_address_from_multisig_xonly_pubkey.base", 1000 * MUL],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ 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 const E_INVALID_XONLY_PUBLIC_KEY: u64 = 6;

pub fn parse(
gas_params: &FromBytesGasParameters,
Expand Down Expand Up @@ -61,34 +61,31 @@ pub fn parse(
))
}

/// Returns true if the given pubkey's bitcoin address is equal to the input bitcoin address.
/// Returns true if the given pubkey is directly related to the address payload.
pub fn verify_with_pk(
gas_params: &FromBytesGasParameters,
_context: &mut NativeContext,
_ty_args: Vec<Type>,
mut args: VecDeque<Value>,
) -> PartialVMResult<NativeResult> {
let pk_bytes = pop_arg!(args, VectorRef);
let addr_bytes = pop_arg!(args, StructRef);
let addr_bytes = pop_arg!(args, VectorRef);

let pk_ref = pk_bytes.as_bytes_ref();
let addr_value = addr_bytes.read_ref()?;
let addr_ref = addr_bytes.as_bytes_ref();

let bitcoin_addr = BitcoinAddress::from_runtime_value(addr_value).map_err(|e| {
PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
.with_message(format!("Failed to parse bitcoin address: {}", e))
})?;
let cost = gas_params.base
+ gas_params.per_byte
* NumBytes::new((pk_ref.len() + bitcoin_addr.to_bytes().len()) as u64);
+ gas_params.per_byte * NumBytes::new((pk_ref.len() + addr_ref.len()) as u64);

// TODO: convert to internal rooch public key and to bitcoin address?
let Ok(pk) = PublicKey::from_slice(&pk_ref) else {
return Ok(NativeResult::ok(cost, smallvec![Value::bool(false)]));
};

// TODO: compare the input bitcoin address with the converted bitcoin address
let addr = match Address::from_str(&bitcoin_addr.to_string()) {
let Ok(addr_str) = std::str::from_utf8(&addr_ref) else {
return Ok(NativeResult::ok(cost, smallvec![Value::bool(false)]));
};

let addr = match Address::from_str(addr_str) {
Ok(addr) => addr.assume_checked(),
Err(_) => {
return Ok(NativeResult::ok(cost, smallvec![Value::bool(false)]));
Expand Down Expand Up @@ -123,6 +120,54 @@ impl FromBytesGasParameters {
}
}

// optional function
/// Returns true if the given pubkey's bitcoin address is equal to the input bitcoin address.
pub fn verify_bitcoin_address_with_public_key(
gas_params: &FromBytesGasParametersOptional,
_context: &mut NativeContext,
_ty_args: Vec<Type>,
mut args: VecDeque<Value>,
) -> PartialVMResult<NativeResult> {
let pk_bytes = pop_arg!(args, VectorRef);
let addr_bytes = pop_arg!(args, StructRef);

let pk_ref = pk_bytes.as_bytes_ref();
let addr_value = addr_bytes.read_ref()?;

let bitcoin_addr = BitcoinAddress::from_runtime_value(addr_value).map_err(|e| {
PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
.with_message(format!("Failed to parse bitcoin address: {}", e))
})?;
let cost = gas_params.base.unwrap()
+ gas_params.per_byte.unwrap()
* NumBytes::new((pk_ref.len() + bitcoin_addr.to_bytes().len()) as u64);

// TODO: convert to internal rooch public key and to bitcoin address?
let Ok(pk) = PublicKey::from_slice(&pk_ref) else {
return Ok(NativeResult::ok(cost, smallvec![Value::bool(false)]));
};

// TODO: compare the input bitcoin address with the converted bitcoin address
let addr = match Address::from_str(&bitcoin_addr.to_string()) {
Ok(addr) => addr.assume_checked(),
Err(_) => {
return Ok(NativeResult::ok(cost, smallvec![Value::bool(false)]));
}
};

let is_ok = match addr.address_type() {
Some(AddressType::P2tr) => {
let xonly_pubkey = XOnlyPublicKey::from(pk.inner);
let secp = Secp256k1::verification_only();
let trust_addr = Address::p2tr(&secp, xonly_pubkey, None, *addr.network());
addr.is_related_to_pubkey(&pk) || trust_addr.to_string() == addr.to_string()
}
_ => addr.is_related_to_pubkey(&pk),
};

Ok(NativeResult::ok(cost, smallvec![Value::bool(is_ok)]))
}

// optional function
pub fn derive_multisig_xonly_pubkey_from_xonly_pubkeys(
gas_params: &FromBytesGasParametersOptional,
Expand Down Expand Up @@ -195,7 +240,7 @@ pub fn derive_bitcoin_taproot_address_from_multisig_xonly_pubkey(
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));
return Ok(NativeResult::err(cost, E_INVALID_XONLY_PUBLIC_KEY));
}
};

Expand Down Expand Up @@ -243,6 +288,7 @@ impl FromBytesGasParametersOptional {
pub struct GasParameters {
pub new: FromBytesGasParameters,
pub verify_with_pk: FromBytesGasParameters,
pub verify_bitcoin_address_with_public_key: FromBytesGasParametersOptional,
pub derive_multisig_xonly_pubkey_from_xonly_pubkeys: FromBytesGasParametersOptional,
pub derive_bitcoin_taproot_address_from_multisig_xonly_pubkey: FromBytesGasParametersOptional,
}
Expand All @@ -252,6 +298,7 @@ impl GasParameters {
Self {
new: FromBytesGasParameters::zeros(),
verify_with_pk: FromBytesGasParameters::zeros(),
verify_bitcoin_address_with_public_key: FromBytesGasParametersOptional::zeros(),
derive_multisig_xonly_pubkey_from_xonly_pubkeys: FromBytesGasParametersOptional::zeros(
),
derive_bitcoin_taproot_address_from_multisig_xonly_pubkey:
Expand All @@ -270,6 +317,16 @@ pub fn make_all(gas_params: GasParameters) -> impl Iterator<Item = (String, Nati
]
.to_vec();

if !gas_params.verify_bitcoin_address_with_public_key.is_empty() {
natives.push((
"verify_bitcoin_address_with_public_key",
make_native(
gas_params.verify_bitcoin_address_with_public_key,
verify_bitcoin_address_with_public_key,
),
));
}

if !gas_params
.derive_multisig_xonly_pubkey_from_xonly_pubkeys
.is_empty()
Expand Down

0 comments on commit 747ac4f

Please sign in to comment.