diff --git a/crates/rooch-framework-tests/src/tests/bitcoin_test.rs b/crates/rooch-framework-tests/src/tests/bitcoin_test.rs index b01109ced3..122dbe131c 100644 --- a/crates/rooch-framework-tests/src/tests/bitcoin_test.rs +++ b/crates/rooch-framework-tests/src/tests/bitcoin_test.rs @@ -147,7 +147,7 @@ fn check_utxo(txs: Vec, binding_test: &binding_test::RustBindingTes let txid = tx.txid(); let rooch_btc_tx = rooch_types::bitcoin::types::Transaction::from(tx.clone()); ord_module - .from_transaction(&rooch_btc_tx, vec![], 0, 0) + .parse_inscription_from_tx(&rooch_btc_tx) .unwrap() .into_iter() .enumerate() @@ -174,9 +174,9 @@ fn check_utxo(txs: Vec, binding_test: &binding_test::RustBindingTes ); let inscription_state = inscription_state.unwrap(); let inscription_object = inscription_state.into_object::().unwrap(); - assert_eq!(inscription_object.value.txid, txid.into_address()); - assert_eq!(inscription_object.value.index, index); - assert_eq!(inscription_object.value.body, inscription.body,); + assert_eq!(inscription_object.value.id.txid, txid.into_address()); + assert_eq!(inscription_object.value.id.index, index); + assert_eq!(inscription_object.value.body, inscription.payload.body,); } } diff --git a/crates/rooch-framework-tests/src/tests/bitcoin_tester_test.rs b/crates/rooch-framework-tests/src/tests/bitcoin_tester_test.rs index b61c25c505..79acda15f8 100644 --- a/crates/rooch-framework-tests/src/tests/bitcoin_tester_test.rs +++ b/crates/rooch-framework-tests/src/tests/bitcoin_tester_test.rs @@ -46,5 +46,5 @@ async fn test_block_790964() { // let expected_sequence_number = 8709019u32; // assert_eq!(inscription.inscription_number, expected_inscription_number); // assert_eq!(inscription.sequence_number, expected_sequence_number); - assert_eq!(inscription.offset, 0u64); + assert_eq!(inscription.location.offset, 0u64); } diff --git a/crates/rooch-framework-tests/src/tests/ord_test.rs b/crates/rooch-framework-tests/src/tests/ord_test.rs index c14d023954..7330831aa3 100644 --- a/crates/rooch-framework-tests/src/tests/ord_test.rs +++ b/crates/rooch-framework-tests/src/tests/ord_test.rs @@ -7,17 +7,16 @@ use crate::{ }; use bitcoin::Transaction; use moveos_types::module_binding::MoveFunctionCaller; -use rooch_types::bitcoin::network::Network; -use rooch_types::bitcoin::ord::Inscription; +use rooch_types::bitcoin::{ + network::Network, + ord::{Envelope, InscriptionRecord}, +}; use tracing::debug; fn decode_inscription( binding_test: &mut binding_test::RustBindingTest, btc_tx: Transaction, - input_utxo_values: Vec, - next_inscription_number: u32, - next_sequence_number: u32, -) -> Vec { +) -> Vec> { debug!("tx_id: {}", btc_tx.txid()); for (i, input) in btc_tx.input.iter().enumerate() { debug!("{}. input: {:?}", i, input.previous_output); @@ -35,14 +34,7 @@ fn decode_inscription( let move_btc_tx: rooch_types::bitcoin::types::Transaction = rooch_types::bitcoin::types::Transaction::from(btc_tx); - ord_module - .from_transaction( - &move_btc_tx, - input_utxo_values, - next_inscription_number, - next_sequence_number, - ) - .unwrap() + ord_module.parse_inscription_from_tx(&move_btc_tx).unwrap() } #[tokio::test] @@ -55,52 +47,33 @@ async fn test_8706753() { Network::Bitcoin, "4b8111663106c242da8580ba38c36f261287b9c35b1aa5974f4c14306905e720", ); - let input_utxo_values = vec![3318u64]; - let next_inscription_number = 8706753; - let next_sequence_number = 8709019; - let inscribe_tx_id = btc_tx.txid(); - let mut inscriptions = decode_inscription( - &mut binding_test, - btc_tx, - input_utxo_values, - next_inscription_number, - next_sequence_number, - ); + //let inscribe_tx_id = btc_tx.txid(); + let inscriptions = decode_inscription(&mut binding_test, btc_tx); assert_eq!(inscriptions.len(), 1); - let inscription = inscriptions.pop().unwrap(); - let object_id = inscription.object_id(); + //let inscription = inscriptions.pop().unwrap(); + //let object_id = inscription.object_id(); - let ord_module = binding_test.as_module_binding::(); + //let ord_module = binding_test.as_module_binding::(); //https://mempool.space/api/tx/e5efc3b2bbf3d738d253e62ffde36b51abb5d12a748abf89d06fca456345fe48 let btc_tx_info: TxInfo = load_tx_info( Network::Bitcoin, "e5efc3b2bbf3d738d253e62ffde36b51abb5d12a748abf89d06fca456345fe48", ); - let input_index = btc_tx_info - .vin - .iter() - .position(|input| input.txid == inscribe_tx_id) - .unwrap() as u64; - let input_utxo_values: Vec = btc_tx_info - .vin - .iter() - .map(|input| input.prevout.value.to_sat()) - .collect(); - let spend_tx: Transaction = btc_tx_info.into(); + let _spend_tx: Transaction = btc_tx_info.into(); //let expect_offset = 316084756u64; - let (is_match, sat_point) = ord_module - .match_utxo_and_generate_sat_point( - inscription.offset, - object_id, - &spend_tx.into(), - input_utxo_values, - input_index, - ) - .unwrap(); - debug!("is_match: {}, sat_point: {:?}", is_match, sat_point); + // let (is_match, sat_point) = ord_module + // .match_utxo_and_generate_sat_point( + // inscription.offset, + // object_id, + // &spend_tx.into(), + // input_utxo_values, + // input_index, + // ) + // .unwrap(); + //debug!("is_match: {}, sat_point: {:?}", is_match, sat_point); //The inscription is spent via fee, so the is_match should be false - assert!(!is_match); + //assert!(!is_match); //TODO how to verify the coinbase sat_point } @@ -114,7 +87,7 @@ async fn test_from_tx() { Network::Bitcoin, "69d52ccb5eb80372b7fc6c4fc3feb17038dd2f58313c5d16302d70f7ef0fff7f", ); - decode_inscription(&mut binding_test, btc_tx, vec![], 0, 0); + decode_inscription(&mut binding_test, btc_tx); } #[tokio::test] @@ -123,8 +96,8 @@ async fn test_local_tx() { let mut binding_test = binding_test::RustBindingTest::new().unwrap(); //commit tx let btx_tx = bitcoin_tx_from_hex("01000000000101303a8b5191266d37103f6c4c6033019b59d98ac468e07f61ddbc6f50b204a7eb0000000000fdffffff029b270000000000002251201d44f728e28f6ffa0b0094edabefb466a348e1e7adbf2ff0e7e70abd2ed8871bcbd0029500000000225120ae68b97d450930db183dede9c33fbb1c147a69672ecc3100a75f9be25c50f0cf014064cf4601628581d59a8dc482f10c67106ffad818869e733d9cabbdab73240da556aa4d04afd5fa1e72d5ca1ea4fa151385e3e8a7596ebd3959212c2a57696c8400000000"); - decode_inscription(&mut binding_test, btx_tx, vec![], 0, 0); + decode_inscription(&mut binding_test, btx_tx); //reveal tx let btc_tx = bitcoin_tx_from_hex("010000000001019cea25cbdacc895f9dbb85e4bfb7aa51d04cc69cc7f75ed49da3ff3f442f2e7f0000000000fdffffff01102700000000000022512036646c76dd6505025341c7cc1cf6c22fcc638c47454945da1948a4637a86f9200340e7f99517f921be44b83854b05d7eb98f7c1a9a0cd373ade7e29c1bcc321190bef1766976726641b69ccc61f9ea62ea55b97e644ec47a15481d3b70b0c44d008f4c207f6ef96528b25ace707fe33f4a23113c824da971dab921a2ad311c309edf0944ac0063036f7264010118746578742f706c61696e3b636861727365743d7574662d38000668656c6c6f0a6821c17f6ef96528b25ace707fe33f4a23113c824da971dab921a2ad311c309edf094400000000"); - decode_inscription(&mut binding_test, btc_tx, vec![], 0, 0); + decode_inscription(&mut binding_test, btc_tx); } diff --git a/crates/rooch-open-rpc-spec/schemas/openrpc.json b/crates/rooch-open-rpc-spec/schemas/openrpc.json index 14d2245415..d7c06222aa 100644 --- a/crates/rooch-open-rpc-spec/schemas/openrpc.json +++ b/crates/rooch-open-rpc-spec/schemas/openrpc.json @@ -896,6 +896,23 @@ } } }, + "BitcoinOutPointView": { + "type": "object", + "required": [ + "txid", + "vout" + ], + "properties": { + "txid": { + "$ref": "#/components/schemas/bitcoin::hash_types::newtypes::Txid" + }, + "vout": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + } + }, "DisplayFieldsView": { "type": "object", "required": [ @@ -1507,24 +1524,25 @@ "InscriptionView": { "type": "object", "required": [ - "bitcoin_txid", "body", - "index", + "charms", + "id", "inscription_number", "is_curse", + "location", "metadata", - "offset", "parents", - "sequence_number", - "txid" + "sequence_number" ], "properties": { - "bitcoin_txid": { - "$ref": "#/components/schemas/bitcoin::hash_types::newtypes::Txid" - }, "body": { "$ref": "#/components/schemas/alloc::vec::Vec" }, + "charms": { + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, "content_encoding": { "anyOf": [ { @@ -1545,10 +1563,8 @@ } ] }, - "index": { - "type": "integer", - "format": "uint32", - "minimum": 0.0 + "id": { + "$ref": "#/components/schemas/rooch_types::bitcoin::ord::BitcoinInscriptionID" }, "inscription_number": { "type": "integer", @@ -1558,6 +1574,9 @@ "is_curse": { "type": "boolean" }, + "location": { + "$ref": "#/components/schemas/SatPointView" + }, "metadata": { "$ref": "#/components/schemas/alloc::vec::Vec" }, @@ -1571,9 +1590,6 @@ } ] }, - "offset": { - "$ref": "#/components/schemas/u64" - }, "parents": { "$ref": "#/components/schemas/alloc::vec::Vec" }, @@ -1591,9 +1607,6 @@ "type": "integer", "format": "uint32", "minimum": 0.0 - }, - "txid": { - "$ref": "#/components/schemas/primitive_types::H256" } } }, @@ -2633,6 +2646,21 @@ } ] }, + "SatPointView": { + "type": "object", + "required": [ + "offset", + "output" + ], + "properties": { + "offset": { + "$ref": "#/components/schemas/u64" + }, + "output": { + "$ref": "#/components/schemas/BitcoinOutPointView" + } + } + }, "ScriptCallView": { "type": "object", "required": [ @@ -3294,6 +3322,9 @@ "rooch_types::address::RoochAddress": { "type": "string" }, + "rooch_types::bitcoin::ord::BitcoinInscriptionID": { + "type": "string" + }, "rooch_types::repair::RepairIndexerType": { "type": "string" }, diff --git a/crates/rooch-rpc-api/src/jsonrpc_types/btc/ord.rs b/crates/rooch-rpc-api/src/jsonrpc_types/btc/ord.rs index dac9532bb4..b974d7bd91 100644 --- a/crates/rooch-rpc-api/src/jsonrpc_types/btc/ord.rs +++ b/crates/rooch-rpc-api/src/jsonrpc_types/btc/ord.rs @@ -1,36 +1,44 @@ // Copyright (c) RoochNetwork // SPDX-License-Identifier: Apache-2.0 -use crate::jsonrpc_types::btc::transaction::{hex_to_txid, TxidView}; +use crate::jsonrpc_types::btc::transaction::hex_to_txid; use crate::jsonrpc_types::{ BytesView, H256View, IndexerObjectStateView, IndexerStateIDView, MoveStringView, ObjectIDVecView, ObjectMetaView, StrView, UnitedAddressView, }; use anyhow::Result; -use bitcoin::hashes::Hash; -use bitcoin::Txid; use moveos_types::move_std::string::MoveString; use moveos_types::state::MoveState; use moveos_types::{moveos_std::object::ObjectID, state::MoveStructType}; -use rooch_types::bitcoin::ord; +use rooch_types::bitcoin::ord::{self, SatPoint}; use rooch_types::bitcoin::ord::{BitcoinInscriptionID, Inscription, InscriptionID}; use rooch_types::indexer::state::ObjectStateFilter; use rooch_types::into_address::IntoAddress; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +use std::fmt::Display; +use std::str::FromStr; -#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq, JsonSchema)] -pub struct BitcoinInscriptionIDView { - pub txid: TxidView, - pub index: u32, +use super::utxo::BitcoinOutPointView; + +pub type BitcoinInscriptionIDView = StrView; + +impl FromStr for BitcoinInscriptionIDView { + type Err = anyhow::Error; + fn from_str(s: &str) -> Result { + Ok(StrView(BitcoinInscriptionID::from_str(s)?)) + } } -impl From for BitcoinInscriptionID { - fn from(inscription: BitcoinInscriptionIDView) -> Self { - BitcoinInscriptionID { - txid: inscription.txid.into(), - index: inscription.index, - } +impl Display for BitcoinInscriptionIDView { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl From for BitcoinInscriptionIDView { + fn from(inscription_id: InscriptionID) -> Self { + StrView(BitcoinInscriptionID::from(inscription_id)) } } @@ -76,14 +84,28 @@ pub struct InscriptionIDView { } #[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)] -pub struct InscriptionView { - pub txid: H256View, - pub bitcoin_txid: TxidView, - pub index: u32, +pub struct SatPointView { + pub output: BitcoinOutPointView, pub offset: StrView, +} + +impl From for SatPointView { + fn from(sat_point: SatPoint) -> Self { + SatPointView { + output: sat_point.outpoint.into(), + offset: StrView(sat_point.offset), + } + } +} + +#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)] +pub struct InscriptionView { + pub id: BitcoinInscriptionIDView, + pub location: SatPointView, pub sequence_number: u32, pub inscription_number: u32, pub is_curse: bool, + pub charms: u16, pub body: BytesView, pub content_encoding: Option, pub content_type: Option, @@ -96,13 +118,12 @@ pub struct InscriptionView { impl From for InscriptionView { fn from(inscription: Inscription) -> Self { InscriptionView { - txid: inscription.txid.into(), - bitcoin_txid: StrView(Txid::from_byte_array(inscription.txid.into_bytes())), - index: inscription.index, - offset: inscription.offset.into(), + id: inscription.id.into(), + location: inscription.location.into(), sequence_number: inscription.sequence_number, inscription_number: inscription.inscription_number, is_curse: inscription.is_curse, + charms: inscription.charms, body: StrView(inscription.body), content_encoding: Option::::from(inscription.content_encoding).map(StrView), content_type: Option::::from(inscription.content_type).map(StrView), diff --git a/crates/rooch-rpc-api/src/jsonrpc_types/btc/utxo.rs b/crates/rooch-rpc-api/src/jsonrpc_types/btc/utxo.rs index 2e7cc275be..aa1e61a6e3 100644 --- a/crates/rooch-rpc-api/src/jsonrpc_types/btc/utxo.rs +++ b/crates/rooch-rpc-api/src/jsonrpc_types/btc/utxo.rs @@ -12,9 +12,10 @@ use bitcoin::Txid; use moveos_types::moveos_std::object::ObjectID; use moveos_types::state::{MoveState, MoveStructType}; +use rooch_types::bitcoin::types::OutPoint; use rooch_types::bitcoin::utxo::{self, UTXO}; use rooch_types::indexer::state::ObjectStateFilter; -use rooch_types::into_address::IntoAddress; +use rooch_types::into_address::{FromAddress, IntoAddress}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -31,6 +32,15 @@ impl From for bitcoin::OutPoint { } } +impl From for BitcoinOutPointView { + fn from(outpoint: OutPoint) -> Self { + BitcoinOutPointView { + txid: Txid::from_address(outpoint.txid).into(), + vout: outpoint.vout, + } + } +} + #[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum UTXOFilterView { diff --git a/crates/rooch-types/src/bitcoin/ord.rs b/crates/rooch-types/src/bitcoin/ord.rs index be137e72be..df948c3705 100644 --- a/crates/rooch-types/src/bitcoin/ord.rs +++ b/crates/rooch-types/src/bitcoin/ord.rs @@ -1,15 +1,13 @@ // Copyright (c) RoochNetwork // SPDX-License-Identifier: Apache-2.0 -use super::types::Transaction; +use super::types::{OutPoint, Transaction}; use crate::addresses::BITCOIN_MOVE_ADDRESS; use crate::into_address::{FromAddress, IntoAddress}; use anyhow::{bail, Result}; use move_core_types::language_storage::{StructTag, TypeTag}; use move_core_types::value::MoveTypeLayout; -use move_core_types::{ - account_address::AccountAddress, ident_str, identifier::IdentStr, value::MoveValue, -}; +use move_core_types::{account_address::AccountAddress, ident_str, identifier::IdentStr}; use moveos_types::state::{MoveState, MoveStructState, MoveStructType}; use moveos_types::{ module_binding::{ModuleBinding, MoveFunctionCaller}, @@ -97,12 +95,12 @@ impl MoveStructState for InscriptionID { #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq)] pub struct Inscription { - pub txid: AccountAddress, - pub index: u32, - pub offset: u64, + pub id: InscriptionID, + pub location: SatPoint, pub sequence_number: u32, pub inscription_number: u32, pub is_curse: bool, + pub charms: u16, pub body: Vec, pub content_encoding: MoveOption, pub content_type: MoveOption, @@ -122,12 +120,12 @@ impl MoveStructType for Inscription { impl MoveStructState for Inscription { fn struct_layout() -> move_core_types::value::MoveStructLayout { move_core_types::value::MoveStructLayout::new(vec![ - AccountAddress::type_layout(), - u32::type_layout(), - u64::type_layout(), + InscriptionID::type_layout(), + SatPoint::type_layout(), u32::type_layout(), u32::type_layout(), bool::type_layout(), + u16::type_layout(), Vec::::type_layout(), MoveOption::::type_layout(), MoveOption::::type_layout(), @@ -142,10 +140,7 @@ impl MoveStructState for Inscription { impl Inscription { pub fn id(&self) -> InscriptionID { - InscriptionID { - txid: self.txid, - index: self.index, - } + self.id } pub fn object_id(&self) -> ObjectID { @@ -251,6 +246,8 @@ pub struct InscriptionStore { pub cursed_inscription_count: u32, /// blessed inscription number generator pub blessed_inscription_count: u32, + pub unbound_inscription_count: u32, + pub lost_sats: u64, /// sequence number generator pub next_sequence_number: u32, } @@ -279,9 +276,8 @@ impl MoveStructState for InscriptionStore { #[derive(Debug, PartialEq, Clone, Eq, Serialize, Deserialize)] pub struct SatPoint { - pub output_index: u32, + pub outpoint: OutPoint, pub offset: u64, - pub object_id: ObjectID, } impl MoveStructType for SatPoint { @@ -293,9 +289,8 @@ impl MoveStructType for SatPoint { impl MoveStructState for SatPoint { fn struct_layout() -> move_core_types::value::MoveStructLayout { move_core_types::value::MoveStructLayout::new(vec![ - u32::type_layout(), + OutPoint::type_layout(), u64::type_layout(), - ObjectID::type_layout(), ]) } } @@ -306,27 +301,19 @@ pub struct OrdModule<'a> { } impl<'a> OrdModule<'a> { - pub const FROM_TRANSACTION_FUNCTION_NAME: &'static IdentStr = - ident_str!("from_transaction_bytes"); + pub const PARSE_INSCRIPTION_FROM_TX_FUNCTION_NAME: &'static IdentStr = + ident_str!("parse_inscription_from_tx"); pub const MATCH_UTXO_AND_GENERATE_SAT_POINT_FUNCTION_NAME: &'static IdentStr = ident_str!("match_utxo_and_generate_sat_point"); - pub fn from_transaction( + pub fn parse_inscription_from_tx( &self, tx: &Transaction, - input_utxo_values: Vec, - next_inscription_number: u32, - next_sequence_number: u32, - ) -> Result> { + ) -> Result>> { let call = Self::create_function_call( - Self::FROM_TRANSACTION_FUNCTION_NAME, + Self::PARSE_INSCRIPTION_FROM_TX_FUNCTION_NAME, vec![], - vec![ - MoveValue::vector_u8(tx.to_bytes()), - input_utxo_values.to_move_value(), - next_inscription_number.to_move_value(), - next_sequence_number.to_move_value(), - ], + vec![tx.to_move_value()], ); let ctx = TxContext::new_readonly_ctx(AccountAddress::ONE); let inscriptions = @@ -335,47 +322,11 @@ impl<'a> OrdModule<'a> { .into_result() .map(|mut values| { let value = values.pop().expect("should have one return value"); - bcs::from_bytes::>(&value.value) + bcs::from_bytes::>>(&value.value) .expect("should be a valid Vec") })?; Ok(inscriptions) } - - pub fn match_utxo_and_generate_sat_point( - &self, - offset: u64, - seal_object_id: ObjectID, - tx: &Transaction, - input_utxo_values: Vec, - input_index: u64, - ) -> Result<(bool, SatPoint)> { - let call = Self::create_function_call( - Self::MATCH_UTXO_AND_GENERATE_SAT_POINT_FUNCTION_NAME, - vec![], - vec![ - offset.to_move_value(), - seal_object_id.to_move_value(), - tx.to_move_value(), - input_utxo_values.to_move_value(), - input_index.to_move_value(), - ], - ); - let ctx = TxContext::new_readonly_ctx(AccountAddress::ONE); - let result = self - .caller - .call_function(&ctx, call)? - .into_result() - .map(|mut values| { - let sat_point_value = values.pop().expect("should have return values"); - let bool_value = values.pop().expect("should have return values"); - let sat_point = bcs::from_bytes::(&sat_point_value.value) - .expect("should be a valid SatPoint"); - let is_match = - bcs::from_bytes::(&bool_value.value).expect("should be a valid bool"); - (is_match, sat_point) - })?; - Ok(result) - } } impl<'a> ModuleBinding<'a> for OrdModule<'a> { diff --git a/crates/rooch/src/commands/statedb/commands/genesis_verify.rs b/crates/rooch/src/commands/statedb/commands/genesis_verify.rs index 0342fc7de7..6510f12f21 100644 --- a/crates/rooch/src/commands/statedb/commands/genesis_verify.rs +++ b/crates/rooch/src/commands/statedb/commands/genesis_verify.rs @@ -790,9 +790,9 @@ pub struct InscriptionForComparison { impl From<&Inscription> for InscriptionForComparison { fn from(ins: &Inscription) -> Self { InscriptionForComparison { - txid: ins.txid, - index: ins.index, - offset: ins.offset, + txid: ins.id.txid, + index: ins.id.index, + offset: ins.location.offset, sequence_number: ins.sequence_number, inscription_number: ins.inscription_number, is_curse: ins.is_curse, diff --git a/crates/rooch/src/commands/statedb/commands/inscription.rs b/crates/rooch/src/commands/statedb/commands/inscription.rs index 6bec7b7222..94cc93081b 100644 --- a/crates/rooch/src/commands/statedb/commands/inscription.rs +++ b/crates/rooch/src/commands/statedb/commands/inscription.rs @@ -10,6 +10,7 @@ use moveos_types::state::{FieldKey, ObjectState}; use rooch_types::address::BitcoinAddress; use rooch_types::bitcoin::ord::{ derive_inscription_id, BitcoinInscriptionID, Inscription, InscriptionID, InscriptionStore, + SatPoint, }; use rooch_types::into_address::IntoAddress; use serde::{Deserialize, Serialize}; @@ -71,14 +72,20 @@ impl InscriptionSource { let txid: AccountAddress = src.id.txid.into_address(); let parents = derive_inscription_ids(src.parent.clone()); - - Inscription { - txid, - index: src.id.index, + let id = InscriptionID::new(txid, src.id.index); + let outpoint = bitcoin::OutPoint::from_str(src.satpoint_outpoint.as_str()).unwrap(); + let location = SatPoint { + outpoint: outpoint.into(), offset: src.satpoint_offset, + }; + Inscription { + id, + location, sequence_number: src.sequence_number, inscription_number: src.inscription_number.unsigned_abs(), is_curse: src.inscription_number.is_negative(), + //TODO how to get charms + charms: 0, body: src.body.clone().unwrap_or_default(), content_encoding: convert_option_string_to_move_type(src.content_encoding.clone()), content_type: convert_option_string_to_move_type(src.content_type.clone()), @@ -94,7 +101,7 @@ impl InscriptionSource { let inscription = self.to_inscription(); let address = self.derive_account_address().unwrap(); - let inscription_id = InscriptionID::new(inscription.txid, inscription.index); + let inscription_id = inscription.id; let obj_id = derive_inscription_id(&inscription_id); let ord_obj = ObjectEntity::new(obj_id.clone(), address, 0u8, None, 0, 0, 0, inscription); @@ -136,6 +143,9 @@ pub(crate) fn create_genesis_inscription_store_object( let inscription_store = InscriptionStore { cursed_inscription_count, blessed_inscription_count, + //TODO set unbound_inscription_count and lost_sats + unbound_inscription_count: 0, + lost_sats: 0, next_sequence_number, }; let obj_id = InscriptionStore::object_id(); diff --git a/examples/bitcoin_plants/sources/plants.move b/examples/bitcoin_plants/sources/plants.move index 66efda545f..2baaa5040b 100644 --- a/examples/bitcoin_plants/sources/plants.move +++ b/examples/bitcoin_plants/sources/plants.move @@ -186,14 +186,12 @@ module bitcoin_plants::plants { #[test_only] use std::option; - #[test_only] - use rooch_framework::genesis; - #[test] fun test() { - genesis::init_for_test(); + bitcoin_move::genesis::init_for_test(); + let id = ord::new_inscription_id(@0x3232423,0); let inscription_obj = ord::new_inscription_object_for_test( - @0x3232423, + id, 0, 0, vector[], diff --git a/frameworks/bitcoin-move/doc/README.md b/frameworks/bitcoin-move/doc/README.md index 7f2fe6439f..274f26a92b 100644 --- a/frameworks/bitcoin-move/doc/README.md +++ b/frameworks/bitcoin-move/doc/README.md @@ -16,6 +16,7 @@ This is the reference documentation of the Bitcoin Move Framework. - [`0x4::bitcoin_hash`](bitcoin_hash.md#0x4_bitcoin_hash) - [`0x4::bitcoin_multisign_validator`](bitcoin_multisign_validator.md#0x4_bitcoin_multisign_validator) - [`0x4::genesis`](genesis.md#0x4_genesis) +- [`0x4::inscription_updater`](inscription_updater.md#0x4_inscription_updater) - [`0x4::multisign_account`](multisign_account.md#0x4_multisign_account) - [`0x4::network`](network.md#0x4_network) - [`0x4::opcode`](opcode.md#0x4_opcode) diff --git a/frameworks/bitcoin-move/doc/bitcoin.md b/frameworks/bitcoin-move/doc/bitcoin.md index 861525a699..7ab19ea82d 100644 --- a/frameworks/bitcoin-move/doc/bitcoin.md +++ b/frameworks/bitcoin-move/doc/bitcoin.md @@ -5,7 +5,7 @@ -- [Struct `TxProgressErrorLogEvent`](#0x4_bitcoin_TxProgressErrorLogEvent) +- [Struct `UTXONotExistsEvent`](#0x4_bitcoin_UTXONotExistsEvent) - [Struct `RepeatCoinbaseTxEvent`](#0x4_bitcoin_RepeatCoinbaseTxEvent) - [Resource `BitcoinBlockStore`](#0x4_bitcoin_BitcoinBlockStore) - [Struct `TransferUTXOEvent`](#0x4_bitcoin_TransferUTXOEvent) @@ -37,12 +37,10 @@ use 0x2::table; use 0x2::table_vec; use 0x2::timestamp; -use 0x2::type_info; use 0x3::address_mapping; use 0x3::bitcoin_address; -use 0x3::chain_id; +use 0x4::inscription_updater; use 0x4::network; -use 0x4::ord; use 0x4::pending_block; use 0x4::types; use 0x4::utxo; @@ -50,13 +48,13 @@ - + -## Struct `TxProgressErrorLogEvent` +## Struct `UTXONotExistsEvent` -
struct TxProgressErrorLogEvent has copy, drop
+
struct UTXONotExistsEvent has copy, drop
 
@@ -108,6 +106,15 @@ + + + + +
const ORDINAL_GENESIS_HEIGHT: u64 = 767430;
+
+ + + https://github.com/bitcoin/bips/blob/master/bip-0034.mediawiki @@ -138,11 +145,11 @@ The reorg is too deep, we need to stop the system and fix the issue - + -
const ORDINAL_GENESIS_HEIGHT: u64 = 767430;
+
const ErrorUTXONotExists: u64 = 4;
 
diff --git a/frameworks/bitcoin-move/doc/inscription_updater.md b/frameworks/bitcoin-move/doc/inscription_updater.md new file mode 100644 index 0000000000..43c0d290cf --- /dev/null +++ b/frameworks/bitcoin-move/doc/inscription_updater.md @@ -0,0 +1,369 @@ + + + +# Module `0x4::inscription_updater` + +The move version inscription_updater +https://github.com/ordinals/ord/blob/e59bd3e73d30ed9bc0b252ba2084bba670d6b0db/src/index/updater/inscription_updater.rs + + +- [Struct `FlotsamNew`](#0x4_inscription_updater_FlotsamNew) +- [Struct `Flotsam`](#0x4_inscription_updater_Flotsam) +- [Struct `InscriptionCreatedEvent`](#0x4_inscription_updater_InscriptionCreatedEvent) +- [Struct `InscriptionTransferredEvent`](#0x4_inscription_updater_InscriptionTransferredEvent) +- [Struct `InscriptionUpdater`](#0x4_inscription_updater_InscriptionUpdater) +- [Struct `Location`](#0x4_inscription_updater_Location) +- [Struct `Range`](#0x4_inscription_updater_Range) +- [Struct `ReinscribeCounter`](#0x4_inscription_updater_ReinscribeCounter) +- [Constants](#@Constants_0) +- [Function `process_tx`](#0x4_inscription_updater_process_tx) +- [Function `need_process_oridinals`](#0x4_inscription_updater_need_process_oridinals) +- [Function `curse_duplicate_field`](#0x4_inscription_updater_curse_duplicate_field) +- [Function `curse_incompleted_field`](#0x4_inscription_updater_curse_incompleted_field) +- [Function `curse_not_at_offset_zero`](#0x4_inscription_updater_curse_not_at_offset_zero) +- [Function `curse_not_in_first_input`](#0x4_inscription_updater_curse_not_in_first_input) +- [Function `curse_pointer`](#0x4_inscription_updater_curse_pointer) +- [Function `curse_pushnum`](#0x4_inscription_updater_curse_pushnum) +- [Function `curse_reinscription`](#0x4_inscription_updater_curse_reinscription) +- [Function `curse_stutter`](#0x4_inscription_updater_curse_stutter) +- [Function `curse_unrecognized_even_field`](#0x4_inscription_updater_curse_unrecognized_even_field) + + +
use 0x1::option;
+use 0x1::string;
+use 0x1::vector;
+use 0x2::compare;
+use 0x2::event;
+use 0x2::object;
+use 0x2::simple_map;
+use 0x2::type_info;
+use 0x4::network;
+use 0x4::ord;
+use 0x4::pending_block;
+use 0x4::script_buf;
+use 0x4::types;
+use 0x4::utxo;
+
+ + + + + +## Struct `FlotsamNew` + + + +
struct FlotsamNew has copy, drop, store
+
+ + + + + +## Struct `Flotsam` + + + +
struct Flotsam has copy, drop, store
+
+ + + + + +## Struct `InscriptionCreatedEvent` + + + +
struct InscriptionCreatedEvent has copy, drop, store
+
+ + + + + +## Struct `InscriptionTransferredEvent` + + + +
struct InscriptionTransferredEvent has copy, drop, store
+
+ + + + + +## Struct `InscriptionUpdater` + + + +
struct InscriptionUpdater has store
+
+ + + + + +## Struct `Location` + + + +
struct Location has copy, drop, store
+
+ + + + + +## Struct `Range` + + + +
struct Range has copy, drop, store
+
+ + + + + +## Struct `ReinscribeCounter` + + + +
struct ReinscribeCounter has copy, drop, store
+
+ + + + + +## Constants + + + + +Curse Inscription + + +
const CURSE_DUPLICATE_FIELD: vector<u8> = [68, 117, 112, 108, 105, 99, 97, 116, 101, 70, 105, 101, 108, 100];
+
+ + + + + + + +
const CURSE_INCOMPLETE_FIELD: vector<u8> = [73, 110, 99, 111, 109, 112, 108, 101, 116, 101, 70, 105, 101, 108, 100];
+
+ + + + + + + +
const CURSE_NOT_AT_OFFSET_ZERO: vector<u8> = [78, 111, 116, 65, 116, 79, 102, 102, 115, 101, 116, 90, 101, 114, 111];
+
+ + + + + + + +
const CURSE_NOT_IN_FIRST_INPUT: vector<u8> = [78, 111, 116, 73, 110, 70, 105, 114, 115, 116, 73, 110, 112, 117, 116];
+
+ + + + + + + +
const CURSE_POINTER: vector<u8> = [80, 111, 105, 110, 116, 101, 114];
+
+ + + + + + + +
const CURSE_PUSHNUM: vector<u8> = [80, 117, 115, 104, 110, 117, 109];
+
+ + + + + + + +
const CURSE_REINSCRIPTION: vector<u8> = [82, 101, 105, 110, 115, 99, 114, 105, 112, 116, 105, 111, 110];
+
+ + + + + + + +
const CURSE_STUTTER: vector<u8> = [83, 116, 117, 116, 116, 101, 114];
+
+ + + + + + + +
const CURSE_UNRECOGNIZED_EVEN_FIELD: vector<u8> = [85, 110, 114, 101, 99, 111, 103, 110, 105, 122, 101, 100, 69, 118, 101, 110, 70, 105, 101, 108, 100];
+
+ + + + + + + +
const ErrorFlotsamNotProcessed: u64 = 2;
+
+ + + + + + + +
const ErrorUTXOBalanceNotMatch: u64 = 1;
+
+ + + + + + + +
const ORDINAL_GENESIS_HEIGHT: u64 = 767430;
+
+ + + + + +## Function `process_tx` + + + +
public(friend) fun process_tx(pending_block: &mut object::Object<pending_block::PendingBlock>, tx: &types::Transaction, input_utxos: &mut vector<utxo::UTXO>): vector<utxo::SealOut>
+
+ + + + + +## Function `need_process_oridinals` + + + +
public(friend) fun need_process_oridinals(block_height: u64): bool
+
+ + + + + +## Function `curse_duplicate_field` + + + +
public fun curse_duplicate_field(): vector<u8>
+
+ + + + + +## Function `curse_incompleted_field` + + + +
public fun curse_incompleted_field(): vector<u8>
+
+ + + + + +## Function `curse_not_at_offset_zero` + + + +
public fun curse_not_at_offset_zero(): vector<u8>
+
+ + + + + +## Function `curse_not_in_first_input` + + + +
public fun curse_not_in_first_input(): vector<u8>
+
+ + + + + +## Function `curse_pointer` + + + +
public fun curse_pointer(): vector<u8>
+
+ + + + + +## Function `curse_pushnum` + + + +
public fun curse_pushnum(): vector<u8>
+
+ + + + + +## Function `curse_reinscription` + + + +
public fun curse_reinscription(): vector<u8>
+
+ + + + + +## Function `curse_stutter` + + + +
public fun curse_stutter(): vector<u8>
+
+ + + + + +## Function `curse_unrecognized_even_field` + + + +
public fun curse_unrecognized_even_field(): vector<u8>
+
diff --git a/frameworks/bitcoin-move/doc/network.md b/frameworks/bitcoin-move/doc/network.md index da64fc26b6..bccd4b6926 100644 --- a/frameworks/bitcoin-move/doc/network.md +++ b/frameworks/bitcoin-move/doc/network.md @@ -19,6 +19,9 @@ - [Function `from_str`](#0x4_network_from_str) - [Function `network_name`](#0x4_network_network_name) - [Function `bech32_hrp`](#0x4_network_bech32_hrp) +- [Function `jubilee_height`](#0x4_network_jubilee_height) +- [Function `first_inscription_height`](#0x4_network_first_inscription_height) +- [Function `subsidy_by_height`](#0x4_network_subsidy_by_height)
use 0x1::string;
@@ -44,6 +47,16 @@ Bitcoin network onchain configuration.
 ## Constants
 
 
+
+
+How many satoshis are in "one bitcoin".
+
+
+
const COIN_VALUE: u64 = 100000000;
+
+ + + @@ -53,6 +66,15 @@ Bitcoin network onchain configuration. + + + + +
const FIRST_POST_SUBSIDY_EPOCH: u32 = 33;
+
+ + + Currently, Move does not support enum types, so we use constants to represent the network type. @@ -94,6 +116,16 @@ Bitcoin's testnet network. + + +How may blocks between halvings. + + +
const SUBSIDY_HALVING_INTERVAL: u32 = 210000;
+
+ + + ## Function `genesis_init` @@ -224,3 +256,41 @@ Get the current network from the onchain configuration.
public fun bech32_hrp(network: u8): string::String
 
+ + + + + +## Function `jubilee_height` + +Ordinals jubilee height. +https://github.com/ordinals/ord/blob/75bf04b22107155f8f8ab6c77f6eefa8117d9ace/src/chain.rs#L49-L56 + + +
public fun jubilee_height(): u64
+
+ + + + + +## Function `first_inscription_height` + +Ordinals first inscription height. +https://github.com/ordinals/ord/blob/75bf04b22107155f8f8ab6c77f6eefa8117d9ace/src/chain.rs#L36-L43 + + +
public fun first_inscription_height(): u64
+
+ + + + + +## Function `subsidy_by_height` + +Block Rewards + + +
public fun subsidy_by_height(height: u64): u64
+
diff --git a/frameworks/bitcoin-move/doc/ord.md b/frameworks/bitcoin-move/doc/ord.md index 0b07377a62..b6e3ef5acf 100644 --- a/frameworks/bitcoin-move/doc/ord.md +++ b/frameworks/bitcoin-move/doc/ord.md @@ -6,7 +6,6 @@ - [Struct `InscriptionID`](#0x4_ord_InscriptionID) -- [Struct `Flotsam`](#0x4_ord_Flotsam) - [Struct `SatPoint`](#0x4_ord_SatPoint) - [Resource `Inscription`](#0x4_ord_Inscription) - [Struct `Envelope`](#0x4_ord_Envelope) @@ -18,30 +17,38 @@ - [Struct `InscriptionEvent`](#0x4_ord_InscriptionEvent) - [Struct `InscriptionCharm`](#0x4_ord_InscriptionCharm) - [Constants](#@Constants_0) -- [Function `curse_duplicate_field`](#0x4_ord_curse_duplicate_field) -- [Function `curse_incompleted_field`](#0x4_ord_curse_incompleted_field) -- [Function `curse_not_at_offset_zero`](#0x4_ord_curse_not_at_offset_zero) -- [Function `curse_not_in_first_input`](#0x4_ord_curse_not_in_first_input) -- [Function `curse_pointer`](#0x4_ord_curse_pointer) -- [Function `curse_pushnum`](#0x4_ord_curse_pushnum) -- [Function `curse_reinscription`](#0x4_ord_curse_reinscription) -- [Function `curse_stutter`](#0x4_ord_curse_stutter) -- [Function `curse_unrecognized_even_field`](#0x4_ord_curse_unrecognized_even_field) - [Function `genesis_init`](#0x4_ord_genesis_init) +- [Function `borrow_mut_inscription_store`](#0x4_ord_borrow_mut_inscription_store) +- [Function `borrow_inscription_store`](#0x4_ord_borrow_inscription_store) +- [Function `blessed_inscription_count`](#0x4_ord_blessed_inscription_count) +- [Function `cursed_inscription_count`](#0x4_ord_cursed_inscription_count) +- [Function `unbound_inscription_count`](#0x4_ord_unbound_inscription_count) +- [Function `lost_sats`](#0x4_ord_lost_sats) +- [Function `next_sequence_number`](#0x4_ord_next_sequence_number) +- [Function `update_cursed_inscription_count`](#0x4_ord_update_cursed_inscription_count) +- [Function `update_blessed_inscription_count`](#0x4_ord_update_blessed_inscription_count) +- [Function `update_next_sequence_number`](#0x4_ord_update_next_sequence_number) +- [Function `update_unbound_inscription_count`](#0x4_ord_update_unbound_inscription_count) +- [Function `update_lost_sats`](#0x4_ord_update_lost_sats) - [Function `new_inscription_id`](#0x4_ord_new_inscription_id) - [Function `derive_inscription_id`](#0x4_ord_derive_inscription_id) - [Function `parse_inscription_id`](#0x4_ord_parse_inscription_id) - [Function `inscription_id_to_string`](#0x4_ord_inscription_id_to_string) - [Function `get_inscription_id_by_sequence_number`](#0x4_ord_get_inscription_id_by_sequence_number) - [Function `get_inscription_next_sequence_number`](#0x4_ord_get_inscription_next_sequence_number) +- [Function `create_object`](#0x4_ord_create_object) +- [Function `transfer_object`](#0x4_ord_transfer_object) +- [Function `take_object`](#0x4_ord_take_object) +- [Function `borrow_object`](#0x4_ord_borrow_object) - [Function `exists_inscription`](#0x4_ord_exists_inscription) - [Function `borrow_inscription`](#0x4_ord_borrow_inscription) -- [Function `borrow_inscription_by_id`](#0x4_ord_borrow_inscription_by_id) -- [Function `spend_utxo`](#0x4_ord_spend_utxo) -- [Function `handle_coinbase_tx`](#0x4_ord_handle_coinbase_tx) -- [Function `process_transaction`](#0x4_ord_process_transaction) - [Function `txid`](#0x4_ord_txid) - [Function `index`](#0x4_ord_index) +- [Function `location`](#0x4_ord_location) +- [Function `sequence_number`](#0x4_ord_sequence_number) +- [Function `inscription_number`](#0x4_ord_inscription_number) +- [Function `is_cursed`](#0x4_ord_is_cursed) +- [Function `charms`](#0x4_ord_charms) - [Function `offset`](#0x4_ord_offset) - [Function `body`](#0x4_ord_body) - [Function `content_encoding`](#0x4_ord_content_encoding) @@ -50,16 +57,31 @@ - [Function `metaprotocol`](#0x4_ord_metaprotocol) - [Function `parents`](#0x4_ord_parents) - [Function `pointer`](#0x4_ord_pointer) +- [Function `id`](#0x4_ord_id) - [Function `inscription_id_txid`](#0x4_ord_inscription_id_txid) - [Function `inscription_id_index`](#0x4_ord_inscription_id_index) -- [Function `new_sat_point`](#0x4_ord_new_sat_point) -- [Function `unpack_sat_point`](#0x4_ord_unpack_sat_point) -- [Function `sat_point_object_id`](#0x4_ord_sat_point_object_id) -- [Function `sat_point_offset`](#0x4_ord_sat_point_offset) -- [Function `sat_point_output_index`](#0x4_ord_sat_point_output_index) -- [Function `new_flotsam`](#0x4_ord_new_flotsam) -- [Function `unpack_flotsam`](#0x4_ord_unpack_flotsam) -- [Function `subsidy_by_height`](#0x4_ord_subsidy_by_height) +- [Function `new_satpoint`](#0x4_ord_new_satpoint) +- [Function `unpack_satpoint`](#0x4_ord_unpack_satpoint) +- [Function `satpoint_offset`](#0x4_ord_satpoint_offset) +- [Function `satpoint_outpoint`](#0x4_ord_satpoint_outpoint) +- [Function `satpoint_vout`](#0x4_ord_satpoint_vout) +- [Function `parse_inscription_from_tx`](#0x4_ord_parse_inscription_from_tx) +- [Function `envelope_input`](#0x4_ord_envelope_input) +- [Function `envelope_offset`](#0x4_ord_envelope_offset) +- [Function `envelope_payload`](#0x4_ord_envelope_payload) +- [Function `envelope_pushnum`](#0x4_ord_envelope_pushnum) +- [Function `envelope_stutter`](#0x4_ord_envelope_stutter) +- [Function `inscription_record_pointer`](#0x4_ord_inscription_record_pointer) +- [Function `inscription_record_parents`](#0x4_ord_inscription_record_parents) +- [Function `inscription_record_unrecognized_even_field`](#0x4_ord_inscription_record_unrecognized_even_field) +- [Function `inscription_record_duplicate_field`](#0x4_ord_inscription_record_duplicate_field) +- [Function `inscription_record_incomplete_field`](#0x4_ord_inscription_record_incomplete_field) +- [Function `inscription_record_metaprotocol`](#0x4_ord_inscription_record_metaprotocol) +- [Function `inscription_record_rune`](#0x4_ord_inscription_record_rune) +- [Function `inscription_record_metadata`](#0x4_ord_inscription_record_metadata) +- [Function `inscription_record_content_type`](#0x4_ord_inscription_record_content_type) +- [Function `inscription_record_content_encoding`](#0x4_ord_inscription_record_content_encoding) +- [Function `inscription_record_body`](#0x4_ord_inscription_record_body) - [Function `add_permanent_state`](#0x4_ord_add_permanent_state) - [Function `contains_permanent_state`](#0x4_ord_contains_permanent_state) - [Function `borrow_permanent_state`](#0x4_ord_borrow_permanent_state) @@ -86,9 +108,21 @@ - [Function `unpack_inscription_event`](#0x4_ord_unpack_inscription_event) - [Function `inscription_event_type_new`](#0x4_ord_inscription_event_type_new) - [Function `inscription_event_type_burn`](#0x4_ord_inscription_event_type_burn) -- [Function `inscription_charm_burned`](#0x4_ord_inscription_charm_burned) -- [Function `exists_inscription_charm`](#0x4_ord_exists_inscription_charm) -- [Function `borrow_inscription_charm`](#0x4_ord_borrow_inscription_charm) +- [Function `charm_coin_flag`](#0x4_ord_charm_coin_flag) +- [Function `charm_cursed_flag`](#0x4_ord_charm_cursed_flag) +- [Function `charm_epic_flag`](#0x4_ord_charm_epic_flag) +- [Function `charm_legendary_flag`](#0x4_ord_charm_legendary_flag) +- [Function `charm_lost_flag`](#0x4_ord_charm_lost_flag) +- [Function `charm_nineball_flag`](#0x4_ord_charm_nineball_flag) +- [Function `charm_rare_flag`](#0x4_ord_charm_rare_flag) +- [Function `charm_reinscription_flag`](#0x4_ord_charm_reinscription_flag) +- [Function `charm_unbound_flag`](#0x4_ord_charm_unbound_flag) +- [Function `charm_uncommon_flag`](#0x4_ord_charm_uncommon_flag) +- [Function `charm_vindicated_flag`](#0x4_ord_charm_vindicated_flag) +- [Function `charm_mythic_flag`](#0x4_ord_charm_mythic_flag) +- [Function `charm_burned_flag`](#0x4_ord_charm_burned_flag) +- [Function `set_charm`](#0x4_ord_set_charm) +- [Function `is_set_charm`](#0x4_ord_is_set_charm) - [Function `view_inscription_charm`](#0x4_ord_view_inscription_charm) @@ -96,9 +130,7 @@ use 0x1::string; use 0x1::vector; use 0x2::bag; -use 0x2::bcs; use 0x2::core_addresses; -use 0x2::event; use 0x2::event_queue; use 0x2::json; use 0x2::object; @@ -106,9 +138,7 @@ use 0x2::string_utils; use 0x2::type_info; use 0x4::bitcoin_hash; -use 0x4::script_buf; use 0x4::types; -use 0x4::utxo;
@@ -124,17 +154,6 @@ - - -## Struct `Flotsam` - - - -
struct Flotsam has copy, drop, store
-
- - - ## Struct `SatPoint` @@ -238,7 +257,7 @@ ## Struct `InscriptionCharm` -Represents the charm of an inscription, containing various properties. +A struct to represent the Inscription Charm
struct InscriptionCharm has copy, drop, store
@@ -260,130 +279,146 @@ Represents the charm of an inscription, containing various properties.
 
 
 
-
+
 
-How many satoshis are in "one bitcoin".
 
 
-
const COIN_VALUE: u64 = 100000000;
+
const CHARM_BURNED_FLAG: u16 = 4096;
 
- + -Curse Inscription -
const CURSE_DUPLICATE_FIELD: vector<u8> = [68, 117, 112, 108, 105, 99, 97, 116, 101, 70, 105, 101, 108, 100];
+
const CHARM_COIN_FLAG: u16 = 1;
 
- + -
const CURSE_INCOMPLETE_FIELD: vector<u8> = [73, 110, 99, 111, 109, 112, 108, 101, 116, 101, 70, 105, 101, 108, 100];
+
const CHARM_CURSED_FLAG: u16 = 2;
 
- + -
const CURSE_NOT_AT_OFFSET_ZERO: vector<u8> = [78, 111, 116, 65, 116, 79, 102, 102, 115, 101, 116, 90, 101, 114, 111];
+
const CHARM_EPIC_FLAG: u16 = 4;
 
- + -
const CURSE_NOT_IN_FIRST_INPUT: vector<u8> = [78, 111, 116, 73, 110, 70, 105, 114, 115, 116, 73, 110, 112, 117, 116];
+
const CHARM_LEGENDARY_FLAG: u16 = 8;
 
- + -
const CURSE_POINTER: vector<u8> = [80, 111, 105, 110, 116, 101, 114];
+
const CHARM_LOST_FLAG: u16 = 16;
 
- + -
const CURSE_PUSHNUM: vector<u8> = [80, 117, 115, 104, 110, 117, 109];
+
const CHARM_MYTHIC_FLAG: u16 = 2048;
 
- + -
const CURSE_REINSCRIPTION: vector<u8> = [82, 101, 105, 110, 115, 99, 114, 105, 112, 116, 105, 111, 110];
+
const CHARM_NINEBALL_FLAG: u16 = 32;
 
- + -
const CURSE_STUTTER: vector<u8> = [83, 116, 117, 116, 116, 101, 114];
+
const CHARM_RARE_FLAG: u16 = 64;
 
- + -
const CURSE_UNRECOGNIZED_EVEN_FIELD: vector<u8> = [85, 110, 114, 101, 99, 111, 103, 110, 105, 122, 101, 100, 69, 118, 101, 110, 70, 105, 101, 108, 100];
+
const CHARM_REINSCRIPTION_FLAG: u16 = 128;
 
- + -
const ErrorMetaprotocolAlreadyRegistered: u64 = 1;
+
const CHARM_UNBOUND_FLAG: u16 = 256;
 
- + -
const ErrorMetaprotocolProtocolMismatch: u64 = 2;
+
const CHARM_UNCOMMON_FLAG: u16 = 512;
+
+ + + + + + + +
const CHARM_VINDICATED_FLAG: u16 = 1024;
+
+ + + + + + + +
const ErrorInscriptionNotExists: u64 = 3;
 
- + -
const FIRST_POST_SUBSIDY_EPOCH: u32 = 33;
+
const ErrorMetaprotocolAlreadyRegistered: u64 = 1;
 
- + -
const INSCRIPTION_CHARM: vector<u8> = [105, 110, 115, 99, 114, 105, 112, 116, 105, 111, 110, 95, 99, 104, 97, 114, 109];
+
const ErrorMetaprotocolProtocolMismatch: u64 = 2;
 
@@ -424,122 +459,145 @@ Curse Inscription - + + +## Function `genesis_init` -How may blocks between halvings. -
const SUBSIDY_HALVING_INTERVAL: u32 = 210000;
+
public(friend) fun genesis_init()
 
- + -## Function `curse_duplicate_field` +## Function `borrow_mut_inscription_store` -
public fun curse_duplicate_field(): vector<u8>
+
public(friend) fun borrow_mut_inscription_store(): &mut ord::InscriptionStore
 
- + -## Function `curse_incompleted_field` +## Function `borrow_inscription_store` -
public fun curse_incompleted_field(): vector<u8>
+
public(friend) fun borrow_inscription_store(): &ord::InscriptionStore
 
- + -## Function `curse_not_at_offset_zero` +## Function `blessed_inscription_count` -
public fun curse_not_at_offset_zero(): vector<u8>
+
public(friend) fun blessed_inscription_count(inscription_store: &ord::InscriptionStore): u32
 
- + -## Function `curse_not_in_first_input` +## Function `cursed_inscription_count` -
public fun curse_not_in_first_input(): vector<u8>
+
public(friend) fun cursed_inscription_count(inscription_store: &ord::InscriptionStore): u32
 
- + -## Function `curse_pointer` +## Function `unbound_inscription_count` -
public fun curse_pointer(): vector<u8>
+
public(friend) fun unbound_inscription_count(inscription_store: &ord::InscriptionStore): u32
 
- + -## Function `curse_pushnum` +## Function `lost_sats` -
public fun curse_pushnum(): vector<u8>
+
public(friend) fun lost_sats(inscription_store: &ord::InscriptionStore): u64
 
- + -## Function `curse_reinscription` +## Function `next_sequence_number` -
public fun curse_reinscription(): vector<u8>
+
public(friend) fun next_sequence_number(inscription_store: &ord::InscriptionStore): u32
 
- + -## Function `curse_stutter` +## Function `update_cursed_inscription_count` -
public fun curse_stutter(): vector<u8>
+
public(friend) fun update_cursed_inscription_count(inscription_store: &mut ord::InscriptionStore, count: u32)
 
- + -## Function `curse_unrecognized_even_field` +## Function `update_blessed_inscription_count` -
public fun curse_unrecognized_even_field(): vector<u8>
+
public(friend) fun update_blessed_inscription_count(inscription_store: &mut ord::InscriptionStore, count: u32)
 
- + + +## Function `update_next_sequence_number` + + + +
public(friend) fun update_next_sequence_number(inscription_store: &mut ord::InscriptionStore, count: u32)
+
-## Function `genesis_init` + -
public(friend) fun genesis_init(_genesis_account: &signer)
+## Function `update_unbound_inscription_count`
+
+
+
+
public(friend) fun update_unbound_inscription_count(inscription_store: &mut ord::InscriptionStore, count: u32)
+
+ + + + + +## Function `update_lost_sats` + + + +
public(friend) fun update_lost_sats(inscription_store: &mut ord::InscriptionStore, count: u64)
 
@@ -611,68 +669,68 @@ Prase InscriptionID from String - + -## Function `exists_inscription` +## Function `create_object` -
public fun exists_inscription(id: ord::InscriptionID): bool
+
public(friend) fun create_object(id: ord::InscriptionID, location: ord::SatPoint, sequence_number: u32, inscription_number: u32, is_cursed: bool, charms: u16, envelope: ord::Envelope<ord::InscriptionRecord>, owner: address): object::ObjectID
 
- + -## Function `borrow_inscription` +## Function `transfer_object` -
public fun borrow_inscription(txid: address, index: u32): &object::Object<ord::Inscription>
+
public(friend) fun transfer_object(inscription_obj: object::Object<ord::Inscription>, to: address, new_location: ord::SatPoint, is_op_return: bool)
 
- + -## Function `borrow_inscription_by_id` +## Function `take_object` -
public fun borrow_inscription_by_id(id: ord::InscriptionID): &ord::Inscription
+
public(friend) fun take_object(inscription_obj_id: object::ObjectID): object::Object<ord::Inscription>
 
- + -## Function `spend_utxo` +## Function `borrow_object` -
public(friend) fun spend_utxo(utxo_obj: &mut object::Object<utxo::UTXO>, tx: &types::Transaction, input_utxo_values: vector<u64>, input_index: u64): (vector<ord::SatPoint>, vector<ord::Flotsam>)
+
public(friend) fun borrow_object(inscription_obj_id: object::ObjectID): &object::Object<ord::Inscription>
 
- + -## Function `handle_coinbase_tx` +## Function `exists_inscription` -
public(friend) fun handle_coinbase_tx(tx: &types::Transaction, flotsams: vector<ord::Flotsam>, block_height: u64): vector<ord::SatPoint>
+
public fun exists_inscription(id: ord::InscriptionID): bool
 
- + -## Function `process_transaction` +## Function `borrow_inscription` -
public(friend) fun process_transaction(tx: &types::Transaction, input_utxo_values: vector<u64>): vector<ord::SatPoint>
+
public fun borrow_inscription(id: ord::InscriptionID): &ord::Inscription
 
@@ -699,6 +757,61 @@ Prase InscriptionID from String + + +## Function `location` + + + +
public fun location(self: &ord::Inscription): &ord::SatPoint
+
+ + + + + +## Function `sequence_number` + + + +
public fun sequence_number(self: &ord::Inscription): u32
+
+ + + + + +## Function `inscription_number` + + + +
public fun inscription_number(self: &ord::Inscription): u32
+
+ + + + + +## Function `is_cursed` + + + +
public fun is_cursed(self: &ord::Inscription): bool
+
+ + + + + +## Function `charms` + + + +
public fun charms(self: &ord::Inscription): u16
+
+ + + ## Function `offset` @@ -771,7 +884,7 @@ Prase InscriptionID from String -
public fun parents(self: &ord::Inscription): vector<object::ObjectID>
+
public fun parents(self: &ord::Inscription): vector<ord::InscriptionID>
 
@@ -787,6 +900,17 @@ Prase InscriptionID from String + + +## Function `id` + + + +
public fun id(self: &ord::Inscription): &ord::InscriptionID
+
+ + + ## Function `inscription_id_txid` @@ -809,94 +933,246 @@ Prase InscriptionID from String - + + +## Function `new_satpoint` + + + +
public fun new_satpoint(outpoint: types::OutPoint, offset: u64): ord::SatPoint
+
+ + + + -## Function `new_sat_point` +## Function `unpack_satpoint` -
public fun new_sat_point(output_index: u32, offset: u64, object_id: object::ObjectID): ord::SatPoint
+
public fun unpack_satpoint(satpoint: ord::SatPoint): (types::OutPoint, u64)
 
- + -## Function `unpack_sat_point` +## Function `satpoint_offset` +Get the SatPoint's offset -
public fun unpack_sat_point(sat_point: ord::SatPoint): (u32, u64, object::ObjectID)
+
public fun satpoint_offset(satpoint: &ord::SatPoint): u64
 
- + -## Function `sat_point_object_id` +## Function `satpoint_outpoint` -Get the SatPoint's object_id +Get the SatPoint's outpoint -
public fun sat_point_object_id(sat_point: &ord::SatPoint): object::ObjectID
+
public fun satpoint_outpoint(satpoint: &ord::SatPoint): &types::OutPoint
 
- + -## Function `sat_point_offset` +## Function `satpoint_vout` + + + +
public fun satpoint_vout(satpoint: &ord::SatPoint): u32
+
+ + + + + +## Function `parse_inscription_from_tx` + + + +
public(friend) fun parse_inscription_from_tx(tx: &types::Transaction): vector<ord::Envelope<ord::InscriptionRecord>>
+
+ + + + + +## Function `envelope_input` + + + +
public(friend) fun envelope_input<T>(envelope: &ord::Envelope<T>): u32
+
+ + + + + +## Function `envelope_offset` + + + +
public(friend) fun envelope_offset<T>(envelope: &ord::Envelope<T>): u32
+
+ + + + + +## Function `envelope_payload` + + + +
public(friend) fun envelope_payload<T>(envelope: &ord::Envelope<T>): &T
+
+ + + + + +## Function `envelope_pushnum` -Get the SatPoint's offset -
public fun sat_point_offset(sat_point: &ord::SatPoint): u64
+
public(friend) fun envelope_pushnum<T>(envelope: &ord::Envelope<T>): bool
 
- + -## Function `sat_point_output_index` +## Function `envelope_stutter` -Get the SatPoint's output_index -
public fun sat_point_output_index(sat_point: &ord::SatPoint): u32
+
public(friend) fun envelope_stutter<T>(envelope: &ord::Envelope<T>): bool
 
- + -## Function `new_flotsam` +## Function `inscription_record_pointer` -
public fun new_flotsam(output_index: u32, offset: u64, object_id: object::ObjectID): ord::Flotsam
+
public(friend) fun inscription_record_pointer(record: &ord::InscriptionRecord): &option::Option<u64>
 
- + -## Function `unpack_flotsam` +## Function `inscription_record_parents` -
public fun unpack_flotsam(flotsam: ord::Flotsam): (u32, u64, object::ObjectID)
+
public(friend) fun inscription_record_parents(record: &ord::InscriptionRecord): &vector<ord::InscriptionID>
 
- + -## Function `subsidy_by_height` +## Function `inscription_record_unrecognized_even_field` -Block Rewards -
public fun subsidy_by_height(height: u64): u64
+
public(friend) fun inscription_record_unrecognized_even_field(record: &ord::InscriptionRecord): bool
+
+ + + + + +## Function `inscription_record_duplicate_field` + + + +
public(friend) fun inscription_record_duplicate_field(record: &ord::InscriptionRecord): bool
+
+ + + + + +## Function `inscription_record_incomplete_field` + + + +
public(friend) fun inscription_record_incomplete_field(record: &ord::InscriptionRecord): bool
+
+ + + + + +## Function `inscription_record_metaprotocol` + + + +
public(friend) fun inscription_record_metaprotocol(record: &ord::InscriptionRecord): &option::Option<string::String>
+
+ + + + + +## Function `inscription_record_rune` + + + +
public(friend) fun inscription_record_rune(record: &ord::InscriptionRecord): &option::Option<u128>
+
+ + + + + +## Function `inscription_record_metadata` + + + +
public(friend) fun inscription_record_metadata(record: &ord::InscriptionRecord): &vector<u8>
+
+ + + + + +## Function `inscription_record_content_type` + + + +
public(friend) fun inscription_record_content_type(record: &ord::InscriptionRecord): &option::Option<string::String>
+
+ + + + + +## Function `inscription_record_content_encoding` + + + +
public(friend) fun inscription_record_content_encoding(record: &ord::InscriptionRecord): &option::Option<string::String>
+
+ + + + + +## Function `inscription_record_body` + + + +
public(friend) fun inscription_record_body(record: &ord::InscriptionRecord): &vector<u8>
 
@@ -1206,44 +1482,167 @@ Get the MetaprotocolValidity's invalid_reason - + + +## Function `charm_coin_flag` + + + +
public fun charm_coin_flag(): u16
+
+ + + + + +## Function `charm_cursed_flag` + + + +
public fun charm_cursed_flag(): u16
+
+ + + + + +## Function `charm_epic_flag` + + + +
public fun charm_epic_flag(): u16
+
+ + + + + +## Function `charm_legendary_flag` + + + +
public fun charm_legendary_flag(): u16
+
+ + + + + +## Function `charm_lost_flag` + + + +
public fun charm_lost_flag(): u16
+
+ + + + + +## Function `charm_nineball_flag` + + + +
public fun charm_nineball_flag(): u16
+
+ + + + -## Function `inscription_charm_burned` +## Function `charm_rare_flag` -Get the InscriptionCharm's burned -
public fun inscription_charm_burned(charm: &ord::InscriptionCharm): bool
+
public fun charm_rare_flag(): u16
 
- + + +## Function `charm_reinscription_flag` + + + +
public fun charm_reinscription_flag(): u16
+
+ + -## Function `exists_inscription_charm` + -Checks if an InscriptionCharm exists for a given InscriptionID. +## Function `charm_unbound_flag` -@param inscription_id - The ID of the inscription -@return Boolean indicating whether the charm exists -
public fun exists_inscription_charm(inscription_id: ord::InscriptionID): bool
+
public fun charm_unbound_flag(): u16
 
- + + +## Function `charm_uncommon_flag` + + + +
public fun charm_uncommon_flag(): u16
+
+ + + + + +## Function `charm_vindicated_flag` + + + +
public fun charm_vindicated_flag(): u16
+
+ + + + + +## Function `charm_mythic_flag` + + + +
public fun charm_mythic_flag(): u16
+
+ + + + + +## Function `charm_burned_flag` + + + +
public fun charm_burned_flag(): u16
+
+ + + + + +## Function `set_charm` + + + +
public fun set_charm(charms: u16, flag: u16): u16
+
+ + -## Function `borrow_inscription_charm` + -Borrows a reference to the InscriptionCharm for a given InscriptionID. +## Function `is_set_charm` -@param inscription_id - The ID of the inscription -@return Reference to the InscriptionCharm -
public fun borrow_inscription_charm(inscription_id: ord::InscriptionID): &ord::InscriptionCharm
+
public fun is_set_charm(charms: u16, flag: u16): bool
 
@@ -1252,8 +1651,8 @@ Borrows a reference to the InscriptionCharm for a given InscriptionID. ## Function `view_inscription_charm` -Views the InscriptionCharm for a given inscription ID string. -Returns None if the inscription doesn't exist or doesn't have a charm. +Views the Inscription charms for a given inscription ID string. +Returns None if the inscription doesn't exist @param inscription_id_str - String representation of the inscription ID @return Option - Some(charm) if exists, None otherwise diff --git a/frameworks/bitcoin-move/doc/pending_block.md b/frameworks/bitcoin-move/doc/pending_block.md index 0212366d73..55e746baaa 100644 --- a/frameworks/bitcoin-move/doc/pending_block.md +++ b/frameworks/bitcoin-move/doc/pending_block.md @@ -14,11 +14,14 @@ PendingStore is used to store the pending blocks and txs, and handle the reorg - [Constants](#@Constants_0) - [Function `genesis_init`](#0x4_pending_block_genesis_init) - [Function `add_pending_block`](#0x4_pending_block_add_pending_block) +- [Function `block_height`](#0x4_pending_block_block_height) +- [Function `take_intermediate`](#0x4_pending_block_take_intermediate) +- [Function `add_intermediate`](#0x4_pending_block_add_intermediate) +- [Function `exists_intermediate`](#0x4_pending_block_exists_intermediate) - [Function `process_pending_tx`](#0x4_pending_block_process_pending_tx) - [Function `finish_pending_tx`](#0x4_pending_block_finish_pending_tx) - [Function `finish_pending_block`](#0x4_pending_block_finish_pending_block) -- [Function `inprocess_block_flotsams_mut`](#0x4_pending_block_inprocess_block_flotsams_mut) -- [Function `inprocess_block_flotsams`](#0x4_pending_block_inprocess_block_flotsams) +- [Function `inprocess_block_pending_block`](#0x4_pending_block_inprocess_block_pending_block) - [Function `inprocess_block_tx`](#0x4_pending_block_inprocess_block_tx) - [Function `inprocess_block_header`](#0x4_pending_block_inprocess_block_header) - [Function `inprocess_block_height`](#0x4_pending_block_inprocess_block_height) @@ -29,12 +32,13 @@ PendingStore is used to store the pending blocks and txs, and handle the reorg
use 0x1::option;
+use 0x1::string;
 use 0x1::vector;
 use 0x2::event;
 use 0x2::object;
 use 0x2::simple_map;
+use 0x2::type_info;
 use 0x3::chain_id;
-use 0x4::ord;
 use 0x4::types;
 
@@ -102,15 +106,6 @@ This is a hot potato struct, can not be store and drop ## Constants - - - - -
const BLOCK_FLOTSAM_KEY: vector<u8> = [98, 108, 111, 99, 107, 95, 102, 108, 111, 116, 115, 97, 109];
-
- - - @@ -205,6 +200,51 @@ This is a hot potato struct, can not be store and drop + + +## Function `block_height` + + + +
public(friend) fun block_height(pending_block: &object::Object<pending_block::PendingBlock>): u64
+
+ + + + + +## Function `take_intermediate` + +The intermediate is used to store the intermediate state during the tx processing + + +
public(friend) fun take_intermediate<I: store>(pending_block: &mut object::Object<pending_block::PendingBlock>): I
+
+ + + + + +## Function `add_intermediate` + + + +
public(friend) fun add_intermediate<I: store>(pending_block: &mut object::Object<pending_block::PendingBlock>, intermediate: I)
+
+ + + + + +## Function `exists_intermediate` + + + +
public(friend) fun exists_intermediate<T>(pending_block: &object::Object<pending_block::PendingBlock>): bool
+
+ + + ## Function `process_pending_tx` @@ -238,24 +278,13 @@ This is a hot potato struct, can not be store and drop - - -## Function `inprocess_block_flotsams_mut` - - - -
public(friend) fun inprocess_block_flotsams_mut(inprocess_block: &mut pending_block::InprocessBlock): &mut vector<ord::Flotsam>
-
- - - - + -## Function `inprocess_block_flotsams` +## Function `inprocess_block_pending_block` -
public(friend) fun inprocess_block_flotsams(inprocess_block: &pending_block::InprocessBlock): vector<ord::Flotsam>
+
public(friend) fun inprocess_block_pending_block(inprocess_block: &mut pending_block::InprocessBlock): &mut object::Object<pending_block::PendingBlock>
 
diff --git a/frameworks/bitcoin-move/doc/types.md b/frameworks/bitcoin-move/doc/types.md index f8d1221448..d0f44c2c47 100644 --- a/frameworks/bitcoin-move/doc/types.md +++ b/frameworks/bitcoin-move/doc/types.md @@ -43,6 +43,7 @@ - [Function `unpack_outpoint`](#0x4_types_unpack_outpoint) - [Function `null_outpoint`](#0x4_types_null_outpoint) - [Function `is_null_outpoint`](#0x4_types_is_null_outpoint) +- [Function `unbound_outpoint`](#0x4_types_unbound_outpoint) - [Function `txout_value`](#0x4_types_txout_value) - [Function `txout_script_pubkey`](#0x4_types_txout_script_pubkey) - [Function `txout_address`](#0x4_types_txout_address) @@ -519,6 +520,18 @@ This value is used for coinbase transactions because they don't have any previou + + +## Function `unbound_outpoint` + +The Inscription unbound outpoint. + + +
public fun unbound_outpoint(): types::OutPoint
+
+ + + ## Function `txout_value` diff --git a/frameworks/bitcoin-move/doc/utxo.md b/frameworks/bitcoin-move/doc/utxo.md index bac6c3da53..2454c8e375 100644 --- a/frameworks/bitcoin-move/doc/utxo.md +++ b/frameworks/bitcoin-move/doc/utxo.md @@ -7,6 +7,7 @@ - [Resource `UTXO`](#0x4_utxo_UTXO) - [Struct `UTXOSeal`](#0x4_utxo_UTXOSeal) +- [Struct `SealOut`](#0x4_utxo_SealOut) - [Resource `BitcoinUTXOStore`](#0x4_utxo_BitcoinUTXOStore) - [Struct `CreatingUTXOEvent`](#0x4_utxo_CreatingUTXOEvent) - [Struct `RemovingUTXOEvent`](#0x4_utxo_RemovingUTXOEvent) @@ -17,6 +18,7 @@ - [Function `next_tx_index`](#0x4_utxo_next_tx_index) - [Function `update_next_tx_index`](#0x4_utxo_update_next_tx_index) - [Function `new`](#0x4_utxo_new) +- [Function `mock_utxo`](#0x4_utxo_mock_utxo) - [Function `derive_utxo_id`](#0x4_utxo_derive_utxo_id) - [Function `value`](#0x4_utxo_value) - [Function `txid`](#0x4_utxo_txid) @@ -27,17 +29,22 @@ - [Function `has_seal`](#0x4_utxo_has_seal) - [Function `get_seals`](#0x4_utxo_get_seals) - [Function `remove_seals`](#0x4_utxo_remove_seals) +- [Function `remove_seals_internal`](#0x4_utxo_remove_seals_internal) - [Function `add_seal`](#0x4_utxo_add_seal) - [Function `transfer`](#0x4_utxo_transfer) - [Function `take`](#0x4_utxo_take) - [Function `remove`](#0x4_utxo_remove) +- [Function `drop`](#0x4_utxo_drop) - [Function `new_utxo_seal`](#0x4_utxo_new_utxo_seal) - [Function `unpack_utxo_seal`](#0x4_utxo_unpack_utxo_seal) +- [Function `new_seal_out`](#0x4_utxo_new_seal_out) +- [Function `unpack_seal_out`](#0x4_utxo_unpack_seal_out) - [Function `add_temp_state`](#0x4_utxo_add_temp_state) - [Function `contains_temp_state`](#0x4_utxo_contains_temp_state) - [Function `borrow_temp_state`](#0x4_utxo_borrow_temp_state) - [Function `borrow_mut_temp_state`](#0x4_utxo_borrow_mut_temp_state) - [Function `remove_temp_state`](#0x4_utxo_remove_temp_state) +- [Function `check_utxo_input`](#0x4_utxo_check_utxo_input)
use 0x1::string;
@@ -46,6 +53,7 @@
 use 0x2::object;
 use 0x2::simple_multimap;
 use 0x2::type_info;
+use 0x3::chain_id;
 use 0x4::types;
 
@@ -74,6 +82,17 @@ The UTXO Object + + +## Struct `SealOut` + + + +
struct SealOut has copy, drop, store
+
+ + + ## Resource `BitcoinUTXOStore` @@ -89,6 +108,7 @@ The UTXO Object ## Struct `CreatingUTXOEvent` +TODO break remove the CreatingUTXOEvent and RemovingUTXOEvent Event for creating UTXO @@ -198,6 +218,17 @@ Event for remove UTXO + + +## Function `mock_utxo` + + + +
public(friend) fun mock_utxo(outpoint: types::OutPoint, value: u64): utxo::UTXO
+
+ + + ## Function `derive_utxo_id` @@ -316,6 +347,17 @@ Maybe we can provide a new way to seal UTXO in the future + + +## Function `remove_seals_internal` + + + +
public(friend) fun remove_seals_internal<T>(utxo: &mut utxo::UTXO): vector<object::ObjectID>
+
+ + + ## Function `add_seal` @@ -355,7 +397,18 @@ Maybe we can provide a new way to seal UTXO in the future -
public(friend) fun remove(utxo_obj: object::Object<utxo::UTXO>): simple_multimap::SimpleMultiMap<string::String, object::ObjectID>
+
public(friend) fun remove(utxo_obj: object::Object<utxo::UTXO>): utxo::UTXO
+
+ + + + + +## Function `drop` + + + +
public(friend) fun drop(utxo: utxo::UTXO)
 
@@ -366,7 +419,7 @@ Maybe we can provide a new way to seal UTXO in the future -
public fun new_utxo_seal(protocol: string::String, seal_object_id: object::ObjectID): utxo::UTXOSeal
+
public(friend) fun new_utxo_seal(protocol: string::String, seal_object_id: object::ObjectID): utxo::UTXOSeal
 
@@ -377,7 +430,29 @@ Maybe we can provide a new way to seal UTXO in the future -
public fun unpack_utxo_seal(utxo_seal: utxo::UTXOSeal): (string::String, object::ObjectID)
+
public(friend) fun unpack_utxo_seal(utxo_seal: utxo::UTXOSeal): (string::String, object::ObjectID)
+
+ + + + + +## Function `new_seal_out` + + + +
public(friend) fun new_seal_out(vout: u32, seal: utxo::UTXOSeal): utxo::SealOut
+
+ + + + + +## Function `unpack_seal_out` + + + +
public(friend) fun unpack_seal_out(seal_out: utxo::SealOut): (u32, utxo::UTXOSeal)
 
@@ -437,3 +512,14 @@ Maybe we can provide a new way to seal UTXO in the future
#[private_generics(#[S])]
 public fun remove_temp_state<S: drop, store>(utxo: &mut object::Object<utxo::UTXO>): S
 
+ + + + + +## Function `check_utxo_input` + + + +
public(friend) fun check_utxo_input(): bool
+
diff --git a/frameworks/bitcoin-move/sources/bitcoin.move b/frameworks/bitcoin-move/sources/bitcoin.move index 88c0277946..8b8b7e9bda 100644 --- a/frameworks/bitcoin-move/sources/bitcoin.move +++ b/frameworks/bitcoin-move/sources/bitcoin.move @@ -4,13 +4,11 @@ module bitcoin_move::bitcoin{ use std::option::{Self, Option}; use std::vector; - use std::string::{Self, String}; + use moveos_std::address::to_string; use moveos_std::event_queue; - use moveos_std::timestamp; use moveos_std::simple_multimap::SimpleMultiMap; - use moveos_std::type_info; use moveos_std::table::{Self, Table}; use moveos_std::bcs; use moveos_std::object::{Self, Object}; @@ -19,15 +17,13 @@ module bitcoin_move::bitcoin{ use moveos_std::signer; use moveos_std::event; - use rooch_framework::chain_id; use rooch_framework::address_mapping; use rooch_framework::bitcoin_address::BitcoinAddress; use bitcoin_move::network; - use bitcoin_move::types::{Self, Block, Header, Transaction, BlockHeightHash}; - use bitcoin_move::ord::{Self, Inscription,Flotsam, SatPoint}; + use bitcoin_move::types::{Self, Block, Header, Transaction, BlockHeightHash, OutPoint}; use bitcoin_move::utxo::{Self, UTXOSeal}; - use bitcoin_move::pending_block; + use bitcoin_move::pending_block::{Self, PendingBlock}; friend bitcoin_move::genesis; @@ -36,14 +32,14 @@ module bitcoin_move::bitcoin{ const ErrorBlockAlreadyProcessed:u64 = 2; /// The reorg is too deep, we need to stop the system and fix the issue const ErrorReorgTooDeep:u64 = 3; + const ErrorUTXONotExists:u64 = 4; 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 UTXONotExistsEvent has copy, drop{ + outpoint: OutPoint, } struct RepeatCoinbaseTxEvent has copy, drop{ @@ -117,18 +113,11 @@ module bitcoin_move::bitcoin{ btc_block_store.latest_block = option::some(types::new_block_height_hash(block_height, block_hash)); } - fun process_tx(btc_block_store: &mut BitcoinBlockStore, tx: &Transaction, block_height: u64): vector{ - let flotsams = process_utxo(tx, 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); - flotsams - } - - fun process_coinbase_tx(btc_block_store: &mut BitcoinBlockStore, tx: &Transaction, flotsams: vector, block_height: u64){ - let repeat_txid = process_coinbase_utxo(tx, flotsams, block_height); + fun process_tx(btc_block_store: &mut BitcoinBlockStore, pblock: &mut Object, tx: &Transaction, is_coinbase: bool){ + let block_height = pending_block::block_height(pblock); let txid = types::tx_id(tx); + let repeat_txid = process_utxo(block_height, pblock, tx, is_coinbase); + if (repeat_txid) { table::upsert(&mut btc_block_store.txs, txid, *tx); table::upsert(&mut btc_block_store.tx_to_height, txid, block_height); @@ -141,34 +130,21 @@ module bitcoin_move::bitcoin{ } } - fun process_utxo(tx: &Transaction, block_height: u64): vector{ + fun process_utxo(block_height: u64, pending_block: &mut Object, tx: &Transaction, is_coinbase: bool) : bool{ let txinput = types::tx_input(tx); - let flotsams = vector::empty(); - - let previous_outputs = vector::empty(); - vector::for_each(*txinput, |txin| { - let outpoint = *types::txin_previous_output(&txin); - vector::push_back(&mut previous_outputs, outpoint); - }); - let input_utxo_values = vector::empty(); - vector::for_each(previous_outputs, |output| { - let utxo_value = if(utxo::exists_utxo(output)){ - let utxo = utxo::borrow_utxo(output); - utxo::value(object::borrow(utxo)) - } else { - 0 - }; - vector::push_back(&mut input_utxo_values, utxo_value); - }); + let input_utxos = vector::empty(); let idx = 0; let output_seals = simple_multimap::new(); - let need_process_oridinal = need_process_oridinals(block_height); let sender: Option
= option::none
(); let find_sender: bool = false; while (idx < vector::length(txinput)) { let txin = vector::borrow(txinput, idx); let outpoint = *types::txin_previous_output(txin); + if (outpoint == types::null_outpoint()) { + idx = idx + 1; + continue + }; if (utxo::exists_utxo(outpoint)) { let object_id = utxo::derive_utxo_id(outpoint); let utxo_obj = utxo::take(object_id); @@ -177,83 +153,47 @@ module bitcoin_move::bitcoin{ sender = option::some(utxo_owner); find_sender = true; }; - if(need_process_oridinal) { - let (sat_points, utxo_flotsams) = ord::spend_utxo(&mut utxo_obj, tx, input_utxo_values, idx); - handle_sat_point(sat_points, &mut output_seals); - vector::append(&mut flotsams, utxo_flotsams); - }; - - let seals = utxo::remove(utxo_obj); - //The seals should be empty after utxo is spent - simple_multimap::destroy_empty(seals); + let utxo = utxo::remove(utxo_obj); + vector::push_back(&mut input_utxos, utxo); }else { - event::emit(TxProgressErrorLogEvent{ - txid: types::tx_id(tx), - message: string::utf8(b"utxo not exists"), + event::emit(UTXONotExistsEvent{ + outpoint: outpoint, }); //We allow the utxo not exists in the utxo store, because we may not sync the block from genesis //But we should not allow the utxo not exists in the mainnet - if(chain_id::is_main()){ - abort ErrorBlockProcessError + if(utxo::check_utxo_input()){ + abort ErrorUTXONotExists }; + let utxo = utxo::mock_utxo(outpoint, 0); + vector::push_back(&mut input_utxos, utxo); }; idx = idx + 1; }; - // Transfer and inscribe may happen at the same transaction - if(need_process_oridinal) { - let sat_points = ord::process_transaction(tx, input_utxo_values); - let idx = 0; - let protocol = type_info::type_name(); - let sat_points_len = vector::length(&sat_points); - while (idx < sat_points_len) { - let sat_point = vector::pop_back(&mut sat_points); - let output_index = ord::sat_point_output_index(&sat_point); - let seal_object_id = ord::sat_point_object_id(&sat_point); - let utxo_seal = utxo::new_utxo_seal(protocol, seal_object_id); + let seal_outs = bitcoin_move::inscription_updater::process_tx(pending_block, tx, &mut input_utxos); + let seal_outs_len = vector::length(&seal_outs); + if (seal_outs_len > 0) { + let seal_out_idx = 0; + while (seal_out_idx < seal_outs_len) { + let seal_out = vector::pop_back(&mut seal_outs); + let (output_index, utxo_seal) = utxo::unpack_seal_out(seal_out); simple_multimap::add(&mut output_seals, output_index, utxo_seal); - idx = idx + 1; + seal_out_idx = seal_out_idx + 1; }; }; - + // create new utxo - handle_new_utxo(tx, &mut output_seals, false, block_height, sender); + let repeat_txid = handle_new_utxo(tx, is_coinbase, &mut output_seals, block_height, sender); simple_multimap::drop(output_seals); - flotsams - } - - fun process_coinbase_utxo(tx: &Transaction, flotsams: vector, block_height: u64) : bool{ - let output_seals = simple_multimap::new(); - 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 - let repeat_txid = handle_new_utxo(tx, &mut output_seals, true, block_height, option::none()); - simple_multimap::drop(output_seals); + vector::for_each(input_utxos, |utxo| { + utxo::drop(utxo); + }); repeat_txid } - fun handle_sat_point(sat_points: vector, output_seals: &mut SimpleMultiMap) { - if (!vector::is_empty(&sat_points)) { - let protocol = type_info::type_name(); - let j = 0; - let sat_points_len = vector::length(&sat_points); - while (j < sat_points_len) { - let sat_point = vector::pop_back(&mut sat_points); - let (output_index, _offset, object_id) = ord::unpack_sat_point(sat_point); - let utxo_seal = utxo::new_utxo_seal(protocol, object_id); - simple_multimap::add(output_seals, output_index, utxo_seal); - j = j + 1; - }; - }; - // output_seals - } - - fun handle_new_utxo(tx: &Transaction, output_seals: &mut SimpleMultiMap, is_coinbase: bool, block_height: u64, sender: Option
) :bool { + fun handle_new_utxo(tx: &Transaction, is_coinbase: bool, output_seals: &mut SimpleMultiMap, block_height: u64, sender: Option
) :bool { let txid = types::tx_id(tx); let txoutput = types::tx_output(tx); let idx = 0; @@ -269,9 +209,10 @@ module bitcoin_move::bitcoin{ //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); + let utxo_obj = utxo::take(utxo_id); + let utxo = utxo::remove(utxo_obj); + utxo::drop(utxo); + //simple_multimap::destroy_empty(seals); event::emit(RepeatCoinbaseTxEvent{ txid: txid, vout: vout, @@ -312,8 +253,7 @@ module bitcoin_move::bitcoin{ value }); }; - - + //Auto create address mapping, we ensure when UTXO object create, the address mapping is recored let bitcoin_address_opt = types::txout_address(txout); bind_bitcoin_address(owner_address, bitcoin_address_opt); @@ -343,16 +283,14 @@ module bitcoin_move::bitcoin{ let btc_block_store = object::borrow_mut(btc_block_store_obj); let inprocess_block = pending_block::process_pending_tx(block_hash, txid); let block_height = pending_block::inprocess_block_height(&inprocess_block); - let tx = pending_block::inprocess_block_tx(&inprocess_block); - if(types::is_coinbase_tx(tx)){ - let flotsams = pending_block::inprocess_block_flotsams(&inprocess_block); - process_coinbase_tx(btc_block_store, tx, flotsams, block_height); + let tx = *pending_block::inprocess_block_tx(&inprocess_block); + let pblock = pending_block::inprocess_block_pending_block(&mut inprocess_block); + let is_coinbase = types::is_coinbase_tx(&tx); + process_tx(btc_block_store, pblock, &tx, is_coinbase); + if(is_coinbase){ let header = pending_block::finish_pending_block(inprocess_block); process_block_header(btc_block_store, block_height, block_hash, header); }else{ - let tx_flotsams = process_tx(btc_block_store, tx, block_height); - let flotsams = pending_block::inprocess_block_flotsams_mut(&mut inprocess_block); - vector::append(flotsams, tx_flotsams); pending_block::finish_pending_tx(inprocess_block); }; } @@ -451,14 +389,6 @@ module bitcoin_move::bitcoin{ } } - fun need_process_oridinals(block_height: u64) : bool { - if(network::is_mainnet()){ - block_height >= ORDINAL_GENESIS_HEIGHT - }else{ - true - } - } - fun bind_bitcoin_address(rooch_address: address, bitcoin_address_opt: Option) { //Auto create address mapping if not exist if(option::is_some(&bitcoin_address_opt)) { diff --git a/frameworks/bitcoin-move/sources/genesis.move b/frameworks/bitcoin-move/sources/genesis.move index fa19467c3f..59fa2c3d83 100644 --- a/frameworks/bitcoin-move/sources/genesis.move +++ b/frameworks/bitcoin-move/sources/genesis.move @@ -29,7 +29,7 @@ module bitcoin_move::genesis{ let genesis_context = option::destroy_some(genesis_context_option); network::genesis_init(genesis_context.network); utxo::genesis_init(); - ord::genesis_init(&genesis_account); + ord::genesis_init(); bitcoin::genesis_init(&genesis_account, genesis_context.genesis_block_height, genesis_context.genesis_block_hash); pending_block::genesis_init(genesis_context.reorg_block_count); bitcoin_multisign_validator::genesis_init(); diff --git a/frameworks/bitcoin-move/sources/inscription_updater.move b/frameworks/bitcoin-move/sources/inscription_updater.move new file mode 100644 index 0000000000..883927f6a8 --- /dev/null +++ b/frameworks/bitcoin-move/sources/inscription_updater.move @@ -0,0 +1,629 @@ +// Copyright (c) RoochNetwork +// SPDX-License-Identifier: Apache-2.0 + +/// The move version inscription_updater +/// https://github.com/ordinals/ord/blob/e59bd3e73d30ed9bc0b252ba2084bba670d6b0db/src/index/updater/inscription_updater.rs +module bitcoin_move::inscription_updater{ + + use std::vector; + use std::option::{Self, Option}; + use std::string::String; + + use moveos_std::object::{Self, Object}; + use moveos_std::simple_map::{Self, SimpleMap}; + use moveos_std::sort; + use moveos_std::event; + use moveos_std::type_info; + + use bitcoin_move::network; + use bitcoin_move::types::{Self, Transaction, TxOut}; + use bitcoin_move::ord::{Self, InscriptionID, SatPoint, Inscription, Envelope, InscriptionRecord}; + use bitcoin_move::pending_block::{Self, PendingBlock}; + use bitcoin_move::utxo::{Self, UTXO, SealOut}; + use bitcoin_move::script_buf; + + friend bitcoin_move::bitcoin; + + const ORDINAL_GENESIS_HEIGHT:u64 = 767430; + + const ErrorUTXOBalanceNotMatch: u64 = 1; + const ErrorFlotsamNotProcessed: u64 = 2; + + struct FlotsamNew has copy, drop, store{ + cursed: bool, + fee: u64, + hidden: bool, + parents: vector, + pointer: Option, + reinscription: bool, + unbound: bool, + vindicated: bool, + envelope: Envelope, + } + + struct Flotsam has copy, drop, store { + inscription_id: InscriptionID, + offset: u64, + new: Option, + old: Option, + } + + //TODO merge the onchain event and offchain event + struct InscriptionCreatedEvent has copy, drop, store { + block_height: u64, + charms: u16, + inscription_id: InscriptionID, + location: Option, + parent_inscription_ids: vector, + sequence_number: u32, + } + + struct InscriptionTransferredEvent has copy, drop, store { + block_height: u64, + inscription_id: InscriptionID, + new_location: SatPoint, + old_location: SatPoint, + sequence_number: u32, + } + + struct InscriptionUpdater has store { + block_height: u64, + seal_protocol: String, + flotsams: vector, + lost_sats: u64, + reward: u64, + blessed_inscription_count: u32, + cursed_inscription_count: u32, + unbound_inscription_count: u32, + next_sequence_number: u32, + } + + struct Location has copy, drop, store { + new_satpoint: SatPoint, + flotsam: Flotsam, + is_op_return: bool, + owner: address, + } + + struct Range has copy, drop, store { + start: u64, + end: u64, + } + + struct ReinscribeCounter has copy, drop, store{ + inscription_id: InscriptionID, + count: u64, + } + + public(friend) fun process_tx(pending_block: &mut Object, tx: &Transaction, input_utxos: &mut vector): vector { + let block_height = pending_block::block_height(pending_block); + let seal_outs = vector::empty(); + if(!need_process_oridinals(block_height)){ + return seal_outs + }; + let check_utxo_input = utxo::check_utxo_input(); + let seal_protocol = type_info::type_name(); + + let txid = types::tx_id(tx); + let txinput = types::tx_input(tx); + let txoutput: &vector = types::tx_output(tx); + let is_coinbase = types::is_coinbase_tx(tx); + + let id_counter = 0; + let inscribed_offsets = simple_map::new(); + let floating_inscriptions = vector::empty(); + + let jubilant = block_height >= network::jubilee_height(); + + let total_input_value = 0; + + let output_len = vector::length(txoutput); + let output_idx = 0; + let total_output_value = 0; + while(output_idx < output_len){ + total_output_value = total_output_value + types::txout_value(vector::borrow(txoutput, output_idx)); + output_idx = output_idx + 1; + }; + + let envelopes = ord::parse_inscription_from_tx(tx); + //reverse the envelopes for pop back to iterate + vector::reverse(&mut envelopes); + + let updater = if (pending_block::exists_intermediate(pending_block)){ + pending_block::take_intermediate(pending_block) + }else{ + let inscription_store = ord::borrow_inscription_store(); + let blessed_inscription_count = ord::blessed_inscription_count(inscription_store); + let cursed_inscription_count = ord::cursed_inscription_count(inscription_store); + let unbound_inscription_count = ord::unbound_inscription_count(inscription_store); + let lost_sats = ord::lost_sats(inscription_store); + let next_sequence_number = ord::next_sequence_number(inscription_store); + InscriptionUpdater{ + block_height, + seal_protocol, + flotsams: vector::empty(), + lost_sats, + reward: network::subsidy_by_height(block_height), + blessed_inscription_count, + cursed_inscription_count, + unbound_inscription_count, + next_sequence_number, + } + }; + + let input_idx = 0; + let input_len = vector::length(txinput); + while(input_idx < input_len){ + let input = vector::borrow(txinput, input_idx); + // skip subsidy since no inscriptions possible + if (types::is_null_outpoint(types::txin_previous_output(input))){ + total_input_value = total_input_value + network::subsidy_by_height(block_height); + input_idx = input_idx + 1; + continue + }; + let utxo = vector::borrow_mut(input_utxos, input_idx); + + //Process inscription transfer + let seals = utxo::remove_seals_internal(utxo); + + let seal_idx = 0; + let seals_len = vector::length(&seals); + while (seal_idx < seals_len) { + let seal_object_id = *vector::borrow(&mut seals, seal_idx); + let inscription_obj = ord::borrow_object(seal_object_id); + let inscription = object::borrow(inscription_obj); + let inscription_id = *ord::id(inscription); + + let old_location = ord::location(inscription); + + let offset = total_input_value + ord::satpoint_offset(old_location); + + let flotsam = Flotsam { + inscription_id: inscription_id, + offset: offset, + new: option::none(), + old: option::some(*old_location), + }; + vector::push_back(&mut floating_inscriptions, flotsam); + simple_map::add(&mut inscribed_offsets, offset, ReinscribeCounter{inscription_id, count: 1}); + seal_idx = seal_idx + 1; + }; + + // Process inscription creation + let offset = total_input_value; + total_input_value = total_input_value + utxo::value(utxo); + let ins_idx = 0; + let ins_len = vector::length(&envelopes); + + while (ins_idx < ins_len) { + let envelope = vector::pop_back(&mut envelopes); + //let (input, offset, pushnum, stutter, payload) = ord::unpack_envelope(envelope); + let input = ord::envelope_input(&envelope); + let payload = ord::envelope_payload(&envelope); + if (input != (input_idx as u32)){ + break + }; + let inscription_id = ord::new_inscription_id(txid, id_counter); + let pointer = *ord::inscription_record_pointer(payload); + let parents = *ord::inscription_record_parents(payload); + + let unrecongized_even_field = ord::inscription_record_unrecognized_even_field(payload); + + //handle curse before fix the offset via pointer + let curse = handle_curse_inscription(&envelope, offset, &inscribed_offsets); + + let offset = if (option::is_some(&pointer)){ + let p = option::destroy_some(pointer); + if (p < total_output_value){ + p + }else{ + offset + } + }else{ + offset + }; + + let flotsam = Flotsam{ + inscription_id: inscription_id, + offset: offset, + new: option::some(FlotsamNew{ + cursed: option::is_some(&curse) && !jubilant, + fee: 0, + //TODO should we handle the hidden + hidden: false, + parents, + pointer, + reinscription: simple_map::contains_key(&inscribed_offsets, &offset), + unbound: (utxo::value(utxo) == 0 + || curse == option::some(curse_unrecognized_even_field()) + || unrecongized_even_field), + vindicated: option::is_some(&curse) && jubilant, + envelope: envelope, + }), + old: option::none(), + }; + vector::push_back(&mut floating_inscriptions, flotsam); + id_counter = id_counter + 1; + ins_idx = ins_idx + 1; + }; + input_idx = input_idx + 1; + }; + + //TODO process the parent + //TODO do we need to handle the fee + + if(is_coinbase) { + //remove all the flotsams from the previous txs + let flotsams = vector::trim(&mut updater.flotsams, 0); + vector::append(&mut floating_inscriptions, flotsams); + }; + + sort::sort_by_key(&mut floating_inscriptions, |flotsam| { + let flotsam: &Flotsam = flotsam; + &flotsam.offset + }); + + let range_to_vout = simple_map::new(); + let new_locations = vector::empty(); + let output_value = 0; + + let output_idx = 0; + let output_len = vector::length(txoutput); + let flotsam_idx = 0; + let flotsam_len = vector::length(&floating_inscriptions); + while(output_idx < output_len){ + //Skip the output process if there is no flotsam + if(flotsam_len == 0){ + break + }; + let output = vector::borrow(txoutput, output_idx); + let value = types::txout_value(output); + let output_script_buf = types::txout_script_pubkey(output); + let is_op_return = script_buf::is_op_return(output_script_buf); + let output_address = types::txout_object_address(output); + let end = output_value + value; + + while(flotsam_idx < flotsam_len){ + let flotsam = vector::borrow(&floating_inscriptions, flotsam_idx); + if (flotsam.offset >= end){ + break + }; + + let new_satpoint = ord::new_satpoint(types::new_outpoint(txid, (output_idx as u32)), flotsam.offset - output_value); + + vector::push_back(&mut new_locations, + Location{ + new_satpoint, + flotsam: *flotsam, + is_op_return, + owner: output_address, + }); + flotsam_idx = flotsam_idx + 1; + }; + + simple_map::add(&mut range_to_vout, output_idx, Range{start: output_value, end: end}); + output_value = end; + + output_idx = output_idx + 1; + }; + + let new_locations_len = vector::length(&new_locations); + let new_locations_idx = 0; + while(new_locations_idx < new_locations_len){ + let location = vector::borrow(&new_locations, new_locations_idx); + let new_satpoint = if (option::is_some(&location.flotsam.new)){ + let new_info = option::borrow(&location.flotsam.new); + if (option::is_some(&new_info.pointer)) { + let pointer = *option::borrow(&new_info.pointer); + if (pointer < output_value) { + let (found, vout, start) = find_range_for_pointer(&range_to_vout, pointer); + if (found) { + //The ordinals code update the flotsam offset via pointer. + //And use the offset to calculate Sat, but we do not handle the Sat, + //So this update is useless. + //location.flotsam.offset = pointer; + ord::new_satpoint(types::new_outpoint(txid, (vout as u32)), pointer - start) + } else { + location.new_satpoint + } + } else { + location.new_satpoint + } + } else { + location.new_satpoint + } + } else { + location.new_satpoint + }; + + update_inscription_location(&mut updater, &mut seal_outs, &location.flotsam, new_satpoint, location.is_op_return, location.owner); + new_locations_idx = new_locations_idx + 1; + }; + + if (is_coinbase) { + while(flotsam_idx < flotsam_len){ + let flotsam = vector::borrow(&floating_inscriptions, flotsam_idx); + let new_satpoint = ord::new_satpoint(types::null_outpoint(), updater.lost_sats + flotsam.offset - output_value); + update_inscription_location(&mut updater, &mut seal_outs, flotsam, new_satpoint, false, @bitcoin_move); + flotsam_idx = flotsam_idx + 1; + }; + updater.lost_sats = updater.lost_sats + updater.reward - output_value; + }else{ + while(flotsam_idx < flotsam_len){ + let flotsam = vector::borrow_mut(&mut floating_inscriptions, flotsam_idx); + let offset = updater.reward + flotsam.offset - output_value; + flotsam.offset = offset; + vector::push_back(&mut updater.flotsams, *flotsam); + flotsam_idx = flotsam_idx + 1; + }; + let tx_fee = if (total_input_value >= total_output_value){ + total_input_value - total_output_value + }else{ + if(check_utxo_input){ + abort ErrorUTXOBalanceNotMatch + }; + 0 + }; + updater.reward = updater.reward + tx_fee; + }; + + let inscription_store = ord::borrow_mut_inscription_store(); + ord::update_cursed_inscription_count(inscription_store, updater.cursed_inscription_count); + ord::update_blessed_inscription_count(inscription_store, updater.blessed_inscription_count); + ord::update_unbound_inscription_count(inscription_store, updater.unbound_inscription_count); + ord::update_lost_sats(inscription_store, updater.lost_sats); + ord::update_next_sequence_number(inscription_store, updater.next_sequence_number); + + if(is_coinbase){ + //The updater lifetime is the same as the pending_block + //The coinbase is the last tx to process, so we can drop the updater here + drop(updater); + }else{ + pending_block::add_intermediate(pending_block, updater); + }; + seal_outs + } + + fun update_inscription_location(updater: &mut InscriptionUpdater, seal_outs: &mut vector, flotsam: &Flotsam, new_satpoint: SatPoint, is_op_return: bool, owner: address) { + let inscription_id = flotsam.inscription_id; + let (unbound, inscription_obj_id) = if (option::is_some(&flotsam.old)){ + let old_satpoint = *option::borrow(&flotsam.old); + + + let inscription_obj_id = ord::derive_inscription_id(inscription_id); + let inscription_obj = ord::take_object(inscription_obj_id); + let inscription = object::borrow(&inscription_obj); + let sequence_number = ord::sequence_number(inscription); + + + ord::transfer_object(inscription_obj, owner, new_satpoint, is_op_return); + + event::emit(InscriptionTransferredEvent{ + block_height: updater.block_height, + inscription_id: inscription_id, + new_location: new_satpoint, + old_location: old_satpoint, + sequence_number, + }); + (false, inscription_obj_id) + }else{ + let FlotsamNew{ + cursed, + fee:_, + hidden:_, + parents, + pointer:_, + reinscription, + unbound, + vindicated, + envelope, + } = *option::borrow(&flotsam.new); + let inscription_number = if(cursed) { + let number = updater.cursed_inscription_count; + updater.cursed_inscription_count = updater.cursed_inscription_count + 1; + //cursed number start from -1 + (number + 1) + }else{ + let number = updater.blessed_inscription_count; + updater.blessed_inscription_count = updater.blessed_inscription_count + 1; + number + }; + let sequence_number = updater.next_sequence_number; + updater.next_sequence_number = updater.next_sequence_number + 1; + + let charms = 0u16; + + if(cursed) { + charms = ord::set_charm(charms, ord::charm_cursed_flag()); + }; + + if(reinscription) { + charms = ord::set_charm(charms, ord::charm_reinscription_flag()); + }; + + //We do not handle the Sat + + if(is_op_return) { + charms = ord::set_charm(charms, ord::charm_burned_flag()); + }; + + if(*ord::satpoint_outpoint(&new_satpoint) == types::null_outpoint()) { + charms = ord::set_charm(charms, ord::charm_lost_flag()); + }; + + let location = if(unbound) { + charms = ord::set_charm(charms, ord::charm_unbound_flag()); + ord::new_satpoint(types::unbound_outpoint(), (updater.unbound_inscription_count as u64)) + }else{ + new_satpoint + }; + + if(vindicated) { + charms = ord::set_charm(charms, ord::charm_vindicated_flag()); + }; + let inscription_obj_id = ord::create_object( + inscription_id, + location, + sequence_number, + inscription_number, + cursed, + charms, + envelope, + owner, + ); + event::emit(InscriptionCreatedEvent{ + block_height: updater.block_height, + charms, + inscription_id, + location: option::some(new_satpoint), + parent_inscription_ids: parents, + sequence_number, + }); + (unbound, inscription_obj_id) + }; + + if(unbound) { + updater.unbound_inscription_count = updater.unbound_inscription_count + 1; + }else{ + let vout = ord::satpoint_vout(&new_satpoint); + let seal = utxo::new_utxo_seal(updater.seal_protocol, inscription_obj_id); + vector::push_back(seal_outs, utxo::new_seal_out(vout, seal)); + }; + } + + + public(friend) fun need_process_oridinals(block_height: u64) : bool { + block_height >= network::first_inscription_height() + } + + fun find_range_for_pointer(range_to_vout: &SimpleMap, pointer: u64): (bool, u64, u64) { + let keys = simple_map::keys(range_to_vout); + let i = 0; + let len = vector::length(&keys); + while (i < len) { + let vout = *vector::borrow(&keys, i); + let range = *simple_map::borrow(range_to_vout, &vout); + if (pointer >= range.start && pointer < range.end) { + return (true, vout, range.start) + }; + i = i + 1; + }; + (false, 0, 0) + } + + fun drop(updater: InscriptionUpdater) { + let InscriptionUpdater { + block_height:_, + seal_protocol:_, + flotsams, + lost_sats:_, + reward:_, + blessed_inscription_count:_, + cursed_inscription_count:_, + unbound_inscription_count:_, + next_sequence_number:_, + } = updater; + //The flotsams must be empty after the process_tx + assert!(vector::is_empty(&flotsams), ErrorFlotsamNotProcessed); + vector::destroy_empty(flotsams); + } + + //======================= Curse ======================= + + /// Curse Inscription + const CURSE_DUPLICATE_FIELD: vector = b"DuplicateField"; + + public fun curse_duplicate_field(): vector { + CURSE_DUPLICATE_FIELD + } + + const CURSE_INCOMPLETE_FIELD: vector = b"IncompleteField"; + + public fun curse_incompleted_field(): vector { + CURSE_INCOMPLETE_FIELD + } + + const CURSE_NOT_AT_OFFSET_ZERO: vector = b"NotAtOffsetZero"; + + public fun curse_not_at_offset_zero(): vector { + CURSE_NOT_AT_OFFSET_ZERO + } + + const CURSE_NOT_IN_FIRST_INPUT: vector = b"NotInFirstInput"; + + public fun curse_not_in_first_input(): vector { + CURSE_NOT_IN_FIRST_INPUT + } + + const CURSE_POINTER: vector = b"Pointer"; + + public fun curse_pointer(): vector { + CURSE_POINTER + } + + const CURSE_PUSHNUM: vector = b"Pushnum"; + + public fun curse_pushnum(): vector { + CURSE_PUSHNUM + } + + const CURSE_REINSCRIPTION: vector = b"Reinscription"; + + public fun curse_reinscription(): vector { + CURSE_REINSCRIPTION + } + + const CURSE_STUTTER: vector = b"Stutter"; + + public fun curse_stutter(): vector { + CURSE_STUTTER + } + + const CURSE_UNRECOGNIZED_EVEN_FIELD: vector = b"UnrecognizedEvenField"; + + public fun curse_unrecognized_even_field(): vector { + CURSE_UNRECOGNIZED_EVEN_FIELD + } + + fun handle_curse_inscription(e: &Envelope, offset: u64, inscribed_offset: &SimpleMap): option::Option> { + let record = ord::envelope_payload(e); + let curse = if (ord::inscription_record_unrecognized_even_field(record)) { + option::some(CURSE_UNRECOGNIZED_EVEN_FIELD) + } else if (ord::inscription_record_duplicate_field(record)) { + option::some(CURSE_DUPLICATE_FIELD) + } else if (ord::inscription_record_incomplete_field(record)) { + option::some(CURSE_INCOMPLETE_FIELD) + } else if (ord::envelope_input(e) != 0) { + option::some(CURSE_NOT_IN_FIRST_INPUT) + } else if (ord::envelope_offset(e) != 0) { + option::some(CURSE_NOT_AT_OFFSET_ZERO) + } else if (option::is_some(ord::inscription_record_pointer(record))) { + option::some(CURSE_POINTER) + } else if (ord::envelope_pushnum(e)) { + option::some(CURSE_PUSHNUM) + } else if (ord::envelope_stutter(e)) { + option::some(CURSE_STUTTER) + }else{ + if(simple_map::contains_key(inscribed_offset, &offset)){ + let counter = *simple_map::borrow(inscribed_offset, &offset); + if(counter.count > 1){ + option::some(CURSE_REINSCRIPTION) + }else{ + //the counter inscription id is from input, so it should exist + let inscription = ord::borrow_inscription(counter.inscription_id); + let is_cursed = ord::is_cursed(inscription); + let charms = ord::charms(inscription); + let initial_inscription_was_cursed_or_vindicated = is_cursed || ord::is_set_charm(charms, ord::charm_vindicated_flag()); + if(initial_inscription_was_cursed_or_vindicated){ + option::none() + }else{ + option::some(CURSE_REINSCRIPTION) + } + } + }else{ + option::none() + } + }; + curse + } +} \ No newline at end of file diff --git a/frameworks/bitcoin-move/sources/network.move b/frameworks/bitcoin-move/sources/network.move index 9dfbde7ef3..a3fad37665 100644 --- a/frameworks/bitcoin-move/sources/network.move +++ b/frameworks/bitcoin-move/sources/network.move @@ -5,10 +5,18 @@ module bitcoin_move::network{ use std::string::{Self, String}; use moveos_std::object; - const ErrorUnknownNetwork: u64 = 1; + /// How may blocks between halvings. + const SUBSIDY_HALVING_INTERVAL: u32 = 210_000; + + const FIRST_POST_SUBSIDY_EPOCH: u32 = 33; + + /// How many satoshis are in "one bitcoin". + const COIN_VALUE: u64 = 100_000_000; friend bitcoin_move::genesis; + const ErrorUnknownNetwork: u64 = 1; + /// Bitcoin network onchain configuration. struct BitcoinNetwork has key{ network: u8 @@ -104,4 +112,48 @@ module bitcoin_move::network{ } } + /// Ordinals jubilee height. + /// https://github.com/ordinals/ord/blob/75bf04b22107155f8f8ab6c77f6eefa8117d9ace/src/chain.rs#L49-L56 + public fun jubilee_height(): u64 { + let n = network(); + if (n == NETWORK_BITCOIN) { + 824544 + } else if (n == NETWORK_REGTEST) { + 110 + } else if (n == NETWORK_SIGNET) { + 175392 + } else if (n == NETWORK_TESTNET) { + 2544192 + } else { + abort ErrorUnknownNetwork + } + } + + /// Ordinals first inscription height. + /// https://github.com/ordinals/ord/blob/75bf04b22107155f8f8ab6c77f6eefa8117d9ace/src/chain.rs#L36-L43 + public fun first_inscription_height() : u64 { + let n = network(); + if (n == NETWORK_BITCOIN) { + 767430 + } else if (n == NETWORK_REGTEST) { + 0 + } else if (n == NETWORK_SIGNET) { + 112402 + } else if (n == NETWORK_TESTNET) { + 2413343 + } else { + abort ErrorUnknownNetwork + } + } + + /// Block Rewards + public fun subsidy_by_height(height: u64): u64 { + let epoch = (height as u32) / SUBSIDY_HALVING_INTERVAL; + if (epoch < FIRST_POST_SUBSIDY_EPOCH) { + (50 * COIN_VALUE) >> (epoch as u8) + } else { + 0 + } + } + } \ No newline at end of file diff --git a/frameworks/bitcoin-move/sources/ord.move b/frameworks/bitcoin-move/sources/ord.move index 0fa77d6968..340f96bfe2 100644 --- a/frameworks/bitcoin-move/sources/ord.move +++ b/frameworks/bitcoin-move/sources/ord.move @@ -7,132 +7,60 @@ module bitcoin_move::ord { use std::string::String; use std::vector; - use bitcoin_move::bitcoin_hash; - use bitcoin_move::script_buf; - use bitcoin_move::types::{Self, Transaction, Witness}; - use bitcoin_move::utxo::{Self, UTXO}; use moveos_std::bag; - use moveos_std::bcs; - use moveos_std::event; use moveos_std::json; use moveos_std::object::{Self, Object, ObjectID}; use moveos_std::simple_map::{Self, SimpleMap}; use moveos_std::string_utils; use moveos_std::type_info; + use bitcoin_move::bitcoin_hash; + use bitcoin_move::types::{Self, Transaction, Witness, OutPoint}; + friend bitcoin_move::genesis; friend bitcoin_move::bitcoin; + friend bitcoin_move::inscription_updater; - /// How may blocks between halvings. - const SUBSIDY_HALVING_INTERVAL: u32 = 210_000; - - const FIRST_POST_SUBSIDY_EPOCH: u32 = 33; const PERMANENT_AREA: vector = b"permanent_area"; const TEMPORARY_AREA: vector = b"temporary_area"; const METAPROTOCOL_VALIDITY: vector = b"metaprotocol_validity"; - const INSCRIPTION_CHARM: vector = b"inscription_charm"; - /// How many satoshis are in "one bitcoin". - const COIN_VALUE: u64 = 100_000_000; const ErrorMetaprotocolAlreadyRegistered: u64 = 1; const ErrorMetaprotocolProtocolMismatch: u64 = 2; - - /// Curse Inscription - const CURSE_DUPLICATE_FIELD: vector = b"DuplicateField"; - - public fun curse_duplicate_field(): vector { - CURSE_DUPLICATE_FIELD - } - - const CURSE_INCOMPLETE_FIELD: vector = b"IncompleteField"; - - public fun curse_incompleted_field(): vector { - CURSE_INCOMPLETE_FIELD - } - - const CURSE_NOT_AT_OFFSET_ZERO: vector = b"NotAtOffsetZero"; - - public fun curse_not_at_offset_zero(): vector { - CURSE_NOT_AT_OFFSET_ZERO - } - - const CURSE_NOT_IN_FIRST_INPUT: vector = b"NotInFirstInput"; - - public fun curse_not_in_first_input(): vector { - CURSE_NOT_IN_FIRST_INPUT - } - - const CURSE_POINTER: vector = b"Pointer"; - - public fun curse_pointer(): vector { - CURSE_POINTER - } - - const CURSE_PUSHNUM: vector = b"Pushnum"; - - public fun curse_pushnum(): vector { - CURSE_PUSHNUM - } - - const CURSE_REINSCRIPTION: vector = b"Reinscription"; - - public fun curse_reinscription(): vector { - CURSE_REINSCRIPTION - } - - const CURSE_STUTTER: vector = b"Stutter"; - - public fun curse_stutter(): vector { - CURSE_STUTTER - } - - const CURSE_UNRECOGNIZED_EVEN_FIELD: vector = b"UnrecognizedEvenField"; - - public fun curse_unrecognized_even_field(): vector { - CURSE_UNRECOGNIZED_EVEN_FIELD - } + const ErrorInscriptionNotExists: u64 = 3; struct InscriptionID has store, copy, drop { txid: address, index: u32, } - struct Flotsam has store, copy, drop { - // inscription_id: InscriptionID, - // offset: u64, - output_index: u32, - // start: u64, - offset: u64, - object_id: ObjectID, - } - struct SatPoint has store, copy, drop { - output_index: u32, + outpoint: OutPoint, offset: u64, - object_id: ObjectID, } struct Inscription has key { - txid: address, - index: u32, - /// inscription offset - offset: u64, + id: InscriptionID, + /// The location of the inscription + location: SatPoint, /// monotonically increasing sequence_number: u32, /// The curse inscription is a negative number, combined with the curse inscription flag to express the negative number inscription_number: u32, - /// curse flag - is_curse: bool, + /// Is the inscription cursed + is_cursed: bool, + /// inscription charms flag + charms: u16, body: vector, content_encoding: Option, content_type: Option, metadata: vector, metaprotocol: Option, - parents: vector, + parents: vector, pointer: Option, // Reserved for extending the Rune protocol rune: Option, @@ -177,6 +105,8 @@ module bitcoin_move::ord { struct InscriptionStore has key { cursed_inscription_count: u32, blessed_inscription_count: u32, + unbound_inscription_count: u32, + lost_sats: u64, next_sequence_number: u32, } @@ -190,29 +120,71 @@ module bitcoin_move::ord { event_type: u8, } - public(friend) fun genesis_init(_genesis_account: &signer) { + public(friend) fun genesis_init() { let store = InscriptionStore { cursed_inscription_count: 0, blessed_inscription_count: 0, + unbound_inscription_count: 0, + lost_sats: 0, next_sequence_number: 0, }; let store_obj = object::new_named_object(store); object::to_shared(store_obj); } - fun borrow_mut_inscription_store(): &mut InscriptionStore { + public(friend) fun borrow_mut_inscription_store(): &mut InscriptionStore { let inscription_store_object_id = object::named_object_id(); let inscription_store_obj = object::borrow_mut_object_shared(inscription_store_object_id); object::borrow_mut(inscription_store_obj) } - fun borrow_inscription_store(): &InscriptionStore { + public(friend) fun borrow_inscription_store(): &InscriptionStore { let inscription_store_object_id = object::named_object_id(); let inscription_store_obj = object::borrow_object(inscription_store_object_id); object::borrow(inscription_store_obj) } + public(friend) fun blessed_inscription_count(inscription_store: &InscriptionStore): u32 { + inscription_store.blessed_inscription_count + } + + public(friend) fun cursed_inscription_count(inscription_store: &InscriptionStore): u32 { + inscription_store.cursed_inscription_count + } + + public(friend) fun unbound_inscription_count(inscription_store: &InscriptionStore): u32 { + inscription_store.unbound_inscription_count + } + + public(friend) fun lost_sats(inscription_store: &InscriptionStore): u64 { + inscription_store.lost_sats + } + + public(friend) fun next_sequence_number(inscription_store: &InscriptionStore): u32 { + inscription_store.next_sequence_number + } + + public(friend) fun update_cursed_inscription_count(inscription_store: &mut InscriptionStore, count: u32) { + inscription_store.cursed_inscription_count = count; + } + + public(friend) fun update_blessed_inscription_count(inscription_store: &mut InscriptionStore, count: u32) { + inscription_store.blessed_inscription_count = count; + } + + public(friend) fun update_next_sequence_number(inscription_store: &mut InscriptionStore, count: u32) { + inscription_store.next_sequence_number = count; + } + + public(friend) fun update_unbound_inscription_count(inscription_store: &mut InscriptionStore, count: u32) { + inscription_store.unbound_inscription_count = count; + } + + public(friend) fun update_lost_sats(inscription_store: &mut InscriptionStore, count: u64) { + inscription_store.lost_sats = count; + } + //===== InscriptionID =====// public fun new_inscription_id(txid: address, index: u32): InscriptionID { @@ -276,62 +248,93 @@ module bitcoin_move::ord { store.next_sequence_number } - fun record_to_inscription( - txid: address, - index: u32, - offset: u64, - record: InscriptionRecord, - is_curse: bool, + public(friend) fun create_object( + id: InscriptionID, + location: SatPoint, + sequence_number: u32, inscription_number: u32, - sequence_number: u32 - ): Inscription { - let parents = vector::empty(); - vector::for_each(record.parents, |f| { - let parent_id = derive_inscription_id(f); - vector::push_back(&mut parents, parent_id); - }); - - Inscription { - txid, - index, - offset, - sequence_number, - inscription_number, - is_curse, - body: record.body, - content_encoding: record.content_encoding, - content_type: record.content_type, - metadata: record.metadata, - metaprotocol: record.metaprotocol, - parents, - pointer: record.pointer, - rune: option::none(), - } - } - - fun create_obj(inscription: Inscription): Object { - let id = InscriptionID { - txid: inscription.txid, - index: inscription.index, + is_cursed: bool, + charms: u16, + envelope: Envelope, + owner: address + ): ObjectID { + + let metaprotocol = envelope.payload.metaprotocol; + let inscription = Inscription { + id, + location, + sequence_number: sequence_number, + inscription_number: inscription_number, + is_cursed: is_cursed, + charms: charms, + body: envelope.payload.body, + content_encoding: envelope.payload.content_encoding, + content_type: envelope.payload.content_type, + metadata: envelope.payload.metadata, + metaprotocol, + parents: envelope.payload.parents, + pointer: envelope.payload.pointer, + rune: envelope.payload.rune, }; - let store_obj_id = object::named_object_id(); - let store_obj = object::borrow_mut_object_shared(store_obj_id); - // record a sequence_number to InscriptionID mapping - let sequence_number = inscription.sequence_number; - let metaprotocol = inscription.metaprotocol; - object::add_field(store_obj, sequence_number, id); - let object = object::new_with_parent_and_id(store_obj, id, inscription); - let obj_id = object::id(&object); + + let obj = create_object_internal(inscription); + let inscription_obj_id = object::id(&obj); + if (option::is_some(&metaprotocol)) { let metaprotocol = option::destroy_some(metaprotocol); moveos_std::event_queue::emit(metaprotocol, InscriptionEvent { metaprotocol: metaprotocol, sequence_number: sequence_number, - inscription_obj_id: obj_id, + inscription_obj_id, event_type: InscriptionEventTypeNew, }); }; - object + object::transfer_extend(obj, owner); + inscription_obj_id + } + + fun create_object_internal(inscription: Inscription): Object{ + let id = inscription.id; + let store_obj_id = object::named_object_id(); + let store_obj = object::borrow_mut_object_shared(store_obj_id); + // record a sequence_number to InscriptionID mapping + object::add_field(store_obj, inscription.sequence_number, id); + let obj = object::new_with_parent_and_id(store_obj, id, inscription); + obj + } + + public(friend) fun transfer_object(inscription_obj: Object, to: address, new_location: SatPoint, is_op_return: bool){ + //drop the temp area when inscription is transferred + drop_temp_area(&mut inscription_obj); + let inscription = object::borrow_mut(&mut inscription_obj); + inscription.location = new_location; + if (is_op_return){ + //if the output is OP_RETURN, set the burn flag and freeze the inscription + inscription.charms = set_charm(inscription.charms, charm_burned_flag()); + let metaprotocol = inscription.metaprotocol; + let sequence_number = inscription.sequence_number; + let inscription_obj_id = object::id(&inscription_obj); + if (option::is_some(&metaprotocol)) { + let metaprotocol = option::destroy_some(metaprotocol); + moveos_std::event_queue::emit(metaprotocol, InscriptionEvent { + metaprotocol: metaprotocol, + sequence_number, + inscription_obj_id, + event_type: InscriptionEventTypeBurn, + }); + }; + object::to_frozen(inscription_obj); + }else{ + object::transfer_extend(inscription_obj, to); + }; + } + + public(friend) fun take_object(inscription_obj_id: ObjectID): Object{ + object::take_object_extend(inscription_obj_id) + } + + public(friend) fun borrow_object(inscription_obj_id: ObjectID): &Object{ + object::borrow_object(inscription_obj_id) } fun parse_json_body(record: &InscriptionRecord): SimpleMap { @@ -352,325 +355,45 @@ module bitcoin_move::ord { object::exists_object_with_type(object_id) } - public fun borrow_inscription(txid: address, index: u32): &Object { - let id = InscriptionID { - txid, - index, - }; + public fun borrow_inscription(id: InscriptionID): &Inscription { let object_id = derive_inscription_id(id); - object::borrow_object(object_id) - } - - public fun borrow_inscription_by_id(id: InscriptionID): &Inscription { - let txid = inscription_id_txid(&id); - let index = inscription_id_index(&id); - let inscription_obj = borrow_inscription(txid, index); + assert!(object::exists_object_with_type(object_id), ErrorInscriptionNotExists); + let inscription_obj = object::borrow_object(object_id); object::borrow(inscription_obj) } - public(friend) fun spend_utxo( - utxo_obj: &mut Object, - tx: &Transaction, - input_utxo_values: vector, - input_index: u64 - ): (vector, vector) { - let utxo = object::borrow_mut(utxo_obj); - - let seals = utxo::remove_seals(utxo); - let new_sat_points = vector::empty(); - let flotsams = vector::empty(); - if (vector::is_empty(&seals)) { - return (new_sat_points, flotsams) - }; - let outputs = types::tx_output(tx); - - // Track the Inscription via SatPoint - let j = 0; - let seals_len = vector::length(&seals); - while (j < seals_len) { - let seal_object_id = *vector::borrow(&mut seals, j); - let inscription_obj = object::take_object_extend(seal_object_id); - let origin_owner = object::owner(&inscription_obj); - let inscription = object::borrow_mut(&mut inscription_obj); - let sequence_number = inscription.sequence_number; - let metaprotocol = inscription.metaprotocol; + // =============== Inscription Getter =============== // - let (is_match, new_sat_point) = match_utxo_and_generate_sat_point( - inscription.offset, - seal_object_id, - tx, - input_utxo_values, - input_index - ); - if (is_match) { - let match_output_index = new_sat_point.output_index; - - let match_output = vector::borrow(outputs, (match_output_index as u64)); - let to_address = types::txout_object_address(match_output); - inscription.offset = new_sat_point.offset; - - // drop the temporary area if inscription is transferred. - drop_temp_area(&mut inscription_obj); - vector::push_back(&mut new_sat_points, new_sat_point); - - // flag inscription burned and frozen inscription - let output_script_buf = types::txout_script_pubkey(match_output); - if (script_buf::is_op_return(output_script_buf)) { - let inscription_clarm = borrow_mut_inscription_charm_inner(&mut inscription_obj); - inscription_clarm.burned = true; - object::to_frozen(inscription_obj); - if (option::is_some(&metaprotocol)) { - let metaprotocol = option::destroy_some(metaprotocol); - moveos_std::event_queue::emit(metaprotocol, InscriptionEvent { - metaprotocol: metaprotocol, - sequence_number: sequence_number, - inscription_obj_id: seal_object_id, - event_type: InscriptionEventTypeBurn, - }); - }; - } else { - object::transfer_extend(inscription_obj, to_address); - }; - } else { - let flotsam = new_flotsam(new_sat_point.output_index, new_sat_point.offset, new_sat_point.object_id); - vector::push_back(&mut flotsams, flotsam); - - drop_temp_area(&mut inscription_obj); - object::transfer_extend(inscription_obj, origin_owner); - }; - j = j + 1; - }; - - (new_sat_points, flotsams) - } - - public(friend) fun handle_coinbase_tx( - tx: &Transaction, - flotsams: vector, - block_height: u64 - ): vector { - let new_sat_points = vector::empty(); - if (vector::is_empty(&flotsams)) { - return new_sat_points - }; - let outputs = types::tx_output(tx); - - // Track the Inscription via SatPoint - let j = 0; - let flotsams_len = vector::length(&flotsams); - while (j < flotsams_len) { - let flotsam = *vector::borrow(&mut flotsams, j); - let inscription_obj = object::take_object_extend(flotsam.object_id); - let inscription = object::borrow_mut(&mut inscription_obj); - - let new_sat_point = match_coinbase_and_generate_sat_point(j, tx, flotsams, block_height); - let match_output_index = new_sat_point.output_index; - - let match_output = vector::borrow(outputs, (match_output_index as u64)); - let to_address = types::txout_object_address(match_output); - - inscription.offset = new_sat_point.offset; - - object::transfer_extend(inscription_obj, to_address); - vector::push_back(&mut new_sat_points, new_sat_point); - - j = j + 1; - }; - - new_sat_points + public fun txid(self: &Inscription): address { + self.id.txid } - /// Match UTXO, via SatPoint offset, generate new SatPoint, UTXO spent follows "First in First out" - fun match_utxo_and_generate_sat_point( - offset: u64, - seal_object_id: ObjectID, - tx: &Transaction, - input_utxo_values: vector, - input_index: u64 - ): (bool, SatPoint) { - let txoutput = types::tx_output(tx); - - let idx = 0; - let input_utxo_value_accumulator = 0; - while (idx < input_index) { - let utxo_value = *vector::borrow(&input_utxo_values, idx); - input_utxo_value_accumulator = input_utxo_value_accumulator + utxo_value; - - idx = idx + 1; - }; - input_utxo_value_accumulator = input_utxo_value_accumulator + offset; - - let idx = 0; - let output_len = vector::length(txoutput); - let output_utxo_value_accumulator = 0; - let new_output_index = 0; - let new_offset = 0; - while (idx < output_len) { - let txout = vector::borrow(txoutput, idx); - let output_value = types::txout_value(txout); - output_utxo_value_accumulator = output_utxo_value_accumulator + output_value; - - if (output_utxo_value_accumulator > input_utxo_value_accumulator) { - new_output_index = idx; - new_offset = output_value - (output_utxo_value_accumulator - input_utxo_value_accumulator); - - break - }; - - idx = idx + 1; - }; - - // match utxo output - if (idx < output_len) { - let new_sat_point = new_sat_point((new_output_index as u32), new_offset, seal_object_id); - (true, new_sat_point) - }else { - // Paid to miners as transaction fees - let new_offset = input_utxo_value_accumulator - output_utxo_value_accumulator; - let new_sat_point = new_sat_point((input_index as u32), new_offset, seal_object_id); - (false, new_sat_point) - } + public fun index(self: &Inscription): u32 { + self.id.index } - /// Match Coinbase, via SatPoint offset, generate new SatPoint - fun match_coinbase_and_generate_sat_point( - flotsam_index: u64, - tx: &Transaction, - flotsams: vector, - block_height: u64 - ): SatPoint { - let txoutput = types::tx_output(tx); - - let idx = 0; - let reward_value_accumulator = 0; - let subsidy = subsidy_by_height(block_height); - reward_value_accumulator = reward_value_accumulator + subsidy; - while (idx <= flotsam_index) { - let flotsam = *vector::borrow(&flotsams, idx); - reward_value_accumulator = reward_value_accumulator + flotsam.offset; - - idx = idx + 1; - }; - - let idx = 0; - let output_len = vector::length(txoutput); - let output_utxo_value_accumulator = 0; - let new_output_index = 0; - let new_offset = 0; - while (idx < output_len) { - let txout = vector::borrow(txoutput, idx); - let output_value = types::txout_value(txout); - output_utxo_value_accumulator = output_utxo_value_accumulator + output_value; - - if (output_utxo_value_accumulator > reward_value_accumulator) { - new_output_index = idx; - new_offset = output_value - (output_utxo_value_accumulator - reward_value_accumulator); - - break - }; - - idx = idx + 1; - }; - - let flatsam = vector::borrow(&flotsams, flotsam_index); - let new_sat_point = new_sat_point((new_output_index as u32), new_offset, flatsam.object_id); - new_sat_point + public fun location(self: &Inscription): &SatPoint { + &self.location } - - public(friend) fun process_transaction(tx: &Transaction, input_utxo_values: vector): vector { - let sat_points = vector::empty(); - let inscription_store = borrow_mut_inscription_store(); - - let next_inscription_number = inscription_store.blessed_inscription_count; - let next_sequence_number = inscription_store.next_sequence_number; - - let inscriptions = from_transaction(tx, input_utxo_values, next_inscription_number, next_sequence_number); - - let inscriptions_len = vector::length(&inscriptions); - inscription_store.blessed_inscription_count = inscription_store.blessed_inscription_count + (inscriptions_len as u32); - inscription_store.next_sequence_number = inscription_store.next_sequence_number + (inscriptions_len as u32); - - if (inscriptions_len == 0) { - vector::destroy_empty(inscriptions); - return sat_points - }; - - let tx_outputs = types::tx_output(tx); - let output_len = vector::length(tx_outputs); - - // Ord has three mode for inscribe: SameSat,SeparateOutputs,SharedOutput: - // SameSat and SharedOutput have only one output - // When SeparateOutputs is used, the number of output and inscription is consistent. - // https://github.com/ordinals/ord/blob/26fcf05a738e68ef8c9c18fcc0997ccf931d6f41/src/wallet/batch/plan.rs#L270-L307 - let is_separate_outputs = output_len == inscriptions_len; - let idx = 0; - // reverse inscriptions and pop from the end - vector::reverse(&mut inscriptions); - while (idx < inscriptions_len) { - let output_index = if (is_separate_outputs) { - idx - }else { - 0 - }; - - let output = vector::borrow(tx_outputs, output_index); - let to_address = types::txout_object_address(output); - - let inscription = vector::pop_back(&mut inscriptions); - let offset = if (is_separate_outputs) { - 0 - }else { - inscription.offset - }; - - let inscription_obj = create_obj(inscription); - let object_id = object::id(&inscription_obj); - object::transfer_extend(inscription_obj, to_address); - - let new_sat_point = new_sat_point((output_index as u32), offset, object_id); - vector::push_back(&mut sat_points, new_sat_point); - - idx = idx + 1; - }; - vector::destroy_empty(inscriptions); - sat_points + public fun sequence_number(self: &Inscription): u32 { + self.sequence_number } - fun validate_inscription_records( - tx_id: address, - input_index: u64, - record: vector - ): vector { - let len = vector::length(&record); - let idx = 0; - let valid_records = vector::empty(); - while (idx < len) { - let record = *vector::borrow(&mut record, idx); - if (!record.duplicate_field && !record.incomplete_field && !record.unrecognized_even_field) { - vector::push_back(&mut valid_records, record); - }else { - event::emit(InvalidInscriptionEvent { - txid: tx_id, - input_index, - record, - }); - }; - idx = idx + 1; - }; - valid_records + public fun inscription_number(self: &Inscription): u32 { + self.inscription_number } - public fun txid(self: &Inscription): address { - self.txid + public fun is_cursed(self: &Inscription): bool { + self.is_cursed } - public fun index(self: &Inscription): u32 { - self.index + public fun charms(self: &Inscription): u16 { + self.charms } public fun offset(self: &Inscription): u64 { - self.offset + self.location.offset } public fun body(self: &Inscription): vector { @@ -693,7 +416,7 @@ module bitcoin_move::ord { self.metaprotocol } - public fun parents(self: &Inscription): vector { + public fun parents(self: &Inscription): vector { self.parents } @@ -701,14 +424,18 @@ module bitcoin_move::ord { self.pointer } + public fun id(self: &Inscription): &InscriptionID { + &self.id + } + fun drop(self: Inscription) { let Inscription { - txid: _, - index: _, - offset: _, + id: _, + location: _, sequence_number: _, inscription_number: _, - is_curse: _, + is_cursed: _, + charms: _, body: _, content_encoding: _, content_type: _, @@ -730,172 +457,113 @@ module bitcoin_move::ord { // ===== SatPoint ========== // - // === SatPoint === - public fun new_sat_point(output_index: u32, offset: u64, object_id: ObjectID): SatPoint { + public fun new_satpoint(outpoint: OutPoint, offset: u64): SatPoint { SatPoint { - output_index, + outpoint, offset, - object_id } } - public fun unpack_sat_point(sat_point: SatPoint): (u32, u64, ObjectID) { - let SatPoint { output_index, offset, object_id } = sat_point; - (output_index, offset, object_id) - } - - /// Get the SatPoint's object_id - public fun sat_point_object_id(sat_point: &SatPoint): ObjectID { - sat_point.object_id + public fun unpack_satpoint(satpoint: SatPoint): (OutPoint, u64) { + let SatPoint { outpoint, offset } = satpoint; + (outpoint, offset) } /// Get the SatPoint's offset - public fun sat_point_offset(sat_point: &SatPoint): u64 { - sat_point.offset + public fun satpoint_offset(satpoint: &SatPoint): u64 { + satpoint.offset } - /// Get the SatPoint's output_index - public fun sat_point_output_index(sat_point: &SatPoint): u32 { - sat_point.output_index + /// Get the SatPoint's outpoint + public fun satpoint_outpoint(satpoint: &SatPoint): &OutPoint { + &satpoint.outpoint } - - // === Flotsam === - public fun new_flotsam(output_index: u32, offset: u64, object_id: ObjectID): Flotsam { - Flotsam { - output_index, - offset, - object_id - } + public fun satpoint_vout(satpoint: &SatPoint): u32 { + types::outpoint_vout(&satpoint.outpoint) } - public fun unpack_flotsam(flotsam: Flotsam): (u32, u64, ObjectID) { - let Flotsam { output_index, offset, object_id } = flotsam; - (output_index, offset, object_id) - } - - // ==== InscriptionRecord ==== // + // ======= Envelope and InscriptionRecord - fun unpack_record(record: InscriptionRecord): - (vector, Option, Option, vector, Option, vector, Option) { - let InscriptionRecord { - body, - content_encoding, - content_type, - duplicate_field: _, - incomplete_field: _, - metadata, - metaprotocol, - parents, - pointer, - unrecognized_even_field: _, - rune: _, - } = record; - (body, content_encoding, content_type, metadata, metaprotocol, parents, pointer) - } + native fun parse_inscription_from_witness(witness: &Witness): vector>; - fun from_transaction(tx: &Transaction, input_utxo_values: vector, next_inscription_number: u32, next_sequence_number: u32): vector { - let tx_id = types::tx_id(tx); - let inscriptions = vector::empty(); + public(friend) fun parse_inscription_from_tx(tx: &Transaction): vector> { let inputs = types::tx_input(tx); let len = vector::length(inputs); let input_idx = 0; - let index_counter = 0; - let next_offset: u64 = 0; + let records = vector::empty(); while (input_idx < len) { let input = vector::borrow(inputs, input_idx); let witness = types::txin_witness(input); - let input_value = if (!vector::is_empty(&input_utxo_values)) { - *vector::borrow(&input_utxo_values, input_idx) - } else { - 0 - }; - - let inscription_records_from_witness = parse_inscription_from_witness(witness); - let inscription_records_len = vector::length(&inscription_records_from_witness); - let j = 0; - - while (j < inscription_records_len) { - let inscription = vector::borrow(&inscription_records_from_witness, j); - let record = inscription.payload; - let pointer = *option::borrow_with_default(&record.pointer, &0u64); - if (pointer >= input_value) { - pointer = 0; - }; - let offset = next_offset + pointer; - - let inscription_number = next_inscription_number; - let sequence_number = next_sequence_number; - let inscription = record_to_inscription( - tx_id, - (index_counter as u32), - offset, - record, - // Inscriptions which are cursed are numbered starting at negative one, counting - // down. Cursed inscriptions on and after the jubilee at block 824544 are - // vindicated, and are assigned positive inscription numbers. - // - // Rooch genesis is after 824544, no new cursed inscription - false, - inscription_number, - sequence_number - ); - vector::push_back(&mut inscriptions, inscription); - index_counter = index_counter + 1; - next_inscription_number = next_inscription_number + 1; - next_sequence_number = next_sequence_number + 1; - j = j + 1; - }; - next_offset = next_offset + input_value; + let inscription_records = parse_inscription_from_witness(witness); + vector::append(&mut records, inscription_records); input_idx = input_idx + 1; }; - inscriptions + records } - fun from_transaction_bytes(transaction_bytes: vector, input_utxo_values: vector, next_inscription_number: u32, next_sequence_number: u32): vector { - let transaction = bcs::from_bytes(transaction_bytes); - from_transaction(&transaction, input_utxo_values, next_inscription_number, next_sequence_number) + public(friend) fun envelope_input(envelope: &Envelope): u32 { + envelope.input } - native fun parse_inscription_from_witness(witness: &Witness): vector>; + public(friend) fun envelope_offset(envelope: &Envelope): u32 { + envelope.offset + } - /// Block Rewards - public fun subsidy_by_height(height: u64): u64 { - let epoch = (height as u32) / SUBSIDY_HALVING_INTERVAL; - if (epoch < FIRST_POST_SUBSIDY_EPOCH) { - (50 * COIN_VALUE) >> (epoch as u8) - } else { - 0 - } + public(friend) fun envelope_payload(envelope: &Envelope): &T { + &envelope.payload } - fun handle_curse_inscription(inscription: &Envelope): option::Option> { - let curse = if (inscription.payload.unrecognized_even_field) { - option::some(CURSE_UNRECOGNIZED_EVEN_FIELD) - } else if (inscription.payload.duplicate_field) { - option::some(CURSE_DUPLICATE_FIELD) - } else if (inscription.payload.incomplete_field) { - option::some(CURSE_INCOMPLETE_FIELD) - } else if (inscription.input != 0) { - option::some(CURSE_NOT_IN_FIRST_INPUT) - } else if (inscription.offset != 0) { - option::some(CURSE_NOT_AT_OFFSET_ZERO) - } else if (option::is_some(&inscription.payload.pointer)) { - option::some(CURSE_POINTER) - } else if (inscription.pushnum) { - option::some(CURSE_PUSHNUM) - } else if (inscription.stutter) { - option::some(CURSE_STUTTER) - // The contract has temporarily skipped the reinscription curse flag processing and - // needs to rely on scanning all SatPoint - // TODO handle reinscription curse and curse vindicated - // else if { - // option::some(CURSE_REINSCRIPTION) - // } - }else { - option::none() - }; - curse + public(friend) fun envelope_pushnum(envelope: &Envelope): bool { + envelope.pushnum + } + + public(friend) fun envelope_stutter(envelope: &Envelope): bool { + envelope.stutter + } + + public(friend) fun inscription_record_pointer(record: &InscriptionRecord): &Option { + &record.pointer + } + + public(friend) fun inscription_record_parents(record: &InscriptionRecord): &vector { + &record.parents + } + + public(friend) fun inscription_record_unrecognized_even_field(record: &InscriptionRecord): bool { + record.unrecognized_even_field + } + + public(friend) fun inscription_record_duplicate_field(record: &InscriptionRecord): bool { + record.duplicate_field + } + + public(friend) fun inscription_record_incomplete_field(record: &InscriptionRecord): bool { + record.incomplete_field + } + + public(friend) fun inscription_record_metaprotocol(record: &InscriptionRecord): &Option { + &record.metaprotocol + } + + public(friend) fun inscription_record_rune(record: &InscriptionRecord): &Option { + &record.rune + } + + public(friend) fun inscription_record_metadata(record: &InscriptionRecord): &vector { + &record.metadata + } + + public(friend) fun inscription_record_content_type(record: &InscriptionRecord): &Option { + &record.content_type + } + + public(friend) fun inscription_record_content_encoding(record: &InscriptionRecord): &Option { + &record.content_encoding + } + + public(friend) fun inscription_record_body(record: &InscriptionRecord): &vector { + &record.body } // ===== permenent area ========== // @@ -1160,9 +828,137 @@ module bitcoin_move::ord { InscriptionEventTypeBurn } + // ==== Inscription Charm ==== // + + //The charm flags are based on the ordinals library + //https://github.com/ordinals/ord/blob/75bf04b22107155f8f8ab6c77f6eefa8117d9ace/crates/ordinals/src/charm.rs + + // pub enum Charm { + // Coin = 0, + // Cursed = 1, + // Epic = 2, + // Legendary = 3, + // Lost = 4, + // Nineball = 5, + // Rare = 6, + // Reinscription = 7, + // Unbound = 8, + // Uncommon = 9, + // Vindicated = 10, + // Mythic = 11, + // Burned = 12, + // } + + const CHARM_COIN_FLAG: u16 = 1<<0; + public fun charm_coin_flag() : u16 {CHARM_COIN_FLAG} + + const CHARM_CURSED_FLAG: u16 = 1 << 1; + public fun charm_cursed_flag() : u16 {CHARM_CURSED_FLAG} + + const CHARM_EPIC_FLAG: u16 = 1 << 2; + public fun charm_epic_flag() : u16 {CHARM_EPIC_FLAG} + + const CHARM_LEGENDARY_FLAG: u16 = 1 << 3; + public fun charm_legendary_flag() : u16 {CHARM_LEGENDARY_FLAG} + + const CHARM_LOST_FLAG: u16 = 1 << 4; + public fun charm_lost_flag() : u16 {CHARM_LOST_FLAG} + + const CHARM_NINEBALL_FLAG: u16 = 1 << 5; + public fun charm_nineball_flag() : u16 {CHARM_NINEBALL_FLAG} + + const CHARM_RARE_FLAG: u16 = 1 << 6; + public fun charm_rare_flag() : u16 {CHARM_RARE_FLAG} + + const CHARM_REINSCRIPTION_FLAG: u16 = 1 << 7; + public fun charm_reinscription_flag() : u16 {CHARM_REINSCRIPTION_FLAG} + + const CHARM_UNBOUND_FLAG: u16 = 1 << 8; + public fun charm_unbound_flag() : u16 {CHARM_UNBOUND_FLAG} + + const CHARM_UNCOMMON_FLAG: u16 = 1 << 9; + public fun charm_uncommon_flag() : u16 {CHARM_UNCOMMON_FLAG} + + const CHARM_VINDICATED_FLAG: u16 = 1 << 10; + public fun charm_vindicated_flag() : u16 {CHARM_VINDICATED_FLAG} + + const CHARM_MYTHIC_FLAG: u16 = 1 << 11; + public fun charm_mythic_flag() : u16 {CHARM_MYTHIC_FLAG} + + const CHARM_BURNED_FLAG: u16 = 1 << 12; + public fun charm_burned_flag() : u16 {CHARM_BURNED_FLAG} + + public fun set_charm(charms: u16, flag: u16) : u16 { + charms | flag + } + + // public fun unset_charm(charms: u16, flag: u16) : u16 { + // charms & ^flag + // } + + public fun is_set_charm(charms: u16, flag: u16) : bool { + (charms & flag) != 0 + } + + /// A struct to represent the Inscription Charm + struct InscriptionCharm has copy, drop, store{ + coin: bool, + cursed: bool, + epic: bool, + legendary: bool, + lost: bool, + nineball: bool, + rare: bool, + reinscription: bool, + unbound: bool, + uncommon: bool, + vindicated: bool, + mythic: bool, + burned: bool, + } + + fun view_charms(charms: u16) : InscriptionCharm { + InscriptionCharm { + coin: is_set_charm(charms, CHARM_COIN_FLAG), + cursed: is_set_charm(charms, CHARM_CURSED_FLAG), + epic: is_set_charm(charms, CHARM_EPIC_FLAG), + legendary: is_set_charm(charms, CHARM_LEGENDARY_FLAG), + lost: is_set_charm(charms, CHARM_LOST_FLAG), + nineball: is_set_charm(charms, CHARM_NINEBALL_FLAG), + rare: is_set_charm(charms, CHARM_RARE_FLAG), + reinscription: is_set_charm(charms, CHARM_REINSCRIPTION_FLAG), + unbound: is_set_charm(charms, CHARM_UNBOUND_FLAG), + uncommon: is_set_charm(charms, CHARM_UNCOMMON_FLAG), + vindicated: is_set_charm(charms, CHARM_VINDICATED_FLAG), + mythic: is_set_charm(charms, CHARM_MYTHIC_FLAG), + burned: is_set_charm(charms, CHARM_BURNED_FLAG), + } + } + + + /// Views the Inscription charms for a given inscription ID string. + /// Returns None if the inscription doesn't exist + /// + /// @param inscription_id_str - String representation of the inscription ID + /// @return Option - Some(charm) if exists, None otherwise + public fun view_inscription_charm(inscription_id_str: String): Option { + let inscription_id_option = parse_inscription_id(&inscription_id_str); + if (option::is_none(&inscription_id_option)) { + return option::none() + }; + + let inscription_id = option::destroy_some(inscription_id_option); + if (!exists_inscription(inscription_id)) { + return option::none() + }; + let inscription = borrow_inscription(inscription_id); + let charms = view_charms(inscription.charms); + option::some(charms) + } + #[test_only] public fun init_for_test(_genesis_account: &signer) { - genesis_init(_genesis_account); + genesis_init(); } #[test_only] @@ -1172,24 +968,62 @@ module bitcoin_move::ord { #[test_only] public fun new_inscription_object_for_test( - txid: address, - index: u32, + id: InscriptionID, + vout: u32, offset: u64, body: vector, content_encoding: Option, content_type: Option, metadata: vector, metaprotocol: Option, - parents: vector, + parents: vector, pointer: Option, ): Object { - let inscription = Inscription { - txid, - index, + let inscription = new_inscription_for_test( + id, + vout, + offset, + body, + content_encoding, + content_type, + metadata, + metaprotocol, + parents, + pointer, + ); + create_object_internal(inscription) + } + + #[test_only] + public fun drop_inscription_object_for_test(inscription: Object) { + let inscription = object::remove(inscription); + drop(inscription); + } + + #[test_only] + public fun new_inscription_for_test( + id: InscriptionID, + vout: u32, + offset: u64, + body: vector, + content_encoding: Option, + content_type: Option, + metadata: vector, + metaprotocol: Option, + parents: vector, + pointer: Option, + ): Inscription { + let location = SatPoint { + outpoint: types::new_outpoint(id.txid, vout), offset, + }; + Inscription { + id, + location, sequence_number: 0, inscription_number: 0, - is_curse: false, + is_cursed: false, + charms: 0, body, content_encoding, content_type, @@ -1198,43 +1032,59 @@ module bitcoin_move::ord { parents, pointer, rune: option::none(), - }; - - object::new(inscription) + } } #[test_only] - public fun drop_inscription_object_for_test(inscription: Object) { - let inscription = object::remove(inscription); - let Inscription { - txid: _, - index: _, - offset: _, - sequence_number: _, - inscription_number: _, - is_curse: _, - body: _, - content_encoding: _, - content_type: _, - metadata: _, - metaprotocol: _, - parents: _, - pointer: _, - rune: _, - } = inscription; + public fun register_metaprotocol_for_test(metaprotocol: String) { + let system = moveos_std::signer::module_signer(); + register_metaprotocol_via_system(&system, metaprotocol); } #[test_only] + public fun setup_inscription_for_test(_genesis_account: &signer, metaprotocol: String): (address, InscriptionID) { + genesis_init(); + + // prepare test inscription + let test_address = @0x5416690eaaf671031dc609ff8d36766d2eb91ca44f04c85c27628db330f40fd1; + let test_txid = @0x21da2ae8cc773b020b4873f597369416cf961a1896c24106b0198459fec2df77; + let test_inscription_id = new_inscription_id(test_txid, 0); + + let content_type = b"application/wasm"; + let body = x"0061736d0100000001080260017f00600000020f0107636f6e736f6c65036c6f670000030201010503010001071702066d656d6f727902000a68656c6c6f576f726c6400010a08010600410010000b0b14010041000b0e48656c6c6f2c20576f726c642100"; + if (!is_metaprotocol_register(metaprotocol)) { + register_metaprotocol_for_test(metaprotocol); + }; + + let ins_obj = new_inscription_object_for_test( + test_inscription_id, + 0, + 0, + body, + option::none(), + option::some(string::utf8(content_type)), + vector[], + option::some(metaprotocol), + vector[], + option::none(), + ); + object::transfer_extend(ins_obj, test_address); + + (test_address, test_inscription_id) + } + + #[test_only] struct PermanentState has store { value: u64, } #[test] fun test_permanent_state() { - // genesis_init(); + genesis_init(); let txid = @0x77dfc2fe598419b00641c296181a96cf16943697f573480b023b77cce82ada21; + let id = new_inscription_id(txid, 0); let inscription_obj = new_inscription_object_for_test( - txid, + id, 0, 0, vector[], @@ -1268,10 +1118,11 @@ module bitcoin_move::ord { #[test] fun test_temp_state() { - // genesis_init(); + genesis_init(); let txid = @0x77dfc2fe598419b00641c296181a96cf16943697f573480b023b77cce82ada21; + let id = new_inscription_id(txid, 0); let inscription_obj = new_inscription_object_for_test( - txid, + id, 0, 0, vector[], @@ -1306,9 +1157,11 @@ module bitcoin_move::ord { // If the inscription is transferred, the permanent area will be kept and the temporary area will be dropped. #[test] fun test_transfer() { + genesis_init(); let txid = @0x77dfc2fe598419b00641c296181a96cf16943697f573480b023b77cce82ada21; + let id = new_inscription_id(txid, 0); let inscription_obj = new_inscription_object_for_test( - txid, + id, 0, 0, vector[], @@ -1338,80 +1191,8 @@ module bitcoin_move::ord { #[test_only] struct TestProtocol has key {} - - #[test_only] - public fun new_inscription_for_test( - txid: address, - index: u32, - offset: u64, - body: vector, - content_encoding: Option, - content_type: Option, - metadata: vector, - metaprotocol: Option, - parents: vector, - pointer: Option, - ): Inscription { - Inscription { - txid, - index, - offset, - sequence_number: 0, - inscription_number: 0, - is_curse: false, - body, - content_encoding, - content_type, - metadata, - metaprotocol, - parents, - pointer, - rune: option::none(), - } - } - - #[test_only] - public fun register_metaprotocol_for_test(metaprotocol: String) { - let system = moveos_std::signer::module_signer(); - register_metaprotocol_via_system(&system, metaprotocol); - } - - #[test_only] - public fun setup_inscription_for_test(genesis_account: &signer, metaprotocol: String): (address, InscriptionID) { - genesis_init(genesis_account); - - // prepare test inscription - let test_address = @0x5416690eaaf671031dc609ff8d36766d2eb91ca44f04c85c27628db330f40fd1; - let test_txid = @0x21da2ae8cc773b020b4873f597369416cf961a1896c24106b0198459fec2df77; - let test_inscription_id = new_inscription_id(test_txid, 0); - - let content_type = b"application/wasm"; - let body = x"0061736d0100000001080260017f00600000020f0107636f6e736f6c65036c6f670000030201010503010001071702066d656d6f727902000a68656c6c6f576f726c6400010a08010600410010000b0b14010041000b0e48656c6c6f2c20576f726c642100"; - if (!is_metaprotocol_register(metaprotocol)) { - register_metaprotocol_for_test(metaprotocol); - }; - - let test_inscription = new_inscription_for_test( - test_txid, - 0, - 0, - body, - option::none(), - option::some(string::utf8(content_type)), - vector[], - option::some(metaprotocol), - vector[], - option::none(), - ); - - let test_inscription_obj = create_obj(test_inscription); - object::transfer_extend(test_inscription_obj, test_address); - - (test_address, test_inscription_id) - } - #[test(genesis_account= @0x4)] - fun test_metaprotocol_validity(genesis_account: &signer) { + fun test_metaprotocol_validity(genesis_account: &signer): InscriptionID { // prepare test inscription let (_test_address, test_inscription_id) = setup_inscription_for_test( genesis_account, @@ -1447,6 +1228,7 @@ module bitcoin_move::ord { let invalid_reason_option = metaprotocol_validity_invalid_reason(metaprotocol_validity); let invalid_reason = option::borrow(&invalid_reason_option); assert!(invalid_reason == &test_invalid_reason, 4); + test_inscription_id } #[test] @@ -1492,141 +1274,17 @@ module bitcoin_move::ord { assert!(option::is_none(&inscription_id_option), 1); } - // ==== Inscription Charm ==== // - - /// Represents the charm of an inscription, containing various properties. - struct InscriptionCharm has store, copy, drop { - /// Indicates whether the inscription has been burned. - burned: bool - } - - /// Get the InscriptionCharm's burned - public fun inscription_charm_burned(charm: &InscriptionCharm): bool { - charm.burned - } - - /// Borrows a mutable reference to the InscriptionCharm of a given Inscription object. - /// If the charm doesn't exist, it creates a new one with default values. - /// - /// @param inscription_mut_obj - Mutable reference to the Inscription object - /// @return Mutable reference to the InscriptionCharm - fun borrow_mut_inscription_charm_inner(inscription_mut_obj: &mut Object): &mut InscriptionCharm { - if (!object::contains_field(inscription_mut_obj, INSCRIPTION_CHARM)) { - let clarm = InscriptionCharm { - burned: false, - }; - - object::upsert_field(inscription_mut_obj, INSCRIPTION_CHARM, clarm); - }; - - object::borrow_mut_field(inscription_mut_obj, INSCRIPTION_CHARM) - } - - /// Upserts (updates or inserts) the InscriptionCharm for a given InscriptionID. - /// - /// @param inscription_id - The ID of the inscription - /// @param charm - The InscriptionCharm to upsert - fun upsert_inscription_charm(inscription_id: InscriptionID, charm: InscriptionCharm) { - let inscription_object_id = derive_inscription_id(inscription_id); - let inscription_mut_obj = object::borrow_mut_object_extend(inscription_object_id); - - object::upsert_field(inscription_mut_obj, INSCRIPTION_CHARM, charm); - } - - /// Borrows a mutable reference to the InscriptionCharm for a given InscriptionID. - /// - /// @param inscription_id - The ID of the inscription - /// @return Mutable reference to the InscriptionCharm - fun borrow_mut_inscription_charm(inscription_id: InscriptionID): &mut InscriptionCharm { - let inscription_object_id = derive_inscription_id(inscription_id); - let inscription_mut_obj = object::borrow_mut_object_extend(inscription_object_id); - - borrow_mut_inscription_charm_inner(inscription_mut_obj) - } - - /// Checks if an InscriptionCharm exists for a given InscriptionID. - /// - /// @param inscription_id - The ID of the inscription - /// @return Boolean indicating whether the charm exists - public fun exists_inscription_charm(inscription_id: InscriptionID): bool { - let inscription_object_id = derive_inscription_id(inscription_id); - let exists = object::exists_object_with_type(inscription_object_id); - if (!exists) { - return false - }; - - let inscription_obj = object::borrow_object(inscription_object_id); - object::contains_field(inscription_obj, INSCRIPTION_CHARM) - } - - /// Borrows a reference to the InscriptionCharm for a given InscriptionID. - /// - /// @param inscription_id - The ID of the inscription - /// @return Reference to the InscriptionCharm - public fun borrow_inscription_charm(inscription_id: InscriptionID): &InscriptionCharm { - let inscription_object_id = derive_inscription_id(inscription_id); - let inscription_obj = object::borrow_object(inscription_object_id); - - object::borrow_field(inscription_obj, INSCRIPTION_CHARM) - } - - /// Views the InscriptionCharm for a given inscription ID string. - /// Returns None if the inscription doesn't exist or doesn't have a charm. - /// - /// @param inscription_id_str - String representation of the inscription ID - /// @return Option - Some(charm) if exists, None otherwise - public fun view_inscription_charm(inscription_id_str: String): Option { - let inscription_id_option = parse_inscription_id(&inscription_id_str); - if (option::is_none(&inscription_id_option)) { - return option::none() - }; - - let inscription_id = option::destroy_some(inscription_id_option); - if (!exists_inscription_charm(inscription_id)) { - return option::none() - }; - - let clarm = borrow_inscription_charm(inscription_id); - option::some(*clarm) - } - #[test(genesis_account= @0x1)] fun test_inscription_charm(genesis_account: &signer) { // Setup - setup_inscription_for_test(genesis_account, string::utf8(b"TestProtocol")); - - // Test inscription ID - let test_txid = @0x21da2ae8cc773b020b4873f597369416cf961a1896c24106b0198459fec2df77; - let test_inscription_id = new_inscription_id(test_txid, 0); - - // Test exists_inscription_charm - assert!(!exists_inscription_charm(test_inscription_id), 1); - - // Test upsert_inscription_charm - let charm = InscriptionCharm { burned: false }; - upsert_inscription_charm(test_inscription_id, charm); - - // Test exists_inscription_charm again - assert!(exists_inscription_charm(test_inscription_id), 2); - - // Test borrow_inscription_charm - let borrowed_charm = borrow_inscription_charm(test_inscription_id); - assert!(!borrowed_charm.burned, 3); - - // Test borrow_mut_inscription_charm - let mut_charm = borrow_mut_inscription_charm(test_inscription_id); - mut_charm.burned = true; - - // Verify the change - let borrowed_charm = borrow_inscription_charm(test_inscription_id); - assert!(borrowed_charm.burned, 4); + let (_addr, test_inscription_id) = setup_inscription_for_test(genesis_account, string::utf8(b"TestProtocol")); // Test view_inscription_charm let inscription_id_str = inscription_id_to_string(&test_inscription_id); let viewed_charm_option = view_inscription_charm(inscription_id_str); assert!(option::is_some(&viewed_charm_option), 5); let viewed_charm = option::destroy_some(viewed_charm_option); - assert!(viewed_charm.burned, 6); + assert!(!viewed_charm.burned, 6); // Test view_inscription_charm with non-existent inscription let non_existent_id_str = string::utf8(b"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdefi0"); @@ -1643,14 +1301,5 @@ module bitcoin_move::ord { let invalid_id_str = string::utf8(b"invalid_id"); let invalid_charm_option = view_inscription_charm(invalid_id_str); assert!(option::is_none(&invalid_charm_option), 1); - - // Test with valid inscription ID but no charm - let test_txid = @0x21da2ae8cc773b020b4873f597369416cf961a1896c24106b0198459fec2df77; - let test_inscription_id = new_inscription_id(test_txid, 1); // Using index 1 which doesn't exist - assert!(!exists_inscription_charm(test_inscription_id), 2); - - let inscription_id_str = inscription_id_to_string(&test_inscription_id); - let non_existent_charm_option = view_inscription_charm(inscription_id_str); - assert!(option::is_none(&non_existent_charm_option), 3); } } diff --git a/frameworks/bitcoin-move/sources/pending_block.move b/frameworks/bitcoin-move/sources/pending_block.move index 993890fc0f..f39f8fdbaf 100644 --- a/frameworks/bitcoin-move/sources/pending_block.move +++ b/frameworks/bitcoin-move/sources/pending_block.move @@ -6,14 +6,17 @@ module bitcoin_move::pending_block{ use std::vector; use std::option::{Self, Option}; + use moveos_std::object::{Self, Object, ObjectID}; use moveos_std::simple_map::{Self, SimpleMap}; use moveos_std::event; + use moveos_std::type_info; + use bitcoin_move::types::{Self, Transaction, Header, Block, BlockHeightHash}; - use bitcoin_move::ord::{Flotsam}; friend bitcoin_move::genesis; friend bitcoin_move::bitcoin; + friend bitcoin_move::inscription_updater; const ErrorBlockAlreadyProcessed:u64 = 1; const ErrorPendingBlockNotFound:u64 = 2; @@ -24,7 +27,6 @@ module bitcoin_move::pending_block{ const ErrorUnsupportedChain:u64 = 7; const TX_IDS_KEY: vector = b"tx_ids"; - const BLOCK_FLOTSAM_KEY: vector = b"block_flotsam"; struct PendingBlock has key{ block_height: u64, @@ -197,14 +199,33 @@ module bitcoin_move::pending_block{ }); }; - if(object::contains_field(&obj, BLOCK_FLOTSAM_KEY)){ - let _flotsam: vector = object::remove_field(&mut obj, BLOCK_FLOTSAM_KEY); - }; let pending_block = object::remove(obj); let PendingBlock{block_height:_, block_hash:_, header, processed_tx:_, next_block_hash:_} = pending_block; header } + public(friend) fun block_height(pending_block: &Object): u64{ + let block = object::borrow(pending_block); + block.block_height + } + + /// The intermediate is used to store the intermediate state during the tx processing + public(friend) fun take_intermediate(pending_block: &mut Object): I{ + let intermediate_name = type_info::type_name(); + let intermediate = object::remove_field(pending_block, intermediate_name); + intermediate + } + + public(friend) fun add_intermediate(pending_block: &mut Object, intermediate: I){ + let intermediate_name = type_info::type_name(); + object::add_field(pending_block, intermediate_name, intermediate); + } + + public(friend) fun exists_intermediate(pending_block: &Object): bool{ + let intermediate_name = type_info::type_name(); + object::contains_field(pending_block, intermediate_name) + } + // ============== Pending Tx Processing ============== public(friend) fun process_pending_tx(block_hash: address, txid: address): InprocessBlock{ @@ -243,13 +264,8 @@ module bitcoin_move::pending_block{ header } - public(friend) fun inprocess_block_flotsams_mut(inprocess_block: &mut InprocessBlock): &mut vector{ - object::borrow_mut_field_with_default(&mut inprocess_block.block_obj, BLOCK_FLOTSAM_KEY, vector::empty()) - } - - public(friend) fun inprocess_block_flotsams(inprocess_block: &InprocessBlock): vector{ - let default = vector::empty(); - *object::borrow_field_with_default(&inprocess_block.block_obj, BLOCK_FLOTSAM_KEY, &default) + public(friend) fun inprocess_block_pending_block(inprocess_block: &mut InprocessBlock): &mut Object{ + &mut inprocess_block.block_obj } public(friend) fun inprocess_block_tx(inprocess_block: &InprocessBlock): &Transaction{ diff --git a/frameworks/bitcoin-move/sources/types.move b/frameworks/bitcoin-move/sources/types.move index e798804c13..cd27ac4b1c 100644 --- a/frameworks/bitcoin-move/sources/types.move +++ b/frameworks/bitcoin-move/sources/types.move @@ -244,6 +244,14 @@ module bitcoin_move::types { *self == null_outpoint() } + /// The Inscription unbound outpoint. + public fun unbound_outpoint(): OutPoint { + OutPoint { + txid: address::zero(), + vout: 0, + } + } + #[data_struct] struct TxOut has store, copy, drop { /// The value of the output, in satoshis. diff --git a/frameworks/bitcoin-move/sources/utxo.move b/frameworks/bitcoin-move/sources/utxo.move index a1448c7b1c..4685616a44 100644 --- a/frameworks/bitcoin-move/sources/utxo.move +++ b/frameworks/bitcoin-move/sources/utxo.move @@ -14,6 +14,7 @@ module bitcoin_move::utxo{ friend bitcoin_move::genesis; friend bitcoin_move::ord; friend bitcoin_move::bitcoin; + friend bitcoin_move::inscription_updater; const TEMPORARY_AREA: vector = b"temporary_area"; @@ -34,6 +35,11 @@ module bitcoin_move::utxo{ struct UTXOSeal has store, copy, drop { protocol: String, object_id: ObjectID, + } + + struct SealOut has store, copy, drop { + vout: u32, + seal: UTXOSeal, } struct BitcoinUTXOStore has key{ @@ -41,6 +47,7 @@ module bitcoin_move::utxo{ next_tx_index: u64, } + ///TODO break remove the CreatingUTXOEvent and RemovingUTXOEvent /// Event for creating UTXO struct CreatingUTXOEvent has drop, store, copy { /// UTXO object id @@ -100,6 +107,16 @@ module bitcoin_move::utxo{ utxo_obj } + public(friend) fun mock_utxo(outpoint: OutPoint, value: u64): UTXO { + let (txid, vout) = types::unpack_outpoint(outpoint); + UTXO{ + txid, + vout, + value, + seals: simple_multimap::new(), + } + } + public fun derive_utxo_id(outpoint: OutPoint) : ObjectID { let parent_id = object::named_object_id(); object::custom_object_id_with_parent(parent_id, outpoint) @@ -153,6 +170,7 @@ module bitcoin_move::utxo{ } #[private_generics(T)] + //TODO break: remove this function public fun remove_seals(utxo: &mut UTXO): vector { let protocol = type_info::type_name(); if(simple_multimap::contains_key(&utxo.seals, &protocol)){ @@ -163,6 +181,16 @@ module bitcoin_move::utxo{ } } + public(friend) fun remove_seals_internal(utxo: &mut UTXO): vector{ + let protocol = type_info::type_name(); + if(simple_multimap::contains_key(&utxo.seals, &protocol)){ + let(_k, value) = simple_multimap::remove(&mut utxo.seals, &protocol); + value + }else{ + vector::empty() + } + } + public(friend) fun add_seal(utxo: &mut UTXO, utxo_seal: UTXOSeal){ let UTXOSeal{protocol, object_id} = utxo_seal; simple_multimap::add(&mut utxo.seals, protocol, object_id); @@ -178,7 +206,7 @@ module bitcoin_move::utxo{ object::take_object_extend(object_id) } - public(friend) fun remove(utxo_obj: Object): SimpleMultiMap{ + public(friend) fun remove(utxo_obj: Object): UTXO{ if(object::contains_field(&utxo_obj, TEMPORARY_AREA)){ let bag = object::remove_field(&mut utxo_obj, TEMPORARY_AREA); bag::drop(bag); @@ -187,21 +215,39 @@ module bitcoin_move::utxo{ event::emit( RemovingUTXOEvent { id: object::id(&utxo_obj) } ); let utxo = object::remove(utxo_obj); - let UTXO{txid:_, vout:_, value:_, seals} = utxo; - seals + // let UTXO{txid:_, vout:_, value:_, seals} = utxo; + // seals + utxo + } + + public(friend) fun drop(utxo: UTXO) { + let UTXO{txid:_, vout:_, value:_, seals:_} = utxo; + //simple_multimap::destroy_empty(seals); } // === UTXOSeal === - public fun new_utxo_seal(protocol: String, seal_object_id: ObjectID) : UTXOSeal { + public(friend) fun new_utxo_seal(protocol: String, seal_object_id: ObjectID) : UTXOSeal { UTXOSeal{ protocol, object_id: seal_object_id, } } - public fun unpack_utxo_seal(utxo_seal: UTXOSeal) : (String, ObjectID) { + public(friend) fun unpack_utxo_seal(utxo_seal: UTXOSeal) : (String, ObjectID) { let UTXOSeal{protocol, object_id} = utxo_seal; (protocol, object_id) + } + + public(friend) fun new_seal_out(vout: u32, seal: UTXOSeal) : SealOut { + SealOut{ + vout, + seal, + } + } + + public(friend) fun unpack_seal_out(seal_out: SealOut) : (u32, UTXOSeal) { + let SealOut{vout, seal} = seal_out; + (vout, seal) } // ==== Temporary Area === @@ -250,15 +296,22 @@ module bitcoin_move::utxo{ bag::remove(bag, name) } + // Should we require the input utxo exists + // Sometimes, we may not sync the Bitcoin block from genesis + public(friend) fun check_utxo_input(): bool{ + //TODO make this to be configurable + rooch_framework::chain_id::is_main() + } + #[test_only] public fun new_for_testing(txid: address, vout: u32, value: u64) : Object { new(txid, vout, value) } #[test_only] - public fun drop_for_testing(utxo: Object){ - let seals = remove(utxo); - simple_multimap::drop(seals); + public fun drop_for_testing(utxo_obj: Object){ + let utxo = remove(utxo_obj); + drop(utxo); } #[test] @@ -275,9 +328,9 @@ module bitcoin_move::utxo{ genesis_init(); let txid = @0x77dfc2fe598419b00641c296181a96cf16943697f573480b023b77cce82ada21; let vout = 0; - let utxo = new(txid, vout, 100); - let seals = remove(utxo); - simple_multimap::drop(seals); + let utxo_obj = new(txid, vout, 100); + let utxo = remove(utxo_obj); + drop(utxo); } #[test_only] @@ -290,18 +343,18 @@ module bitcoin_move::utxo{ genesis_init(); let txid = @0x77dfc2fe598419b00641c296181a96cf16943697f573480b023b77cce82ada21; let vout = 0; - let utxo = new(txid, vout, 100); - add_temp_state(&mut utxo, TempState{value: 10}); - assert!(contains_temp_state(&utxo), 1000); - assert!(borrow_temp_state(&utxo).value == 10, 1001); + let utxo_obj = new(txid, vout, 100); + add_temp_state(&mut utxo_obj, TempState{value: 10}); + assert!(contains_temp_state(&utxo_obj), 1000); + assert!(borrow_temp_state(&utxo_obj).value == 10, 1001); { - let state = borrow_mut_temp_state(&mut utxo); + let state = borrow_mut_temp_state(&mut utxo_obj); state.value = 20; }; - let state = remove_temp_state(&mut utxo); + let state = remove_temp_state(&mut utxo_obj); assert!(state.value == 20, 1); - assert!(!contains_temp_state(&utxo), 1002); - let seals = remove(utxo); - simple_multimap::drop(seals); + assert!(!contains_temp_state(&utxo_obj), 1002); + let utxo = remove(utxo_obj); + drop(utxo); } } \ No newline at end of file diff --git a/frameworks/bitcoin-move/tests/ord_test.move b/frameworks/bitcoin-move/tests/ord_test.move new file mode 100644 index 0000000000..dcecd4960a --- /dev/null +++ b/frameworks/bitcoin-move/tests/ord_test.move @@ -0,0 +1,62 @@ +#[test_only] +module bitcoin_move::ord_test { + + use bitcoin_move::ord; + + #[test] + public fun test_charm(){ + let charms = 0u16; + assert!(!ord::is_set_charm(charms, ord::charm_coin_flag()), 1); + charms = ord::set_charm(charms, ord::charm_coin_flag()); + + assert!(!ord::is_set_charm(charms, ord::charm_cursed_flag()), 2); + charms = ord::set_charm(charms, ord::charm_cursed_flag()); + + assert!(!ord::is_set_charm(charms, ord::charm_epic_flag()), 3); + charms = ord::set_charm(charms, ord::charm_epic_flag()); + + assert!(!ord::is_set_charm(charms, ord::charm_legendary_flag()), 4); + charms = ord::set_charm(charms, ord::charm_legendary_flag()); + + assert!(!ord::is_set_charm(charms, ord::charm_lost_flag()), 5); + charms = ord::set_charm(charms, ord::charm_lost_flag()); + + assert!(!ord::is_set_charm(charms, ord::charm_nineball_flag()), 6); + charms = ord::set_charm(charms, ord::charm_nineball_flag()); + + assert!(!ord::is_set_charm(charms, ord::charm_rare_flag()), 7); + charms = ord::set_charm(charms, ord::charm_rare_flag()); + + assert!(!ord::is_set_charm(charms, ord::charm_reinscription_flag()), 8); + charms = ord::set_charm(charms, ord::charm_reinscription_flag()); + + assert!(!ord::is_set_charm(charms, ord::charm_unbound_flag()), 9); + charms = ord::set_charm(charms, ord::charm_unbound_flag()); + + assert!(!ord::is_set_charm(charms, ord::charm_uncommon_flag()), 10); + charms = ord::set_charm(charms, ord::charm_uncommon_flag()); + + assert!(!ord::is_set_charm(charms, ord::charm_vindicated_flag()), 11); + charms = ord::set_charm(charms, ord::charm_vindicated_flag()); + + assert!(!ord::is_set_charm(charms, ord::charm_mythic_flag()), 12); + charms = ord::set_charm(charms, ord::charm_mythic_flag()); + + assert!(!ord::is_set_charm(charms, ord::charm_burned_flag()), 13); + charms = ord::set_charm(charms, ord::charm_burned_flag()); + + assert!(ord::is_set_charm(charms, ord::charm_coin_flag()), 14); + assert!(ord::is_set_charm(charms, ord::charm_cursed_flag()), 15); + assert!(ord::is_set_charm(charms, ord::charm_epic_flag()), 16); + assert!(ord::is_set_charm(charms, ord::charm_legendary_flag()), 17); + assert!(ord::is_set_charm(charms, ord::charm_lost_flag()), 18); + assert!(ord::is_set_charm(charms, ord::charm_nineball_flag()), 19); + assert!(ord::is_set_charm(charms, ord::charm_rare_flag()), 20); + assert!(ord::is_set_charm(charms, ord::charm_reinscription_flag()), 21); + assert!(ord::is_set_charm(charms, ord::charm_unbound_flag()), 22); + assert!(ord::is_set_charm(charms, ord::charm_uncommon_flag()), 23); + assert!(ord::is_set_charm(charms, ord::charm_vindicated_flag()), 24); + assert!(ord::is_set_charm(charms, ord::charm_mythic_flag()), 25); + assert!(ord::is_set_charm(charms, ord::charm_burned_flag()), 26); + } +} \ No newline at end of file diff --git a/frameworks/framework-release/released/8/stdlib b/frameworks/framework-release/released/8/stdlib new file mode 100644 index 0000000000..cf81b625ba Binary files /dev/null and b/frameworks/framework-release/released/8/stdlib differ diff --git a/frameworks/moveos-stdlib/doc/simple_map.md b/frameworks/moveos-stdlib/doc/simple_map.md index 63251b6d91..2b27fa5269 100644 --- a/frameworks/moveos-stdlib/doc/simple_map.md +++ b/frameworks/moveos-stdlib/doc/simple_map.md @@ -46,7 +46,7 @@ This module provides a solution for unsorted maps, that is it has the properties -
struct SimpleMap<Key, Value> has store
+
struct SimpleMap<Key, Value> has copy, drop, store
 
@@ -57,7 +57,7 @@ This module provides a solution for unsorted maps, that is it has the properties -
struct Element<Key, Value> has store
+
struct Element<Key, Value> has copy, drop, store
 
@@ -93,7 +93,7 @@ Map key is not found -
public fun length<Key: store, Value: store>(map: &simple_map::SimpleMap<Key, Value>): u64
+
public fun length<Key, Value>(map: &simple_map::SimpleMap<Key, Value>): u64
 
@@ -105,7 +105,7 @@ Map key is not found Create an empty SimpleMap. -
public fun new<Key: store, Value: store>(): simple_map::SimpleMap<Key, Value>
+
public fun new<Key, Value>(): simple_map::SimpleMap<Key, Value>
 
@@ -119,7 +119,7 @@ This function is deprecated, use new instead.
#[deprecated]
-public fun create<Key: store, Value: store>(): simple_map::SimpleMap<Key, Value>
+public fun create<Key, Value>(): simple_map::SimpleMap<Key, Value>
 
@@ -141,7 +141,7 @@ This function is deprecated, use new instead. -
public fun borrow<Key: store, Value: store>(map: &simple_map::SimpleMap<Key, Value>, key: &Key): &Value
+
public fun borrow<Key, Value>(map: &simple_map::SimpleMap<Key, Value>, key: &Key): &Value
 
@@ -152,7 +152,7 @@ This function is deprecated, use new instead. -
public fun borrow_with_default<Key: store, Value: store>(map: &simple_map::SimpleMap<Key, Value>, key: &Key, default: &Value): &Value
+
public fun borrow_with_default<Key, Value>(map: &simple_map::SimpleMap<Key, Value>, key: &Key, default: &Value): &Value
 
@@ -163,7 +163,7 @@ This function is deprecated, use new instead. -
public fun borrow_mut<Key: store, Value: store>(map: &mut simple_map::SimpleMap<Key, Value>, key: &Key): &mut Value
+
public fun borrow_mut<Key, Value>(map: &mut simple_map::SimpleMap<Key, Value>, key: &Key): &mut Value
 
@@ -174,7 +174,7 @@ This function is deprecated, use new instead. -
public fun contains_key<Key: store, Value: store>(map: &simple_map::SimpleMap<Key, Value>, key: &Key): bool
+
public fun contains_key<Key, Value>(map: &simple_map::SimpleMap<Key, Value>, key: &Key): bool
 
@@ -185,7 +185,7 @@ This function is deprecated, use new instead. -
public fun destroy_empty<Key: store, Value: store>(map: simple_map::SimpleMap<Key, Value>)
+
public fun destroy_empty<Key, Value>(map: simple_map::SimpleMap<Key, Value>)
 
@@ -195,9 +195,10 @@ This function is deprecated, use new instead. ## Function `drop` Drop all keys and values in the map. This requires keys and values to be dropable. +Deprecated: The SimpleMap is dropable if the keys and values are dropable, so we don't need this function. -
public fun drop<Key: copy, drop, Value: drop>(map: simple_map::SimpleMap<Key, Value>)
+
public fun drop<Key: drop, Value: drop>(_map: simple_map::SimpleMap<Key, Value>)
 
@@ -208,7 +209,7 @@ Drop all keys and values in the map. This requires keys and values to be dropabl -
public fun add<Key: store, Value: store>(map: &mut simple_map::SimpleMap<Key, Value>, key: Key, value: Value)
+
public fun add<Key, Value>(map: &mut simple_map::SimpleMap<Key, Value>, key: Key, value: Value)
 
@@ -220,7 +221,7 @@ Drop all keys and values in the map. This requires keys and values to be dropabl Insert key/value pair or update an existing key to a new value -
public fun upsert<Key: store, Value: store>(map: &mut simple_map::SimpleMap<Key, Value>, key: Key, value: Value): (option::Option<Key>, option::Option<Value>)
+
public fun upsert<Key, Value>(map: &mut simple_map::SimpleMap<Key, Value>, key: Key, value: Value): (option::Option<Key>, option::Option<Value>)
 
@@ -257,7 +258,7 @@ Transform the map into two vectors with the keys and values respectively Primarily used to destroy a map -
public fun to_vec_pair<Key: store, Value: store>(map: simple_map::SimpleMap<Key, Value>): (vector<Key>, vector<Value>)
+
public fun to_vec_pair<Key, Value>(map: simple_map::SimpleMap<Key, Value>): (vector<Key>, vector<Value>)
 
@@ -268,5 +269,5 @@ Primarily used to destroy a map -
public fun remove<Key: store, Value: store>(map: &mut simple_map::SimpleMap<Key, Value>, key: &Key): (Key, Value)
+
public fun remove<Key, Value>(map: &mut simple_map::SimpleMap<Key, Value>, key: &Key): (Key, Value)
 
diff --git a/frameworks/moveos-stdlib/doc/simple_multimap.md b/frameworks/moveos-stdlib/doc/simple_multimap.md index afeafb79e6..c93ae16825 100644 --- a/frameworks/moveos-stdlib/doc/simple_multimap.md +++ b/frameworks/moveos-stdlib/doc/simple_multimap.md @@ -39,7 +39,7 @@ A simple map that stores key/value pairs in a vector, and support multi values f -
struct SimpleMultiMap<Key, Value> has store
+
struct SimpleMultiMap<Key, Value> has copy, drop, store
 
@@ -50,7 +50,7 @@ A simple map that stores key/value pairs in a vector, and support multi values f -
struct Element<Key, Value> has store
+
struct Element<Key, Value> has copy, drop, store
 
@@ -77,7 +77,7 @@ Map key is not found Create an empty SimpleMultiMap. -
public fun new<Key: store, Value: store>(): simple_multimap::SimpleMultiMap<Key, Value>
+
public fun new<Key, Value>(): simple_multimap::SimpleMultiMap<Key, Value>
 
@@ -88,7 +88,7 @@ Create an empty SimpleMultiMap. -
public fun length<Key: store, Value: store>(map: &simple_multimap::SimpleMultiMap<Key, Value>): u64
+
public fun length<Key, Value>(map: &simple_multimap::SimpleMultiMap<Key, Value>): u64
 
@@ -99,7 +99,7 @@ Create an empty SimpleMultiMap. -
public fun is_empty<Key: store, Value: store>(map: &simple_multimap::SimpleMultiMap<Key, Value>): bool
+
public fun is_empty<Key, Value>(map: &simple_multimap::SimpleMultiMap<Key, Value>): bool
 
@@ -110,7 +110,7 @@ Create an empty SimpleMultiMap. -
public fun borrow<Key: store, Value: store>(map: &simple_multimap::SimpleMultiMap<Key, Value>, key: &Key): &vector<Value>
+
public fun borrow<Key, Value>(map: &simple_multimap::SimpleMultiMap<Key, Value>, key: &Key): &vector<Value>
 
@@ -121,7 +121,7 @@ Create an empty SimpleMultiMap. -
public fun borrow_mut<Key: store, Value: store>(map: &mut simple_multimap::SimpleMultiMap<Key, Value>, key: &Key): &mut vector<Value>
+
public fun borrow_mut<Key, Value>(map: &mut simple_multimap::SimpleMultiMap<Key, Value>, key: &Key): &mut vector<Value>
 
@@ -132,7 +132,7 @@ Create an empty SimpleMultiMap. -
public fun borrow_first<Key: store, Value: store>(map: &simple_multimap::SimpleMultiMap<Key, Value>, key: &Key): &Value
+
public fun borrow_first<Key, Value>(map: &simple_multimap::SimpleMultiMap<Key, Value>, key: &Key): &Value
 
@@ -143,7 +143,7 @@ Create an empty SimpleMultiMap. -
public fun borrow_first_mut<Key: store, Value: store>(map: &mut simple_multimap::SimpleMultiMap<Key, Value>, key: &Key): &mut Value
+
public fun borrow_first_mut<Key, Value>(map: &mut simple_multimap::SimpleMultiMap<Key, Value>, key: &Key): &mut Value
 
@@ -154,7 +154,7 @@ Create an empty SimpleMultiMap. -
public fun borrow_first_with_default<Key: store, Value: store>(map: &simple_multimap::SimpleMultiMap<Key, Value>, key: &Key, default: &Value): &Value
+
public fun borrow_first_with_default<Key, Value>(map: &simple_multimap::SimpleMultiMap<Key, Value>, key: &Key, default: &Value): &Value
 
@@ -165,7 +165,7 @@ Create an empty SimpleMultiMap. -
public fun contains_key<Key: store, Value: store>(map: &simple_multimap::SimpleMultiMap<Key, Value>, key: &Key): bool
+
public fun contains_key<Key, Value>(map: &simple_multimap::SimpleMultiMap<Key, Value>, key: &Key): bool
 
@@ -176,7 +176,7 @@ Create an empty SimpleMultiMap. -
public fun destroy_empty<Key: store, Value: store>(map: simple_multimap::SimpleMultiMap<Key, Value>)
+
public fun destroy_empty<Key, Value>(map: simple_multimap::SimpleMultiMap<Key, Value>)
 
@@ -186,9 +186,10 @@ Create an empty SimpleMultiMap. ## Function `drop` Drop all keys and values in the map. This requires keys and values to be dropable. +Deprecated: The SimpleMultiMap is dropable if the keys and values are dropable, so we don't need this function. -
public fun drop<Key: copy, drop, Value: drop>(map: simple_multimap::SimpleMultiMap<Key, Value>)
+
public fun drop<Key: drop, Value: drop>(_map: simple_multimap::SimpleMultiMap<Key, Value>)
 
@@ -238,7 +239,7 @@ Primarily used to destroy a map Note: Do not assume the key's order -
public fun to_vec_pair<Key: store, Value: store>(map: simple_multimap::SimpleMultiMap<Key, Value>): (vector<Key>, vector<vector<Value>>)
+
public fun to_vec_pair<Key, Value>(map: simple_multimap::SimpleMultiMap<Key, Value>): (vector<Key>, vector<vector<Value>>)
 
@@ -249,5 +250,5 @@ Note: Do not assume the key's order -
public fun remove<Key: store, Value: store>(map: &mut simple_multimap::SimpleMultiMap<Key, Value>, key: &Key): (Key, vector<Value>)
+
public fun remove<Key, Value>(map: &mut simple_multimap::SimpleMultiMap<Key, Value>, key: &Key): (Key, vector<Value>)
 
diff --git a/frameworks/moveos-stdlib/sources/simple_map.move b/frameworks/moveos-stdlib/sources/simple_map.move index f1910ec1ac..52ca0729f0 100644 --- a/frameworks/moveos-stdlib/sources/simple_map.move +++ b/frameworks/moveos-stdlib/sources/simple_map.move @@ -18,21 +18,21 @@ module moveos_std::simple_map { /// Map key is not found const ErrorKeyNotFound: u64 = 2; - struct SimpleMap has store { + struct SimpleMap has copy, drop, store { data: vector>, } - struct Element has store { + struct Element has copy, drop, store { key: Key, value: Value, } - public fun length(map: &SimpleMap): u64 { + public fun length(map: &SimpleMap): u64 { vector::length(&map.data) } /// Create an empty SimpleMap. - public fun new(): SimpleMap { + public fun new(): SimpleMap { SimpleMap { data: vector::empty(), } @@ -41,7 +41,7 @@ module moveos_std::simple_map { #[deprecated] /// Create an empty SimpleMap. /// This function is deprecated, use `new` instead. - public fun create(): SimpleMap { + public fun create(): SimpleMap { new() } @@ -56,7 +56,7 @@ module moveos_std::simple_map { } } - public fun borrow( + public fun borrow( map: &SimpleMap, key: &Key, ): &Value { @@ -66,7 +66,7 @@ module moveos_std::simple_map { &vector::borrow(&map.data, idx).value } - public fun borrow_with_default( + public fun borrow_with_default( map: &SimpleMap, key: &Key, default: &Value, @@ -80,7 +80,7 @@ module moveos_std::simple_map { } } - public fun borrow_mut( + public fun borrow_mut( map: &mut SimpleMap, key: &Key, ): &mut Value { @@ -90,7 +90,7 @@ module moveos_std::simple_map { &mut vector::borrow_mut(&mut map.data, idx).value } - public fun contains_key( + public fun contains_key( map: &SimpleMap, key: &Key, ): bool { @@ -98,20 +98,18 @@ module moveos_std::simple_map { option::is_some(&maybe_idx) } - public fun destroy_empty(map: SimpleMap) { + public fun destroy_empty(map: SimpleMap) { let SimpleMap { data } = map; vector::destroy_empty(data); } /// Drop all keys and values in the map. This requires keys and values to be dropable. - public fun drop(map: SimpleMap) { - let SimpleMap { data } = map; - vector::for_each(data, |e| { - let Element { key:_, value:_ } = e; - }); + /// Deprecated: The `SimpleMap` is dropable if the keys and values are dropable, so we don't need this function. + public fun drop(_map: SimpleMap) { + } - public fun add( + public fun add( map: &mut SimpleMap, key: Key, value: Value, @@ -123,7 +121,7 @@ module moveos_std::simple_map { } /// Insert key/value pair or update an existing key to a new value - public fun upsert( + public fun upsert( map: &mut SimpleMap, key: Key, value: Value @@ -173,7 +171,7 @@ module moveos_std::simple_map { /// Transform the map into two vectors with the keys and values respectively /// Primarily used to destroy a map - public fun to_vec_pair( + public fun to_vec_pair( map: SimpleMap ): (vector, vector) { let keys: vector = vector::empty(); @@ -192,7 +190,7 @@ module moveos_std::simple_map { (keys, values) } - public fun remove( + public fun remove( map: &mut SimpleMap, key: &Key, ): (Key, Value) { @@ -203,7 +201,7 @@ module moveos_std::simple_map { (key, value) } - fun find( + fun find( map: &SimpleMap, key: &Key, ): option::Option{ diff --git a/frameworks/moveos-stdlib/sources/simple_multimap.move b/frameworks/moveos-stdlib/sources/simple_multimap.move index 11d7f08cd4..307a941ba2 100644 --- a/frameworks/moveos-stdlib/sources/simple_multimap.move +++ b/frameworks/moveos-stdlib/sources/simple_multimap.move @@ -10,32 +10,32 @@ module moveos_std::simple_multimap { /// Map key is not found const ErrorKeyNotFound: u64 = 1; - struct SimpleMultiMap has store { + struct SimpleMultiMap has copy, drop, store { data: vector>, } - struct Element has store { + struct Element has copy, drop, store { key: Key, value: vector, } /// Create an empty SimpleMultiMap. - public fun new(): SimpleMultiMap { + public fun new(): SimpleMultiMap { SimpleMultiMap { data: vector::empty(), } } - public fun length(map: &SimpleMultiMap): u64 { + public fun length(map: &SimpleMultiMap): u64 { vector::length(&map.data) } - public fun is_empty(map: &SimpleMultiMap): bool { + public fun is_empty(map: &SimpleMultiMap): bool { vector::is_empty(&map.data) } - public fun borrow( + public fun borrow( map: &SimpleMultiMap, key: &Key, ): &vector { @@ -46,7 +46,7 @@ module moveos_std::simple_multimap { &element.value } - public fun borrow_mut( + public fun borrow_mut( map: &mut SimpleMultiMap, key: &Key, ): &mut vector { @@ -57,7 +57,7 @@ module moveos_std::simple_multimap { &mut element.value } - public fun borrow_first( + public fun borrow_first( map: &SimpleMultiMap, key: &Key, ): &Value { @@ -68,7 +68,7 @@ module moveos_std::simple_multimap { vector::borrow(&element.value, 0) } - public fun borrow_first_mut( + public fun borrow_first_mut( map: &mut SimpleMultiMap, key: &Key, ): &mut Value { @@ -79,7 +79,7 @@ module moveos_std::simple_multimap { vector::borrow_mut(&mut element.value, 0) } - public fun borrow_first_with_default( + public fun borrow_first_with_default( map: &SimpleMultiMap, key: &Key, default: &Value, @@ -98,7 +98,7 @@ module moveos_std::simple_multimap { } } - public fun contains_key( + public fun contains_key( map: &SimpleMultiMap, key: &Key, ): bool { @@ -106,17 +106,14 @@ module moveos_std::simple_multimap { option::is_some(&maybe_idx) } - public fun destroy_empty(map: SimpleMultiMap) { + public fun destroy_empty(map: SimpleMultiMap) { let SimpleMultiMap { data } = map; vector::destroy_empty(data); } /// Drop all keys and values in the map. This requires keys and values to be dropable. - public fun drop(map: SimpleMultiMap) { - let SimpleMultiMap { data } = map; - vector::for_each(data, |e| { - let Element { key:_, value:_ } = e; - }); + /// Deprecated: The `SimpleMultiMap` is dropable if the keys and values are dropable, so we don't need this function. + public fun drop(_map: SimpleMultiMap) { } public fun add( @@ -171,7 +168,7 @@ module moveos_std::simple_multimap { /// Transform the map into two vectors with the keys and values respectively /// Primarily used to destroy a map /// Note: Do not assume the key's order - public fun to_vec_pair( + public fun to_vec_pair( map: SimpleMultiMap ): (vector, vector>) { let keys: vector = vector::empty(); @@ -190,7 +187,7 @@ module moveos_std::simple_multimap { (keys, values) } - public fun remove( + public fun remove( map: &mut SimpleMultiMap, key: &Key, ): (Key, vector) { @@ -201,7 +198,7 @@ module moveos_std::simple_multimap { (key, value) } - fun find( + fun find( map: &SimpleMultiMap, key: &Key, ): option::Option{ diff --git a/frameworks/moveos-stdlib/sources/sort.move b/frameworks/moveos-stdlib/sources/sort.move index 353bfd561e..bf38a03477 100644 --- a/frameworks/moveos-stdlib/sources/sort.move +++ b/frameworks/moveos-stdlib/sources/sort.move @@ -46,6 +46,11 @@ module moveos_std::sort { let len = vector::length(data); let swapped = true; while(swapped) { + //we can not return in the inline function, + //so put the length check here. + if(len <= 1){ + break + }; swapped = false; let i = 1; while(i < len) { diff --git a/frameworks/moveos-stdlib/src/natives/moveos_stdlib/event.rs b/frameworks/moveos-stdlib/src/natives/moveos_stdlib/event.rs index b45bca574c..30f6b51bde 100644 --- a/frameworks/moveos-stdlib/src/natives/moveos_stdlib/event.rs +++ b/frameworks/moveos-stdlib/src/natives/moveos_stdlib/event.rs @@ -76,6 +76,9 @@ pub fn native_emit( ty )) })?; + if log::log_enabled!(log::Level::Trace) { + log::trace!("Emitting event {}, {:?}", struct_tag, msg); + } let event_data = msg .simple_serialize(&layout) .ok_or_else(|| PartialVMError::new(StatusCode::INTERNAL_TYPE_ERROR))?; diff --git a/frameworks/rooch-nursery/sources/inscribe_factory.move b/frameworks/rooch-nursery/sources/inscribe_factory.move index b05b378509..2736d4cf26 100644 --- a/frameworks/rooch-nursery/sources/inscribe_factory.move +++ b/frameworks/rooch-nursery/sources/inscribe_factory.move @@ -9,7 +9,7 @@ module rooch_nursery::inscribe_factory { use moveos_std::address; use moveos_std::hash; use moveos_std::hex; - use moveos_std::object::{Self, Object}; + use moveos_std::object::Object; use moveos_std::string_utils; use moveos_std::simple_map::{Self, SimpleMap}; use moveos_std::wasm; @@ -307,12 +307,9 @@ module rooch_nursery::inscribe_factory { return err_str(b"generator_inscription_id is not validity bitseed") }; - let generator_txid = ord::inscription_id_txid(&generator_inscription_id); - let generator_index = ord::inscription_id_index(&generator_inscription_id); - let inscription_obj = ord::borrow_inscription(generator_txid, generator_index); + let inscription = ord::borrow_inscription(generator_inscription_id); - let inscrption = object::borrow(inscription_obj); - let wasm_bytes = ord::body(inscrption); + let wasm_bytes = ord::body(inscription); let (is_valid, reason) = inscribe_verify(wasm_bytes, deploy_args, seed, user_input, metadata, content_type, body); if (!is_valid) {