Skip to content

Commit

Permalink
[framework] Implement compare and sort (#2495)
Browse files Browse the repository at this point in the history
* [moveos_std] Implement compare::compare

* [moveos_std] Implement sort::sort

* [move_std] Implement vector::sub_vector

* [bitcoin-move] use sort::sort and vector::sub_vector

* [moveos_std] sort fun use mut vector as argument

* [moveos_std] Implement sort::sort_by_key

* [move_std] Use vector::slice to replace vector::sub_vector

* fixup add vector.md
  • Loading branch information
jolestar authored Aug 24, 2024
1 parent c7eb08f commit be3a23a
Show file tree
Hide file tree
Showing 12 changed files with 1,489 additions and 69 deletions.
2 changes: 1 addition & 1 deletion frameworks/bitcoin-move/doc/multisign_account.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ Bitcoin multisign account module
<b>use</b> <a href="">0x1::vector</a>;
<b>use</b> <a href="">0x2::account</a>;
<b>use</b> <a href="">0x2::bcs</a>;
<b>use</b> <a href="">0x2::compare</a>;
<b>use</b> <a href="">0x2::object</a>;
<b>use</b> <a href="">0x2::result</a>;
<b>use</b> <a href="">0x2::signer</a>;
<b>use</b> <a href="">0x2::simple_map</a>;
<b>use</b> <a href="">0x2::sort</a>;
<b>use</b> <a href="">0x3::address_mapping</a>;
<b>use</b> <a href="">0x3::bitcoin_address</a>;
<b>use</b> <a href="">0x3::ecdsa_k1</a>;
Expand Down
61 changes: 10 additions & 51 deletions frameworks/bitcoin-move/sources/multisign_account.move
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ module bitcoin_move::multisign_account{
use moveos_std::object::{Object};
use moveos_std::account::{Self, Account};
use moveos_std::bcs;
use moveos_std::compare;
use moveos_std::sort;
use moveos_std::simple_map::{Self, SimpleMap};
use bitcoin_move::opcode;
use bitcoin_move::script_buf::{Self, ScriptBuf};
Expand Down Expand Up @@ -105,10 +105,10 @@ module bitcoin_move::multisign_account{
let to_x_only_public_keys = to_x_only_public_keys(public_keys);
//We need to sort the public keys to generate the same multisign address
//And we sort the x_only_public_keys, not the original public keys
let sorted_public_keys = quick_sort(to_x_only_public_keys);
let merkle_root = generate_taproot(threshold, &sorted_public_keys);
sort::sort(&mut to_x_only_public_keys);
let merkle_root = generate_taproot(threshold, &to_x_only_public_keys);
//Use the sorted first public key as the internal pubkey
let internal_pubkey = vector::borrow(&sorted_public_keys, 0);
let internal_pubkey = vector::borrow(&to_x_only_public_keys, 0);
bitcoin_address::p2tr(internal_pubkey, option::some(merkle_root))
}

Expand Down Expand Up @@ -149,58 +149,17 @@ module bitcoin_move::multisign_account{
let public_key = *vector::borrow(&public_keys, idx);
let public_key_len = vector::length(&public_key);
let x_only_key = if (public_key_len == BITCOIN_COMPRESSED_PUBLIC_KEY_LEN){
sub_vector(&public_key, 1, BITCOIN_COMPRESSED_PUBLIC_KEY_LEN)
vector::slice(&public_key, 1, BITCOIN_COMPRESSED_PUBLIC_KEY_LEN)
}else{
//TODO should we support uncompressed public key?
abort ErrorInvalidPublicKey
};
sub_vector(&public_key, 1, 33);
vector::slice(&public_key, 1, 33);
vector::push_back(&mut result, x_only_key);
idx = idx + 1;
};
result
}

//TODO put this function in a more general module
fun sub_vector(bytes: &vector<u8>, start: u64, end: u64): vector<u8>{
let result = vector::empty();
let i = start;
while(i < end) {
vector::push_back(&mut result, *vector::borrow(bytes, i));
i = i + 1;
};
result
}

//TODO migrate this function to a suitable module
fun quick_sort(data: vector<vector<u8>>): vector<vector<u8>> {
if (vector::length(&data) <= 1) {
return data
};

let pivot = *vector::borrow(&data, 0);
let less = vector::empty();
let equal = vector::empty();
let greater = vector::empty();

while (vector::length(&data) > 0) {
let value = vector::remove(&mut data, 0);
let cmp = compare::compare_vector_u8(&value, &pivot);
if (cmp == compare::result_less_than()) {
vector::push_back(&mut less, value);
} else if (cmp == 0) {
vector::push_back(&mut equal, value);
} else {
vector::push_back(&mut greater, value);
};
};

let sortedData = vector::empty();
vector::append(&mut sortedData, quick_sort(less));
vector::append(&mut sortedData, equal);
vector::append(&mut sortedData, quick_sort(greater));
sortedData
}

public fun is_participant(multisign_address: address, participant_address: address) : bool {
if(!account::exists_at(multisign_address)){
Expand Down Expand Up @@ -319,8 +278,8 @@ module bitcoin_move::multisign_account{
vector::push_back(&mut public_keys, x"0338121decf4ea2dbfd2ad1fe05a32a67448e78bf97a18bc107b4da177c27af752");
vector::push_back(&mut public_keys, x"03786e2d94b8aaac17b2846ea908a245ab8b3c9df7ff34be8c75c27beba8e1f579");
let x_only_public_keys = to_x_only_public_keys(public_keys);
let sorted_public_keys = quick_sort(x_only_public_keys);
let buf = create_multisign_script(2, &sorted_public_keys);
sort::sort(&mut x_only_public_keys);
let buf = create_multisign_script(2, &x_only_public_keys);
std::debug::print(&buf);
let expect_result = x"2008839c624d3da34ae240086f60196409d619f285365cc3498fdd3a90b72599e4ac2038121decf4ea2dbfd2ad1fe05a32a67448e78bf97a18bc107b4da177c27af752ba20786e2d94b8aaac17b2846ea908a245ab8b3c9df7ff34be8c75c27beba8e1f579ba52a2";
assert!(script_buf::into_bytes(buf) == expect_result, 1000);
Expand All @@ -333,8 +292,8 @@ module bitcoin_move::multisign_account{
vector::push_back(&mut public_keys, x"0338121decf4ea2dbfd2ad1fe05a32a67448e78bf97a18bc107b4da177c27af752");
vector::push_back(&mut public_keys, x"03786e2d94b8aaac17b2846ea908a245ab8b3c9df7ff34be8c75c27beba8e1f579");
let x_only_public_keys = to_x_only_public_keys(public_keys);
let sorted_public_keys = quick_sort(x_only_public_keys);
let merkle_root = generate_taproot(2, &sorted_public_keys);
sort::sort(&mut x_only_public_keys);
let merkle_root = generate_taproot(2, &x_only_public_keys);
//std::debug::print(&merkle_root);
let expected_root = @0x2dd3a13df28795832b0efbd279ddf0a432f6942ca82172f82abb2e15461c4402;
assert!(merkle_root == expected_root, 1000);
Expand Down
17 changes: 3 additions & 14 deletions frameworks/bitcoin-move/sources/script_buf.move
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ module bitcoin_move::script_buf{
/// Get the script hash from a P2SH script.
/// This function does not check if the script is a P2SH script, the caller must do that.
public fun p2sh_script_hash(self: &ScriptBuf): vector<u8>{
sub_vector(&self.bytes, 2, 22)
vector::slice(&self.bytes, 2, 22)
}

/// Checks if the given script is a P2PKH script.
Expand All @@ -68,7 +68,7 @@ module bitcoin_move::script_buf{
/// Get the public key hash from a P2PKH script.
/// This function does not check if the script is a P2PKH script, the caller must do that.
public fun p2pkh_pubkey_hash(self: &ScriptBuf): vector<u8>{
sub_vector(&self.bytes, 3, 23)
vector::slice(&self.bytes, 3, 23)
}

public fun is_witness_program(self: &ScriptBuf): bool{
Expand All @@ -85,7 +85,7 @@ module bitcoin_move::script_buf{

/// Get the witness program from a witness program script.
public fun witness_program(self: &ScriptBuf): vector<u8>{
sub_vector(&self.bytes, 2, vector::length(&self.bytes))
vector::slice(&self.bytes, 2, vector::length(&self.bytes))
}

/// try to get a BitcoinAddress from a ScriptBuf.
Expand All @@ -105,17 +105,6 @@ module bitcoin_move::script_buf{
}
}

//TODO put this function in a more general module
fun sub_vector(bytes: &vector<u8>, start: u64, end: u64): vector<u8>{
let result = vector::empty();
let i = start;
while(i < end) {
vector::push_back(&mut result, *vector::borrow(bytes, i));
i = i + 1;
};
result
}

/// Checks if the given script is an OP_RETURN script.
public fun is_op_return(self: &ScriptBuf): bool {
vector::length(&self.bytes) > 0 &&
Expand Down
56 changes: 56 additions & 0 deletions frameworks/move-stdlib/doc/vector.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ the return on investment didn't seem worth it for these simple functions.
- [Function `any`](#0x1_vector_any)
- [Function `all`](#0x1_vector_all)
- [Function `destroy`](#0x1_vector_destroy)
- [Function `range`](#0x1_vector_range)
- [Function `range_with_step`](#0x1_vector_range_with_step)
- [Function `slice`](#0x1_vector_slice)
- [Module Specification](#@Module_Specification_1)


Expand Down Expand Up @@ -95,6 +98,26 @@ The index into the vector is out of bounds



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

The range in <code>slice</code> is invalid.


<pre><code><b>const</b> <a href="vector.md#0x1_vector_EINVALID_SLICE_RANGE">EINVALID_SLICE_RANGE</a>: <a href="u64.md#0x1_u64">u64</a> = 131076;
</code></pre>



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

The step provided in <code>range</code> is invalid, must be greater than zero.


<pre><code><b>const</b> <a href="vector.md#0x1_vector_EINVALID_STEP">EINVALID_STEP</a>: <a href="u64.md#0x1_u64">u64</a> = 131075;
</code></pre>



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

The length of the vectors are not equal.
Expand Down Expand Up @@ -707,6 +730,39 @@ when used in the context of destroying a vector.



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

## Function `range`



<pre><code><b>public</b> <b>fun</b> <a href="vector.md#0x1_vector_range">range</a>(start: <a href="u64.md#0x1_u64">u64</a>, end: <a href="u64.md#0x1_u64">u64</a>): <a href="vector.md#0x1_vector">vector</a>&lt;<a href="u64.md#0x1_u64">u64</a>&gt;
</code></pre>



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

## Function `range_with_step`



<pre><code><b>public</b> <b>fun</b> <a href="vector.md#0x1_vector_range_with_step">range_with_step</a>(start: <a href="u64.md#0x1_u64">u64</a>, end: <a href="u64.md#0x1_u64">u64</a>, step: <a href="u64.md#0x1_u64">u64</a>): <a href="vector.md#0x1_vector">vector</a>&lt;<a href="u64.md#0x1_u64">u64</a>&gt;
</code></pre>



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

## Function `slice`



<pre><code><b>public</b> <b>fun</b> <a href="vector.md#0x1_vector_slice">slice</a>&lt;Element: <b>copy</b>&gt;(self: &<a href="vector.md#0x1_vector">vector</a>&lt;Element&gt;, start: <a href="u64.md#0x1_u64">u64</a>, end: <a href="u64.md#0x1_u64">u64</a>): <a href="vector.md#0x1_vector">vector</a>&lt;Element&gt;
</code></pre>



<a name="@Module_Specification_1"></a>

## Module Specification
36 changes: 36 additions & 0 deletions frameworks/move-stdlib/sources/vector.move
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ module std::vector {
/// The length of the vectors are not equal.
const EVECTORS_LENGTH_MISMATCH: u64 = 0x20002;

/// The step provided in `range` is invalid, must be greater than zero.
const EINVALID_STEP: u64 = 0x20003;

/// The range in `slice` is invalid.
const EINVALID_SLICE_RANGE: u64 = 0x20004;

#[bytecode_instruction]
/// Create an empty vector.
native public fun empty<Element>(): vector<Element>;
Expand Down Expand Up @@ -604,6 +610,36 @@ module std::vector {
for_each_reverse(v, |e| d(e))
}

public fun range(start: u64, end: u64): vector<u64> {
range_with_step(start, end, 1)
}

public fun range_with_step(start: u64, end: u64, step: u64): vector<u64> {
assert!(step > 0, EINVALID_STEP);

let vec = vector[];
while (start < end) {
push_back(&mut vec, start);
start = start + step;
};
vec
}

public fun slice<Element: copy>(
self: &vector<Element>,
start: u64,
end: u64
): vector<Element> {
assert!(start <= end && end <= length(self), EINVALID_SLICE_RANGE);

let vec = vector[];
while (start < end) {
push_back(&mut vec, *borrow(self, start));
start = start + 1;
};
vec
}

// =================================================================
// Module Specification

Expand Down
Loading

0 comments on commit be3a23a

Please sign in to comment.