Skip to content

Commit

Permalink
[Bitcoin] Handle repeat coinbase txid (#2186)
Browse files Browse the repository at this point in the history
* [bitcoin] Handle repeat coinbase tx

* bump version to v0.6.1
  • Loading branch information
jolestar authored Jul 16, 2024
1 parent eca655b commit d7a0bea
Show file tree
Hide file tree
Showing 12 changed files with 165 additions and 87 deletions.
98 changes: 49 additions & 49 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ license = "Apache-2.0"
publish = false
repository = "https://github.com/rooch-network/rooch"
rust-version = "1.78.0"
version = "0.6.0"
version = "0.6.1"

[workspace.dependencies]
# Internal crate dependencies.
Expand Down
Binary file not shown.
52 changes: 28 additions & 24 deletions crates/rooch-framework-tests/src/tests/bitcoin_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,24 +78,24 @@ fn test_submit_block() {
assert_eq!(now_milliseconds, duration.as_millis() as u64);
}

fn test_block_process(height: u64, block: Block) {
fn test_block_process(blocks: Vec<(u64, Block)>) {
let mut binding_test = binding_test::RustBindingTest::new().unwrap();

let block_hash = block.header.block_hash();
let move_block = rooch_types::bitcoin::types::Block::from(block.clone());

binding_test
.execute_l1_block_and_tx(L1BlockWithBody {
block: rooch_types::transaction::L1Block {
chain_id: RoochMultiChainID::Bitcoin.multichain_id(),
block_height: height,
block_hash: block_hash.to_byte_array().to_vec(),
},
block_body: move_block.encode(),
})
.unwrap();

check_utxo(block.txdata, &binding_test);
for (height, block) in blocks {
let block_hash = block.header.block_hash();
let move_block = rooch_types::bitcoin::types::Block::from(block.clone());
binding_test
.execute_l1_block_and_tx(L1BlockWithBody {
block: rooch_types::transaction::L1Block {
chain_id: RoochMultiChainID::Bitcoin.multichain_id(),
block_height: height,
block_hash: block_hash.to_byte_array().to_vec(),
},
block_body: move_block.encode(),
})
.unwrap();
check_utxo(block.txdata, &binding_test);
}
}

fn check_utxo(txs: Vec<Transaction>, binding_test: &binding_test::RustBindingTest) {
Expand Down Expand Up @@ -193,21 +193,25 @@ fn test_real_bocks() {
return;
}
let cases = vec![
(Network::Bitcoin, 91812u64),
(Network::Bitcoin, 818677u64),
(Network::Testnet, 2821527u64),
(Network::Bitcoin, vec![91812u64, 91842u64]),
(Network::Bitcoin, vec![818677u64]),
(Network::Testnet, vec![2821527u64]),
];
for (network, height) in cases {
for (network, heights) in cases {
info!(
"test_real_bocks: network: {:?}, height: {}",
network, height
"test_real_bocks: network: {:?}, height: {:?}",
network, heights
);
let block = load_block(network, height);
test_block_process(height, block);
let blocks = heights
.into_iter()
.map(|height| (height, load_block(network, height)))
.collect();
test_block_process(blocks);
}
}
// Download the bitcoin block via the following command:
// curl -sSL "https://mempool.space/api/block/00000000000af0aed4792b1acee3d966af36cf5def14935db8de83d6f9306f2f/raw" > crates/rooch-framework-tests/blocks/bitcoin/91812.blob
// curl -sSL "https://mempool.space/api/block/00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec/raw" > crates/rooch-framework-tests/blocks/bitcoin/91842.blob
// curl -sSL "https://mempool.space/api/block/000000000000000000020750f322f4e72e99c2f0b9738fb4f46607860bd18c13/raw" > crates/rooch-framework-tests/blocks/bitcoin/818677.blob
// curl -sSL "https://mempool.space/testnet/api/block/0000000016412abe1778a347da773ff8bc087ad1a91ae5daad349bc268285c2d/raw" > crates/rooch-framework-tests/blocks/testnet/2821527.blob
pub(crate) const STATIC_BLOCK_DIR: Dir = include_dir!("blocks");
Expand Down
2 changes: 1 addition & 1 deletion crates/rooch-open-rpc-spec/schemas/openrpc.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"name": "Apache-2.0",
"url": "https://raw.githubusercontent.com/rooch-network/rooch/main/LICENSE"
},
"version": "0.6.0"
"version": "0.6.1"
},
"methods": [
{
Expand Down
2 changes: 1 addition & 1 deletion crates/rooch-types/src/genesis_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,6 @@ pub static G_MAIN_CONFIG: Lazy<GenesisConfig> = Lazy::new(|| {
),
(ObjectState::new_module_store(), ModuleStore::type_layout()),
],
stdlib_version: StdlibVersion::Version(1),
stdlib_version: StdlibVersion::Version(2),
}
});
22 changes: 22 additions & 0 deletions frameworks/bitcoin-move/doc/bitcoin.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@


