diff --git a/Cargo.toml b/Cargo.toml index 13f74afca6..3cf748ff41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,7 +67,7 @@ homepage = "https://rooch.network" license = "Apache-2.0" publish = false repository = "https://github.com/rooch-network/rooch" -rust-version = "1.77.1" +rust-version = "1.77.2" version = "0.5.4" diff --git a/crates/rooch-framework-tests/src/tests/bitcoin_test.rs b/crates/rooch-framework-tests/src/tests/bitcoin_test.rs index 253481d51d..ed2316677f 100644 --- a/crates/rooch-framework-tests/src/tests/bitcoin_test.rs +++ b/crates/rooch-framework-tests/src/tests/bitcoin_test.rs @@ -189,7 +189,7 @@ fn check_utxo(txs: Vec, binding_test: &binding_test::RustBindingTes assert_eq!(inscription_object.value.index, index); assert_eq!( inscription_object.value.body, - inscription.body.unwrap_or_default() + inscription.payload.body.unwrap_or_default() ); } } diff --git a/crates/rooch-framework-tests/src/tests/ord_test.rs b/crates/rooch-framework-tests/src/tests/ord_test.rs index da591ac1f9..6b98e52932 100644 --- a/crates/rooch-framework-tests/src/tests/ord_test.rs +++ b/crates/rooch-framework-tests/src/tests/ord_test.rs @@ -38,7 +38,7 @@ fn decode_inscription(btx_tx_hex: &str) { { debug!("{}. inscription: {:?}", i, inscription); assert_eq!( - inscription.body.unwrap_or_default(), + inscription.payload.body.unwrap_or_default(), inscription_from_move.body ); } diff --git a/crates/rooch-genesis/src/lib.rs b/crates/rooch-genesis/src/lib.rs index 12be743c25..c30f72d7f4 100644 --- a/crates/rooch-genesis/src/lib.rs +++ b/crates/rooch-genesis/src/lib.rs @@ -447,13 +447,11 @@ mod tests { use rooch_types::rooch_network::RoochNetwork; use tracing::info; - fn genesis_init_test_case(network: RoochNetwork) { + fn genesis_init_test_case(network: RoochNetwork, genesis: RoochGenesis) { info!( "genesis init test case for network: {:?}", network.chain_id.id ); - let genesis = - super::RoochGenesis::build(network.clone()).expect("build rooch genesis failed"); let opt = RoochOpt::new_with_temp_store().expect("create rooch opt failed"); let rooch_db = RoochDB::init(&opt.store_config()).expect("init rooch db failed"); @@ -465,8 +463,18 @@ mod tests { .expect("load gas parameter from chain failed"); assert_eq!( - FrameworksGasParameters::initial().to_gas_schedule_config(), - gas_parameter.to_gas_schedule_config() + genesis + .initial_gas_config + .entries + .into_iter() + .map(|entry| (entry.key, entry.val)) + .collect::>(), + gas_parameter + .to_gas_schedule_config() + .entries + .into_iter() + .map(|entry| (entry.key, entry.val)) + .collect::>(), ); let module_store_state = resolver @@ -523,13 +531,36 @@ mod tests { ); } + // #[test] + // fn test_builtin_genesis_init() { + // let _ = tracing_subscriber::fmt::try_init(); + // { + // let network = BuiltinChainID::Local.into(); + // let genesis = RoochGenesis::load(BuiltinChainID::Local).unwrap(); + // genesis_init_test_case(network, genesis); + // } + // { + // let network = BuiltinChainID::Dev.into(); + // let genesis = RoochGenesis::load(BuiltinChainID::Dev).unwrap(); + // genesis_init_test_case(network, genesis); + // } + // { + // let network = BuiltinChainID::Test.into(); + // let genesis = RoochGenesis::load(BuiltinChainID::Test).unwrap(); + // genesis_init_test_case(network, genesis); + // } + // { + // let network = BuiltinChainID::Main.into(); + // let genesis = RoochGenesis::load(BuiltinChainID::Main).unwrap(); + // genesis_init_test_case(network, genesis); + // } + // } + #[test] - fn test_genesis_init() { - let _ = tracing_subscriber::fmt::try_init(); - genesis_init_test_case(RoochNetwork::local()); - genesis_init_test_case(RoochNetwork::dev()); - genesis_init_test_case(RoochNetwork::test()); - genesis_init_test_case(RoochNetwork::main()); + fn test_custom_genesis_init() { + let network = RoochNetwork::new(100.into(), BuiltinChainID::Local.genesis_config().clone()); + let genesis = RoochGenesis::build(network.clone()).unwrap(); + genesis_init_test_case(network, genesis); } #[test] diff --git a/crates/rooch-indexer/src/tests/test_indexer.rs b/crates/rooch-indexer/src/tests/test_indexer.rs index d1bbc40b4e..2ba8bce7e1 100644 --- a/crates/rooch-indexer/src/tests/test_indexer.rs +++ b/crates/rooch-indexer/src/tests/test_indexer.rs @@ -8,11 +8,9 @@ use anyhow::Result; use move_core_types::account_address::AccountAddress; use move_core_types::vm_status::KeptVMStatus; use moveos_types::h256::H256; -use moveos_types::move_types::random_type_tag; use moveos_types::moveos_std::object::{ObjectEntity, ObjectID}; use moveos_types::moveos_std::tx_context::TxContext; -use moveos_types::state::{KeyState, MoveStructType, State}; -use moveos_types::test_utils::random_bytes; +use moveos_types::state::MoveStructType; use moveos_types::transaction::{TransactionExecutionInfo, VerifiedMoveOSTransaction}; use rand::{random, thread_rng, Rng}; use rooch_config::store_config::DEFAULT_DB_INDEXER_SUBDIR; @@ -22,8 +20,8 @@ use rooch_types::indexer::event::{EventFilter, IndexerEvent}; use rooch_types::indexer::state::{IndexerObjectState, ObjectStateFilter}; use rooch_types::indexer::transaction::{IndexerTransaction, TransactionFilter}; use rooch_types::test_utils::{ - random_event, random_function_calls, random_ledger_transaction, random_string, - random_table_object, random_verified_move_action, + random_event, random_function_calls, random_ledger_transaction, random_table_object, + random_verified_move_action, }; fn random_update_object_states(states: Vec) -> Vec { diff --git a/crates/rooch-open-rpc-spec/schemas/openrpc.json b/crates/rooch-open-rpc-spec/schemas/openrpc.json index 5f93b0f98d..7d7efbd388 100644 --- a/crates/rooch-open-rpc-spec/schemas/openrpc.json +++ b/crates/rooch-open-rpc-spec/schemas/openrpc.json @@ -1418,8 +1418,12 @@ "bitcoin_txid", "body", "index", + "inscription_number", + "is_curse", "metadata", "offset", + "parents", + "sequence_number", "txid" ], "properties": { @@ -1454,6 +1458,14 @@ "format": "uint32", "minimum": 0.0 }, + "inscription_number": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "is_curse": { + "type": "boolean" + }, "metadata": { "$ref": "#/components/schemas/alloc::vec::Vec" }, @@ -1472,15 +1484,8 @@ "format": "uint64", "minimum": 0.0 }, - "parent": { - "anyOf": [ - { - "$ref": "#/components/schemas/ObjectID" - }, - { - "type": "null" - } - ] + "parents": { + "$ref": "#/components/schemas/alloc::vec::Vec" }, "pointer": { "type": [ @@ -1490,6 +1495,11 @@ "format": "uint64", "minimum": 0.0 }, + "sequence_number": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, "txid": { "$ref": "#/components/schemas/primitive_types::H256" } 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 e5d58e0f03..3cd1eae392 100644 --- a/crates/rooch-rpc-api/src/jsonrpc_types/btc/ord.rs +++ b/crates/rooch-rpc-api/src/jsonrpc_types/btc/ord.rs @@ -4,7 +4,7 @@ use crate::jsonrpc_types::address::BitcoinAddressView; use crate::jsonrpc_types::btc::transaction::{hex_to_txid, TxidView}; use crate::jsonrpc_types::{ - BytesView, H256View, MoveStringView, RoochAddressView, StrView, StructTagView, + BytesView, H256View, MoveStringView, ObjectIDVecView, RoochAddressView, StrView, StructTagView, }; use anyhow::Result; use bitcoin::hashes::Hash; @@ -86,12 +86,15 @@ pub struct InscriptionView { pub bitcoin_txid: TxidView, pub index: u32, pub offset: u64, + pub sequence_number: u32, + pub inscription_number: u32, + pub is_curse: bool, pub body: BytesView, pub content_encoding: Option, pub content_type: Option, pub metadata: BytesView, pub metaprotocol: Option, - pub parent: Option, + pub parents: ObjectIDVecView, pub pointer: Option, } @@ -102,12 +105,15 @@ impl From for InscriptionView { bitcoin_txid: StrView(Txid::from_byte_array(inscription.txid.into_bytes())), index: inscription.index, offset: inscription.offset, + sequence_number: inscription.sequence_number, + inscription_number: inscription.inscription_number, + is_curse: inscription.is_curse, body: StrView(inscription.body), content_encoding: Option::::from(inscription.content_encoding).map(StrView), content_type: Option::::from(inscription.content_type).map(StrView), metadata: StrView(inscription.metadata), metaprotocol: Option::::from(inscription.metaprotocol).map(StrView), - parent: Option::::from(inscription.parent), + parents: inscription.parents.into(), pointer: Option::::from(inscription.pointer), } } diff --git a/crates/rooch-types/src/bitcoin/ord.rs b/crates/rooch-types/src/bitcoin/ord.rs index fdaaa4cedf..d2928f036f 100644 --- a/crates/rooch-types/src/bitcoin/ord.rs +++ b/crates/rooch-types/src/bitcoin/ord.rs @@ -7,10 +7,12 @@ use crate::addresses::BITCOIN_MOVE_ADDRESS; use crate::indexer::state::IndexerObjectState; use crate::into_address::IntoAddress; use anyhow::Result; -use move_core_types::language_storage::StructTag; +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 moveos_types::state::{MoveState, MoveStructState, MoveStructType}; use moveos_types::{ module_binding::{ModuleBinding, MoveFunctionCaller}, move_std::{option::MoveOption, string::MoveString}, @@ -18,8 +20,8 @@ use moveos_types::{ object::{self, ObjectID}, tx_context::TxContext, }, - state::{MoveState, MoveStructState, MoveStructType}, }; +use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use std::fmt; use std::fmt::{Display, Formatter}; @@ -57,7 +59,6 @@ impl MoveStructState for InscriptionID { pub struct Inscription { pub txid: AccountAddress, pub index: u32, - pub input: u32, pub offset: u64, pub sequence_number: u32, pub inscription_number: u32, @@ -67,8 +68,9 @@ pub struct Inscription { pub content_type: MoveOption, pub metadata: Vec, pub metaprotocol: MoveOption, - pub parent: MoveOption, + pub parents: Vec, pub pointer: MoveOption, + pub rune: Option, } impl MoveStructType for Inscription { @@ -82,7 +84,6 @@ impl MoveStructState for Inscription { move_core_types::value::MoveStructLayout::new(vec![ AccountAddress::type_layout(), u32::type_layout(), - u32::type_layout(), u64::type_layout(), u32::type_layout(), u32::type_layout(), @@ -92,8 +93,9 @@ impl MoveStructState for Inscription { MoveOption::::type_layout(), Vec::::type_layout(), MoveOption::::type_layout(), - MoveOption::::type_layout(), + Vec::::type_layout(), MoveOption::::type_layout(), + MoveOption::::type_layout(), ]) } } @@ -106,6 +108,52 @@ pub fn derive_inscription_id(inscription_id: &InscriptionID) -> ObjectID { ) } +#[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize)] +pub struct Envelope { + pub input: u32, + pub offset: u32, + pub pushnum: bool, + pub stutter: bool, + pub payload: T, +} + +impl MoveStructType for Envelope +where + T: MoveStructType + DeserializeOwned, +{ + const ADDRESS: AccountAddress = BITCOIN_MOVE_ADDRESS; + const MODULE_NAME: &'static IdentStr = MODULE_NAME; + const STRUCT_NAME: &'static IdentStr = ident_str!("Envelope"); + + fn type_params() -> Vec { + vec![TypeTag::Struct(Box::new(T::struct_tag()))] + } + + fn struct_tag() -> StructTag { + StructTag { + address: Self::ADDRESS, + module: Self::MODULE_NAME.to_owned(), + name: Self::STRUCT_NAME.to_owned(), + type_params: vec![T::struct_tag().into()], + } + } +} + +impl MoveStructState for Envelope +where + T: MoveStructState, +{ + fn struct_layout() -> move_core_types::value::MoveStructLayout { + move_core_types::value::MoveStructLayout::new(vec![ + u32::type_layout(), + u32::type_layout(), + bool::type_layout(), + bool::type_layout(), + MoveTypeLayout::Struct(T::struct_layout()), + ]) + } +} + #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq, Default)] pub struct InscriptionRecord { pub body: Vec, @@ -115,9 +163,10 @@ pub struct InscriptionRecord { pub incomplete_field: bool, pub metadata: Vec, pub metaprotocol: MoveOption, - pub parent: MoveOption, + pub parents: Vec, pub pointer: MoveOption, pub unrecognized_even_field: bool, + pub rune: Option, } impl MoveStructType for InscriptionRecord { @@ -136,21 +185,24 @@ impl MoveStructState for InscriptionRecord { bool::type_layout(), Vec::::type_layout(), MoveOption::::type_layout(), - MoveOption::::type_layout(), + Vec::::type_layout(), MoveOption::::type_layout(), bool::type_layout(), + MoveOption::::type_layout(), ]) } } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct InscriptionStore { - /// The latest transaction index has been processed - pub latest_tx_index: u64, - /// The inscriptions table id + /// The inscriptions ids table_vec object id pub inscriptions: ObjectID, - /// The inscription ids table_vec id - pub inscription_ids: ObjectID, + /// cursed inscription number generator + pub cursed_inscription_count: u32, + /// blessed inscription number generator + pub blessed_inscription_count: u32, + /// sequence number generator + pub next_sequence_number: u32, } impl InscriptionStore { @@ -168,9 +220,10 @@ impl MoveStructType for InscriptionStore { impl MoveStructState for InscriptionStore { fn struct_layout() -> move_core_types::value::MoveStructLayout { move_core_types::value::MoveStructLayout::new(vec![ - u64::type_layout(), - ObjectID::type_layout(), ObjectID::type_layout(), + u32::type_layout(), + u32::type_layout(), + u32::type_layout(), ]) } } @@ -283,3 +336,16 @@ impl From for InscriptionID { } } } + +#[derive(Debug, PartialEq, Clone)] +pub enum Curse { + DuplicateField, + IncompleteField, + NotAtOffsetZero, + NotInFirstInput, + Pointer, + Pushnum, + Reinscription, + Stutter, + UnrecognizedEvenField, +} diff --git a/examples/bitcoin_plants/sources/plants.move b/examples/bitcoin_plants/sources/plants.move index 25f5bb7568..66efda545f 100644 --- a/examples/bitcoin_plants/sources/plants.move +++ b/examples/bitcoin_plants/sources/plants.move @@ -196,13 +196,12 @@ module bitcoin_plants::plants { @0x3232423, 0, 0, - 0, vector[], option::none(), option::none(), vector[], option::none(), - option::none(), + vector[], option::none(), ); diff --git a/frameworks/bitcoin-move/doc/ord.md b/frameworks/bitcoin-move/doc/ord.md index b93aa74bbd..1118eb5bd5 100644 --- a/frameworks/bitcoin-move/doc/ord.md +++ b/frameworks/bitcoin-move/doc/ord.md @@ -9,11 +9,21 @@ - [Struct `Flotsam`](#0x4_ord_Flotsam) - [Struct `SatPoint`](#0x4_ord_SatPoint) - [Resource `Inscription`](#0x4_ord_Inscription) +- [Struct `Envelope`](#0x4_ord_Envelope) - [Struct `InscriptionRecord`](#0x4_ord_InscriptionRecord) - [Struct `InvalidInscriptionEvent`](#0x4_ord_InvalidInscriptionEvent) - [Struct `MetaprotocolValidity`](#0x4_ord_MetaprotocolValidity) - [Resource `InscriptionStore`](#0x4_ord_InscriptionStore) - [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 `new_inscription_id`](#0x4_ord_new_inscription_id) - [Function `derive_inscription_id`](#0x4_ord_derive_inscription_id) @@ -27,14 +37,13 @@ - [Function `process_transaction`](#0x4_ord_process_transaction) - [Function `txid`](#0x4_ord_txid) - [Function `index`](#0x4_ord_index) -- [Function `input`](#0x4_ord_input) - [Function `offset`](#0x4_ord_offset) - [Function `body`](#0x4_ord_body) - [Function `content_encoding`](#0x4_ord_content_encoding) - [Function `content_type`](#0x4_ord_content_type) - [Function `metadata`](#0x4_ord_metadata) - [Function `metaprotocol`](#0x4_ord_metaprotocol) -- [Function `parent`](#0x4_ord_parent) +- [Function `parents`](#0x4_ord_parents) - [Function `pointer`](#0x4_ord_pointer) - [Function `inscription_id_txid`](#0x4_ord_inscription_id_txid) - [Function `inscription_id_index`](#0x4_ord_inscription_id_index) @@ -135,6 +144,17 @@ + + +## Struct `Envelope` + + + +
struct Envelope<T> has copy, drop, store
+
+ + + ## Struct `InscriptionRecord` @@ -203,6 +223,88 @@ How many satoshis are in "one bitcoin". + + +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];
+
+ + + @@ -240,6 +342,105 @@ How may blocks between halvings. + + +## 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>
+
+ + + ## Function `genesis_init` @@ -383,17 +584,6 @@ How may blocks between halvings. - - -## Function `input` - - - -
public fun input(self: &ord::Inscription): u32
-
- - - ## Function `offset` @@ -460,13 +650,13 @@ How may blocks between halvings. - + -## Function `parent` +## Function `parents` -
public fun parent(self: &ord::Inscription): option::Option<object::ObjectID>
+
public fun parents(self: &ord::Inscription): vector<object::ObjectID>
 
@@ -590,7 +780,7 @@ Get the SatPoint's output_index -
public fun unpack_record(record: ord::InscriptionRecord): (vector<u8>, option::Option<string::String>, option::Option<string::String>, vector<u8>, option::Option<string::String>, option::Option<ord::InscriptionID>, option::Option<u64>)
+
public fun unpack_record(record: ord::InscriptionRecord): (vector<u8>, option::Option<string::String>, option::Option<string::String>, vector<u8>, option::Option<string::String>, vector<ord::InscriptionID>, option::Option<u64>)
 
diff --git a/frameworks/bitcoin-move/sources/ord.move b/frameworks/bitcoin-move/sources/ord.move index b68f956900..358009616a 100644 --- a/frameworks/bitcoin-move/sources/ord.move +++ b/frameworks/bitcoin-move/sources/ord.move @@ -37,6 +37,52 @@ module bitcoin_move::ord { /// How many satoshis are in "one bitcoin". const COIN_VALUE: u64 = 100_000_000; + /// 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 + } + struct InscriptionID has store, copy, drop { txid: address, index: u32, @@ -60,8 +106,6 @@ module bitcoin_move::ord { struct Inscription has key { txid: address, index: u32, - /// Transaction input index - input: u32, /// inscription offset offset: u64, /// monotonically increasing @@ -76,8 +120,18 @@ module bitcoin_move::ord { content_type: Option, metadata: vector, metaprotocol: Option, - parent: Option, + parents: vector, pointer: Option, + // Reserved for extending the Rune protocol + rune: Option, + } + + struct Envelope has store, copy, drop { + input: u32, + offset: u32, + pushnum: bool, + stutter: bool, + payload: T, } struct InscriptionRecord has store, copy, drop { @@ -88,9 +142,10 @@ module bitcoin_move::ord { incomplete_field: bool, metadata: vector, metaprotocol: Option, - parent: Option, + parents: vector, pointer: Option, unrecognized_even_field: bool, + rune: Option, } struct InvalidInscriptionEvent has store, copy, drop { @@ -107,12 +162,18 @@ module bitcoin_move::ord { struct InscriptionStore has key{ inscriptions: TableVec, + cursed_inscription_count: u32, + blessed_inscription_count: u32, + next_sequence_number: u32, } public(friend) fun genesis_init(_genesis_account: &signer){ let inscriptions = table_vec::new(); let store = InscriptionStore{ inscriptions, + cursed_inscription_count: 0, + blessed_inscription_count: 0, + next_sequence_number: 0, }; let store_obj = object::new_named_object(store); object::to_shared(store_obj); @@ -159,23 +220,28 @@ module bitcoin_move::ord { table_vec::length(& store.inscriptions) } - fun record_to_inscription(txid: address, index: u32, input: u32, offset: u64, record: InscriptionRecord): Inscription{ - let parent = option::map(record.parent, |e| derive_inscription_id(e)); + fun record_to_inscription(txid: address, index: u32, offset: u64, record: InscriptionRecord, is_curse: bool, 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, - input, offset, - sequence_number: 0, - inscription_number: 0, - is_curse: false, + 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, - parent, + parents, pointer: record.pointer, + rune: option::none(), } } @@ -252,9 +318,6 @@ module bitcoin_move::ord { let to_address = types::txout_object_address(match_output); inscription.offset = new_sat_point.offset; - // TODO handle curse inscription - // https://github.com/rooch-network/rooch/issues/1447 - // drop the temporary area if inscription is transferred. drop_temp_area(&mut inscription_obj); object::transfer_extend(inscription_obj, to_address); @@ -296,7 +359,6 @@ module bitcoin_move::ord { inscription.offset = new_sat_point.offset; - // TODO handle curse inscription object::transfer_extend(inscription_obj, to_address); vector::push_back(&mut new_sat_points, new_sat_point); @@ -366,7 +428,6 @@ module bitcoin_move::ord { idx = idx + 1; }; - // input_utxo_value_accumulator = input_utxo_value_accumulator + offset; let idx = 0; let output_len = vector::length(txoutput); @@ -457,7 +518,7 @@ module bitcoin_move::ord { txid: tx_id, input_index, record, - }); + }); }; idx = idx + 1; }; @@ -472,10 +533,6 @@ module bitcoin_move::ord { self.index } - public fun input(self: &Inscription): u32{ - self.input - } - public fun offset(self: &Inscription): u64{ self.offset } @@ -500,8 +557,8 @@ module bitcoin_move::ord { self.metaprotocol } - public fun parent(self: &Inscription): Option{ - self.parent + public fun parents(self: &Inscription): vector{ + self.parents } public fun pointer(self: &Inscription): Option{ @@ -512,7 +569,6 @@ module bitcoin_move::ord { let Inscription{ txid: _, index: _, - input: _, offset: _, sequence_number: _, inscription_number: _, @@ -522,8 +578,9 @@ module bitcoin_move::ord { content_type: _, metadata: _, metaprotocol: _, - parent: _, + parents: _, pointer: _, + rune: _, } = self; } @@ -584,7 +641,7 @@ module bitcoin_move::ord { // ==== InscriptionRecord ==== // public fun unpack_record(record: InscriptionRecord): - (vector, Option, Option, vector, Option, Option, Option){ + (vector, Option, Option, vector, Option, vector, Option){ let InscriptionRecord{ body, content_encoding, @@ -593,14 +650,16 @@ module bitcoin_move::ord { incomplete_field: _, metadata, metaprotocol, - parent, + parents, pointer, unrecognized_even_field: _, + rune: _, } = record; - (body, content_encoding, content_type, metadata, metaprotocol, parent, pointer) + (body, content_encoding, content_type, metadata, metaprotocol, parents, pointer) } public fun from_transaction(tx: &Transaction, input_utxo_values: Option>): vector{ + let inscription_store = borrow_mut_inscription_store(); let tx_id = types::tx_id(tx); let inscriptions = vector::empty(); let inputs = types::tx_input(tx); @@ -617,19 +676,34 @@ module bitcoin_move::ord { 0 }; - let inscription_records_from_witness = from_witness(witness); + 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 record = vector::borrow(&inscription_records_from_witness, j); + 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 = record_to_inscription(tx_id, (index_counter as u32), (input_idx as u32), offset, *record); + + // Handle curse inscription + let curse = handle_curse_inscription(inscription); + let is_curse = if(option::is_some(&curse)) {true} else {false}; + let inscription_number = if(is_curse) { + let number: u32 = inscription_store.cursed_inscription_count; + inscription_store.cursed_inscription_count = inscription_store.cursed_inscription_count + 1; + (number + 1) + } else { + let number: u32 = inscription_store.blessed_inscription_count; + inscription_store.blessed_inscription_count = inscription_store.blessed_inscription_count + 1; + number + }; + let sequence_number = inscription_store.next_sequence_number; + inscription_store.next_sequence_number = inscription_store.next_sequence_number + 1; + let inscription = record_to_inscription(tx_id, (index_counter as u32), offset, record, is_curse, inscription_number, sequence_number); vector::push_back(&mut inscriptions, inscription); index_counter = index_counter + 1; j = j + 1; @@ -645,7 +719,7 @@ module bitcoin_move::ord { from_transaction(&transaction, option::none()) } - native fun from_witness(witness: &Witness): vector; + native fun parse_inscription_from_witness(witness: &Witness): vector>; /// Block Rewards public fun subsidy_by_height(height: u64): u64 { @@ -657,6 +731,35 @@ module bitcoin_move::ord { } } + 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 + } + // ===== permenent area ========== // #[private_generics(S)] public fun add_permanent_state(inscription: &mut Object, state: S){ @@ -847,20 +950,18 @@ module bitcoin_move::ord { public fun new_inscription_object_for_test( txid: address, index: u32, - input: u32, offset: u64, body: vector, content_encoding: Option, content_type: Option, metadata: vector, metaprotocol: Option, - parent: Option, + parents: vector, pointer: Option, ): Object { let inscription = Inscription { txid, index, - input, offset, sequence_number: 0, inscription_number: 0, @@ -870,8 +971,9 @@ module bitcoin_move::ord { content_type, metadata, metaprotocol, - parent, + parents, pointer, + rune: option::none(), }; object::new(inscription) @@ -883,7 +985,6 @@ module bitcoin_move::ord { let Inscription { txid: _, index: _, - input: _, offset: _, sequence_number: _, inscription_number: _, @@ -893,8 +994,9 @@ module bitcoin_move::ord { content_type: _, metadata: _, metaprotocol: _, - parent: _, + parents: _, pointer: _, + rune: _, } = inscription; } @@ -911,13 +1013,12 @@ module bitcoin_move::ord { txid, 0, 0, - 0, vector[], option::none(), option::none(), vector[], option::none(), - option::none(), + vector[], option::none(), ); add_permanent_state(&mut inscription_obj, PermanentState{value: 10}); @@ -949,13 +1050,12 @@ module bitcoin_move::ord { txid, 0, 0, - 0, vector[], option::none(), option::none(), vector[], option::none(), - option::none(), + vector[], option::none(), ); add_temp_state(&mut inscription_obj, TempState{value: 10}); @@ -987,13 +1087,12 @@ module bitcoin_move::ord { txid, 0, 0, - 0, vector[], option::none(), option::none(), vector[], option::none(), - option::none(), + vector[], option::none(), ); @@ -1030,20 +1129,18 @@ module bitcoin_move::ord { public fun new_inscription_for_test( txid: address, index: u32, - input: u32, offset: u64, body: vector, content_encoding: Option, content_type: Option, metadata: vector, metaprotocol: Option, - parent: Option, + parents: vector, pointer: Option, ): Inscription { Inscription { txid, index, - input, offset, sequence_number: 0, inscription_number: 0, @@ -1053,8 +1150,9 @@ module bitcoin_move::ord { content_type, metadata, metaprotocol, - parent, + parents, pointer, + rune: option::none(), } } @@ -1074,13 +1172,12 @@ module bitcoin_move::ord { test_txid, 0, 0, - 0, body, option::none(), option::some(string::utf8(content_type)), vector[], option::none(), - option::none(), + vector[], option::none(), ); diff --git a/frameworks/bitcoin-move/src/natives/gas_parameter/ord.rs b/frameworks/bitcoin-move/src/natives/gas_parameter/ord.rs index 00faa77c23..104e0e17ce 100644 --- a/frameworks/bitcoin-move/src/natives/gas_parameter/ord.rs +++ b/frameworks/bitcoin-move/src/natives/gas_parameter/ord.rs @@ -6,5 +6,7 @@ use rooch_framework::natives::gas_parameter::native::MUL; rooch_framework::natives::gas_parameter::native::define_gas_parameters_for_natives!(GasParameters, "ord", [ [.from_witness.base, "from_witness.base", 10000 * MUL], - [.from_witness.per_byte, "from_witness.per_byte", 50 * MUL] + [.from_witness.per_byte, "from_witness.per_byte", 50 * MUL], + [.parse_inscription_from_witness.base, "parse_inscription_from_witness.base", 10000 * MUL], + [.parse_inscription_from_witness.per_byte, "parse_inscription_from_witness.per_byte", 50 * MUL] ]); diff --git a/frameworks/bitcoin-move/src/natives/ord/envelope.rs b/frameworks/bitcoin-move/src/natives/ord/envelope.rs index 2599838eff..07581b3861 100644 --- a/frameworks/bitcoin-move/src/natives/ord/envelope.rs +++ b/frameworks/bitcoin-move/src/natives/ord/envelope.rs @@ -2,8 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 // Code from https://github.com/ordinals/ord/ +use bitcoin::script::Instruction::{Op, PushBytes}; use bitcoin::{Script, Transaction}; use std::collections::BTreeMap; +use std::iter::Peekable; use { super::inscription::Inscription, bitcoin::blockdata::{ @@ -21,17 +23,19 @@ pub(crate) const PARENT_TAG: [u8; 1] = [3]; pub(crate) const METADATA_TAG: [u8; 1] = [5]; pub(crate) const METAPROTOCOL_TAG: [u8; 1] = [7]; pub(crate) const CONTENT_ENCODING_TAG: [u8; 1] = [9]; +pub(crate) const RUNE_TAG: [u8; 1] = [13]; type Result = std::result::Result; -pub(crate) type RawEnvelope = Envelope>>; -pub(crate) type ParsedEnvelope = Envelope; +pub type RawEnvelope = Envelope>>; +pub type ParsedEnvelope = Envelope; #[derive(Debug, Default, PartialEq, Clone)] -pub(crate) struct Envelope { - pub(crate) payload: T, - pub(crate) input: u32, - pub(crate) offset: u32, - pub(crate) pushnum: bool, +pub struct Envelope { + pub payload: T, + pub input: u32, + pub offset: u32, + pub pushnum: bool, + pub stutter: bool, } fn remove_field(fields: &mut BTreeMap<&[u8], Vec<&[u8]>>, field: &[u8]) -> Option> { @@ -50,6 +54,15 @@ fn remove_field(fields: &mut BTreeMap<&[u8], Vec<&[u8]>>, field: &[u8]) -> Optio } } +fn remove_array_field(fields: &mut BTreeMap<&[u8], Vec<&[u8]>>, field: &[u8]) -> Vec> { + fields + .remove(field) + .unwrap_or_default() + .into_iter() + .map(|v| v.to_vec()) + .collect() +} + fn remove_and_concatenate_field( fields: &mut BTreeMap<&[u8], Vec<&[u8]>>, field: &[u8], @@ -88,8 +101,9 @@ impl From for ParsedEnvelope { let content_type = remove_field(&mut fields, &CONTENT_TYPE_TAG); let metadata = remove_and_concatenate_field(&mut fields, &METADATA_TAG); let metaprotocol = remove_field(&mut fields, &METAPROTOCOL_TAG); - let parent = remove_field(&mut fields, &PARENT_TAG); + let parents = remove_array_field(&mut fields, &PARENT_TAG); let pointer = remove_field(&mut fields, &POINTER_TAG); + let rune = remove_field(&mut fields, &RUNE_TAG); let unrecognized_even_field = fields .keys() @@ -110,19 +124,21 @@ impl From for ParsedEnvelope { incomplete_field, metadata, metaprotocol, - parent, + parents, pointer, unrecognized_even_field, + rune, }, input: envelope.input, offset: envelope.offset, pushnum: envelope.pushnum, + stutter: envelope.stutter, } } } impl ParsedEnvelope { - pub(crate) fn from_transaction(transaction: &Transaction) -> Vec { + pub fn from_transaction(transaction: &Transaction) -> Vec { RawEnvelope::from_transaction(transaction) .into_iter() .map(|envelope| envelope.into()) @@ -148,14 +164,17 @@ impl RawEnvelope { pub(crate) fn from_tapscript(tapscript: &Script, input: usize) -> Result> { let mut envelopes = Vec::new(); - let mut instructions = tapscript.instructions(); + let mut instructions = tapscript.instructions().peekable(); - while let Some(instruction) = instructions.next() { - if instruction? == Instruction::PushBytes((&[]).into()) { - if let Some(envelope) = - Self::from_instructions(&mut instructions, input, envelopes.len())? - { + let mut stuttered = false; + while let Some(instruction) = instructions.next().transpose()? { + if instruction == Instruction::PushBytes((&[]).into()) { + let (stutter, envelope) = + Self::from_instructions(&mut instructions, input, envelopes.len(), stuttered)?; + if let Some(envelope) = envelope { envelopes.push(envelope); + } else { + stuttered = stutter; } } } @@ -163,17 +182,29 @@ impl RawEnvelope { Ok(envelopes) } + fn accept(instructions: &mut Peekable, instruction: Instruction) -> Result { + if instructions.peek() == Some(&Ok(instruction)) { + instructions.next().transpose()?; + Ok(true) + } else { + Ok(false) + } + } + fn from_instructions( - instructions: &mut Instructions, + instructions: &mut Peekable, input: usize, offset: usize, - ) -> Result> { - if instructions.next().transpose()? != Some(Instruction::Op(opcodes::all::OP_IF)) { - return Ok(None); + stutter: bool, + ) -> Result<(bool, Option)> { + if !Self::accept(instructions, Op(opcodes::all::OP_IF))? { + let stutter = instructions.peek() == Some(&Ok(PushBytes((&[]).into()))); + return Ok((stutter, None)); } - if instructions.next().transpose()? != Some(Instruction::PushBytes((&PROTOCOL_ID).into())) { - return Ok(None); + if !Self::accept(instructions, PushBytes((&PROTOCOL_ID).into()))? { + let stutter = instructions.peek() == Some(&Ok(PushBytes((&[]).into()))); + return Ok((stutter, None)); } let mut pushnum = false; @@ -182,87 +213,91 @@ impl RawEnvelope { loop { match instructions.next().transpose()? { - None => return Ok(None), - Some(Instruction::Op(opcodes::all::OP_ENDIF)) => { - return Ok(Some(Envelope { - input: input.try_into().unwrap(), - offset: offset.try_into().unwrap(), - payload, - pushnum, - })); + None => return Ok((false, None)), + Some(Op(opcodes::all::OP_ENDIF)) => { + return Ok(( + false, + Some(Envelope { + input: input.try_into().unwrap(), + offset: offset.try_into().unwrap(), + payload, + pushnum, + stutter, + }), + )); } - Some(Instruction::Op(opcodes::all::OP_PUSHNUM_NEG1)) => { + Some(Op(opcodes::all::OP_PUSHNUM_NEG1)) => { pushnum = true; payload.push(vec![0x81]); } - Some(Instruction::Op(opcodes::all::OP_PUSHNUM_1)) => { + Some(Op(opcodes::all::OP_PUSHNUM_1)) => { pushnum = true; payload.push(vec![1]); } - Some(Instruction::Op(opcodes::all::OP_PUSHNUM_2)) => { + Some(Op(opcodes::all::OP_PUSHNUM_2)) => { pushnum = true; payload.push(vec![2]); } - Some(Instruction::Op(opcodes::all::OP_PUSHNUM_3)) => { + Some(Op(opcodes::all::OP_PUSHNUM_3)) => { pushnum = true; payload.push(vec![3]); } - Some(Instruction::Op(opcodes::all::OP_PUSHNUM_4)) => { + Some(Op(opcodes::all::OP_PUSHNUM_4)) => { pushnum = true; payload.push(vec![4]); } - Some(Instruction::Op(opcodes::all::OP_PUSHNUM_5)) => { + Some(Op(opcodes::all::OP_PUSHNUM_5)) => { pushnum = true; payload.push(vec![5]); } - Some(Instruction::Op(opcodes::all::OP_PUSHNUM_6)) => { + Some(Op(opcodes::all::OP_PUSHNUM_6)) => { pushnum = true; payload.push(vec![6]); } - Some(Instruction::Op(opcodes::all::OP_PUSHNUM_7)) => { + Some(Op(opcodes::all::OP_PUSHNUM_7)) => { pushnum = true; payload.push(vec![7]); } - Some(Instruction::Op(opcodes::all::OP_PUSHNUM_8)) => { + Some(Op(opcodes::all::OP_PUSHNUM_8)) => { pushnum = true; payload.push(vec![8]); } - Some(Instruction::Op(opcodes::all::OP_PUSHNUM_9)) => { + Some(Op(opcodes::all::OP_PUSHNUM_9)) => { pushnum = true; payload.push(vec![9]); } - Some(Instruction::Op(opcodes::all::OP_PUSHNUM_10)) => { + Some(Op(opcodes::all::OP_PUSHNUM_10)) => { pushnum = true; payload.push(vec![10]); } - Some(Instruction::Op(opcodes::all::OP_PUSHNUM_11)) => { + Some(Op(opcodes::all::OP_PUSHNUM_11)) => { pushnum = true; payload.push(vec![11]); } - Some(Instruction::Op(opcodes::all::OP_PUSHNUM_12)) => { + Some(Op(opcodes::all::OP_PUSHNUM_12)) => { pushnum = true; payload.push(vec![12]); } - Some(Instruction::Op(opcodes::all::OP_PUSHNUM_13)) => { + Some(Op(opcodes::all::OP_PUSHNUM_13)) => { pushnum = true; payload.push(vec![13]); } - Some(Instruction::Op(opcodes::all::OP_PUSHNUM_14)) => { + Some(Op(opcodes::all::OP_PUSHNUM_14)) => { pushnum = true; payload.push(vec![14]); } - Some(Instruction::Op(opcodes::all::OP_PUSHNUM_15)) => { + Some(Op(opcodes::all::OP_PUSHNUM_15)) => { pushnum = true; payload.push(vec![15]); } - Some(Instruction::Op(opcodes::all::OP_PUSHNUM_16)) => { + Some(Op(opcodes::all::OP_PUSHNUM_16)) => { pushnum = true; payload.push(vec![16]); } - Some(Instruction::PushBytes(push)) => { + Some(PushBytes(push)) => { payload.push(push.as_bytes().to_vec()); } - Some(_) => return Ok(None), + Some(_) => return Ok((false, None)), } } } diff --git a/frameworks/bitcoin-move/src/natives/ord/inscription.rs b/frameworks/bitcoin-move/src/natives/ord/inscription.rs index 435e0b0671..d3cb1dcb68 100644 --- a/frameworks/bitcoin-move/src/natives/ord/inscription.rs +++ b/frameworks/bitcoin-move/src/natives/ord/inscription.rs @@ -2,8 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 // Code from https://github.com/ordinals/ord/ +use crate::natives::ord::envelope::Envelope; +use crate::natives::ord::tag::Tag; +use bitcoin::constants::MAX_SCRIPT_ELEMENT_SIZE; use bitcoin::{hashes::Hash, Txid, Witness}; use moveos_types::move_std::string::MoveString; +use rooch_types::bitcoin::ord::InscriptionID; use { super::envelope, super::inscription_id::InscriptionId, @@ -11,7 +15,7 @@ use { bitcoin::{ blockdata::{ opcodes, - script::{self, PushBytesBuf}, + script::{self}, }, ScriptBuf, }, @@ -29,9 +33,24 @@ pub struct Inscription { pub incomplete_field: bool, pub metadata: Option>, pub metaprotocol: Option>, - pub parent: Option>, + pub parents: Vec>, pub pointer: Option>, pub unrecognized_even_field: bool, + pub rune: Option>, +} + +impl From> + for rooch_types::bitcoin::ord::Envelope +{ + fn from(val: Envelope) -> Self { + Self { + input: val.input, + offset: val.offset, + pushnum: val.pushnum, + stutter: val.stutter, + payload: val.payload.into(), + } + } } impl From for rooch_types::bitcoin::ord::InscriptionRecord { @@ -48,7 +67,7 @@ impl From for rooch_types::bitcoin::ord::InscriptionRecord { .metaprotocol() .map(|s| MoveString::from(s.to_owned())) .into(); - let parent = val.parent().map(Into::into).into(); + let parents = val.parents().into_iter().map(InscriptionID::from).collect(); let pointer = val.pointer().into(); rooch_types::bitcoin::ord::InscriptionRecord { body: val.body.unwrap_or_default(), @@ -58,9 +77,10 @@ impl From for rooch_types::bitcoin::ord::InscriptionRecord { incomplete_field: val.incomplete_field, metadata: val.metadata.unwrap_or_default(), metaprotocol, - parent, + parents, pointer, unrecognized_even_field: val.unrecognized_even_field, + rune: None, } } } @@ -94,47 +114,17 @@ impl Inscription { .push_opcode(opcodes::all::OP_IF) .push_slice(envelope::PROTOCOL_ID); - if let Some(content_type) = self.content_type.clone() { - builder = builder - .push_slice(envelope::CONTENT_TYPE_TAG) - .push_slice(PushBytesBuf::try_from(content_type).unwrap()); - } - - if let Some(content_encoding) = self.content_encoding.clone() { - builder = builder - .push_slice(envelope::CONTENT_ENCODING_TAG) - .push_slice(PushBytesBuf::try_from(content_encoding).unwrap()); - } - - if let Some(protocol) = self.metaprotocol.clone() { - builder = builder - .push_slice(envelope::METAPROTOCOL_TAG) - .push_slice(PushBytesBuf::try_from(protocol).unwrap()); - } - - if let Some(parent) = self.parent.clone() { - builder = builder - .push_slice(envelope::PARENT_TAG) - .push_slice(PushBytesBuf::try_from(parent).unwrap()); - } - - if let Some(pointer) = self.pointer.clone() { - builder = builder - .push_slice(envelope::POINTER_TAG) - .push_slice(PushBytesBuf::try_from(pointer).unwrap()); - } - - if let Some(metadata) = &self.metadata { - for chunk in metadata.chunks(520) { - builder = builder.push_slice(envelope::METADATA_TAG); - builder = builder.push_slice(PushBytesBuf::try_from(chunk.to_vec()).unwrap()); - } - } + Tag::ContentType.append(&mut builder, &self.content_type); + Tag::ContentEncoding.append(&mut builder, &self.content_encoding); + Tag::Metaprotocol.append(&mut builder, &self.metaprotocol); + Tag::Parent.append_array(&mut builder, &self.parents); + Tag::Pointer.append(&mut builder, &self.pointer); + Tag::Metadata.append(&mut builder, &self.metadata); if let Some(body) = &self.body { builder = builder.push_slice(envelope::BODY_TAG); - for chunk in body.chunks(520) { - builder = builder.push_slice(PushBytesBuf::try_from(chunk.to_vec()).unwrap()); + for chunk in body.chunks(MAX_SCRIPT_ELEMENT_SIZE) { + builder = builder.push_slice::<&script::PushBytes>(chunk.try_into().unwrap()); } } @@ -204,8 +194,15 @@ impl Inscription { str::from_utf8(self.metaprotocol.as_ref()?).ok() } - pub(crate) fn parent(&self) -> Option { - let value = self.parent.as_ref()?; + pub(crate) fn parents(&self) -> Vec { + self.parents + .iter() + .filter_map(|parent| Self::inscription_id_field(Some(parent))) + .collect() + } + + fn inscription_id_field(field: Option<&[u8]>) -> Option { + let value = field.as_ref()?; if value.len() < Txid::LEN { return None; @@ -424,79 +421,81 @@ mod tests { #[test] fn inscription_with_no_parent_field_has_no_parent() { assert!(Inscription { - parent: None, + parents: vec![], ..Default::default() } - .parent() - .is_none()); + .parents() + .is_empty()); } #[test] - fn inscription_with_parent_field_shorter_than_txid_length_has_no_parent() { + fn inscription_with_parent_field_shorter_than_txid_length_has_no_parents() { assert!(Inscription { - parent: Some(vec![]), + parents: vec![], ..Default::default() } - .parent() - .is_none()); + .parents() + .is_empty()); } #[test] - fn inscription_with_parent_field_longer_than_txid_and_index_has_no_parent() { + fn inscription_with_parent_field_longer_than_txid_and_index_has_no_parents() { assert!(Inscription { - parent: Some(vec![1; 37]), + parents: vec![vec![1; 37]], ..Default::default() } - .parent() - .is_none()); + .parents() + .is_empty()); } #[test] - fn inscription_with_parent_field_index_with_trailing_zeroes_and_fixed_length_has_parent() { + fn inscription_with_parent_field_index_with_trailing_zeroes_and_fixed_length_has_parents() { let mut parent = vec![1; 36]; parent[35] = 0; - assert!(Inscription { - parent: Some(parent), - ..Default::default() - } - .parent() - .is_some()); + assert!( + !(Inscription { + parents: vec![parent], + ..Default::default() + } + .parents() + .is_empty()) + ); } #[test] - fn inscription_with_parent_field_index_with_trailing_zeroes_and_variable_length_has_no_parent() + fn inscription_with_parent_field_index_with_trailing_zeroes_and_variable_length_has_no_parents() { let mut parent = vec![1; 35]; parent[34] = 0; assert!(Inscription { - parent: Some(parent), + parents: vec![parent], ..Default::default() } - .parent() - .is_none()); + .parents() + .is_empty()); } #[test] fn inscription_parent_txid_is_deserialized_correctly() { assert_eq!( Inscription { - parent: Some(vec![ + parents: vec![vec![ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - ]), + ]], ..Default::default() } - .parent() - .unwrap() - .txid, - "1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100" - .parse() - .unwrap() + .parents(), + [ + "1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100i0" + .parse() + .unwrap() + ], ); } @@ -504,13 +503,15 @@ mod tests { fn inscription_parent_with_zero_byte_index_field_is_deserialized_correctly() { assert_eq!( Inscription { - parent: Some(vec![1; 32]), + parents: vec![vec![1; 32]], ..Default::default() } - .parent() - .unwrap() - .index, - 0 + .parents(), + [ + "0101010101010101010101010101010101010101010101010101010101010101i0" + .parse() + .unwrap() + ], ); } @@ -518,17 +519,19 @@ mod tests { fn inscription_parent_with_one_byte_index_field_is_deserialized_correctly() { assert_eq!( Inscription { - parent: Some(vec![ + parents: vec![vec![ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01 - ]), + ]], ..Default::default() } - .parent() - .unwrap() - .index, - 1 + .parents(), + [ + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffi1" + .parse() + .unwrap() + ], ); } @@ -536,17 +539,19 @@ mod tests { fn inscription_parent_with_two_byte_index_field_is_deserialized_correctly() { assert_eq!( Inscription { - parent: Some(vec![ + parents: vec![vec![ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x02 - ]), + ]], ..Default::default() } - .parent() - .unwrap() - .index, - 0x0201, + .parents(), + [ + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffi513" + .parse() + .unwrap() + ], ); } @@ -554,17 +559,19 @@ mod tests { fn inscription_parent_with_three_byte_index_field_is_deserialized_correctly() { assert_eq!( Inscription { - parent: Some(vec![ + parents: vec![vec![ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x02, 0x03 - ]), + ]], ..Default::default() } - .parent() - .unwrap() - .index, - 0x030201, + .parents(), + [ + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffi197121" + .parse() + .unwrap() + ], ); } @@ -572,17 +579,19 @@ mod tests { fn inscription_parent_with_four_byte_index_field_is_deserialized_correctly() { assert_eq!( Inscription { - parent: Some(vec![ + parents: vec![vec![ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x02, 0x03, 0x04, - ]), + ]], ..Default::default() } - .parent() - .unwrap() - .index, - 0x04030201, + .parents(), + [ + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffi67305985" + .parse() + .unwrap() + ], ); } diff --git a/frameworks/bitcoin-move/src/natives/ord/mod.rs b/frameworks/bitcoin-move/src/natives/ord/mod.rs index 7a3a9f7704..a0f0672165 100644 --- a/frameworks/bitcoin-move/src/natives/ord/mod.rs +++ b/frameworks/bitcoin-move/src/natives/ord/mod.rs @@ -8,6 +8,7 @@ pub mod inscription; #[allow(dead_code)] pub mod inscription_id; pub mod media; +pub mod tag; #[cfg(test)] #[allow(dead_code)] pub(crate) mod test; @@ -25,12 +26,13 @@ use move_vm_types::{ }; use moveos_stdlib::natives::helpers::{make_module_natives, make_native}; use moveos_types::state::{MoveState, MoveType}; +use rooch_types::bitcoin::ord::{Envelope, InscriptionRecord}; use rooch_types::bitcoin::types::Witness; use serde::{Deserialize, Serialize}; use smallvec::smallvec; use std::collections::VecDeque; use tracing::error; -use {envelope::ParsedEnvelope, envelope::RawEnvelope, inscription::Inscription}; +use {envelope::ParsedEnvelope, envelope::RawEnvelope}; #[derive(Clone, Debug, Serialize, PartialEq, Eq, Deserialize)] pub struct FromWitnessGasParameters { @@ -47,7 +49,7 @@ impl FromWitnessGasParameters { } } -/// Rust implementation of parse Inscription from witness +/// Rust implementation of parse Inscription from witness, to be removed after upgraded #[inline] pub(crate) fn native_from_witness( gas_params: &FromWitnessGasParameters, @@ -60,6 +62,56 @@ pub(crate) fn native_from_witness( let mut cost = gas_params.base; + let witness_ref = pop_arg!(args, StructRef); + let wintness_value = witness_ref.read_ref()?; + let witness = Witness::from_runtime_value(wintness_value).map_err(|e| { + PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR) + .with_message(format!("Failed to parse witness: {}", e)) + })?; + cost += gas_params.per_byte + * NumBytes::new( + witness + .witness + .iter() + .map(|inner_vec| inner_vec.len()) + .sum::() as u64, + ); + let inscription_vm_type = context + .load_type(&InscriptionRecord::type_tag()) + .map_err(|e| e.to_partial())?; + let val = Vector::pack(&inscription_vm_type, vec![])?; + + Ok(NativeResult::ok(cost, smallvec![val])) +} + +#[derive(Clone, Debug, Serialize, PartialEq, Eq, Deserialize)] +pub struct ParseInscriptionFromWitnessGasParameters { + pub base: InternalGas, + pub per_byte: InternalGasPerByte, +} + +impl ParseInscriptionFromWitnessGasParameters { + pub fn zeros() -> Self { + Self { + base: 0.into(), + per_byte: 0.into(), + } + } +} + +/// Rust implementation of parse Inscription from witness +#[inline] +pub(crate) fn native_parse_inscription_from_witness( + gas_params: &ParseInscriptionFromWitnessGasParameters, + context: &mut NativeContext, + ty_args: Vec, + mut args: VecDeque, +) -> PartialVMResult { + debug_assert_eq!(ty_args.len(), 0); + debug_assert_eq!(args.len(), 1); + + let mut cost = gas_params.base; + let witness_ref = pop_arg!(args, StructRef); let wintness_value = witness_ref.read_ref()?; let witness = Witness::from_runtime_value(wintness_value).map_err(|e| { @@ -77,15 +129,13 @@ pub(crate) fn native_from_witness( let bitcoin_witness = bitcoin::Witness::from_slice(witness.witness.as_slice()); let inscriptions = from_witness(&bitcoin_witness); let inscription_vm_type = context - .load_type(&rooch_types::bitcoin::ord::InscriptionRecord::type_tag()) + .load_type(&Envelope::::type_tag()) .map_err(|e| e.to_partial())?; let val = Vector::pack( &inscription_vm_type, inscriptions .into_iter() - .map(|i| { - Into::::into(i).to_runtime_value() - }) + .map(|i| Into::>::into(i).to_runtime_value()) .collect::>(), )?; @@ -95,33 +145,43 @@ pub(crate) fn native_from_witness( #[derive(Clone, Debug, Serialize, PartialEq, Eq, Deserialize)] pub struct GasParameters { pub from_witness: FromWitnessGasParameters, + pub parse_inscription_from_witness: ParseInscriptionFromWitnessGasParameters, } impl GasParameters { pub fn zeros() -> Self { Self { from_witness: FromWitnessGasParameters::zeros(), + parse_inscription_from_witness: ParseInscriptionFromWitnessGasParameters::zeros(), } } } pub fn make_all(gas_params: GasParameters) -> impl Iterator { - let natives = [( - "from_witness", - make_native(gas_params.from_witness, native_from_witness), - )]; + let natives = [ + ( + "from_witness", + make_native(gas_params.from_witness, native_from_witness), + ), + ( + "parse_inscription_from_witness", + make_native( + gas_params.parse_inscription_from_witness, + native_parse_inscription_from_witness, + ), + ), + ]; make_module_natives(natives) } -pub fn from_witness(witness: &bitcoin::Witness) -> Vec { +pub fn from_witness(witness: &bitcoin::Witness) -> Vec { witness .tapscript() .map(|script| match RawEnvelope::from_tapscript(script, 0usize) { Ok(envelopes) => envelopes .into_iter() .map(ParsedEnvelope::from) - .map(|e| e.payload) .collect::>(), Err(e) => { if tracing::enabled!(tracing::Level::TRACE) { @@ -136,7 +196,7 @@ pub fn from_witness(witness: &bitcoin::Witness) -> Vec { .unwrap_or_default() } -pub fn from_transaction(transaction: &bitcoin::Transaction) -> Vec { +pub fn from_transaction(transaction: &bitcoin::Transaction) -> Vec { transaction .input .iter() diff --git a/frameworks/bitcoin-move/src/natives/ord/tag.rs b/frameworks/bitcoin-move/src/natives/ord/tag.rs new file mode 100644 index 0000000000..9c4c89378d --- /dev/null +++ b/frameworks/bitcoin-move/src/natives/ord/tag.rs @@ -0,0 +1,111 @@ +// Copyright (c) RoochNetwork +// SPDX-License-Identifier: Apache-2.0 +// Code from https://github.com/ordinals/ord/ + +use bitcoin::constants::MAX_SCRIPT_ELEMENT_SIZE; +use bitcoin::script; +use std::collections::BTreeMap; +use std::mem; + +#[allow(dead_code)] +#[derive(Copy, Clone)] +#[repr(u8)] +pub(crate) enum Tag { + Pointer = 2, + #[allow(unused)] + Unbound = 66, + + ContentType = 1, + Parent = 3, + Metadata = 5, + Metaprotocol = 7, + ContentEncoding = 9, + Delegate = 11, + Rune = 13, + #[allow(unused)] + Note = 15, + #[allow(unused)] + Nop = 255, +} + +#[allow(dead_code)] +impl Tag { + fn chunked(self) -> bool { + matches!(self, Self::Metadata) + } + + pub(crate) fn bytes(self) -> [u8; 1] { + [self as u8] + } + + pub(crate) fn append(self, builder: &mut script::Builder, value: &Option>) { + if let Some(value) = value { + let mut tmp = script::Builder::new(); + mem::swap(&mut tmp, builder); + + if self.chunked() { + for chunk in value.chunks(MAX_SCRIPT_ELEMENT_SIZE) { + tmp = tmp + .push_slice::<&script::PushBytes>( + self.bytes().as_slice().try_into().unwrap(), + ) + .push_slice::<&script::PushBytes>(chunk.try_into().unwrap()); + } + } else { + tmp = tmp + .push_slice::<&script::PushBytes>(self.bytes().as_slice().try_into().unwrap()) + .push_slice::<&script::PushBytes>(value.as_slice().try_into().unwrap()); + } + + mem::swap(&mut tmp, builder); + } + } + + pub(crate) fn append_array(self, builder: &mut script::Builder, values: &Vec>) { + let mut tmp = script::Builder::new(); + mem::swap(&mut tmp, builder); + + for value in values { + tmp = tmp + .push_slice::<&script::PushBytes>(self.bytes().as_slice().try_into().unwrap()) + .push_slice::<&script::PushBytes>(value.as_slice().try_into().unwrap()); + } + + mem::swap(&mut tmp, builder); + } + + pub(crate) fn take(self, fields: &mut BTreeMap<&[u8], Vec<&[u8]>>) -> Option> { + if self.chunked() { + let value = fields.remove(self.bytes().as_slice())?; + + if value.is_empty() { + None + } else { + Some(value.into_iter().flatten().cloned().collect()) + } + } else { + let values = fields.get_mut(self.bytes().as_slice())?; + + if values.is_empty() { + None + } else { + let value = values.remove(0).to_vec(); + + if values.is_empty() { + fields.remove(self.bytes().as_slice()); + } + + Some(value) + } + } + } + + pub(crate) fn take_array(self, fields: &mut BTreeMap<&[u8], Vec<&[u8]>>) -> Vec> { + fields + .remove(self.bytes().as_slice()) + .unwrap_or_default() + .into_iter() + .map(|v| v.to_vec()) + .collect() + } +} diff --git a/frameworks/bitcoin-move/src/natives/ord/test.rs b/frameworks/bitcoin-move/src/natives/ord/test.rs index 22558fb739..01bbc607b3 100644 --- a/frameworks/bitcoin-move/src/natives/ord/test.rs +++ b/frameworks/bitcoin-move/src/natives/ord/test.rs @@ -113,13 +113,13 @@ pub(crate) fn tx_out(value: u64, address: Address) -> TxOut { } pub(crate) struct InscriptionTemplate { - pub(crate) parent: Option, + pub(crate) parents: Vec, } impl From for Inscription { fn from(template: InscriptionTemplate) -> Self { Self { - parent: template.parent.map(|id| id.parent_value()), + parents: template.parents.iter().map(|v| v.parent_value()).collect(), ..Default::default() } } diff --git a/frameworks/framework-release/released/2/stdlib b/frameworks/framework-release/released/2/stdlib new file mode 100644 index 0000000000..0526b1ab7e Binary files /dev/null and b/frameworks/framework-release/released/2/stdlib differ diff --git a/frameworks/rooch-nursery/sources/bitseed.move b/frameworks/rooch-nursery/sources/bitseed.move index 2ccd3f4eb0..8ecfc3e7dc 100644 --- a/frameworks/rooch-nursery/sources/bitseed.move +++ b/frameworks/rooch-nursery/sources/bitseed.move @@ -495,7 +495,7 @@ module rooch_nursery::bitseed { let tx = option::destroy_some(tx_option); let input = types::tx_input(&tx); - let index = ord::input(inscription); + let index = ord::index(inscription); let txin = vector::borrow(input, (index as u64)); let outpoint = types::txin_previous_output(txin); diff --git a/rust-toolchain b/rust-toolchain index 9c47a6d326..6cdeba3855 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -1.77.1 \ No newline at end of file +1.77.2 \ No newline at end of file diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 8ed2fd2b6b..8c10d70dae 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ #we keep the `rust-toolchain` and `rust-toolchain.yaml` #because some tools dose not support rust-toolchain.yaml yet [toolchain] -channel = "1.77.1" \ No newline at end of file +channel = "1.77.2" \ No newline at end of file