From aa4637db4e0f4fb7fd4c5a8ba9270a142007304d Mon Sep 17 00:00:00 2001 From: raphjaph Date: Tue, 15 Nov 2022 12:33:44 +0100 Subject: [PATCH 01/12] stash --- src/subcommand/wallet/inscribe.rs | 1 + tests/wallet.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index 332fbc38ff..5845b061b3 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -75,6 +75,7 @@ impl Inscribe { .push_opcode(opcodes::OP_FALSE) .push_opcode(opcodes::all::OP_IF) .push_slice(content) + .push_slice(mime_type) .push_opcode(opcodes::all::OP_ENDIF) .into_script(); diff --git a/tests/wallet.rs b/tests/wallet.rs index fa3ee6b6a5..b69aa952f6 100644 --- a/tests/wallet.rs +++ b/tests/wallet.rs @@ -165,7 +165,7 @@ fn inscribe() { let rpc_server = test_bitcoincore_rpc::spawn_with(Network::Regtest, "ord"); rpc_server.mine_blocks(1); - CommandBuilder::new("--chain regtest wallet inscribe 5000000000 HELLOWORLD") + CommandBuilder::new("--chain regtest wallet inscribe --ordinal 5000000000 --mime-type HELLOWORLD") .rpc_server(&rpc_server) .stdout_regex("commit\t[[:xdigit:]]{64}\nreveal\t[[:xdigit:]]{64}\n") .run(); From dad6594400d1ac76767f6b0a16374564ef1ccc03 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Tue, 15 Nov 2022 12:34:09 +0100 Subject: [PATCH 02/12] stash --- tests/wallet.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/wallet.rs b/tests/wallet.rs index b69aa952f6..6beea87aa9 100644 --- a/tests/wallet.rs +++ b/tests/wallet.rs @@ -165,7 +165,7 @@ fn inscribe() { let rpc_server = test_bitcoincore_rpc::spawn_with(Network::Regtest, "ord"); rpc_server.mine_blocks(1); - CommandBuilder::new("--chain regtest wallet inscribe --ordinal 5000000000 --mime-type HELLOWORLD") + CommandBuilder::new("--chain regtest wallet inscribe --ordinal 5000000000 --content HELLOWORLD --mime-type text/") .rpc_server(&rpc_server) .stdout_regex("commit\t[[:xdigit:]]{64}\nreveal\t[[:xdigit:]]{64}\n") .run(); From 1817f3101b0ad5893756c129a5347032a32ca887 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Tue, 15 Nov 2022 23:19:04 +0100 Subject: [PATCH 03/12] stash --- Cargo.lock | 145 +++++++++++---------- Cargo.toml | 2 +- src/index.rs | 5 +- src/index/updater.rs | 7 +- src/inscription.rs | 52 ++++++-- src/subcommand/server/templates/ordinal.rs | 6 +- src/subcommand/wallet/inscribe.rs | 29 ++++- templates/ordinal.html | 13 +- tests/wallet.rs | 29 ++++- 9 files changed, 189 insertions(+), 99 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6241423d4b..8186d04f92 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -117,7 +117,7 @@ dependencies = [ "num-traits", "rusticata-macros", "thiserror", - "time 0.3.16", + "time 0.3.17", ] [[package]] @@ -149,7 +149,7 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28" dependencies = [ - "concurrent-queue", + "concurrent-queue 1.2.4", "event-listener", "futures-core", ] @@ -166,15 +166,15 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" +checksum = "17adb73da160dfb475c183343c8cccd80721ea5a605d3eb57125f0a7b7a92d0b" dependencies = [ + "async-lock", "async-task", - "concurrent-queue", + "concurrent-queue 2.0.0", "fastrand", "futures-lite", - "once_cell", "slab", ] @@ -192,9 +192,9 @@ dependencies = [ [[package]] name = "async-global-executor" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0da5b41ee986eed3f524c380e6d64965aea573882a8907682ad100f7859305ca" +checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" dependencies = [ "async-channel", "async-executor", @@ -229,7 +229,7 @@ checksum = "e8121296a9f05be7f34aa4196b1747243b3b62e048bb7906f644f3fbfc490cf7" dependencies = [ "async-lock", "autocfg", - "concurrent-queue", + "concurrent-queue 1.2.4", "futures-lite", "libc", "log", @@ -396,9 +396,9 @@ dependencies = [ [[package]] name = "axum-server" -version = "0.4.2" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba6170b61f7b086609dabcae68d2e07352539c6ef04a7c82980bdfa01a159d" +checksum = "8456dab8f11484979a86651da8e619b355ede5d61a160755155f6c344bd18c47" dependencies = [ "arc-swap", "bytes", @@ -549,9 +549,9 @@ checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" [[package]] name = "cc" -version = "1.0.73" +version = "1.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f" [[package]] name = "cfg-if" @@ -567,9 +567,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.22" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" dependencies = [ "iana-time-zone", "js-sys", @@ -647,6 +647,15 @@ dependencies = [ "cache-padded", ] +[[package]] +name = "concurrent-queue" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd7bef69dc86e3c610e4e7aed41035e2a7ed12e72dd7530f61327a6579a4390b" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "console" version = "0.15.2" @@ -781,9 +790,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.80" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b7d4e43b25d3c994662706a1d4fcfc32aaa6afd287502c111b237093bb23f3a" +checksum = "97abf9f0eca9e52b7f81b945524e76710e6cb2366aead23b7d4fbf72e281f888" dependencies = [ "cc", "cxxbridge-flags", @@ -793,9 +802,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.80" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84f8829ddc213e2c1368e51a2564c552b65a8cb6a28f31e576270ac81d5e5827" +checksum = "7cc32cc5fea1d894b77d269ddb9f192110069a8a9c1f1d441195fba90553dea3" dependencies = [ "cc", "codespan-reporting", @@ -808,15 +817,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.80" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e72537424b474af1460806647c41d4b6d35d09ef7fe031c5c2fa5766047cc56a" +checksum = "8ca220e4794c934dc6b1207c3b42856ad4c302f2df1712e9f8d2eec5afaacf1f" [[package]] name = "cxxbridge-macro" -version = "1.0.80" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "309e4fb93eed90e1e14bea0da16b209f81813ba9fc7830c20ed151dd7bc0a4d7" +checksum = "b846f081361125bfc8dc9d3940c84e1fd83ba54bbca7b17cd29483c828be0704" dependencies = [ "proc-macro2", "quote", @@ -970,9 +979,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.9.1" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c90bf5f19754d10198ccb95b70664fc925bd1fc090a0fd9a6ebc54acc8cd6272" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" dependencies = [ "atty", "humantime", @@ -1379,9 +1388,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.20" +version = "0.14.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" +checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" dependencies = [ "bytes", "futures-channel", @@ -1477,12 +1486,13 @@ dependencies = [ [[package]] name = "indicatif" -version = "0.17.1" +version = "0.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfddc9561e8baf264e0e45e197fd7696320026eb10a8180340debc27b18f535b" +checksum = "4295cbb7573c16d310e99e713cf9e75101eb190ab31fccd35f2d2691b4352b19" dependencies = [ "console", "number_prefix", + "portable-atomic", "unicode-width", ] @@ -1503,9 +1513,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.5.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" +checksum = "f88c5561171189e69df9d98bcf18fd5f9558300f7ea7b801eb8a0fd748bd8745" [[package]] name = "itoa" @@ -1747,9 +1757,9 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" dependencies = [ "lazy_static", "libc", @@ -1838,23 +1848,14 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.13.1" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" dependencies = [ "hermit-abi", "libc", ] -[[package]] -name = "num_threads" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" -dependencies = [ - "libc", -] - [[package]] name = "number_prefix" version = "0.4.0" @@ -1881,9 +1882,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" +checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" [[package]] name = "opaque-debug" @@ -2009,9 +2010,9 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.3.1" +version = "6.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3baf96e39c5359d2eb0dd6ccb42c62b91d9678aa68160d261b9e0ccbf9e9dea9" +checksum = "7b5bf27447411e9ee3ff51186bf7a08e16c341efdde93f4d823e8844429bed7e" [[package]] name = "output_vt100" @@ -2137,11 +2138,17 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "portable-atomic" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15eb2c6e362923af47e13c23ca5afb859e83d54452c55b0b9ac763b8f7c1ac16" + [[package]] name = "ppv-lite86" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "pretty_assertions" @@ -2217,9 +2224,9 @@ dependencies = [ [[package]] name = "pyo3-build-config" -version = "0.17.2" +version = "0.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf0708c9ed01692635cbf056e286008e5a2927ab1a5e48cdd3aeb1ba5a6fef47" +checksum = "28fcd1e73f06ec85bf3280c48c67e731d8290ad3d730f8be9dc07946923005c8" dependencies = [ "once_cell", "target-lexicon", @@ -2313,15 +2320,15 @@ checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" dependencies = [ "pem", "ring", - "time 0.3.16", + "time 0.3.17", "yasna", ] [[package]] name = "redb" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5200f6a8af2c98fc1f7d7dff4558449d881cb761e60a8395d78930cbcdac7c09" +checksum = "a1ef150b3fa8f49af740c121f9c6a6abf92042501e61184c465651dcceea14fd" dependencies = [ "libc", "pyo3-build-config", @@ -2349,9 +2356,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" dependencies = [ "aho-corasick", "memchr", @@ -2360,9 +2367,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.27" +version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" [[package]] name = "remove_dir_all" @@ -2938,9 +2945,9 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02424087780c9b71cc96799eaeddff35af2bc513278cda5c99fc1f5d026d3c1" +checksum = "9410d0f6853b1d94f0e519fb95df60f29d2c1eff2d921ffdf01a4c8a3b54f12d" [[package]] name = "tempfile" @@ -3044,16 +3051,14 @@ dependencies = [ [[package]] name = "time" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fab5c8b9980850e06d92ddbe3ab839c062c801f3927c0fb8abd6fc8e918fbca" +checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" dependencies = [ "itoa", - "libc", - "num_threads", "serde", "time-core", - "time-macros 0.2.5", + "time-macros 0.2.6", ] [[package]] @@ -3074,9 +3079,9 @@ dependencies = [ [[package]] name = "time-macros" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bb801831d812c562ae7d2bfb531f26e66e4e1f6b17307ba4149c5064710e5b" +checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" dependencies = [ "time-core", ] @@ -3711,7 +3716,7 @@ dependencies = [ "oid-registry", "rusticata-macros", "thiserror", - "time 0.3.16", + "time 0.3.17", ] [[package]] @@ -3726,5 +3731,5 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "346d34a236c9d3e5f3b9b74563f238f955bbd05fa0b8b4efa53c130c43982f4c" dependencies = [ - "time 0.3.16", + "time 0.3.17", ] diff --git a/Cargo.toml b/Cargo.toml index caf1cc8e74..f8496da228 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ log = "0.4.14" mime = "0.3.16" mime_guess = "2.0.4" ord-bitcoincore-rpc = "0.16.3" -redb = "0.8.0" +redb = "0.9.0" regex = "1.6.0" reqwest = { version = "0.11.10", features = ["blocking"] } rust-embed = "6.4.0" diff --git a/src/index.rs b/src/index.rs index 347ca8ce76..ebbde90e0b 100644 --- a/src/index.rs +++ b/src/index.rs @@ -339,7 +339,10 @@ impl Index { .begin_read()? .open_table(ORDINAL_TO_INSCRIPTION)? .get(&ordinal.n())? - .map(|inscription| Inscription(inscription.to_owned())), + .map(|inscription| Inscription { + media_type: inscription.0.to_owned(), + content: inscription.1.to_owned(), + }), ) } diff --git a/src/index/updater.rs b/src/index/updater.rs index f90237506d..7c17ff7d56 100644 --- a/src/index/updater.rs +++ b/src/index/updater.rs @@ -291,7 +291,12 @@ impl Updater { outputs_traversed: &mut u64, ) -> Result { if let Some((ordinal, inscription)) = Inscription::from_transaction(tx, input_ordinal_ranges) { - ordinal_to_inscription.insert(&ordinal.n(), &inscription.0)?; + // let json = match inscription { + // Inscription::Text(content) => serde_json::json!({ "media_type": "text/plain;charset=utf-8", "content": content,}), + // Inscription::Png(content) => serde_json::json!({ "media_type": "image/png", "content": content,}), + // }; + let json = serde_json::to_string(&inscription).unwrap(); + ordinal_to_inscription.insert(&ordinal.n(), &json)?; } for (vout, output) in tx.output.iter().enumerate() { diff --git a/src/inscription.rs b/src/inscription.rs index 79b41aeac1..6dc5434e64 100644 --- a/src/inscription.rs +++ b/src/inscription.rs @@ -11,8 +11,11 @@ use { std::str::{self, Utf8Error}, }; -#[derive(Debug, PartialEq)] -pub(crate) struct Inscription(pub(crate) String); +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub(crate) enum Inscription { + Text(String), + Png(Vec), +} impl Inscription { pub(crate) fn from_transaction( @@ -99,19 +102,41 @@ impl<'a> InscriptionParser<'a> { fn parse_inscription(&mut self) -> Result> { if self.advance()? == Instruction::Op(opcodes::all::OP_IF) { - let content = self.advance()?; + let media_type = self.advance()?; - let content = if let Instruction::PushBytes(bytes) = content { + let media_type = if let Instruction::PushBytes(bytes) = media_type { str::from_utf8(bytes).map_err(InscriptionError::Utf8Decode)? } else { return Err(InscriptionError::InvalidInscription); }; + let inscription = match media_type { + "text/plain;charset=utf-8" => { + let content = if let Instruction::PushBytes(bytes) = self.advance()? { + str::from_utf8(bytes).map_err(InscriptionError::Utf8Decode)? + } else { + return Err(InscriptionError::InvalidInscription); + }; + + Some(Inscription::Text(content.to_string())) + } + "image/png" => { + let content = if let Instruction::PushBytes(bytes) = self.advance()? { + bytes.to_vec() + } else { + return Err(InscriptionError::InvalidInscription); + }; + + Some(Inscription::Png(content)) + } + _ => None, + }; + if self.advance()? != Instruction::Op(opcodes::all::OP_ENDIF) { return Err(InscriptionError::InvalidInscription); } - return Ok(Some(Inscription(content.to_string()))); + return Ok(inscription); } Ok(None) @@ -167,13 +192,14 @@ mod tests { let script = script::Builder::new() .push_opcode(opcodes::OP_FALSE) .push_opcode(opcodes::all::OP_IF) + .push_slice("text/plain;charset=utf-8".as_bytes()) .push_slice("ord".as_bytes()) .push_opcode(opcodes::all::OP_ENDIF) .into_script(); assert_eq!( InscriptionParser::parse(&Witness::from_vec(vec![script.into_bytes(), vec![]])), - Ok(Inscription("ord".into())) + Ok(Inscription::Text("ord".to_string())) ); } @@ -182,6 +208,7 @@ mod tests { let script = script::Builder::new() .push_opcode(opcodes::OP_FALSE) .push_opcode(opcodes::all::OP_IF) + .push_slice("text/plain;charset=utf-8".as_bytes()) .push_slice("ord".as_bytes()) .push_opcode(opcodes::all::OP_ENDIF) .push_opcode(opcodes::all::OP_CHECKSIG) @@ -189,7 +216,7 @@ mod tests { assert_eq!( InscriptionParser::parse(&Witness::from_vec(vec![script.into_bytes(), vec![]])), - Ok(Inscription("ord".into())) + Ok(Inscription::Text("ord".to_string())) ); } @@ -199,13 +226,14 @@ mod tests { .push_opcode(opcodes::all::OP_CHECKSIG) .push_opcode(opcodes::OP_FALSE) .push_opcode(opcodes::all::OP_IF) + .push_slice("text/plain;charset=utf-8".as_bytes()) .push_slice("ord".as_bytes()) .push_opcode(opcodes::all::OP_ENDIF) .into_script(); assert_eq!( InscriptionParser::parse(&Witness::from_vec(vec![script.into_bytes(), vec![]])), - Ok(Inscription("ord".into())) + Ok(Inscription::Text("ord".into())) ); } @@ -214,17 +242,19 @@ mod tests { let script = script::Builder::new() .push_opcode(opcodes::OP_FALSE) .push_opcode(opcodes::all::OP_IF) + .push_slice("text/plain;charset=utf-8".as_bytes()) .push_slice("foo".as_bytes()) .push_opcode(opcodes::all::OP_ENDIF) .push_opcode(opcodes::OP_FALSE) .push_opcode(opcodes::all::OP_IF) + .push_slice("text/plain;charset=utf-8".as_bytes()) .push_slice("bar".as_bytes()) .push_opcode(opcodes::all::OP_ENDIF) .into_script(); assert_eq!( InscriptionParser::parse(&Witness::from_vec(vec![script.into_bytes(), vec![]])), - Ok(Inscription("foo".into())) + Ok(Inscription::Text("ord".into())) ); } @@ -233,6 +263,7 @@ mod tests { let script = script::Builder::new() .push_opcode(opcodes::OP_FALSE) .push_opcode(opcodes::all::OP_IF) + .push_slice("text/plain;charset=utf-8".as_bytes()) .push_slice(&[0b10000000]) .push_opcode(opcodes::all::OP_ENDIF) .into_script(); @@ -292,6 +323,7 @@ mod tests { let script = script::Builder::new() .push_opcode(opcodes::OP_FALSE) .push_opcode(opcodes::all::OP_IF) + .push_slice("text/plain;charset=utf-8".as_bytes()) .push_slice("ord".as_bytes()) .push_opcode(opcodes::all::OP_ENDIF) .into_script(); @@ -313,7 +345,7 @@ mod tests { assert_eq!( Inscription::from_transaction(&tx, &ranges), - Some((Ordinal(1), Inscription("ord".into()))) + Some((Ordinal(1), Inscription::Text("ord".into()))) ); } diff --git a/src/subcommand/server/templates/ordinal.rs b/src/subcommand/server/templates/ordinal.rs index 63545eab97..49bf2f9b05 100644 --- a/src/subcommand/server/templates/ordinal.rs +++ b/src/subcommand/server/templates/ordinal.rs @@ -85,7 +85,7 @@ mod tests { OrdinalHtml { ordinal: Ordinal(0), blocktime: Blocktime::Confirmed(0), - inscription: Some(Inscription("HELLOWORLD".to_string())), + inscription: Some(Inscription::Text("HELLOWORLD".to_string())), } .to_string(), " @@ -117,9 +117,7 @@ mod tests { OrdinalHtml { ordinal: Ordinal(0), blocktime: Blocktime::Confirmed(0), - inscription: Some(Inscription( - "".to_string() - )), + inscription: Some(Inscription::Text("".to_string())), } .to_string(), " diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index 5845b061b3..ff6edfb27b 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -10,18 +10,33 @@ use { util::taproot::{LeafVersion, TapLeafHash, TaprootBuilder}, PackedLockTime, SchnorrSighashType, Witness, }, + mime::Mime, }; #[derive(Debug, Parser)] pub(crate) struct Inscribe { + #[clap(long, help = "Inscribe on ")] ordinal: Ordinal, - content: String, + #[clap(long, help = "Inscribe contents of this ")] + content: PathBuf, + #[clap(long, help = "Set the media type of the contents")] + media_type: Mime, } impl Inscribe { pub(crate) fn run(self, options: Options) -> Result { let client = options.bitcoin_rpc_client_mainnet_forbidden("ord wallet inscribe")?; + if !matches!(self.media_type.as_ref(), "text/plain;charset=utf-8") + & !matches!(self.media_type.as_ref(), "image/png") + { + return Err(anyhow!( + "inscribe only accepts text/plain;charset=utf-8 and image/png" + )); + } + + let content = fs::read(self.content).unwrap(); + let index = Index::open(&options)?; index.update()?; @@ -33,7 +48,8 @@ impl Inscribe { let (unsigned_commit_tx, reveal_tx) = Inscribe::create_inscription_transactions( self.ordinal, - self.content.as_bytes(), + self.media_type, + &content, options.chain.network(), utxos, commit_tx_change, @@ -59,6 +75,7 @@ impl Inscribe { fn create_inscription_transactions( ordinal: Ordinal, + media_type: Mime, content: &[u8], network: bitcoin::Network, utxos: Vec<(OutPoint, Vec<(u64, u64)>)>, @@ -74,8 +91,8 @@ impl Inscribe { .push_opcode(opcodes::all::OP_CHECKSIG) .push_opcode(opcodes::OP_FALSE) .push_opcode(opcodes::all::OP_IF) + .push_slice(media_type.as_ref().as_bytes()) .push_slice(content) - .push_slice(mime_type) .push_opcode(opcodes::all::OP_ENDIF) .into_script(); @@ -181,6 +198,7 @@ mod tests { #[test] fn reveal_transaction_pays_fee() { let utxos = vec![(outpoint(1), vec![(10_000, 15_000)])]; + let media_type = Mime::from_str("text/plain").unwrap(); let content = b"ord"; let ordinal = Ordinal(10_000); let commit_address = change(0); @@ -188,6 +206,7 @@ mod tests { let (commit_tx, reveal_tx) = Inscribe::create_inscription_transactions( ordinal, + media_type, content, bitcoin::Network::Signet, utxos, @@ -207,6 +226,7 @@ mod tests { #[test] fn reveal_transaction_value_insufficient_to_pay_fee() { let utxos = vec![(outpoint(1), vec![(10_000, 11_000)])]; + let media_type = Mime::from_str("text/plain").unwrap(); let content = [b'a'; 5000]; let ordinal = Ordinal(10_000); let commit_address = change(0); @@ -214,6 +234,7 @@ mod tests { assert!(Inscribe::create_inscription_transactions( ordinal, + media_type, &content, bitcoin::Network::Signet, utxos, @@ -228,6 +249,7 @@ mod tests { #[test] fn reveal_transaction_would_create_dust() { let utxos = vec![(outpoint(1), vec![(10_000, 10_600)])]; + let media_type = Mime::from_str("text/plain").unwrap(); let content = [b'a'; 1]; let ordinal = Ordinal(10_000); let commit_address = change(0); @@ -235,6 +257,7 @@ mod tests { let error = Inscribe::create_inscription_transactions( ordinal, + media_type, &content, bitcoin::Network::Signet, utxos, diff --git a/templates/ordinal.html b/templates/ordinal.html index dd8fc948ce..915d59ebbc 100644 --- a/templates/ordinal.html +++ b/templates/ordinal.html @@ -11,9 +11,16 @@

Ordinal {{ self.ordinal.n() }}

offset
{{ self.ordinal.third() }}
rarity
{{ self.ordinal.rarity() }}
time
{{ self.blocktime }}
-%% if let Some(Inscription(inscription)) = &self.inscription { -
inscription
{{inscription}}
-%% } + + %% match &self.inscription { + %% Some(Inscription::Text(content) => { +
inscription
{{content}}
+ %% } + %% } + + %% if let Some(Inscription::Text(content)) = &self.inscription { +
inscription
{{content}}
+ %% } %% if self.ordinal.n() > 0 { prev diff --git a/tests/wallet.rs b/tests/wallet.rs index 6beea87aa9..d2feb1e2c9 100644 --- a/tests/wallet.rs +++ b/tests/wallet.rs @@ -165,7 +165,8 @@ fn inscribe() { let rpc_server = test_bitcoincore_rpc::spawn_with(Network::Regtest, "ord"); rpc_server.mine_blocks(1); - CommandBuilder::new("--chain regtest wallet inscribe --ordinal 5000000000 --content HELLOWORLD --mime-type text/") + CommandBuilder::new("--chain regtest wallet inscribe --ordinal 5000000000 --content hello.txt --media-type text/plain;charset=utf-8") + .write("hello.txt", "HELLOWORLD") .rpc_server(&rpc_server) .stdout_regex("commit\t[[:xdigit:]]{64}\nreveal\t[[:xdigit:]]{64}\n") .run(); @@ -185,9 +186,25 @@ fn inscribe_forbidden_on_mainnet() { let rpc_server = test_bitcoincore_rpc::spawn_with(Network::Bitcoin, "ord"); rpc_server.mine_blocks(1); - CommandBuilder::new("wallet inscribe 5000000000 HELLOWORLD") - .rpc_server(&rpc_server) - .expected_exit_code(1) - .expected_stderr("error: `ord wallet inscribe` is unstable and not yet supported on mainnet.\n") - .run(); + CommandBuilder::new( + "wallet inscribe --ordinal 5000000000 --content hello.txt --media-type text/plain;charset=utf-8", + ) + .rpc_server(&rpc_server) + .expected_exit_code(1) + .expected_stderr("error: `ord wallet inscribe` is unstable and not yet supported on mainnet.\n") + .run(); +} + +#[test] +fn inscribe_invalid_mime_type() { + let rpc_server = test_bitcoincore_rpc::spawn_with(Network::Regtest, "ord"); + rpc_server.mine_blocks(1); + + CommandBuilder::new( + "--chain regtest wallet inscribe --ordinal 5000000000 --content pepe.jpg --media-type image/jpg", + ) + .rpc_server(&rpc_server) + .expected_exit_code(1) + .expected_stderr("error: inscribe only accepts text/plain;charset=utf-8 and image/png\n") + .run(); } From 9da6a812fc9c7013759e2920c9202271969f7387 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Wed, 16 Nov 2022 00:28:00 +0100 Subject: [PATCH 04/12] accepting text and pngs for inscription --- Cargo.lock | 1 + Cargo.toml | 1 + src/index.rs | 5 +---- src/inscription.rs | 2 +- templates/ordinal.html | 19 +++++++++---------- tests/wallet.rs | 23 +++++++++++++++++++++++ 6 files changed, 36 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8186d04f92..ba9a1931cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1944,6 +1944,7 @@ dependencies = [ "anyhow", "axum", "axum-server", + "base64", "bitcoin", "boilerplate", "chrono", diff --git a/Cargo.toml b/Cargo.toml index f8496da228..79c7e863d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ members = [".", "test-bitcoincore-rpc"] anyhow = { version = "1.0.56", features = ["backtrace"] } axum = "0.5.6" axum-server = "0.4.0" +base64 = "0.13.1" bitcoin = { version = "0.29.1", features = ["rand"] } boilerplate = { version = "0.2.1", features = ["axum"] } chrono = "0.4.19" diff --git a/src/index.rs b/src/index.rs index ebbde90e0b..0302db28ed 100644 --- a/src/index.rs +++ b/src/index.rs @@ -339,10 +339,7 @@ impl Index { .begin_read()? .open_table(ORDINAL_TO_INSCRIPTION)? .get(&ordinal.n())? - .map(|inscription| Inscription { - media_type: inscription.0.to_owned(), - content: inscription.1.to_owned(), - }), + .map(|inscription| serde_json::from_str(inscription).unwrap()) ) } diff --git a/src/inscription.rs b/src/inscription.rs index 6dc5434e64..f791b74927 100644 --- a/src/inscription.rs +++ b/src/inscription.rs @@ -254,7 +254,7 @@ mod tests { assert_eq!( InscriptionParser::parse(&Witness::from_vec(vec![script.into_bytes(), vec![]])), - Ok(Inscription::Text("ord".into())) + Ok(Inscription::Text("foo".into())) ); } diff --git a/templates/ordinal.html b/templates/ordinal.html index 915d59ebbc..43a0de0e4a 100644 --- a/templates/ordinal.html +++ b/templates/ordinal.html @@ -11,16 +11,15 @@

Ordinal {{ self.ordinal.n() }}

offset
{{ self.ordinal.third() }}
rarity
{{ self.ordinal.rarity() }}
time
{{ self.blocktime }}
- - %% match &self.inscription { - %% Some(Inscription::Text(content) => { -
inscription
{{content}}
- %% } - %% } - - %% if let Some(Inscription::Text(content)) = &self.inscription { -
inscription
{{content}}
- %% } +%% match &self.inscription { +%% Some(Inscription::Text(content)) => { +
inscription
{{ content }}
+%% }, +%% Some(Inscription::Png(content)) => { +
inscription
+%% }, +%% None => {} +%% }; %% if self.ordinal.n() > 0 { prev diff --git a/tests/wallet.rs b/tests/wallet.rs index d2feb1e2c9..307f5a9dc9 100644 --- a/tests/wallet.rs +++ b/tests/wallet.rs @@ -208,3 +208,26 @@ fn inscribe_invalid_mime_type() { .expected_stderr("error: inscribe only accepts text/plain;charset=utf-8 and image/png\n") .run(); } + +#[test] +fn inscribe_png() { + let rpc_server = test_bitcoincore_rpc::spawn_with(Network::Regtest, "ord"); + rpc_server.mine_blocks(1); + + let png = base64::decode("iVBORw0KGgoAAAANSUhEUgAAAEEAAABBCAYAAACO98lFAAACbElEQVR4nM2bW3LCQAwEY+5/5wSRWsos+5A0LUNXJQR7Lc108cNHjt87PwGO47j//m6ClX5u958Q0QVXk8kXlmBkFl1BNldKgpFdWIWSJy3BUBaTqDkkCYYaQIXYL0swiCAZqL2IBIMK5IXch0kwyGAr6D2oBIMO2FMxH5dgVAQ1quaWSDDowPS8M7fK7wJUcGrOCOv/+CTYH1WoBdTnV7TeDwlGu1BBtkj2OQ/nvk8JxvkGTbRQ9HyEvueLBKM/QOIt5j2XYdTvTYIxOkixK7i7rzDrNZRgzB4gmBWdXSdY9ZlKMFYPqvSF+/ckux5LCcZugEIr3l4r8OS3E64ElUGr8Agwtp+EhnfgtxDJ65ZgRAZ/kmjOkAQjuuBqMvnCEozMoivI5kpJMLILq1DypCUYymISNYckwVADqBD7ZQkGESQDtReRYFCBvJD7MAkGGWwFvQeVYNABeyrm4xKqv2NUzEclVAQcQe/BJNDBdpD7EAlkoAjUXlkCFSQLsV+SQAQgUHOkJaiLaZQ8KQnKwkqyucISsouuIpMvJCGz4BNEc7olRAd/mkhel4TIwCgV3wUa3txbCd5BGZqA9lqBJ/9SgmdAlr54/55k12MqYfegwqzw7DrBqs9QwuoBlV3R3X2FWa83CbODBN6C3nMZRv1eJIwOUESLRc9H6Hs+JfQ3SLKFss95OPd9SDhfoFGLqM+vaL3D/wgWgSxQGPP/k1ABKcCg550pkVAVuGouLqEqaKNiPiqhIuAIeg8mgQ62g9yHSCADRaD2yhKoIFmI/ZIEIgCBmiMtQV1Mo+RJSVAWVpLNFZaQXXQVmXx/8lkCgwQhAzMAAAAASUVORK5CYII=").unwrap(); + + CommandBuilder::new("--chain regtest wallet inscribe --ordinal 5000000000 --content degenerate.png --media-type image/png") + .write("degenerate.png", png) + .rpc_server(&rpc_server) + .stdout_regex("commit\t[[:xdigit:]]{64}\nreveal\t[[:xdigit:]]{64}\n") + .run(); + + rpc_server.mine_blocks(1); + + let ord_server = TestServer::spawn(&rpc_server); + + ord_server.assert_response_regex( + "/ordinal/5000000000", + ".*
inscription
Date: Wed, 16 Nov 2022 00:32:58 +0100 Subject: [PATCH 05/12] fmt --- src/index.rs | 2 +- src/subcommand/server/templates/ordinal.rs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/index.rs b/src/index.rs index 0302db28ed..4aeb6e22cb 100644 --- a/src/index.rs +++ b/src/index.rs @@ -339,7 +339,7 @@ impl Index { .begin_read()? .open_table(ORDINAL_TO_INSCRIPTION)? .get(&ordinal.n())? - .map(|inscription| serde_json::from_str(inscription).unwrap()) + .map(|inscription| serde_json::from_str(inscription).unwrap()), ) } diff --git a/src/subcommand/server/templates/ordinal.rs b/src/subcommand/server/templates/ordinal.rs index 49bf2f9b05..ae170ec1b7 100644 --- a/src/subcommand/server/templates/ordinal.rs +++ b/src/subcommand/server/templates/ordinal.rs @@ -117,7 +117,9 @@ mod tests { OrdinalHtml { ordinal: Ordinal(0), blocktime: Blocktime::Confirmed(0), - inscription: Some(Inscription::Text("".to_string())), + inscription: Some(Inscription::Text( + "".to_string() + )), } .to_string(), " From 8b0aa0af7ca8d3948b9c65f0d850327fa3e2ed1e Mon Sep 17 00:00:00 2001 From: raphjaph Date: Wed, 16 Nov 2022 00:34:29 +0100 Subject: [PATCH 06/12] keep lockfile --- Cargo.lock | 141 ++++++++++++++++++++++++++--------------------------- 1 file changed, 68 insertions(+), 73 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ba9a1931cd..5d827520b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -117,7 +117,7 @@ dependencies = [ "num-traits", "rusticata-macros", "thiserror", - "time 0.3.17", + "time 0.3.16", ] [[package]] @@ -149,7 +149,7 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28" dependencies = [ - "concurrent-queue 1.2.4", + "concurrent-queue", "event-listener", "futures-core", ] @@ -166,15 +166,15 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.5.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17adb73da160dfb475c183343c8cccd80721ea5a605d3eb57125f0a7b7a92d0b" +checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" dependencies = [ - "async-lock", "async-task", - "concurrent-queue 2.0.0", + "concurrent-queue", "fastrand", "futures-lite", + "once_cell", "slab", ] @@ -192,9 +192,9 @@ dependencies = [ [[package]] name = "async-global-executor" -version = "2.3.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" +checksum = "0da5b41ee986eed3f524c380e6d64965aea573882a8907682ad100f7859305ca" dependencies = [ "async-channel", "async-executor", @@ -229,7 +229,7 @@ checksum = "e8121296a9f05be7f34aa4196b1747243b3b62e048bb7906f644f3fbfc490cf7" dependencies = [ "async-lock", "autocfg", - "concurrent-queue 1.2.4", + "concurrent-queue", "futures-lite", "libc", "log", @@ -396,9 +396,9 @@ dependencies = [ [[package]] name = "axum-server" -version = "0.4.4" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8456dab8f11484979a86651da8e619b355ede5d61a160755155f6c344bd18c47" +checksum = "87ba6170b61f7b086609dabcae68d2e07352539c6ef04a7c82980bdfa01a159d" dependencies = [ "arc-swap", "bytes", @@ -549,9 +549,9 @@ checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" [[package]] name = "cc" -version = "1.0.76" +version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" [[package]] name = "cfg-if" @@ -567,9 +567,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.23" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" dependencies = [ "iana-time-zone", "js-sys", @@ -647,15 +647,6 @@ dependencies = [ "cache-padded", ] -[[package]] -name = "concurrent-queue" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd7bef69dc86e3c610e4e7aed41035e2a7ed12e72dd7530f61327a6579a4390b" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "console" version = "0.15.2" @@ -790,9 +781,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.81" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97abf9f0eca9e52b7f81b945524e76710e6cb2366aead23b7d4fbf72e281f888" +checksum = "6b7d4e43b25d3c994662706a1d4fcfc32aaa6afd287502c111b237093bb23f3a" dependencies = [ "cc", "cxxbridge-flags", @@ -802,9 +793,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.81" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cc32cc5fea1d894b77d269ddb9f192110069a8a9c1f1d441195fba90553dea3" +checksum = "84f8829ddc213e2c1368e51a2564c552b65a8cb6a28f31e576270ac81d5e5827" dependencies = [ "cc", "codespan-reporting", @@ -817,15 +808,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.81" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca220e4794c934dc6b1207c3b42856ad4c302f2df1712e9f8d2eec5afaacf1f" +checksum = "e72537424b474af1460806647c41d4b6d35d09ef7fe031c5c2fa5766047cc56a" [[package]] name = "cxxbridge-macro" -version = "1.0.81" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b846f081361125bfc8dc9d3940c84e1fd83ba54bbca7b17cd29483c828be0704" +checksum = "309e4fb93eed90e1e14bea0da16b209f81813ba9fc7830c20ed151dd7bc0a4d7" dependencies = [ "proc-macro2", "quote", @@ -979,9 +970,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.9.3" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +checksum = "c90bf5f19754d10198ccb95b70664fc925bd1fc090a0fd9a6ebc54acc8cd6272" dependencies = [ "atty", "humantime", @@ -1388,9 +1379,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.23" +version = "0.14.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" +checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" dependencies = [ "bytes", "futures-channel", @@ -1486,13 +1477,12 @@ dependencies = [ [[package]] name = "indicatif" -version = "0.17.2" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4295cbb7573c16d310e99e713cf9e75101eb190ab31fccd35f2d2691b4352b19" +checksum = "bfddc9561e8baf264e0e45e197fd7696320026eb10a8180340debc27b18f535b" dependencies = [ "console", "number_prefix", - "portable-atomic", "unicode-width", ] @@ -1513,9 +1503,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.5.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f88c5561171189e69df9d98bcf18fd5f9558300f7ea7b801eb8a0fd748bd8745" +checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" [[package]] name = "itoa" @@ -1757,9 +1747,9 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.11" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" dependencies = [ "lazy_static", "libc", @@ -1848,14 +1838,23 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.14.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" dependencies = [ "hermit-abi", "libc", ] +[[package]] +name = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", +] + [[package]] name = "number_prefix" version = "0.4.0" @@ -1882,9 +1881,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.16.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" +checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" [[package]] name = "opaque-debug" @@ -2011,9 +2010,9 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.4.0" +version = "6.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5bf27447411e9ee3ff51186bf7a08e16c341efdde93f4d823e8844429bed7e" +checksum = "3baf96e39c5359d2eb0dd6ccb42c62b91d9678aa68160d261b9e0ccbf9e9dea9" [[package]] name = "output_vt100" @@ -2139,17 +2138,11 @@ dependencies = [ "universal-hash", ] -[[package]] -name = "portable-atomic" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15eb2c6e362923af47e13c23ca5afb859e83d54452c55b0b9ac763b8f7c1ac16" - [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" [[package]] name = "pretty_assertions" @@ -2225,9 +2218,9 @@ dependencies = [ [[package]] name = "pyo3-build-config" -version = "0.17.3" +version = "0.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28fcd1e73f06ec85bf3280c48c67e731d8290ad3d730f8be9dc07946923005c8" +checksum = "bf0708c9ed01692635cbf056e286008e5a2927ab1a5e48cdd3aeb1ba5a6fef47" dependencies = [ "once_cell", "target-lexicon", @@ -2321,7 +2314,7 @@ checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" dependencies = [ "pem", "ring", - "time 0.3.17", + "time 0.3.16", "yasna", ] @@ -2357,9 +2350,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" dependencies = [ "aho-corasick", "memchr", @@ -2368,9 +2361,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" [[package]] name = "remove_dir_all" @@ -2946,9 +2939,9 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.5" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9410d0f6853b1d94f0e519fb95df60f29d2c1eff2d921ffdf01a4c8a3b54f12d" +checksum = "c02424087780c9b71cc96799eaeddff35af2bc513278cda5c99fc1f5d026d3c1" [[package]] name = "tempfile" @@ -3052,14 +3045,16 @@ dependencies = [ [[package]] name = "time" -version = "0.3.17" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" +checksum = "0fab5c8b9980850e06d92ddbe3ab839c062c801f3927c0fb8abd6fc8e918fbca" dependencies = [ "itoa", + "libc", + "num_threads", "serde", "time-core", - "time-macros 0.2.6", + "time-macros 0.2.5", ] [[package]] @@ -3080,9 +3075,9 @@ dependencies = [ [[package]] name = "time-macros" -version = "0.2.6" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" +checksum = "65bb801831d812c562ae7d2bfb531f26e66e4e1f6b17307ba4149c5064710e5b" dependencies = [ "time-core", ] @@ -3717,7 +3712,7 @@ dependencies = [ "oid-registry", "rusticata-macros", "thiserror", - "time 0.3.17", + "time 0.3.16", ] [[package]] @@ -3732,5 +3727,5 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "346d34a236c9d3e5f3b9b74563f238f955bbd05fa0b8b4efa53c130c43982f4c" dependencies = [ - "time 0.3.17", + "time 0.3.16", ] From c66f488a2170706561159a30e96bfe2334f5406c Mon Sep 17 00:00:00 2001 From: raphjaph Date: Wed, 16 Nov 2022 19:24:28 +0100 Subject: [PATCH 07/12] small fixes --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- src/index.rs | 5 ++++- src/index/updater.rs | 7 ++----- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5d827520b8..fc10fec9c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2320,9 +2320,9 @@ dependencies = [ [[package]] name = "redb" -version = "0.9.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ef150b3fa8f49af740c121f9c6a6abf92042501e61184c465651dcceea14fd" +checksum = "5200f6a8af2c98fc1f7d7dff4558449d881cb761e60a8395d78930cbcdac7c09" dependencies = [ "libc", "pyo3-build-config", diff --git a/Cargo.toml b/Cargo.toml index 79c7e863d1..d0cc484531 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ log = "0.4.14" mime = "0.3.16" mime_guess = "2.0.4" ord-bitcoincore-rpc = "0.16.3" -redb = "0.9.0" +redb = "0.8.0" regex = "1.6.0" reqwest = { version = "0.11.10", features = ["blocking"] } rust-embed = "6.4.0" diff --git a/src/index.rs b/src/index.rs index 4aeb6e22cb..56aa818615 100644 --- a/src/index.rs +++ b/src/index.rs @@ -339,7 +339,10 @@ impl Index { .begin_read()? .open_table(ORDINAL_TO_INSCRIPTION)? .get(&ordinal.n())? - .map(|inscription| serde_json::from_str(inscription).unwrap()), + .map(|inscription| { + serde_json::from_str(inscription) + .expect("failed to deserialize inscription (JSON) from database") + }), ) } diff --git a/src/index/updater.rs b/src/index/updater.rs index 7c17ff7d56..2889e5a850 100644 --- a/src/index/updater.rs +++ b/src/index/updater.rs @@ -291,11 +291,8 @@ impl Updater { outputs_traversed: &mut u64, ) -> Result { if let Some((ordinal, inscription)) = Inscription::from_transaction(tx, input_ordinal_ranges) { - // let json = match inscription { - // Inscription::Text(content) => serde_json::json!({ "media_type": "text/plain;charset=utf-8", "content": content,}), - // Inscription::Png(content) => serde_json::json!({ "media_type": "image/png", "content": content,}), - // }; - let json = serde_json::to_string(&inscription).unwrap(); + let json = serde_json::to_string(&inscription) + .expect("Inscription serialization should always succeed"); ordinal_to_inscription.insert(&ordinal.n(), &json)?; } From 30cd184002f2e9b7086105bcc6db163c0b1eed9e Mon Sep 17 00:00:00 2001 From: raphjaph Date: Wed, 16 Nov 2022 23:13:47 +0100 Subject: [PATCH 08/12] check file size does not exceed 520 bytes --- src/inscription.rs | 70 +++++++++++++++------- src/subcommand/server/templates/ordinal.rs | 4 +- src/subcommand/wallet/inscribe.rs | 56 ++++++++--------- templates/ordinal.html | 12 ++-- tests/wallet.rs | 47 ++++++++------- 5 files changed, 109 insertions(+), 80 deletions(-) diff --git a/src/inscription.rs b/src/inscription.rs index f791b74927..5d2f1c1e2c 100644 --- a/src/inscription.rs +++ b/src/inscription.rs @@ -28,6 +28,20 @@ impl Inscription { Some((Ordinal(*start), inscription)) } + + pub(crate) fn media_type(&self) -> &[u8] { + match self { + Inscription::Text(_) => "text/plain;charset=utf-8".as_bytes(), + Inscription::Png(_) => "image/png".as_bytes(), + } + } + + pub(crate) fn content(&self) -> &[u8] { + match self { + Inscription::Text(text) => text.as_bytes(), + Inscription::Png(png) => png.as_ref(), + } + } } #[derive(Debug, PartialEq)] @@ -102,34 +116,28 @@ impl<'a> InscriptionParser<'a> { fn parse_inscription(&mut self) -> Result> { if self.advance()? == Instruction::Op(opcodes::all::OP_IF) { - let media_type = self.advance()?; - - let media_type = if let Instruction::PushBytes(bytes) = media_type { + let media_type = if let Instruction::PushBytes(bytes) = self.advance()? { str::from_utf8(bytes).map_err(InscriptionError::Utf8Decode)? } else { return Err(InscriptionError::InvalidInscription); }; + let content = if let Instruction::PushBytes(bytes) = self.advance()? { + bytes + } else { + return Err(InscriptionError::InvalidInscription); + }; + let inscription = match media_type { - "text/plain;charset=utf-8" => { - let content = if let Instruction::PushBytes(bytes) = self.advance()? { - str::from_utf8(bytes).map_err(InscriptionError::Utf8Decode)? - } else { - return Err(InscriptionError::InvalidInscription); - }; - - Some(Inscription::Text(content.to_string())) - } - "image/png" => { - let content = if let Instruction::PushBytes(bytes) = self.advance()? { - bytes.to_vec() - } else { - return Err(InscriptionError::InvalidInscription); - }; - - Some(Inscription::Png(content)) + "text/plain;charset=utf-8" => Some(Inscription::Text( + str::from_utf8(content) + .map_err(InscriptionError::Utf8Decode)? + .into(), + )), + "image/png" => Some(Inscription::Png(content.to_vec())), + _ => { + return Err(InscriptionError::InvalidInscription); } - _ => None, }; if self.advance()? != Instruction::Op(opcodes::all::OP_ENDIF) { @@ -199,7 +207,7 @@ mod tests { assert_eq!( InscriptionParser::parse(&Witness::from_vec(vec![script.into_bytes(), vec![]])), - Ok(Inscription::Text("ord".to_string())) + Ok(Inscription::Text("ord".into())) ); } @@ -216,7 +224,7 @@ mod tests { assert_eq!( InscriptionParser::parse(&Witness::from_vec(vec![script.into_bytes(), vec![]])), - Ok(Inscription::Text("ord".to_string())) + Ok(Inscription::Text("ord".into())) ); } @@ -409,4 +417,20 @@ mod tests { assert_eq!(Inscription::from_transaction(&tx, &ranges), None,); } + + #[test] + fn inscribe_png() { + let script = script::Builder::new() + .push_opcode(opcodes::OP_FALSE) + .push_opcode(opcodes::all::OP_IF) + .push_slice("image/png".as_bytes()) + .push_slice(&[1; 100]) + .push_opcode(opcodes::all::OP_ENDIF) + .into_script(); + + assert_eq!( + InscriptionParser::parse(&Witness::from_vec(vec![script.into_bytes(), vec![]])), + Ok(Inscription::Png(vec![1; 100])) + ); + } } diff --git a/src/subcommand/server/templates/ordinal.rs b/src/subcommand/server/templates/ordinal.rs index ae170ec1b7..ac099aad8a 100644 --- a/src/subcommand/server/templates/ordinal.rs +++ b/src/subcommand/server/templates/ordinal.rs @@ -85,7 +85,7 @@ mod tests { OrdinalHtml { ordinal: Ordinal(0), blocktime: Blocktime::Confirmed(0), - inscription: Some(Inscription::Text("HELLOWORLD".to_string())), + inscription: Some(Inscription::Text("HELLOWORLD".into())), } .to_string(), " @@ -118,7 +118,7 @@ mod tests { ordinal: Ordinal(0), blocktime: Blocktime::Confirmed(0), inscription: Some(Inscription::Text( - "".to_string() + "".into() )), } .to_string(), diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index ff6edfb27b..5d060ab2fd 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -10,32 +10,36 @@ use { util::taproot::{LeafVersion, TapLeafHash, TaprootBuilder}, PackedLockTime, SchnorrSighashType, Witness, }, - mime::Mime, }; #[derive(Debug, Parser)] pub(crate) struct Inscribe { #[clap(long, help = "Inscribe on ")] ordinal: Ordinal, - #[clap(long, help = "Inscribe contents of this ")] - content: PathBuf, - #[clap(long, help = "Set the media type of the contents")] - media_type: Mime, + #[clap(long, help = "Inscribe contents of ")] + file: PathBuf, } impl Inscribe { pub(crate) fn run(self, options: Options) -> Result { let client = options.bitcoin_rpc_client_mainnet_forbidden("ord wallet inscribe")?; - if !matches!(self.media_type.as_ref(), "text/plain;charset=utf-8") - & !matches!(self.media_type.as_ref(), "image/png") + let inscription = match &self + .file + .extension() + .expect("file should always have an extension") + .to_str() { - return Err(anyhow!( - "inscribe only accepts text/plain;charset=utf-8 and image/png" - )); - } + Some("txt") => Inscription::Text(String::from_utf8_lossy(&fs::read(&self.file).unwrap()).into()), + Some("png") => Inscription::Png(fs::read(&self.file).unwrap()), + _ => { + return Err(anyhow!("inscribe only accepts .txt and .png")); + } + }; - let content = fs::read(self.content).unwrap(); + if fs::read(&self.file).unwrap().len() > 520 { + return Err(anyhow!("file size exceeds 520 bytes")); + } let index = Index::open(&options)?; index.update()?; @@ -48,8 +52,7 @@ impl Inscribe { let (unsigned_commit_tx, reveal_tx) = Inscribe::create_inscription_transactions( self.ordinal, - self.media_type, - &content, + inscription, options.chain.network(), utxos, commit_tx_change, @@ -75,8 +78,7 @@ impl Inscribe { fn create_inscription_transactions( ordinal: Ordinal, - media_type: Mime, - content: &[u8], + inscription: Inscription, network: bitcoin::Network, utxos: Vec<(OutPoint, Vec<(u64, u64)>)>, change: Vec
, @@ -91,8 +93,8 @@ impl Inscribe { .push_opcode(opcodes::all::OP_CHECKSIG) .push_opcode(opcodes::OP_FALSE) .push_opcode(opcodes::all::OP_IF) - .push_slice(media_type.as_ref().as_bytes()) - .push_slice(content) + .push_slice(inscription.media_type()) + .push_slice(inscription.content()) .push_opcode(opcodes::all::OP_ENDIF) .into_script(); @@ -198,16 +200,14 @@ mod tests { #[test] fn reveal_transaction_pays_fee() { let utxos = vec![(outpoint(1), vec![(10_000, 15_000)])]; - let media_type = Mime::from_str("text/plain").unwrap(); - let content = b"ord"; + let inscription = Inscription::Text("ord".into()); let ordinal = Ordinal(10_000); let commit_address = change(0); let reveal_address = recipient(); let (commit_tx, reveal_tx) = Inscribe::create_inscription_transactions( ordinal, - media_type, - content, + inscription, bitcoin::Network::Signet, utxos, vec![commit_address, change(1)], @@ -226,16 +226,14 @@ mod tests { #[test] fn reveal_transaction_value_insufficient_to_pay_fee() { let utxos = vec![(outpoint(1), vec![(10_000, 11_000)])]; - let media_type = Mime::from_str("text/plain").unwrap(); - let content = [b'a'; 5000]; let ordinal = Ordinal(10_000); + let inscription = Inscription::Png([1; 10_000].to_vec()); let commit_address = change(0); let reveal_address = recipient(); assert!(Inscribe::create_inscription_transactions( ordinal, - media_type, - &content, + inscription, bitcoin::Network::Signet, utxos, vec![commit_address, change(1)], @@ -249,16 +247,14 @@ mod tests { #[test] fn reveal_transaction_would_create_dust() { let utxos = vec![(outpoint(1), vec![(10_000, 10_600)])]; - let media_type = Mime::from_str("text/plain").unwrap(); - let content = [b'a'; 1]; + let inscription = Inscription::Text("ord".into()); let ordinal = Ordinal(10_000); let commit_address = change(0); let reveal_address = recipient(); let error = Inscribe::create_inscription_transactions( ordinal, - media_type, - &content, + inscription, bitcoin::Network::Signet, utxos, vec![commit_address, change(1)], diff --git a/templates/ordinal.html b/templates/ordinal.html index 43a0de0e4a..33b3cce015 100644 --- a/templates/ordinal.html +++ b/templates/ordinal.html @@ -11,15 +11,17 @@

Ordinal {{ self.ordinal.n() }}

offset
{{ self.ordinal.third() }}
rarity
{{ self.ordinal.rarity() }}
time
{{ self.blocktime }}
-%% match &self.inscription { -%% Some(Inscription::Text(content)) => { +%% if let Some(inscription) = &self.inscription { +%% match inscription { +%% Inscription::Text(content) => {
inscription
{{ content }}
%% }, -%% Some(Inscription::Png(content)) => { +%% Inscription::Png(content) => {
inscription
%% }, -%% None => {} -%% }; +%% _ => {} +%% } +%% } %% if self.ordinal.n() > 0 { prev diff --git a/tests/wallet.rs b/tests/wallet.rs index 307f5a9dc9..d2946f42fd 100644 --- a/tests/wallet.rs +++ b/tests/wallet.rs @@ -165,7 +165,7 @@ fn inscribe() { let rpc_server = test_bitcoincore_rpc::spawn_with(Network::Regtest, "ord"); rpc_server.mine_blocks(1); - CommandBuilder::new("--chain regtest wallet inscribe --ordinal 5000000000 --content hello.txt --media-type text/plain;charset=utf-8") + CommandBuilder::new("--chain regtest wallet inscribe --ordinal 5000000000 --file hello.txt") .write("hello.txt", "HELLOWORLD") .rpc_server(&rpc_server) .stdout_regex("commit\t[[:xdigit:]]{64}\nreveal\t[[:xdigit:]]{64}\n") @@ -186,27 +186,23 @@ fn inscribe_forbidden_on_mainnet() { let rpc_server = test_bitcoincore_rpc::spawn_with(Network::Bitcoin, "ord"); rpc_server.mine_blocks(1); - CommandBuilder::new( - "wallet inscribe --ordinal 5000000000 --content hello.txt --media-type text/plain;charset=utf-8", - ) - .rpc_server(&rpc_server) - .expected_exit_code(1) - .expected_stderr("error: `ord wallet inscribe` is unstable and not yet supported on mainnet.\n") - .run(); + CommandBuilder::new("wallet inscribe --ordinal 5000000000 --file hello.txt") + .rpc_server(&rpc_server) + .expected_exit_code(1) + .expected_stderr("error: `ord wallet inscribe` is unstable and not yet supported on mainnet.\n") + .run(); } #[test] -fn inscribe_invalid_mime_type() { +fn inscribe_unknown_file_extension() { let rpc_server = test_bitcoincore_rpc::spawn_with(Network::Regtest, "ord"); rpc_server.mine_blocks(1); - CommandBuilder::new( - "--chain regtest wallet inscribe --ordinal 5000000000 --content pepe.jpg --media-type image/jpg", - ) - .rpc_server(&rpc_server) - .expected_exit_code(1) - .expected_stderr("error: inscribe only accepts text/plain;charset=utf-8 and image/png\n") - .run(); + CommandBuilder::new("--chain regtest wallet inscribe --ordinal 5000000000 --file pepe.jpg") + .rpc_server(&rpc_server) + .expected_exit_code(1) + .expected_stderr("error: inscribe only accepts .txt and .png\n") + .run(); } #[test] @@ -214,10 +210,8 @@ fn inscribe_png() { let rpc_server = test_bitcoincore_rpc::spawn_with(Network::Regtest, "ord"); rpc_server.mine_blocks(1); - let png = base64::decode("iVBORw0KGgoAAAANSUhEUgAAAEEAAABBCAYAAACO98lFAAACbElEQVR4nM2bW3LCQAwEY+5/5wSRWsos+5A0LUNXJQR7Lc108cNHjt87PwGO47j//m6ClX5u958Q0QVXk8kXlmBkFl1BNldKgpFdWIWSJy3BUBaTqDkkCYYaQIXYL0swiCAZqL2IBIMK5IXch0kwyGAr6D2oBIMO2FMxH5dgVAQ1quaWSDDowPS8M7fK7wJUcGrOCOv/+CTYH1WoBdTnV7TeDwlGu1BBtkj2OQ/nvk8JxvkGTbRQ9HyEvueLBKM/QOIt5j2XYdTvTYIxOkixK7i7rzDrNZRgzB4gmBWdXSdY9ZlKMFYPqvSF+/ckux5LCcZugEIr3l4r8OS3E64ElUGr8Agwtp+EhnfgtxDJ65ZgRAZ/kmjOkAQjuuBqMvnCEozMoivI5kpJMLILq1DypCUYymISNYckwVADqBD7ZQkGESQDtReRYFCBvJD7MAkGGWwFvQeVYNABeyrm4xKqv2NUzEclVAQcQe/BJNDBdpD7EAlkoAjUXlkCFSQLsV+SQAQgUHOkJaiLaZQ8KQnKwkqyucISsouuIpMvJCGz4BNEc7olRAd/mkhel4TIwCgV3wUa3txbCd5BGZqA9lqBJ/9SgmdAlr54/55k12MqYfegwqzw7DrBqs9QwuoBlV3R3X2FWa83CbODBN6C3nMZRv1eJIwOUESLRc9H6Hs+JfQ3SLKFss95OPd9SDhfoFGLqM+vaL3D/wgWgSxQGPP/k1ABKcCg550pkVAVuGouLqEqaKNiPiqhIuAIeg8mgQ62g9yHSCADRaD2yhKoIFmI/ZIEIgCBmiMtQV1Mo+RJSVAWVpLNFZaQXXQVmXx/8lkCgwQhAzMAAAAASUVORK5CYII=").unwrap(); - - CommandBuilder::new("--chain regtest wallet inscribe --ordinal 5000000000 --content degenerate.png --media-type image/png") - .write("degenerate.png", png) + CommandBuilder::new("--chain regtest wallet inscribe --ordinal 5000000000 --file degenerate.png") + .write("degenerate.png", &[1; 520]) .rpc_server(&rpc_server) .stdout_regex("commit\t[[:xdigit:]]{64}\nreveal\t[[:xdigit:]]{64}\n") .run(); @@ -231,3 +225,16 @@ fn inscribe_png() { ".*
inscription
Date: Thu, 17 Nov 2022 21:48:04 +0100 Subject: [PATCH 09/12] tweaks --- src/inscription.rs | 63 +++++++++++++++++++++++++++---- src/subcommand/wallet/inscribe.rs | 23 ++--------- tests/wallet.rs | 7 ++-- 3 files changed, 63 insertions(+), 30 deletions(-) diff --git a/src/inscription.rs b/src/inscription.rs index 5d2f1c1e2c..ec9ffe28b8 100644 --- a/src/inscription.rs +++ b/src/inscription.rs @@ -15,6 +15,7 @@ use { pub(crate) enum Inscription { Text(String), Png(Vec), + Unknown { media_type: (), content: () }, } impl Inscription { @@ -29,10 +30,32 @@ impl Inscription { Some((Ordinal(*start), inscription)) } - pub(crate) fn media_type(&self) -> &[u8] { + pub(crate) fn from_file(path: PathBuf) -> Result { + let file = fs::read(&path).with_context(|| format!("io error reading {}", path.display()))?; + + if file.len() > 520 { + bail!("file size exceeds 520 bytes"); + } + + match path + .extension() + .ok_or_else(|| anyhow!("file must have extension"))? + .to_str() + .ok_or_else(|| anyhow!("unrecognized extension"))? + { + "txt" => Ok(Inscription::Text(String::from_utf8(file)?)), + "png" => Ok(Inscription::Png(file)), + other => Err(anyhow!( + "unrecognized file extension `.{other}`, only .txt and .png accepted" + )), + } + } + + pub(crate) fn media_type(&self) -> &'static str { match self { - Inscription::Text(_) => "text/plain;charset=utf-8".as_bytes(), - Inscription::Png(_) => "image/png".as_bytes(), + Inscription::Text(_) => "text/plain;charset=utf-8", + Inscription::Png(_) => "image/png", + _ => "unknown", } } @@ -40,12 +63,13 @@ impl Inscription { match self { Inscription::Text(text) => text.as_bytes(), Inscription::Png(png) => png.as_ref(), + _ => &[], } } } #[derive(Debug, PartialEq)] -enum InscriptionError { +pub(crate) enum InscriptionError { EmptyWitness, KeyPathSpend, Script(script::Error), @@ -135,9 +159,10 @@ impl<'a> InscriptionParser<'a> { .into(), )), "image/png" => Some(Inscription::Png(content.to_vec())), - _ => { - return Err(InscriptionError::InvalidInscription); - } + _ => Some(Inscription::Unknown { + media_type: (), + content: (), + }), }; if self.advance()? != Instruction::Op(opcodes::all::OP_ENDIF) { @@ -322,7 +347,10 @@ mod tests { assert_eq!( InscriptionParser::parse(&Witness::from_vec(vec![script.into_bytes(), vec![]])), - Err(InscriptionError::InvalidInscription) + Ok(Inscription::Unknown { + media_type: (), + content: () + }) ); } @@ -433,4 +461,23 @@ mod tests { Ok(Inscription::Png(vec![1; 100])) ); } + + #[test] + fn inscribe_unknown_media_type() { + let script = script::Builder::new() + .push_opcode(opcodes::OP_FALSE) + .push_opcode(opcodes::all::OP_IF) + .push_slice("image/jpg".as_bytes()) + .push_slice(&[1; 100]) + .push_opcode(opcodes::all::OP_ENDIF) + .into_script(); + + assert_eq!( + InscriptionParser::parse(&Witness::from_vec(vec![script.into_bytes(), vec![]])), + Ok(Inscription::Unknown { + media_type: (), + content: () + }) + ); + } } diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index 5d060ab2fd..04d75e36e9 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -14,9 +14,9 @@ use { #[derive(Debug, Parser)] pub(crate) struct Inscribe { - #[clap(long, help = "Inscribe on ")] + #[clap(long, help = "Inscribe ")] ordinal: Ordinal, - #[clap(long, help = "Inscribe contents of ")] + #[clap(long, help = "Inscribe ordinal with contents of ")] file: PathBuf, } @@ -24,22 +24,7 @@ impl Inscribe { pub(crate) fn run(self, options: Options) -> Result { let client = options.bitcoin_rpc_client_mainnet_forbidden("ord wallet inscribe")?; - let inscription = match &self - .file - .extension() - .expect("file should always have an extension") - .to_str() - { - Some("txt") => Inscription::Text(String::from_utf8_lossy(&fs::read(&self.file).unwrap()).into()), - Some("png") => Inscription::Png(fs::read(&self.file).unwrap()), - _ => { - return Err(anyhow!("inscribe only accepts .txt and .png")); - } - }; - - if fs::read(&self.file).unwrap().len() > 520 { - return Err(anyhow!("file size exceeds 520 bytes")); - } + let inscription = Inscription::from_file(self.file)?; let index = Index::open(&options)?; index.update()?; @@ -93,7 +78,7 @@ impl Inscribe { .push_opcode(opcodes::all::OP_CHECKSIG) .push_opcode(opcodes::OP_FALSE) .push_opcode(opcodes::all::OP_IF) - .push_slice(inscription.media_type()) + .push_slice(inscription.media_type().as_bytes()) .push_slice(inscription.content()) .push_opcode(opcodes::all::OP_ENDIF) .into_script(); diff --git a/tests/wallet.rs b/tests/wallet.rs index d2946f42fd..4029855871 100644 --- a/tests/wallet.rs +++ b/tests/wallet.rs @@ -199,9 +199,10 @@ fn inscribe_unknown_file_extension() { rpc_server.mine_blocks(1); CommandBuilder::new("--chain regtest wallet inscribe --ordinal 5000000000 --file pepe.jpg") + .write("pepe.jpg", [1; 520]) .rpc_server(&rpc_server) .expected_exit_code(1) - .expected_stderr("error: inscribe only accepts .txt and .png\n") + .expected_stderr("error: unrecognized file extension `.jpg`, only .txt and .png accepted\n") .run(); } @@ -211,7 +212,7 @@ fn inscribe_png() { rpc_server.mine_blocks(1); CommandBuilder::new("--chain regtest wallet inscribe --ordinal 5000000000 --file degenerate.png") - .write("degenerate.png", &[1; 520]) + .write("degenerate.png", [1; 520]) .rpc_server(&rpc_server) .stdout_regex("commit\t[[:xdigit:]]{64}\nreveal\t[[:xdigit:]]{64}\n") .run(); @@ -232,7 +233,7 @@ fn inscribe_exceeds_push_byte_limit() { rpc_server.mine_blocks(1); CommandBuilder::new("--chain regtest wallet inscribe --ordinal 5000000000 --file degenerate.png") - .write("degenerate.png", &[1; 521]) + .write("degenerate.png", [1; 521]) .rpc_server(&rpc_server) .expected_exit_code(1) .expected_stderr("error: file size exceeds 520 bytes\n") From e7763c1773ab67881c1fec3ba2db7024622ba107 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Thu, 17 Nov 2022 22:26:11 +0100 Subject: [PATCH 10/12] tweaks --- src/inscription.rs | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/inscription.rs b/src/inscription.rs index ec9ffe28b8..72b9685257 100644 --- a/src/inscription.rs +++ b/src/inscription.rs @@ -15,7 +15,10 @@ use { pub(crate) enum Inscription { Text(String), Png(Vec), - Unknown { media_type: (), content: () }, + Unknown { + media_type: String, + content: Vec, + }, } impl Inscription { @@ -51,11 +54,14 @@ impl Inscription { } } - pub(crate) fn media_type(&self) -> &'static str { + pub(crate) fn media_type(&self) -> &str { match self { Inscription::Text(_) => "text/plain;charset=utf-8", Inscription::Png(_) => "image/png", - _ => "unknown", + Inscription::Unknown { + media_type, + content: _, + } => media_type, } } @@ -63,7 +69,10 @@ impl Inscription { match self { Inscription::Text(text) => text.as_bytes(), Inscription::Png(png) => png.as_ref(), - _ => &[], + Inscription::Unknown { + media_type: _, + content, + } => content.as_ref(), } } } @@ -159,9 +168,9 @@ impl<'a> InscriptionParser<'a> { .into(), )), "image/png" => Some(Inscription::Png(content.to_vec())), - _ => Some(Inscription::Unknown { - media_type: (), - content: (), + media_type => Some(Inscription::Unknown { + media_type: media_type.into(), + content: content.into(), }), }; @@ -342,15 +351,13 @@ mod tests { .push_opcode(opcodes::all::OP_IF) .push_slice("ord".as_bytes()) .push_slice("ord".as_bytes()) + .push_slice("ord".as_bytes()) .push_opcode(opcodes::all::OP_ENDIF) .into_script(); assert_eq!( InscriptionParser::parse(&Witness::from_vec(vec![script.into_bytes(), vec![]])), - Ok(Inscription::Unknown { - media_type: (), - content: () - }) + Err(InscriptionError::InvalidInscription), ); } @@ -475,8 +482,8 @@ mod tests { assert_eq!( InscriptionParser::parse(&Witness::from_vec(vec![script.into_bytes(), vec![]])), Ok(Inscription::Unknown { - media_type: (), - content: () + media_type: "image/jpg".into(), + content: vec![1; 100], }) ); } From 039a3db1a7b8b41990b2e6e099908b4a7fecfdc1 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Thu, 17 Nov 2022 22:34:52 +0100 Subject: [PATCH 11/12] remove Inscription::Unknown --- src/inscription.rs | 36 +----------------------------------- templates/ordinal.html | 1 - 2 files changed, 1 insertion(+), 36 deletions(-) diff --git a/src/inscription.rs b/src/inscription.rs index 72b9685257..07da0b6249 100644 --- a/src/inscription.rs +++ b/src/inscription.rs @@ -15,10 +15,6 @@ use { pub(crate) enum Inscription { Text(String), Png(Vec), - Unknown { - media_type: String, - content: Vec, - }, } impl Inscription { @@ -58,10 +54,6 @@ impl Inscription { match self { Inscription::Text(_) => "text/plain;charset=utf-8", Inscription::Png(_) => "image/png", - Inscription::Unknown { - media_type, - content: _, - } => media_type, } } @@ -69,10 +61,6 @@ impl Inscription { match self { Inscription::Text(text) => text.as_bytes(), Inscription::Png(png) => png.as_ref(), - Inscription::Unknown { - media_type: _, - content, - } => content.as_ref(), } } } @@ -168,10 +156,7 @@ impl<'a> InscriptionParser<'a> { .into(), )), "image/png" => Some(Inscription::Png(content.to_vec())), - media_type => Some(Inscription::Unknown { - media_type: media_type.into(), - content: content.into(), - }), + _ => None, }; if self.advance()? != Instruction::Op(opcodes::all::OP_ENDIF) { @@ -468,23 +453,4 @@ mod tests { Ok(Inscription::Png(vec![1; 100])) ); } - - #[test] - fn inscribe_unknown_media_type() { - let script = script::Builder::new() - .push_opcode(opcodes::OP_FALSE) - .push_opcode(opcodes::all::OP_IF) - .push_slice("image/jpg".as_bytes()) - .push_slice(&[1; 100]) - .push_opcode(opcodes::all::OP_ENDIF) - .into_script(); - - assert_eq!( - InscriptionParser::parse(&Witness::from_vec(vec![script.into_bytes(), vec![]])), - Ok(Inscription::Unknown { - media_type: "image/jpg".into(), - content: vec![1; 100], - }) - ); - } } diff --git a/templates/ordinal.html b/templates/ordinal.html index 1949d52895..a2841cad51 100644 --- a/templates/ordinal.html +++ b/templates/ordinal.html @@ -19,7 +19,6 @@

Ordinal {{ self.ordinal.n() }}

%% Inscription::Png(content) => {
inscription
%% }, -%% _ => {} %% } %% } From 2d1eabc9973df3d3f2d83bd0f22c4fa69828eb01 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Thu, 17 Nov 2022 22:37:49 +0100 Subject: [PATCH 12/12] remove scoping of InscriptionError --- src/inscription.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/inscription.rs b/src/inscription.rs index 07da0b6249..2b82fbe37d 100644 --- a/src/inscription.rs +++ b/src/inscription.rs @@ -66,7 +66,7 @@ impl Inscription { } #[derive(Debug, PartialEq)] -pub(crate) enum InscriptionError { +enum InscriptionError { EmptyWitness, KeyPathSpend, Script(script::Error),