- [Struct `TxProgressErrorLogEvent`](#0x4_bitcoin_TxProgressErrorLogEvent)
- [Struct `RepeatCoinbaseTxEvent`](#0x4_bitcoin_RepeatCoinbaseTxEvent)
- [Resource `BitcoinBlockStore`](#0x4_bitcoin_BitcoinBlockStore)
- [Constants](#@Constants_0)
- [Function `genesis_init`](#0x4_bitcoin_genesis_init)
Expand Down Expand Up @@ -56,6 +57,17 @@



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

## Struct `RepeatCoinbaseTxEvent`



<pre><code><b>struct</b> <a href="bitcoin.md#0x4_bitcoin_RepeatCoinbaseTxEvent">RepeatCoinbaseTxEvent</a> <b>has</b> <b>copy</b>, drop
</code></pre>



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

## Resource `BitcoinBlockStore`
Expand All @@ -81,6 +93,16 @@



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

https://github.com/bitcoin/bips/blob/master/bip-0034.mediawiki


<pre><code><b>const</b> <a href="bitcoin.md#0x4_bitcoin_BIP_34_HEIGHT">BIP_34_HEIGHT</a>: u64 = 227835;
</code></pre>



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

If the process block failed, we need to stop the system and fix the issue
Expand Down
51 changes: 43 additions & 8 deletions frameworks/bitcoin-move/sources/bitcoin.move
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,20 @@ module bitcoin_move::bitcoin{
const ErrorReorgTooDeep:u64 = 3;

const ORDINAL_GENESIS_HEIGHT:u64 = 767430;
/// https://github.com/bitcoin/bips/blob/master/bip-0034.mediawiki
const BIP_34_HEIGHT:u64 = 227835;

struct TxProgressErrorLogEvent has copy, drop{
txid: address,
message: String,
}

struct RepeatCoinbaseTxEvent has copy, drop{
txid: address,
vout: u32,
block_height: u64,
}

struct BitcoinBlockStore has key{
/// The genesis start block
genesis_block: BlockHeightHash,
Expand Down Expand Up @@ -109,11 +117,18 @@ module bitcoin_move::bitcoin{
}

fun process_coinbase_tx(btc_block_store: &mut BitcoinBlockStore, tx: &Transaction, flotsams: vector<Flotsam>, block_height: u64){
process_coinbase_utxo(tx, flotsams, block_height);
let repeat_txid = process_coinbase_utxo(tx, flotsams, block_height);
let txid = types::tx_id(tx);
table::add(&mut btc_block_store.txs, txid, *tx);
table::add(&mut btc_block_store.tx_to_height, txid, block_height);
table_vec::push_back(&mut btc_block_store.tx_ids, txid);
if (repeat_txid) {
table::upsert(&mut btc_block_store.txs, txid, *tx);
table::upsert(&mut btc_block_store.tx_to_height, txid, block_height);
//We append the repeat txid, the developer want to scan the txs, need to handle the repeat txid
table_vec::push_back(&mut btc_block_store.tx_ids, txid);
}else{
table::add(&mut btc_block_store.txs, txid, *tx);
table::add(&mut btc_block_store.tx_to_height, txid, block_height);
table_vec::push_back(&mut btc_block_store.tx_ids, txid);
}
}

fun process_utxo(tx: &Transaction, block_height: u64): vector<Flotsam>{
Expand Down Expand Up @@ -186,22 +201,23 @@ module bitcoin_move::bitcoin{
};

// create new utxo
handle_new_utxo(tx, &mut output_seals);
handle_new_utxo(tx, &mut output_seals, false, block_height);

simple_multimap::drop(output_seals);
flotsams
}

fun process_coinbase_utxo(tx: &Transaction, flotsams: vector<Flotsam>, block_height: u64){
fun process_coinbase_utxo(tx: &Transaction, flotsams: vector<Flotsam>, block_height: u64) : bool{
let output_seals = simple_multimap::new<u32, UTXOSeal>();
if(need_process_oridinals(block_height)) {
let sat_points = ord::handle_coinbase_tx(tx, flotsams, block_height);
handle_sat_point(sat_points, &mut output_seals);
};

// create new utxo
handle_new_utxo(tx, &mut output_seals);
let repeat_txid = handle_new_utxo(tx, &mut output_seals, true, block_height);
simple_multimap::drop(output_seals);
repeat_txid
}

fun handle_sat_point(sat_points: vector<SatPoint>, output_seals: &mut SimpleMultiMap<u32, UTXOSeal>) {
Expand All @@ -220,15 +236,33 @@ module bitcoin_move::bitcoin{
// output_seals
}

fun handle_new_utxo(tx: &Transaction, output_seals: &mut SimpleMultiMap<u32, UTXOSeal>) {
fun handle_new_utxo(tx: &Transaction, output_seals: &mut SimpleMultiMap<u32, UTXOSeal>, is_coinbase: bool, block_height: u64) :bool {
let txid = types::tx_id(tx);
let txoutput = types::tx_output(tx);
let idx = 0;
let txoutput_len = vector::length(txoutput);
let repeat_txid = false;
while(idx < txoutput_len){
let txout = vector::borrow(txoutput, idx);
let vout = (idx as u32);
let value = types::txout_value(txout);
if (is_coinbase && ((block_height < BIP_34_HEIGHT && network::is_mainnet()) || !network::is_mainnet())) {
let outpoint = types::new_outpoint(txid, vout);
let utxo_id = utxo::derive_utxo_id(outpoint);
//Before BIP34, some coinbase txid may be reused, we need to remove the old utxo
//https://github.com/rooch-network/rooch/issues/2178
if (object::exists_object(utxo_id)){
let utxo = utxo::take(utxo_id);
let seals = utxo::remove(utxo);
simple_multimap::destroy_empty(seals);
event::emit(RepeatCoinbaseTxEvent{
txid: txid,
vout: vout,
block_height: block_height,
});
repeat_txid = true;
};
};
let utxo_obj = utxo::new(txid, vout, value);
let utxo = object::borrow_mut(&mut utxo_obj);
let seal_index = (idx as u32);
Expand All @@ -250,6 +284,7 @@ module bitcoin_move::bitcoin{
bind_bitcoin_address(owner_address, bitcoin_address_opt);
idx = idx + 1;
};
repeat_txid
}


Expand Down
Binary file added frameworks/framework-release/released/2/stdlib
Binary file not shown.
16 changes: 16 additions & 0 deletions frameworks/moveos-stdlib/sources/object.move
Original file line number Diff line number Diff line change
Expand Up @@ -954,6 +954,22 @@ module moveos_std::object {
let TestStruct2 { count: _ } = remove(child2);
}

#[test]
fun test_child_object_with_same_id_remove_and_add_again(){
let parent = new(TestParent {});
let parent_id = id(&parent);
to_shared(parent);
let parent_ref = borrow_mut_object_shared<TestParent>(parent_id);
let id = 1u64;
let child1 = new_with_parent_and_id(parent_ref, id, TestStruct { count: 1 });
let child_id1 = id(&child1);
let TestStruct { count: _ } = remove(child1);
let child2 = new_with_parent_and_id(parent_ref, id, TestStruct { count: 2 });
let child_id2 = id(&child2);
assert!(child_id1 == child_id2, 1000);
let TestStruct { count: _ } = remove(child2);
}

#[test_only]
fun field_key_derive_test<Name: store + copy + drop>(name: Name, expect_result: address){
let key = derive_field_key(name);
Expand Down
3 changes: 2 additions & 1 deletion moveos/moveos/src/moveos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,8 @@ impl MoveOS {
need_respawn
);
}
if need_respawn {
// If it is a system call, we should not respawn the session.
if !is_system_call && need_respawn {
let mut s = session.respawn(system_env);
//Because the session is respawned, the pre_execute function should be called again.
s.execute_function_call(self.system_pre_execute_functions.clone(), false)
Expand Down
4 changes: 2 additions & 2 deletions sdk/typescript/rooch-sdk/src/version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@

// This file is generated by genversion.mjs. Do not edit it directly.

export const PACKAGE_VERSION = '0.2.1'
export const TARGETED_RPC_VERSION = '0.6.0'
export const PACKAGE_VERSION = '0.2.2'
export const TARGETED_RPC_VERSION = '0.6.1'

0 comments on commit d7a0bea

Please sign in to comment.