diff --git a/CHANGELOG.md b/CHANGELOG.md index 32f7cfb5f8..2d5543c023 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,55 @@ Changelog ========= +[0.21.3](https://github.com/ordinals/ord/releases/tag/0.21.3) - 2024-11-11 +-------------------------------------------------------------------------- + +### Added +- Get output information by address ([#4056](https://github.com/ordinals/ord/pull/4056) by [raphjaph](https://github.com/raphjaph)) +- Allow including metadata when burning inscriptions ([#4045](https://github.com/ordinals/ord/pull/4045) by [casey](https://github.com/casey)) +- BIP322 sign file ([#4026](https://github.com/ordinals/ord/pull/4026) by [raphjaph](https://github.com/raphjaph)) +- Add `ord wallet split` command for splitting utxos ([#4030](https://github.com/ordinals/ord/pull/4030) by [casey](https://github.com/casey)) +- Allow fallback for satpoints and addresses ([#4033](https://github.com/ordinals/ord/pull/4033) by [casey](https://github.com/casey)) +- Add palindrome charm ([#4064](https://github.com/ordinals/ord/pull/4064) by [casey](https://github.com/casey)) +- Allow restoring wallet with custom timestamp ([#4065](https://github.com/ordinals/ord/pull/4065) by [raphjaph](https://github.com/raphjaph)) + +### Changed +- Do not chunk runestone data pushes ([#4036](https://github.com/ordinals/ord/pull/4036) by [casey](https://github.com/casey)) +- Rescan wallet on restore ([#4041](https://github.com/ordinals/ord/pull/4041) by [casey](https://github.com/casey)) + +### Misc +- Add assert_html function ([#4058](https://github.com/ordinals/ord/pull/4058) by [casey](https://github.com/casey)) +- Identify collapsible nodes with class=collapse ([#4055](https://github.com/ordinals/ord/pull/4055) by [casey](https://github.com/casey)) +- Collapse long strings in HTML ([#4053](https://github.com/ordinals/ord/pull/4053) by [casey](https://github.com/casey)) +- Add simple taproot HD wallet to mockcore ([#4038](https://github.com/ordinals/ord/pull/4038) by [raphjaph](https://github.com/raphjaph)) +- Hide image preview and thumbnail scrollbars ([#4042](https://github.com/ordinals/ord/pull/4042) by [casey](https://github.com/casey)) +- Un-pin redb dependency and update to 2.2.0 ([#4032](https://github.com/ordinals/ord/pull/4032) by [casey](https://github.com/casey)) + +[0.21.2](https://github.com/ordinals/ord/releases/tag/0.21.2) - 2024-10-26 +-------------------------------------------------------------------------- + +### Fixed +- Create change output when inputs containing non-outgoing runes are selected ([#4028](https://github.com/ordinals/ord/pull/4028) by [casey](https://github.com/casey)) + +### Added +- Show total child count ([#4009](https://github.com/ordinals/ord/pull/4009) by [arik-so](https://github.com/arik-so)) +- Add `/r/undelegated-content/` ([#3932](https://github.com/ordinals/ord/pull/3932) by [elocremarc](https://github.com/elocremarc)) +- Add BIP322 `wallet sign` ([#3988](https://github.com/ordinals/ord/pull/3988) by [raphjaph](https://github.com/raphjaph)) +- Add `wallet addresses` ([#4005](https://github.com/ordinals/ord/pull/4005) by [raphjaph](https://github.com/raphjaph)) +- Show if JSON API is enabled on /status ([#4014](https://github.com/ordinals/ord/pull/4014) by [casey](https://github.com/casey)) + +### Changed +- Only show rune mint progress during mint ([#4013](https://github.com/ordinals/ord/pull/4013) by [casey](https://github.com/casey)) +- Change mint progress to `mints / terms.cap` ([#4012](https://github.com/ordinals/ord/pull/4012) by [casey](https://github.com/casey)) + +### Misc +- Add more info to `wallet outputs` ([#4019](https://github.com/ordinals/ord/pull/4019) by [raphjaph](https://github.com/raphjaph)) +- Add authors to Handbook ([#4018](https://github.com/ordinals/ord/pull/4018) by [raphjaph](https://github.com/raphjaph)) +- Document POST method for /inscriptions ([#4017](https://github.com/ordinals/ord/pull/4017) by [cryptoni9n](https://github.com/cryptoni9n)) +- Update JSON-API & Recursive documentation ([#3984](https://github.com/ordinals/ord/pull/3984) by [cryptoni9n](https://github.com/cryptoni9n)) +- Remove pre-alpha warning from ord help ([#4011](https://github.com/ordinals/ord/pull/4011) by [cryptoni9n](https://github.com/cryptoni9n)) +- Update Bitcoin Core install script ([#4007](https://github.com/ordinals/ord/pull/4007) by [raphjaph](https://github.com/raphjaph)) + [0.21.1](https://github.com/ordinals/ord/releases/tag/0.21.1) - 2024-10-20 -------------------------------------------------------------------------- diff --git a/Cargo.lock b/Cargo.lock index d900997d55..fd11433ca0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -64,9 +64,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -79,43 +79,43 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.91" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8" +checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" dependencies = [ "backtrace", ] @@ -153,7 +153,7 @@ dependencies = [ "nom", "num-traits", "rusticata-macros", - "thiserror", + "thiserror 1.0.69", "time", ] @@ -166,7 +166,7 @@ dependencies = [ "proc-macro2", "quote", "syn 1.0.109", - "synstructure", + "synstructure 0.12.6", ] [[package]] @@ -273,7 +273,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -294,7 +294,7 @@ dependencies = [ "log", "rustls 0.22.4", "rustls-pki-types", - "thiserror", + "thiserror 1.0.69", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -317,7 +317,7 @@ dependencies = [ "rand", "ring 0.16.20", "strum", - "thiserror", + "thiserror 1.0.69", "utf-8", ] @@ -345,7 +345,7 @@ name = "audit-cache" version = "0.0.0" dependencies = [ "colored", - "reqwest 0.12.8", + "reqwest 0.12.9", ] [[package]] @@ -353,7 +353,7 @@ name = "audit-content-security-policy" version = "0.0.0" dependencies = [ "colored", - "reqwest 0.12.8", + "reqwest 0.12.9", ] [[package]] @@ -539,9 +539,9 @@ dependencies = [ [[package]] name = "bitcoin-io" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "340e09e8399c7bd8912f495af6aa58bea0c9214773417ffaa8f6460f93aaee56" +checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" [[package]] name = "bitcoin-units" @@ -628,7 +628,7 @@ dependencies = [ "async-channel", "async-task", "futures-io", - "futures-lite 2.3.0", + "futures-lite 2.5.0", "piper", ] @@ -643,7 +643,7 @@ dependencies = [ "new_mime_guess", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -703,9 +703,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.1.31" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" +checksum = "1aeb932158bd710538c73702db6945cb68a8fb08c519e6e12706b94263b36db8" dependencies = [ "shlex", ] @@ -772,9 +772,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.20" +version = "4.5.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" +checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" dependencies = [ "clap_builder", "clap_derive", @@ -782,9 +782,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.20" +version = "4.5.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" +checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" dependencies = [ "anstream", "anstyle", @@ -801,20 +801,20 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "colored" @@ -844,7 +844,7 @@ dependencies = [ "encode_unicode", "lazy_static", "libc", - "unicode-width", + "unicode-width 0.1.14", "windows-sys 0.52.0", ] @@ -866,9 +866,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6" dependencies = [ "libc", ] @@ -990,7 +990,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -1001,7 +1001,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -1052,7 +1052,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -1062,7 +1062,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -1082,7 +1082,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", "unicode-xid", ] @@ -1140,7 +1140,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -1247,9 +1247,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" [[package]] name = "flate2" @@ -1356,9 +1356,9 @@ dependencies = [ [[package]] name = "futures-lite" -version = "2.3.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1" dependencies = [ "futures-core", "pin-project-lite", @@ -1372,7 +1372,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -1473,7 +1473,7 @@ checksum = "9902a044653b26b99f7e3693a42f171312d9be8b26b5697bd1e43ad1f8a35e10" dependencies = [ "gloo-utils", "js-sys", - "thiserror", + "thiserror 1.0.69", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -1546,9 +1546,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" [[package]] name = "heck" @@ -1741,7 +1741,7 @@ dependencies = [ "http 1.1.0", "hyper 1.5.0", "hyper-util", - "rustls 0.23.15", + "rustls 0.23.16", "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", @@ -1779,9 +1779,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" dependencies = [ "bytes", "futures-channel", @@ -1819,6 +1819,124 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec 1.13.2", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -1827,12 +1945,23 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.5.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "idna_adapter", + "smallvec 1.13.2", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", ] [[package]] @@ -1853,21 +1982,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.15.0", + "hashbrown 0.15.1", "serde", ] [[package]] name = "indicatif" -version = "0.17.8" +version = "0.17.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" +checksum = "cbf675b85ed934d3c67b5c5469701eec7db22689d0a2139d856e0925fa28b281" dependencies = [ "console", - "instant", "number_prefix", "portable-atomic", - "unicode-width", + "unicode-width 0.2.0", + "web-time", ] [[package]] @@ -2027,9 +2156,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.161" +version = "0.2.162" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" [[package]] name = "libredox" @@ -2053,6 +2182,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "litemap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" + [[package]] name = "lock_api" version = "0.4.12" @@ -2162,7 +2297,7 @@ dependencies = [ "jsonrpc-derive", "jsonrpc-http-server", "ord-bitcoincore-rpc", - "reqwest 0.12.8", + "reqwest 0.12.9", "serde", "serde_json", "tempfile", @@ -2179,7 +2314,7 @@ dependencies = [ "num-rational", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -2368,7 +2503,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -2397,7 +2532,7 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "ord" -version = "0.21.1" +version = "0.21.3" dependencies = [ "anyhow", "async-trait", @@ -2487,7 +2622,7 @@ dependencies = [ [[package]] name = "ordinals" -version = "0.0.11" +version = "0.0.12" dependencies = [ "bitcoin", "derive_more", @@ -2495,7 +2630,7 @@ dependencies = [ "serde", "serde_json", "serde_with", - "thiserror", + "thiserror 2.0.3", ] [[package]] @@ -2546,29 +2681,29 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf123a161dde1e524adf36f90bc5d8d3462824a9c43553ad07a8183161189ec" +checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4502d8515ca9f32f1fb543d987f63d95a14934883db45bdb48060b6b69257f8" +checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pin-utils" @@ -2583,7 +2718,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" dependencies = [ "atomic-waker", - "fastrand 2.1.1", + "fastrand 2.2.0", "futures-io", ] @@ -2769,9 +2904,9 @@ dependencies = [ [[package]] name = "redb" -version = "2.1.3" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4760ad04a88ef77075ba86ba9ea79b919e6bab29c1764c5747237cd6eaedcaa" +checksum = "84b1de48a7cf7ba193e81e078d17ee2b786236eed1d3f7c60f8a09545efc4925" dependencies = [ "libc", ] @@ -2793,7 +2928,7 @@ checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom", "libredox", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -2813,14 +2948,14 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] name = "regex" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", @@ -2830,9 +2965,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -2889,9 +3024,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.8" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b" +checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" dependencies = [ "base64 0.22.1", "bytes", @@ -2993,7 +3128,7 @@ dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn 2.0.85", + "syn 2.0.87", "walkdir", ] @@ -3038,9 +3173,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.37" +version = "0.38.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" dependencies = [ "bitflags 2.6.0", "errno", @@ -3077,9 +3212,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.15" +version = "0.23.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fbb44d7acc4e873d613422379f69f237a1b141928c02f6bc6ccfddddc2d7993" +checksum = "eee87ff5d9b36712a58574e12e9f0ea80f915a5b0ac518d322b24a465617925e" dependencies = [ "once_cell", "rustls-pki-types", @@ -3110,7 +3245,7 @@ dependencies = [ "ring 0.16.20", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-util 0.7.12", "webpki-roots", @@ -3244,9 +3379,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" dependencies = [ "core-foundation-sys", "libc", @@ -3254,9 +3389,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.213" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ea7893ff5e2466df8d720bb615088341b295f849602c6956047f8f80f0e9bc1" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] @@ -3274,13 +3409,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.213" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e85ad2009c50b58e87caa8cd6dac16bdf511bbfb7af6c33df902396aa480fa5" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -3345,7 +3480,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -3430,7 +3565,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -3465,6 +3600,12 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "strsim" version = "0.11.1" @@ -3512,9 +3653,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.85" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -3548,6 +3689,17 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "sysinfo" version = "0.32.0" @@ -3606,35 +3758,55 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" dependencies = [ "cfg-if 1.0.0", - "fastrand 2.1.1", + "fastrand 2.2.0", "once_cell", - "rustix 0.38.37", + "rustix 0.38.40", "windows-sys 0.59.0", ] [[package]] name = "thiserror" -version = "1.0.65" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" +checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.3", ] [[package]] name = "thiserror-impl" -version = "1.0.65" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", ] [[package]] @@ -3668,6 +3840,16 @@ dependencies = [ "time-core", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinytemplate" version = "1.2.1" @@ -3695,9 +3877,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.41.0" +version = "1.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb" +checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" dependencies = [ "backtrace", "bytes", @@ -3717,7 +3899,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -3746,7 +3928,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.23.15", + "rustls 0.23.16", "rustls-pki-types", "tokio", ] @@ -3888,12 +4070,6 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" -[[package]] -name = "unicode-bidi" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" - [[package]] name = "unicode-ident" version = "1.0.13" @@ -3915,6 +4091,12 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -3954,9 +4136,9 @@ dependencies = [ [[package]] name = "url" -version = "2.5.2" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" dependencies = [ "form_urlencoded", "idna", @@ -3975,6 +4157,18 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -4046,7 +4240,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", "wasm-bindgen-shared", ] @@ -4080,7 +4274,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4101,6 +4295,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webpki-roots" version = "0.25.4" @@ -4177,7 +4381,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -4188,7 +4392,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -4388,6 +4592,18 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "x509-parser" version = "0.13.2" @@ -4402,7 +4618,7 @@ dependencies = [ "nom", "oid-registry", "rusticata-macros", - "thiserror", + "thiserror 1.0.69", "time", ] @@ -4421,6 +4637,30 @@ dependencies = [ "time", ] +[[package]] +name = "yoke" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", + "synstructure 0.13.1", +] + [[package]] name = "zerocopy" version = "0.7.35" @@ -4439,7 +4679,28 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", +] + +[[package]] +name = "zerofrom" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", + "synstructure 0.13.1", ] [[package]] @@ -4447,3 +4708,25 @@ name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] diff --git a/Cargo.toml b/Cargo.toml index 8d9f939b3a..aaf39e7652 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ord" description = "◉ Ordinal wallet and block explorer" -version = "0.21.1" +version = "0.21.3" license = "CC0-1.0" edition = "2021" autotests = false @@ -25,7 +25,7 @@ axum-server = "0.5.0" base64 = "0.22.0" bip322 = "0.0.8" bip39 = "2.0.0" -bitcoin = { version = "0.32.3", features = ["rand"] } +bitcoin = { version = "=0.32.3", features = ["rand"] } bitcoincore-rpc = "0.19.0" boilerplate = { version = "1.0.0", features = ["axum"] } brotli = "7.0.0" @@ -49,8 +49,8 @@ mime = "0.3.16" mime_guess = "2.0.4" miniscript = "12.0.0" mp4 = "0.14.0" -ordinals = { version = "0.0.11", path = "crates/ordinals" } -redb = "=2.1.3" +ordinals = { version = "0.0.12", path = "crates/ordinals" } +redb = "2.2.0" ref-cast = "1.0.23" regex = "1.6.0" reqwest = { version = "0.11.27", features = ["blocking", "json"] } @@ -76,10 +76,10 @@ urlencoding = "2.1.3" [dev-dependencies] criterion = "0.5.1" executable-path = "1.0.0" +mockcore = { path = "crates/mockcore" } nix = { version = "0.29.0", features = ["signal"] } pretty_assertions = "1.2.1" reqwest = { version = "0.11.27", features = ["blocking", "brotli", "json"] } -mockcore = { path = "crates/mockcore" } unindent = "0.2.1" [[bin]] diff --git a/crates/mockcore/Cargo.toml b/crates/mockcore/Cargo.toml index 50cd155cdd..9ba3495753 100644 --- a/crates/mockcore/Cargo.toml +++ b/crates/mockcore/Cargo.toml @@ -4,12 +4,12 @@ description = "Mock Bitcoin Core RPC server" version = "0.0.1" edition = "2021" license = "CC0-1.0" -homepage = "https://github.com/ordinals/ord" +homepage = "https://github.com/ordinals/ord/tree/master/crates/mockcore" repository = "https://github.com/ordinals/ord" [dependencies] -bitcoin = { version = "0.32.3", features = ["serde", "rand"] } base64 = "0.22.0" +bitcoin = { version = "0.32.3", features = ["serde", "rand"] } hex = "0.4.3" jsonrpc-core = "18.0.0" jsonrpc-derive = "18.0.0" diff --git a/crates/mockcore/src/lib.rs b/crates/mockcore/src/lib.rs index badc8114ad..8bb2c43891 100644 --- a/crates/mockcore/src/lib.rs +++ b/crates/mockcore/src/lib.rs @@ -5,15 +5,19 @@ use { bitcoin::{ address::{Address, NetworkUnchecked}, amount::SignedAmount, + bip32::{ChildNumber, DerivationPath, Xpriv}, block::Header, blockdata::{script, transaction::Version}, consensus::encode::{deserialize, serialize}, hash_types::{BlockHash, TxMerkleNode}, hashes::Hash, + key::{Keypair, Secp256k1, TapTweak, XOnlyPublicKey}, locktime::absolute::LockTime, pow::CompactTarget, - Amount, Block, Network, OutPoint, ScriptBuf, Sequence, Transaction, TxIn, TxOut, Txid, Witness, - Wtxid, + secp256k1::{self, rand}, + sighash::{self, SighashCache, TapSighashType}, + Amount, Block, Network, OutPoint, ScriptBuf, Sequence, Transaction, TxIn, TxOut, Txid, + WPubkeyHash, Witness, Wtxid, }, bitcoincore_rpc::json::{ Bip125Replaceable, CreateRawTransactionInput, EstimateMode, FeeRatePercentiles, @@ -40,6 +44,7 @@ use { time::Duration, }, tempfile::TempDir, + wallet::Wallet, }; const COIN_VALUE: u64 = 100_000_000; @@ -47,6 +52,7 @@ const COIN_VALUE: u64 = 100_000_000; mod api; mod server; mod state; +mod wallet; #[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)] pub struct Descriptor { @@ -157,7 +163,8 @@ pub struct TransactionTemplate<'a> { pub recipient: Option
, } -#[derive(Serialize, Deserialize, Debug)]pub struct JsonOutPoint { +#[derive(Serialize, Deserialize, Debug)] +pub struct JsonOutPoint { txid: Txid, vout: u32, } @@ -293,16 +300,43 @@ impl Handle { .clone() } + #[track_caller] + pub fn tx_index(&self, txid: Txid) -> (usize, usize) { + let state = self.state(); + + for (block_hash, block) in &state.blocks { + for (t, tx) in block.txdata.iter().enumerate() { + if tx.compute_txid() == txid { + let b = state + .hashes + .iter() + .enumerate() + .find(|(_b, hash)| *hash == block_hash) + .unwrap() + .0; + return (b, t); + } + } + } + + panic!("unknown transaction"); + } + pub fn mempool(&self) -> Vec { self.state().mempool().to_vec() } pub fn descriptors(&self) -> Vec { - self.state().descriptors.clone() + self + .state() + .descriptors + .iter() + .map(|(descriptor, _timestamp)| descriptor.clone()) + .collect() } pub fn import_descriptor(&self, desc: String) { - self.state().descriptors.push(desc); + self.state().descriptors.push((desc, Timestamp::Now)); } pub fn lock(&self, output: OutPoint) { diff --git a/crates/mockcore/src/server.rs b/crates/mockcore/src/server.rs index 10b7b299d6..0fe3176b60 100644 --- a/crates/mockcore/src/server.rs +++ b/crates/mockcore/src/server.rs @@ -476,12 +476,20 @@ impl Api for Server { fn sign_raw_transaction_with_wallet( &self, tx: String, - _utxos: Option>, + utxos: Option>, sighash_type: Option<()>, ) -> Result { assert_eq!(sighash_type, None, "sighash_type param not supported"); let mut transaction: Transaction = deserialize(&hex::decode(tx).unwrap()).unwrap(); + + if let Some(utxos) = &utxos { + // sign for zero-value UTXOs produced by `ord wallet sign` + if utxos[0].amount == Some(Amount::ZERO) { + transaction.input[0].witness = self.state().wallet.sign_bip322(&utxos[0], &transaction); + } + } + for input in &mut transaction.input { if input.witness.is_empty() { input.witness = Witness::from_slice(&[&[0; 64]]); @@ -754,7 +762,7 @@ impl Api for Server { label: None, redeem_script: None, witness_script: None, - script_pub_key: ScriptBuf::new(), + script_pub_key: tx_out.script_pubkey.clone(), amount, confirmations: 0, spendable: true, @@ -803,10 +811,11 @@ impl Api for Server { &self, req: Vec, ) -> Result, jsonrpc_core::Error> { - self - .state() - .descriptors - .extend(req.into_iter().map(|params| params.descriptor)); + self.state().descriptors.extend( + req + .into_iter() + .map(|params| (params.descriptor, params.timestamp)), + ); Ok(vec![ImportMultiResult { success: true, @@ -901,9 +910,9 @@ impl Api for Server { .state() .descriptors .iter() - .map(|desc| Descriptor { + .map(|(desc, timestamp)| Descriptor { desc: desc.to_string(), - timestamp: Timestamp::Now, + timestamp: *timestamp, active: true, internal: None, range: None, diff --git a/crates/mockcore/src/state.rs b/crates/mockcore/src/state.rs index e2678f61a0..be7f33e781 100644 --- a/crates/mockcore/src/state.rs +++ b/crates/mockcore/src/state.rs @@ -1,16 +1,9 @@ -use { - super::*, - bitcoin::{ - key::{Keypair, Secp256k1, XOnlyPublicKey}, - secp256k1::rand, - WPubkeyHash, - }, -}; +use super::*; #[derive(Debug)] pub struct State { pub blocks: BTreeMap, - pub descriptors: Vec, + pub descriptors: Vec<(String, bitcoincore_rpc::json::Timestamp)>, pub fail_lock_unspent: bool, pub hashes: Vec, pub loaded_wallets: BTreeSet, @@ -25,6 +18,7 @@ pub struct State { pub receive_addresses: Vec
, pub change_addresses: Vec
, pub wallets: BTreeSet, + pub wallet: Wallet, } impl State { @@ -54,14 +48,13 @@ impl State { utxos: BTreeMap::new(), version, wallets: BTreeSet::new(), + wallet: Wallet::new(network), } } pub(crate) fn new_address(&mut self, change: bool) -> Address { - let secp256k1 = Secp256k1::new(); - let key_pair = Keypair::new(&secp256k1, &mut rand::thread_rng()); - let (public_key, _parity) = XOnlyPublicKey::from_keypair(&key_pair); - let address = Address::p2tr(&secp256k1, public_key, None, self.network); + let address = self.wallet.new_address(); + if change { &mut self.change_addresses } else { @@ -200,8 +193,28 @@ impl State { let mut total_value = 0; let mut input = Vec::new(); for (height, tx, vout, witness) in template.inputs.iter() { - let tx = &self.blocks.get(&self.hashes[*height]).unwrap().txdata[*tx]; - total_value += tx.output[*vout].value.to_sat(); + let block_hash = self + .hashes + .get(*height) + .ok_or_else(|| format!("invalid block height {height}")) + .unwrap(); + + let block = self.blocks.get(block_hash).unwrap(); + + let tx = block + .txdata + .get(*tx) + .ok_or_else(|| format!("invalid transaction index {tx}")) + .unwrap(); + + let tx_out = tx + .output + .get(*vout) + .ok_or_else(|| format!("invalid output index {vout}")) + .unwrap(); + + total_value += tx_out.value.to_sat(); + input.push(TxIn { previous_output: OutPoint::new(tx.compute_txid(), *vout as u32), script_sig: ScriptBuf::new(), diff --git a/crates/mockcore/src/wallet.rs b/crates/mockcore/src/wallet.rs new file mode 100644 index 0000000000..370895e9de --- /dev/null +++ b/crates/mockcore/src/wallet.rs @@ -0,0 +1,111 @@ +use super::*; + +#[derive(Debug)] +pub struct Wallet { + address_indices: HashMap, + master_key: Xpriv, + network: Network, + next_index: u32, + secp: Secp256k1, + derivation_path: DerivationPath, +} + +impl Wallet { + pub fn new(network: Network) -> Self { + let derivation_path = DerivationPath::master() + .child(ChildNumber::Hardened { index: 86 }) + .child(ChildNumber::Hardened { index: 0 }) + .child(ChildNumber::Hardened { index: 0 }) + .child(ChildNumber::Normal { index: 0 }); + + Self { + address_indices: HashMap::new(), + master_key: Xpriv::new_master(network, &[]).unwrap(), + network, + next_index: 0, + secp: Secp256k1::new(), + derivation_path, + } + } + + pub fn new_address(&mut self) -> Address { + let address = { + let derived_key = self + .master_key + .derive_priv( + &self.secp, + &self.derivation_path.child(ChildNumber::Normal { + index: self.next_index, + }), + ) + .unwrap(); + + let keypair = derived_key.to_keypair(&self.secp); + let (internal_key, _parity) = XOnlyPublicKey::from_keypair(&keypair); + + let script = ScriptBuf::new_p2tr(&self.secp, internal_key, None); + + Address::from_script(&script, self.network).unwrap() + }; + + self + .address_indices + .insert(address.clone(), self.next_index); + self.next_index += 1; + + address + } + + pub fn sign_bip322( + &self, + to_spend_input: &SignRawTransactionInput, + to_sign: &Transaction, + ) -> Witness { + let address = Address::from_script(&to_spend_input.script_pub_key, self.network).unwrap(); + let index = self.address_indices[&address]; + let derivation_path = self.derivation_path.child(ChildNumber::Normal { index }); + + let private_key = self + .master_key + .derive_priv(&self.secp, &derivation_path) + .unwrap(); + + let keypair = private_key.to_keypair(&self.secp); + let tweaked_keypair = keypair.tap_tweak(&self.secp, None); + + let sighash_type = TapSighashType::All; + + let mut sighash_cache = SighashCache::new(to_sign.clone()); + + let sighash = sighash_cache + .taproot_key_spend_signature_hash( + 0, + &sighash::Prevouts::All(&[TxOut { + value: Amount::from_sat(0), + script_pubkey: to_spend_input.script_pub_key.clone(), + }]), + sighash_type, + ) + .expect("signature hash should compute"); + + let signature = self.secp.sign_schnorr_no_aux_rand( + &secp256k1::Message::from_digest_slice(sighash.as_ref()) + .expect("should be cryptographically secure hash"), + &tweaked_keypair.to_inner(), + ); + + let witness = sighash_cache + .witness_mut(0) + .expect("getting mutable witness reference should work"); + + witness.push( + bitcoin::taproot::Signature { + signature, + sighash_type, + } + .to_vec(), + ); + + witness.to_owned() + } +} diff --git a/crates/ordinals/Cargo.toml b/crates/ordinals/Cargo.toml index 9e124ceb96..d8b44b4208 100644 --- a/crates/ordinals/Cargo.toml +++ b/crates/ordinals/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ordinals" -version = "0.0.11" +version = "0.0.12" edition = "2021" description = "Library for interoperating with ordinals and inscriptions" homepage = "https://github.com/ordinals/ord" @@ -13,7 +13,7 @@ bitcoin = { version = "0.32.3", features = ["rand"] } derive_more = { version = "1.0.0", features = ["display", "from_str"] } serde = { version = "1.0.137", features = ["derive"] } serde_with = "3.7.0" -thiserror = "1.0.56" +thiserror = "2.0.0" [dev-dependencies] serde_json = { version = "1.0.81", features = ["preserve_order"] } diff --git a/crates/ordinals/src/charm.rs b/crates/ordinals/src/charm.rs index 53e4aae770..af5067649a 100644 --- a/crates/ordinals/src/charm.rs +++ b/crates/ordinals/src/charm.rs @@ -15,10 +15,11 @@ pub enum Charm { Vindicated = 10, Mythic = 11, Burned = 12, + Palindrome = 13, } impl Charm { - pub const ALL: [Self; 13] = [ + pub const ALL: [Self; 14] = [ Self::Coin, Self::Uncommon, Self::Rare, @@ -26,6 +27,7 @@ impl Charm { Self::Legendary, Self::Mythic, Self::Nineball, + Self::Palindrome, Self::Reinscription, Self::Cursed, Self::Unbound, @@ -34,7 +36,7 @@ impl Charm { Self::Burned, ]; - fn flag(self) -> u16 { + pub fn flag(self) -> u16 { 1 << self as u16 } @@ -60,6 +62,7 @@ impl Charm { Self::Lost => "🤔", Self::Mythic => "🎃", Self::Nineball => "\u{39}\u{fe0f}\u{20e3}", + Self::Palindrome => "🦋", Self::Rare => "🧿", Self::Reinscription => "♻️", Self::Unbound => "🔓", @@ -90,6 +93,7 @@ impl Display for Charm { Self::Lost => "lost", Self::Mythic => "mythic", Self::Nineball => "nineball", + Self::Palindrome => "palindrome", Self::Rare => "rare", Self::Reinscription => "reinscription", Self::Unbound => "unbound", diff --git a/crates/ordinals/src/lib.rs b/crates/ordinals/src/lib.rs index 98b16f0355..1e7c63e19b 100644 --- a/crates/ordinals/src/lib.rs +++ b/crates/ordinals/src/lib.rs @@ -4,7 +4,7 @@ use { bitcoin::{ consensus::{Decodable, Encodable}, - constants::{DIFFCHANGE_INTERVAL, MAX_SCRIPT_ELEMENT_SIZE, SUBSIDY_HALVING_INTERVAL}, + constants::{DIFFCHANGE_INTERVAL, SUBSIDY_HALVING_INTERVAL}, opcodes, script::{self, Instruction}, Network, OutPoint, ScriptBuf, Transaction, diff --git a/crates/ordinals/src/runestone.rs b/crates/ordinals/src/runestone.rs index 82cb43694b..710ae5abd4 100644 --- a/crates/ordinals/src/runestone.rs +++ b/crates/ordinals/src/runestone.rs @@ -186,7 +186,7 @@ impl Runestone { .push_opcode(opcodes::all::OP_RETURN) .push_opcode(Runestone::MAGIC_NUMBER); - for chunk in payload.chunks(MAX_SCRIPT_ELEMENT_SIZE) { + for chunk in payload.chunks(u32::MAX.try_into().unwrap()) { let push: &script::PushBytes = chunk.try_into().unwrap(); builder = builder.push_slice(push); } @@ -1794,7 +1794,7 @@ mod tests { } #[test] - fn runestone_payload_is_chunked() { + fn runestone_payloads_are_not_chunked() { let script = Runestone { edicts: vec![ Edict { @@ -1823,7 +1823,7 @@ mod tests { } .encipher(); - assert_eq!(script.instructions().count(), 4); + assert_eq!(script.instructions().count(), 3); } #[test] diff --git a/crates/ordinals/src/sat.rs b/crates/ordinals/src/sat.rs index cba16e01d8..6a09b5d134 100644 --- a/crates/ordinals/src/sat.rs +++ b/crates/ordinals/src/sat.rs @@ -29,6 +29,18 @@ impl Sat { self.n() >= 50 * COIN_VALUE * 9 && self.n() < 50 * COIN_VALUE * 10 } + pub fn palindrome(self) -> bool { + let mut n = self.0; + let mut reversed = 0; + + while n > 0 { + reversed = reversed * 10 + n % 10; + n /= 10; + } + + self.0 == reversed + } + pub fn percentile(self) -> String { format!("{}%", (self.0 as f64 / Self::LAST.0 as f64) * 100.0) } @@ -97,6 +109,10 @@ impl Sat { Charm::Nineball.set(&mut charms); } + if self.palindrome() { + Charm::Palindrome.set(&mut charms); + } + if self.coin() { Charm::Coin.set(&mut charms); } @@ -803,4 +819,18 @@ mod tests { "failed to parse sat `foo`: invalid percentile", ); } + + #[test] + fn palindrome() { + assert!(Sat(0).palindrome()); + assert!(!Sat(10).palindrome()); + assert!(Sat(11).palindrome()); + } + + #[test] + fn palindrome_charm() { + assert!(Charm::Palindrome.is_set(Sat(0).charms())); + assert!(!Charm::Palindrome.is_set(Sat(10).charms())); + assert!(Charm::Palindrome.is_set(Sat(11).charms())); + } } diff --git a/crates/ordinals/src/sat_point.rs b/crates/ordinals/src/sat_point.rs index 17bf4a2f51..144cf9e215 100644 --- a/crates/ordinals/src/sat_point.rs +++ b/crates/ordinals/src/sat_point.rs @@ -44,7 +44,7 @@ impl Encodable for SatPoint { } impl Decodable for SatPoint { - fn consensus_decode( + fn consensus_decode( d: &mut D, ) -> Result { Ok(SatPoint { diff --git a/docs/book.toml b/docs/book.toml index 8b8dc9e8e6..77bb124b57 100644 --- a/docs/book.toml +++ b/docs/book.toml @@ -1,5 +1,6 @@ [book] title = "Ordinal Theory Handbook" +authors = ["Casey Rodarmor", "Raph Japh"] language = "en" src = "src" @@ -13,7 +14,7 @@ cname = "docs.ordinals.com" default-theme = "coal" git-repository-url = "https://github.com/ordinals/ord" preferred-dark-theme = "coal" -additional-css = ["language-picker.css"] +additional-css = ["index.css"] [output.linkcheck] diff --git a/docs/language-picker.css b/docs/index.css similarity index 77% rename from docs/language-picker.css rename to docs/index.css index 1f7d5377e1..b6a87ef1b9 100644 --- a/docs/language-picker.css +++ b/docs/index.css @@ -1,3 +1,7 @@ +table { + float: left; +} + #language-list { left: auto; right: 10px; diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 54238dd56b..fedeecea3f 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -18,9 +18,11 @@ Summary - [Contributing](contributing.md) - [Donate](donate.md) - [Guides](guides.md) + - [API](guides/api.md) - [Explorer](guides/explorer.md) - [Wallet](guides/wallet.md) - [Batch Inscribing](guides/batch-inscribing.md) + - [Splitting](guides/splitting.md) - [Collecting](guides/collecting.md) - [Sparrow Wallet](guides/collecting/sparrow-wallet.md) - [Moderation](guides/moderation.md) diff --git a/docs/src/guides/api.md b/docs/src/guides/api.md new file mode 100644 index 0000000000..c368d036e2 --- /dev/null +++ b/docs/src/guides/api.md @@ -0,0 +1,5132 @@ +# JSON-API + +By default, the `ord server` gives access to endpoints that return JSON instead of HTML if you set the HTTP `Accept: application/json` header. The structure of these objects closely follows what is shown in the HTML. These endpoints are: + +## Endpoints + +
+ + GET + /address/<ADDRESS> + + +### Description + +List all assets of an address. Requires index with `--index-addresses` flag. + +### Example + +```bash +curl -s -H "Accept: application/json" \ + http://0.0.0.0:80/address/bc1pdrm7tcyk4k6c3cdcjwkp49jmfrwmtvt0dvqyy7y4qp79tgks4lmqdpj6rw +``` + +```json +{ + "outputs": [ + "ddf44a0e0080f458a1a1b6255a9fa0957f2611883a483c1901ccb0f59e3eb302:0", + "77c5a00da7dcf2c8f965effd25dda16ec8ec8d6b8937e89bbbdf10a1dc5aeb0d:0", + "36f5a76644ee3002483e08345feaa97a71c7a210050333a8f02e942af1294227:1434", + "e2a15acfb519ac6d95bbfd411f1f3dba4692672ea0b0a8f868da8b3f565fb428:0", + "2b84aab0b4b9869a005ae2571a94064163652f2aeffecd4fedf0397dd6b7cf41:1", + "e267548a8cc0c6e6033a6f82b355163bc1d041879206d27feb46e605b3e82759:246", + "f5b586cf0e61b7d89c18a74c47a1f8df9ff530a66ed62c02cec72fde9a23a45a:0", + "4fd271181e901809f6e2d5f89ce95ddfeb886f8db1582a35c812401af8e77661:42", + "29f8633939e956b078fb2fa0e1219089bbe2544169e7a2755e97cc254b783cb2:0", + "7aeca5c346aec84acde229e5927dd09aef680992223cfa57fe6f1ff7698b12da:0", + "cccc35d597cd5a8079f6fe54bb9c743e5297d9165b0dcfa74e74687514c66be0:0", + "590745241244d41a90df7e2cf0d7745877e4cedac573525946cc8ac7f18757e8:0", + "590745241244d41a90df7e2cf0d7745877e4cedac573525946cc8ac7f18757e8:1", + "590745241244d41a90df7e2cf0d7745877e4cedac573525946cc8ac7f18757e8:2", + "6b23a6cf6d2850f437a50f1673fc8410ae36146541b3101d8573539871a91bf0:0", + "fe130d3ca1577c65ac768f4b5b9d12a88d947ddcc31196bcf870ed5ff18403f5:2", + "5fddcbdc3eb21a93e8dd1dd3f9087c3677f422b82d5ba39a6b1ec37338154af6:0", + "c63c4910be259007e1119dbbe6fe0d923b207e78058a4f69bd54df6a3a6488f6:0" + ], + "inscriptions": [ + "77c5a00da7dcf2c8f965effd25dda16ec8ec8d6b8937e89bbbdf10a1dc5aeb0di0", + "1417086d6abf96f68287b799b13b0081ec895d0b4a5fb7b70d2fde404eeb8aa1i0", + "eb6636995ba074472e4193dbf65bb268ef5379509d9fffb20ddd5857039f80abi1", + "4fd271181e901809f6e2d5f89ce95ddfeb886f8db1582a35c812401af8e77661i42", + "40ab704e6123c681554102556ae3f37b0525863968311f845322fe2f2403a4c6i0", + "0b36fa5ebce6c0e028b61647a89f9488a9c9f6ad0b90a215d10eb96ee8aedf9ei0", + "87a0088e83e43a79e0e9b451037067bca726f5fd3da083e8684996dd1e6b6c70i0", + "54abce9b4380e2fe90ac0cb49b442afee76838ffd91f1ffcac46f6a6fea790c5i72", + "54abce9b4380e2fe90ac0cb49b442afee76838ffd91f1ffcac46f6a6fea790c5i768", + "b4ba20c4eb45425f4960820f493a04a3b1c2e1364927d6001e7dc7dd524cf922i931", + "781938d9e2e93698d41f30b4d1c7f7bfcd403761bce3c0ab579be47b408809e2i0", + "fe130d3ca1577c65ac768f4b5b9d12a88d947ddcc31196bcf870ed5ff18403f5i1", + "26482871f33f1051f450f2da9af275794c0b5f1c61ebf35e4467fb42c2813403i0" + ], + "sat_balance": 22635, + "runes_balances": [ + [ + "RSIC•AUBERGINE", + "1100000000", + "🍆" + ], + [ + "SPACEY•CODARMOR", + "279550", + "🚀" + ], + [ + "ISABEL•FOXEN•DUKE", + "10000", + "⚡" + ], + [ + "EPIC•EPIC•EPIC•EPIC", + "1000", + "💥" + ] + ] +} +``` +
+ +
+ + GET + /block/<BLOCKHASH> + + +### Description + +Returns info about the specified block. + +### Example + +```bash +curl -s -H "Accept: application/json" \ + http://0.0.0.0:80/block/000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f +``` + +```json +{ + "best_height": 864325, + "hash": "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f", + "height": 0, + "inscriptions": [], + "runes": [], + "target": "00000000ffff0000000000000000000000000000000000000000000000000000", + "transactions": [ + { + "version": 1, + "lock_time": 0, + "input": [ + { + "previous_output": "0000000000000000000000000000000000000000000000000000000000000000:4294967295", + "script_sig": "04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73", + "sequence": 4294967295, + "witness": [] + } + ], + "output": [ + { + "value": 5000000000, + "script_pubkey": "4104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac" + } + ] + } + ] +} +``` +
+ +
+ + GET + /block/<BLOCKHEIGHT> + + +### Description + +Returns info about the specified block. + +### Example + +```bash +curl -s -H "Accept: application/json" \ + http://0.0.0.0:80/block/0 +``` + +```json + { + "best_height": 864325, + "hash": "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f", + "height": 0, + "inscriptions": [], + "runes": [], + "target": "00000000ffff0000000000000000000000000000000000000000000000000000", + "transactions": [ + { + "version": 1, + "lock_time": 0, + "input": [ + { + "previous_output": "0000000000000000000000000000000000000000000000000000000000000000:4294967295", + "script_sig": "04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73", + "sequence": 4294967295, + "witness": [] + } + ], + "output": [ + { + "value": 5000000000, + "script_pubkey": "4104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac" + } + ] + } + ] + } +``` +
+ +
+ + GET + /blockcount + + +### Description + +Returns the height of the latest block. + +### Example + +```bash +curl -s -H "Accept: application/json" \ + http://0.0.0.0:80/blockcount +``` + +```json +864328 +``` +
+ +
+ + GET + /blockhash + + +### Description + +Returns blockhash for the latest block. + +### Example + +```bash +curl -s -H "Accept: application/json" \ + http://0.0.0.0:80/blockhash +``` + +```text +00000000000000000000c82c12a925a224605b1bb767f696ae4ff10332dbe9bc +``` +
+ +
+ + GET + /blockhash/<BLOCKHEIGHT> +     + + +### Description + +Returns blockhash of specified block. + +### Example + +```bash +curl -s -H "Accept: application/json" \ + http://0.0.0.0:80/blockhash/840000 +``` + +```text +0000000000000000000320283a032748cef8227873ff4872689bf23f1cda83a5 +``` +
+ +
+ + GET + /blockheight + + +### Description + +Returns the height of the latest block. + +### Example + +```bash +curl -s -H "Accept: application/json" \ + http://0.0.0.0:80/blockheight +``` + +```json +864330 +``` +
+ +
+ + GET + /blocks + + +### Description + +Returns the height of the latest block, the blockhashes of the last 100 blocks, and featured inscriptions from them. + +### Example + +```bash +curl -s -H "Accept: application/json" \ + http://0.0.0.0:80/blocks +``` + +```json +{ + "last": 864335, + "blocks": [ + "00000000000000000002794398a350a04cc371ee33659296a980214f0f060adc", + "000000000000000000000470180b94350be751ea1ade67c4235c5b9515380b1f", + "000000000000000000016e1769c5aa0f3781dd99ce2d5172a696c546d442e481", + "00000000000000000002043c5ed07ad806a1c7133cf34670333326009d6195a6", + "000000000000000000017cd6200b2711c024094e64797619263d74433c2bc880", + "00000000000000000002dd6a13fffde71c09e67855d03340787e6a9b951c44df", + "00000000000000000000c82c12a925a224605b1bb767f696ae4ff10332dbe9bc", + "000000000000000000024ea66d1cddf1cfd8a3926a8e691844143da1596526db", + "00000000000000000001bd9376dfbd9689239e9c5d11d579d6c8885a0efa199c", + "0000000000000000000081f76cbccc29d92024f07f0e0b7e6b7dd063bed69bcc", + "00000000000000000000e1ca2bab230aeb6cb75b1bb5b766cb55f1a391a7d408", + "00000000000000000001fc567723ff6ccf674981202617384ae2152a711710d3", + "00000000000000000002381ab1fa4661bfecc3429424c415788cef2c62c630bb", + "000000000000000000022a1cacf15fa28d4d3698506c7b76fc62d7e50053be1f", + "000000000000000000023b6d0182255bcc633e27ecdf8a86918830fdfd4f9612", + "00000000000000000001135bd270114428c2c021e6c4161be93ba7ec9dc4e720", + "0000000000000000000269e44d995970caf720ecc272f3554d923b74c57e84ed", + "00000000000000000000e0224234536f4724c144c8da5cbaea486f3b26ef808a", + "0000000000000000000102ae83593c0b5046cc6ec3beadf133e2a9b69fb761da", + "000000000000000000014d52c9b6d9ca1fd2419562d24ff87214fcdf1688b8c4", + "00000000000000000001bc24775ec320b6af4c1210395a4092c29b7af265153c", + "00000000000000000000f4498b608a6a476bed5c4164478f618d19bcc02da3fa", + "0000000000000000000255810324c89ec4ef87a0d028968dc70aed1817bac8e8", + "0000000000000000000213bbddd4cce2831bda4865ae7025074b2a30fb228c7c", + "00000000000000000002b4cf1c7c051fd712df4447ac5e90ecde1d4429a06358", + "000000000000000000006b39a84f7bfc592293bc044c28fb57dfa660d41acc36", + "00000000000000000001cd132f83def8f13b87974eb4d2629b11f52e3016c097", + "00000000000000000001963de3de854dd9da9f384fb2ef753ba94c105cc807c6", + "00000000000000000000fc1b08733842cb0f2d3dae7f56545805b403aa0d3621", + "0000000000000000000049464eaf610aa71edaaf33e465c47981811395c3cdc7", + "0000000000000000000137881c0f7bc6b762daf8370935444fdb13b98ed4572e", + "00000000000000000001e7cc406d66013c17db6e9f8c90b807c93936fa18f192", + "0000000000000000000084d8e77f14bcdc71acedf0ba5be6b70562dcf76e2ba2", + "00000000000000000002e278d6c35e96eebb964694c430527db43301efdf367f", + "00000000000000000002ace24c94d6f927e4cad8d72839508a275d6a2882c408", + "00000000000000000002c165514bb47cef5b8eacedbabce070fc7147f6b8a48e", + "0000000000000000000251f0eabbbf2bb58837cd284a1a44275e76d11b6da62a", + "00000000000000000000650e34e08c4bc732961ce33a2b9051044ed95e95d82f", + "00000000000000000000ecd0dfe9c0a52b2a7bcf48edcdcb2df19b827afcbed2", + "0000000000000000000048131b07192e8f4466e36d025ea773e0dadcf442713f", + "000000000000000000012eb14a615f799bf628e371ee5e7dd0b518d108fc74cd", + "0000000000000000000025d47721b228c712aeb50bfd13768d8925274c1015ef", + "0000000000000000000326c89fe7dfe7737f75368ce78404c1ffb1b08c422641", + "00000000000000000002ff417f03781bbce1a1082cfaff8cf5c066c9a7547a28", + "00000000000000000002bd4acc44f416975f25aa719e07abc2c0dd12761e4d17", + "0000000000000000000188b4408d6131395ef6ca544b35cf37e7575779b15471", + "00000000000000000003253f74e3f5d35aacbef57aee3225c9e071036309aad6", + "0000000000000000000322bfda974265420bb6b604cd577410b9ca5cccbeae17", + "00000000000000000001535fdb2eb0efe673bd505bcec47a9fdedd7b83d22a6c", + "0000000000000000000169fb1a4daaaf4e08d12fcf670a81ed0f7bb4f5328494", + "0000000000000000000315eb8d0ea1cbd251c7ea2404041c352823e29a6f376a", + "000000000000000000021aef6c217e2eae81d1702d1331ab8f91360e55a60c51", + "00000000000000000000ffb1ee2423e399153433e634db68ca4aad8a829b61da", + "00000000000000000000a6e99c9e050d4345606016673d674da4aade02a8ff8a", + "00000000000000000000349de7338756bdb425cc13a3e22e986b4035d00f097b", + "00000000000000000000045218f05f939e0386ddec2460c815e5c671bfd20892", + "000000000000000000007f99d51dd0738c42ce7dc83e59061a2b33f971b6d3ab", + "00000000000000000002fc37d0f7ec804a1063a4ff8613521fcc99f1ab8fe07a", + "00000000000000000002daff4047da69c658a1badb00d14d7d3e709f76b8bf3b", + "00000000000000000001a427c71546cda9a5577d5e38bc95a5d3450df7c1d26f", + "000000000000000000004648af338d38563d26c3a5bef3ca9582ea2ccb72f8ea", + "00000000000000000001428e153a325e9aa859589a80e8b0271d1ba48e8749c7", + "000000000000000000005ea10805f8ab474b9888bdc2c2840cd2e5529bbd0d49", + "00000000000000000002c7b5bcf3372c7441e79bda1310c53f35eb59483b9092", + "00000000000000000001e2486f12c01ca0f76481b40181bf6f8f48802ade8c49", + "000000000000000000024590edc9d2d4878b32a4944dfda1a3929a6e4c9c3592", + "000000000000000000028f255235dca42b10e5da593c2d4eb006cb329a041587", + "000000000000000000006fb8f4a5d906e9c0112d5a97188f392407ab8e95bd81", + "00000000000000000002864156220f1093e76caf233009c1b6be9ee0d810ac29", + "000000000000000000009d28b5b1336abfe552aa8d92e56c1c254a1eee0e0b4b", + "000000000000000000029ad7c816c8a4f79f93e60defbc6aee7cf25e61b46008", + "00000000000000000002af0693f1c73282516b97031b7d956d07756a6f8a13d0", + "00000000000000000001376f75d1785015b1c4717b2612a7c1bc8de69817c768", + "00000000000000000000cd6e3f3ec308a26831d8866ea51beab6b02d3a5d0812", + "00000000000000000002cf32a666fabf1789ffa4fa4215f78b52406b716936d2", + "0000000000000000000310254c2c405a46c9710e52a7a7728bac5079b90e25ba", + "00000000000000000002cb11925574071edd904390823344b7ca616640971081", + "000000000000000000022e5bf2570eaee0532c0edee2a2682d4a74488ca0522f", + "000000000000000000031542e9c2b0dbd43b4e7caa3f24537af0d39bfa3997cf", + "00000000000000000002215bb1138bbc4a7611826b13e532b51d5b4e82eeac3d", + "00000000000000000001c3a1c78d27f0072f27dc1d0060273e0ef03f1bfc0ce9", + "00000000000000000001fe6ba288a1b9a14d15d3e915418cbfb54685595b0cc1", + "0000000000000000000067f8164cd2e75b3ba172cb98cd00f0894faee5c6f763", + "000000000000000000018ef9990389ca9052a0c1c93b65f780d3071346e531f3", + "000000000000000000023e7bee6b1b4647411b0279df23c9ad470d91c1b99081", + "00000000000000000000a085f77681ddf175c74b897758e9f406a17f1a278030", + "000000000000000000001bf9c32af2d6a8a4f3d50c40f927e0867d4ad9481fdd", + "00000000000000000000cde89e34036ece454ca2d07ddd7f71ab46307ca87423", + "00000000000000000001141c91e70decadd60a93f32b70b08a8ec6d74b270b08", + "000000000000000000023562ac878ab6f62329a70a15954bd56e088f3a836426", + "000000000000000000006a4455949ef37cf3c3ee6b4cc2da27137f24445c7058", + "0000000000000000000297397401eee3019168e761464c3716892951a5e33cbc", + "000000000000000000015b68955519ab2925858ebbd02f897ff81cfc4a360dd4", + "000000000000000000018a0932deb92c6bc40d46a34e654f8a2afbd6c745c6a3", + "00000000000000000001996de65cc72f1fdeaebc3141db0a2a2dd269233c8e56", + "00000000000000000000d0434cc36c19d49b9e873661ff171d632543d5c2f454", + "00000000000000000003184a301f7c76332ec629a51bcaab5652f2ba82da55d8", + "00000000000000000001e47fd13c25e24f8933b02a38c3490c0a430c0b71ea9e", + "000000000000000000027fe376111297406696afa48be122d6596b13ac15156a", + "00000000000000000002ab8ba2529a468c0f2781e3afc0f832209c94f95d4f1d" + ], + "featured_blocks": { + "000000000000000000000470180b94350be751ea1ade67c4235c5b9515380b1f": [ + "0ae94b05b21aa6b7f0620075db618a70124cb422fc5ced577bffbd0d103d4ce7i0", + "65f1922bc83ee43485ed884dbec24c0c1cef6c4f6d999a8ac0c09d7adc8b39dbi0", + "e87c21c7c8ba8b194bd8e389f6cb9ecb2312c076139aff31c629f93df86b98ffi0", + "aeb8d90de7e92efc11ffa6b411e829b6dcb0e00b7fd4f912947065b9084d99bai0", + "6d8f58c7f24e277d614bc6c9bb6648543e47db5431c6c073a6bd5e3be1e47c5ci0", + "770cde7a5c49ae8a4f109bd83fb364ef9b83bc6f72d3654c793f5452d7b30831i0", + "65e51357e67da9dd64a65fff1d9d26153c9969f4acfbab028e74b408559dfc07i0", + "7c63687fabdcd421de925e99b4152b2327328afe51c63903aa4a9cc9fba31872i0" + ], + "000000000000000000017cd6200b2711c024094e64797619263d74433c2bc880": [ + "c970b695f491a8812b5293da2673f4e6c9ae3d8be07d9da1fbb9c33a45f6fd1fi0", + "d001827b7c48e44399587f12e2fa33b2c0b1eb12c309f1c21729f1e3bc95c5fci0", + "facefc9cd6dec1cc25d7b7321cbbdaed735049a9a3da834a66975d98e23ac4dfi0", + "09353363c2e95891db553f3742a40a74c5dd1b7668669f732d58e52e7c132b92i0", + "48f3f7cbf3061957c06f66c0fe66be9ad4ad73df65b9ded1345e05f904e1e63di0", + "4e65b1d0b36c6727c646d5d6f45f00db35158a49a139282d6544f127734db9adi0", + "b8c744320e735aaaec18fd6b306d6dd678f99461e88dfa25f178627b8480e483i0", + "55d27ab1b4321addc5c34c10ef2ac4957add8b8485f465df7f2883315c9cf5f5i0" + ], + "000000000000000000016e1769c5aa0f3781dd99ce2d5172a696c546d442e481": [ + "cc2415293c275bea4d73ff8f45f68f269686b819de447f50ec6988ac04a62d1bi0", + "c642cd4cc7a075c61d3a32b949217990aa91dfc928f12a2cdba1f2f228c699c7i0", + "5342721d044e9e9999484b988ce9fb71097d9209c77f6549df9e31ec9b344c5bi0", + "a75f792be155a0b53691289433a6413c1efb1aeaf970f752ee70be3c6e755a06i0", + "19c0d770abaaeb5b24e718231684d53b768450cc324c8fee435910de65c459e2i0", + "30eb7c46bf4f5af33e665a119af40dd45d127cb6cdc2596de75e08f094651fa5i0", + "122631e7b8bab4238582229273a9dbe08544d2d97ad0c9a80b5829ae10ac3f27i0", + "41c304db88c60a27f45957442b857c0affefdfdca45bdf72ab4cbf9fce4d97a0i0" + ], + "00000000000000000002043c5ed07ad806a1c7133cf34670333326009d6195a6": [ + "2f62d6ed309f838bab143cf3a53ba758eb940b43c30c32e22d9dbf6fe7882613i0", + "83642352c5b670387874995954f79e270cb78b05a9a88b9d4d65e6f94c6df0a3i0", + "68831e3c8669ad5e8fc3585a9e8a55673123ada4c33a699e98e4d9e0297f1800i0", + "20fa9d317af18cc976a6b77797ceb5884127ac5dd7e3f131565a18dd712311c6i0", + "a286d7f705fd410cdd3f1081c4c22f196bdea4c64cfbd963f45302cdec1fe968i0", + "11eb110f86d880d8dcac852edcca7007904fda34ad031fc01f24a3e6b02ef47ci0", + "9fbec6d72d71169dc041693e740dae7bb7bb195ccd4a7f40c4c12bd4afbf7354i0", + "7c823fe74fa783debea8339fbea44b8395805295652749a651aa2133d9a1832di0" + ], + "00000000000000000002794398a350a04cc371ee33659296a980214f0f060adc": [ + "2596a275dca4b5cc18cd1060ab92d6df3df5507738b8f2b6b7c18c4ff1d1b36ai0", + "93256e5da147f0067d6b11e09d853b838ad1d95cf59664cccbcd52859f9ea1aci0", + "f404b5ebabd4b7fb8b88df52289b983b28f3e36fcbb63e649edea6e7ba62e582i1", + "f404b5ebabd4b7fb8b88df52289b983b28f3e36fcbb63e649edea6e7ba62e582i0", + "1bfbd226fded339cbe197153ab8b6da622c9a20e7d4911013abd385da7e05b89i0", + "af7b8810755bdf7bd62dbb6c5f2639e107a6d9d2c7199ae3650f1e7583d4bd66i0", + "9c594cb991bfecdf9d2116b644262927365f20f03ccdc8a64cbb640c11a58907i0", + "29628c91948bc100185605d11cde0aebda572d73b752bd6ed668bd86e455aa8di0" + ] + } +} +``` +
+ +
+ + GET + /blocktime + + +### Description + +Returns the UNIX timestamp of when the latest block was mined. + +### Example + +```bash +curl -s -H "Accept: application/json" \ + http://0.0.0.0:80/blocktime +``` + +```json +1728158372 +``` +
+ +
+ + GET + /decode/<TRANSCATION_ID> + + +### Description + +Decode a transaction, congruent to the `ord decode` command + +### Example + +```bash +curl -s -H "Accept: application/json" \ + http://0.0.0.0:80/decode/6fb976ab49dcec017f1e201e84395983204ae1a7c2abf7ced0a85d692e442799 +``` + +```json +{ + "inscriptions": [ + { + "input": 0, + "offset": 0, + "payload": { + "body": [ + 137, + 80, + 78, + 71, + 13, + 10, + 26, + 10, + 0, + 0, + 0, + 13, + 73, + 72, + 68, + 82, + 0, + 0, + 0, + 100, + 0, + 0, + 0, + 100, + 1, + 3, + 0, + 0, + 0, + 74, + 44, + 7, + 23, + 0, + 0, + 0, + 6, + 80, + 76, + 84, + 69, + 255, + 255, + 255, + 0, + 0, + 0, + 85, + 194, + 211, + 126, + 0, + 0, + 2, + 206, + 73, + 68, + 65, + 84, + 56, + 203, + 149, + 212, + 75, + 104, + 19, + 65, + 24, + 7, + 240, + 148, + 74, + 19, + 16, + 93, + 20, + 180, + 20, + 105, + 22, + 193, + 179, + 61, + 21, + 11, + 125, + 44, + 228, + 90, + 176, + 39, + 41, + 90, + 75, + 14, + 30, + 74, + 91, + 74, + 43, + 69, + 18, + 250, + 200, + 86, + 60, + 120, + 80, + 154, + 187, + 104, + 5, + 17, + 81, + 170, + 205, + 161, + 96, + 11, + 77, + 178, + 161, + 120, + 145, + 98, + 2, + 30, + 4, + 109, + 147, + 77, + 201, + 33, + 133, + 154, + 221, + 196, + 144, + 108, + 146, + 221, + 157, + 191, + 33, + 51, + 59, + 1, + 193, + 67, + 231, + 246, + 227, + 155, + 239, + 49, + 51, + 236, + 186, + 206, + 184, + 36, + 172, + 181, + 209, + 9, + 212, + 218, + 18, + 178, + 46, + 210, + 214, + 136, + 8, + 161, + 189, + 181, + 14, + 148, + 179, + 60, + 205, + 0, + 108, + 158, + 232, + 214, + 36, + 172, + 91, + 142, + 196, + 180, + 170, + 4, + 100, + 222, + 237, + 80, + 85, + 150, + 101, + 167, + 140, + 172, + 198, + 186, + 21, + 57, + 193, + 4, + 77, + 246, + 39, + 193, + 138, + 118, + 168, + 165, + 198, + 104, + 9, + 11, + 172, + 65, + 24, + 68, + 7, + 25, + 96, + 13, + 194, + 168, + 22, + 64, + 134, + 168, + 4, + 213, + 172, + 84, + 76, + 132, + 152, + 74, + 90, + 161, + 170, + 149, + 8, + 149, + 247, + 38, + 10, + 85, + 104, + 97, + 170, + 222, + 95, + 234, + 92, + 85, + 157, + 13, + 48, + 77, + 162, + 82, + 129, + 49, + 206, + 78, + 167, + 106, + 149, + 138, + 134, + 121, + 58, + 154, + 148, + 55, + 235, + 101, + 211, + 2, + 83, + 29, + 181, + 28, + 202, + 76, + 50, + 113, + 141, + 232, + 107, + 2, + 232, + 216, + 193, + 58, + 146, + 183, + 229, + 114, + 142, + 42, + 16, + 128, + 49, + 9, + 59, + 77, + 53, + 55, + 15, + 243, + 26, + 200, + 15, + 170, + 25, + 213, + 109, + 238, + 89, + 226, + 12, + 139, + 69, + 175, + 219, + 59, + 197, + 167, + 159, + 89, + 222, + 195, + 115, + 171, + 198, + 151, + 75, + 44, + 47, + 152, + 93, + 24, + 54, + 30, + 39, + 88, + 77, + 249, + 32, + 19, + 215, + 35, + 75, + 50, + 213, + 224, + 126, + 195, + 171, + 159, + 63, + 89, + 166, + 179, + 244, + 135, + 177, + 17, + 145, + 72, + 63, + 213, + 224, + 38, + 89, + 215, + 250, + 26, + 123, + 84, + 146, + 128, + 245, + 45, + 137, + 136, + 84, + 189, + 34, + 146, + 154, + 140, + 110, + 246, + 40, + 227, + 24, + 139, + 251, + 109, + 63, + 149, + 32, + 34, + 109, + 200, + 240, + 50, + 9, + 120, + 45, + 74, + 132, + 201, + 189, + 73, + 94, + 25, + 125, + 141, + 40, + 191, + 121, + 69, + 84, + 200, + 16, + 211, + 126, + 67, + 194, + 179, + 147, + 21, + 166, + 131, + 204, + 106, + 119, + 106, + 201, + 81, + 118, + 193, + 54, + 142, + 19, + 22, + 83, + 176, + 211, + 220, + 171, + 117, + 30, + 81, + 117, + 141, + 8, + 166, + 73, + 132, + 231, + 44, + 38, + 195, + 232, + 5, + 142, + 184, + 170, + 23, + 184, + 4, + 25, + 191, + 223, + 1, + 25, + 42, + 17, + 248, + 185, + 45, + 3, + 84, + 18, + 176, + 125, + 87, + 129, + 243, + 42, + 50, + 201, + 223, + 215, + 161, + 210, + 91, + 90, + 133, + 153, + 212, + 150, + 97, + 80, + 25, + 10, + 54, + 116, + 9, + 177, + 108, + 75, + 150, + 10, + 209, + 47, + 98, + 170, + 165, + 142, + 211, + 113, + 226, + 247, + 143, + 217, + 247, + 138, + 45, + 153, + 119, + 106, + 170, + 242, + 160, + 50, + 65, + 101, + 169, + 127, + 82, + 241, + 105, + 76, + 81, + 65, + 169, + 78, + 69, + 191, + 65, + 161, + 58, + 197, + 206, + 98, + 79, + 90, + 105, + 180, + 228, + 170, + 146, + 239, + 75, + 163, + 95, + 231, + 11, + 180, + 67, + 125, + 115, + 160, + 120, + 156, + 123, + 177, + 77, + 21, + 90, + 33, + 110, + 75, + 200, + 167, + 216, + 107, + 62, + 209, + 109, + 131, + 212, + 36, + 42, + 9, + 101, + 211, + 172, + 59, + 103, + 16, + 176, + 146, + 143, + 230, + 85, + 194, + 110, + 130, + 40, + 205, + 57, + 35, + 22, + 85, + 7, + 81, + 136, + 142, + 72, + 209, + 249, + 252, + 82, + 68, + 39, + 139, + 89, + 166, + 91, + 1, + 162, + 219, + 233, + 53, + 166, + 158, + 212, + 225, + 68, + 104, + 145, + 193, + 229, + 141, + 223, + 120, + 27, + 142, + 56, + 186, + 24, + 59, + 52, + 3, + 91, + 142, + 4, + 25, + 64, + 134, + 11, + 0, + 8, + 223, + 169, + 120, + 124, + 34, + 223, + 233, + 14, + 26, + 177, + 220, + 17, + 87, + 224, + 101, + 126, + 184, + 173, + 57, + 143, + 239, + 106, + 91, + 211, + 30, + 223, + 229, + 255, + 197, + 116, + 143, + 207, + 107, + 113, + 205, + 2, + 13, + 30, + 235, + 250, + 208, + 204, + 251, + 200, + 245, + 169, + 153, + 199, + 229, + 126, + 228, + 241, + 93, + 105, + 87, + 49, + 154, + 221, + 121, + 149, + 206, + 221, + 230, + 100, + 187, + 92, + 104, + 78, + 93, + 115, + 212, + 161, + 3, + 164, + 232, + 200, + 101, + 2, + 102, + 150, + 43, + 244, + 230, + 125, + 36, + 193, + 37, + 218, + 227, + 248, + 231, + 63, + 120, + 182, + 245, + 23, + 127, + 181, + 197, + 106, + 45, + 115, + 252, + 75, + 0, + 0, + 0, + 0, + 73, + 69, + 78, + 68, + 174, + 66, + 96, + 130 + ], + "content_encoding": null, + "content_type": [ + 105, + 109, + 97, + 103, + 101, + 47, + 112, + 110, + 103 + ], + "delegate": null, + "duplicate_field": false, + "incomplete_field": false, + "metadata": null, + "metaprotocol": null, + "parents": [], + "pointer": null, + "rune": null, + "unrecognized_even_field": false + }, + "pushnum": false, + "stutter": false + } + ], + "runestone": null +} +``` +
+ +
+ + GET + /inscription/<INSCRIPTION_ID> + + +### Description + +Fetch details about a specific inscription by its ID. + +### Example + +```bash +curl -s -H "Accept: application/json" / + http://0.0.0.0:80/inscription/6fb976ab49dcec017f1e201e84395983204ae1a7c2abf7ced0a85d692e442799i0 +``` + +```json +{ + "address": "bc1ppth27qnr74qhusy9pmcyeaelgvsfky6qzquv9nf56gqmte59vfhqwkqguh", + "charms": [], + "children": [ + "681b5373c03e3f819231afd9227f54101395299c9e58356bda278e2f32bef2cdi0", + "b1ef66c2d1a047cbaa6260b74daac43813924378fe08ef8545da4cb79e8fcf00i0", + "47c7260764af2ee17aa584d9c035f2e5429aefd96b8016cfe0e3f0bcf04869a3i0" + ], + "content_length": 793, + "content_type": "image/png", + "effective_content_type": "image/png", + "fee": 322, + "height": 767430, + "id": "6fb976ab49dcec017f1e201e84395983204ae1a7c2abf7ced0a85d692e442799i0", + "next": "26482871f33f1051f450f2da9af275794c0b5f1c61ebf35e4467fb42c2813403i0", + "number": 0, + "parents": [], + "previous": null, + "rune": null, + "sat": null, + "satpoint": "47c7260764af2ee17aa584d9c035f2e5429aefd96b8016cfe0e3f0bcf04869a3:0:0", + "timestamp": 1671049920, + "value": 606 +} +``` +
+ +
+ + GET + /inscription/<INSCRIPTION_ID>/<CHILD> + + +### Description + +Returns the inscription information for the specified child. + +### Example + +```bash +curl -s -H "Accept: application/json" \ + http://0.0.0.0:80/inscription/b1ef66c2d1a047cbaa6260b74daac43813924378fe08ef8545da4cb79e8fcf00i0/0 +``` + +```json +{ + "address": "bc1pnhyyzpetra3zvm376ng8ncnv9phtt45fczpt7sv2eatedtjj9vjqwhj080", + "charms": [ + "vindicated" + ], + "children": [], + "content_length": 106268, + "content_type": "image/avif", + "effective_content_type": "image/avif", + "fee": 1470535, + "height": 839704, + "id": "ab924ff229beca227bf40221faf492a20b5e2ee4f084524c84a5f98b80fe527fi0", + "next": "ab924ff229beca227bf40221faf492a20b5e2ee4f084524c84a5f98b80fe527fi1", + "number": 69994605, + "parents": [ + "b1ef66c2d1a047cbaa6260b74daac43813924378fe08ef8545da4cb79e8fcf00i0" + ], + "previous": "e2619e0fa641ed2dfba083dc57a15ca1d3f195f15d187de353e1576a0cb6e87ci8", + "rune": null, + "sat": null, + "satpoint": "ab924ff229beca227bf40221faf492a20b5e2ee4f084524c84a5f98b80fe527f:1:0", + "timestamp": 1713399652, + "value": 10000 +} +``` +
+ +
+ + POST + /inscriptions + + +### Description + +Fetch details for a list of inscription IDs. + +### Example + +```bash +curl -s -X POST \ + -H "Accept: application/json" \ + -H "Content-Type: application/json" \ + -d '["ab924ff229beca227bf40221faf492a20b5e2ee4f084524c84a5f98b80fe527fi1", "ab924ff229beca227bf40221faf492a20b5e2ee4f084524c84a5f98b80fe527fi0"]' \ + http://0.0.0.0:80/inscriptions +``` + +```json +[ + { + "address": "bc1pnhyyzpetra3zvm376ng8ncnv9phtt45fczpt7sv2eatedtjj9vjqwhj080", + "charms": [ + "vindicated" + ], + "children": [], + "content_length": 116597, + "content_type": "image/avif", + "effective_content_type": "image/avif", + "fee": 1470535, + "height": 839704, + "id": "ab924ff229beca227bf40221faf492a20b5e2ee4f084524c84a5f98b80fe527fi1", + "next": "ab924ff229beca227bf40221faf492a20b5e2ee4f084524c84a5f98b80fe527fi2", + "number": 69994606, + "parents": [ + "b1ef66c2d1a047cbaa6260b74daac43813924378fe08ef8545da4cb79e8fcf00i0" + ], + "previous": "ab924ff229beca227bf40221faf492a20b5e2ee4f084524c84a5f98b80fe527fi0", + "rune": null, + "sat": null, + "satpoint": "ab924ff229beca227bf40221faf492a20b5e2ee4f084524c84a5f98b80fe527f:2:0", + "timestamp": 1713399652, + "value": 10000 + }, + { + "address": "bc1pnhyyzpetra3zvm376ng8ncnv9phtt45fczpt7sv2eatedtjj9vjqwhj080", + "charms": [ + "vindicated" + ], + "children": [], + "content_length": 106268, + "content_type": "image/avif", + "effective_content_type": "image/avif", + "fee": 1470535, + "height": 839704, + "id": "ab924ff229beca227bf40221faf492a20b5e2ee4f084524c84a5f98b80fe527fi0", + "next": "ab924ff229beca227bf40221faf492a20b5e2ee4f084524c84a5f98b80fe527fi1", + "number": 69994605, + "parents": [ + "b1ef66c2d1a047cbaa6260b74daac43813924378fe08ef8545da4cb79e8fcf00i0" + ], + "previous": "e2619e0fa641ed2dfba083dc57a15ca1d3f195f15d187de353e1576a0cb6e87ci8", + "rune": null, + "sat": null, + "satpoint": "ab924ff229beca227bf40221faf492a20b5e2ee4f084524c84a5f98b80fe527f:1:0", + "timestamp": 1713399652, + "value": 10000 + } +] +``` +
+ +
+ + GET + /inscriptions + + +### Description + +Get a list of the latest 100 inscriptions. + +### Example + +```bash +curl -s -H "Accept: application/json" \ + http://0.0.0.0:80/inscriptions +``` + +```json +{ + "ids": [ + "dca3da701a2607de6c89dd0bfe6106532dcefe279d13b105301a2d85eb4ffaafi0", + "0e50a465fc0ca415f3cb8a4aac1555b12a4bf3f33bc039f2a4d39f809e83af7ai0", + "934905624f847731e7f173ba70bfa3a1389b0a7fe2a4ffce8793eef2730b9ab9i0", + "50a42e51e6ce0ef76699f017a1017d7b5b6203e67d283c625ba7d1567b2e43bai0", + "65a78bdbc1e01ac02cda181a71304a8d82305bc2a24bf01e62bea4cfff3e2dd8i0", + "05ab6d843099fb30a1da1bbfe31117cb56466b3ba40a4b3f389cc37174d339b8i0", + "47825a32dd6e3de5fd7d97488d755e6d1005e5c8552b9ede5bc67900b074d09bi0", + "737552653d4424a523f8c652710d0f9416561ea67ee25242f8606b49fb428d9ai0", + "1d7d15ab48fccf7011435584556ee9106be71f7073a857689594c143d7899333i0", + "321e4f598ae0f4841af04d1a84f3abafa44802c7d35315ead91b32ffed0f400di0", + "eb1578eaca0a04eaf174296382fc5d77530f0feceb7747938b29c433c21d1afdi0", + "70d6136e949b5f07b6ac7d50aa9aea1fa6573e1b0e4f490170235ac74738bf5ai0", + "aab2c8514876fb81cb28f0f0516620cf189222e0ffc6fe6282863bb846955409i0", + "ef36dd247b98f12d19d15bab92ea7f8491b0766fb0b8074b7606614dbbab6c13i0", + "cec42963619240ede36fb03cd95d8fba883c9c1af72b1e2fc9746151a60729dci0", + "3124d086c59ce2205f52a108e21380e2c98b1ac6a21fc2f457fb5750317997d2i0", + "c2d19ab0d9e508ed20eb6620a4ed6b5700bcee835278eb171ad15e3d9e9cf3cci0", + "6aa9e8efbc0410adebca732a2baa6812bd4d9678771023503d20c8e90f632853i0", + "96fd8d9b06c9d55d57c926889716b05f03e508d05320ffbe052aed38f49a8a4fi0", + "9429a355eecc994380920e8c9a2fd17adcb2e745bc1c8a460ed016d37e02d11ei0", + "196fa44615bd2215e17f428d9cb6ea5de62e4fc6635e45089623f757189cc3b1i0", + "077ccaf7424917873fe217bc45cfe923d20a9732373fc2b08749106569a198a8i0", + "ea5d4f47955e9ac306113ebd616587d2eaef3fb242474fb5819562ec007db32fi0", + "db377bb1c8ad40dfa6bf69b2ff8f5417b419ee6b0657e75060e088b1ec8b1c93i0", + "3c9720eeaad27cad478404905c9d5dcd332878f95dd65fc9912bfd598041af0bi0", + "61ef119d102389c3daaa5c057514f30cf1cd410b7d5c41a28c58a9a902cb265ai0", + "f971ea01b40b35b8548a902e013a3a1b799d4c2c1613d37ef3a994120d65c10ei0", + "6021306cf760dfbb0da58bce59ffbc703db5c7d9b180a3ae5268ce4c5341bf34i0", + "10aebd52ccd20124d5aa1c7d3e52fa81776ac6a3fb79ada582495328fa968ca4i0", + "9ed8d1fc12ab4d4b50c869bda1a38bb0e82b6eb18d2c14ef880aa2bb1757dbf7i0", + "5f5db3b301aa766f1a22f796248b2cceb8c111419bcefb4a3365d8bb1ff6ca05i0", + "15737d13a3583ef3559090431d5ea846e5126963046041b1f4d42b2fcc9a03eci0", + "05fdf04307e006eefec908ef93806f96c472af9e073837f4b1f5ad52e1d719a9i0", + "559280eb5ceefadee232a5b4dcc2c05dddfc1f123293482fef30ab7632855b85i0", + "76939794300cac687832e68253640e69993b46e0b75e5af4678b3c4b2037bb4bi0", + "1967e5170c27e707545ec05624db313735799fc58142e2bd2b475b088b761022i0", + "b320472502a8750fc6b3cb87ac6a0b3eaef402fa5f218f1153f658642e3d1b3di0", + "456b48e7ef556004e4a9a8b98aee8c797d75e1027dc56982ef6936f8627eeda7i0", + "02fb3081fc7317cbec7adc63761ea373ec239c7703a13f5752c3344acf6312eei0", + "75e567e30b84205ca9f5b6280b29581310bed27504949996b64858110d38c5e8i0", + "47214c34652fda5745b56ac80512e7d353db9d949fac9e0e5a6d8b27507fe4c6i0", + "c3ccc1508fb08a6bc487b25e4d5a994ff73cb44251749619c53d87c7626d74c6i0", + "a8ce87da5b67d9782846d2f718058873c51bcdbbee536540266f868bb5376c8fi0", + "47dd14ebf43bc35ff753cb5acf8335eb1acb788d05a2b0b9d83302e16170127bi0", + "ae7c0ebe825c2bf0d5820bc28da095cfa1cb6913a5913142bc327ab985b3dd7ai0", + "cf68e9b9d1967859b7d832a9c815ae3c837c94031dc8e56d848d151ae24e4776i0", + "0fe7d513cf8c19734f84321a3c49d0e0e39255702ba18546740c2bb1a95c5170i0", + "e760f5028719b2130e0d2305c3531174f4f6167282251adebfc968d127b79369i0", + "aa81f43a1412b0d04c2ef825c3829707aa32cf4cca3452077c2819f012905b5ci0", + "cd3675b40f8056c7b816c02a537a6d997912a26302f77cd3d0ad83b657d24e4bi0", + "3f8bf38d3cd3e50693b9bf187e1a374ff9990ba8e8f6337829f0d7312805741bi0", + "415a9516e1dcad84a60cc7d012c2475361d575f713b1d3aa16f982d2e43e330di0", + "6db363228406a71744cbf9b86e2b58c21b4f2dd0a0ad0affa211b32af20e8809i0", + "ff1aa5bec2a626c8b6f90e6765ceb227d44565a90f9e54cf05f5360ef6e33708i0", + "161191b5de6a1b1ed53e816545176d47e214c50711474b1a4e3ab34d70634189i0", + "f3f7488bc66000965a36f4ddf000c3d3ca3cf94d7cd4defaf3ca0b68e86b3af8i0", + "b2fb38073ade49a3f0f2522a15f4f63122a60d03a9eaed5c1c4198d339a32a1ei0", + "2f99c317739ca8cb6eb904915648ac2044f815d01ecfae6762ecf3885ee3778ai0", + "9d30636a2c5b6e064e6868fe796986014ac4cf9ea7a859d12e2dea07128c04f5i0", + "62ea57535dbe1c748d79c693e507d787af60076eaec7629365c31f52607f1279i0", + "9540b2f1d24ad5750f155ee232b03e4bfe258fde8c396844471bd595cbf0d4e9i0", + "98bfe331d267749357857e86433f974595bdb1d76ff60d35e576b217d7eae4e3i0", + "00bea5fcc8723ce5d177ca1cd4e87f7f2792fa3043231554d584b869d791a9e0i0", + "2ca9e9aedb2bf622c5c499701ce74a1dae456569082704ade20ba125019ea5f9i0", + "83290426401ac68ce29306f6a1ec5c86c69ce66049a1d85fefa49088a0f5a11fi0", + "ebc452becb7438e43281317045ab5c675376486a9344625b5dec09d5a65a9905i8", + "ebc452becb7438e43281317045ab5c675376486a9344625b5dec09d5a65a9905i7", + "ebc452becb7438e43281317045ab5c675376486a9344625b5dec09d5a65a9905i6", + "ebc452becb7438e43281317045ab5c675376486a9344625b5dec09d5a65a9905i5", + "ebc452becb7438e43281317045ab5c675376486a9344625b5dec09d5a65a9905i4", + "ebc452becb7438e43281317045ab5c675376486a9344625b5dec09d5a65a9905i3", + "ebc452becb7438e43281317045ab5c675376486a9344625b5dec09d5a65a9905i2", + "ebc452becb7438e43281317045ab5c675376486a9344625b5dec09d5a65a9905i1", + "ebc452becb7438e43281317045ab5c675376486a9344625b5dec09d5a65a9905i0", + "d431778a7290951d463f356f637a801c4c8b77767f2f53686176202bfd3a1af7i0", + "8bc9f9d88f91d851eeb84481fb33baabd6ea172c0c5152e5b8d4140f8102671ai0", + "12454b1620904b63e8c47f31e17939735515923e674fc42f299b5466258b640ai0", + "a67d21421a27918ab052c4dee3dcca86ad0610ccf4a449f98d3316008953d54ci0", + "920512aa32b5d525495832a3146f32efb0bfa308519bc3e1d5bc151ec6c9412ai0", + "8defe7abfd7d4f9dc94be83ca0b2f823f196a80ea37ebe217702368ffd2c7807i0", + "6b26e994bdabb558d41f5824b3d427ec628e7a1e7596ac20dcf05e889a994fd2i0", + "327610662171136ee252724b6860d0b64b45f81cb2bf8a0606256db730946a39i0", + "e01ec43055caa4bdb73f300076501deb85780891181d07773231db700a7d2099i0", + "9c2dc67e959bf949396d31157f16b6d60e4469ff43ba1ed44957d197f3ebf78bi0", + "89126f596c644721edc65ef293730078f16f0894baf29a1d807aab4afc013d72i0", + "3ea79b7a166ed230046e3d890d6c39a7c64dec8443de933860534449fa3180a0i0", + "2f1b248a957dcfb442b89c4684f65ba7bab7061cd0dfa4eaae8f5c65d7b41985i0", + "ac0fb3d3d301a28d3d979ca7f522eb4fdf0b0fed9d8062ff4944d5dac353092fi0", + "d0ebf39a32d409eb92bdefb354a99408367709830d03d4c6bc36786e79782720i0", + "7bfa54eb0141a93cd9cd2d3a6a52de5c1116653035bd8179608e115c823b7574i0", + "14fbc773b0b7635c4fd598c102a0a5019aee75a1184ac8d189c59478931ba6e9i0", + "0101a253d50138b4ef67c4036246df3e2a74d70874ad3c8f943af54e4b37648ci0", + "d5b41d45f3c45e2bdf36a415d9ece493cca23e762ff5de34a6abcf79936fc614i0", + "4c92503dc1f38bf77c2b1219504bb6eb82dd1d8af172f84d86d433f7bc557d4fi0", + "7b6df715cf052fdf28dbe213fada59b910c9e339137f0bd35698f23d0140a826i0", + "da06b7b4d298c837566b8daafae7cba1d4be19ca3b9e63d867cc2a9f06dd6315i0", + "958655c68793fe9e4dc6c8155c28c6b14c0ed58c5aa340d6bd6ef085134d3fb7i0", + "f8dbba73e65bf996e7cd8388ad85f7303f2caa52acf1ce793d8141dd9f70f6e6i0", + "8d166e2e3ea2a9e5d6460964d533b61656b6a3d671e5f046030319bb73f93e9fi0", + "2a60d61dff2ba192ca81614f8f0bda6c24eaac2c45f879ef84302e8c4c859bc9i2" + ], + "more": true, + "page_index": 0 +} +``` +
+ +
+ + GET + /inscriptions/<PAGE> + + +### Description + +Pagination allows you to choose which page of 100 inscriptions to return. + +### Example + +```bash +curl -s -H "Accept: application/json" \ + http://0.0.0.0:80/inscriptions/9 +``` + +```json +{ + "ids": [ + "6fa8b4d1840fdd2172b51a40bde3c8b88ff2ca8b668e56fe05edb1d5eec91fc7i0", + "c6f11a3269e7ea108abb9d596c4273067f33f7e951bb4762b915a6c3c3e1ebc6i0", + "24829232f529c1c4d1bfc5c1c1410313c6388c1db14137fdc351f8659eab72c6i0", + "c068402416ec57e773d9d072ad51950b77359eddbf515a775bc6c70bf75869c6i0", + "3ffdf269a5a6a306c6e2e03b73b505a4f2dac3e0708257bca37c12d2ceec3ac6i0", + "f505cc5a01e603bee41e3986c0bfe020cd4054cbdfd0a35b57d89e375ba1e6c5i0", + "3caeb09bc1a6c7e3ac33528f69b9b10755072aac2c7b6b4f58878df45572ccc5i0", + "2233ee78d07be90ae18d12d51cc89734eb691b550b687c1547b0791de668b2c5i0", + "86475391a0e7f13f3b475e3b4aedb8ada36b63bf9bc4f9ac9203fb083a39a2c5i0", + "18fa7b8a0949b57fa4798ccf48e4ba4a16ecb14651edd5a5adc3806eaea0c9c4i0", + "fb6a338c0de40e88e03e7ae5231b036e5f452343db128b849049c2e63d0bc6c4i0", + "374e71e371dfedcfa2f9ac1d6f2d0664effe46ca27907792e396a3176a82a3c4i0", + "bc2b2fef1231c07232cd1333978366255e317e000a04c050262a7d71eaab0cc4i0", + "d627b48539c497f768279669be7690af5af8f302bfb2641989dacce8c4eed8c3i0", + "632cf2db36977e4e091ed50d61185ad78d97e7a6c6ba468b844bfd7ac9b8aec3i0", + "2fc44592a0d8924c8f48c9fcea8b189f9008f2795380446c0d13a9e452f284c3i0", + "2e84632f9f2965d8648a36e2695070e3f9a06fab1fa72176d95652a19d6d3dc3i0", + "c78e74a90bb23e55d23b221d6f184581d75f0e97acd94b6ab9c2536bae79f2c2i0", + "b4eb0dc05c24f48105d80c38c2ced8789c7910960d07db3e7326cbfae5ded9c2i0", + "5f166abe3f70f72479518451f11d67b6217a67e539c08440f844c6f71f2ea1c2i0", + "32c2d37d9bd7f6a019e48bc8bbcd0b07cee07314724f517935b1e0ff490e5cc2i0", + "0876e126bf57724045c799b0f1f6ae206d2bd15c4533212ec243951f03d834c2i0", + "6492faedbf75e28c4637b6a1e518d063c0da130c461bb193bf7215364c7bf5c1i0", + "be6f1f3e8ac1841f05dc0d67b650890dc845fbfd2d3833f48a0adb5016a6a2c1i0", + "1cb2cb5519aea30e3921d59862bb1ca7d2a61430fdf6b64dc2d84a35fcc52dc1i0", + "00d0f2dab82c0f1ba5208cd95cf204505617cdbaab854675875035f584fc0fc1i0", + "4fd6ea5ecc0660d4b238deeeef7c7a238ed324a5343e5a83d0cd34d0cba7f0c0i0", + "cb1d5b0b9c88e1cd2646939e2809119ba857770e0aacfa069ecf992745435bc0i0", + "30394ffad8c25f93083e9044b3faca9fdcce9610af522a3d72c8bf6478e612c0i0", + "2c80a5b7628e1cba9b890d4946d202fa9d534e0d4edb575ca18fc8fede1d05c0i0", + "e3bca997a4494d2c43b441eefda53ec1c63277fb79e93204787d3733bf9f91bfi0", + "7efeed6060c4a0749bd537b36d469fd874e66914b661de992a053e4702d618bfi0", + "39cc481cad92dfbe5a7db974a8f40f0b945ec0a10cf0e525a1e40214ae9b9ebei0", + "776725263fec5b995932dde0c79a511838b2f4da976d767ec357490d8e5142bei0", + "01d5456b25bf80cf0bc661f5fe65167382cb67c324ab88f9a622c0722f3934bei0", + "6cd9d02f08c818eca61fd40362855dce8157af0708460023710b2982053b2fbei0", + "ff4f062a8e1fba6d5089a7517bfadf996a24a79181cfffa479fb5142227c0bbei0", + "da23c5f3ca73c51fecdbfb7a77f028eb269bc438192e08fa7828850f7907b9bdi0", + "804a382fe000066845dd2f53bb33d880dce201b0595da73843f115d85f789dbdi0", + "3a837c80348691f965dbacf9414498c19eea184be8872509830ddc8e555611bdi0", + "d87bdf8547ff587af6ab4e9ba58cfabd81e9dbae29ebee7f91ee4ce504b1e7bci0", + "47f448eab72fa27e3ecd48cd9366f3900e13e3f385081a63027c3252452dcebci0", + "f98248bd62d1893623d07789d2b77c76c726343272fe33cffd0598496792bfbci0", + "9f4f89d78bf18eec65fad5a7e1e4b48023733678df1f831f762713aa28a7adbci0", + "99603a91e9c394b8a08e41292afa612773054a1852ad50b70b926e8ed5ff98bci0", + "ab9a8bc85f80436eb801f0b44525e735949b702b88165f276d9d5370a08792bci0", + "68a66f966af6a8df8a697d026f53ac3d1bbf16fe60e4c00046c38ca42e4c6abci0", + "f85395c84a44416973091c7b5b54093511a4e420d79b8a95f25392f60ee164bci0", + "0d94b03575c0abcd9b50463402c57c05a8fb13fdc4838b3ea38fdb4214a93fbci0", + "9101836abf01e3c2ec3b131bd392063aab15aafc15c83331e33bd5f27bddeabbi0", + "a6b1b98105d3a8b6552e191e0bc300ac432bdba02b87d7e69cca7a5f22e9cebbi0", + "38031a62f117119561f095109367359b1ec5b513cce605e99d3ad4fb3d73ccbbi0", + "518505a149382542af4a249a0ea3e8393eb11baaf1e607bb7fa089ccb0acb7bbi0", + "d6033366e191c597b5d060ccd11213625f7ca276a8dd3649db9463c401d654bbi0", + "23c94df33db29f2068237528c50bccb9af14dabeb1b4c370c1ce2cfaf2bb12bbi0", + "11407eeb6ecd4b5f721d3bdbb24d80c57bf978438466d44a37f4400dcf40dcbai0", + "26fd15fe036f3ff842e060207150594d5327963a5af729d9d7bb37f9b27cc9bai0", + "c85d49988d0a9e63b57a42b0f43b085ac848b4eec3c7567c6ff9835b28b7bfbai0", + "6f9d8c063ebd8777d42609563d5a2753739ba9822afdbd3f30248aa3622c1bbai0", + "59a5c6c8ebf33e8af27c5ca3a1fc34c6ec4a3933024431d74a7107c4cdb518bai0", + "113a792a0665cc766fe1725e94da88af51d637f0b4b2d8bab8acefc60a7fa2b9i0", + "74f75991f2f1f877c01834c8840778a67a66403ec6fe6db4889bd773a0c8f2b8i0", + "1aea70b0b26f38543f5ac323c88287b8b128f275eac1b26e316a86e14bf6c4b8i0", + "fb24445c829b8e9739be2153bf44f8962191c9ef470fa5a0a8cf6014d3939ab8i0", + "4f7bd6fb95500aad569bd9772f49545f997ffed98782938e6d030d1f0ea482b8i0", + "340716bc1585d9b57fc6c21e298caed04c84b27bd45873799b31b63d7fb965b8i0", + "cc515eb5a3125b80a8d7a2ac8e0ba54206185715332ebe6434dfbc86661053b8i0", + "e1ad8b866a5b25b67ccaf2b4e63eddb02b24e2a7abd8c3fd2c5d4ae488f83bb8i0", + "bd723e4bc055e8a43d52e80041664b94dd24a7e1a1c4aa02f39841596a0d76b7i0", + "45efc579e0fbbc539eeaf6fedc30fdd156fca6e32d7d0fff87c568b411a651b7i0", + "6ff468ac685ea84a44977322e23371aee5c6eb75d35207a60dd8b43d32632db7i0", + "9adda4d80df93b592ed215aee39da04fe4a43aec06a97f7228b483a747f4ebb6i0", + "adf97725b496134ebfd0eaaceb63f23d94052a585f557206f33443c2d659e6b6i0", + "41565db258d48adc4e0ff3467534890ee6a12beaed5378847667735affb8e2b6i0", + "fab5be5f8860e29eb394e56bd0a668752c346d1bdda73dc6a2fc2e824a17dbb6i0", + "1307d9531f2759ffcd125bdaf31ed9116c103a991a17d5b43b2e41a7e17460b6i0", + "5494d587b738c901b727c39628d94eb021a836bd78e82b20f6e331ed5c2850b6i0", + "6e98fb69311cf79bd271b13411df9e6b6138705fd08db20fe36a897eb4b513b6i0", + "f6f5d494bd9211ec6b71e9270f4a87237647e7f655ce7c10392fe1c80d8affb5i0", + "7fe37c78b2be6788af0fe810d5b6aedb1bb9c166b70667105e43de13234ee6b5i0", + "8093e0684c094a22b23f328b1dbd50c487c3ab37bc230de456a12b7fde95bcb5i0", + "7511c5ef23ab23f8e009e368b7954c4ed7e67a7a1cd94bae99b7d93a192a90b5i0", + "c98658f7731c9b5342c6a51f0860fec09fbcab9867b986d4704736abf1b0f6b4i0", + "b56001aa7fc59eb40068ea41e0f35a54f4d73c3483cd69ae0c26bb95dfc9e9b4i0", + "4147fbe40586287b1e6144c066731e43959e1aa7d3c7c8ea301ee44fd0b37fb4i0", + "3d43b7b45e4c0e062b21147be0ebdd68f9094f4e9c7b8a686aeb2948b40fbfb3i0", + "6e66c9e03e18250806515a3a60e4a6012f37e87aa1446a679ade384c7e55a3b3i0", + "8215caa5d781be0d5fae9ce7cb1a04efa17f82fb66cb2fa99e4c7bb1a2f479b3i0", + "ce288cac29042474740fa477163767a0fcf74b228e48748630ac7193118429b3i0", + "6d35d614a3574e85d80e27fdc5854a055c484dbf09f155411e279a839aa8ddb2i0", + "906804e50f92a51329b5009d65e5f6e3c32e512279c835c3171ea6765eaca6b2i0", + "70bd0c3531d62ab836187dd956e1e3fb7ef9903124b818a78e5ecd5198f5a3b2i0", + "92c2668efad88467edded7ffc50fb05a063e7b2b555ccc2073f41d599bb037b2i0", + "e97700fc461598ac01bcb2b74cde9ee31e608bfc7f53047e9e494697509f1fb2i0", + "f9d7f767ae23e67ccb9ffd21d9f83ef9a7b6617f5988a08481e1f722de05d1b1i0", + "262f07835303d1e3a8dce57c93488ed1512ad8ed633c9f129c1bc82535c99ab1i0", + "4ea5e8e9cc2c7414d2652c8db87ef556b48e61d60f68cef9c319eb87566e3db1i0", + "acfae264071fa0bb8bd7875e2d607ad48fac549c0817c2dba40858ee95571eb1i0", + "ed150d8980b923b214b8ea115a31933bbebf82666f93c68a1e11ebd3fee3d9b0i0", + "d9ea50a1c374d2feaf87a4ba82967aab419c1ecc4caac3964f69dac7323ca0b0i0" + ], + "more": true, + "page_index": 9 +} +``` +
+ +
+ + GET + /inscriptions/block/<BLOCKHEIGHT> + + +### Description + +Get inscriptions for a specific block. + +### Example + +```bash +curl -s -H "Accept: application/json" \ + http://0.0.0.0:80/inscriptions/block/767430 +``` + +```json +{ + "ids": [ + "6fb976ab49dcec017f1e201e84395983204ae1a7c2abf7ced0a85d692e442799i0" + ], + "more": false, + "page_index": 0 +} +``` +
+ +
+ + GET + /install.sh + + +### Description + +Installs the latest pre-built binary of `ord` + +### Example + +See [wallet.md](wallet.md#installing-ord) +
+ +
+ + GET + /output/<OUTPOINT> + + +### Description + +Returns information about a UTXO, including inscriptions within it. + +### Example + +```bash +curl -s -H "Accept: application/json" \ + http://0.0.0.0:80/output/bc4c30829a9564c0d58e6287195622b53ced54a25711d1b86be7cd3a70ef61ed:0 +``` + +```json +{ + "address": "bc1pz4kvfpurqc2hwgrq0nwtfve2lfxvdpfcdpzc6ujchyr3ztj6gd9sfr6ayf", + "indexed": false, + "inscriptions": [], + "outpoint": "bc4c30829a9564c0d58e6287195622b53ced54a25711d1b86be7cd3a70ef61ed:0", + "runes": {}, + "sat_ranges": null, + "script_pubkey": "OP_PUSHNUM_1 OP_PUSHBYTES_32 156cc4878306157720607cdcb4b32afa4cc6853868458d7258b907112e5a434b", + "spent": true, + "transaction": "bc4c30829a9564c0d58e6287195622b53ced54a25711d1b86be7cd3a70ef61ed", + "value": 10000 +} +``` +
+ +
+ + POST + /outputs + + +### Description + +List information from a list of outputs. + +### Example + +```bash +curl -s -X POST \ + -H "Accept: application/json" \ + -H "Content-Type: application/json" \ + -d '["bc4c30829a9564c0d58e6287195622b53ced54a25711d1b86be7cd3a70ef61ed:0", "bc4c30829a9564c0d58e6287195622b53ced54a25711d1b86be7cd3a70ef61ed:1"]' \ + http://0.0.0.0:80/outputs +``` + +```json +[ + { + "address": "bc1pz4kvfpurqc2hwgrq0nwtfve2lfxvdpfcdpzc6ujchyr3ztj6gd9sfr6ayf", + "indexed": false, + "inscriptions": [], + "outpoint": "bc4c30829a9564c0d58e6287195622b53ced54a25711d1b86be7cd3a70ef61ed:0", + "runes": {}, + "sat_ranges": null, + "script_pubkey": "OP_PUSHNUM_1 OP_PUSHBYTES_32 156cc4878306157720607cdcb4b32afa4cc6853868458d7258b907112e5a434b", + "spent": true, + "transaction": "bc4c30829a9564c0d58e6287195622b53ced54a25711d1b86be7cd3a70ef61ed", + "value": 10000 + }, + { + "address": "bc1pkc2cdnm6xermt2vzxg9wwcur5prgpl6pms3xf9ydtyax5pnqsgwqvuu5cq", + "indexed": false, + "inscriptions": [], + "outpoint": "bc4c30829a9564c0d58e6287195622b53ced54a25711d1b86be7cd3a70ef61ed:1", + "runes": {}, + "sat_ranges": null, + "script_pubkey": "5120b61586cf7a3647b5a982320ae76383a04680ff41dc2264948d593a6a0660821c", + "spent": true, + "transaction": "bc4c30829a9564c0d58e6287195622b53ced54a25711d1b86be7cd3a70ef61ed", + "value": 483528 + } +] +``` +
+ +
+ + GET + /outputs/<ADDRESS> + + +### Description + +Get UTXOs held by `
`. + +### Query Parameters + +#### `type` (optional) + +| Value | Description | +|-------------|-------------| +| `any` | return all UTXOs | +| `cardinal` | return UTXOs not containing inscriptions or runes | +| `inscribed` | return UTXOs containing inscriptions | +| `runic` | return UTXOs containing runes | + +### Example + +```bash +curl -s -H "Accept: application/json" \ + "http://0.0.0.0:80/outputs/358mMRwcxuCSkKheuVWaXHJBGKrXo3f6JW?type=cardinal" +``` + +```json +[ + { + "address": "358mMRwcxuCSkKheuVWaXHJBGKrXo3f6JW", + "indexed": true, + "inscriptions": [], + "outpoint": "6737d77ee9fba5f37e5f4128b03479209030bf44f78ffa3f4e94bf9783691b00:0", + "runes": {}, + "sat_ranges": [ + [ + 567775159437503, + 567775159443555 + ], + [ + 1266853954166100, + 1266853954177531 + ], + [ + 1210436862054339, + 1210436862084993 + ], + [ + 690914221328806, + 690914221362332 + ], + [ + 957021421066680, + 957021421075017 + ] + ], + "script_pubkey": "a91425c70777dfcf84ba7479483e262e1bc7bb0bf4d587", + "spent": false, + "transaction": "6737d77ee9fba5f37e5f4128b03479209030bf44f78ffa3f4e94bf9783691b00", + "value": 90000 + }, + { + "address": "358mMRwcxuCSkKheuVWaXHJBGKrXo3f6JW", + "indexed": true, + "inscriptions": [], + "outpoint": "0cfa3e55f14812c119e47936d95abbb4e04f3094f6d86ac16c6e10018b0b2900:0", + "runes": {}, + "sat_ranges": [ + [ + 1773029001419378, + 1773029001509378 + ] + ], + "script_pubkey": "a91425c70777dfcf84ba7479483e262e1bc7bb0bf4d587", + "spent": false, + "transaction": "0cfa3e55f14812c119e47936d95abbb4e04f3094f6d86ac16c6e10018b0b2900", + "value": 90000 + } +] +``` +
+ +
+ + GET + /rune/<RUNE> + + +### Description + +Returns details about the specified rune. Requires index with `--index-runes` flag. + +### Example + +```bash +curl -s -H "Accept: application/json" \ + http://localhost/rune/UNCOMMONGOODS +``` + +```json +{ + "entry": { + "block": 1, + "burned": 139, + "divisibility": 0, + "etching": "0000000000000000000000000000000000000000000000000000000000000000", + "mints": 33891693, + "number": 0, + "premine": 0, + "spaced_rune": "UNCOMMON•GOODS", + "symbol": "⧉", + "terms": { + "amount": 1, + "cap": 340282366920938463463374607431768211455, + "height": [ + 840000, + 1050000 + ], + "offset": [ + null, + null + ] + }, + "timestamp": 0, + "turbo": true + }, + "id": "1:0", + "mintable": true, + "parent": null +} +``` +
+ +
+ + GET + /runes + + +### Description + +Returns details for last 100 inscribed runes. Requires index with `--index-runes` flag. + +### Example + +```bash +curl -s -H "Accept: application/json" \ + http://0.0.0.0:80/runes +``` + +```json +{ + "entries": [ + [ + "864348:823", + { + "block": 864348, + "burned": 0, + "divisibility": 0, + "etching": "645431123f5ff8b92d057803f2ba786689fd04f2d968d8fb6a4162b63cabc4fd", + "mints": 0, + "number": 119793, + "premine": 0, + "spaced_rune": "ZKSKOOUGYPXB", + "symbol": null, + "terms": { + "amount": 1, + "cap": 87187755, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728166072, + "turbo": false + } + ], + [ + "864348:822", + { + "block": 864348, + "burned": 0, + "divisibility": 0, + "etching": "9d3a1200adfcb2e0ef07e4975120980befcc265cd85b9f2300bc12d4a1ab1beb", + "mints": 0, + "number": 119792, + "premine": 0, + "spaced_rune": "VEMRWZCGQRLL", + "symbol": null, + "terms": { + "amount": 1, + "cap": 183543298, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728166072, + "turbo": false + } + ], + [ + "864346:427", + { + "block": 864346, + "burned": 0, + "divisibility": 0, + "etching": "2acaba44a6dc31cc5f8a8f4ee3a10eb9ca74e47d62975709cb8e81723d91a20d", + "mints": 0, + "number": 119791, + "premine": 0, + "spaced_rune": "LBQPCHACURXD", + "symbol": null, + "terms": { + "amount": 1, + "cap": 12894945, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728165011, + "turbo": false + } + ], + [ + "864343:2413", + { + "block": 864343, + "burned": 0, + "divisibility": 0, + "etching": "6698cd13f630107ccc4b3058cc09b1718aa435e8f9c4eba6b08eea5d13ee809b", + "mints": 0, + "number": 119790, + "premine": 1000000000, + "spaced_rune": "BABY•LEN•SASSAMAN", + "symbol": "Ⱡ", + "terms": { + "amount": 100000, + "cap": 11000, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728162943, + "turbo": false + } + ], + [ + "864342:2591", + { + "block": 864342, + "burned": 0, + "divisibility": 1, + "etching": "095513866c6e7aca84a39f403caac493eaa2f53eda848aaee3e96463571ec6d6", + "mints": 0, + "number": 119789, + "premine": 30000, + "spaced_rune": "COMPLETED•IT•MATE", + "symbol": "⚽", + "terms": { + "amount": 100, + "cap": 299999700, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728162376, + "turbo": true + } + ], + [ + "864338:4768", + { + "block": 864338, + "burned": 0, + "divisibility": 0, + "etching": "0d04505188efc69d4e2cb389607663ff556c062e1e2f8c890bfc598c637700ab", + "mints": 0, + "number": 119788, + "premine": 0, + "spaced_rune": "IJEIKMFKELRFRGRGRGEFREFGR", + "symbol": "d", + "terms": { + "amount": 211, + "cap": 554553, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728160156, + "turbo": false + } + ], + [ + "864338:4767", + { + "block": 864338, + "burned": 0, + "divisibility": 0, + "etching": "e0490721505254c83a69ce1411b1659b6ecd0690751cf43ac45240ca7d3ab4fb", + "mints": 0, + "number": 119787, + "premine": 0, + "spaced_rune": "CQHMUFFTWWPF", + "symbol": null, + "terms": { + "amount": 1, + "cap": 14372222, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728160156, + "turbo": false + } + ], + [ + "864338:4766", + { + "block": 864338, + "burned": 0, + "divisibility": 0, + "etching": "ada836a0e9c834977161543ba7bace0b552e55f88da0398626b1c49a170502dd", + "mints": 0, + "number": 119786, + "premine": 0, + "spaced_rune": "KJMKPVMKREMVBVBFBVFD", + "symbol": "3", + "terms": { + "amount": 332, + "cap": 211222, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728160156, + "turbo": false + } + ], + [ + "864337:4402", + { + "block": 864337, + "burned": 0, + "divisibility": 0, + "etching": "ed45aaf2e9b82d55e35a8d0654d0bb044d1d3e2fdd3eb8787d572854316c53c2", + "mints": 0, + "number": 119785, + "premine": 0, + "spaced_rune": "JNJKMLKMNJCMPMCESCVDSV•DV", + "symbol": "2", + "terms": { + "amount": 3222, + "cap": 1111111, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728160097, + "turbo": false + } + ], + [ + "864335:913", + { + "block": 864335, + "burned": 0, + "divisibility": 0, + "etching": "435cc412c946ced0a5ae5a50ee41d2b541f06f09b6f587619507dfbcc61b8842", + "mints": 0, + "number": 119784, + "premine": 0, + "spaced_rune": "UOBYCVAGPLNO", + "symbol": null, + "terms": { + "amount": 1, + "cap": 194090811, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728158372, + "turbo": false + } + ], + [ + "864335:912", + { + "block": 864335, + "burned": 0, + "divisibility": 0, + "etching": "79d77e44d66af6ec82ff7970eb3f15b9537408e3888ed0348a265810e99ddd3a", + "mints": 0, + "number": 119783, + "premine": 0, + "spaced_rune": "YNJMQPGPUGWN", + "symbol": null, + "terms": { + "amount": 1, + "cap": 71782828, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728158372, + "turbo": false + } + ], + [ + "864335:910", + { + "block": 864335, + "burned": 0, + "divisibility": 0, + "etching": "b014db8f651ec05a1f261f3569c66973318787ad4c7410d6677fc6fcc45e5cfe", + "mints": 0, + "number": 119782, + "premine": 0, + "spaced_rune": "FDLQGMGRYAMF", + "symbol": null, + "terms": { + "amount": 1, + "cap": 135966360, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728158372, + "turbo": false + } + ], + [ + "864335:909", + { + "block": 864335, + "burned": 0, + "divisibility": 0, + "etching": "bd649ba830b262ddcf24b0d6da5091f2dbf1276af26ad0809b65a95c42ddbec2", + "mints": 0, + "number": 119781, + "premine": 0, + "spaced_rune": "LBPOUDNUAIDK", + "symbol": null, + "terms": { + "amount": 1, + "cap": 128338720, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728158372, + "turbo": false + } + ], + [ + "864335:908", + { + "block": 864335, + "burned": 0, + "divisibility": 0, + "etching": "4ee02e12ba76c8c85208510e078810efbb3843fdaa1323d4e84f40a753d97380", + "mints": 0, + "number": 119780, + "premine": 0, + "spaced_rune": "RNVHGUYHAUCM", + "symbol": null, + "terms": { + "amount": 1, + "cap": 3346818, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728158372, + "turbo": false + } + ], + [ + "864335:907", + { + "block": 864335, + "burned": 0, + "divisibility": 0, + "etching": "c9b47a71a2a552450f6259262fc0c23c45148fccb52ee32cd5bb668a467a9f5d", + "mints": 0, + "number": 119779, + "premine": 0, + "spaced_rune": "RTSQQFKTEEBX", + "symbol": null, + "terms": { + "amount": 1, + "cap": 85692692, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728158372, + "turbo": false + } + ], + [ + "864335:906", + { + "block": 864335, + "burned": 0, + "divisibility": 0, + "etching": "df772301fef3107549d200fea54f47e46d6aae197f85e93b0068749640028055", + "mints": 0, + "number": 119778, + "premine": 0, + "spaced_rune": "IWHXSPKPYQOX", + "symbol": null, + "terms": { + "amount": 1, + "cap": 166869547, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728158372, + "turbo": false + } + ], + [ + "864335:905", + { + "block": 864335, + "burned": 0, + "divisibility": 0, + "etching": "186049ed6091d0a4d9e1abf6d436a6af7bc7603a33c71031b8bb0ba02f386b3a", + "mints": 0, + "number": 119777, + "premine": 0, + "spaced_rune": "OHDKZWZHYLVL", + "symbol": null, + "terms": { + "amount": 1, + "cap": 189310557, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728158372, + "turbo": false + } + ], + [ + "864335:904", + { + "block": 864335, + "burned": 0, + "divisibility": 0, + "etching": "74e72d9c58ce6300807d1ca6343fa95f5fa34f3d7e29fc95a94b553ff4c66b36", + "mints": 0, + "number": 119776, + "premine": 0, + "spaced_rune": "NSZNPZDDFYCT", + "symbol": null, + "terms": { + "amount": 1, + "cap": 72959668, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728158372, + "turbo": false + } + ], + [ + "864335:386", + { + "block": 864335, + "burned": 0, + "divisibility": 0, + "etching": "76e81c2a204074d61869f58ce86bf8ecfe66f1213bd444c4f22c6f638a401ef9", + "mints": 0, + "number": 119775, + "premine": 0, + "spaced_rune": "NTOOWMNTOOWMNTOOWM", + "symbol": null, + "terms": { + "amount": 1, + "cap": 1000000, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728158372, + "turbo": false + } + ], + [ + "864334:4073", + { + "block": 864334, + "burned": 0, + "divisibility": 0, + "etching": "6c132c6b69ff19d3dbbd0165bcf2fb5db9bba717824a3ff93e94e976b7da5f9e", + "mints": 0, + "number": 119774, + "premine": 0, + "spaced_rune": "HIDDEN•SELDOM•DISEASE•WISE", + "symbol": null, + "terms": { + "amount": 1, + "cap": 1127, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728158138, + "turbo": false + } + ], + [ + "864334:4070", + { + "block": 864334, + "burned": 0, + "divisibility": 0, + "etching": "adcbc4dc91e0b354baacb37be52e187fab2cf619c43f0675b26c5e7d58ad1ded", + "mints": 0, + "number": 119773, + "premine": 0, + "spaced_rune": "TYDSJXISYECCOQYYSS", + "symbol": null, + "terms": { + "amount": 1, + "cap": 2361833545833, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728158138, + "turbo": false + } + ], + [ + "864334:762", + { + "block": 864334, + "burned": 0, + "divisibility": 0, + "etching": "259fc5e99770c5d2ed0547571981ad191554282e6ab4b2a6eb4083c392edc1cb", + "mints": 0, + "number": 119772, + "premine": 0, + "spaced_rune": "BEGCOAJVXEHW", + "symbol": null, + "terms": { + "amount": 1, + "cap": 38385326, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728158138, + "turbo": false + } + ], + [ + "864334:433", + { + "block": 864334, + "burned": 0, + "divisibility": 0, + "etching": "4d324233f38c0cbf36bf1a76e161cbe0ff9f0efb6ee78d94dffdd5f16ec7e8ba", + "mints": 0, + "number": 119771, + "premine": 0, + "spaced_rune": "BEDIALAMDARBEDIALAMDAR", + "symbol": null, + "terms": { + "amount": 5, + "cap": 100000, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728158138, + "turbo": false + } + ], + [ + "864334:432", + { + "block": 864334, + "burned": 0, + "divisibility": 0, + "etching": "f7b804462b33fd468ef3b171071094f3498968b0a488d08489e16058d470d809", + "mints": 0, + "number": 119770, + "premine": 0, + "spaced_rune": "RUTHMARTINRUTHMARTIN", + "symbol": null, + "terms": { + "amount": 1, + "cap": 999999, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728158138, + "turbo": false + } + ], + [ + "864334:431", + { + "block": 864334, + "burned": 0, + "divisibility": 0, + "etching": "51ce542a9557a4894b0dfd705d13268682aa16c83e5eee9c5b1ba4d67113def8", + "mints": 0, + "number": 119769, + "premine": 0, + "spaced_rune": "ULTIVERSEULTIVERSE", + "symbol": null, + "terms": { + "amount": 1, + "cap": 7777777, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728158138, + "turbo": false + } + ], + [ + "864334:182", + { + "block": 864334, + "burned": 0, + "divisibility": 0, + "etching": "195dc952cb7c9e8a5c370fe098b4aa1d8bba8225bb4706ee7243b8e3c43f2b32", + "mints": 0, + "number": 119768, + "premine": 0, + "spaced_rune": "NUQHRKVWSYEA", + "symbol": null, + "terms": { + "amount": 1, + "cap": 3063483, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728158138, + "turbo": false + } + ], + [ + "864333:3461", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "65078629f16f0ce11a91da3de877a0ac5a25b5ed4c68d0ba3f6a8e75eab5f871", + "mints": 0, + "number": 119767, + "premine": 0, + "spaced_rune": "FMTJRFVGNHVZNUCB", + "symbol": null, + "terms": { + "amount": 1, + "cap": 5541274870406, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:3458", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "8471194b68cfab89a9d6112caf62f97819172d397e91674ec5413ad8f27b2828", + "mints": 0, + "number": 119766, + "premine": 0, + "spaced_rune": "WEELZZLGHGDRTO", + "symbol": null, + "terms": { + "amount": 1, + "cap": 507317119633, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:3440", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "90d530d1daf7f1f6ece388a846fe8173a427f71b7e1c5cfc1c035dcd1fc0b017", + "mints": 0, + "number": 119765, + "premine": 0, + "spaced_rune": "MIIOBBPODENFJ", + "symbol": null, + "terms": { + "amount": 1, + "cap": 503174265447, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:3437", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "5c0d2bbf9543cd50293fd6671d94502fa08c8c6d11431e0eee4ac3aedbdbc5bc", + "mints": 0, + "number": 119764, + "premine": 0, + "spaced_rune": "TASTE•RISING•FULL", + "symbol": null, + "terms": { + "amount": 1, + "cap": 4812, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:3434", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "1766810d3f53cfce81a4e0620c21e8e4643c7a40936dbafa6e88339c025fb5f6", + "mints": 0, + "number": 119763, + "premine": 0, + "spaced_rune": "REGION•MARK•LOW", + "symbol": null, + "terms": { + "amount": 1, + "cap": 2470, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:3433", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "f2a6805462cebffc6eb5855d1205dedf9c7f746a7dfd420c153011bb572f58ba", + "mints": 0, + "number": 119762, + "premine": 0, + "spaced_rune": "QHKKEWPTDMNB", + "symbol": null, + "terms": { + "amount": 1, + "cap": 53660832, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:3432", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "5d2127d84533fc9d486eaec1a2b76b2d349fe63a06a9d14847b667d360af6e19", + "mints": 0, + "number": 119761, + "premine": 0, + "spaced_rune": "IWLUKGYIWMBP", + "symbol": null, + "terms": { + "amount": 1, + "cap": 94339731, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:3431", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "ac4668d63f66c94515dbc2a74faa9152018758a75432cc085a7e7638a24cbc12", + "mints": 0, + "number": 119760, + "premine": 0, + "spaced_rune": "KWUFVEOJVKGQ", + "symbol": null, + "terms": { + "amount": 1, + "cap": 196312580, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:2714", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "c642cd4cc7a075c61d3a32b949217990aa91dfc928f12a2cdba1f2f228c699c7", + "mints": 26, + "number": 119759, + "premine": 210000, + "spaced_rune": "BOUNCE•THE•BITCOIN•CAT", + "symbol": "🐱", + "terms": { + "amount": 1000, + "cap": 20790, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": true + } + ], + [ + "864333:2482", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "cc2415293c275bea4d73ff8f45f68f269686b819de447f50ec6988ac04a62d1b", + "mints": 0, + "number": 119758, + "premine": 30000000, + "spaced_rune": "BITCAT•IS•IN•CONTROL", + "symbol": "🐈", + "terms": { + "amount": 5000, + "cap": 194000, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": true + } + ], + [ + "864333:2462", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "a75f792be155a0b53691289433a6413c1efb1aeaf970f752ee70be3c6e755a06", + "mints": 0, + "number": 119757, + "premine": 0, + "spaced_rune": "FIRST•CAT•EATING•BITCOINER", + "symbol": "🙀", + "terms": { + "amount": 1000, + "cap": 21000, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": true + } + ], + [ + "864333:1142", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "7488c2909e2bb5f39fb836ee1e18c23487d078e48e2420cc11776c8d7931fea5", + "mints": 0, + "number": 119756, + "premine": 0, + "spaced_rune": "AI•CRYPTO•AI•CRYPTO", + "symbol": "A", + "terms": { + "amount": 1000, + "cap": 1, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:1140", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "a024e2d4c4e15eab941376a954bb9176bc95990ba6b2a6d31e5b7c26cd8d7e7c", + "mints": 0, + "number": 119755, + "premine": 0, + "spaced_rune": "SACMKSOKCMPOKMWCLWMCLWCDWC", + "symbol": "c", + "terms": { + "amount": 221, + "cap": 2111111, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:1136", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "d6358e0601130c5ebbdb535aa93bbe2e752fd7fd6eee8601fe5af29e7ff179e1", + "mints": 0, + "number": 119754, + "premine": 0, + "spaced_rune": "XQOFVAHHLCQR", + "symbol": null, + "terms": { + "amount": 1, + "cap": 94964916, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:1135", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "62aa2bd48b0eb8a1c3bb090c6129bdc52a2348f3b8e25a2e2eeaa27313e242af", + "mints": 0, + "number": 119753, + "premine": 0, + "spaced_rune": "YEPWCVNODTII", + "symbol": null, + "terms": { + "amount": 1, + "cap": 39185064, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:1134", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "e3e6a144d3ac57d35f7f141f79ea818bd26a78bf900c2d0aeaa2a95ce68f8c9e", + "mints": 0, + "number": 119752, + "premine": 0, + "spaced_rune": "SDFGJUJTYHTGRSFAD", + "symbol": null, + "terms": { + "amount": 1, + "cap": 5, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:1133", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "97600a89179c0bfd4b7c69bc5f4e9fc2f206124fbc08d4872f18ac6be29a525e", + "mints": 0, + "number": 119751, + "premine": 0, + "spaced_rune": "XQEKAAGEYDXY", + "symbol": null, + "terms": { + "amount": 1, + "cap": 147617461, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:1131", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "8ecabca3a2b1518c67c5ee41c93e7874d1117edfd0b36e46ea68eb83e6f9eaad", + "mints": 0, + "number": 119750, + "premine": 0, + "spaced_rune": "XFHSGMZJEUML", + "symbol": null, + "terms": { + "amount": 1, + "cap": 1014672, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:1130", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "77b1518d7ad77d89118eeb8eb92c120e1732d2e7ce9d6780cda180f5f4968df6", + "mints": 0, + "number": 119749, + "premine": 0, + "spaced_rune": "DJLNUHRYYTGR", + "symbol": null, + "terms": { + "amount": 1, + "cap": 146717679, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:1129", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "4da09c158447950fabd281c7910c6e3f251b9b9a98ab7058e2f4b26304e332ee", + "mints": 0, + "number": 119748, + "premine": 0, + "spaced_rune": "CBAQVALKVMYP", + "symbol": null, + "terms": { + "amount": 1, + "cap": 181932658, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:1128", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "b947075130f5a5f93a5cdfa9a216c76b761ff7cd2fb7ca677b3d00a3ca5d53e0", + "mints": 0, + "number": 119747, + "premine": 0, + "spaced_rune": "POJSRGWQBBWQ", + "symbol": null, + "terms": { + "amount": 1, + "cap": 100105873, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:1127", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "a356dd06600bb163cb4d68bbe601f83d987c3c2cd456e3784616ab297d1843c0", + "mints": 0, + "number": 119746, + "premine": 0, + "spaced_rune": "FMPQPSLKENKY", + "symbol": null, + "terms": { + "amount": 1, + "cap": 82531312, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:1126", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "b6ecdb27bb269949f58ace2ba162726483070e80c140dc60329b5fdbbd3e6395", + "mints": 0, + "number": 119745, + "premine": 0, + "spaced_rune": "GOARBTCEASGJ", + "symbol": null, + "terms": { + "amount": 1, + "cap": 99967467, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:1125", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "abf680ed211d18428ddda208f164539fbf662705bd88d4041575c53e655ed794", + "mints": 0, + "number": 119744, + "premine": 0, + "spaced_rune": "MNBIUEEAKPBJ", + "symbol": null, + "terms": { + "amount": 1, + "cap": 168164931, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:1124", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "74e290bc2ed6b39c887ab3b456f86d91edbadb829936c63bb166d42233527491", + "mints": 0, + "number": 119743, + "burned": 0, + "divisibility": 0, + "etching": "74e290bc2ed6b39c887ab3b456f86d91edbadb829936c63bb166d42233527491", + "mints": 0, + "number": 119743, + "divisibility": 0, + "etching": "74e290bc2ed6b39c887ab3b456f86d91edbadb829936c63bb166d42233527491", + "mints": 0, + "number": 119743, + "premine": 0, + "spaced_rune": "CWTYCFSOTBSU", + "symbol": null, + "etching": "74e290bc2ed6b39c887ab3b456f86d91edbadb829936c63bb166d42233527491", + "mints": 0, + "number": 119743, + "premine": 0, + "spaced_rune": "CWTYCFSOTBSU", + "symbol": null, + "terms": { + "amount": 1, + "cap": 29807122, + "mints": 0, + "number": 119743, + "premine": 0, + "spaced_rune": "CWTYCFSOTBSU", + "symbol": null, + "terms": { + "amount": 1, + "cap": 29807122, + "premine": 0, + "spaced_rune": "CWTYCFSOTBSU", + "symbol": null, + "terms": { + "amount": 1, + "cap": 29807122, + "height": [ + null, + null + "terms": { + "amount": 1, + "cap": 29807122, + "height": [ + null, + null + "height": [ + null, + null + null + ], + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ] + ], + "more": true, + "prev": null, + "next": 1 +} +``` +
+ +
+ + GET + /runes/<PAGE> + + +### Description + +Pagination allows you to specify which page of 100 runes you'd like to return. + +### Example + +```bash +curl -s -H "Accept: application/json" \ + http://0.0.0.0:80/runes/0 +``` + +```json +{ + "entries": [ + [ + "864348:823", + { + "block": 864348, + "burned": 0, + "divisibility": 0, + "etching": "645431123f5ff8b92d057803f2ba786689fd04f2d968d8fb6a4162b63cabc4fd", + "mints": 0, + "number": 119793, + "premine": 0, + "spaced_rune": "ZKSKOOUGYPXB", + "symbol": null, + "terms": { + "amount": 1, + "cap": 87187755, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728166072, + "turbo": false + } + ], + [ + "864348:822", + { + "block": 864348, + "burned": 0, + "divisibility": 0, + "etching": "9d3a1200adfcb2e0ef07e4975120980befcc265cd85b9f2300bc12d4a1ab1beb", + "mints": 0, + "number": 119792, + "premine": 0, + "spaced_rune": "VEMRWZCGQRLL", + "symbol": null, + "terms": { + "amount": 1, + "cap": 183543298, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728166072, + "turbo": false + } + ], + [ + "864346:427", + { + "block": 864346, + "burned": 0, + "divisibility": 0, + "etching": "2acaba44a6dc31cc5f8a8f4ee3a10eb9ca74e47d62975709cb8e81723d91a20d", + "mints": 0, + "number": 119791, + "premine": 0, + "spaced_rune": "LBQPCHACURXD", + "symbol": null, + "terms": { + "amount": 1, + "cap": 12894945, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728165011, + "turbo": false + } + ], + [ + "864343:2413", + { + "block": 864343, + "burned": 0, + "divisibility": 0, + "etching": "6698cd13f630107ccc4b3058cc09b1718aa435e8f9c4eba6b08eea5d13ee809b", + "mints": 0, + "number": 119790, + "premine": 1000000000, + "spaced_rune": "BABY•LEN•SASSAMAN", + "symbol": "Ⱡ", + "terms": { + "amount": 100000, + "cap": 11000, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728162943, + "turbo": false + } + ], + [ + "864342:2591", + { + "block": 864342, + "burned": 0, + "divisibility": 1, + "etching": "095513866c6e7aca84a39f403caac493eaa2f53eda848aaee3e96463571ec6d6", + "mints": 0, + "number": 119789, + "premine": 30000, + "spaced_rune": "COMPLETED•IT•MATE", + "symbol": "⚽", + "terms": { + "amount": 100, + "cap": 299999700, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728162376, + "turbo": true + } + ], + [ + "864338:4768", + { + "block": 864338, + "burned": 0, + "divisibility": 0, + "etching": "0d04505188efc69d4e2cb389607663ff556c062e1e2f8c890bfc598c637700ab", + "mints": 0, + "number": 119788, + "premine": 0, + "spaced_rune": "IJEIKMFKELRFRGRGRGEFREFGR", + "symbol": "d", + "terms": { + "amount": 211, + "cap": 554553, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728160156, + "turbo": false + } + ], + [ + "864338:4767", + { + "block": 864338, + "burned": 0, + "divisibility": 0, + "etching": "e0490721505254c83a69ce1411b1659b6ecd0690751cf43ac45240ca7d3ab4fb", + "mints": 0, + "number": 119787, + "premine": 0, + "spaced_rune": "CQHMUFFTWWPF", + "symbol": null, + "terms": { + "amount": 1, + "cap": 14372222, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728160156, + "turbo": false + } + ], + [ + "864338:4766", + { + "block": 864338, + "burned": 0, + "divisibility": 0, + "etching": "ada836a0e9c834977161543ba7bace0b552e55f88da0398626b1c49a170502dd", + "mints": 0, + "number": 119786, + "premine": 0, + "spaced_rune": "KJMKPVMKREMVBVBFBVFD", + "symbol": "3", + "terms": { + "amount": 332, + "cap": 211222, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728160156, + "turbo": false + } + ], + [ + "864337:4402", + { + "block": 864337, + "burned": 0, + "divisibility": 0, + "etching": "ed45aaf2e9b82d55e35a8d0654d0bb044d1d3e2fdd3eb8787d572854316c53c2", + "mints": 0, + "number": 119785, + "premine": 0, + "spaced_rune": "JNJKMLKMNJCMPMCESCVDSV•DV", + "symbol": "2", + "terms": { + "amount": 3222, + "cap": 1111111, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728160097, + "turbo": false + } + ], + [ + "864335:913", + { + "block": 864335, + "burned": 0, + "divisibility": 0, + "etching": "435cc412c946ced0a5ae5a50ee41d2b541f06f09b6f587619507dfbcc61b8842", + "mints": 0, + "number": 119784, + "premine": 0, + "spaced_rune": "UOBYCVAGPLNO", + "symbol": null, + "terms": { + "amount": 1, + "cap": 194090811, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728158372, + "turbo": false + } + ], + [ + "864335:912", + { + "block": 864335, + "burned": 0, + "divisibility": 0, + "etching": "79d77e44d66af6ec82ff7970eb3f15b9537408e3888ed0348a265810e99ddd3a", + "mints": 0, + "number": 119783, + "premine": 0, + "spaced_rune": "YNJMQPGPUGWN", + "symbol": null, + "terms": { + "amount": 1, + "cap": 71782828, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728158372, + "turbo": false + } + ], + [ + "864335:910", + { + "block": 864335, + "burned": 0, + "divisibility": 0, + "etching": "b014db8f651ec05a1f261f3569c66973318787ad4c7410d6677fc6fcc45e5cfe", + "mints": 0, + "number": 119782, + "premine": 0, + "spaced_rune": "FDLQGMGRYAMF", + "symbol": null, + "terms": { + "amount": 1, + "cap": 135966360, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728158372, + "turbo": false + } + ], + [ + "864335:909", + { + "block": 864335, + "burned": 0, + "divisibility": 0, + "etching": "bd649ba830b262ddcf24b0d6da5091f2dbf1276af26ad0809b65a95c42ddbec2", + "mints": 0, + "number": 119781, + "premine": 0, + "spaced_rune": "LBPOUDNUAIDK", + "symbol": null, + "terms": { + "amount": 1, + "cap": 128338720, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728158372, + "turbo": false + } + ], + [ + "864335:908", + { + "block": 864335, + "burned": 0, + "divisibility": 0, + "etching": "4ee02e12ba76c8c85208510e078810efbb3843fdaa1323d4e84f40a753d97380", + "mints": 0, + "number": 119780, + "premine": 0, + "spaced_rune": "RNVHGUYHAUCM", + "symbol": null, + "terms": { + "amount": 1, + "cap": 3346818, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728158372, + "turbo": false + } + ], + [ + "864335:907", + { + "block": 864335, + "burned": 0, + "divisibility": 0, + "etching": "c9b47a71a2a552450f6259262fc0c23c45148fccb52ee32cd5bb668a467a9f5d", + "mints": 0, + "number": 119779, + "premine": 0, + "spaced_rune": "RTSQQFKTEEBX", + "symbol": null, + "terms": { + "amount": 1, + "cap": 85692692, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728158372, + "turbo": false + } + ], + [ + "864335:906", + { + "block": 864335, + "burned": 0, + "divisibility": 0, + "etching": "df772301fef3107549d200fea54f47e46d6aae197f85e93b0068749640028055", + "mints": 0, + "number": 119778, + "premine": 0, + "spaced_rune": "IWHXSPKPYQOX", + "symbol": null, + "terms": { + "amount": 1, + "cap": 166869547, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728158372, + "turbo": false + } + ], + [ + "864335:905", + { + "block": 864335, + "burned": 0, + "divisibility": 0, + "etching": "186049ed6091d0a4d9e1abf6d436a6af7bc7603a33c71031b8bb0ba02f386b3a", + "mints": 0, + "number": 119777, + "premine": 0, + "spaced_rune": "OHDKZWZHYLVL", + "symbol": null, + "terms": { + "amount": 1, + "cap": 189310557, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728158372, + "turbo": false + } + ], + [ + "864335:904", + { + "block": 864335, + "burned": 0, + "divisibility": 0, + "etching": "74e72d9c58ce6300807d1ca6343fa95f5fa34f3d7e29fc95a94b553ff4c66b36", + "mints": 0, + "number": 119776, + "premine": 0, + "spaced_rune": "NSZNPZDDFYCT", + "symbol": null, + "terms": { + "amount": 1, + "cap": 72959668, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728158372, + "turbo": false + } + ], + [ + "864335:386", + { + "block": 864335, + "burned": 0, + "divisibility": 0, + "etching": "76e81c2a204074d61869f58ce86bf8ecfe66f1213bd444c4f22c6f638a401ef9", + "mints": 0, + "number": 119775, + "premine": 0, + "spaced_rune": "NTOOWMNTOOWMNTOOWM", + "symbol": null, + "terms": { + "amount": 1, + "cap": 1000000, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728158372, + "turbo": false + } + ], + [ + "864334:4073", + { + "block": 864334, + "burned": 0, + "divisibility": 0, + "etching": "6c132c6b69ff19d3dbbd0165bcf2fb5db9bba717824a3ff93e94e976b7da5f9e", + "mints": 0, + "number": 119774, + "premine": 0, + "spaced_rune": "HIDDEN•SELDOM•DISEASE•WISE", + "symbol": null, + "terms": { + "amount": 1, + "cap": 1127, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728158138, + "turbo": false + } + ], + [ + "864334:4070", + { + "block": 864334, + "burned": 0, + "divisibility": 0, + "etching": "adcbc4dc91e0b354baacb37be52e187fab2cf619c43f0675b26c5e7d58ad1ded", + "mints": 0, + "number": 119773, + "premine": 0, + "spaced_rune": "TYDSJXISYECCOQYYSS", + "symbol": null, + "terms": { + "amount": 1, + "cap": 2361833545833, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728158138, + "turbo": false + } + ], + [ + "864334:762", + { + "block": 864334, + "burned": 0, + "divisibility": 0, + "etching": "259fc5e99770c5d2ed0547571981ad191554282e6ab4b2a6eb4083c392edc1cb", + "mints": 0, + "number": 119772, + "premine": 0, + "spaced_rune": "BEGCOAJVXEHW", + "symbol": null, + "terms": { + "amount": 1, + "cap": 38385326, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728158138, + "turbo": false + } + ], + [ + "864334:433", + { + "block": 864334, + "burned": 0, + "divisibility": 0, + "etching": "4d324233f38c0cbf36bf1a76e161cbe0ff9f0efb6ee78d94dffdd5f16ec7e8ba", + "mints": 0, + "number": 119771, + "premine": 0, + "spaced_rune": "BEDIALAMDARBEDIALAMDAR", + "symbol": null, + "terms": { + "amount": 5, + "cap": 100000, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728158138, + "turbo": false + } + ], + [ + "864334:432", + { + "block": 864334, + "burned": 0, + "divisibility": 0, + "etching": "f7b804462b33fd468ef3b171071094f3498968b0a488d08489e16058d470d809", + "mints": 0, + "number": 119770, + "premine": 0, + "spaced_rune": "RUTHMARTINRUTHMARTIN", + "symbol": null, + "terms": { + "amount": 1, + "cap": 999999, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728158138, + "turbo": false + } + ], + [ + "864334:431", + { + "block": 864334, + "burned": 0, + "divisibility": 0, + "etching": "51ce542a9557a4894b0dfd705d13268682aa16c83e5eee9c5b1ba4d67113def8", + "mints": 0, + "number": 119769, + "premine": 0, + "spaced_rune": "ULTIVERSEULTIVERSE", + "symbol": null, + "terms": { + "amount": 1, + "cap": 7777777, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728158138, + "turbo": false + } + ], + [ + "864334:182", + { + "block": 864334, + "burned": 0, + "divisibility": 0, + "etching": "195dc952cb7c9e8a5c370fe098b4aa1d8bba8225bb4706ee7243b8e3c43f2b32", + "mints": 0, + "number": 119768, + "premine": 0, + "spaced_rune": "NUQHRKVWSYEA", + "symbol": null, + "terms": { + "amount": 1, + "cap": 3063483, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728158138, + "turbo": false + } + ], + [ + "864333:3461", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "65078629f16f0ce11a91da3de877a0ac5a25b5ed4c68d0ba3f6a8e75eab5f871", + "mints": 0, + "number": 119767, + "premine": 0, + "spaced_rune": "FMTJRFVGNHVZNUCB", + "symbol": null, + "terms": { + "amount": 1, + "cap": 5541274870406, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:3458", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "8471194b68cfab89a9d6112caf62f97819172d397e91674ec5413ad8f27b2828", + "mints": 0, + "number": 119766, + "premine": 0, + "spaced_rune": "WEELZZLGHGDRTO", + "symbol": null, + "terms": { + "amount": 1, + "cap": 507317119633, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:3440", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "90d530d1daf7f1f6ece388a846fe8173a427f71b7e1c5cfc1c035dcd1fc0b017", + "mints": 0, + "number": 119765, + "premine": 0, + "spaced_rune": "MIIOBBPODENFJ", + "symbol": null, + "terms": { + "amount": 1, + "cap": 503174265447, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:3437", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "5c0d2bbf9543cd50293fd6671d94502fa08c8c6d11431e0eee4ac3aedbdbc5bc", + "mints": 0, + "number": 119764, + "premine": 0, + "spaced_rune": "TASTE•RISING•FULL", + "symbol": null, + "terms": { + "amount": 1, + "cap": 4812, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:3434", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "1766810d3f53cfce81a4e0620c21e8e4643c7a40936dbafa6e88339c025fb5f6", + "mints": 0, + "number": 119763, + "premine": 0, + "spaced_rune": "REGION•MARK•LOW", + "symbol": null, + "terms": { + "amount": 1, + "cap": 2470, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:3433", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "f2a6805462cebffc6eb5855d1205dedf9c7f746a7dfd420c153011bb572f58ba", + "mints": 0, + "number": 119762, + "premine": 0, + "spaced_rune": "QHKKEWPTDMNB", + "symbol": null, + "terms": { + "amount": 1, + "cap": 53660832, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:3432", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "5d2127d84533fc9d486eaec1a2b76b2d349fe63a06a9d14847b667d360af6e19", + "mints": 0, + "number": 119761, + "premine": 0, + "spaced_rune": "IWLUKGYIWMBP", + "symbol": null, + "terms": { + "amount": 1, + "cap": 94339731, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:3431", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "ac4668d63f66c94515dbc2a74faa9152018758a75432cc085a7e7638a24cbc12", + "mints": 0, + "number": 119760, + "premine": 0, + "spaced_rune": "KWUFVEOJVKGQ", + "symbol": null, + "terms": { + "amount": 1, + "cap": 196312580, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:2714", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "c642cd4cc7a075c61d3a32b949217990aa91dfc928f12a2cdba1f2f228c699c7", + "mints": 26, + "number": 119759, + "premine": 210000, + "spaced_rune": "BOUNCE•THE•BITCOIN•CAT", + "symbol": "🐱", + "terms": { + "amount": 1000, + "cap": 20790, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": true + } + ], + [ + "864333:2482", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "cc2415293c275bea4d73ff8f45f68f269686b819de447f50ec6988ac04a62d1b", + "mints": 0, + "number": 119758, + "premine": 30000000, + "spaced_rune": "BITCAT•IS•IN•CONTROL", + "symbol": "🐈", + "terms": { + "amount": 5000, + "cap": 194000, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": true + } + ], + [ + "864333:2462", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "a75f792be155a0b53691289433a6413c1efb1aeaf970f752ee70be3c6e755a06", + "mints": 0, + "number": 119757, + "premine": 0, + "spaced_rune": "FIRST•CAT•EATING•BITCOINER", + "symbol": "🙀", + "terms": { + "amount": 1000, + "cap": 21000, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": true + } + ], + [ + "864333:1142", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "7488c2909e2bb5f39fb836ee1e18c23487d078e48e2420cc11776c8d7931fea5", + "mints": 0, + "number": 119756, + "premine": 0, + "spaced_rune": "AI•CRYPTO•AI•CRYPTO", + "symbol": "A", + "terms": { + "amount": 1000, + "cap": 1, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:1140", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "a024e2d4c4e15eab941376a954bb9176bc95990ba6b2a6d31e5b7c26cd8d7e7c", + "mints": 0, + "number": 119755, + "premine": 0, + "spaced_rune": "SACMKSOKCMPOKMWCLWMCLWCDWC", + "symbol": "c", + "terms": { + "amount": 221, + "cap": 2111111, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:1136", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "d6358e0601130c5ebbdb535aa93bbe2e752fd7fd6eee8601fe5af29e7ff179e1", + "mints": 0, + "number": 119754, + "premine": 0, + "spaced_rune": "XQOFVAHHLCQR", + "symbol": null, + "terms": { + "amount": 1, + "cap": 94964916, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:1135", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "62aa2bd48b0eb8a1c3bb090c6129bdc52a2348f3b8e25a2e2eeaa27313e242af", + "mints": 0, + "number": 119753, + "premine": 0, + "spaced_rune": "YEPWCVNODTII", + "symbol": null, + "terms": { + "amount": 1, + "cap": 39185064, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:1134", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "e3e6a144d3ac57d35f7f141f79ea818bd26a78bf900c2d0aeaa2a95ce68f8c9e", + "mints": 0, + "number": 119752, + "premine": 0, + "spaced_rune": "SDFGJUJTYHTGRSFAD", + "symbol": null, + "terms": { + "amount": 1, + "cap": 5, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:1133", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "97600a89179c0bfd4b7c69bc5f4e9fc2f206124fbc08d4872f18ac6be29a525e", + "mints": 0, + "number": 119751, + "premine": 0, + "spaced_rune": "XQEKAAGEYDXY", + "symbol": null, + "terms": { + "amount": 1, + "cap": 147617461, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:1131", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "8ecabca3a2b1518c67c5ee41c93e7874d1117edfd0b36e46ea68eb83e6f9eaad", + "mints": 0, + "number": 119750, + "premine": 0, + "spaced_rune": "XFHSGMZJEUML", + "symbol": null, + "terms": { + "amount": 1, + "cap": 1014672, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:1130", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "77b1518d7ad77d89118eeb8eb92c120e1732d2e7ce9d6780cda180f5f4968df6", + "mints": 0, + "number": 119749, + "premine": 0, + "spaced_rune": "DJLNUHRYYTGR", + "symbol": null, + "terms": { + "amount": 1, + "cap": 146717679, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:1129", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "4da09c158447950fabd281c7910c6e3f251b9b9a98ab7058e2f4b26304e332ee", + "mints": 0, + "number": 119748, + "premine": 0, + "spaced_rune": "CBAQVALKVMYP", + "symbol": null, + "terms": { + "amount": 1, + "cap": 181932658, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:1128", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "b947075130f5a5f93a5cdfa9a216c76b761ff7cd2fb7ca677b3d00a3ca5d53e0", + "mints": 0, + "number": 119747, + "premine": 0, + "spaced_rune": "POJSRGWQBBWQ", + "symbol": null, + "terms": { + "amount": 1, + "cap": 100105873, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:1127", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "a356dd06600bb163cb4d68bbe601f83d987c3c2cd456e3784616ab297d1843c0", + "mints": 0, + "number": 119746, + "premine": 0, + "spaced_rune": "FMPQPSLKENKY", + "symbol": null, + "terms": { + "amount": 1, + "cap": 82531312, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:1126", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "b6ecdb27bb269949f58ace2ba162726483070e80c140dc60329b5fdbbd3e6395", + "mints": 0, + "number": 119745, + "premine": 0, + "spaced_rune": "GOARBTCEASGJ", + "symbol": null, + "terms": { + "amount": 1, + "cap": 99967467, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:1125", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "abf680ed211d18428ddda208f164539fbf662705bd88d4041575c53e655ed794", + "mints": 0, + "number": 119744, + "premine": 0, + "spaced_rune": "MNBIUEEAKPBJ", + "symbol": null, + "terms": { + "amount": 1, + "cap": 168164931, + "height": [ + null, + null + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ], + [ + "864333:1124", + { + "block": 864333, + "burned": 0, + "divisibility": 0, + "etching": "74e290bc2ed6b39c887ab3b456f86d91edbadb829936c63bb166d42233527491", + "mints": 0, + "number": 119743, + "burned": 0, + "divisibility": 0, + "etching": "74e290bc2ed6b39c887ab3b456f86d91edbadb829936c63bb166d42233527491", + "mints": 0, + "number": 119743, + "divisibility": 0, + "etching": "74e290bc2ed6b39c887ab3b456f86d91edbadb829936c63bb166d42233527491", + "mints": 0, + "number": 119743, + "premine": 0, + "spaced_rune": "CWTYCFSOTBSU", + "symbol": null, + "etching": "74e290bc2ed6b39c887ab3b456f86d91edbadb829936c63bb166d42233527491", + "mints": 0, + "number": 119743, + "premine": 0, + "spaced_rune": "CWTYCFSOTBSU", + "symbol": null, + "terms": { + "amount": 1, + "cap": 29807122, + "mints": 0, + "number": 119743, + "premine": 0, + "spaced_rune": "CWTYCFSOTBSU", + "symbol": null, + "terms": { + "amount": 1, + "cap": 29807122, + "premine": 0, + "spaced_rune": "CWTYCFSOTBSU", + "symbol": null, + "terms": { + "amount": 1, + "cap": 29807122, + "height": [ + null, + null + "terms": { + "amount": 1, + "cap": 29807122, + "height": [ + null, + null + "height": [ + null, + null + null + ], + ], + "offset": [ + null, + null + ] + }, + "timestamp": 1728157837, + "turbo": false + } + ] + ], + "more": true, + "prev": null, + "next": 1 +} +``` +
+ +
+ + GET + /sat/<SAT> + + +### Description + +Returns details about a specific satoshi. Requires index with `--index-sats` flag. + +### Example + +```bash +curl -s -H "Accept: application/json" \ + http://0.0.0.0:80/sat/2099994106992659 +``` + +```json +{ + "block": 3891094, + "charms": [], + "cycle": 3, + "decimal": "3891094.16797", + "degree": "3°111094′214″16797‴", + "epoch": 18, + "inscriptions": [], + "name": "satoshi", + "number": 2099994106992659, + "offset": 16797, + "percentile": "99.99971949060254%", + "period": 1930, + "rarity": "common", + "satpoint": null, + "timestamp": 3544214021 +} +``` +
+ +
+ + GET + /status + + +### Description + +Returns details about the server installation and index. + +### Example + +```bash +curl -s -H "Accept: application/json" \ + http://0.0.0.0:80/status +``` + +```json +{ + "address_index": true, + "blessed_inscriptions": 76332641, + "chain": "mainnet", + "cursed_inscriptions": 472043, + "height": 864351, + "initial_sync_time": { + "secs": 59213, + "nanos": 979632000 + }, + "inscriptions": 76804684, + "lost_sats": 0, + "minimum_rune_for_next_block": "PVHGFEDCAZZ", + "rune_index": true, + "runes": 119811, + "sat_index": false, + "started": "2024-09-27T17:43:39.291876400Z", + "transaction_index": false, + "unrecoverably_reorged": false, + "uptime": { + "secs": 709843, + "nanos": 910346200 + } +} +``` +
+ +
+ + GET + /tx/<TRANSACTION_ID> + + +### Description + +Returns details about the specified transaction. + +### Example + +```bash +curl -s -H "Accept: application/json" \ + http://0.0.0.0:80/tx/99811de396ff10152cdfc9588d9750d0151501f081df2e56071c42dc3532b743 +``` + +```json +{ + "chain": "mainnet", + "etching": null, + "inscription_count": 1, + "transaction": { + "version": 2, + "lock_time": 0, + "input": [ + { + "previous_output": "7d154f826f68e86370105641e3b5b1c6afc697613b8dfce48e4e40db01e8317a:1", + "script_sig": "", + "sequence": 4294967295, + "witness": [ + "e68e67c60dfc06f570dfa8fe880cc09f1041a9b10b285743dd72b8f2e672987f7176ced40d46d279385c148a0c39b9914b91d9d503b7388791f6758884f0c2f4", + "200c97fe0e7bb78d8dd7447bc098386c61248e1e9a7dfd263fd828c5373b945735ac0063036f7264010109696d6167652f706e67004d080289504e470d0a1a0a0000000d49484452000000360000003608060000008c456add000000017352474200aece1ce900000306494441546881ed9abf4e1b4110c63f477903a4b4a6a04186c21532a54de1c214a432051406d14471830405a2b25cb8701344e75c43a4d0514041112843a8281c642972111ec0cfb029ec3976d7b7e7fd670cd1fe24747b6b666ebf9bbdd9b9b581402010083c93f1e5a8130d18b5776b73defcdae26500a56a8df1e73fbe7f1d3acf6404ff2f29fe9d0f27c5fcba705cdbdcc1dae60ed8086028aad1aea0d1ae18f96612ba76ef8daea24131bf8edb874b004381fd6e0f8c3136bfb46aec4bb6fbfbfba7b6ad97881d1d6e6446471c1d6e089f2d2c2f627e69d56850c0b82853bc08234ad51a00e0e63c12fa49dcf1fe95961f5ed4d3e31d9e1eef70bc7f35f6cca6e155188f2c0e001aed4aaab84e34609d6820881af51b271b6f998932a32c88a20800fd6e2f6e2709e4138b8b28600ac94397b4ece82a0a98e25424f8082e2c2fc66d1abc0cf59fd64f9cd6ba99450c508bf3c1d423362bbc444c2ea9542465ca69613d879bad0b461506210d5cf09dcd1562f17bdb07906d937cb8240f2b233e42095150facce60a8c4f202a7c88339e8aa56a8d513d68220a1866452a942962fcda46647305e7c462953c14d348ebaede3e5ca68a22b2b9020060b73627bceee862953c6ece23a1a230b505c48a24293a24cc164d44012b610917d57e0664db490bb52dffed3a1684bd3582b0b78695b0848ca5bdced0abfe246692ee6dd730575b138c23d6eff650ccaf27550d13a346b60afb18bea4b2ad15ad8a60be524f98562a9f6c643bd13fefd35698ed3396d9db3e00301ca83458a6f88b074db60adf71db657bc069974ab53325fdcf589fce0be769fd049fbe7c7e5d7b1eb6627ce26b6b20b1df568c6bb4802944eca523a3c25a98bc81f35a0411b60b74da9e8792d3fa8970feebcf3700c0d9f5bdcd3052319ec726a23ad1403857a5eeadf20a0344812e3b5480a1b049a2642180d957b25be515e6539c16cdd6052b556bacd9ba10165efaeaa7130dd8e8cec7fd36d7e17db8f8d17ec66867898e141dfe8ed29472e1ecfa3e2347ce066d611f3fe49fdb29a5ce56790580dbaf02489cad7d20100804026f9d7f6be4942aeb43f7890000000049454e44ae42608268", + "c10c97fe0e7bb78d8dd7447bc098386c61248e1e9a7dfd263fd828c5373b945735" + ] + } + ], + "output": [ + { + "value": 546, + "script_pubkey": "51208a2ade400b30af7cae07e30a9afa8ac49f54fb3ff7d0f42bbf4a66578a34c280" + } + ] + }, + "txid": "99811de396ff10152cdfc9588d9750d0151501f081df2e56071c42dc3532b743" +} +``` +
+ +## Recursive Endpoints + +See [Recursion](../inscriptions/recursion.md) for an explanation of these. + +{{#include ../inscriptions/recursion.md:35:3371}} diff --git a/docs/src/guides/explorer.md b/docs/src/guides/explorer.md index 0e3f1ea2e2..d13339a3f9 100644 --- a/docs/src/guides/explorer.md +++ b/docs/src/guides/explorer.md @@ -18,7 +18,7 @@ To specify a port add the `--http-port` flag: `ord server --http-port 8080` The JSON-API endpoints are enabled by default, to disable them add the -`--disable-json-api` flag (see [here](#json-api) for more info): +`--disable-json-api` flag (see [here](api.md) for more info): `ord server --disable-json-api` @@ -72,46 +72,4 @@ been issued when they are mined: [100%](https://ordinals.com/search/100%) -JSON-API --------- - -By default the `ord server` gives access to endpoints that -return JSON instead of HTML if you set the HTTP `Accept: application/json` -header. The structure of these objects closely follows -what is shown in the HTML. These endpoints are: - -- `/inscription/` -- `/inscriptions` -- `/inscriptions/block/` -- `/inscriptions/block//` -- `/inscriptions/` -- `/inscriptions//` -- `/output/` -- `/sat/` - -To get a list of the latest 100 inscriptions you would do: - -``` -curl -s -H "Accept: application/json" 'http://0.0.0.0:80/inscriptions' -``` - -To see information about a UTXO, which includes inscriptions inside it, do: - -``` -curl -s -H "Accept: application/json" 'http://0.0.0.0:80/output/bc4c30829a9564c0d58e6287195622b53ced54a25711d1b86be7cd3a70ef61ed:0' -``` - -Which returns: - -``` -{ - "value": 10000, - "script_pubkey": "OP_PUSHNUM_1 OP_PUSHBYTES_32 156cc4878306157720607cdcb4b32afa4cc6853868458d7258b907112e5a434b", - "address": "bc1pz4kvfpurqc2hwgrq0nwtfve2lfxvdpfcdpzc6ujchyr3ztj6gd9sfr6ayf", - "transaction": "bc4c30829a9564c0d58e6287195622b53ced54a25711d1b86be7cd3a70ef61ed", - "sat_ranges": null, - "inscriptions": [ - "6fb976ab49dcec017f1e201e84395983204ae1a7c2abf7ced0a85d692e442799i0" - ] -} ``` diff --git a/docs/src/guides/splitting.md b/docs/src/guides/splitting.md new file mode 100644 index 0000000000..c5263d8462 --- /dev/null +++ b/docs/src/guides/splitting.md @@ -0,0 +1,26 @@ +Splitting +========= + +Complex transactions can be created using the `ord wallet split` command. + +The `split` command takes a YAML configuration file, which specifies any number +of outputs to be created, their bitcoin value, and their value of any number of +runes. It does not currently allow assigning inscriptions to outputs. + +The `split` command can be used to split cardinal, bitcoin-only outputs for +transacting, distribute runes to large numbers of recipients in a single +transaction. + +To send a split transaction using the configuration in `splits.yaml`, run the +following command: + +```bash +ord wallet split --fee-rate 21 --splits split.yaml +``` + +Example `splits.yaml` +--------------------` + +```yaml +{{#include ../../../splits.yaml}} +``` diff --git a/docs/src/inscriptions/burning.md b/docs/src/inscriptions/burning.md new file mode 100644 index 0000000000..e45edb1916 --- /dev/null +++ b/docs/src/inscriptions/burning.md @@ -0,0 +1,24 @@ +Burning +======= + +Inscriptions may be burned by constructing a transaction that spends them to a +script pubkey beginning with `OP_RETURN`. + +Sending inscriptions to a so-called "burn address" is not recognized by `ord`. + +Burned inscriptions receive the "burned" charm, recognized with 🔥 on the +inscription's `/inscription` page. + +When burning inscriptions, CBOR metadata may be included in a data push +immediately following the `OP_RETURN`. + +Burn metadata is unstructured, having no meaning to the underlying protocol, +and should be human readable. It is displayed on the burned inscription's +`/inscription` page, in the same manner as inscription metadata, under the +heading "burn metadata". + +Use it, if you feel like it, to commemorate the inscription, celebrate the +closing of a collection, or for whatever other purposes you so desire. + +Data pushes after the first are currently ignored by `ord`. However, they may +be given future meaning by the protocol, and should not be used. diff --git a/docs/src/inscriptions/recursion.md b/docs/src/inscriptions/recursion.md index dfe7dc540b..ec16d549d1 100644 --- a/docs/src/inscriptions/recursion.md +++ b/docs/src/inscriptions/recursion.md @@ -30,58 +30,108 @@ Recursion has a number of interesting use-cases: inscribed as individual images, or in a shared texture atlas, and then combined, collage-style, in unique combinations in multiple inscriptions. -The recursive endpoints are: - -- `/content/`: the content of the inscription with `` -- `/r/blockhash/`: block hash at given block height. -- `/r/blockhash`: latest block hash. -- `/r/blockheight`: latest block height. -- `/r/blockinfo/`: block info. `` may be a block height or block hash. -- `/r/blocktime`: UNIX time stamp of latest block. -- `/r/children/`: the first 100 child inscription ids. -- `/r/children//`: the set of 100 child inscription ids on ``. -- `/r/children//inscriptions`: details of the first 100 child inscriptions. -- `/r/children//inscriptions/`: details of the set of 100 child inscriptions on ``. -- `/r/inscription/`: information about an inscription -- `/r/metadata/`: JSON string containing the hex-encoded CBOR metadata. -- `/r/parents/`: the first 100 parent inscription ids. -- `/r/parents//`: the set of 100 parent inscription ids on ``. -- `/r/sat/`: the first 100 inscription ids on a sat. -- `/r/sat//`: the set of 100 inscription ids on ``. -- `/r/sat//at/`: the inscription id at `` of all inscriptions on a sat. `` may be a negative number to index from the back. `0` being the first and `-1` being the most recent for example. +## Endpoints -Note: `` only allows the actual number of a sat no other sat -notations like degree, percentile or decimal. We may expand to allow those in -the future. +
+ + GET + /content/<INSCRIPTION_ID> + -Responses from the above recursive endpoints are JSON. For backwards -compatibility additional endpoints are supported, some of which return -plain-text responses. +### Description -- `/blockheight`: latest block height. -- `/blockhash`: latest block hash. -- `/blockhash/`: block hash at given block height. -- `/blocktime`: UNIX time stamp of latest block. +The content of the inscription with ``. + +### Example + +```bash +curl -s -H "Accept: application/json" \ + http://0.0.0.0:80/content/6fb976ab49dcec017f1e201e84395983204ae1a7c2abf7ced0a85d692e442799i0 > skull.jpg +``` + +no terminal output, just file creation +
+ +
+ + GET + /r/blockhash + + +### Description +Latest block hash. + +### Example +```bash +curl -s \ + http://0.0.0.0:80/r/blockhash +``` -Examples --------- +```json +"00000000000000000002891b440944e0ce40b37b6ccaa138c280e9edfc319d5d" +``` +
+ +
+ + GET + /r/blockhash/<HEIGHT> + + +### Description -- `/r/blockhash/0`: +Block hash at given block height as JSON string. + +### Example + +```bash +curl -s \ + http://0.0.0.0:80/r/blockhash/840000 +``` ```json -"000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f" +"0000000000000000000320283a032748cef8227873ff4872689bf23f1cda83a5" ``` +
+ +
+ + GET + /r/blockheight + -- `/r/blockheight`: +### Description + +Latest block height. + +### Example + +```bash +curl -s \ + http://0.0.0.0:80/r/blockheight +``` ```json -777000 +866393 ``` +
-- `/r/blockinfo/0`: +
+ + GET + /r/blockinfo/<QUERY> + -Note: `feerate_percentiles` are feerates at the 10th, 25th, 50th, 75th, and 90th -percentile in sats/vB. +### Description + +Block info. `` may be a block height or block hash. + +### Example (blockheight) + +```bash +curl -s -H "Accept: application/json" \ + http://0.0.0.0:80/r/blockinfo/0 +``` ```json { @@ -89,21 +139,27 @@ percentile in sats/vB. "average_fee_rate": 0, "bits": 486604799, "chainwork": "0000000000000000000000000000000000000000000000000000000100010001", - "confirmations": 0, - "difficulty": 0.0, + "confirmations": 866396, + "difficulty": 1.0, "hash": "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f", - "feerate_percentiles": [0, 0, 0, 0, 0], + "feerate_percentiles": [ + 0, + 0, + 0, + 0, + 0 + ], "height": 0, "max_fee": 0, "max_fee_rate": 0, "max_tx_size": 0, "median_fee": 0, "median_time": 1231006505, - "merkle_root": "0000000000000000000000000000000000000000000000000000000000000000", + "merkle_root": "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b", "min_fee": 0, "min_fee_rate": 0, - "next_block": null, - "nonce": 0, + "next_block": "00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048", + "nonce": 2083236893, "previous_block": null, "subsidy": 5000000000, "target": "00000000ffff0000000000000000000000000000000000000000000000000000", @@ -116,113 +172,3221 @@ percentile in sats/vB. } ``` -- `/r/blocktime`: +### Example (blockhash) -```json -1700770905 +```bash +curl -s -H "Accept: application/json" \ + http://0.0.0.0:80/r/blockinfo/0000000000000000000320283a032748cef8227873ff4872689bf23f1cda83a5 ``` -- `/r/children/60bcf821240064a9c55225c4f01711b0ebbcab39aa3fafeefe4299ab158536fai0/49`: - ```json { - "ids":[ - "7cd66b8e3a63dcd2fada917119830286bca0637267709d6df1ca78d98a1b4487i4900", - "7cd66b8e3a63dcd2fada917119830286bca0637267709d6df1ca78d98a1b4487i4901", - ... - "7cd66b8e3a63dcd2fada917119830286bca0637267709d6df1ca78d98a1b4487i4935", - "7cd66b8e3a63dcd2fada917119830286bca0637267709d6df1ca78d98a1b4487i4936" - ], - "more":false, - "page":49 + "average_fee": 1234031, + "average_fee_rate": 3770, + "bits": 386089497, + "chainwork": "0000000000000000000000000000000000000000753bdab0e0d745453677442b", + "confirmations": 26397, + "difficulty": 86388558925171.02, + "hash": "0000000000000000000320283a032748cef8227873ff4872689bf23f1cda83a5", + "feerate_percentiles": [ + 108, + 134, + 200, + 350, + 1063 + ], + ], + "height": 840000, + "height": 840000, + "max_fee": 799987800, + "max_fee_rate": 3604819, + "max_tx_size": 166989, + "median_fee": 34800, + "median_fee": 34800, + "median_time": 1713570208, + "merkle_root": "031b417c3a1828ddf3d6527fc210daafcc9218e81f98257f88d4d43bd7a5894f", + "min_fee": 2060, + "min_fee_rate": 15, + "next_block": "00000000000000000001b48a75d5a3077913f3f441eb7e08c13c43f768db2463", + "nonce": 3932395645, + "previous_block": "0000000000000000000172014ba58d66455762add0512355ad651207918494ab", + "subsidy": 312500000, + "target": "0000000000000000000342190000000000000000000000000000000000000000", + "timestamp": 1713571767, + "total_fee": 3762561499, + "total_size": 2325218, + "total_weight": 3991793, + "transaction_count": 3050, + "version": 710926336 } ``` +
+ +
+ + GET + /r/blocktime + + +### Description -- `/r/children/60bcf821240064a9c55225c4f01711b0ebbcab39aa3fafeefe4299ab158536fai0/inscriptions/49`: +UNIX time stamp of latest block. + +### Example + +```bash +curl -s \ + http://0.0.0.0:80/r/blocktime +``` ```json -{ - "children": [ - { - "charms": [ - "cursed" - ], - "fee": 44, - "height": 813929, - "id": "7cd66b8e3a63dcd2fada917119830286bca0637267709d6df1ca78d98a1b4487i4900", - "number": -223695, - "output": "dcaaeacf58faea0927468ea5a93f33b7d7447841e66f75db5a655d735510c518:0", - "sat": 1897135510683785, - "satpoint": "dcaaeacf58faea0927468ea5a93f33b7d7447841e66f75db5a655d735510c518:0:74188588", - "timestamp": 1698326262 - }, - ... - { - "charms": [ - "cursed" - ], - "fee": 44, - "height": 813929, - "id": "7cd66b8e3a63dcd2fada917119830286bca0637267709d6df1ca78d98a1b4487i4936", - "number": -223731, - "output": "dcaaeacf58faea0927468ea5a93f33b7d7447841e66f75db5a655d735510c518:0", - "sat": 1897135510683821, - "satpoint": "dcaaeacf58faea0927468ea5a93f33b7d7447841e66f75db5a655d735510c518:0:74188624", - "timestamp": 1698326262 - } - ], - "more": false, - "page": 49 -} +1729362253 ``` +
+ +
+ + GET + /r/children/<INSCRIPTION_ID> + + +### Description + +The first 100 child inscription ids. -- `/r/inscription/3bd72a7ef68776c9429961e43043ff65efa7fb2d8bb407386a9e3b19f149bc36i0` +### Example + +```bash +curl -s -H "Accept: application/json" \ + http://0.0.0.0:80/r/children/e317a2a5d68bd1004ae15a06175a319272a10389ff125c98820389edef8b0a94i0 +``` ```json { - "charms": [], - "content_type": "image/png", - "content_length": 144037, - "delegate": null, - "fee": 36352, - "height": 209, - "id": "3bd72a7ef68776c9429961e43043ff65efa7fb2d8bb407386a9e3b19f149bc36i0", - "number": 2, - "output": "3bd72a7ef68776c9429961e43043ff65efa7fb2d8bb407386a9e3b19f149bc36:0", - "sat": null, - "satpoint": "3bd72a7ef68776c9429961e43043ff65efa7fb2d8bb407386a9e3b19f149bc36:0:0", - "timestamp": 1708312562, - "value": 10000, - "address": "bc1pz4kvfpurqc2hwgrq0nwtfve2lfxvdpfcdpzc6ujchyr3ztj6gd9sfr6ayf" + "ids": [ + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei0", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei1", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei2", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei3", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei4", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei5", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei6", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei7", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei8", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei9", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei10", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei11", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei12", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei13", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei14", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei15", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei16", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei17", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei18", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei19", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei20", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei21", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei22", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei23", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei24", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei25", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei26", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei27", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei28", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei29", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei30", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei31", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei32", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei33", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei34", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei35", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei36", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei37", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei38", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei39", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei40", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei41", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei42", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei43", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei44", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei45", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei46", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei47", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei48", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei49", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei50", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei51", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei52", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei53", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei54", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei55", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei56", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei57", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei58", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei59", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei60", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei61", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei62", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei63", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei64", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei65", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei66", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei67", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei68", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei69", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei70", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei71", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei72", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei73", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei74", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei75", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei76", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei77", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei78", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei79", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei80", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei81", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei82", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei83", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei84", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei85", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei86", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei87", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei88", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei89", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei90", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei91", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei92", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei93", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei94", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei95", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei96", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei97", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei98", + "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei99" + ], + "more": true, + "page": 0 } ``` +
-- `/r/metadata/35b66389b44535861c44b2b18ed602997ee11db9a30d384ae89630c9fc6f011fi3`: +
+ + GET + /r/children/<INSCRIPTION_ID>/<PAGE> + -```json -"a2657469746c65664d656d6f727966617574686f726e79656c6c6f775f6f72645f626f74" -``` +### Description + +The set of 100 child inscription ids on ``. -- `/r/sat/1023795949035695`: +### Example + +```bash +curl -s -H "Accept: application/json" \ + http://0.0.0.0:80/r/children/e317a2a5d68bd1004ae15a06175a319272a10389ff125c98820389edef8b0a94i0/9 +``` ```json { - "ids":[ - "17541f6adf6eb160d52bc6eb0a3546c7c1d2adfe607b1a3cddc72cc0619526adi0" - ], - "more":false, - "page":0 + "ids": [ + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci60", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci61", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci62", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci63", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci64", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci65", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci66", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci67", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci68", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci69", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci70", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci71", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci72", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci73", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci74", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci75", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci76", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci77", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci78", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci79", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci80", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci81", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci82", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci83", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci84", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci85", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci86", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci87", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci88", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci89", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci90", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci91", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci92", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci93", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci94", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci95", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci96", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci97", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci98", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci99", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci100", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci101", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci102", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci103", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci104", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci105", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci106", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci107", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci108", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci109", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci110", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci111", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci112", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci113", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci114", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci115", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci116", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci117", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci118", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci119", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci120", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci121", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci122", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci123", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci124", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci125", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci126", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci127", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci128", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci129", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci130", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci131", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci132", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci133", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci134", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci135", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci136", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci137", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci138", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci139", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci140", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci141", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci142", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci143", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci144", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci145", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci146", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci147", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci148", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci149", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci150", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci151", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci152", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci153", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci154", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci155", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci156", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci157", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci158", + "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci159" + ], + "more": true, + "page": 9 } ``` +
+ +
+ + GET + /r/children/<INSCRIPTION_ID>/inscriptions + -- `/r/sat/1023795949035695/at/-1`: +### Description + +Details of the first 100 child inscriptions. + +### Example + +```bash +curl -s -H "Accept: application/json" \ + http://0.0.0.0:80/r/children/e317a2a5d68bd1004ae15a06175a319272a10389ff125c98820389edef8b0a94i0/inscriptions +``` ```json { - "id":"17541f6adf6eb160d52bc6eb0a3546c7c1d2adfe607b1a3cddc72cc0619526adi0" -} -``` + "children": [ + { + "charms": [], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei0", + "number": 75744297, + "output": "236ce10d9cd3f9f7f824a07686f7d7bce0d64a400f0328ce5bb2191a60d15262:0", + "sat": null, + "satpoint": "236ce10d9cd3f9f7f824a07686f7d7bce0d64a400f0328ce5bb2191a60d15262:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei1", + "number": 75744298, + "output": "0648b2a2d6dc6c882a4c61f86f1e2e4354eeafebad96e319dc98510f1bc4f2cd:1", + "sat": null, + "satpoint": "0648b2a2d6dc6c882a4c61f86f1e2e4354eeafebad96e319dc98510f1bc4f2cd:1:0", + "timestamp": 1726282054 + }, + ... + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei2", + "number": 75744299, + "output": "fa1f214886f45a094cc360d8057e90c6317512b6c567dede4a7feb820785bd62:1", + "sat": null, + "satpoint": "fa1f214886f45a094cc360d8057e90c6317512b6c567dede4a7feb820785bd62:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei3", + "number": 75744300, + "output": "a85fbe33c76a8171a15e8ca56f56baaf0a30d2bbd5b75b6527b5ff29c2a25aea:1", + "sat": null, + "satpoint": "a85fbe33c76a8171a15e8ca56f56baaf0a30d2bbd5b75b6527b5ff29c2a25aea:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei4", + "number": 75744301, + "output": "872032304823012b4cd5b402f8798a63306ddcfd8727007c77e2622305367dc1:1", + "sat": null, + "satpoint": "872032304823012b4cd5b402f8798a63306ddcfd8727007c77e2622305367dc1:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei5", + "number": 75744302, + "output": "4e084ee3f3cb02ed9dd4a2745ca4c90da4cc8549c98c5f92531185ea508cd120:0", + "sat": null, + "satpoint": "4e084ee3f3cb02ed9dd4a2745ca4c90da4cc8549c98c5f92531185ea508cd120:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei6", + "number": 75744303, + "output": "ac0af852b76616514f8571bdfaf02c8f5a4086a2aa356a15792a236470bd67ff:0", + "sat": null, + "satpoint": "ac0af852b76616514f8571bdfaf02c8f5a4086a2aa356a15792a236470bd67ff:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei7", + "number": 75744304, + "output": "2a3a457027ee3ea821655613d418355c19d61ee200359e3a9d2a18808c2e6bb8:0", + "sat": null, + "satpoint": "2a3a457027ee3ea821655613d418355c19d61ee200359e3a9d2a18808c2e6bb8:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei8", + "number": 75744305, + "output": "c7907dc99df915e33ed48a674764cd76bdb220a6bc5693c035b8554914701c57:0", + "sat": null, + "satpoint": "c7907dc99df915e33ed48a674764cd76bdb220a6bc5693c035b8554914701c57:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei9", + "number": 75744306, + "output": "2b8ef45c4a8eb92cb8660cb11ad5a0693f8af5427b8b1e9c1e415ab4dbe5fca3:1", + "sat": null, + "satpoint": "2b8ef45c4a8eb92cb8660cb11ad5a0693f8af5427b8b1e9c1e415ab4dbe5fca3:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei10", + "number": 75744307, + "output": "e8165d6bfa9af9237be642fb2f049bec6095d04dd507ccf3e0e813867746b319:1", + "sat": null, + "satpoint": "e8165d6bfa9af9237be642fb2f049bec6095d04dd507ccf3e0e813867746b319:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei11", + "number": 75744308, + "output": "429a8d0d0f2c52fceecebb40ad7e83ceb35d218e2fd8e967e3412345c391ba17:1", + "sat": null, + "satpoint": "429a8d0d0f2c52fceecebb40ad7e83ceb35d218e2fd8e967e3412345c391ba17:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei12", + "number": 75744309, + "output": "9e0388156e59ec8543f8421068d313deca62fd4a8a79d0fb69701ddf9e7516db:0", + "sat": null, + "satpoint": "9e0388156e59ec8543f8421068d313deca62fd4a8a79d0fb69701ddf9e7516db:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei13", + "number": 75744310, + "output": "0cf00796f3ed9ac9c50ba6e63f33dfd66ac017caf5d3a6b100896d227f712771:1", + "sat": null, + "satpoint": "0cf00796f3ed9ac9c50ba6e63f33dfd66ac017caf5d3a6b100896d227f712771:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei14", + "number": 75744311, + "output": "e98508d438bc8e8f68b7afc75585476b8612d98fb1ec7316772b0fe3a24bfa88:1", + "sat": null, + "satpoint": "e98508d438bc8e8f68b7afc75585476b8612d98fb1ec7316772b0fe3a24bfa88:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei15", + "number": 75744312, + "output": "373c54c23c3b30a88e7384574c863c89e0194f4a68cf84cab5fb8f04861e5655:1", + "sat": null, + "satpoint": "373c54c23c3b30a88e7384574c863c89e0194f4a68cf84cab5fb8f04861e5655:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei16", + "number": 75744313, + "output": "189c3fce8e472068c9bcf10bbf2b6be831ba033ea21e85d01613e6545d62e949:0", + "sat": null, + "satpoint": "189c3fce8e472068c9bcf10bbf2b6be831ba033ea21e85d01613e6545d62e949:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei17", + "number": 75744314, + "output": "38dc164c7b21c5ed6e3d7595e3e0b4c57e364e83542d437fb1312cc10ad09753:1", + "sat": null, + "satpoint": "38dc164c7b21c5ed6e3d7595e3e0b4c57e364e83542d437fb1312cc10ad09753:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei18", + "number": 75744315, + "output": "316a757866f5aa54b2e3fce8ba7090462a7c406f93c6be17ace7aa28a344ff80:0", + "sat": null, + "satpoint": "316a757866f5aa54b2e3fce8ba7090462a7c406f93c6be17ace7aa28a344ff80:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei19", + "number": 75744316, + "output": "737c122bdc85aad7a740b2761e21222fef3d4279d7f6ee2fdde1298f4380fb17:1", + "sat": null, + "satpoint": "737c122bdc85aad7a740b2761e21222fef3d4279d7f6ee2fdde1298f4380fb17:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei20", + "number": 75744317, + "output": "8f8be7f28a1e0dad4ebc0a3bfd9e1ad2f5513267432b88bd5ffd0caca76ae46b:0", + "sat": null, + "satpoint": "8f8be7f28a1e0dad4ebc0a3bfd9e1ad2f5513267432b88bd5ffd0caca76ae46b:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei21", + "number": 75744318, + "output": "32523c806832b7a49f4f139a55ebf0978942ec14d9fe01248f716ffe2a7e4782:1", + "sat": null, + "satpoint": "32523c806832b7a49f4f139a55ebf0978942ec14d9fe01248f716ffe2a7e4782:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei22", + "number": 75744319, + "output": "9f3ed6c97d302ecf8c28fe35e526b86e0819cd917eae479458a19efd01a443f1:0", + "sat": null, + "satpoint": "9f3ed6c97d302ecf8c28fe35e526b86e0819cd917eae479458a19efd01a443f1:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei23", + "number": 75744320, + "output": "a28e38b49231130d1506e5a12a0d69649876ae0d9b039e75fa732eb717e33a0b:1", + "sat": null, + "satpoint": "a28e38b49231130d1506e5a12a0d69649876ae0d9b039e75fa732eb717e33a0b:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei24", + "number": 75744321, + "output": "4207bffaf5b60219ac108d55d4503e3d2577377b4679f1a2f5bfdbe925905809:5", + "sat": null, + "satpoint": "4207bffaf5b60219ac108d55d4503e3d2577377b4679f1a2f5bfdbe925905809:5:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei25", + "number": 75744322, + "output": "b24f8c5b7a9ef5ee72954bd24383934361b38d4ddc117a53419f2124a888c9ec:0", + "sat": null, + "satpoint": "b24f8c5b7a9ef5ee72954bd24383934361b38d4ddc117a53419f2124a888c9ec:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei26", + "number": 75744323, + "output": "2470fa6efea8de1ced36478321b2b59b4fdb6c416b2a9048ea721b05c7eb043e:1", + "sat": null, + "satpoint": "2470fa6efea8de1ced36478321b2b59b4fdb6c416b2a9048ea721b05c7eb043e:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei27", + "number": 75744324, + "output": "37f16683576ed13fd1fb3bd2fdf99905e1e40b5c6a7b50c63ff2b2be578f90c2:0", + "sat": null, + "satpoint": "37f16683576ed13fd1fb3bd2fdf99905e1e40b5c6a7b50c63ff2b2be578f90c2:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei28", + "number": 75744325, + "output": "591dad7e5c50be787e821eb3fda3c917d98ac459b6d71df3b83eb0da5768bb09:2", + "sat": null, + "satpoint": "591dad7e5c50be787e821eb3fda3c917d98ac459b6d71df3b83eb0da5768bb09:2:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei29", + "number": 75744326, + "output": "38e82c3c46633d74d9ee40ef6d18bcc54733dd9f58dfe70def7492a6d89c31c8:1", + "sat": null, + "satpoint": "38e82c3c46633d74d9ee40ef6d18bcc54733dd9f58dfe70def7492a6d89c31c8:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei30", + "number": 75744327, + "output": "b49058d36a95390f84077e90652d7309f2b6cf304a9ba25c0072a01ace3e7c37:2", + "sat": null, + "satpoint": "b49058d36a95390f84077e90652d7309f2b6cf304a9ba25c0072a01ace3e7c37:2:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei31", + "number": 75744328, + "output": "d3c83ed7e6b99f6123c912aa218f458f4c02e5ed35f4e322a289ca3c2aae3ca9:0", + "sat": null, + "satpoint": "d3c83ed7e6b99f6123c912aa218f458f4c02e5ed35f4e322a289ca3c2aae3ca9:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei32", + "number": 75744329, + "output": "668a4513098f160571cb6a1c620b38a148190c16043048f9bf28e74eb6e9d4b9:1", + "sat": null, + "satpoint": "668a4513098f160571cb6a1c620b38a148190c16043048f9bf28e74eb6e9d4b9:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei33", + "number": 75744330, + "output": "18bae3ea9fe55260116efe9294701a7131214a13b5b0c5340b6b70b36bad13b3:0", + "sat": null, + "satpoint": "18bae3ea9fe55260116efe9294701a7131214a13b5b0c5340b6b70b36bad13b3:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei34", + "number": 75744331, + "output": "57e2178fea149fdbf2eeb287b66f9680135008c004edf0b4b08cf9e2eace63d4:1", + "sat": null, + "satpoint": "57e2178fea149fdbf2eeb287b66f9680135008c004edf0b4b08cf9e2eace63d4:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei35", + "number": 75744332, + "output": "ed056db972b25eb528b609b0b86305ff32f351b9834c50091772be34f7581221:0", + "sat": null, + "satpoint": "ed056db972b25eb528b609b0b86305ff32f351b9834c50091772be34f7581221:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei36", + "number": 75744333, + "output": "6e14107fa11eaa6b16d1b498dbdf8e826c077a817a8086322e9e3aab5c42062b:2", + "sat": null, + "satpoint": "6e14107fa11eaa6b16d1b498dbdf8e826c077a817a8086322e9e3aab5c42062b:2:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei37", + "number": 75744334, + "output": "5682b75eabcf2d7c7995e51933e65eb65279c107a470d9e3b32f033abf75b79d:0", + "sat": null, + "satpoint": "5682b75eabcf2d7c7995e51933e65eb65279c107a470d9e3b32f033abf75b79d:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei38", + "number": 75744335, + "output": "77a0317d41f8eb83c9cc39d09edb59e1cb80a7ff38b0682f35494d78be21866d:0", + "sat": null, + "satpoint": "77a0317d41f8eb83c9cc39d09edb59e1cb80a7ff38b0682f35494d78be21866d:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei39", + "number": 75744336, + "output": "c888490846ee8ecfbf1a192bcd98577874d9bcd2d7142bb2c9f72088e17d7f3d:1", + "sat": null, + "satpoint": "c888490846ee8ecfbf1a192bcd98577874d9bcd2d7142bb2c9f72088e17d7f3d:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei40", + "number": 75744337, + "output": "6f33b433d753548116573b29c1cc3a7a768b87e7406e19a29bf8f540c2b87b04:1", + "sat": null, + "satpoint": "6f33b433d753548116573b29c1cc3a7a768b87e7406e19a29bf8f540c2b87b04:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei41", + "number": 75744338, + "output": "03d55f39c181195028a4abd734e118f23315e2f08744b8cec498ff3c1e998b3d:4", + "sat": null, + "satpoint": "03d55f39c181195028a4abd734e118f23315e2f08744b8cec498ff3c1e998b3d:4:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei42", + "number": 75744339, + "output": "aee2e0829731ed11e98264103443e58756dd3ed040a8c10a6ee6bda4d6ac54d4:0", + "sat": null, + "satpoint": "aee2e0829731ed11e98264103443e58756dd3ed040a8c10a6ee6bda4d6ac54d4:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei43", + "number": 75744340, + "output": "a37afaa8753732e16f62112de86c9c1fbc70c37d1aa9e4c8eb9f1f014050d8ea:1", + "sat": null, + "satpoint": "a37afaa8753732e16f62112de86c9c1fbc70c37d1aa9e4c8eb9f1f014050d8ea:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei44", + "number": 75744341, + "output": "3e689c84346bfd555c25af2d0a8b4e93320f8e8ebc1bfc899587bf8c09a6d05c:1", + "sat": null, + "satpoint": "3e689c84346bfd555c25af2d0a8b4e93320f8e8ebc1bfc899587bf8c09a6d05c:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei45", + "number": 75744342, + "output": "92e4e3ee896e5094ee8d8f18c036ca022695cab25b4a8c1f53e3d6d182fbcf84:0", + "sat": null, + "satpoint": "92e4e3ee896e5094ee8d8f18c036ca022695cab25b4a8c1f53e3d6d182fbcf84:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei46", + "number": 75744343, + "output": "df27377145efaeadcd3c694dcdbdc3396b27db6acd05ecc5b48dc71028fab5b5:0", + "sat": null, + "satpoint": "df27377145efaeadcd3c694dcdbdc3396b27db6acd05ecc5b48dc71028fab5b5:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei47", + "number": 75744344, + "output": "75cea14db5c6fd7eafd1ca63c3a1c19f5dd7d5873740b02f12656a7893f436f2:0", + "sat": null, + "satpoint": "75cea14db5c6fd7eafd1ca63c3a1c19f5dd7d5873740b02f12656a7893f436f2:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei48", + "number": 75744345, + "output": "2a540dbc23db80ba09b4ef28f711a4c6fa1c52ef7c97b7c3c9fd6b6d5df02a47:1", + "sat": null, + "satpoint": "2a540dbc23db80ba09b4ef28f711a4c6fa1c52ef7c97b7c3c9fd6b6d5df02a47:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei49", + "number": 75744346, + "output": "3203241d789e212ec9be99511c03e510cb3d6c5c6fc0f8d4c09ecada0b5a84ae:1", + "sat": null, + "satpoint": "3203241d789e212ec9be99511c03e510cb3d6c5c6fc0f8d4c09ecada0b5a84ae:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei50", + "number": 75744347, + "output": "7bf299a3d81e15fe9ce63e4a38af9612e61d72392f9dc7eef0d85596f37927f0:1", + "sat": null, + "satpoint": "7bf299a3d81e15fe9ce63e4a38af9612e61d72392f9dc7eef0d85596f37927f0:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei51", + "number": 75744348, + "output": "5af59053d38ba2af28981784e36605982e1cfa8343e85701c682a4bbdaf5c975:1", + "sat": null, + "satpoint": "5af59053d38ba2af28981784e36605982e1cfa8343e85701c682a4bbdaf5c975:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei52", + "number": 75744349, + "output": "7828955c0e0c659f0ab5d4a2cc01fef8461c5238ae388b9221894a262711bb0a:0", + "sat": null, + "satpoint": "7828955c0e0c659f0ab5d4a2cc01fef8461c5238ae388b9221894a262711bb0a:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei53", + "number": 75744350, + "output": "fefafa8bf2ea7a26db2ff42207b9d72fcc433cd9743d689958482fbdfcac90bb:1", + "sat": null, + "satpoint": "fefafa8bf2ea7a26db2ff42207b9d72fcc433cd9743d689958482fbdfcac90bb:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei54", + "number": 75744351, + "output": "22f7781b5f798fffa2fa70570821e9ab1de331a267ca8089934a15372aaaf230:1", + "sat": null, + "satpoint": "22f7781b5f798fffa2fa70570821e9ab1de331a267ca8089934a15372aaaf230:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei55", + "number": 75744352, + "output": "f82fbc7a2c63ca950a548b1ffa328a7a964ee6e6949747e7a3d43f26849a043b:1", + "sat": null, + "satpoint": "f82fbc7a2c63ca950a548b1ffa328a7a964ee6e6949747e7a3d43f26849a043b:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei56", + "number": 75744353, + "output": "4f6fda2629946d93ed3b80f3ef9d41338cc113f265cd715a4d52ab6fc1710864:1", + "sat": null, + "satpoint": "4f6fda2629946d93ed3b80f3ef9d41338cc113f265cd715a4d52ab6fc1710864:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei57", + "number": 75744354, + "output": "b55897244a5396e4fd1e0b6320f790ed8fdc8512ed95123654bcae29bfe9cd13:2", + "sat": null, + "satpoint": "b55897244a5396e4fd1e0b6320f790ed8fdc8512ed95123654bcae29bfe9cd13:2:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei58", + "number": 75744355, + "output": "d6abf2cbd8d206395fc52ffeb1acdd8029239bdd3e1a0e262f3311f9c665f71a:1", + "sat": null, + "satpoint": "d6abf2cbd8d206395fc52ffeb1acdd8029239bdd3e1a0e262f3311f9c665f71a:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei59", + "number": 75744356, + "output": "07eb14a592b6b563803b0b3d79a799eb87479e3c74aec777ff7aa84c861377f0:0", + "sat": null, + "satpoint": "07eb14a592b6b563803b0b3d79a799eb87479e3c74aec777ff7aa84c861377f0:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei60", + "number": 75744357, + "output": "57bc43e002ce6e587bbee201878281c78fcac58f9b0093156a60f3a852aef01a:0", + "sat": null, + "satpoint": "57bc43e002ce6e587bbee201878281c78fcac58f9b0093156a60f3a852aef01a:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei61", + "number": 75744358, + "output": "c3dfc2ef3f87ef09683921bb5cf8ee1bddfbb501617d062525f1a53d50869451:1", + "sat": null, + "satpoint": "c3dfc2ef3f87ef09683921bb5cf8ee1bddfbb501617d062525f1a53d50869451:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei62", + "number": 75744359, + "output": "ef4334a1932cbdb69f9eda4425fbb5460f812a329d3a22b8780cd32caece412f:0", + "sat": null, + "satpoint": "ef4334a1932cbdb69f9eda4425fbb5460f812a329d3a22b8780cd32caece412f:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei63", + "number": 75744360, + "output": "beeb8426bbb7eb9107753877c5ff192245008b7afb247e28a37ec5d8bff0a323:0", + "sat": null, + "satpoint": "beeb8426bbb7eb9107753877c5ff192245008b7afb247e28a37ec5d8bff0a323:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei64", + "number": 75744361, + "output": "83e0e849813e139fd602d48f6053853901f29b40a582ccf27809f061a5dc5f24:0", + "sat": null, + "satpoint": "83e0e849813e139fd602d48f6053853901f29b40a582ccf27809f061a5dc5f24:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei65", + "number": 75744362, + "output": "01cf721fdb91635f04b935c3cb48fcad2a11db60df153312ce268f3abec6a292:1", + "sat": null, + "satpoint": "01cf721fdb91635f04b935c3cb48fcad2a11db60df153312ce268f3abec6a292:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei66", + "number": 75744363, + "output": "7ffa90a032caa13fbbbe13791c2a497697199bb4c6cf490f141ac3b6c37a6db4:31", + "sat": null, + "satpoint": "7ffa90a032caa13fbbbe13791c2a497697199bb4c6cf490f141ac3b6c37a6db4:31:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei67", + "number": 75744364, + "output": "46766bfb9df9fc427d11ddc7e3809dacd96f8ada19b652b3b386e747f32dc076:0", + "sat": null, + "satpoint": "46766bfb9df9fc427d11ddc7e3809dacd96f8ada19b652b3b386e747f32dc076:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei68", + "number": 75744365, + "output": "d72ebcb29ace83aa4ad39a5514149d52581e50cef16e694b433db0793339bf3a:1", + "sat": null, + "satpoint": "d72ebcb29ace83aa4ad39a5514149d52581e50cef16e694b433db0793339bf3a:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei69", + "number": 75744366, + "output": "078e3ce7e3357d183f65baaa3a241339ea2990ddcaec35b21b8e88597b68fc8c:0", + "sat": null, + "satpoint": "078e3ce7e3357d183f65baaa3a241339ea2990ddcaec35b21b8e88597b68fc8c:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei70", + "number": 75744367, + "output": "b7f4f886792120931eac134079e23dd0d08bb61a6106f461a3d04517df0fb6ea:1", + "sat": null, + "satpoint": "b7f4f886792120931eac134079e23dd0d08bb61a6106f461a3d04517df0fb6ea:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei71", + "number": 75744368, + "output": "2da24b193e3d6074369b0ca095cbf4a894ebf4a685ac6c64600a2e577e197dbd:0", + "sat": null, + "satpoint": "2da24b193e3d6074369b0ca095cbf4a894ebf4a685ac6c64600a2e577e197dbd:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei72", + "number": 75744369, + "output": "6620dcdf30ef1192b43549cd7170faab2dadf301e9545d021e14edf990153227:0", + "sat": null, + "satpoint": "6620dcdf30ef1192b43549cd7170faab2dadf301e9545d021e14edf990153227:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei73", + "number": 75744370, + "output": "7cabbf3d5823cf4f606a187c1e99c32ec26bfcb577870e03a707e54ef0de6887:1", + "sat": null, + "satpoint": "7cabbf3d5823cf4f606a187c1e99c32ec26bfcb577870e03a707e54ef0de6887:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei74", + "number": 75744371, + "output": "7ffa90a032caa13fbbbe13791c2a497697199bb4c6cf490f141ac3b6c37a6db4:32", + "sat": null, + "satpoint": "7ffa90a032caa13fbbbe13791c2a497697199bb4c6cf490f141ac3b6c37a6db4:32:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei75", + "number": 75744372, + "output": "5152b89c83cbb6c24baaf5cf567cefdef7e65abd0c816e002d9cc7c68d7af93c:0", + "sat": null, + "satpoint": "5152b89c83cbb6c24baaf5cf567cefdef7e65abd0c816e002d9cc7c68d7af93c:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei76", + "number": 75744373, + "output": "16c512c67272743d5c4f6ba63fb637dce54717cae8413b76cadeb7cd9eddc37a:2", + "sat": null, + "satpoint": "16c512c67272743d5c4f6ba63fb637dce54717cae8413b76cadeb7cd9eddc37a:2:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei77", + "number": 75744374, + "output": "5db74a14965d67a0cf8b5373712080e0577a77ff1d702da181ece534c0b9e502:1", + "sat": null, + "satpoint": "5db74a14965d67a0cf8b5373712080e0577a77ff1d702da181ece534c0b9e502:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei78", + "number": 75744375, + "output": "77b689937b8dfd32ff42ab61579d1a662575387edd7e29834ae6174da59cf56e:0", + "sat": null, + "satpoint": "77b689937b8dfd32ff42ab61579d1a662575387edd7e29834ae6174da59cf56e:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei79", + "number": 75744376, + "output": "a44efd0e34e04aa93aed95f16a3c0a1cb48457b4c17680e71c23bb2f2e62fbcd:3", + "sat": null, + "satpoint": "a44efd0e34e04aa93aed95f16a3c0a1cb48457b4c17680e71c23bb2f2e62fbcd:3:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei80", + "number": 75744377, + "output": "2120b62e2cd3a744a100747be11df2e1537a01f0fdfcb5c0eee77ae99b961998:2", + "sat": null, + "satpoint": "2120b62e2cd3a744a100747be11df2e1537a01f0fdfcb5c0eee77ae99b961998:2:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei81", + "number": 75744378, + "output": "f484caadcf3b947995409abf42a15b244ecad397fe01ab8221b4c1e3b579124f:0", + "sat": null, + "satpoint": "f484caadcf3b947995409abf42a15b244ecad397fe01ab8221b4c1e3b579124f:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei82", + "number": 75744379, + "output": "03aedb2147860cc4be91ad34f328b385358ee98a5f76a776a3eee6180f28d675:1", + "sat": null, + "satpoint": "03aedb2147860cc4be91ad34f328b385358ee98a5f76a776a3eee6180f28d675:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei83", + "number": 75744380, + "output": "da6da8dfd4f0e4e5372ebf29e9b1f5c39fd72ed8808c63ce468407f691915636:1", + "sat": null, + "satpoint": "da6da8dfd4f0e4e5372ebf29e9b1f5c39fd72ed8808c63ce468407f691915636:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei84", + "number": 75744381, + "output": "15476e602cd0cd36ee418e1c0107d329dafc180c22588f5105a252c18898d3ad:0", + "sat": null, + "satpoint": "15476e602cd0cd36ee418e1c0107d329dafc180c22588f5105a252c18898d3ad:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei85", + "number": 75744382, + "output": "7ffa90a032caa13fbbbe13791c2a497697199bb4c6cf490f141ac3b6c37a6db4:33", + "sat": null, + "satpoint": "7ffa90a032caa13fbbbe13791c2a497697199bb4c6cf490f141ac3b6c37a6db4:33:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei86", + "number": 75744383, + "output": "6b072f4710f657a875f5124c0bda1bd4d9961b64630d13511ff77c85387ad003:1", + "sat": null, + "satpoint": "6b072f4710f657a875f5124c0bda1bd4d9961b64630d13511ff77c85387ad003:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei87", + "number": 75744384, + "output": "47f2236975e17795093c0276b48b343c80f2c54812a9dbe0a68d891055eb48cb:1", + "sat": null, + "satpoint": "47f2236975e17795093c0276b48b343c80f2c54812a9dbe0a68d891055eb48cb:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei88", + "number": 75744385, + "output": "7ffa90a032caa13fbbbe13791c2a497697199bb4c6cf490f141ac3b6c37a6db4:34", + "sat": null, + "satpoint": "7ffa90a032caa13fbbbe13791c2a497697199bb4c6cf490f141ac3b6c37a6db4:34:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei89", + "number": 75744386, + "output": "065a2c23fe1fa2eac8a82491e3b7cbb82ea524aab7a6265013a3484e599ba897:0", + "sat": null, + "satpoint": "065a2c23fe1fa2eac8a82491e3b7cbb82ea524aab7a6265013a3484e599ba897:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei90", + "number": 75744387, + "output": "f44ac6c78399a6bf5a0f24dc0c39d68fe61e474f41d33596135941d5bd1c8703:1", + "sat": null, + "satpoint": "f44ac6c78399a6bf5a0f24dc0c39d68fe61e474f41d33596135941d5bd1c8703:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei91", + "number": 75744388, + "output": "d13ba90d57ff04e28429c675a0fbb727df96bc059083c9e6bba8a1ea396a93dc:1", + "sat": null, + "satpoint": "d13ba90d57ff04e28429c675a0fbb727df96bc059083c9e6bba8a1ea396a93dc:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei92", + "number": 75744389, + "output": "931b9940c19e5d7252c061ec7cab473496c889974cccd9d9175f28161818b0cd:0", + "sat": null, + "satpoint": "931b9940c19e5d7252c061ec7cab473496c889974cccd9d9175f28161818b0cd:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei93", + "number": 75744390, + "output": "7ffa90a032caa13fbbbe13791c2a497697199bb4c6cf490f141ac3b6c37a6db4:35", + "sat": null, + "satpoint": "7ffa90a032caa13fbbbe13791c2a497697199bb4c6cf490f141ac3b6c37a6db4:35:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei94", + "number": 75744391, + "output": "21927781b6b09a88e320c0f6579d877fb0ab8149a120ceee062ab75a95dabe72:1", + "sat": null, + "satpoint": "21927781b6b09a88e320c0f6579d877fb0ab8149a120ceee062ab75a95dabe72:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei95", + "number": 75744392, + "output": "6872b619b113bfae8e7d00d14c3407bc8abfa3eb7df1ab2091618e1d1649bbf9:1", + "sat": null, + "satpoint": "6872b619b113bfae8e7d00d14c3407bc8abfa3eb7df1ab2091618e1d1649bbf9:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei96", + "number": 75744393, + "output": "25569ef89fe9ab7efc9147045d3c7b8ffcc81e84929a74c4f3bd9d3764d9ee71:0", + "sat": null, + "satpoint": "25569ef89fe9ab7efc9147045d3c7b8ffcc81e84929a74c4f3bd9d3764d9ee71:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei97", + "number": 75744394, + "output": "bd66e0a16eaf404cb451e9940de515c077191ae29db748165843303ef31f8afb:1", + "sat": null, + "satpoint": "bd66e0a16eaf404cb451e9940de515c077191ae29db748165843303ef31f8afb:1:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei98", + "number": 75744395, + "output": "aebdb5312f5954267b5497d5c4265048af75dd6d6bbf782729439df987d978ed:0", + "sat": null, + "satpoint": "aebdb5312f5954267b5497d5c4265048af75dd6d6bbf782729439df987d978ed:0:0", + "timestamp": 1726282054 + }, + { + "charms": [ + "vindicated" + ], + "fee": 417, + "height": 861224, + "id": "89e4fb2e5ea5c6301b9ac915d1d05619776f5ca41fc02fb6e5dced16f2cabfdei99", + "number": 75744396, + "output": "9d02bfc670d0169dde485ec7ca981194fb9b9d02510999c2add0eded9f9f508d:0", + "sat": null, + "satpoint": "9d02bfc670d0169dde485ec7ca981194fb9b9d02510999c2add0eded9f9f508d:0:0", + "timestamp": 1726282054 + } + ], + "more": true, + "page": 0 +} +``` +
+ +
+ + GET + /r/children/<INSCRIPTION_ID>/inscriptions/<PAGE> + + +### Description + +Details of the set of 100 child inscriptions on <PAGE>. + +### Example + +```bash +curl -s -H "Accept: application/json" \ + http://0.0.0.0:80/r/children/e317a2a5d68bd1004ae15a06175a319272a10389ff125c98820389edef8b0a94i0/inscriptions/9 +``` + +```json +{ + "children": [ + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci60", + "number": 75750346, + "output": "e8ebadbd9ce4e4372b1b9b30fd5cb831c1f48ff2d0f8f1d1de2e190a2f5bcbe8:1", + "sat": null, + "satpoint": "e8ebadbd9ce4e4372b1b9b30fd5cb831c1f48ff2d0f8f1d1de2e190a2f5bcbe8:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci61", + "number": 75750347, + "output": "aa46f14bec8842edd7b7c1b79224cd186dda6c5577cd65196da77d7e27b00b0c:0", + "sat": null, + "satpoint": "aa46f14bec8842edd7b7c1b79224cd186dda6c5577cd65196da77d7e27b00b0c:0:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci62", + "number": 75750348, + "output": "dc6232e0485856a3aee3303622d54fc0ffb805d6c800aaa13a7fd9583946b051:0", + "sat": null, + "satpoint": "dc6232e0485856a3aee3303622d54fc0ffb805d6c800aaa13a7fd9583946b051:0:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci63", + "number": 75750349, + "output": "a56beaf3f3ae7f24aafc92fea414bb07fbc84b180bb49e0a0c30132bdf7864a0:1", + "sat": null, + "satpoint": "a56beaf3f3ae7f24aafc92fea414bb07fbc84b180bb49e0a0c30132bdf7864a0:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci64", + "number": 75750350, + "output": "2fc53b80ace52c6a807c6babb81f97745ede915157d400d691a5ca0bde6c41fc:1", + "sat": null, + "satpoint": "2fc53b80ace52c6a807c6babb81f97745ede915157d400d691a5ca0bde6c41fc:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci65", + "number": 75750351, + "output": "cb291756e9370e306ed5b0b8637395df46db95d288b4ad8a1838a4bf75f0457a:4", + "sat": null, + "satpoint": "cb291756e9370e306ed5b0b8637395df46db95d288b4ad8a1838a4bf75f0457a:4:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci66", + "number": 75750352, + "output": "c166fa8977df8f20940bb3a4de7a8bd75352fcc4baef27b09f598d2f258556cd:1", + "sat": null, + "satpoint": "c166fa8977df8f20940bb3a4de7a8bd75352fcc4baef27b09f598d2f258556cd:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci67", + "number": 75750353, + "output": "4165f7a3ebd52030318472e39002258e5252eaffc5e340e3efe5f5b8ba22ea77:0", + "sat": null, + "satpoint": "4165f7a3ebd52030318472e39002258e5252eaffc5e340e3efe5f5b8ba22ea77:0:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci68", + "number": 75750354, + "output": "798d2402a1672aa476f6c764199d78579b95a91503cf158e1957f615769fbdba:1", + "sat": null, + "satpoint": "798d2402a1672aa476f6c764199d78579b95a91503cf158e1957f615769fbdba:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci69", + "number": 75750355, + "output": "4dfce1b60e29e13d7e16c6e4db1b9dd509212c9c6c3b2e79d9a39883abcffde7:0", + "sat": null, + "satpoint": "4dfce1b60e29e13d7e16c6e4db1b9dd509212c9c6c3b2e79d9a39883abcffde7:0:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci70", + "number": 75750356, + "output": "584c219533e974299eae3a0793c1488d7faea8cc5fa189d90e9db858cd74c848:0", + "sat": null, + "satpoint": "584c219533e974299eae3a0793c1488d7faea8cc5fa189d90e9db858cd74c848:0:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci71", + "number": 75750357, + "output": "354b4fdb93520277e19ab8da7511d48eacb20bc0b81071ca00a8a491ca1286a5:0", + "sat": null, + "satpoint": "354b4fdb93520277e19ab8da7511d48eacb20bc0b81071ca00a8a491ca1286a5:0:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci72", + "number": 75750358, + "output": "d4d15ee8803554c7afa35cfcce16517ea1095501c7186b7dbba7c6f5dff5afe7:1", + "sat": null, + "satpoint": "d4d15ee8803554c7afa35cfcce16517ea1095501c7186b7dbba7c6f5dff5afe7:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci73", + "number": 75750359, + "output": "5a1be04ce6bb93fe8797ad59631a6fadbfb75126ec650f2660c76a926ee951e0:1", + "sat": null, + "satpoint": "5a1be04ce6bb93fe8797ad59631a6fadbfb75126ec650f2660c76a926ee951e0:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci74", + "number": 75750360, + "output": "5481718592ecbb1ef6cc033a267ec709d89a9b437d3833edad1e0e48356aafb6:1", + "sat": null, + "satpoint": "5481718592ecbb1ef6cc033a267ec709d89a9b437d3833edad1e0e48356aafb6:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci75", + "number": 75750361, + "output": "59aa6d5f0b832ec4f768aef08450d059788e182d685bc2cba50db9a32e851e20:7", + "sat": null, + "satpoint": "59aa6d5f0b832ec4f768aef08450d059788e182d685bc2cba50db9a32e851e20:7:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci76", + "number": 75750362, + "output": "ad7247eef612064c86fa5f080b35c4c1e0e80179b807f95995bf594914fdc85e:2", + "sat": null, + "satpoint": "ad7247eef612064c86fa5f080b35c4c1e0e80179b807f95995bf594914fdc85e:2:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci77", + "number": 75750363, + "output": "ac0ec9edd074ad4222f603b6d6f2903c4723517acc069a22ae7693b0b3e5b580:2", + "sat": null, + "satpoint": "ac0ec9edd074ad4222f603b6d6f2903c4723517acc069a22ae7693b0b3e5b580:2:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci78", + "number": 75750364, + "output": "6d685494081dcff31902f15b3b21019721cf3e01fa51eee4b3fd1dc74d09fabc:1", + "sat": null, + "satpoint": "6d685494081dcff31902f15b3b21019721cf3e01fa51eee4b3fd1dc74d09fabc:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci79", + "number": 75750365, + "output": "3a851b46da63b0a0dcb8c45c2eaedaf8ed339139cad07b98a23cac39d579979a:0", + "sat": null, + "satpoint": "3a851b46da63b0a0dcb8c45c2eaedaf8ed339139cad07b98a23cac39d579979a:0:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci80", + "number": 75750366, + "output": "3f2bd3b8a29e69ae92343685a46e1f18a60b1e64d5baff13ddc4bdf6e46b67ae:1", + "sat": null, + "satpoint": "3f2bd3b8a29e69ae92343685a46e1f18a60b1e64d5baff13ddc4bdf6e46b67ae:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci81", + "number": 75750367, + "output": "aa7c9bd9f982a94f7ad0791128a0ab2c004bb92dfa0cb6b088afb5b58177aba0:3", + "sat": null, + "satpoint": "aa7c9bd9f982a94f7ad0791128a0ab2c004bb92dfa0cb6b088afb5b58177aba0:3:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci82", + "number": 75750368, + "output": "53564fcdc12118214ad9d4527a2750d091ca2f7beb6045e20e520250a529935e:3", + "sat": null, + "satpoint": "53564fcdc12118214ad9d4527a2750d091ca2f7beb6045e20e520250a529935e:3:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci83", + "number": 75750369, + "output": "d913288dc9d502291c72f48c71db7944dc624efada498916409d92b7d9fa7cfa:1", + "sat": null, + "satpoint": "d913288dc9d502291c72f48c71db7944dc624efada498916409d92b7d9fa7cfa:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci84", + "number": 75750370, + "output": "a0d0ea93298c384ed0400e9357e53ca3cc9113578f8cc80aad8d2ba90a522120:1", + "sat": null, + "satpoint": "a0d0ea93298c384ed0400e9357e53ca3cc9113578f8cc80aad8d2ba90a522120:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci85", + "number": 75750371, + "output": "02e767e651595c404f74a279653378e29214bf36472fbb513295a8fc58e612ec:0", + "sat": null, + "satpoint": "02e767e651595c404f74a279653378e29214bf36472fbb513295a8fc58e612ec:0:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci86", + "number": 75750372, + "output": "b7516e65f1ec7e4d5e787056f1586907bd7986953ae6854cda4719a1ca953e36:0", + "sat": null, + "satpoint": "b7516e65f1ec7e4d5e787056f1586907bd7986953ae6854cda4719a1ca953e36:0:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci87", + "number": 75750373, + "output": "1075880b07a8bc26bafe16e75c6678c6503caa06b8b0f56bc996e475d7276331:1", + "sat": null, + "satpoint": "1075880b07a8bc26bafe16e75c6678c6503caa06b8b0f56bc996e475d7276331:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci88", + "number": 75750374, + "output": "03b3f1a7e103b70651a704c38d6cf77f6be7e50a9c27018dd27b7b439eb0b81d:3", + "sat": null, + "satpoint": "03b3f1a7e103b70651a704c38d6cf77f6be7e50a9c27018dd27b7b439eb0b81d:3:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci89", + "number": 75750375, + "output": "a686365ca5f10cd4408045ffc05c0dc207bf807bc6dcdda5b14675ef34606a38:0", + "sat": null, + "satpoint": "a686365ca5f10cd4408045ffc05c0dc207bf807bc6dcdda5b14675ef34606a38:0:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci90", + "number": 75750376, + "output": "7ffa90a032caa13fbbbe13791c2a497697199bb4c6cf490f141ac3b6c37a6db4:90", + "sat": null, + "satpoint": "7ffa90a032caa13fbbbe13791c2a497697199bb4c6cf490f141ac3b6c37a6db4:90:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci91", + "number": 75750377, + "output": "0e21dbf80690488c12cd49b93dafa17092b38f8d3e0e6a02af6140dbab42625b:1", + "sat": null, + "satpoint": "0e21dbf80690488c12cd49b93dafa17092b38f8d3e0e6a02af6140dbab42625b:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci92", + "number": 75750378, + "output": "2ad902f909c91996510291ac77447dbbc353e0219f6b8e022a0079d624812d1b:0", + "sat": null, + "satpoint": "2ad902f909c91996510291ac77447dbbc353e0219f6b8e022a0079d624812d1b:0:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci93", + "number": 75750379, + "output": "1748563d45c5e45ab607a6748469f79606d777a9408118c6f66f03bd0c8ec6ab:0", + "sat": null, + "satpoint": "1748563d45c5e45ab607a6748469f79606d777a9408118c6f66f03bd0c8ec6ab:0:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci94", + "number": 75750380, + "output": "a738ec116cf443bc2cd2520b3c3b281548a079f33538122ef7544717f8d03036:1", + "sat": null, + "satpoint": "a738ec116cf443bc2cd2520b3c3b281548a079f33538122ef7544717f8d03036:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci95", + "number": 75750381, + "output": "4b8472a729f7b73feaf1de84895641e62a8b1103818c1878e4da18a585cb1047:1", + "sat": null, + "satpoint": "4b8472a729f7b73feaf1de84895641e62a8b1103818c1878e4da18a585cb1047:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci96", + "number": 75750382, + "output": "1525f3a1e7eebc18e5128fdffb674f4662e91e9f07a239b065d29cc7df0a8280:1", + "sat": null, + "satpoint": "1525f3a1e7eebc18e5128fdffb674f4662e91e9f07a239b065d29cc7df0a8280:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci97", + "number": 75750383, + "output": "36fb0e2dd250585e48525bf27ff3701376196d33ea03fb4b9020ad907edf7790:0", + "sat": null, + "satpoint": "36fb0e2dd250585e48525bf27ff3701376196d33ea03fb4b9020ad907edf7790:0:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci98", + "number": 75750384, + "output": "e9c4003ddd4e72f20c9893086c116f8919a6c339d3c58ca02912a686a7502423:1", + "sat": null, + "satpoint": "e9c4003ddd4e72f20c9893086c116f8919a6c339d3c58ca02912a686a7502423:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci99", + "number": 75750385, + "output": "46133b85a20ebbf1f38eff2242d205b458b451a7a33c45333731bb73e2729f0d:0", + "sat": null, + "satpoint": "46133b85a20ebbf1f38eff2242d205b458b451a7a33c45333731bb73e2729f0d:0:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci100", + "number": 75750386, + "output": "7e6713290ef906ebfdbd8258a38479c991fd1ff97ee254b545972c6b5a8888f7:1", + "sat": null, + "satpoint": "7e6713290ef906ebfdbd8258a38479c991fd1ff97ee254b545972c6b5a8888f7:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci101", + "number": 75750387, + "output": "c85bd3da8a8a3fd43b19ce9602bc032710eb44bbfa93a907156391c7f79882b7:1", + "sat": null, + "satpoint": "c85bd3da8a8a3fd43b19ce9602bc032710eb44bbfa93a907156391c7f79882b7:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci102", + "number": 75750388, + "output": "d4553de8386558c844053fed6badb3604326f92e8fed61b4f50e87a6027cf637:2", + "sat": null, + "satpoint": "d4553de8386558c844053fed6badb3604326f92e8fed61b4f50e87a6027cf637:2:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci103", + "number": 75750389, + "output": "d7d6bb4de735264fd31a8174535bc7b018a966be77ea92a74d16ad9cb78b4e40:0", + "sat": null, + "satpoint": "d7d6bb4de735264fd31a8174535bc7b018a966be77ea92a74d16ad9cb78b4e40:0:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci104", + "number": 75750390, + "output": "9ea97f7291b05bd7338c7a8322b5400d9b8224d573ec9f2d4eb98232948d2f7a:4", + "sat": null, + "satpoint": "9ea97f7291b05bd7338c7a8322b5400d9b8224d573ec9f2d4eb98232948d2f7a:4:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci105", + "number": 75750391, + "output": "abea0fb6bea56f2638961516e50e1c13418cc937bde1f6392e7baf2377888c1d:6", + "sat": null, + "satpoint": "abea0fb6bea56f2638961516e50e1c13418cc937bde1f6392e7baf2377888c1d:6:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci106", + "number": 75750392, + "output": "0cbaf378b811e6f5c6c08798ce58c2ada3970fda4fa19110a57cc3a7950950a5:1", + "sat": null, + "satpoint": "0cbaf378b811e6f5c6c08798ce58c2ada3970fda4fa19110a57cc3a7950950a5:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci107", + "number": 75750393, + "output": "1e1b1cb2601e50ea6a6854244822738e17529f06b5f718e2ba621668bbbc552f:0", + "sat": null, + "satpoint": "1e1b1cb2601e50ea6a6854244822738e17529f06b5f718e2ba621668bbbc552f:0:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci108", + "number": 75750394, + "output": "5fbd75a09f49bd06a20195e95a1328bfcc33e495c0d82f3491671dbf30c26f2c:8", + "sat": null, + "satpoint": "5fbd75a09f49bd06a20195e95a1328bfcc33e495c0d82f3491671dbf30c26f2c:8:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci109", + "number": 75750395, + "output": "a957af246666aa6e08d3d1834ff4d404d6ae028e4f316c733169a187a059056c:1", + "sat": null, + "satpoint": "a957af246666aa6e08d3d1834ff4d404d6ae028e4f316c733169a187a059056c:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci110", + "number": 75750396, + "output": "87edfa3f457fa1113083ecb5c1c47c2173feeb044da01b79a571057c7eedf3a0:0", + "sat": null, + "satpoint": "87edfa3f457fa1113083ecb5c1c47c2173feeb044da01b79a571057c7eedf3a0:0:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci111", + "number": 75750397, + "output": "f6ee23933295af316cd6b721ce90807af73cf5e93813d61d8f6b61311a813ee7:1", + "sat": null, + "satpoint": "f6ee23933295af316cd6b721ce90807af73cf5e93813d61d8f6b61311a813ee7:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci112", + "number": 75750398, + "output": "6df1fe9659aa0a6850d4140c790409cb4391484b0f3359da046c51088bfd105b:0", + "sat": null, + "satpoint": "6df1fe9659aa0a6850d4140c790409cb4391484b0f3359da046c51088bfd105b:0:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci113", + "number": 75750399, + "output": "2be9a16127fc9fe5d4aeb20e26c9efb76a0af3c52bfac9249f55521e8fcd46a2:1", + "sat": null, + "satpoint": "2be9a16127fc9fe5d4aeb20e26c9efb76a0af3c52bfac9249f55521e8fcd46a2:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci114", + "number": 75750400, + "output": "2015aaa9ba4c0f7aee895234dec9b46bde5f673d2d03f21989d8e87bc4c663e6:0", + "sat": null, + "satpoint": "2015aaa9ba4c0f7aee895234dec9b46bde5f673d2d03f21989d8e87bc4c663e6:0:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci115", + "number": 75750401, + "output": "6f1577e2f1222d932f1e1ae3762bb9f1acf2646d1efa3bebe0d8b0a1f50e8cdc:1", + "sat": null, + "satpoint": "6f1577e2f1222d932f1e1ae3762bb9f1acf2646d1efa3bebe0d8b0a1f50e8cdc:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci116", + "number": 75750402, + "output": "dda3674abf67cc1b2ff122474aa6c8c1c3419f1c2991e84a45ba912a5824fd1b:9", + "sat": null, + "satpoint": "dda3674abf67cc1b2ff122474aa6c8c1c3419f1c2991e84a45ba912a5824fd1b:9:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci117", + "number": 75750403, + "output": "cce948e560ba7b1494291a3460273dba668603dca6e892959e1a7d513134c1f0:1", + "sat": null, + "satpoint": "cce948e560ba7b1494291a3460273dba668603dca6e892959e1a7d513134c1f0:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci118", + "number": 75750404, + "output": "8bd0968c3adaf6e5428ea706547987da3dd39a1f467b87ec3c248726a9fbd1c4:1", + "sat": null, + "satpoint": "8bd0968c3adaf6e5428ea706547987da3dd39a1f467b87ec3c248726a9fbd1c4:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci119", + "number": 75750405, + "output": "c377651a9fdd81c248ce47722ef676803be6c9969ee8976bab35cc7a1c6dcf8c:1", + "sat": null, + "satpoint": "c377651a9fdd81c248ce47722ef676803be6c9969ee8976bab35cc7a1c6dcf8c:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci120", + "number": 75750406, + "output": "dd8f12b201ebbfa0f0256824fb8929dac200bd1905e59c994dbc996414d6029b:0", + "sat": null, + "satpoint": "dd8f12b201ebbfa0f0256824fb8929dac200bd1905e59c994dbc996414d6029b:0:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci121", + "number": 75750407, + "output": "87b7b3b0af113fed12c0f0b459bac1308c4f34aff87b2904d97a1d3c83c51f23:0", + "sat": null, + "satpoint": "87b7b3b0af113fed12c0f0b459bac1308c4f34aff87b2904d97a1d3c83c51f23:0:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci122", + "number": 75750408, + "output": "668a4513098f160571cb6a1c620b38a148190c16043048f9bf28e74eb6e9d4b9:2", + "sat": null, + "satpoint": "668a4513098f160571cb6a1c620b38a148190c16043048f9bf28e74eb6e9d4b9:2:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci123", + "number": 75750409, + "output": "7c90eb647de009a95ab07d4347f16f7a565b546c8c78efe456abf2600b2b3d74:1", + "sat": null, + "satpoint": "7c90eb647de009a95ab07d4347f16f7a565b546c8c78efe456abf2600b2b3d74:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci124", + "number": 75750410, + "output": "00820bdbbcfb5c94cdbdc41e67e93e575b163385564528f26a549a3a3b9dbc61:1", + "sat": null, + "satpoint": "00820bdbbcfb5c94cdbdc41e67e93e575b163385564528f26a549a3a3b9dbc61:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci125", + "number": 75750411, + "output": "7acaecb9489085ddd62c1e5600a6a387fd6034fdf2da23fb69f622d1c7d2fdcc:0", + "sat": null, + "satpoint": "7acaecb9489085ddd62c1e5600a6a387fd6034fdf2da23fb69f622d1c7d2fdcc:0:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci126", + "number": 75750412, + "output": "7c888cba84c665f9f18cc92015af22735426e0d00ed58b9504aa1845d464b987:0", + "sat": null, + "satpoint": "7c888cba84c665f9f18cc92015af22735426e0d00ed58b9504aa1845d464b987:0:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci127", + "number": 75750413, + "output": "5aafdf1ee7e0c34500da6379a73729e3daca28e841be342d7f2bbec5d0814c76:1", + "sat": null, + "satpoint": "5aafdf1ee7e0c34500da6379a73729e3daca28e841be342d7f2bbec5d0814c76:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci128", + "number": 75750414, + "output": "2d4a2865039556bc67b7cbc6ba11d42cf0bbca37fea210ed0204fb912f8afd3e:1", + "sat": null, + "satpoint": "2d4a2865039556bc67b7cbc6ba11d42cf0bbca37fea210ed0204fb912f8afd3e:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci129", + "number": 75750415, + "output": "295b382183030285aca0c18cdfbee35f3e5892c1c0b7e28585d00d2ffb2bd34c:2", + "sat": null, + "satpoint": "295b382183030285aca0c18cdfbee35f3e5892c1c0b7e28585d00d2ffb2bd34c:2:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci130", + "number": 75750416, + "output": "f19c645e42b1a8f1d2f5ad9627d4da567dd0839165e897ab61092d5aaf8e5a81:2", + "sat": null, + "satpoint": "f19c645e42b1a8f1d2f5ad9627d4da567dd0839165e897ab61092d5aaf8e5a81:2:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci131", + "number": 75750417, + "output": "c91daab05a5814a7f5f05b6e0979b0dcc3a48498d7a5fe5d6bd554db7c846436:0", + "sat": null, + "satpoint": "c91daab05a5814a7f5f05b6e0979b0dcc3a48498d7a5fe5d6bd554db7c846436:0:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci132", + "number": 75750418, + "output": "7ffa90a032caa13fbbbe13791c2a497697199bb4c6cf490f141ac3b6c37a6db4:75", + "sat": null, + "satpoint": "7ffa90a032caa13fbbbe13791c2a497697199bb4c6cf490f141ac3b6c37a6db4:75:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci133", + "number": 75750419, + "output": "95995534b21d087048d2d8193f26c44bfbf47902e0358c6f9ecb8ec9975a000d:2", + "sat": null, + "satpoint": "95995534b21d087048d2d8193f26c44bfbf47902e0358c6f9ecb8ec9975a000d:2:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci134", + "number": 75750420, + "output": "15916470561640fae4cee956f68c37347c49013273954ec108d393e653150ab4:3", + "sat": null, + "satpoint": "15916470561640fae4cee956f68c37347c49013273954ec108d393e653150ab4:3:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci135", + "number": 75750421, + "output": "7ffa90a032caa13fbbbe13791c2a497697199bb4c6cf490f141ac3b6c37a6db4:76", + "sat": null, + "satpoint": "7ffa90a032caa13fbbbe13791c2a497697199bb4c6cf490f141ac3b6c37a6db4:76:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci136", + "number": 75750422, + "output": "1b48bfabc6c10d569a2d41f16fbd8e9a11ceb2ffa24da29123b69042a6617721:0", + "sat": null, + "satpoint": "1b48bfabc6c10d569a2d41f16fbd8e9a11ceb2ffa24da29123b69042a6617721:0:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci137", + "number": 75750423, + "output": "87c1a5aec245bfc81d3b0a53cef3b29a1e8c497f9ac3ffdd8937dc8bd9c358bc:1", + "sat": null, + "satpoint": "87c1a5aec245bfc81d3b0a53cef3b29a1e8c497f9ac3ffdd8937dc8bd9c358bc:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci138", + "number": 75750424, + "output": "1b0edeadfc4e0065597b04441c1bf84acddf907bfd119e41a1ae9c127f1876d8:0", + "sat": null, + "satpoint": "1b0edeadfc4e0065597b04441c1bf84acddf907bfd119e41a1ae9c127f1876d8:0:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci139", + "number": 75750425, + "output": "cb27eb1d10ef05c001f9ddd4f91375a70d0fdf7ab2aff400f67da37472ca6bd9:0", + "sat": null, + "satpoint": "cb27eb1d10ef05c001f9ddd4f91375a70d0fdf7ab2aff400f67da37472ca6bd9:0:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci140", + "number": 75750426, + "output": "15ad3fd1fa45aaefe966c3155f3e98a5d8514fd39b44fd29ecff2903dcd44a21:1", + "sat": null, + "satpoint": "15ad3fd1fa45aaefe966c3155f3e98a5d8514fd39b44fd29ecff2903dcd44a21:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci141", + "number": 75750427, + "output": "bdf46228725cfa21b8b39a69c25a18252412256849b640cac5d36cd75e7b1636:0", + "sat": null, + "satpoint": "bdf46228725cfa21b8b39a69c25a18252412256849b640cac5d36cd75e7b1636:0:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci142", + "number": 75750428, + "output": "1250f0191891f921f3cbd54e96085e5010d85295427302dc17479160d301eb66:5", + "sat": null, + "satpoint": "1250f0191891f921f3cbd54e96085e5010d85295427302dc17479160d301eb66:5:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci143", + "number": 75750429, + "output": "805143e852c3bcb8f84c0464e04805afc33fa1f8c0310cc3f90d4a8a3a6a9ada:0", + "sat": null, + "satpoint": "805143e852c3bcb8f84c0464e04805afc33fa1f8c0310cc3f90d4a8a3a6a9ada:0:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci144", + "number": 75750430, + "output": "265a1ff523f6d1ea9e28b5fb3dd793faf50774f389e79f3b0d9cd2c9f17b4355:0", + "sat": null, + "satpoint": "265a1ff523f6d1ea9e28b5fb3dd793faf50774f389e79f3b0d9cd2c9f17b4355:0:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci145", + "number": 75750431, + "output": "2c7a8040f40dc467e4e2cc8e33898ffd38f7d96f8f5c8a803a51b6eb688e44cd:0", + "sat": null, + "satpoint": "2c7a8040f40dc467e4e2cc8e33898ffd38f7d96f8f5c8a803a51b6eb688e44cd:0:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci146", + "number": 75750432, + "output": "6f7e77be9eab7e1527a9b5a7e9318e9a7d6451e2d0569e7c224dd1a4a467da70:1", + "sat": null, + "satpoint": "6f7e77be9eab7e1527a9b5a7e9318e9a7d6451e2d0569e7c224dd1a4a467da70:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci147", + "number": 75750433, + "output": "7ffa90a032caa13fbbbe13791c2a497697199bb4c6cf490f141ac3b6c37a6db4:77", + "sat": null, + "satpoint": "7ffa90a032caa13fbbbe13791c2a497697199bb4c6cf490f141ac3b6c37a6db4:77:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci148", + "number": 75750434, + "output": "18e0dd8a0d01e987e1643900f6e8822712eadcb23038283df9401bc65a6cba52:1", + "sat": null, + "satpoint": "18e0dd8a0d01e987e1643900f6e8822712eadcb23038283df9401bc65a6cba52:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci149", + "number": 75750435, + "output": "c2f53742a37ea2fc7efc10aad3c14c8c6dd2a7e09d14b693d639b82eb0826173:3", + "sat": null, + "satpoint": "c2f53742a37ea2fc7efc10aad3c14c8c6dd2a7e09d14b693d639b82eb0826173:3:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci150", + "number": 75750436, + "output": "72c38a6b8d68dbc474ebb9cba1ed068eef06d7c40c721fa1a50b19d2b1d55238:0", + "sat": null, + "satpoint": "72c38a6b8d68dbc474ebb9cba1ed068eef06d7c40c721fa1a50b19d2b1d55238:0:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci151", + "number": 75750437, + "output": "c5bf4a93584a464b4d63f98fa718e4824a0186bfceb40ea191d04392fc421ecb:1", + "sat": null, + "satpoint": "c5bf4a93584a464b4d63f98fa718e4824a0186bfceb40ea191d04392fc421ecb:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci152", + "number": 75750438, + "output": "03d55f39c181195028a4abd734e118f23315e2f08744b8cec498ff3c1e998b3d:3", + "sat": null, + "satpoint": "03d55f39c181195028a4abd734e118f23315e2f08744b8cec498ff3c1e998b3d:3:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci153", + "number": 75750439, + "output": "53efe970e50e416c8f49cc1293b504069a1bd852f55e2c0b41d8174c822b2f56:1", + "sat": null, + "satpoint": "53efe970e50e416c8f49cc1293b504069a1bd852f55e2c0b41d8174c822b2f56:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci154", + "number": 75750440, + "output": "ed0614de63479dc233799e3763c4f62a2660f7915b871924ead501cc3c95adee:4", + "sat": null, + "satpoint": "ed0614de63479dc233799e3763c4f62a2660f7915b871924ead501cc3c95adee:4:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci155", + "number": 75750441, + "output": "9ca556ff7c56291f13fc348eecc61b54dd7ff26d15d93cc314095c942b24da69:0", + "sat": null, + "satpoint": "9ca556ff7c56291f13fc348eecc61b54dd7ff26d15d93cc314095c942b24da69:0:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci156", + "number": 75750442, + "output": "b5e75034f8a31d725a1932b0eb081491ceca710f6baabfd67e8a05575d2cbf39:0", + "sat": null, + "satpoint": "b5e75034f8a31d725a1932b0eb081491ceca710f6baabfd67e8a05575d2cbf39:0:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci157", + "number": 75750443, + "output": "d5332c7be176f9a57f759fa2de9b1b7df9cbe42a53b16dc1382440ac903571e2:1", + "sat": null, + "satpoint": "d5332c7be176f9a57f759fa2de9b1b7df9cbe42a53b16dc1382440ac903571e2:1:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci158", + "number": 75750444, + "output": "b50d033746d6bae2d3416c8e6c449dffe1df70b189deb421840fa7ff822ce669:2", + "sat": null, + "satpoint": "b50d033746d6bae2d3416c8e6c449dffe1df70b189deb421840fa7ff822ce669:2:0", + "timestamp": 1726292222 + }, + { + "charms": [ + "vindicated" + ], + "fee": 418, + "height": 861239, + "id": "b205c9d1dc054f24c13aeb886fba42d9dd0aac3cd9bdc4f034affc90f3a0bf3ci159", + "number": 75750445, + "output": "752a63dd11ac46e9cc702f1520dd704618519bcc3f34742d91c6fdb29fed8425:1", + "sat": null, + "satpoint": "752a63dd11ac46e9cc702f1520dd704618519bcc3f34742d91c6fdb29fed8425:1:0", + "timestamp": 1726292222 + } + ], + "more": true, + "page": 9 +} +``` +
+ +
+ + GET + /r/undelegated-content/<INSCRIPTION_ID> + + +### Description + +Undelegated content of an inscription. + +
+ + +
+ + GET + /r/inscription/<INSCRIPTION_ID> + + +### Description + +Information about an inscription. + +### Example + +```bash +curl -s -H "Accept: application/json" \ + http://0.0.0.0:80/r/inscriptions/13130e4b299ed361f2a734f6433844ef0f0211cd504e0ca8f4d4ab20f51b8127i0 +``` + +```json +{ + "charms": [ + "vindicated" + ], + "content_type": "model/gltf-binary", + "content_length": 3726620, + "delegate": null, + "fee": 7499396, + "height": 866266, + "id": "13130e4b299ed361f2a734f6433844ef0f0211cd504e0ca8f4d4ab20f51b8127i0", + "number": 76545890, + "output": "13130e4b299ed361f2a734f6433844ef0f0211cd504e0ca8f4d4ab20f51b8127:1", + "sat": null, + "satpoint": "13130e4b299ed361f2a734f6433844ef0f0211cd504e0ca8f4d4ab20f51b8127:1:0", + "timestamp": 1729297535, + "value": 1313, + "address": "bc1phj8hgzeptthkur9se2jq5vex7vlyhc8ul689svxea0xsn6r43z7sekz6qh" +} +``` +
+ +
+ + GET + /r/metadata/<INSCRIPTION_ID> + + +### Description + +JSON string containing the hex-encoded CBOR metadata. + +### Example +```bash +curl -s -H "Accept: application/json" \ + http://0.0.0.0:80/r/metadata/b1ef66c2d1a047cbaa6260b74daac43813924378fe08ef8545da4cb79e8fcf00i0 +``` + +```json +"ac6c50484f544f475241504845526a5041524b4552204441596643414d4552416c43414e4f4e20454f532d31566446494c4d6f4b4f44414b20454b54415220313030644c454e53781a5a4549535320504c414e415220542a2038354d4d20462f312e346d5348555454455220535045454465312f31323568415045525455524563462f38664d4f44454c5318646650484f544f531903e8684c4f434154494f4e774c4f5320414e47454c45532c2043414c49464f524e49416443524557a36a415353495354414e4345826e41524941532042555244454c4c49684e4153204e495858664d414b45555087754544454e2053594d4f4e45204c415454414e5a494f6a4d494d49204d455945526e53414d414e544841204c455052456f4c4953455454452053414e54414e416e4a45535349434120564552474f4e63504f4e724d415941204e414b415241205352554f4348644841495283694a414b4920494348556c4a4f43454c594e2056454741724a4546464552534f4e2054414e475241444966504154524f4e6e434153455920524f4441524d4f52674c4943454e534563434330" +``` +
+ +
+ + GET + /r/parents/<INSCRIPTION_ID> + + +### Description + +The first 100 parent inscription ids. + +### Example + +```bash +curl -s -H "Accept: application/json" \ + http://0.0.0.0:80/r/parents/b1ef66c2d1a047cbaa6260b74daac43813924378fe08ef8545da4cb79e8fcf00i0 +``` + +```json +{ + "ids": [ + "6fb976ab49dcec017f1e201e84395983204ae1a7c2abf7ced0a85d692e442799i0" + ], + "more": false, + "page_index": 0 +} +``` +
+ +
+ + GET + /r/parents/<INSCRIPTION_ID>/<PAGE> + + +### Description + +The set of 100 parent inscription ids on ``. + +### Example + +```bash +curl -s -H "Accept: application/json" \ + http://0.0.0.0:80/r/parents/b1ef66c2d1a047cbaa6260b74daac43813924378fe08ef8545da4cb79e8fcf00i0/9 +``` + +```json +{ + "ids": [], + "more": false, + "page_index": 9 +} +``` +
+ +
+ + GET + /r/sat/<SAT_NUMBER> + + +### Description + +The first 100 inscription ids on a sat. Requires index with `--index-sats` flag. + +### Example + +```bash +curl -s -H "Accept: application/json" \ + http://0.0.0.0:80/r/sat/153899938226999 +``` + +```json +{ + "ids": [ + "f4ad941ee3892598f34777c4b7b3e2ccccece58ab21aa4364d0d2066daf5b427i3", + "a4bca99fba23122e113bfb9a8010095b2005c4d73fa5b5532de60752b768a3e5i0", + "11b4097bc9ff238c930ed4df44a6a5943ac1b570d424d7e13425244e3f345db7i0", + "488c32e4dfcdc0fa376c2c2af2d572a12f4d33d3245689d1a9f74167f1e14678i0" + ], + "more": false, + "page": 0 +} +``` +
+ +
+ + GET + /r/sat/<SAT_NUMBER>/<PAGE> + + +### Description + +The set of 100 inscription ids on ``. Requires index with `--index-sats` flag. + +### Example + +```bash +curl -s -H "Accept: application/json" \ + http://0.0.0.0:80/r/sat/1499676120331756/1 +``` + +```json +{ + "ids": [ + "c18b2db646cd23b9745bd40a249fc84975b1105a637f3784aa4dbd46a839750fi0", + "7d7c2db251779ea4147ed881daac210bfa416f39846b60e3e6813b713a393d9ai0", + "f42913d8c95f055b586fa9a6c71d2432c7ac860a9a4524c0abf83b1dbe175383i0", + "52fd615dc56a8efb241e4de141692cfa57b1af0ac5d65da7e9d5f12841c2c56ci0", + "cd65b92b9d4080a850eaf2c67c8e0c40c61ecdebeea9ae03936947f981a7b54ai0", + "708ac95fe35bcfef5403f13e5e32c927adb413ce39597abc20f8e8fa4fa1d005i0", + "2399e57a8f598b4487dda149942404e5002321139997280c736dcd0c3a806672i0", + "4a2b37c1e017646a9ba2aa13487ae55b8621972aac349426df680eaf66b90571i0", + "2a7b8b23f2a36bcff7ab23013cd13b303b8797cfac75e88d4daf1a9ddcdbdc6ai0", + "b4cac4e0c9a9ccf6428c1e3869bbbcc0e094d39d972094af21a3ca342a9afedbi0", + "c5f4bb989cc8bca10079287272d07b77b562938eaad35b3dface018cb6ac1c38i0" + ], + "more": false, + "page": 1 +} +``` +
+ +
+ + GET + /r/sat/<SAT_NUMBER>/at/<INDEX> + + +### Description + +The inscription id at `` of all inscriptions on a sat. `` may be a negative number to index from the back. `0` being the first and `-1` being the most recent for example. Requires index with `--index-sats` flag. + +### Example + +```bash +curl -s -H "Accept: application/json" \ + http://0.0.0.0:80/r/sat/153899938226999/at/-1 +``` + +```json +{ + "id": "488c32e4dfcdc0fa376c2c2af2d572a12f4d33d3245689d1a9f74167f1e14678i0" +} +``` +
+ +  +  + +Note: `` only allows the actual number of a sat no other sat +notations like degree, percentile or decimal. We may expand to allow those in +the future. + +Responses from most of the above recursive endpoints are JSON. For backwards +compatibility, some endpoints are supported which only return +plain-text responses. + +- `/blockheight`: latest block height. +- `/blockhash`: latest block hash. +- `/blockhash/`: block hash at given block height. +- `/blocktime`: UNIX time stamp of latest block. See -[examples](examples.md#recursion) for on-chain examples of inscriptions that feature this functionality. \ No newline at end of file +[examples](examples.md#recursion) for on-chain examples of inscriptions that feature this functionality. diff --git a/docs/src/introduction.md b/docs/src/introduction.md index f5c314e9a5..668853247f 100644 --- a/docs/src/introduction.md +++ b/docs/src/introduction.md @@ -32,6 +32,13 @@ When you're ready to get your hands dirty, a good place to start is with [inscriptions](guides/wallet.md), a curious species of digital artifact enabled by ordinal theory. + +Authors +------- + +- [Casey Rodarmor](https://github.com/casey) +- [Raph Japh](https://github.com/raphjaph) + Links ----- diff --git a/justfile b/justfile index 24e51606dc..35e321e426 100644 --- a/justfile +++ b/justfile @@ -46,6 +46,9 @@ deploy-mainnet-bravo branch='master' remote='ordinals/ord': \ deploy-mainnet-charlie branch='master' remote='ordinals/ord': \ (deploy branch remote 'main' 'charlie.ordinals.net') +deploy-regtest branch='master' remote='ordinals/ord': \ + (deploy branch remote 'regtest' 'regtest.ordinals.net') + deploy-signet branch='master' remote='ordinals/ord': \ (deploy branch remote 'signet' 'signet.ordinals.net') @@ -53,6 +56,7 @@ deploy-testnet branch='master' remote='ordinals/ord': \ (deploy branch remote 'test' 'testnet.ordinals.net') deploy-all: \ + deploy-regtest \ deploy-testnet \ deploy-signet \ deploy-mainnet-alpha \ @@ -60,6 +64,7 @@ deploy-all: \ deploy-mainnet-charlie delete-indices: \ + (delete-index "regtest.ordinals.net") \ (delete-index "signet.ordinals.net") \ (delete-index "testnet.ordinals.net") @@ -158,6 +163,9 @@ publish-tag-and-crate revision='master': outdated: cargo outdated --root-deps-only --workspace +unused: + cargo +nightly udeps --workspace + update-modern-normalize: curl \ https://raw.githubusercontent.com/sindresorhus/modern-normalize/main/modern-normalize.css \ diff --git a/splits.yaml b/splits.yaml new file mode 100644 index 0000000000..ef7d4c1547 --- /dev/null +++ b/splits.yaml @@ -0,0 +1,15 @@ +# example split file + +# output fields: +# address: output recipient bitcoin address +# value: output bitcoin value (optional, defaults to minimal-non dust value for `address`) +# runes: output rune value map (values respect rune divisibility) +outputs: +- address: bc1p5d7rjq7g6rdk2yhzks9smlaqtedr4dekq08ge8ztwac72sfr9rusxg3297 + value: 10 sat + runes: + UNCOMMON•GOODS: 1234 + GRIEF•WAGE: 5000000 +- address: 3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy + runes: + HELLO•WORLD: 22.5 diff --git a/src/api.rs b/src/api.rs index edd2f5d348..57def2d02e 100644 --- a/src/api.rs +++ b/src/api.rs @@ -93,6 +93,7 @@ pub struct ChildInscriptions { pub struct Inscription { pub address: Option, pub charms: Vec, + pub child_count: u64, pub children: Vec, pub content_length: Option, pub content_type: Option, @@ -155,11 +156,12 @@ pub struct Inscriptions { pub page_index: u32, } -#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] pub struct Output { pub address: Option>, pub indexed: bool, pub inscriptions: Vec, + pub outpoint: OutPoint, pub runes: BTreeMap, pub sat_ranges: Option>, pub script_pubkey: ScriptBuf, @@ -186,6 +188,7 @@ impl Output { .map(|address| uncheck(&address)), indexed, inscriptions, + outpoint, runes, sat_ranges, script_pubkey: tx_out.script_pubkey, diff --git a/src/index.rs b/src/index.rs index d36a84d4bd..3d79db0d3a 100644 --- a/src/index.rs +++ b/src/index.rs @@ -684,7 +684,7 @@ impl Index { } pub fn export(&self, filename: &String, include_addresses: bool) -> Result { - let mut writer = BufWriter::new(fs::File::create(filename)?); + let mut writer = BufWriter::new(File::create(filename)?); let rtx = self.database.begin_read()?; let blocks_indexed = rtx @@ -2034,9 +2034,13 @@ impl Index { .get(sequence_number + 1)? .map(|guard| InscriptionEntry::load(guard.value()).id); - let children = rtx + let all_children = rtx .open_multimap_table(SEQUENCE_NUMBER_TO_CHILDREN)? - .get(sequence_number)? + .get(sequence_number)?; + + let child_count = all_children.len(); + + let children = all_children .take(4) .map(|result| { result @@ -2107,6 +2111,7 @@ impl Index { }) .map(|address| address.to_string()), charms: Charm::charms(charms), + child_count, children, content_encoding: inscription.content_encoding_str().map(|s| s.to_string()), content_length: inscription.content_length(), diff --git a/src/inscriptions/media.rs b/src/inscriptions/media.rs index cdccbf99ab..a6ebbd97e7 100644 --- a/src/inscriptions/media.rs +++ b/src/inscriptions/media.rs @@ -5,7 +5,6 @@ use { self, BROTLI_MODE_FONT as FONT, BROTLI_MODE_GENERIC as GENERIC, BROTLI_MODE_TEXT as TEXT, }, mp4::{MediaType, Mp4Reader, TrackType}, - std::{fs::File, io::BufReader}, }; #[derive(Debug, PartialEq, Copy, Clone)] diff --git a/src/lib.rs b/src/lib.rs index 1557538e29..526b1f0857 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,6 +41,7 @@ use { consensus::{self, Decodable, Encodable}, hash_types::{BlockHash, TxMerkleNode}, hashes::Hash, + policy::MAX_STANDARD_TX_WEIGHT, script, transaction::Version, Amount, Block, Network, OutPoint, Script, ScriptBuf, Sequence, Transaction, TxIn, TxOut, Txid, @@ -52,7 +53,7 @@ use { clap::{ArgGroup, Parser}, error::{ResultExt, SnafuError}, html_escaper::{Escape, Trusted}, - http::HeaderMap, + http::{HeaderMap, StatusCode}, lazy_static::lazy_static, ordinals::{ varint, Artifact, Charm, Edict, Epoch, Etching, Height, Pile, Rarity, Rune, RuneId, Runestone, @@ -70,8 +71,8 @@ use { env, ffi::OsString, fmt::{self, Display, Formatter}, - fs, - io::{self, Cursor, Read}, + fs::{self, File}, + io::{self, BufReader, Cursor, Read}, mem, net::ToSocketAddrs, path::{Path, PathBuf}, @@ -136,6 +137,7 @@ mod ordzaar; type Result = std::result::Result; type SnafuResult = std::result::Result; +const MAX_STANDARD_OP_RETURN_SIZE: usize = 83; const TARGET_POSTAGE: Amount = Amount::from_sat(10_000); static SHUTTING_DOWN: AtomicBool = AtomicBool::new(false); @@ -199,7 +201,7 @@ fn target_as_block_hash(target: bitcoin::Target) -> BlockHash { BlockHash::from_raw_hash(Hash::from_byte_array(target.to_le_bytes())) } -fn unbound_outpoint() -> OutPoint { +pub fn unbound_outpoint() -> OutPoint { OutPoint { txid: Hash::all_zeros(), vout: 0, diff --git a/src/settings.rs b/src/settings.rs index 7cc5ffb1ff..2fe46fad32 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -74,7 +74,7 @@ impl Settings { }; let config = if let Some(config_path) = config_path { - serde_yaml::from_reader(fs::File::open(&config_path).context(anyhow!( + serde_yaml::from_reader(File::open(&config_path).context(anyhow!( "failed to open config file `{}`", config_path.display() ))?) @@ -994,7 +994,7 @@ mod tests { #[test] fn example_config_file_is_valid() { - let _: Settings = serde_yaml::from_reader(fs::File::open("ord.yaml").unwrap()).unwrap(); + let _: Settings = serde_yaml::from_reader(File::open("ord.yaml").unwrap()).unwrap(); } #[test] diff --git a/src/subcommand.rs b/src/subcommand.rs index 3b607ceee3..d3213303a7 100644 --- a/src/subcommand.rs +++ b/src/subcommand.rs @@ -9,7 +9,7 @@ pub mod index; pub mod list; pub mod parse; pub mod runes; -pub(crate) mod server; +pub mod server; mod settings; pub mod subsidy; pub mod supply; diff --git a/src/subcommand/decode.rs b/src/subcommand/decode.rs index 2e28d6e8dc..2c5f5438b4 100644 --- a/src/subcommand/decode.rs +++ b/src/subcommand/decode.rs @@ -77,7 +77,7 @@ impl Decode { .bitcoin_rpc_client(None)? .get_raw_transaction(&txid, None)? } else if let Some(file) = self.file { - Transaction::consensus_decode(&mut io::BufReader::new(fs::File::open(file)?))? + Transaction::consensus_decode(&mut io::BufReader::new(File::open(file)?))? } else { Transaction::consensus_decode(&mut io::BufReader::new(io::stdin()))? }; diff --git a/src/subcommand/env.rs b/src/subcommand/env.rs index 1e38a8a576..897082f13c 100644 --- a/src/subcommand/env.rs +++ b/src/subcommand/env.rs @@ -203,7 +203,7 @@ rpcport={bitcoind_port} } serde_json::to_writer_pretty( - fs::File::create(self.directory.join("env.json"))?, + File::create(self.directory.join("env.json"))?, &Info { bitcoind_port, ord_port, diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 889a8025a0..ded6afdd0e 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -32,7 +32,7 @@ use { caches::DirCache, AcmeConfig, }, - std::{collections::HashMap, sync::Arc}, + std::{collections::HashMap, str, sync::Arc}, tokio_stream::StreamExt, tower_http::{ compression::CompressionLayer, @@ -42,7 +42,7 @@ use { }, }; -pub(crate) use server_config::ServerConfig; +pub use server_config::ServerConfig; mod accept_encoding; mod accept_json; @@ -56,6 +56,22 @@ enum SpawnConfig { Redirect(String), } +#[derive(Deserialize)] +pub(crate) struct OutputsQuery { + #[serde(rename = "type")] + pub(crate) ty: Option, +} + +#[derive(Clone, Copy, Deserialize, Default, PartialEq)] +#[serde(rename_all = "lowercase")] +pub(crate) enum OutputType { + #[default] + Any, + Cardinal, + Inscribed, + Runic, +} + #[derive(Deserialize)] struct Search { query: String, @@ -215,6 +231,7 @@ impl Server { .route("/ordinal/:sat", get(Self::ordinal)) .route("/output/:output", get(Self::output)) .route("/outputs", post(Self::outputs)) + .route("/outputs/:address", get(Self::outputs_address)) .route("/parents/:inscription_id", get(Self::parents)) .route( "/parents/:inscription_id/:page", @@ -246,6 +263,10 @@ impl Server { "/r/children/:inscription_id/inscriptions/:page", get(Self::child_inscriptions_recursive_paginated), ) + .route( + "/r/undelegated-content/:inscription_id", + get(Self::undelegated_content), + ) .route("/r/metadata/:inscription_id", get(Self::metadata)) .route("/r/parents/:inscription_id", get(Self::parents_recursive)) .route( @@ -265,6 +286,7 @@ impl Server { .route("/rune/:rune", get(Self::rune)) .route("/runes", get(Self::runes)) .route("/runes/:page", get(Self::runes_paginated)) + .route("/runes/balances", get(Self::runes_balances)) .route("/sat/:sat", get(Self::sat)) .route("/satpoint/:satpoint", get(Self::satpoint)) .route("/search", get(Self::search_by_query)) @@ -664,12 +686,16 @@ impl Server { "rune" } else if re::OUTPOINT.is_match(&path) { "output" + } else if re::SATPOINT.is_match(&path) { + "satpoint" } else if re::HASH.is_match(&path) { if index.block_header(path.parse().unwrap())?.is_some() { "block" } else { "tx" } + } else if re::ADDRESS.is_match(&path) { + "address" } else { return Ok(StatusCode::NOT_FOUND.into_response()); }; @@ -815,6 +841,75 @@ impl Server { }) } + async fn outputs_address( + Extension(server_config): Extension>, + Extension(index): Extension>, + AcceptJson(accept_json): AcceptJson, + Path(address): Path>, + Query(query): Query, + ) -> ServerResult { + task::block_in_place(|| { + if !index.has_address_index() { + return Err(ServerError::NotFound( + "this server has no address index".to_string(), + )); + } + + if !accept_json { + return Ok(StatusCode::NOT_FOUND.into_response()); + } + + let output_type = query.ty.unwrap_or_default(); + + if output_type != OutputType::Any { + if !index.has_rune_index() { + return Err(ServerError::BadRequest( + "this server has no runes index".to_string(), + )); + } + + if !index.has_inscription_index() { + return Err(ServerError::BadRequest( + "this server has no inscriptions index".to_string(), + )); + } + } + + let address = address + .require_network(server_config.chain.network()) + .map_err(|err| ServerError::BadRequest(err.to_string()))?; + + let outputs = index.get_address_info(&address)?; + + let mut response = Vec::new(); + for output in outputs.into_iter() { + let include = match output_type { + OutputType::Any => true, + OutputType::Cardinal => { + index + .get_inscriptions_on_output_with_satpoints(output)? + .is_empty() + && index.get_rune_balances_for_output(output)?.is_empty() + } + OutputType::Inscribed => !index + .get_inscriptions_on_output_with_satpoints(output)? + .is_empty(), + OutputType::Runic => !index.get_rune_balances_for_output(output)?.is_empty(), + }; + + if include { + let (output_info, _) = index + .get_output_info(output)? + .ok_or_not_found(|| format!("output {output}"))?; + + response.push(output_info); + } + } + + Ok(Json(response).into_response()) + }) + } + async fn rare_txt(Extension(index): Extension>) -> ServerResult { task::block_in_place(|| Ok(RareTxt(index.rare_sat_satpoints()?))) } @@ -919,6 +1014,34 @@ impl Server { }) } + async fn runes_balances( + Extension(index): Extension>, + AcceptJson(accept_json): AcceptJson, + ) -> ServerResult { + task::block_in_place(|| { + Ok(if accept_json { + Json( + index + .get_rune_balance_map()? + .into_iter() + .map(|(rune, balances)| { + ( + rune, + balances + .into_iter() + .map(|(outpoint, pile)| (outpoint, pile.amount)) + .collect(), + ) + }) + .collect::>>(), + ) + .into_response() + } else { + StatusCode::NOT_FOUND.into_response() + }) + }) + } + async fn home( Extension(server_config): Extension>, Extension(index): Extension>, @@ -1620,6 +1743,30 @@ impl Server { }) } + async fn undelegated_content( + Extension(index): Extension>, + Extension(settings): Extension>, + Extension(server_config): Extension>, + Path(inscription_id): Path, + accept_encoding: AcceptEncoding, + ) -> ServerResult { + task::block_in_place(|| { + if settings.is_hidden(inscription_id) { + return Ok(PreviewUnknownHtml.into_response()); + } + + let inscription = index + .get_inscription_by_id(inscription_id)? + .ok_or_not_found(|| format!("inscription {inscription_id}"))?; + + Ok( + Self::content_response(inscription, accept_encoding, &server_config)? + .ok_or_not_found(|| format!("inscription {inscription_id} content"))? + .into_response(), + ) + }) + } + fn content_response( inscription: Inscription, accept_encoding: AcceptEncoding, @@ -1821,6 +1968,7 @@ impl Server { charm.set(&mut acc); acc })), + child_count: info.child_count, children: info.children, fee: info.fee, height: info.height, @@ -2598,6 +2746,30 @@ mod tests { assert_regex_match!(response.text().unwrap(), regex.as_ref()); } + #[track_caller] + fn assert_html(&self, path: impl AsRef, content: impl PageContent) { + let response = self.get(path); + + assert_eq!( + response.status(), + StatusCode::OK, + "{}", + response.text().unwrap() + ); + + let expected_response = PageHtml::new( + content, + Arc::new(ServerConfig { + chain: Chain::Regtest, + domain: Some(System::host_name().unwrap()), + ..Default::default() + }), + ) + .to_string(); + + pretty_assert_eq!(response.text().unwrap(), expected_response); + } + fn assert_response_csp( &self, path: impl AsRef, @@ -3069,12 +3241,28 @@ mod tests { "/output/4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b:0", ); server.assert_redirect( - "/search/000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f", + "/4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b:0:0", + "/satpoint/4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b:0:0", + ); + server.assert_redirect( + "/000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f", "/block/000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f", ); server.assert_redirect( - "/search/0000000000000000000000000000000000000000000000000000000000000000", - "/tx/0000000000000000000000000000000000000000000000000000000000000000", + "/000000000000000000000000000000000000000000000000000000000000000f", + "/tx/000000000000000000000000000000000000000000000000000000000000000f", + ); + server.assert_redirect( + "/bc1p5d7rjq7g6rdk2yhzks9smlaqtedr4dekq08ge8ztwac72sfr9rusxg3297", + "/address/bc1p5d7rjq7g6rdk2yhzks9smlaqtedr4dekq08ge8ztwac72sfr9rusxg3297", + ); + server.assert_redirect( + "/bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq", + "/address/bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq", + ); + server.assert_redirect( + "/1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2", + "/address/1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2", ); server.assert_response_regex("/hello", StatusCode::NOT_FOUND, ""); @@ -3188,10 +3376,14 @@ mod tests { server.mine_blocks(1); - server.assert_response_regex( + server.assert_html( "/runes", - StatusCode::OK, - ".*Runes.*

Runes

\n
    \n
\n
\n prev\n next\n
.*", + RunesHtml { + entries: Vec::new(), + more: false, + prev: None, + next: None, + }, ); let (txid, id) = server.etch( @@ -3237,14 +3429,23 @@ mod tests { [(OutPoint { txid, vout: 0 }, vec![(id, u128::MAX)])] ); - server.assert_response_regex( + server.assert_html( "/runes", - StatusCode::OK, - ".*Runes.* -

Runes

-.*", + RunesHtml { + entries: vec![( + RuneId::default(), + RuneEntry { + spaced_rune: SpacedRune { + rune: Rune(RUNE), + spacers: 0, + }, + ..default() + }, + )], + more: false, + prev: None, + next: None, + }, ); } @@ -3289,73 +3490,38 @@ mod tests { ), ); - assert_eq!( - server.index.runes().unwrap(), - [( - id, - RuneEntry { - block: id.block, - etching: txid, - spaced_rune: SpacedRune { rune, spacers: 0 }, - premine: u128::MAX, - symbol: Some('%'), - timestamp: id.block, - turbo: true, - ..default() - } - )] - ); + let entry = RuneEntry { + block: id.block, + etching: txid, + spaced_rune: SpacedRune { rune, spacers: 0 }, + premine: u128::MAX, + symbol: Some('%'), + timestamp: id.block, + turbo: true, + ..default() + }; + + assert_eq!(server.index.runes().unwrap(), [(id, entry)]); assert_eq!( server.index.get_rune_balances().unwrap(), [(OutPoint { txid, vout: 0 }, vec![(id, u128::MAX)])] ); - server.assert_response_regex( + let parent = InscriptionId { txid, index: 0 }; + + server.assert_html( format!("/rune/{rune}"), - StatusCode::OK, - format!( - ".*Rune AAAAAAAAAAAAA.* -

AAAAAAAAAAAAA

-.*.* -
-
number
-
0
-
timestamp
-
-
id
-
8:1
-
etching block
-
8
-
etching transaction
-
1
-
mint
-
no
-
supply
-
340282366920938463463374607431768211455\u{A0}%
-
premine
-
340282366920938463463374607431768211455\u{A0}%
-
premine percentage
-
100%
-
burned
-
0\u{A0}%
-
divisibility
-
0
-
symbol
-
%
-
turbo
-
true
-
etching
-
{txid}
-
parent
-
{txid}i0
-
-.*" - ), + RuneHtml { + id, + entry, + mintable: false, + parent: Some(parent), + }, ); server.assert_response_regex( - format!("/inscription/{txid}i0"), + format!("/inscription/{parent}"), StatusCode::OK, ".*
@@ -3666,6 +3832,7 @@ mod tests { sat_ranges: None, indexed: true, inscriptions: Vec::new(), + outpoint: output, runes: vec![( SpacedRune { rune: Rune(RUNE), @@ -3793,7 +3960,7 @@ mod tests {
.*
git commit
- + [[:xdigit:]]{40}
@@ -3946,7 +4113,7 @@ mod tests {
value
5000000000
script pubkey
OP_PUSHBYTES_65 [[:xdigit:]]{{130}} OP_CHECKSIG
-
transaction
{txid}
+
transaction
{txid}
spent
false

1 Sat Range

@@ -3968,7 +4135,7 @@ mod tests {
value
5000000000
script pubkey
OP_PUSHBYTES_65 [[:xdigit:]]{{130}} OP_CHECKSIG
-
transaction
{txid}
+
transaction
{txid}
spent
false
.*" ), @@ -3991,7 +4158,7 @@ mod tests {
value
5000000000
script pubkey
-
transaction
{txid}
+
transaction
{txid}
spent
false

1 Sat Range

@@ -4039,8 +4206,8 @@ mod tests { format!( ".*
id
-
{inscription_id}
.*
output
-
0000000000000000000000000000000000000000000000000000000000000000:0
.*" +
{inscription_id}
.*
output
+
0000000000000000000000000000000000000000000000000000000000000000:0
.*" ), ); @@ -4051,7 +4218,7 @@ mod tests {
inscriptions
- +
.*", ); } @@ -4161,7 +4328,7 @@ mod tests { test_server.assert_response_regex( "/blocks", StatusCode::OK, - ".*
    \n(
  1. [[:xdigit:]]{64}
  2. \n){95}
.*" + ".*
    \n(
  1. [[:xdigit:]]{64}
  2. \n){95}
.*" ); } @@ -4259,12 +4426,12 @@ mod tests {

1 Input

1 Output

  • - + {txid}:0
    @@ -5529,7 +5696,7 @@ next .*.* .*.* .*" ), ); @@ -5596,7 +5763,7 @@ next format!( ".*Inscription 1.* .*
    id
    -.*
    {child0}
    .*" +.*
    {child0}
    .*" ), ); @@ -5611,7 +5778,7 @@ next format!( ".*Inscription -1.* .*
    id
    -.*
    {child1}
    .*" +.*
    {child1}
    .*" ), ); } @@ -5772,7 +5939,7 @@ next ".*

    Inscription 0

    .*
    id
    -
    {inscription_id}
    .*" +
    {inscription_id}
    .*" ), ); server.assert_response_regex( @@ -5782,7 +5949,7 @@ next ".*

    Inscription 0

    .*
    id
    -
    {inscription_id}
    .*" +
    {inscription_id}
    .*" ), ); @@ -5793,7 +5960,7 @@ next ".*

    Inscription -1

    .*
    id
    -
    {cursed_inscription_id}
    .*" +
    {cursed_inscription_id}
    .*" ), ) } @@ -5824,7 +5991,7 @@ next ".*

    Inscription -1

    .*
    id
    -
    {id}
    +
    {id}
    charms
    👹 @@ -5863,7 +6030,7 @@ next ".*

    Inscription 0

    .*
    id
    -
    {id}
    +
    {id}
    .*
    value
    .* @@ -5899,7 +6066,7 @@ next ".*

    Inscription 0

    .*
    id
    -
    {id}
    +
    {id}
    charms
    .*🪙.*
    .* @@ -5935,7 +6102,7 @@ next ".*

    Inscription 0

    .*
    id
    -
    {id}
    +
    {id}
    charms
    .*🌱.*
    .* @@ -5971,7 +6138,7 @@ next ".*

    Inscription 0

    .*
    id
    -
    {id}
    +
    {id}
    charms
    .*9️⃣.*
    .* @@ -6011,7 +6178,7 @@ next ".*

    Inscription -1

    .*
    id
    -
    {id}
    +
    {id}
    charms
    ♻️ @@ -6075,7 +6242,7 @@ next ".*

    Inscription 0

    .*
    id
    -
    {id}
    +
    {id}
    .*
    value
    .* @@ -6136,7 +6303,7 @@ next ".*

    Inscription 0

    .*
    id
    -
    {id}
    +
    {id}
    .*
    value
    .* @@ -6186,7 +6353,7 @@ next ".*

    Inscription -1

    .*
    id
    -
    {id}
    +
    {id}
    charms
    👹 @@ -6222,7 +6389,7 @@ next ".*

    Inscription 0

    .*
    id
    -
    {id}
    +
    {id}
    .*
    value
    5000000000
    @@ -6248,7 +6415,7 @@ next ".*

    Inscription 0

    .*
    id
    -
    {id}
    +
    {id}
    charms
    🤔 @@ -6707,7 +6874,7 @@ next ".*

    Inscription 1

    .*
    id
    -
    {id}
    +
    {id}
    .*
    delegate
    {delegate}
    @@ -6729,6 +6896,81 @@ next ); } + #[test] + fn undelegated_content() { + let server = TestServer::builder().chain(Chain::Regtest).build(); + + server.mine_blocks(1); + + let delegate = Inscription { + content_type: Some("text/plain".into()), + body: Some("foo".into()), + ..default() + }; + + let delegate_txid = server.core.broadcast_tx(TransactionTemplate { + inputs: &[(1, 0, 0, delegate.to_witness())], + ..default() + }); + + let delegate_id = InscriptionId { + txid: delegate_txid, + index: 0, + }; + + server.mine_blocks(1); + + let inscription = Inscription { + content_type: Some("text/plain".into()), + body: Some("bar".into()), + delegate: Some(delegate_id.value()), + ..default() + }; + + let txid = server.core.broadcast_tx(TransactionTemplate { + inputs: &[(2, 0, 0, inscription.to_witness())], + ..default() + }); + + server.mine_blocks(1); + + let id = InscriptionId { txid, index: 0 }; + + server.assert_response( + format!("/r/undelegated-content/{id}"), + StatusCode::OK, + "bar", + ); + + server.assert_response(format!("/content/{id}"), StatusCode::OK, "foo"); + + // Test normal inscription without delegate + let normal_inscription = Inscription { + content_type: Some("text/plain".into()), + body: Some("baz".into()), + ..default() + }; + + let normal_txid = server.core.broadcast_tx(TransactionTemplate { + inputs: &[(3, 0, 0, normal_inscription.to_witness())], + ..default() + }); + + server.mine_blocks(1); + + let normal_id = InscriptionId { + txid: normal_txid, + index: 0, + }; + + server.assert_response( + format!("/r/undelegated-content/{normal_id}"), + StatusCode::OK, + "baz", + ); + server.assert_response(format!("/content/{normal_id}"), StatusCode::OK, "baz"); + } + #[test] fn content_proxy() { let server = TestServer::builder().chain(Chain::Regtest).build(); diff --git a/src/subcommand/server/server_config.rs b/src/subcommand/server/server_config.rs index 8a426888ba..b0406027b5 100644 --- a/src/subcommand/server/server_config.rs +++ b/src/subcommand/server/server_config.rs @@ -1,14 +1,14 @@ use {super::*, axum::http::HeaderName}; #[derive(Default)] -pub(crate) struct ServerConfig { - pub(crate) chain: Chain, - pub(crate) csp_origin: Option, - pub(crate) decompress: bool, - pub(crate) domain: Option, - pub(crate) index_sats: bool, - pub(crate) json_api_enabled: bool, - pub(crate) proxy: Option, +pub struct ServerConfig { + pub chain: Chain, + pub csp_origin: Option, + pub decompress: bool, + pub domain: Option, + pub index_sats: bool, + pub json_api_enabled: bool, + pub proxy: Option, } impl ServerConfig { diff --git a/src/subcommand/verify.rs b/src/subcommand/verify.rs index 1c2dd71a42..75f2e3bd5a 100644 --- a/src/subcommand/verify.rs +++ b/src/subcommand/verify.rs @@ -1,7 +1,15 @@ -use super::*; +use { + super::*, + base64::{engine::general_purpose, Engine}, +}; #[derive(Debug, Parser)] -#[clap(group( +#[clap( +group( + ArgGroup::new("input") + .required(true) + .args(&["message", "file"])), +group( ArgGroup::new("signature") .required(true) .args(&["transaction", "witness"])) @@ -10,7 +18,9 @@ pub(crate) struct Verify { #[arg(long, help = "Verify signature made by
    .")] address: Address, #[arg(long, help = "Verify signature over .")] - message: String, + message: Option, + #[arg(long, help = "Verify signature over contents of .")] + file: Option, #[arg(long, help = "Verify base64-encoded .")] witness: Option, #[arg(long, help = "Verify base64-encoded .")] @@ -19,18 +29,22 @@ pub(crate) struct Verify { impl Verify { pub(crate) fn run(self) -> SubcommandResult { + let message = if let Some(message) = &self.message { + message.as_bytes() + } else if let Some(file) = &self.file { + &fs::read(file)? + } else { + unreachable!() + }; + if let Some(witness) = self.witness { - bip322::verify_simple_encoded( - &self.address.assume_checked().to_string(), - &self.message, - &witness, - )?; + let mut cursor = bitcoin::io::Cursor::new(general_purpose::STANDARD.decode(witness)?); + let witness = Witness::consensus_decode_from_finite_reader(&mut cursor)?; + bip322::verify_simple(&self.address.assume_checked(), message, witness)?; } else if let Some(transaction) = self.transaction { - bip322::verify_full_encoded( - &self.address.assume_checked().to_string(), - &self.message, - &transaction, - )?; + let mut cursor = bitcoin::io::Cursor::new(general_purpose::STANDARD.decode(transaction)?); + let transaction = Transaction::consensus_decode_from_finite_reader(&mut cursor)?; + bip322::verify_full(&self.address.assume_checked(), message, transaction)?; } else { unreachable!(); } diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index e1d7343f07..be2c459f1c 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -4,6 +4,7 @@ use { shared_args::SharedArgs, }; +pub mod addresses; pub mod balance; mod batch_command; pub mod burn; @@ -23,6 +24,8 @@ pub mod runics; pub mod sats; pub mod send; mod shared_args; +pub mod sign; +pub mod split; pub mod transactions; #[derive(Debug, Parser)] @@ -43,6 +46,8 @@ pub(crate) struct WalletCommand { #[derive(Debug, Parser)] #[allow(clippy::large_enum_variant)] pub(crate) enum Subcommand { + #[command(about = "Get wallet addresses")] + Addresses, #[command(about = "Get wallet balance")] Balance, #[command(about = "Create inscriptions and runes")] @@ -79,6 +84,10 @@ pub(crate) enum Subcommand { Sats(sats::Sats), #[command(about = "Send sat or inscription")] Send(send::Send), + #[command(about = "Sign message")] + Sign(sign::Sign), + #[command(about = "Split outputs")] + Split(split::Split), #[command(about = "See wallet transactions")] Transactions(transactions::Transactions), } @@ -106,6 +115,7 @@ impl WalletCommand { )?; match self.subcommand { + Subcommand::Addresses => addresses::run(wallet), Subcommand::Balance => balance::run(wallet), Subcommand::Batch(batch) => batch.run(wallet), Subcommand::Burn(burn) => burn.run(wallet), @@ -123,7 +133,31 @@ impl WalletCommand { Subcommand::Runics => runics::run(wallet), Subcommand::Sats(sats) => sats.run(wallet), Subcommand::Send(send) => send.run(wallet), + Subcommand::Sign(sign) => sign.run(wallet), + Subcommand::Split(split) => split.run(wallet), Subcommand::Transactions(transactions) => transactions.run(wallet), } } + + fn parse_metadata(cbor: Option, json: Option) -> Result>> { + match (cbor, json) { + (None, None) => Ok(None), + (Some(path), None) => { + let cbor = fs::read(path)?; + let _value: Value = ciborium::from_reader(Cursor::new(cbor.clone())) + .context("failed to parse CBOR metadata")?; + + Ok(Some(cbor)) + } + (None, Some(path)) => { + let value: serde_json::Value = + serde_json::from_reader(File::open(path)?).context("failed to parse JSON metadata")?; + let mut cbor = Vec::new(); + ciborium::into_writer(&value, &mut cbor)?; + + Ok(Some(cbor)) + } + (Some(_), Some(_)) => panic!(), + } + } } diff --git a/src/subcommand/wallet/addresses.rs b/src/subcommand/wallet/addresses.rs new file mode 100644 index 0000000000..f8db7b3a06 --- /dev/null +++ b/src/subcommand/wallet/addresses.rs @@ -0,0 +1,59 @@ +use super::*; + +#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)] +pub struct Output { + pub output: OutPoint, + pub amount: u64, + #[serde(skip_serializing_if = "Option::is_none")] + pub inscriptions: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub runes: Option>, +} + +pub(crate) fn run(wallet: Wallet) -> SubcommandResult { + let mut addresses: BTreeMap, Vec> = BTreeMap::new(); + + for (output, txout) in wallet.utxos() { + let address = wallet.chain().address_from_script(&txout.script_pubkey)?; + + let inscriptions = if wallet.has_inscription_index() { + Some(wallet.get_inscriptions_in_output(output)) + } else { + None + }; + + let runes = if wallet.has_rune_index() { + Some( + wallet + .get_runes_balances_in_output(output)? + .iter() + .map(|(rune, pile)| { + ( + *rune, + Decimal { + value: pile.amount, + scale: pile.divisibility, + }, + ) + }) + .collect(), + ) + } else { + None + }; + + let output = Output { + output: *output, + amount: txout.value.to_sat(), + inscriptions, + runes, + }; + + addresses + .entry(address.as_unchecked().clone()) + .or_default() + .push(output); + } + + Ok(Some(Box::new(addresses))) +} diff --git a/src/subcommand/wallet/batch_command.rs b/src/subcommand/wallet/batch_command.rs index 5da34d6348..43e4c79133 100644 --- a/src/subcommand/wallet/batch_command.rs +++ b/src/subcommand/wallet/batch_command.rs @@ -6,7 +6,8 @@ pub(crate) struct Batch { shared: SharedArgs, #[arg( long, - help = "Inscribe multiple inscriptions and rune defined in YAML ." + help = "Inscribe multiple inscriptions and rune defined in YAML .", + value_name = "BATCH_FILE" )] pub(crate) batch: PathBuf, } diff --git a/src/subcommand/wallet/burn.rs b/src/subcommand/wallet/burn.rs index 2268784652..4312987e1d 100644 --- a/src/subcommand/wallet/burn.rs +++ b/src/subcommand/wallet/burn.rs @@ -2,10 +2,32 @@ use {super::*, bitcoin::opcodes}; #[derive(Debug, Parser)] pub struct Burn { + #[arg( + long, + conflicts_with = "json_metadata", + help = "Include CBOR from in OP_RETURN.", + value_name = "PATH" + )] + cbor_metadata: Option, #[arg(long, help = "Don't sign or broadcast transaction.")] dry_run: bool, #[arg(long, help = "Use fee rate of sats/vB.")] fee_rate: FeeRate, + #[arg( + long, + help = "Include JSON from converted to CBOR in OP_RETURN.", + conflicts_with = "cbor_metadata", + value_name = "PATH" + )] + json_metadata: Option, + #[arg( + long, + alias = "nolimit", + help = "Allow OP_RETURN greater than 83 bytes. Transactions over this limit are nonstandard \ + and will not be relayed by bitcoind in its default configuration. Do not use this flag unless \ + you understand the implications." + )] + no_limit: bool, #[arg( long, help = "Target postage with sent inscriptions. [default: 10000 sat]", @@ -23,12 +45,16 @@ impl Burn { .ok_or_else(|| anyhow!("inscription {} not found", self.inscription))? .clone(); + let metadata = WalletCommand::parse_metadata(self.cbor_metadata, self.json_metadata)?; + let Some(value) = inscription_info.value else { bail!("Cannot burn unbound inscription"); }; + let value = Amount::from_sat(value); + ensure! { - value <= TARGET_POSTAGE.to_sat(), + value <= TARGET_POSTAGE, "Cannot burn inscription contained in UTXO exceeding {TARGET_POSTAGE}", } @@ -37,14 +63,38 @@ impl Burn { "Postage may not exceed {TARGET_POSTAGE}", } + let mut builder = script::Builder::new().push_opcode(opcodes::all::OP_RETURN); + + if let Some(metadata) = metadata { + let push: &script::PushBytes = metadata.as_slice().try_into().with_context(|| { + format!( + "metadata length {} over maximum {}", + metadata.len(), + u32::MAX + ) + })?; + builder = builder.push_slice(push); + } + + let script_pubkey = builder.into_script(); + + ensure!( + self.no_limit || script_pubkey.len() <= MAX_STANDARD_OP_RETURN_SIZE, + "OP_RETURN with metadata larger than maximum: {} > {}", + script_pubkey.len(), + MAX_STANDARD_OP_RETURN_SIZE, + ); + let unsigned_transaction = Self::create_unsigned_burn_transaction( &wallet, inscription_info.satpoint, self.postage, self.fee_rate, + script_pubkey, )?; - let (txid, psbt, fee) = wallet.sign_transaction(unsigned_transaction, self.dry_run)?; + let (txid, psbt, fee) = + wallet.sign_and_broadcast_transaction(unsigned_transaction, self.dry_run)?; Ok(Some(Box::new(send::Output { txid, @@ -59,6 +109,7 @@ impl Burn { satpoint: SatPoint, postage: Option, fee_rate: FeeRate, + script_pubkey: ScriptBuf, ) -> Result { let runic_outputs = wallet.get_runic_outputs()?; @@ -71,10 +122,6 @@ impl Burn { let postage = postage.map(Target::ExactPostage).unwrap_or(Target::Postage); - let script_pubkey = script::Builder::new() - .push_opcode(opcodes::all::OP_RETURN) - .into_script(); - Ok( TransactionBuilder::new( satpoint, diff --git a/src/subcommand/wallet/create.rs b/src/subcommand/wallet/create.rs index 768071b0bd..136b8b50e8 100644 --- a/src/subcommand/wallet/create.rs +++ b/src/subcommand/wallet/create.rs @@ -26,7 +26,12 @@ impl Create { let mnemonic = Mnemonic::from_entropy(&entropy)?; - Wallet::initialize(name, settings, mnemonic.to_seed(&self.passphrase))?; + Wallet::initialize( + name, + settings, + mnemonic.to_seed(&self.passphrase), + bitcoincore_rpc::json::Timestamp::Now, + )?; Ok(Some(Box::new(Output { mnemonic, diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index 6a69a71935..c71c652318 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -72,7 +72,7 @@ impl Inscribe { chain, self.shared.compress, self.delegate, - Inscribe::parse_metadata(self.cbor_metadata, self.json_metadata)?, + WalletCommand::parse_metadata(self.cbor_metadata, self.json_metadata)?, self.metaprotocol, self.parent.into_iter().collect(), self.file, @@ -100,25 +100,6 @@ impl Inscribe { &wallet, ) } - - fn parse_metadata(cbor: Option, json: Option) -> Result>> { - if let Some(path) = cbor { - let cbor = fs::read(path)?; - let _value: Value = ciborium::from_reader(Cursor::new(cbor.clone())) - .context("failed to parse CBOR metadata")?; - - Ok(Some(cbor)) - } else if let Some(path) = json { - let value: serde_json::Value = - serde_json::from_reader(fs::File::open(path)?).context("failed to parse JSON metadata")?; - let mut cbor = Vec::new(); - ciborium::into_writer(&value, &mut cbor)?; - - Ok(Some(cbor)) - } else { - Ok(None) - } - } } #[cfg(test)] diff --git a/src/subcommand/wallet/mint.rs b/src/subcommand/wallet/mint.rs index 99fb51f3fe..2715dda891 100644 --- a/src/subcommand/wallet/mint.rs +++ b/src/subcommand/wallet/mint.rs @@ -66,9 +66,10 @@ impl Mint { let script_pubkey = runestone.encipher(); ensure!( - script_pubkey.len() <= 82, - "runestone greater than maximum OP_RETURN size: {} > 82", - script_pubkey.len() + script_pubkey.len() <= MAX_STANDARD_OP_RETURN_SIZE, + "runestone greater than maximum OP_RETURN size: {} > {}", + script_pubkey.len(), + MAX_STANDARD_OP_RETURN_SIZE, ); let unfunded_transaction = Transaction { diff --git a/src/subcommand/wallet/outputs.rs b/src/subcommand/wallet/outputs.rs index c3923123e3..1bddcaeff1 100644 --- a/src/subcommand/wallet/outputs.rs +++ b/src/subcommand/wallet/outputs.rs @@ -6,11 +6,17 @@ pub(crate) struct Outputs { ranges: bool, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, PartialEq)] pub struct Output { pub output: OutPoint, + #[serde(skip_serializing_if = "Option::is_none")] + pub address: Option>, pub amount: u64, #[serde(skip_serializing_if = "Option::is_none")] + pub inscriptions: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub runes: Option>, + #[serde(skip_serializing_if = "Option::is_none")] pub sat_ranges: Option>, } @@ -18,6 +24,38 @@ impl Outputs { pub(crate) fn run(&self, wallet: Wallet) -> SubcommandResult { let mut outputs = Vec::new(); for (output, txout) in wallet.utxos() { + let address = wallet + .chain() + .address_from_script(&txout.script_pubkey) + .ok() + .map(|address| address.as_unchecked().clone()); + + let inscriptions = if wallet.has_inscription_index() { + Some(wallet.get_inscriptions_in_output(output)) + } else { + None + }; + + let runes = if wallet.has_rune_index() { + Some( + wallet + .get_runes_balances_in_output(output)? + .iter() + .map(|(rune, pile)| { + ( + *rune, + Decimal { + value: pile.amount, + scale: pile.divisibility, + }, + ) + }) + .collect(), + ) + } else { + None + }; + let sat_ranges = if wallet.has_sat_index() && self.ranges { Some( wallet @@ -31,8 +69,11 @@ impl Outputs { }; outputs.push(Output { - output: *output, + address, amount: txout.value.to_sat(), + inscriptions, + output: *output, + runes, sat_ranges, }); } diff --git a/src/subcommand/wallet/restore.rs b/src/subcommand/wallet/restore.rs index f4569043c1..a6eaef28e0 100644 --- a/src/subcommand/wallet/restore.rs +++ b/src/subcommand/wallet/restore.rs @@ -1,11 +1,32 @@ use super::*; +#[derive(Debug, Clone)] +pub(crate) struct Timestamp(bitcoincore_rpc::json::Timestamp); + +impl FromStr for Timestamp { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(if s == "now" { + Self(bitcoincore_rpc::json::Timestamp::Now) + } else { + Self(bitcoincore_rpc::json::Timestamp::Time(s.parse()?)) + }) + } +} + #[derive(Debug, Parser)] pub(crate) struct Restore { #[clap(value_enum, long, help = "Restore wallet from on stdin.")] from: Source, - #[arg(long, help = "Use when deriving wallet")] + #[arg(long, help = "Use when deriving wallet.")] pub(crate) passphrase: Option, + #[arg( + long, + help = "Scan chain from onwards. Can be a unix timestamp in \ + seconds or the string `now`, to skip scanning" + )] + pub(crate) timestamp: Option, } #[derive(clap::ValueEnum, Debug, Clone)] @@ -31,10 +52,17 @@ impl Restore { match self.from { Source::Descriptor => { io::stdin().read_to_string(&mut buffer)?; + ensure!( self.passphrase.is_none(), "descriptor does not take a passphrase" ); + + ensure!( + self.timestamp.is_none(), + "descriptor does not take a timestamp" + ); + let wallet_descriptors: ListDescriptorsResult = serde_json::from_str(&buffer)?; Wallet::initialize_from_descriptors(name, settings, wallet_descriptors.descriptors)?; } @@ -45,6 +73,10 @@ impl Restore { name, settings, mnemonic.to_seed(self.passphrase.unwrap_or_default()), + self + .timestamp + .unwrap_or(Timestamp(bitcoincore_rpc::json::Timestamp::Time(0))) + .0, )?; } } diff --git a/src/subcommand/wallet/send.rs b/src/subcommand/wallet/send.rs index b3557f245f..0e86501d37 100644 --- a/src/subcommand/wallet/send.rs +++ b/src/subcommand/wallet/send.rs @@ -73,7 +73,8 @@ impl Send { )?, }; - let (txid, psbt, fee) = wallet.sign_transaction(unsigned_transaction, self.dry_run)?; + let (txid, psbt, fee) = + wallet.sign_and_broadcast_transaction(unsigned_transaction, self.dry_run)?; Ok(Some(Box::new(Output { txid, @@ -195,22 +196,33 @@ impl Send { output, balance .into_iter() - .map(|(spaced_rune, pile)| (spaced_rune.rune, pile)) + .map(|(spaced_rune, pile)| (spaced_rune.rune, pile.amount)) .collect(), ) }) }) - .collect::>>>()?; + .collect::>>>()?; let mut inputs = Vec::new(); let mut input_rune_balances: BTreeMap = BTreeMap::new(); for (output, runes) in balances { if let Some(balance) = runes.get(&spaced_rune.rune) { - if balance.amount > 0 { - *input_rune_balances.entry(spaced_rune.rune).or_default() += balance.amount; + if *balance > 0 { + for (rune, balance) in runes { + *input_rune_balances.entry(rune).or_default() += balance; + } inputs.push(output); + + if input_rune_balances + .get(&spaced_rune.rune) + .cloned() + .unwrap_or_default() + >= amount + { + break; + } } } diff --git a/src/subcommand/wallet/shared_args.rs b/src/subcommand/wallet/shared_args.rs index e9db3b5cb7..d5f6c4d2fb 100644 --- a/src/subcommand/wallet/shared_args.rs +++ b/src/subcommand/wallet/shared_args.rs @@ -18,7 +18,10 @@ pub(super) struct SharedArgs { #[arg( long, alias = "nolimit", - help = "Do not check that transactions are equal to or below the MAX_STANDARD_TX_WEIGHT of 400,000 weight units. Transactions over this limit are currently nonstandard and will not be relayed by bitcoind in its default configuration. Do not use this flag unless you understand the implications." + help = "Allow transactions larger than MAX_STANDARD_TX_WEIGHT of 400,000 weight units and \ + OP_RETURNs greater than 83 bytes. Transactions over this limit are nonstandard and will not be \ + relayed by bitcoind in its default configuration. Do not use this flag unless you understand \ + the implications." )] pub(crate) no_limit: bool, } diff --git a/src/subcommand/wallet/sign.rs b/src/subcommand/wallet/sign.rs new file mode 100644 index 0000000000..4da96578da --- /dev/null +++ b/src/subcommand/wallet/sign.rs @@ -0,0 +1,71 @@ +use { + super::*, + base64::{engine::general_purpose, Engine}, +}; + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub struct Output { + pub address: Address, + pub message: Option, + pub witness: String, +} + +#[derive(Debug, Parser)] +#[clap(group( + ArgGroup::new("input") + .required(true) + .args(&["message", "file"])) +)] +pub(crate) struct Sign { + #[arg(long, help = "Sign for
    .")] + address: Address, + #[arg(long, help = "Sign .")] + message: Option, + #[arg(long, help = "Sign contents of .")] + file: Option, +} + +impl Sign { + pub(crate) fn run(&self, wallet: Wallet) -> SubcommandResult { + let address = &self + .address + .clone() + .require_network(wallet.chain().network())?; + + let message = if let Some(message) = &self.message { + message.as_bytes() + } else if let Some(file) = &self.file { + &fs::read(file)? + } else { + unreachable!() + }; + + let to_spend = bip322::create_to_spend(address, message)?; + + let to_sign = bip322::create_to_sign(&to_spend, None)?; + + let result = wallet.bitcoin_client().sign_raw_transaction_with_wallet( + &to_sign.extract_tx()?, + Some(&[bitcoincore_rpc::json::SignRawTransactionInput { + txid: to_spend.compute_txid(), + vout: 0, + script_pub_key: address.script_pubkey(), + redeem_script: None, + amount: Some(Amount::ZERO), + }]), + None, + )?; + + let mut buffer = Vec::new(); + + Transaction::consensus_decode(&mut result.hex.as_slice())?.input[0] + .witness + .consensus_encode(&mut buffer)?; + + Ok(Some(Box::new(Output { + address: address.as_unchecked().clone(), + message: self.message.clone(), + witness: general_purpose::STANDARD.encode(buffer), + }))) + } +} diff --git a/src/subcommand/wallet/split.rs b/src/subcommand/wallet/split.rs new file mode 100644 index 0000000000..92348a155f --- /dev/null +++ b/src/subcommand/wallet/split.rs @@ -0,0 +1,1421 @@ +use {super::*, splitfile::Splitfile}; + +mod splitfile; + +#[derive(Debug, PartialEq)] +enum Error { + DustOutput { + value: Amount, + threshold: Amount, + output: usize, + }, + DustPostage { + value: Amount, + threshold: Amount, + }, + NoOutputs, + RunestoneSize { + size: usize, + }, + Shortfall { + rune: SpacedRune, + have: Pile, + need: Pile, + }, + ZeroValue { + output: usize, + rune: SpacedRune, + }, +} + +impl Display for Error { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + Self::DustOutput { + value, + threshold, + output, + } => write!( + f, + "output {output} value {value} below dust threshold {threshold}" + ), + Self::DustPostage { value, threshold } => { + write!(f, "postage value {value} below dust threshold {threshold}") + } + Self::NoOutputs => write!(f, "split file must contain at least one output"), + Self::RunestoneSize { size } => write!( + f, + "runestone size {size} over maximum standard OP_RETURN size {MAX_STANDARD_OP_RETURN_SIZE}" + ), + Self::Shortfall { rune, have, need } => { + write!(f, "wallet contains {have} of {rune} but need {need}") + } + Self::ZeroValue { output, rune } => { + write!(f, "output {output} has zero value for rune {rune}") + } + } + } +} + +impl std::error::Error for Error {} + +#[derive(Debug, Parser)] +pub(crate) struct Split { + #[arg(long, help = "Don't sign or broadcast transaction")] + pub(crate) dry_run: bool, + #[arg(long, help = "Use fee rate of sats/vB")] + fee_rate: FeeRate, + #[arg( + long, + help = "Include postage with change output. [default: 10000 sat]", + value_name = "AMOUNT" + )] + pub(crate) postage: Option, + #[arg( + long, + help = "Split outputs multiple inscriptions and rune defined in YAML .", + value_name = "SPLIT_FILE" + )] + pub(crate) splits: PathBuf, + #[arg( + long, + alias = "nolimit", + help = "Allow OP_RETURN greater than 83 bytes. Transactions over this limit are nonstandard \ + and will not be relayed by bitcoind in its default configuration. Do not use this flag unless \ + you understand the implications." + )] + pub(crate) no_limit: bool, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Output { + pub txid: Txid, + pub psbt: String, + pub fee: u64, +} + +impl Split { + pub(crate) fn run(self, wallet: Wallet) -> SubcommandResult { + ensure!( + wallet.has_rune_index(), + "`ord wallet split` requires index created with `--index-runes`", + ); + + wallet.lock_non_cardinal_outputs()?; + + let splits = Splitfile::load(&self.splits, &wallet)?; + + let inscribed_outputs = wallet + .inscriptions() + .keys() + .map(|satpoint| satpoint.outpoint) + .collect::>(); + + let balances = wallet + .get_runic_outputs()? + .into_iter() + .filter(|output| !inscribed_outputs.contains(output)) + .map(|output| { + wallet.get_runes_balances_in_output(&output).map(|balance| { + ( + output, + balance + .into_iter() + .map(|(spaced_rune, pile)| (spaced_rune.rune, pile.amount)) + .collect(), + ) + }) + }) + .collect::>>>()?; + + let unfunded_transaction = Self::build_transaction( + self.no_limit, + balances, + &wallet.get_change_address()?, + self.postage, + &splits, + )?; + + let unsigned_transaction = fund_raw_transaction( + wallet.bitcoin_client(), + self.fee_rate, + &unfunded_transaction, + )?; + + let unsigned_transaction = consensus::encode::deserialize(&unsigned_transaction)?; + + let (txid, psbt, fee) = + wallet.sign_and_broadcast_transaction(unsigned_transaction, self.dry_run)?; + + Ok(Some(Box::new(Output { txid, psbt, fee }))) + } + + fn build_transaction( + no_runestone_limit: bool, + balances: BTreeMap>, + change_address: &Address, + postage: Option, + splits: &Splitfile, + ) -> Result { + if splits.outputs.is_empty() { + return Err(Error::NoOutputs); + } + + let postage = postage.unwrap_or(TARGET_POSTAGE); + + let change_script_pubkey = change_address.script_pubkey(); + + let change_dust_threshold = change_script_pubkey.minimal_non_dust(); + + if postage < change_script_pubkey.minimal_non_dust() { + return Err(Error::DustPostage { + value: postage, + threshold: change_dust_threshold, + }); + } + + let mut input_runes_required = BTreeMap::::new(); + + for (i, output) in splits.outputs.iter().enumerate() { + for (&rune, &amount) in &output.runes { + if amount == 0 { + return Err(Error::ZeroValue { + rune: splits.rune_info[&rune].spaced_rune, + output: i, + }); + } + let required = input_runes_required.entry(rune).or_default(); + *required = (*required).checked_add(amount).unwrap(); + } + } + + let mut input_rune_balances: BTreeMap = BTreeMap::new(); + + let mut inputs = Vec::new(); + + for (output, runes) in balances { + for (rune, required) in &input_runes_required { + if input_rune_balances.get(rune).copied().unwrap_or_default() >= *required { + continue; + } + + if runes.get(rune).copied().unwrap_or_default() == 0 { + continue; + } + + for (rune, balance) in &runes { + *input_rune_balances.entry(*rune).or_default() += balance; + } + + inputs.push(output); + + break; + } + } + + for (&rune, &need) in &input_runes_required { + let have = input_rune_balances.get(&rune).copied().unwrap_or_default(); + if have < need { + let info = splits.rune_info[&rune]; + return Err(Error::Shortfall { + rune: info.spaced_rune, + have: Pile { + amount: have, + divisibility: info.divisibility, + symbol: info.symbol, + }, + need: Pile { + amount: need, + divisibility: info.divisibility, + symbol: info.symbol, + }, + }); + } + } + + let mut need_rune_change_output = false; + for (rune, input) in input_rune_balances { + if input > input_runes_required.get(&rune).copied().unwrap_or_default() { + need_rune_change_output = true; + } + } + + let mut edicts = Vec::new(); + + let base = if need_rune_change_output { 2 } else { 1 }; + + for (i, output) in splits.outputs.iter().enumerate() { + for (rune, amount) in &output.runes { + edicts.push(Edict { + id: splits.rune_info.get(rune).unwrap().id, + amount: *amount, + output: (i + base).try_into().unwrap(), + }); + } + } + + let runestone = Runestone { + edicts, + ..default() + }; + + let mut output = Vec::new(); + + let runestone_script_pubkey = runestone.encipher(); + let size = runestone_script_pubkey.len(); + + if !no_runestone_limit && size > MAX_STANDARD_OP_RETURN_SIZE { + return Err(Error::RunestoneSize { size }); + } + + output.push(TxOut { + script_pubkey: runestone_script_pubkey, + value: Amount::from_sat(0), + }); + + if need_rune_change_output { + output.push(TxOut { + script_pubkey: change_script_pubkey, + value: postage, + }); + } + + for (i, split_output) in splits.outputs.iter().enumerate() { + let script_pubkey = split_output.address.script_pubkey(); + let threshold = script_pubkey.minimal_non_dust(); + let value = split_output.value.unwrap_or(threshold); + if value < threshold { + return Err(Error::DustOutput { + output: i, + threshold, + value, + }); + } + output.push(TxOut { + script_pubkey, + value, + }); + } + + let tx = Transaction { + version: Version(2), + lock_time: LockTime::ZERO, + input: inputs + .into_iter() + .map(|previous_output| TxIn { + previous_output, + script_sig: ScriptBuf::new(), + sequence: Sequence::MAX, + witness: Witness::new(), + }) + .collect(), + output, + }; + + for output in &tx.output { + assert!(output.value >= output.script_pubkey.minimal_non_dust()); + } + + assert_eq!( + Runestone::decipher(&tx), + Some(Artifact::Runestone(runestone)), + ); + + Ok(tx) + } +} + +#[cfg(test)] +mod tests { + use {super::*, splitfile::RuneInfo}; + + #[test] + fn splits_must_have_at_least_one_output() { + assert_eq!( + Split::build_transaction( + false, + BTreeMap::new(), + &change(0), + None, + &Splitfile { + outputs: Vec::new(), + rune_info: BTreeMap::new(), + }, + ) + .unwrap_err(), + Error::NoOutputs, + ); + } + + #[test] + fn postage_may_not_be_dust() { + assert_eq!( + Split::build_transaction( + false, + BTreeMap::new(), + &change(0), + Some(Amount::from_sat(100)), + &Splitfile { + outputs: vec![splitfile::Output { + address: address(0), + runes: [(Rune(0), 1000)].into(), + value: Some(Amount::from_sat(1000)), + }], + rune_info: BTreeMap::new(), + }, + ) + .unwrap_err(), + Error::DustPostage { + value: Amount::from_sat(100), + threshold: Amount::from_sat(294), + }, + ); + } + + #[test] + fn output_rune_value_may_not_be_zero() { + assert_eq!( + Split::build_transaction( + false, + BTreeMap::new(), + &change(0), + None, + &Splitfile { + outputs: vec![splitfile::Output { + address: address(0), + runes: [(Rune(0), 0)].into(), + value: Some(Amount::from_sat(1000)), + }], + rune_info: [( + Rune(0), + RuneInfo { + id: RuneId { block: 1, tx: 1 }, + divisibility: 10, + symbol: Some('@'), + spaced_rune: SpacedRune { + rune: Rune(0), + spacers: 1, + }, + }, + )] + .into() + }, + ) + .unwrap_err(), + Error::ZeroValue { + output: 0, + rune: SpacedRune { + rune: Rune(0), + spacers: 1, + }, + }, + ); + + assert_eq!( + Split::build_transaction( + false, + BTreeMap::new(), + &change(0), + None, + &Splitfile { + outputs: vec![ + splitfile::Output { + address: address(0), + runes: [(Rune(0), 100)].into(), + value: Some(Amount::from_sat(1000)), + }, + splitfile::Output { + address: address(0), + runes: [(Rune(0), 0)].into(), + value: Some(Amount::from_sat(1000)), + }, + ], + rune_info: [( + Rune(0), + RuneInfo { + id: RuneId { block: 1, tx: 1 }, + divisibility: 10, + symbol: Some('@'), + spaced_rune: SpacedRune { + rune: Rune(0), + spacers: 10, + }, + }, + )] + .into() + }, + ) + .unwrap_err(), + Error::ZeroValue { + output: 1, + rune: SpacedRune { + rune: Rune(0), + spacers: 10, + }, + }, + ); + } + + #[test] + fn wallet_must_have_enough_runes() { + assert_eq!( + Split::build_transaction( + false, + BTreeMap::new(), + &change(0), + None, + &Splitfile { + outputs: vec![splitfile::Output { + address: address(0), + runes: [(Rune(0), 1000)].into(), + value: Some(Amount::from_sat(1000)), + }], + rune_info: [( + Rune(0), + RuneInfo { + id: RuneId { block: 1, tx: 1 }, + divisibility: 10, + symbol: Some('@'), + spaced_rune: SpacedRune { + rune: Rune(0), + spacers: 2, + }, + }, + )] + .into(), + }, + ) + .unwrap_err(), + Error::Shortfall { + rune: SpacedRune { + rune: Rune(0), + spacers: 2 + }, + have: Pile { + amount: 0, + divisibility: 10, + symbol: Some('@'), + }, + need: Pile { + amount: 1000, + divisibility: 10, + symbol: Some('@'), + }, + }, + ); + + assert_eq!( + Split::build_transaction( + false, + [(outpoint(0), [(Rune(0), 1000)].into())].into(), + &change(0), + None, + &Splitfile { + outputs: vec![splitfile::Output { + address: address(0), + runes: [(Rune(0), 2000)].into(), + value: Some(Amount::from_sat(1000)), + }], + rune_info: [( + Rune(0), + RuneInfo { + id: RuneId { block: 1, tx: 1 }, + divisibility: 2, + symbol: Some('x'), + spaced_rune: SpacedRune { + rune: Rune(0), + spacers: 1 + }, + }, + )] + .into() + }, + ) + .unwrap_err(), + Error::Shortfall { + rune: SpacedRune { + rune: Rune(0), + spacers: 1, + }, + have: Pile { + amount: 1000, + divisibility: 2, + symbol: Some('x'), + }, + need: Pile { + amount: 2000, + divisibility: 2, + symbol: Some('x'), + }, + }, + ); + } + + #[test] + fn split_output_values_may_not_be_dust() { + assert_eq!( + Split::build_transaction( + false, + [(outpoint(0), [(Rune(0), 1000)].into())].into(), + &change(0), + None, + &Splitfile { + outputs: vec![splitfile::Output { + address: address(0), + runes: [(Rune(0), 1000)].into(), + value: Some(Amount::from_sat(1)), + }], + rune_info: [( + Rune(0), + RuneInfo { + id: RuneId { block: 1, tx: 1 }, + divisibility: 0, + symbol: None, + spaced_rune: SpacedRune { + rune: Rune(0), + spacers: 0, + }, + }, + )] + .into(), + }, + ) + .unwrap_err(), + Error::DustOutput { + value: Amount::from_sat(1), + threshold: Amount::from_sat(294), + output: 0, + } + ); + + assert_eq!( + Split::build_transaction( + false, + [(outpoint(0), [(Rune(0), 2000)].into())].into(), + &change(0), + None, + &Splitfile { + outputs: vec![ + splitfile::Output { + address: address(0), + runes: [(Rune(0), 1000)].into(), + value: Some(Amount::from_sat(1000)), + }, + splitfile::Output { + address: address(0), + runes: [(Rune(0), 1000)].into(), + value: Some(Amount::from_sat(10)), + }, + ], + rune_info: [( + Rune(0), + RuneInfo { + id: RuneId { block: 1, tx: 1 }, + divisibility: 0, + symbol: None, + spaced_rune: SpacedRune { + rune: Rune(0), + spacers: 0, + }, + }, + )] + .into() + }, + ) + .unwrap_err(), + Error::DustOutput { + value: Amount::from_sat(10), + threshold: Amount::from_sat(294), + output: 1, + } + ); + } + + #[test] + fn one_output_no_change() { + let address = address(0); + let output = outpoint(0); + let rune = Rune(0); + let id = RuneId { block: 1, tx: 1 }; + + let balances = [(output, [(rune, 1000)].into())].into(); + + let splits = Splitfile { + outputs: vec![splitfile::Output { + address: address.clone(), + runes: [(rune, 1000)].into(), + value: None, + }], + rune_info: [( + rune, + RuneInfo { + id, + divisibility: 0, + symbol: None, + spaced_rune: SpacedRune { + rune: Rune(0), + spacers: 0, + }, + }, + )] + .into(), + }; + + let tx = Split::build_transaction(false, balances, &change(0), None, &splits).unwrap(); + + pretty_assert_eq!( + tx, + Transaction { + version: Version(2), + lock_time: LockTime::ZERO, + input: vec![TxIn { + previous_output: output, + script_sig: ScriptBuf::new(), + sequence: Sequence::MAX, + witness: Witness::new(), + }], + output: vec![ + TxOut { + value: Amount::from_sat(0), + script_pubkey: Runestone { + edicts: vec![Edict { + id, + amount: 1000, + output: 1 + }], + etching: None, + mint: None, + pointer: None, + } + .encipher() + }, + TxOut { + script_pubkey: address.into(), + value: Amount::from_sat(294), + } + ], + }, + ); + } + + #[test] + fn one_output_with_change_for_outgoing_rune_with_default_postage() { + let address = address(0); + let output = outpoint(0); + let rune = Rune(0); + let id = RuneId { block: 1, tx: 1 }; + let change = change(0); + + let balances = [(output, [(rune, 2000)].into())].into(); + + let splits = Splitfile { + outputs: vec![splitfile::Output { + address: address.clone(), + runes: [(rune, 1000)].into(), + value: None, + }], + rune_info: [( + rune, + RuneInfo { + id, + divisibility: 0, + symbol: None, + spaced_rune: SpacedRune { + rune: Rune(0), + spacers: 0, + }, + }, + )] + .into(), + }; + + let tx = Split::build_transaction(false, balances, &change, None, &splits).unwrap(); + + pretty_assert_eq!( + tx, + Transaction { + version: Version(2), + lock_time: LockTime::ZERO, + input: vec![TxIn { + previous_output: output, + script_sig: ScriptBuf::new(), + sequence: Sequence::MAX, + witness: Witness::new(), + }], + output: vec![ + TxOut { + value: Amount::from_sat(0), + script_pubkey: Runestone { + edicts: vec![Edict { + id, + amount: 1000, + output: 2 + }], + etching: None, + mint: None, + pointer: None, + } + .encipher() + }, + TxOut { + script_pubkey: change.into(), + value: TARGET_POSTAGE, + }, + TxOut { + script_pubkey: address.into(), + value: Amount::from_sat(294), + } + ], + }, + ); + } + + #[test] + fn one_output_with_change_for_outgoing_rune_with_non_default_postage() { + let address = address(0); + let output = outpoint(0); + let rune = Rune(0); + let id = RuneId { block: 1, tx: 1 }; + let change = change(0); + + let balances = [(output, [(rune, 2000)].into())].into(); + + let splits = Splitfile { + outputs: vec![splitfile::Output { + address: address.clone(), + runes: [(rune, 1000)].into(), + value: None, + }], + rune_info: [( + rune, + RuneInfo { + id, + divisibility: 0, + symbol: None, + spaced_rune: SpacedRune { + rune: Rune(0), + spacers: 0, + }, + }, + )] + .into(), + }; + + let tx = Split::build_transaction( + false, + balances, + &change, + Some(Amount::from_sat(500)), + &splits, + ) + .unwrap(); + + pretty_assert_eq!( + tx, + Transaction { + version: Version(2), + lock_time: LockTime::ZERO, + input: vec![TxIn { + previous_output: output, + script_sig: ScriptBuf::new(), + sequence: Sequence::MAX, + witness: Witness::new(), + }], + output: vec![ + TxOut { + value: Amount::from_sat(0), + script_pubkey: Runestone { + edicts: vec![Edict { + id, + amount: 1000, + output: 2 + }], + etching: None, + mint: None, + pointer: None, + } + .encipher() + }, + TxOut { + script_pubkey: change.into(), + value: Amount::from_sat(500), + }, + TxOut { + script_pubkey: address.into(), + value: Amount::from_sat(294), + } + ], + }, + ); + } + + #[test] + fn one_output_with_change_for_non_outgoing_rune() { + let address = address(0); + let output = outpoint(0); + let change = change(0); + + let balances = [(output, [(Rune(0), 1000), (Rune(1), 1000)].into())].into(); + + let splits = Splitfile { + outputs: vec![splitfile::Output { + address: address.clone(), + runes: [(Rune(0), 1000)].into(), + value: None, + }], + rune_info: [( + Rune(0), + RuneInfo { + id: rune_id(0), + divisibility: 0, + symbol: None, + spaced_rune: SpacedRune { + rune: Rune(0), + spacers: 0, + }, + }, + )] + .into(), + }; + + let tx = Split::build_transaction(false, balances, &change, None, &splits).unwrap(); + + pretty_assert_eq!( + tx, + Transaction { + version: Version(2), + lock_time: LockTime::ZERO, + input: vec![TxIn { + previous_output: output, + script_sig: ScriptBuf::new(), + sequence: Sequence::MAX, + witness: Witness::new(), + }], + output: vec![ + TxOut { + value: Amount::from_sat(0), + script_pubkey: Runestone { + edicts: vec![Edict { + id: rune_id(0), + amount: 1000, + output: 2 + }], + etching: None, + mint: None, + pointer: None, + } + .encipher() + }, + TxOut { + script_pubkey: change.into(), + value: TARGET_POSTAGE, + }, + TxOut { + script_pubkey: address.into(), + value: Amount::from_sat(294), + } + ], + }, + ); + } + + #[test] + fn outputs_without_value_use_correct_dust_amount() { + let address = "bc1p5d7rjq7g6rdk2yhzks9smlaqtedr4dekq08ge8ztwac72sfr9rusxg3297" + .parse::>() + .unwrap() + .assume_checked(); + let output = outpoint(0); + let rune = Rune(0); + let id = RuneId { block: 1, tx: 1 }; + + let balances = [(output, [(rune, 1000)].into())].into(); + + let splits = Splitfile { + outputs: vec![splitfile::Output { + address: address.clone(), + runes: [(rune, 1000)].into(), + value: None, + }], + rune_info: [( + rune, + RuneInfo { + id, + divisibility: 0, + symbol: None, + spaced_rune: SpacedRune { + rune: Rune(0), + spacers: 0, + }, + }, + )] + .into(), + }; + + let tx = Split::build_transaction(false, balances, &change(0), None, &splits).unwrap(); + + pretty_assert_eq!( + tx, + Transaction { + version: Version(2), + lock_time: LockTime::ZERO, + input: vec![TxIn { + previous_output: output, + script_sig: ScriptBuf::new(), + sequence: Sequence::MAX, + witness: Witness::new(), + }], + output: vec![ + TxOut { + value: Amount::from_sat(0), + script_pubkey: Runestone { + edicts: vec![Edict { + id, + amount: 1000, + output: 1 + }], + etching: None, + mint: None, + pointer: None, + } + .encipher() + }, + TxOut { + script_pubkey: address.into(), + value: Amount::from_sat(330), + } + ], + }, + ); + } + + #[test] + fn excessive_inputs_are_not_selected() { + let address = address(0); + let output = outpoint(0); + let rune = Rune(0); + let id = RuneId { block: 1, tx: 1 }; + + let balances = [ + (output, [(rune, 1000)].into()), + (outpoint(1), [(rune, 1000)].into()), + ] + .into(); + + let splits = Splitfile { + outputs: vec![splitfile::Output { + address: address.clone(), + runes: [(rune, 1000)].into(), + value: None, + }], + rune_info: [( + rune, + RuneInfo { + id, + divisibility: 0, + symbol: None, + spaced_rune: SpacedRune { + rune: Rune(0), + spacers: 0, + }, + }, + )] + .into(), + }; + + let tx = Split::build_transaction(false, balances, &change(0), None, &splits).unwrap(); + + pretty_assert_eq!( + tx, + Transaction { + version: Version(2), + lock_time: LockTime::ZERO, + input: vec![TxIn { + previous_output: output, + script_sig: ScriptBuf::new(), + sequence: Sequence::MAX, + witness: Witness::new(), + }], + output: vec![ + TxOut { + value: Amount::from_sat(0), + script_pubkey: Runestone { + edicts: vec![Edict { + id, + amount: 1000, + output: 1 + }], + etching: None, + mint: None, + pointer: None, + } + .encipher() + }, + TxOut { + script_pubkey: address.into(), + value: Amount::from_sat(294), + } + ], + }, + ); + } + + #[test] + fn multiple_inputs_may_be_selected() { + let address = address(0); + let rune = Rune(0); + let id = RuneId { block: 1, tx: 1 }; + + let balances = [ + (outpoint(0), [(rune, 1000)].into()), + (outpoint(1), [(rune, 1000)].into()), + ] + .into(); + + let splits = Splitfile { + outputs: vec![splitfile::Output { + address: address.clone(), + runes: [(rune, 2000)].into(), + value: None, + }], + rune_info: [( + rune, + RuneInfo { + id, + divisibility: 0, + symbol: None, + spaced_rune: SpacedRune { + rune: Rune(0), + spacers: 0, + }, + }, + )] + .into(), + }; + + let tx = Split::build_transaction(false, balances, &change(0), None, &splits).unwrap(); + + pretty_assert_eq!( + tx, + Transaction { + version: Version(2), + lock_time: LockTime::ZERO, + input: vec![ + TxIn { + previous_output: outpoint(0), + script_sig: ScriptBuf::new(), + sequence: Sequence::MAX, + witness: Witness::new(), + }, + TxIn { + previous_output: outpoint(1), + script_sig: ScriptBuf::new(), + sequence: Sequence::MAX, + witness: Witness::new(), + }, + ], + output: vec![ + TxOut { + value: Amount::from_sat(0), + script_pubkey: Runestone { + edicts: vec![Edict { + id, + amount: 2000, + output: 1 + }], + etching: None, + mint: None, + pointer: None, + } + .encipher() + }, + TxOut { + script_pubkey: address.into(), + value: Amount::from_sat(294), + } + ], + }, + ); + } + + #[test] + fn two_outputs_no_change() { + let output = outpoint(0); + let rune = Rune(0); + let id = RuneId { block: 1, tx: 1 }; + + let balances = [(output, [(rune, 1000)].into())].into(); + + let splits = Splitfile { + outputs: vec![ + splitfile::Output { + address: address(0), + runes: [(rune, 500)].into(), + value: None, + }, + splitfile::Output { + address: address(1), + runes: [(rune, 500)].into(), + value: None, + }, + ], + rune_info: [( + rune, + RuneInfo { + id, + divisibility: 0, + symbol: None, + spaced_rune: SpacedRune { + rune: Rune(0), + spacers: 0, + }, + }, + )] + .into(), + }; + + let tx = Split::build_transaction(false, balances, &change(0), None, &splits).unwrap(); + + pretty_assert_eq!( + tx, + Transaction { + version: Version(2), + lock_time: LockTime::ZERO, + input: vec![TxIn { + previous_output: output, + script_sig: ScriptBuf::new(), + sequence: Sequence::MAX, + witness: Witness::new(), + }], + output: vec![ + TxOut { + value: Amount::from_sat(0), + script_pubkey: Runestone { + edicts: vec![ + Edict { + id, + amount: 500, + output: 1 + }, + Edict { + id, + amount: 500, + output: 2 + } + ], + etching: None, + mint: None, + pointer: None, + } + .encipher() + }, + TxOut { + script_pubkey: address(0).into(), + value: Amount::from_sat(294), + }, + TxOut { + script_pubkey: address(1).into(), + value: Amount::from_sat(294), + } + ], + }, + ); + } + + #[test] + fn outputs_may_receive_multiple_runes() { + let address = address(0); + + let balances = [ + (outpoint(0), [(Rune(0), 1000)].into()), + (outpoint(1), [(Rune(1), 2000)].into()), + ] + .into(); + + let splits = Splitfile { + outputs: vec![splitfile::Output { + address: address.clone(), + runes: [(Rune(0), 1000), (Rune(1), 2000)].into(), + value: None, + }], + rune_info: [ + ( + Rune(0), + RuneInfo { + id: rune_id(0), + divisibility: 0, + symbol: None, + spaced_rune: SpacedRune { + rune: Rune(0), + spacers: 0, + }, + }, + ), + ( + Rune(1), + RuneInfo { + id: rune_id(1), + divisibility: 0, + symbol: None, + spaced_rune: SpacedRune { + rune: Rune(1), + spacers: 0, + }, + }, + ), + ] + .into(), + }; + + let tx = Split::build_transaction(false, balances, &change(0), None, &splits).unwrap(); + + pretty_assert_eq!( + tx, + Transaction { + version: Version(2), + lock_time: LockTime::ZERO, + input: vec![ + TxIn { + previous_output: outpoint(0), + script_sig: ScriptBuf::new(), + sequence: Sequence::MAX, + witness: Witness::new(), + }, + TxIn { + previous_output: outpoint(1), + script_sig: ScriptBuf::new(), + sequence: Sequence::MAX, + witness: Witness::new(), + }, + ], + output: vec![ + TxOut { + value: Amount::from_sat(0), + script_pubkey: Runestone { + edicts: vec![ + Edict { + id: rune_id(0), + amount: 1000, + output: 1 + }, + Edict { + id: rune_id(1), + amount: 2000, + output: 1 + }, + ], + etching: None, + mint: None, + pointer: None, + } + .encipher() + }, + TxOut { + script_pubkey: address.into(), + value: Amount::from_sat(294), + } + ], + }, + ); + } + + #[test] + fn oversize_op_return_is_an_error() { + let balances = [(outpoint(0), [(Rune(0), 10_000_000_000)].into())].into(); + + let splits = Splitfile { + outputs: (0..10) + .map(|i| splitfile::Output { + address: address(i).clone(), + runes: [(Rune(0), 1_000_000_000)].into(), + value: None, + }) + .collect(), + rune_info: [( + Rune(0), + RuneInfo { + id: rune_id(0), + divisibility: 0, + symbol: None, + spaced_rune: SpacedRune { + rune: Rune(0), + spacers: 0, + }, + }, + )] + .into(), + }; + + assert_eq!( + Split::build_transaction(false, balances, &change(0), None, &splits).unwrap_err(), + Error::RunestoneSize { size: 85 }, + ); + } + + #[test] + fn oversize_op_return_is_allowed_with_flag() { + let balances = [(outpoint(0), [(Rune(0), 10_000_000_000)].into())].into(); + + let splits = Splitfile { + outputs: (0..10) + .map(|i| splitfile::Output { + address: address(i).clone(), + runes: [(Rune(0), 1_000_000_000)].into(), + value: None, + }) + .collect(), + rune_info: [( + Rune(0), + RuneInfo { + id: rune_id(0), + divisibility: 0, + symbol: None, + spaced_rune: SpacedRune { + rune: Rune(0), + spacers: 0, + }, + }, + )] + .into(), + }; + + pretty_assert_eq!( + Split::build_transaction(true, balances, &change(0), None, &splits).unwrap(), + Transaction { + version: Version(2), + lock_time: LockTime::ZERO, + input: vec![TxIn { + previous_output: outpoint(0), + script_sig: ScriptBuf::new(), + sequence: Sequence::MAX, + witness: Witness::new(), + }], + output: (0..11) + .map(|i| if i == 0 { + TxOut { + value: Amount::from_sat(0), + script_pubkey: Runestone { + edicts: (0..10) + .map(|i| Edict { + id: rune_id(0), + amount: 1_000_000_000, + output: i + 1, + }) + .collect(), + etching: None, + mint: None, + pointer: None, + } + .encipher(), + } + } else { + TxOut { + script_pubkey: address(i - 1).into(), + value: Amount::from_sat(294), + } + }) + .collect() + } + ); + } +} diff --git a/src/subcommand/wallet/split/splitfile.rs b/src/subcommand/wallet/split/splitfile.rs new file mode 100644 index 0000000000..751b542bf4 --- /dev/null +++ b/src/subcommand/wallet/split/splitfile.rs @@ -0,0 +1,96 @@ +use super::*; + +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +struct SplitfileUnchecked { + outputs: Vec, +} + +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +struct OutputUnchecked { + address: Address, + value: Option>, + runes: BTreeMap, +} + +pub(crate) struct Splitfile { + pub(crate) outputs: Vec, + pub(crate) rune_info: BTreeMap, +} + +pub(crate) struct Output { + pub(crate) address: Address, + pub(crate) value: Option, + pub(crate) runes: BTreeMap, +} + +#[derive(Clone, Copy)] +pub(crate) struct RuneInfo { + pub(crate) divisibility: u8, + pub(crate) id: RuneId, + pub(crate) spaced_rune: SpacedRune, + pub(crate) symbol: Option, +} + +impl Splitfile { + pub(crate) fn load(path: &Path, wallet: &Wallet) -> Result { + let network = wallet.chain().network(); + + let unchecked = Self::load_unchecked(path)?; + + let mut rune_info = BTreeMap::::new(); + + let mut outputs = Vec::new(); + + for output in unchecked.outputs { + let mut runes = BTreeMap::new(); + + for (spaced_rune, decimal) in output.runes { + let info = if let Some(info) = rune_info.get(&spaced_rune.rune) { + info + } else { + let (id, entry, _parent) = wallet + .get_rune(spaced_rune.rune)? + .with_context(|| format!("rune `{}` has not been etched", spaced_rune.rune))?; + rune_info.insert( + spaced_rune.rune, + RuneInfo { + divisibility: entry.divisibility, + id, + spaced_rune: entry.spaced_rune, + symbol: entry.symbol, + }, + ); + rune_info.get(&spaced_rune.rune).unwrap() + }; + + let amount = decimal.to_integer(info.divisibility)?; + + runes.insert(spaced_rune.rune, amount); + } + + outputs.push(Output { + address: output.address.require_network(network)?, + value: output.value.map(|DeserializeFromStr(value)| value), + runes, + }); + } + + Ok(Self { outputs, rune_info }) + } + + fn load_unchecked(path: &Path) -> Result { + Ok(serde_yaml::from_reader(File::open(path)?)?) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn example_split_file_is_valid() { + Splitfile::load_unchecked("splits.yaml".as_ref()).unwrap(); + } +} diff --git a/src/templates.rs b/src/templates.rs index a0eec45dcc..7898d2a5f8 100644 --- a/src/templates.rs +++ b/src/templates.rs @@ -10,7 +10,6 @@ pub(crate) use { home::HomeHtml, iframe::Iframe, input::InputHtml, - inscription::InscriptionHtml, inscriptions::InscriptionsHtml, inscriptions_block::InscriptionsBlockHtml, metadata::MetadataHtml, @@ -25,8 +24,8 @@ pub(crate) use { }; pub use { - blocks::BlocksHtml, rune::RuneHtml, runes::RunesHtml, status::StatusHtml, - transaction::TransactionHtml, + blocks::BlocksHtml, inscription::InscriptionHtml, rune::RuneHtml, runes::RunesHtml, + status::StatusHtml, transaction::TransactionHtml, }; pub mod address; @@ -53,7 +52,7 @@ pub mod status; pub mod transaction; #[derive(Boilerplate)] -pub(crate) struct PageHtml { +pub struct PageHtml { content: T, config: Arc, } @@ -62,7 +61,7 @@ impl PageHtml where T: PageContent, { - pub(crate) fn new(content: T, config: Arc) -> Self { + pub fn new(content: T, config: Arc) -> Self { Self { content, config } } @@ -83,7 +82,7 @@ where } } -pub(crate) trait PageContent: Display + 'static { +pub trait PageContent: Display + 'static { fn title(&self) -> String; fn page(self, server_config: Arc) -> PageHtml @@ -137,7 +136,7 @@ mod tests { - +
    diff --git a/src/templates/address.rs b/src/templates/address.rs index db4cd29003..6eb8ebe5e0 100644 --- a/src/templates/address.rs +++ b/src/templates/address.rs @@ -87,7 +87,7 @@ mod tests { #[test] fn test_outputs_rendering() { let address_html = setup(); - let expected_pattern = r#".*
    outputs
    \n\s*
    \n\s*\n\s*
    .*"#; + let expected_pattern = r#".*
    outputs
    \n\s*
    \n\s*\n\s*
    .*"#; assert_regex_match!(address_html, expected_pattern); } } diff --git a/src/templates/block.rs b/src/templates/block.rs index 1f2e8088f3..4bc2425631 100644 --- a/src/templates/block.rs +++ b/src/templates/block.rs @@ -58,8 +58,8 @@ mod tests { "

    Block 0

    -
    hash
    [[:xdigit:]]{64}
    -
    target
    [[:xdigit:]]{64}
    +
    hash
    [[:xdigit:]]{64}
    +
    target
    [[:xdigit:]]{64}
    timestamp
    size
    285
    weight
    1140
    @@ -73,8 +73,8 @@ mod tests {

    1 Transaction

    -
      -
    • [[:xdigit:]]{64}
    • + " .unindent() diff --git a/src/templates/blocks.rs b/src/templates/blocks.rs index 8bf1747d8f..6ef4b3c7b6 100644 --- a/src/templates/blocks.rs +++ b/src/templates/blocks.rs @@ -79,8 +79,8 @@ mod tests {
        -
      1. 1{64}
      2. -
      3. 0{64}
      4. +
      5. 1{64}
      6. +
      7. 0{64}
      " .unindent(), diff --git a/src/templates/iframe.rs b/src/templates/iframe.rs index 0880a17863..f7d00f5ccf 100644 --- a/src/templates/iframe.rs +++ b/src/templates/iframe.rs @@ -24,20 +24,21 @@ impl Iframe { impl Display for Iframe { fn fmt(&self, f: &mut Formatter) -> fmt::Result { if self.thumbnail { - write!(f, "", self.inscription_id)?; + write!( + f, + "\ + \ + ", + self.inscription_id, self.inscription_id, + ) + } else { + write!( + f, + "", + self.inscription_id, + ) } - - write!( - f, - "", - self.inscription_id - )?; - - if self.thumbnail { - write!(f, "",)? - } - - Ok(()) } } @@ -50,7 +51,7 @@ mod tests { assert_regex_match!( Iframe::thumbnail(inscription_id(1)) .0.to_string(), - "", + "", ); } diff --git a/src/templates/input.rs b/src/templates/input.rs index 763758eac1..7aca6f1a1d 100644 --- a/src/templates/input.rs +++ b/src/templates/input.rs @@ -39,7 +39,7 @@ mod tests { "

      Input /1/2/3

      -
      previous output
      0000000000000000000000000000000000000000000000000000000000000000:0
      +
      previous output
      0000000000000000000000000000000000000000000000000000000000000000:0
      witness
      010101
      script sig
      OP_PUSHBYTES_3 666f6f
      text
      \x03foo
      diff --git a/src/templates/inscription.rs b/src/templates/inscription.rs index 3ff2f05d7b..2cfe83d836 100644 --- a/src/templates/inscription.rs +++ b/src/templates/inscription.rs @@ -1,23 +1,24 @@ use super::*; #[derive(Boilerplate, Default)] -pub(crate) struct InscriptionHtml { - pub(crate) chain: Chain, - pub(crate) charms: u16, - pub(crate) children: Vec, - pub(crate) fee: u64, - pub(crate) height: u32, - pub(crate) inscription: Inscription, - pub(crate) id: InscriptionId, - pub(crate) number: i32, - pub(crate) next: Option, - pub(crate) output: Option, - pub(crate) parents: Vec, - pub(crate) previous: Option, - pub(crate) rune: Option, - pub(crate) sat: Option, - pub(crate) satpoint: SatPoint, - pub(crate) timestamp: DateTime, +pub struct InscriptionHtml { + pub chain: Chain, + pub charms: u16, + pub child_count: u64, + pub children: Vec, + pub fee: u64, + pub height: u32, + pub inscription: Inscription, + pub id: InscriptionId, + pub number: i32, + pub next: Option, + pub output: Option, + pub parents: Vec, + pub previous: Option, + pub rune: Option, + pub sat: Option, + pub satpoint: SatPoint, + pub timestamp: DateTime, } impl PageContent for InscriptionHtml { @@ -26,6 +27,23 @@ impl PageContent for InscriptionHtml { } } +impl InscriptionHtml { + pub fn burn_metadata(&self) -> Option { + let script_pubkey = &self.output.as_ref()?.script_pubkey; + + if !script_pubkey.is_op_return() { + return None; + } + + let script::Instruction::PushBytes(metadata) = script_pubkey.instructions().nth(1)?.ok()? + else { + return None; + }; + + ciborium::from_reader(Cursor::new(metadata)).ok() + } +} + #[cfg(test)] mod tests { use super::*; @@ -50,7 +68,7 @@ mod tests {
      id
      -
      1{64}i1
      +
      1{64}i1
      preview
      link
      content
      @@ -66,15 +84,15 @@ mod tests {
      fee
      1
      reveal transaction
      -
      1{64}
      +
      1{64}
      location
      -
      1{64}:1:0
      +
      1{64}:1:0
      output
      -
      1{64}:1
      +
      1{64}:1
      offset
      0
      ethereum teleburn address
      -
      0xa1DfBd1C519B9323FD7Fd8e498Ac16c2E502F059
      +
      0xa1DfBd1C519B9323FD7Fd8e498Ac16c2E502F059
      " .unindent() @@ -89,7 +107,7 @@ mod tests { inscription: inscription("text/plain;charset=utf-8", "HELLOWORLD"), id: inscription_id(1), number: 1, - output: Some(tx_out(1, address())), + output: Some(tx_out(1, address(0))), satpoint: satpoint(1, 0), ..default() }, @@ -103,7 +121,7 @@ mod tests {
      .*
      address
      -
      bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4
      +
      bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4
      value
      1
      .* @@ -121,7 +139,7 @@ mod tests { inscription: inscription("text/plain;charset=utf-8", "HELLOWORLD"), id: inscription_id(1), number: 1, - output: Some(tx_out(1, address())), + output: Some(tx_out(1, address(0))), sat: Some(Sat(1)), satpoint: satpoint(1, 0), ..default() @@ -153,7 +171,7 @@ mod tests { id: inscription_id(2), next: Some(inscription_id(3)), number: 1, - output: Some(tx_out(1, address())), + output: Some(tx_out(1, address(0))), previous: Some(inscription_id(1)), satpoint: satpoint(1, 0), ..default() @@ -179,7 +197,7 @@ mod tests { inscription: inscription("text/plain;charset=utf-8", "HELLOWORLD"), id: inscription_id(2), number: -1, - output: Some(tx_out(1, address())), + output: Some(tx_out(1, address(0))), satpoint: SatPoint { outpoint: unbound_outpoint(), offset: 0 @@ -193,9 +211,9 @@ mod tests {
      .*
      location
      -
      0{64}:0:0
      +
      0{64}:0:0
      output
      -
      0{64}:0
      +
      0{64}:0
      .*
      " @@ -233,7 +251,7 @@ mod tests {
    id
    -
    1{64}i1
    +
    1{64}i1
    preview
    link
    content
    @@ -249,15 +267,15 @@ mod tests {
    fee
    1
    reveal transaction
    -
    1{64}
    +
    1{64}
    location
    -
    1{64}:1:0
    +
    1{64}:1:0
    output
    -
    1{64}:1
    +
    1{64}:1
    offset
    0
    ethereum teleburn address
    -
    0xa1DfBd1C519B9323FD7Fd8e498Ac16c2E502F059
    +
    0xa1DfBd1C519B9323FD7Fd8e498Ac16c2E502F059
    " .unindent() @@ -268,6 +286,7 @@ mod tests { fn with_children() { assert_regex_match!( InscriptionHtml { + child_count: 2, children: vec![inscription_id(2), inscription_id(3)], fee: 1, inscription: inscription("text/plain;charset=utf-8", "HELLOWORLD"), @@ -291,11 +310,11 @@ mod tests {
    id
    -
    1{64}i1
    +
    1{64}i1
    preview
    link
    content
    @@ -311,15 +330,15 @@ mod tests {
    fee
    1
    reveal transaction
    -
    1{64}
    +
    1{64}
    location
    -
    1{64}:1:0
    +
    1{64}:1:0
    output
    -
    1{64}:1
    +
    1{64}:1
    offset
    0
    ethereum teleburn address
    -
    0xa1DfBd1C519B9323FD7Fd8e498Ac16c2E502F059
    +
    0xa1DfBd1C519B9323FD7Fd8e498Ac16c2E502F059
    " .unindent() @@ -330,6 +349,7 @@ mod tests { fn with_paginated_children() { assert_regex_match!( InscriptionHtml { + child_count: 1, children: vec![inscription_id(2)], fee: 1, inscription: inscription("text/plain;charset=utf-8", "HELLOWORLD"), @@ -352,11 +372,11 @@ mod tests {
    id
    -
    1{64}i1
    +
    1{64}i1
    preview
    link
    content
    @@ -372,15 +392,15 @@ mod tests {
    fee
    1
    reveal transaction
    -
    1{64}
    +
    1{64}
    location
    -
    1{64}:1:0
    +
    1{64}:1:0
    output
    -
    1{64}:1
    +
    1{64}:1
    offset
    0
    ethereum teleburn address
    -
    0xa1DfBd1C519B9323FD7Fd8e498Ac16c2E502F059
    +
    0xa1DfBd1C519B9323FD7Fd8e498Ac16c2E502F059
    " .unindent() @@ -442,4 +462,45 @@ mod tests { .unindent() ); } + + #[test] + fn with_burn_metadata() { + let script_pubkey = script::Builder::new() + .push_opcode(opcodes::all::OP_RETURN) + .push_slice([ + 0xA2, 0x63, b'f', b'o', b'o', 0x63, b'b', b'a', b'r', 0x63, b'b', b'a', b'z', 0x01, + ]) + .into_script(); + + assert_regex_match!( + InscriptionHtml { + fee: 1, + inscription: Inscription { + content_encoding: Some("br".into()), + ..inscription("text/plain;charset=utf-8", "HELLOWORLD") + }, + id: inscription_id(1), + number: 1, + satpoint: satpoint(1, 0), + output: Some(TxOut { + value: Amount::from_sat(1), + script_pubkey, + }), + ..default() + }, + " +

    Inscription 1

    + .* +
    + .* +
    burn metadata
    +
    +
    foo
    bar
    baz
    1
    +
    + .* +
    + " + .unindent() + ); + } } diff --git a/src/templates/output.rs b/src/templates/output.rs index 31af3ca034..49e3f8107c 100644 --- a/src/templates/output.rs +++ b/src/templates/output.rs @@ -41,8 +41,8 @@ mod tests {
    value
    3
    script pubkey
    OP_DUP OP_HASH160 OP_PUSHBYTES_20 0{40} OP_EQUALVERIFY OP_CHECKSIG
    -
    address
    1111111111111111111114oLvT2
    -
    transaction
    1{64}
    +
    address
    1111111111111111111114oLvT2
    +
    transaction
    1{64}
    spent
    false

    2 Sat Ranges

    @@ -75,7 +75,7 @@ mod tests {
    value
    1
    script pubkey
    OP_0
    -
    transaction
    1{64}
    +
    transaction
    1{64}
    spent
    true
    " @@ -100,8 +100,8 @@ mod tests {
    value
    3
    script pubkey
    OP_DUP OP_HASH160 OP_PUSHBYTES_20 0{40} OP_EQUALVERIFY OP_CHECKSIG
    -
    address
    1111111111111111111114oLvT2
    -
    transaction
    1{64}
    +
    address
    1111111111111111111114oLvT2
    +
    transaction
    1{64}
    spent
    true

    2 Sat Ranges

    @@ -132,8 +132,8 @@ mod tests {
    value
    3
    script pubkey
    OP_DUP OP_HASH160 OP_PUSHBYTES_20 0{40} OP_EQUALVERIFY OP_CHECKSIG
    -
    address
    1111111111111111111114oLvT2
    -
    transaction
    1{64}
    +
    address
    1111111111111111111114oLvT2
    +
    transaction
    1{64}
    spent
    false
    " diff --git a/src/templates/rune.rs b/src/templates/rune.rs index 3d1671b70d..d7e4515b38 100644 --- a/src/templates/rune.rs +++ b/src/templates/rune.rs @@ -122,9 +122,9 @@ mod tests {
    turbo
    true
    etching
    -
    0{64}
    +
    0{64}
    parent
    -
    0{64}i0
    +
    0{64}i0
    " ); diff --git a/src/templates/sat.rs b/src/templates/sat.rs index b0428422ec..e9366626e4 100644 --- a/src/templates/sat.rs +++ b/src/templates/sat.rs @@ -45,6 +45,7 @@ mod tests {
    🪙 🎃 + 🦋
    .* @@ -171,7 +172,7 @@ mod tests { blocktime: Blocktime::confirmed(0), inscriptions: Vec::new(), }, - "

    Sat 0

    .*
    location
    1{64}:1:0
    .*", + "

    Sat 0

    .*
    location
    1{64}:1:0
    .*", ); } } diff --git a/src/templates/transaction.rs b/src/templates/transaction.rs index 3cdffce40a..a353e8a7ba 100644 --- a/src/templates/transaction.rs +++ b/src/templates/transaction.rs @@ -59,12 +59,12 @@ mod tests {

    1 Input

    2 Outputs

    • - + {txid}:0
      @@ -73,7 +73,7 @@ mod tests {
    • - + {txid}:1
      diff --git a/src/test.rs b/src/test.rs index f4aa2c1176..748fec9990 100644 --- a/src/test.rs +++ b/src/test.rs @@ -12,7 +12,11 @@ pub(crate) use { unindent::Unindent, }; -pub(crate) fn txid(n: u64) -> Txid { +pub(crate) fn rune_id(tx: u32) -> RuneId { + RuneId { block: 1, tx } +} + +pub(crate) fn txid(n: u32) -> Txid { let hex = format!("{n:x}"); if hex.is_empty() || hex.len() > 1 { @@ -22,22 +26,37 @@ pub(crate) fn txid(n: u64) -> Txid { hex.repeat(64).parse().unwrap() } -pub(crate) fn outpoint(n: u64) -> OutPoint { - format!("{}:{}", txid(n), n).parse().unwrap() +pub(crate) fn outpoint(n: u32) -> OutPoint { + OutPoint { + txid: txid(n), + vout: n, + } } -pub(crate) fn satpoint(n: u64, offset: u64) -> SatPoint { +pub(crate) fn satpoint(n: u32, offset: u64) -> SatPoint { SatPoint { - outpoint: outpoint(n), offset, + outpoint: outpoint(n), } } -pub(crate) fn address() -> Address { - "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4" - .parse::>() - .unwrap() - .assume_checked() +pub(crate) fn address(n: u32) -> Address { + match n { + 0 => "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4", + 1 => "bc1qhl452zcq3ng5kzajzkx9jnzncml9tnsk3w96s6", + 2 => "bc1qqqcjq9jydx79rywltc38g5qfrjq485a8xfmkf7", + 3 => "bc1qcq2uv5nk6hec6kvag3wyevp6574qmsm9scjxc2", + 4 => "bc1qukgekwq8e68ay0mewdrvg0d3cfuc094aj2rvx9", + 5 => "bc1qtdjs8tgkaja5ddxs0j7rn52uqfdtqa53mum8xc", + 6 => "bc1qd3ex6kwlc5ett55hgsnk94y8q2zhdyxyqyujkl", + 7 => "bc1q8dcv8r903evljd87mcg0hq8lphclch7pd776wt", + 8 => "bc1q9j6xvm3td447ygnhfra5tfkpkcupwe9937nhjq", + 9 => "bc1qlyrhjzvxdzmvxe2mnr37p68vkl5fysyhfph8z0", + _ => panic!(), + } + .parse::>() + .unwrap() + .assume_checked() } pub(crate) fn recipient() -> ScriptBuf { diff --git a/src/wallet.rs b/src/wallet.rs index b169e3d6da..d3793212df 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2,12 +2,12 @@ use { super::*, base64::{self, Engine}, batch::ParentInfo, - bitcoin::secp256k1::{All, Secp256k1}, bitcoin::{ - bip32::{ChildNumber, DerivationPath, Fingerprint, Xpriv}, + bip32::{ChildNumber, DerivationPath, Xpriv}, psbt::Psbt, + secp256k1::Secp256k1, }, - bitcoincore_rpc::bitcoincore_rpc_json::{ImportDescriptors, Timestamp}, + bitcoincore_rpc::json::ImportDescriptors, entry::{EtchingEntry, EtchingEntryValue}, fee_rate::FeeRate, index::entry::Entry, @@ -75,6 +75,7 @@ pub(crate) enum Maturity { pub(crate) struct Wallet { bitcoin_client: Client, database: Database, + has_inscription_index: bool, has_rune_index: bool, has_sat_index: bool, rpc_url: Url, @@ -216,6 +217,10 @@ impl Wallet { ) } + pub(crate) fn get_inscriptions_in_output(&self, output: &OutPoint) -> Vec { + self.output_info.get(output).unwrap().inscriptions.clone() + } + pub(crate) fn get_parent_info(&self, parents: &[InscriptionId]) -> Result> { let mut parent_info = Vec::new(); for parent_id in parents { @@ -225,7 +230,7 @@ impl Wallet { let satpoint = self .inscription_info - .get(parent_id) + .get(&parent_id) .ok_or_else(|| anyhow!("parent {parent_id} not in wallet"))? .satpoint; @@ -285,10 +290,12 @@ impl Wallet { ) .send()?; - if !response.status().is_success() { + if response.status() == StatusCode::NOT_FOUND { return Ok(None); } + let response = response.error_for_status()?; + let rune_json: api::Rune = serde_json::from_str(&response.text()?)?; Ok(Some((rune_json.id, rune_json.entry, rune_json.parent))) @@ -304,6 +311,10 @@ impl Wallet { ) } + pub(crate) fn has_inscription_index(&self) -> bool { + self.has_inscription_index + } + pub(crate) fn has_sat_index(&self) -> bool { self.has_sat_index } @@ -484,7 +495,12 @@ impl Wallet { Ok(()) } - pub(crate) fn initialize(name: String, settings: &Settings, seed: [u8; 64]) -> Result { + pub(crate) fn initialize( + name: String, + settings: &Settings, + seed: [u8; 64], + timestamp: bitcoincore_rpc::json::Timestamp, + ) -> Result { Self::check_version(settings.bitcoin_rpc_client(None)?)?.create_wallet( &name, None, @@ -510,55 +526,38 @@ impl Wallet { let derived_private_key = master_private_key.derive_priv(&secp, &derivation_path)?; + let mut descriptors = Vec::new(); for change in [false, true] { - Self::derive_and_import_descriptor( - name.clone(), - settings, - &secp, - (fingerprint, derivation_path.clone()), - derived_private_key, - change, - )?; - } - - Ok(()) - } - - fn derive_and_import_descriptor( - name: String, - settings: &Settings, - secp: &Secp256k1, - origin: (Fingerprint, DerivationPath), - derived_private_key: Xpriv, - change: bool, - ) -> Result { - let secret_key = DescriptorSecretKey::XPrv(DescriptorXKey { - origin: Some(origin), - xkey: derived_private_key, - derivation_path: DerivationPath::master().child(ChildNumber::Normal { - index: change.into(), - }), - wildcard: Wildcard::Unhardened, - }); + let secret_key = DescriptorSecretKey::XPrv(DescriptorXKey { + origin: Some((fingerprint, derivation_path.clone())), + xkey: derived_private_key, + derivation_path: DerivationPath::master().child(ChildNumber::Normal { + index: change.into(), + }), + wildcard: Wildcard::Unhardened, + }); - let public_key = secret_key.to_public(secp)?; + let public_key = secret_key.to_public(&secp)?; - let mut key_map = BTreeMap::new(); - key_map.insert(public_key.clone(), secret_key); + let mut key_map = BTreeMap::new(); + key_map.insert(public_key.clone(), secret_key); - let descriptor = miniscript::descriptor::Descriptor::new_tr(public_key, None)?; + let descriptor = miniscript::descriptor::Descriptor::new_tr(public_key, None)?; - settings - .bitcoin_rpc_client(Some(name.clone()))? - .import_descriptors(ImportDescriptors { + descriptors.push(ImportDescriptors { descriptor: descriptor.to_string_with_secret(&key_map), - timestamp: Timestamp::Now, + timestamp, active: Some(true), range: None, next_index: None, internal: Some(change), label: None, - })?; + }); + } + + settings + .bitcoin_rpc_client(Some(name.clone()))? + .call::("importdescriptors", &[serde_json::to_value(descriptors)?])?; Ok(()) } @@ -740,7 +739,7 @@ impl Wallet { ) } - pub(super) fn sign_transaction( + pub(super) fn sign_and_broadcast_transaction( &self, unsigned_transaction: Transaction, dry_run: bool, diff --git a/src/wallet/batch.rs b/src/wallet/batch.rs index 665dc989ac..1d775cebbe 100644 --- a/src/wallet/batch.rs +++ b/src/wallet/batch.rs @@ -4,7 +4,6 @@ use { blockdata::{opcodes, script}, key::PrivateKey, key::{TapTweak, TweakedKeypair, TweakedPublicKey, UntweakedKeypair}, - policy::MAX_STANDARD_TX_WEIGHT, secp256k1::{self, constants::SCHNORR_SIGNATURE_SIZE, rand, Secp256k1, XOnlyPublicKey}, sighash::{Prevouts, SighashCache, TapSighashType}, taproot::Signature, @@ -74,7 +73,7 @@ mod tests { #[test] fn reveal_transaction_pays_fee() { - let utxos = vec![(outpoint(1), tx_out(20000, address()))]; + let utxos = vec![(outpoint(1), tx_out(20000, address(0)))]; let inscription = inscription("text/plain", "ord"); let commit_address = change(0); let reveal_address = recipient_address(); @@ -120,7 +119,7 @@ mod tests { #[test] fn inscribe_transactions_opt_in_to_rbf() { - let utxos = vec![(outpoint(1), tx_out(20000, address()))]; + let utxos = vec![(outpoint(1), tx_out(20000, address(0)))]; let inscription = inscription("text/plain", "ord"); let commit_address = change(0); let reveal_address = recipient_address(); @@ -160,7 +159,7 @@ mod tests { #[test] fn inscribe_with_no_satpoint_and_no_cardinal_utxos() { - let utxos = vec![(outpoint(1), tx_out(1000, address()))]; + let utxos = vec![(outpoint(1), tx_out(1000, address(0)))]; let mut inscriptions = BTreeMap::new(); inscriptions.insert( SatPoint { @@ -210,8 +209,8 @@ mod tests { #[test] fn inscribe_with_no_satpoint_and_enough_cardinal_utxos() { let utxos = vec![ - (outpoint(1), tx_out(20_000, address())), - (outpoint(2), tx_out(20_000, address())), + (outpoint(1), tx_out(20_000, address(0))), + (outpoint(2), tx_out(20_000, address(0))), ]; let mut inscriptions = BTreeMap::new(); inscriptions.insert( @@ -255,8 +254,8 @@ mod tests { #[test] fn inscribe_with_custom_fee_rate() { let utxos = vec![ - (outpoint(1), tx_out(10_000, address())), - (outpoint(2), tx_out(20_000, address())), + (outpoint(1), tx_out(10_000, address(0))), + (outpoint(2), tx_out(20_000, address(0))), ]; let mut inscriptions = BTreeMap::new(); inscriptions.insert( @@ -330,8 +329,8 @@ mod tests { #[test] fn inscribe_with_parent() { let utxos = vec![ - (outpoint(1), tx_out(10_000, address())), - (outpoint(2), tx_out(20_000, address())), + (outpoint(1), tx_out(10_000, address(0))), + (outpoint(2), tx_out(20_000, address(0))), ]; let mut inscriptions = BTreeMap::new(); @@ -428,8 +427,8 @@ mod tests { #[test] fn inscribe_with_commit_fee_rate() { let utxos = vec![ - (outpoint(1), tx_out(10_000, address())), - (outpoint(2), tx_out(20_000, address())), + (outpoint(1), tx_out(10_000, address(0))), + (outpoint(2), tx_out(20_000, address(0))), ]; let mut inscriptions = BTreeMap::new(); inscriptions.insert( @@ -503,7 +502,7 @@ mod tests { #[test] fn inscribe_over_max_standard_tx_weight() { - let utxos = vec![(outpoint(1), tx_out(50 * COIN_VALUE, address()))]; + let utxos = vec![(outpoint(1), tx_out(50 * COIN_VALUE, address(0)))]; let inscription = inscription("text/plain", [0; MAX_STANDARD_TX_WEIGHT as usize]); let satpoint = None; @@ -544,7 +543,7 @@ mod tests { #[test] fn inscribe_with_no_max_standard_tx_weight() { - let utxos = vec![(outpoint(1), tx_out(50 * COIN_VALUE, address()))]; + let utxos = vec![(outpoint(1), tx_out(50 * COIN_VALUE, address(0)))]; let inscription = inscription("text/plain", [0; MAX_STANDARD_TX_WEIGHT as usize]); let satpoint = None; @@ -581,8 +580,8 @@ mod tests { #[test] fn batch_inscribe_with_parent() { let utxos = vec![ - (outpoint(1), tx_out(10_000, address())), - (outpoint(2), tx_out(50_000, address())), + (outpoint(1), tx_out(10_000, address(0))), + (outpoint(2), tx_out(50_000, address(0))), ]; let parent = inscription_id(1); @@ -690,12 +689,12 @@ mod tests { #[test] fn batch_inscribe_satpoints_with_parent() { let utxos = vec![ - (outpoint(1), tx_out(1_111, address())), - (outpoint(2), tx_out(2_222, address())), - (outpoint(3), tx_out(3_333, address())), - (outpoint(4), tx_out(10_000, address())), - (outpoint(5), tx_out(50_000, address())), - (outpoint(6), tx_out(60_000, address())), + (outpoint(1), tx_out(1_111, address(0))), + (outpoint(2), tx_out(2_222, address(0))), + (outpoint(3), tx_out(3_333, address(0))), + (outpoint(4), tx_out(10_000, address(0))), + (outpoint(5), tx_out(50_000, address(0))), + (outpoint(6), tx_out(60_000, address(0))), ]; let parent = inscription_id(1); @@ -822,8 +821,8 @@ mod tests { #[test] fn batch_inscribe_with_parent_not_enough_cardinals_utxos_fails() { let utxos = vec![ - (outpoint(1), tx_out(10_000, address())), - (outpoint(2), tx_out(20_000, address())), + (outpoint(1), tx_out(10_000, address(0))), + (outpoint(2), tx_out(20_000, address(0))), ]; let parent = inscription_id(1); @@ -899,8 +898,8 @@ mod tests { #[should_panic(expected = "invariant: shared-output has only one destination")] fn batch_inscribe_with_inconsistent_reveal_addresses_panics() { let utxos = vec![ - (outpoint(1), tx_out(10_000, address())), - (outpoint(2), tx_out(80_000, address())), + (outpoint(1), tx_out(10_000, address(0))), + (outpoint(2), tx_out(80_000, address(0))), ]; let parent = inscription_id(1); @@ -968,7 +967,7 @@ mod tests { #[test] fn batch_inscribe_over_max_standard_tx_weight() { - let utxos = vec![(outpoint(1), tx_out(50 * COIN_VALUE, address()))]; + let utxos = vec![(outpoint(1), tx_out(50 * COIN_VALUE, address(0)))]; let wallet_inscriptions = BTreeMap::new(); @@ -1016,8 +1015,8 @@ mod tests { #[test] fn batch_inscribe_into_separate_outputs() { let utxos = vec![ - (outpoint(1), tx_out(10_000, address())), - (outpoint(2), tx_out(80_000, address())), + (outpoint(1), tx_out(10_000, address(0))), + (outpoint(2), tx_out(80_000, address(0))), ]; let wallet_inscriptions = BTreeMap::new(); @@ -1073,8 +1072,8 @@ mod tests { #[test] fn batch_inscribe_into_separate_outputs_with_parent() { let utxos = vec![ - (outpoint(1), tx_out(10_000, address())), - (outpoint(2), tx_out(50_000, address())), + (outpoint(1), tx_out(10_000, address(0))), + (outpoint(2), tx_out(50_000, address(0))), ]; let parent = inscription_id(1); diff --git a/src/wallet/batch/plan.rs b/src/wallet/batch/plan.rs index 9f844ca845..7639903fbf 100644 --- a/src/wallet/batch/plan.rs +++ b/src/wallet/batch/plan.rs @@ -483,9 +483,10 @@ impl Plan { runestone = Some(inner); ensure!( - self.no_limit || script_pubkey.len() <= 82, - "runestone greater than maximum OP_RETURN size: {} > 82", - script_pubkey.len() + self.no_limit || script_pubkey.len() <= MAX_STANDARD_OP_RETURN_SIZE, + "runestone greater than maximum OP_RETURN size: {} > {}", + script_pubkey.len(), + MAX_STANDARD_OP_RETURN_SIZE, ); reveal_outputs.push(TxOut { diff --git a/src/wallet/transaction_builder.rs b/src/wallet/transaction_builder.rs index eb411c1df0..4e97eee01b 100644 --- a/src/wallet/transaction_builder.rs +++ b/src/wallet/transaction_builder.rs @@ -727,9 +727,9 @@ mod tests { #[test] fn select_sat() { let mut utxos = vec![ - (outpoint(1), tx_out(5_000, address())), - (outpoint(2), tx_out(49 * COIN_VALUE, address())), - (outpoint(3), tx_out(2_000, address())), + (outpoint(1), tx_out(5_000, address(0))), + (outpoint(2), tx_out(49 * COIN_VALUE, address(0))), + (outpoint(3), tx_out(2_000, address(0))), ]; let tx_builder = TransactionBuilder::new( @@ -765,9 +765,9 @@ mod tests { #[test] fn tx_builder_to_transaction() { let mut amounts = BTreeMap::new(); - amounts.insert(outpoint(1), tx_out(5_000, address())); - amounts.insert(outpoint(2), tx_out(5_000, address())); - amounts.insert(outpoint(3), tx_out(2_000, address())); + amounts.insert(outpoint(1), tx_out(5_000, address(0))); + amounts.insert(outpoint(2), tx_out(5_000, address(0))); + amounts.insert(outpoint(3), tx_out(2_000, address(0))); let tx_builder = TransactionBuilder { amounts, @@ -816,7 +816,7 @@ mod tests { #[test] fn transactions_are_rbf() { - let utxos = vec![(outpoint(1), tx_out(5_000, address()))]; + let utxos = vec![(outpoint(1), tx_out(5_000, address(0)))]; assert!(TransactionBuilder::new( satpoint(1, 0), @@ -837,7 +837,7 @@ mod tests { #[test] fn deduct_fee() { - let utxos = vec![(outpoint(1), tx_out(5_000, address()))]; + let utxos = vec![(outpoint(1), tx_out(5_000, address(0)))]; pretty_assert_eq!( TransactionBuilder::new( @@ -865,7 +865,7 @@ mod tests { #[test] #[should_panic(expected = "invariant: deducting fee does not consume sat")] fn invariant_deduct_fee_does_not_consume_sat() { - let utxos = vec![(outpoint(1), tx_out(5_000, address()))]; + let utxos = vec![(outpoint(1), tx_out(5_000, address(0)))]; TransactionBuilder::new( satpoint(1, 4_950), @@ -889,8 +889,8 @@ mod tests { #[test] fn additional_postage_added_when_required() { let utxos = vec![ - (outpoint(1), tx_out(5_000, address())), - (outpoint(2), tx_out(5_000, address())), + (outpoint(1), tx_out(5_000, address(0))), + (outpoint(2), tx_out(5_000, address(0))), ]; pretty_assert_eq!( @@ -918,7 +918,7 @@ mod tests { #[test] fn insufficient_padding_to_add_postage_no_utxos() { - let utxos = vec![(outpoint(1), tx_out(5_000, address()))]; + let utxos = vec![(outpoint(1), tx_out(5_000, address(0)))]; pretty_assert_eq!( TransactionBuilder::new( @@ -941,8 +941,8 @@ mod tests { #[test] fn insufficient_padding_to_add_postage_small_utxos() { let utxos = vec![ - (outpoint(1), tx_out(5_000, address())), - (outpoint(2), tx_out(1, address())), + (outpoint(1), tx_out(5_000, address(0))), + (outpoint(2), tx_out(1, address(0))), ]; pretty_assert_eq!( @@ -966,8 +966,8 @@ mod tests { #[test] fn excess_additional_postage_is_stripped() { let utxos = vec![ - (outpoint(1), tx_out(5_000, address())), - (outpoint(2), tx_out(25_000, address())), + (outpoint(1), tx_out(5_000, address(0))), + (outpoint(2), tx_out(25_000, address(0))), ]; pretty_assert_eq!( @@ -1003,7 +1003,7 @@ mod tests { TransactionBuilder::new( satpoint(2, 0), BTreeMap::new(), - vec![(outpoint(1), tx_out(4, address()))] + vec![(outpoint(1), tx_out(4, address(0)))] .into_iter() .collect(), BTreeSet::new(), @@ -1024,7 +1024,7 @@ mod tests { TransactionBuilder::new( satpoint(1, 4), BTreeMap::new(), - vec![(outpoint(1), tx_out(4, address()))] + vec![(outpoint(1), tx_out(4, address(0)))] .into_iter() .collect(), BTreeSet::new(), @@ -1045,7 +1045,7 @@ mod tests { TransactionBuilder::new( satpoint(1, 2), BTreeMap::new(), - vec![(outpoint(1), tx_out(5, address()))] + vec![(outpoint(1), tx_out(5, address(0)))] .into_iter() .collect(), BTreeSet::new(), @@ -1066,7 +1066,7 @@ mod tests { let mut builder = TransactionBuilder::new( satpoint(1, 2), BTreeMap::new(), - vec![(outpoint(1), tx_out(5, address()))] + vec![(outpoint(1), tx_out(5, address(0)))] .into_iter() .collect(), BTreeSet::new(), @@ -1095,7 +1095,7 @@ mod tests { let mut builder = TransactionBuilder::new( satpoint(1, 2), BTreeMap::new(), - vec![(outpoint(1), tx_out(5, address()))] + vec![(outpoint(1), tx_out(5, address(0)))] .into_iter() .collect(), BTreeSet::new(), @@ -1116,7 +1116,7 @@ mod tests { #[test] fn excess_postage_is_stripped() { - let utxos = vec![(outpoint(1), tx_out(1_000_000, address()))]; + let utxos = vec![(outpoint(1), tx_out(1_000_000, address(0)))]; pretty_assert_eq!( TransactionBuilder::new( @@ -1147,7 +1147,7 @@ mod tests { #[test] #[should_panic(expected = "invariant: excess postage is stripped")] fn invariant_excess_postage_is_stripped() { - let utxos = vec![(outpoint(1), tx_out(1_000_000, address()))]; + let utxos = vec![(outpoint(1), tx_out(1_000_000, address(0)))]; TransactionBuilder::new( satpoint(1, 0), @@ -1169,7 +1169,7 @@ mod tests { #[test] fn sat_is_aligned() { - let utxos = vec![(outpoint(1), tx_out(10_000, address()))]; + let utxos = vec![(outpoint(1), tx_out(10_000, address(0)))]; pretty_assert_eq!( TransactionBuilder::new( @@ -1197,8 +1197,8 @@ mod tests { #[test] fn alignment_output_under_dust_limit_is_padded() { let utxos = vec![ - (outpoint(1), tx_out(10_000, address())), - (outpoint(2), tx_out(10_000, address())), + (outpoint(1), tx_out(10_000, address(0))), + (outpoint(2), tx_out(10_000, address(0))), ]; pretty_assert_eq!( @@ -1230,7 +1230,7 @@ mod tests { #[test] #[should_panic(expected = "invariant: all outputs are either change or recipient")] fn invariant_all_output_are_recognized() { - let utxos = vec![(outpoint(1), tx_out(10_000, address()))]; + let utxos = vec![(outpoint(1), tx_out(10_000, address(0)))]; let mut builder = TransactionBuilder::new( satpoint(1, 3_333), @@ -1260,7 +1260,7 @@ mod tests { #[test] #[should_panic(expected = "invariant: all outputs are above dust limit")] fn invariant_all_output_are_above_dust_limit() { - let utxos = vec![(outpoint(1), tx_out(10_000, address()))]; + let utxos = vec![(outpoint(1), tx_out(10_000, address(0)))]; TransactionBuilder::new( satpoint(1, 1), @@ -1288,7 +1288,7 @@ mod tests { #[test] #[should_panic(expected = "invariant: sat is at first position in recipient output")] fn invariant_sat_is_aligned() { - let utxos = vec![(outpoint(1), tx_out(10_000, address()))]; + let utxos = vec![(outpoint(1), tx_out(10_000, address(0)))]; TransactionBuilder::new( satpoint(1, 3_333), @@ -1313,7 +1313,7 @@ mod tests { #[test] #[should_panic(expected = "invariant: fee estimation is correct")] fn invariant_fee_is_at_least_target_fee_rate() { - let utxos = vec![(outpoint(1), tx_out(10_000, address()))]; + let utxos = vec![(outpoint(1), tx_out(10_000, address(0)))]; TransactionBuilder::new( satpoint(1, 0), @@ -1338,9 +1338,9 @@ mod tests { #[should_panic(expected = "invariant: recipient address appears exactly once in outputs")] fn invariant_recipient_appears_exactly_once() { let mut amounts = BTreeMap::new(); - amounts.insert(outpoint(1), tx_out(5_000, address())); - amounts.insert(outpoint(2), tx_out(5_000, address())); - amounts.insert(outpoint(3), tx_out(2_000, address())); + amounts.insert(outpoint(1), tx_out(5_000, address(0))); + amounts.insert(outpoint(2), tx_out(5_000, address(0))); + amounts.insert(outpoint(3), tx_out(2_000, address(0))); TransactionBuilder { amounts, @@ -1379,9 +1379,9 @@ mod tests { #[should_panic(expected = "invariant: change addresses appear at most once in outputs")] fn invariant_change_appears_at_most_once() { let mut amounts = BTreeMap::new(); - amounts.insert(outpoint(1), tx_out(5_000, address())); - amounts.insert(outpoint(2), tx_out(5_000, address())); - amounts.insert(outpoint(3), tx_out(2_000, address())); + amounts.insert(outpoint(1), tx_out(5_000, address(0))); + amounts.insert(outpoint(2), tx_out(5_000, address(0))); + amounts.insert(outpoint(3), tx_out(2_000, address(0))); TransactionBuilder { amounts, @@ -1419,8 +1419,8 @@ mod tests { #[test] fn do_not_select_already_inscribed_sats_for_cardinal_utxos() { let utxos = vec![ - (outpoint(1), tx_out(100, address())), - (outpoint(2), tx_out(49 * COIN_VALUE, address())), + (outpoint(1), tx_out(100, address(0))), + (outpoint(2), tx_out(49 * COIN_VALUE, address(0))), ]; pretty_assert_eq!( @@ -1444,8 +1444,8 @@ mod tests { #[test] fn do_not_select_runic_utxos_for_cardinal_utxos() { let utxos = vec![ - (outpoint(1), tx_out(100, address())), - (outpoint(2), tx_out(49 * COIN_VALUE, address())), + (outpoint(1), tx_out(100, address(0))), + (outpoint(2), tx_out(49 * COIN_VALUE, address(0))), ]; pretty_assert_eq!( @@ -1468,7 +1468,7 @@ mod tests { #[test] fn do_not_send_two_inscriptions_at_once() { - let utxos = vec![(outpoint(1), tx_out(1_000, address()))]; + let utxos = vec![(outpoint(1), tx_out(1_000, address(0)))]; pretty_assert_eq!( TransactionBuilder::new( @@ -1494,7 +1494,7 @@ mod tests { #[test] fn build_transaction_with_custom_fee_rate() { - let utxos = vec![(outpoint(1), tx_out(10_000, address()))]; + let utxos = vec![(outpoint(1), tx_out(10_000, address(0)))]; let fee_rate = FeeRate::try_from(17.3).unwrap(); @@ -1529,7 +1529,7 @@ mod tests { #[test] fn exact_transaction_has_correct_value() { - let utxos = vec![(outpoint(1), tx_out(5_000, address()))]; + let utxos = vec![(outpoint(1), tx_out(5_000, address(0)))]; pretty_assert_eq!( TransactionBuilder::new( @@ -1557,8 +1557,8 @@ mod tests { #[test] fn exact_transaction_adds_output_to_cover_value() { let utxos = vec![ - (outpoint(1), tx_out(1_000, address())), - (outpoint(2), tx_out(1_000, address())), + (outpoint(1), tx_out(1_000, address(0))), + (outpoint(2), tx_out(1_000, address(0))), ]; pretty_assert_eq!( @@ -1586,7 +1586,7 @@ mod tests { #[test] fn refuse_to_send_dust() { - let utxos = vec![(outpoint(1), tx_out(1_000, address()))]; + let utxos = vec![(outpoint(1), tx_out(1_000, address(0)))]; pretty_assert_eq!( TransactionBuilder::new( @@ -1612,8 +1612,8 @@ mod tests { #[test] fn do_not_select_outputs_which_do_not_pay_for_their_own_fee_at_default_fee_rate() { let utxos = vec![ - (outpoint(1), tx_out(1_000, address())), - (outpoint(2), tx_out(100, address())), + (outpoint(1), tx_out(1_000, address(0))), + (outpoint(2), tx_out(100, address(0))), ]; pretty_assert_eq!( @@ -1637,8 +1637,8 @@ mod tests { #[test] fn do_not_select_outputs_which_do_not_pay_for_their_own_fee_at_higher_fee_rate() { let utxos = vec![ - (outpoint(1), tx_out(1_000, address())), - (outpoint(2), tx_out(500, address())), + (outpoint(1), tx_out(1_000, address(0))), + (outpoint(2), tx_out(500, address(0))), ]; pretty_assert_eq!( @@ -1689,7 +1689,7 @@ mod tests { TransactionBuilder::new( satpoint(1, 0), BTreeMap::new(), - vec![(outpoint(1), tx_out(1_000, address()))] + vec![(outpoint(1), tx_out(1_000, address(0)))] .into_iter() .collect(), BTreeSet::new(), @@ -1716,7 +1716,7 @@ mod tests { TransactionBuilder::new( satpoint(1, 0), BTreeMap::new(), - vec![(outpoint(1), tx_out(20_099, address()))] + vec![(outpoint(1), tx_out(20_099, address(0)))] .into_iter() .collect(), BTreeSet::new(), @@ -1743,7 +1743,7 @@ mod tests { TransactionBuilder::new( satpoint(1, 0), BTreeMap::new(), - vec![(outpoint(1), tx_out(1_500, address()))] + vec![(outpoint(1), tx_out(1_500, address(0)))] .into_iter() .collect(), BTreeSet::new(), @@ -1770,7 +1770,7 @@ mod tests { TransactionBuilder::new( satpoint(1, 0), BTreeMap::new(), - vec![(outpoint(1), tx_out(1_500, address()))] + vec![(outpoint(1), tx_out(1_500, address(0)))] .into_iter() .collect(), BTreeSet::new(), @@ -1792,7 +1792,7 @@ mod tests { TransactionBuilder::new( satpoint(1, 0), BTreeMap::new(), - vec![(outpoint(1), tx_out(1000, address()))] + vec![(outpoint(1), tx_out(1000, address(0)))] .into_iter() .collect(), BTreeSet::new(), @@ -1814,7 +1814,7 @@ mod tests { TransactionBuilder::new( satpoint(1, 0), BTreeMap::new(), - vec![(outpoint(1), tx_out(1000, address()))] + vec![(outpoint(1), tx_out(1000, address(0)))] .into_iter() .collect(), BTreeSet::new(), @@ -1836,7 +1836,7 @@ mod tests { TransactionBuilder::new( satpoint(1, 0), BTreeMap::new(), - vec![(outpoint(1), tx_out(2000, address()))] + vec![(outpoint(1), tx_out(2000, address(0)))] .into_iter() .collect(), BTreeSet::new(), @@ -1863,7 +1863,7 @@ mod tests { TransactionBuilder::new( satpoint(1, 0), BTreeMap::new(), - vec![(outpoint(1), tx_out(45000, address()))] + vec![(outpoint(1), tx_out(45000, address(0)))] .into_iter() .collect(), BTreeSet::new(), @@ -1887,12 +1887,12 @@ mod tests { #[test] fn select_outgoing_can_select_multiple_utxos() { let mut utxos = vec![ - (outpoint(2), tx_out(3_006, address())), // 2. biggest utxo is selected 2nd leaving us needing 4206 more - (outpoint(1), tx_out(3_003, address())), // 1. satpoint is selected 1st leaving us needing 7154 more - (outpoint(5), tx_out(3_004, address())), - (outpoint(4), tx_out(3_001, address())), // 4. smallest utxo >= 1259 is selected 4th, filling deficit - (outpoint(3), tx_out(3_005, address())), // 3. next biggest utxo is selected 3rd leaving us needing 1259 more - (outpoint(6), tx_out(3_002, address())), + (outpoint(2), tx_out(3_006, address(0))), // 2. biggest utxo is selected 2nd leaving us needing 4206 more + (outpoint(1), tx_out(3_003, address(0))), // 1. satpoint is selected 1st leaving us needing 7154 more + (outpoint(5), tx_out(3_004, address(0))), + (outpoint(4), tx_out(3_001, address(0))), // 4. smallest utxo >= 1259 is selected 4th, filling deficit + (outpoint(3), tx_out(3_005, address(0))), // 3. next biggest utxo is selected 3rd leaving us needing 1259 more + (outpoint(6), tx_out(3_002, address(0))), ]; let tx_builder = TransactionBuilder::new( @@ -1936,13 +1936,13 @@ mod tests { #[test] fn pad_alignment_output_can_select_multiple_utxos() { let mut utxos = vec![ - (outpoint(4), tx_out(101, address())), // 4. smallest utxo >= 84 is selected 4th, filling deficit - (outpoint(1), tx_out(20_000, address())), // 1. satpoint is selected 1st leaving deficit 293 - (outpoint(2), tx_out(105, address())), // 2. biggest utxo <= 293 is selected 2nd leaving deficit 188 - (outpoint(5), tx_out(103, address())), - (outpoint(6), tx_out(10_000, address())), - (outpoint(3), tx_out(104, address())), // 3. biggest utxo <= 188 is selected 3rd leaving deficit 84 - (outpoint(7), tx_out(102, address())), + (outpoint(4), tx_out(101, address(0))), // 4. smallest utxo >= 84 is selected 4th, filling deficit + (outpoint(1), tx_out(20_000, address(0))), // 1. satpoint is selected 1st leaving deficit 293 + (outpoint(2), tx_out(105, address(0))), // 2. biggest utxo <= 293 is selected 2nd leaving deficit 188 + (outpoint(5), tx_out(103, address(0))), + (outpoint(6), tx_out(10_000, address(0))), + (outpoint(3), tx_out(104, address(0))), // 3. biggest utxo <= 188 is selected 3rd leaving deficit 84 + (outpoint(7), tx_out(102, address(0))), ]; let tx_builder = TransactionBuilder::new( @@ -1996,13 +1996,13 @@ mod tests { expected_value: Amount, ) { let utxos = vec![ - (outpoint(4), tx_out(101, address())), - (outpoint(1), tx_out(20_000, address())), - (outpoint(2), tx_out(105, address())), - (outpoint(5), tx_out(103, address())), - (outpoint(6), tx_out(10_000, address())), - (outpoint(3), tx_out(104, address())), - (outpoint(7), tx_out(102, address())), + (outpoint(4), tx_out(101, address(0))), + (outpoint(1), tx_out(20_000, address(0))), + (outpoint(2), tx_out(105, address(0))), + (outpoint(5), tx_out(103, address(0))), + (outpoint(6), tx_out(10_000, address(0))), + (outpoint(3), tx_out(104, address(0))), + (outpoint(7), tx_out(102, address(0))), ]; let mut tx_builder = TransactionBuilder::new( @@ -2058,7 +2058,7 @@ mod tests { #[test] fn build_transaction_with_custom_postage() { - let utxos = vec![(outpoint(1), tx_out(1_000_000, address()))]; + let utxos = vec![(outpoint(1), tx_out(1_000_000, address(0)))]; let fee_rate = FeeRate::try_from(17.3).unwrap(); @@ -2096,7 +2096,7 @@ mod tests { #[test] fn select_cardinal_utxo_ignores_locked_utxos_and_errors_if_none_available() { - let utxos = vec![(outpoint(1), tx_out(500, address()))]; + let utxos = vec![(outpoint(1), tx_out(500, address(0)))]; let locked_utxos = vec![outpoint(1)]; let mut tx_builder = TransactionBuilder::new( @@ -2121,8 +2121,8 @@ mod tests { #[test] fn select_cardinal_utxo_ignores_locked_utxos() { let utxos = vec![ - (outpoint(1), tx_out(500, address())), - (outpoint(2), tx_out(500, address())), + (outpoint(1), tx_out(500, address(0))), + (outpoint(2), tx_out(500, address(0))), ]; let locked_utxos = vec![outpoint(1)]; @@ -2148,8 +2148,8 @@ mod tests { #[test] fn prefer_further_away_utxos_if_they_are_newly_under_target() { let utxos = vec![ - (outpoint(1), tx_out(510, address())), - (outpoint(2), tx_out(400, address())), + (outpoint(1), tx_out(510, address(0))), + (outpoint(2), tx_out(400, address(0))), ]; let mut tx_builder = TransactionBuilder::new( @@ -2174,8 +2174,8 @@ mod tests { #[test] fn prefer_further_away_utxos_if_they_are_newly_over_target() { let utxos = vec![ - (outpoint(1), tx_out(490, address())), - (outpoint(2), tx_out(600, address())), + (outpoint(1), tx_out(490, address(0))), + (outpoint(2), tx_out(600, address(0))), ]; let mut tx_builder = TransactionBuilder::new( diff --git a/src/wallet/wallet_constructor.rs b/src/wallet/wallet_constructor.rs index b9faf1b0bc..d9d60fce5a 100644 --- a/src/wallet/wallet_constructor.rs +++ b/src/wallet/wallet_constructor.rs @@ -105,6 +105,7 @@ impl WalletConstructor { Ok(Wallet { bitcoin_client, database, + has_inscription_index: status.inscription_index, has_rune_index: status.rune_index, has_sat_index: status.sat_index, inscription_info, diff --git a/static/index.css b/static/index.css index 1631fe3a40..c494de8261 100644 --- a/static/index.css +++ b/static/index.css @@ -115,8 +115,6 @@ ol, ul { } .block-list { - font-family: monospace, monospace; - list-style-position: inside; white-space: nowrap; } @@ -141,6 +139,14 @@ ol, ul { text-align: center; } +.collapse { + font-family: monospace, monospace; +} + +.collapse:not(a) { + user-select: all; +} + .monospace { font-family: monospace, monospace; } diff --git a/static/index.js b/static/index.js index e3153179b7..ee62318344 100644 --- a/static/index.js +++ b/static/index.js @@ -1,34 +1,77 @@ -for (let time of document.body.getElementsByTagName('time')) { - time.setAttribute('title', new Date(time.textContent)); -} +addEventListener("DOMContentLoaded", () => { + for (let time of document.body.getElementsByTagName('time')) { + time.setAttribute('title', new Date(time.textContent)); + } -let next = document.querySelector('a.next'); -let prev = document.querySelector('a.prev'); + let next = document.querySelector('a.next'); + let prev = document.querySelector('a.prev'); -window.addEventListener('keydown', e => { - if (document.activeElement.tagName == 'INPUT') { - return; - } + window.addEventListener('keydown', e => { + if (document.activeElement.tagName == 'INPUT') { + return; + } + + switch (e.key) { + case 'ArrowRight': + if (next) { + window.location = next.href; + } + return; + case 'ArrowLeft': + if (prev) { + window.location = prev.href; + } + return; + } + }); + + const search = document.querySelector('form[action="/search"]'); + const query = search.querySelector('input[name="query"]'); + + search.addEventListener('submit', (e) => { + if (!query.value) { + e.preventDefault(); + } + }); + + let collapse = document.getElementsByClassName('collapse'); + + let context = document.createElement('canvas').getContext('2d'); - switch (e.key) { - case 'ArrowRight': - if (next) { - window.location = next.href; + function resize() { + for (let node of collapse) { + if (!('original' in node.dataset)) { + node.dataset.original = node.textContent.trim(); } - return; - case 'ArrowLeft': - if (prev) { - window.location = prev.href; + let original = node.dataset.original; + let length = original.length; + let width = node.clientWidth; + if (width == 0) { + width = node.parentNode.getBoundingClientRect().width; } - return; + context.font = window.getComputedStyle(node).font; + let capacity = width / (context.measureText(original).width / length); + if (capacity >= length) { + node.textContent = original + } else { + let count = Math.floor((capacity - 1) / 2); + let start = original.substring(0, count); + let end = original.substring(length - count); + node.textContent = `${start}…${end}`; + } + } } -}); -const search = document.querySelector('form[action="/search"]'); -const query = search.querySelector('input[name="query"]'); - -search.addEventListener('submit', (e) => { - if (!query.value) { - e.preventDefault(); + function copy(e) { + if ('original' in e.target.dataset && window.getSelection().toString().includes('…')) { + e.clipboardData.setData('text/plain', e.target.dataset.original); + e.preventDefault(); + } } + + addEventListener('resize', resize); + + addEventListener('copy', copy); + + resize(); }); diff --git a/templates/address.html b/templates/address.html index a0697f3502..6b7146ae65 100644 --- a/templates/address.html +++ b/templates/address.html @@ -22,7 +22,7 @@

      Address {{ self.address }}

      diff --git a/templates/block.html b/templates/block.html index 763eac103e..1f3cdc6096 100644 --- a/templates/block.html +++ b/templates/block.html @@ -1,12 +1,12 @@

      Block {{ self.height }}

      -
      hash
      {{self.hash}}
      -
      target
      {{self.target}}
      +
      hash
      {{self.hash}}
      +
      target
      {{self.target}}
      timestamp
      size
      {{self.block.total_size()}}
      weight
      {{self.block.weight()}}
      %% if self.height.0 > 0 { -
      previous blockhash
      {{self.block.header.prev_blockhash}}
      +
      previous blockhash
      {{self.block.header.prev_blockhash}}
      %% }
      @@ -41,9 +41,9 @@

      {{"Inscription".tally(self.inscription_count)}}

      %% }

      {{"Transaction".tally(self.block.txdata.len())}}

      -
        +
          %% for tx in &self.block.txdata { %% let txid = tx.txid(); -
        • {{txid}}
        • +
        • {{txid}}
        • %% }
        diff --git a/templates/blocks.html b/templates/blocks.html index b2cd37bb4b..c7c81174db 100644 --- a/templates/blocks.html +++ b/templates/blocks.html @@ -13,7 +13,7 @@

        Block {{ self.last - i as u32 }} %% } -
      • {{ hash }}
      • +
      • {{ hash }}
      • %% } %% } diff --git a/templates/input.html b/templates/input.html index 8575091f16..fb965eaed0 100644 --- a/templates/input.html +++ b/templates/input.html @@ -1,7 +1,7 @@

        Input /{{self.path.0}}/{{self.path.1}}/{{self.path.2}}

        %% if !self.input.previous_output.is_null() { -
        previous output
        {{self.input.previous_output}}
        +
        previous output
        {{self.input.previous_output}}
        %% } %% if self.input.sequence != Sequence::MAX {
        sequence
        {{self.input.sequence}}
        diff --git a/templates/inscription.html b/templates/inscription.html index cbf3c5e646..fa50f66e4b 100644 --- a/templates/inscription.html +++ b/templates/inscription.html @@ -35,7 +35,7 @@

        Inscription {{ self.number }}

        %% }
    %% } @@ -44,7 +44,7 @@

    Inscription {{ self.number }}

    {{ rune }}
    %% }
    id
    -
    {{ self.id }}
    +
    {{ self.id }}
    %% if self.charms != 0 {
    charms
    @@ -61,11 +61,17 @@

    Inscription {{ self.number }}

    {{ Trusted(MetadataHtml(&metadata)) }}
    %% } +%% if let Some(burn_metadata) = self.burn_metadata() { +
    burn metadata
    +
    + {{ Trusted(MetadataHtml(&burn_metadata)) }} +
    +%% } %% if let Some(output) = &self.output { -%% if let Ok(address) = self.chain.address_from_script(&output.script_pubkey ) { +%% if let Ok(address) = self.chain.address_from_script(&output.script_pubkey ) {
    address
    -
    {{ address }}
    -%% } +
    {{ address }}
    +%% }
    value
    {{ output.value.to_sat() }}
    %% } @@ -108,13 +114,13 @@

    Inscription {{ self.number }}

    fee
    {{ self.fee }}
    reveal transaction
    -
    {{ self.id.txid }}
    +
    {{ self.id.txid }}
    location
    -
    {{ self.satpoint }}
    +
    {{ self.satpoint }}
    output
    -
    {{ self.satpoint.outpoint }}
    +
    {{ self.satpoint.outpoint }}
    offset
    {{ self.satpoint.offset }}
    ethereum teleburn address
    -
    {{ teleburn::Ethereum::from(self.id) }}
    +
    {{ teleburn::Ethereum::from(self.id) }}
    diff --git a/templates/output.html b/templates/output.html index 74ce3f6102..16853d091a 100644 --- a/templates/output.html +++ b/templates/output.html @@ -28,9 +28,9 @@

    Output {{self.outpoint}}

    value
    {{ self.output.value.to_sat() }}
    script pubkey
    {{ self.output.script_pubkey.to_asm_string() }}
    %% if let Ok(address) = self.chain.address_from_script(&self.output.script_pubkey ) { -
    address
    {{ address }}
    +
    address
    {{ address }}
    %% } -
    transaction
    {{ self.outpoint.txid }}
    +
    transaction
    {{ self.outpoint.txid }}
    spent
    {{ self.spent }}
    %% if let Some(sat_ranges) = &self.sat_ranges { diff --git a/templates/page.html b/templates/page.html index 0456d99498..243a29593a 100644 --- a/templates/page.html +++ b/templates/page.html @@ -13,7 +13,7 @@ - +
    diff --git a/templates/preview-image.html b/templates/preview-image.html index 260af3eeb9..c4fbab41b6 100644 --- a/templates/preview-image.html +++ b/templates/preview-image.html @@ -20,6 +20,7 @@ } img { + float: left; height: 100%; opacity: 0; width: 100%; diff --git a/templates/rune-balances.html b/templates/rune-balances.html index d39c0a36e8..5e705c5e06 100644 --- a/templates/rune-balances.html +++ b/templates/rune-balances.html @@ -11,10 +11,10 @@

    Rune Balances

    %% for (outpoint, balance) in balances { - - diff --git a/templates/rune.html b/templates/rune.html index e2b4bb4bba..3da0d745ca 100644 --- a/templates/rune.html +++ b/templates/rune.html @@ -71,9 +71,9 @@

    {{ self.entry.spaced_rune }}

    turbo
    {{ self.entry.turbo }}
    etching
    -
    {{ self.entry.etching }}
    +
    {{ self.entry.etching }}
    %% if let Some(parent) = self.parent {
    parent
    -
    {{ parent }}
    +
    {{ parent }}
    %% } diff --git a/templates/sat.html b/templates/sat.html index 9709d79c5a..539c70d557 100644 --- a/templates/sat.html +++ b/templates/sat.html @@ -31,7 +31,7 @@

    Sat {{ self.sat.n() }}

    %% } %% if let Some(satpoint) = self.satpoint { -
    location
    {{ satpoint }}
    +
    location
    {{ satpoint }}
    %% }
    diff --git a/templates/status.html b/templates/status.html index e403777dc3..e1ad62f0a9 100644 --- a/templates/status.html +++ b/templates/status.html @@ -47,7 +47,7 @@

    Status

    %% if !env!("GIT_COMMIT").is_empty() {
    git commit
    - + {{ env!("GIT_COMMIT") }}
    diff --git a/templates/transaction.html b/templates/transaction.html index d855246fcb..aa183ec59a 100644 --- a/templates/transaction.html +++ b/templates/transaction.html @@ -16,7 +16,7 @@

    Inscription Geneses

    {{"Input".tally(self.transaction.input.len())}}

    {{"Output".tally(self.transaction.output.len())}}

    @@ -24,14 +24,14 @@

    {{"Output".tally(self.transaction.output.len())}}

    %% for (vout, output) in self.transaction.output.iter().enumerate() { %% let outpoint = OutPoint::new(self.txid, vout as u32);
  • - + {{ outpoint }}
    value
    {{ output.value.to_sat() }}
    script pubkey
    {{ output.script_pubkey.to_asm_string() }}
    %% if let Ok(address) = self.chain.address_from_script(&output.script_pubkey) { -
    address
    {{ address }}
    +
    address
    {{ address }}
    %% }
  • diff --git a/tests/balances.rs b/tests/balances.rs index c290737e4c..0ade8992b0 100644 --- a/tests/balances.rs +++ b/tests/balances.rs @@ -1,4 +1,4 @@ -use {super::*, ord::subcommand::balances::Output}; +use super::*; #[test] fn flag_is_required() { @@ -17,11 +17,11 @@ fn no_runes() { let output = CommandBuilder::new("--regtest --index-runes balances") .core(&core) - .run_and_deserialize_output::(); + .run_and_deserialize_output::(); assert_eq!( output, - Output { + Balances { runes: BTreeMap::new() } ); @@ -40,15 +40,15 @@ fn with_runes() { let output = CommandBuilder::new("--regtest --index-runes balances") .core(&core) - .run_and_deserialize_output::(); + .run_and_deserialize_output::(); assert_eq!( output, - Output { - runes: vec![ + Balances { + runes: [ ( SpacedRune::new(Rune(RUNE), 0), - vec![( + [( OutPoint { txid: a.output.reveal, vout: 1 @@ -59,12 +59,11 @@ fn with_runes() { symbol: Some('¢') }, )] - .into_iter() - .collect() + .into() ), ( SpacedRune::new(Rune(RUNE + 1), 0), - vec![( + [( OutPoint { txid: b.output.reveal, vout: 1 @@ -75,12 +74,10 @@ fn with_runes() { symbol: Some('¢') }, )] - .into_iter() - .collect() + .into() ), ] - .into_iter() - .collect(), + .into() } ); } diff --git a/tests/json_api.rs b/tests/json_api.rs index 80323c45af..9505ea1057 100644 --- a/tests/json_api.rs +++ b/tests/json_api.rs @@ -1,6 +1,7 @@ use { super::*, bitcoin::{BlockHash, ScriptBuf}, + ord::subcommand::wallet::send::Output, ord::{Envelope, Inscription}, }; @@ -158,6 +159,7 @@ fn get_inscription() { api::Inscription { address: None, charms: vec![Charm::Coin, Charm::Uncommon], + child_count: 0, children: Vec::new(), content_length: Some(3), content_type: Some("text/plain;charset=utf-8".to_string()), @@ -343,6 +345,7 @@ fn get_output() { .parse() .unwrap() ), + outpoint: OutPoint { txid, vout: 0 }, inscriptions: vec![ InscriptionId { txid, index: 0 }, InscriptionId { txid, index: 1 }, @@ -692,3 +695,221 @@ fn get_decode_tx() { } ); } + +#[test] +fn outputs_address() { + let core = mockcore::builder().network(Network::Regtest).build(); + let ord = + TestServer::spawn_with_args(&core, &["--index-runes", "--index-addresses", "--regtest"]); + + create_wallet(&core, &ord); + + let address = "bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw"; + + let (inscription_id, reveal) = inscribe(&core, &ord); + + let inscription_send = CommandBuilder::new(format!( + "--chain regtest --index-runes wallet send --fee-rate 1 {address} {inscription_id}", + )) + .core(&core) + .ord(&ord) + .stdout_regex(".*") + .run_and_deserialize_output::(); + + core.mine_blocks(1); + + etch(&core, &ord, Rune(RUNE)); + + let rune_send = CommandBuilder::new(format!( + "--chain regtest --index-runes wallet send --fee-rate 1 {address} 1000:{}", + Rune(RUNE) + )) + .core(&core) + .ord(&ord) + .stdout_regex(".*") + .run_and_deserialize_output::(); + + let cardinal_send = CommandBuilder::new(format!( + "--chain regtest --index-runes wallet send --fee-rate 13.3 {address} 2btc" + )) + .core(&core) + .ord(&ord) + .run_and_deserialize_output::(); + + core.mine_blocks(6); + + let cardinals_response = ord.json_request(format!("/outputs/{}?type=cardinal", address)); + + assert_eq!(cardinals_response.status(), StatusCode::OK); + + let cardinals_json: Vec = + serde_json::from_str(&cardinals_response.text().unwrap()).unwrap(); + + pretty_assert_eq!( + cardinals_json, + vec![api::Output { + address: Some(address.parse().unwrap()), + inscriptions: vec![], + outpoint: OutPoint { + txid: cardinal_send.txid, + vout: 0 + }, + indexed: true, + runes: BTreeMap::new(), + sat_ranges: None, + script_pubkey: ScriptBuf::from( + address + .parse::>() + .unwrap() + .assume_checked() + ), + spent: false, + transaction: cardinal_send.txid, + value: 2 * COIN_VALUE, + }] + ); + + let runes_response = ord.json_request(format!("/outputs/{}?type=runic", address)); + + assert_eq!(runes_response.status(), StatusCode::OK); + + let runes_json: Vec = serde_json::from_str(&runes_response.text().unwrap()).unwrap(); + + let mut expected_runes = BTreeMap::new(); + + expected_runes.insert( + SpacedRune { + rune: Rune(RUNE), + spacers: 0, + }, + Pile { + amount: 1000, + divisibility: 0, + symbol: Some('¢'), + }, + ); + + pretty_assert_eq!( + runes_json, + vec![api::Output { + address: Some(address.parse().unwrap()), + inscriptions: vec![], + outpoint: OutPoint { + txid: rune_send.txid, + vout: 0 + }, + indexed: true, + runes: expected_runes, + sat_ranges: None, + script_pubkey: ScriptBuf::from( + address + .parse::>() + .unwrap() + .assume_checked() + ), + spent: false, + transaction: rune_send.txid, + value: 9901, + }] + ); + + let inscriptions_response = ord.json_request(format!("/outputs/{}?type=inscribed", address)); + + assert_eq!(inscriptions_response.status(), StatusCode::OK); + + let inscriptions_json: Vec = + serde_json::from_str(&inscriptions_response.text().unwrap()).unwrap(); + + pretty_assert_eq!( + inscriptions_json, + vec![api::Output { + address: Some(address.parse().unwrap()), + inscriptions: vec![InscriptionId { + txid: reveal, + index: 0 + },], + outpoint: OutPoint { + txid: inscription_send.txid, + vout: 0 + }, + indexed: true, + runes: BTreeMap::new(), + sat_ranges: None, + script_pubkey: ScriptBuf::from( + address + .parse::>() + .unwrap() + .assume_checked() + ), + spent: false, + transaction: inscription_send.txid, + value: 9901, + }] + ); + + let any: Vec = serde_json::from_str( + &ord + .json_request(format!("/outputs/{}?type=any", address)) + .text() + .unwrap(), + ) + .unwrap(); + + let default: Vec = serde_json::from_str( + &ord + .json_request(format!("/outputs/{}", address)) + .text() + .unwrap(), + ) + .unwrap(); + + assert_eq!(any.len(), 3); + assert!(any.iter().any(|output| output.runes.len() == 1)); + assert!(any.iter().any(|output| output.inscriptions.len() == 1)); + assert!(any + .iter() + .any(|output| output.inscriptions.is_empty() && output.runes.is_empty())); + assert_eq!(any, default); +} + +#[test] +fn outputs_address_returns_400_for_missing_indices() { + let core = mockcore::builder().network(Network::Regtest).build(); + let ord = TestServer::spawn_with_args( + &core, + &[ + "--no-index-inscriptions", + "--index-runes", + "--index-addresses", + "--regtest", + ], + ); + + let address = "bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw"; + + let inscriptions_response = ord.json_request(format!("/outputs/{}?type=inscribed", address)); + assert_eq!(inscriptions_response.status(), StatusCode::BAD_REQUEST); + + let runes_response = ord.json_request(format!("/outputs/{}?type=runic", address)); + assert_eq!(runes_response.status(), StatusCode::BAD_REQUEST); + + let cardinal_response = ord.json_request(format!("/outputs/{}?type=runic", address)); + assert_eq!(cardinal_response.status(), StatusCode::BAD_REQUEST); +} + +#[test] +fn outputs_address_returns_400_for_missing_rune_index() { + let core = mockcore::builder().network(Network::Regtest).build(); + let ord = TestServer::spawn_with_args(&core, &["--index-addresses", "--regtest"]); + + let address = "bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw"; + + let inscriptions_response = ord.json_request(format!("/outputs/{}?type=inscribed", address)); + assert_eq!(inscriptions_response.status(), StatusCode::BAD_REQUEST); + + let runes_response = ord.json_request(format!("/outputs/{}?type=runic", address)); + assert_eq!(runes_response.status(), StatusCode::BAD_REQUEST); + + let cardinal_response = ord.json_request(format!("/outputs/{}?type=runic", address)); + assert_eq!(cardinal_response.status(), StatusCode::BAD_REQUEST); +} diff --git a/tests/lib.rs b/tests/lib.rs index e6f9647103..cedafcaf2e 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -4,14 +4,14 @@ use { self::{command_builder::CommandBuilder, expected::Expected, test_server::TestServer}, bitcoin::{ address::{Address, NetworkUnchecked}, - Amount, Network, OutPoint, Sequence, Txid, Witness, + opcodes, script, Amount, Network, OutPoint, Sequence, TxOut, Txid, Witness, }, chrono::{DateTime, Utc}, executable_path::executable_path, mockcore::TransactionTemplate, ord::{ - api, chain::Chain, outgoing::Outgoing, subcommand::runes::RuneInfo, wallet::batch, - wallet::ListDescriptorsResult, InscriptionId, RuneEntry, + api, chain::Chain, outgoing::Outgoing, subcommand::runes::RuneInfo, templates::InscriptionHtml, + wallet::batch, wallet::ListDescriptorsResult, Inscription, InscriptionId, RuneEntry, }, ordinals::{ Artifact, Charm, Edict, Pile, Rarity, Rune, RuneId, Runestone, Sat, SatPoint, SpacedRune, @@ -75,10 +75,12 @@ mod wallet; const RUNE: u128 = 99246114928149462; type Balance = ord::subcommand::wallet::balance::Output; +type Balances = ord::subcommand::balances::Output; type Batch = ord::wallet::batch::Output; type Create = ord::subcommand::wallet::create::Output; type Inscriptions = Vec; type Send = ord::subcommand::wallet::send::Output; +type Split = ord::subcommand::wallet::split::Output; type Supply = ord::subcommand::supply::Output; fn create_wallet(core: &mockcore::Handle, ord: &TestServer) { @@ -366,9 +368,9 @@ fn batch(core: &mockcore::Handle, ord: &TestServer, batchfile: batch::File) -> E
    turbo
    {turbo}
    etching
    -
    {reveal}
    +
    {reveal}
    parent
    -
    {parent}
    +
    {parent}
    .*", mint_definition.join("\\s+"), ), diff --git a/tests/server.rs b/tests/server.rs index d92e8104d8..249b12b90b 100644 --- a/tests/server.rs +++ b/tests/server.rs @@ -54,7 +54,7 @@ fn address_page_shows_outputs_and_sat_balance() { ord.assert_response_regex( format!("/address/{address}"), format!( - ".*

    Address {address}

    .*
    200000000
    .*Address {address}.*
    200000000
    .*
    .*
    id
    -
    {inscription}
    +
    {inscription}
    address
    -
    bc1.*
    +
    bc1.*
    value
    10000
    preview
    @@ -268,15 +268,15 @@ fn inscription_page() {
    fee
    138
    reveal transaction
    -
    {reveal}
    +
    {reveal}
    location
    -
    {reveal}:0:0
    +
    {reveal}:0:0
    output
    -
    {reveal}:0
    +
    {reveal}:0
    offset
    0
    ethereum teleburn address
    -
    {ethereum_teleburn_address}
    +
    {ethereum_teleburn_address}
    .*", ), ); @@ -362,7 +362,7 @@ fn inscription_page_after_send() { ord.assert_response_regex( format!("/inscription/{inscription}"), format!( - r".*

    Inscription 0

    .*
    location
    \s*
    {reveal}:0:0
    .*", + r".*

    Inscription 0

    .*
    location
    \s*
    {reveal}:0:0
    .*", ), ); @@ -380,7 +380,7 @@ fn inscription_page_after_send() { ord.assert_response_regex( format!("/inscription/{inscription}"), format!( - r".*

    Inscription 0

    .*
    address
    \s*
    bc1qcqgs2pps4u4yedfyl5pysdjjncs8et5utseepv
    .*
    location
    \s*
    {txid}:0:0
    .*", + r".*

    Inscription 0

    .*
    address
    \s*
    bc1qcqgs2pps4u4yedfyl5pysdjjncs8et5utseepv
    .*
    location
    \s*
    {txid}:0:0
    .*", ), ) } diff --git a/tests/test_server.rs b/tests/test_server.rs index e351418f9c..57bdce2a8b 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -4,6 +4,7 @@ use { bitcoincore_rpc::{Auth, Client, RpcApi}, ord::{parse_ord_server_args, Index}, reqwest::blocking::Response, + sysinfo::System, }; pub(crate) struct TestServer { @@ -108,6 +109,36 @@ impl TestServer { pretty_assert_eq!(response.text().unwrap(), expected_response); } + #[track_caller] + pub(crate) fn assert_html( + &self, + path: impl AsRef, + chain: Chain, + content: impl ord::templates::PageContent, + ) { + self.sync_server(); + let response = reqwest::blocking::get(self.url().join(path.as_ref()).unwrap()).unwrap(); + + assert_eq!( + response.status(), + StatusCode::OK, + "{}", + response.text().unwrap() + ); + + let expected_response = ord::templates::PageHtml::new( + content, + Arc::new(ord::subcommand::server::ServerConfig { + chain, + domain: Some(System::host_name().unwrap()), + ..Default::default() + }), + ) + .to_string(); + + pretty_assert_eq!(response.text().unwrap(), expected_response); + } + pub(crate) fn request(&self, path: impl AsRef) -> Response { self.sync_server(); diff --git a/tests/wallet.rs b/tests/wallet.rs index 9ca09a3305..50c15c665a 100644 --- a/tests/wallet.rs +++ b/tests/wallet.rs @@ -1,5 +1,6 @@ use super::*; +mod addresses; mod authentication; mod balance; mod batch_command; @@ -22,4 +23,6 @@ mod runics; mod sats; mod selection; mod send; +mod sign; +mod split; mod transactions; diff --git a/tests/wallet/addresses.rs b/tests/wallet/addresses.rs new file mode 100644 index 0000000000..c5efbec83f --- /dev/null +++ b/tests/wallet/addresses.rs @@ -0,0 +1,76 @@ +use {super::*, ord::subcommand::wallet::addresses::Output}; + +#[test] +fn addresses() { + let core = mockcore::builder().network(Network::Regtest).build(); + + let ord = TestServer::spawn_with_server_args(&core, &["--regtest", "--index-runes"], &[]); + + create_wallet(&core, &ord); + + let rune = Rune(RUNE); + + let etched = batch( + &core, + &ord, + batch::File { + etching: Some(batch::Etching { + divisibility: 3, + premine: "1.111".parse().unwrap(), + rune: SpacedRune { rune, spacers: 1 }, + supply: "2.222".parse().unwrap(), + symbol: '¢', + terms: Some(batch::Terms { + amount: "1.111".parse().unwrap(), + cap: 1, + ..default() + }), + turbo: false, + }), + inscriptions: vec![batch::Entry { + file: Some("inscription.jpeg".into()), + ..default() + }], + ..default() + }, + ); + + let output = CommandBuilder::new("--regtest --index-runes wallet addresses") + .core(&core) + .ord(&ord) + .run_and_deserialize_output::, Vec>>(); + + pretty_assert_eq!( + output + .get(&etched.output.rune.clone().unwrap().destination.unwrap()) + .unwrap(), + &vec![Output { + output: etched.output.rune.unwrap().location.unwrap(), + amount: 10000, + inscriptions: Some(Vec::new()), + runes: Some( + vec![( + SpacedRune { rune, spacers: 1 }, + ord::decimal::Decimal { + value: 1111, + scale: 3, + } + )] + .into_iter() + .collect() + ), + }] + ); + + pretty_assert_eq!( + output + .get(&etched.output.inscriptions[0].destination) + .unwrap(), + &vec![Output { + output: etched.output.inscriptions[0].location.outpoint, + amount: 10000, + inscriptions: Some(vec![etched.output.inscriptions[0].id]), + runes: Some(BTreeMap::new()), + }] + ); +} diff --git a/tests/wallet/batch_command.rs b/tests/wallet/batch_command.rs index 34c9ac0df7..38904db6d9 100644 --- a/tests/wallet/batch_command.rs +++ b/tests/wallet/batch_command.rs @@ -682,7 +682,7 @@ inscriptions: format!("/inscription/{}", output.inscriptions[0].id), ".*
    address
    -
    bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4
    .*", +
    bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4
    .*", ); ord.assert_response_regex( @@ -690,7 +690,7 @@ inscriptions: format!( ".*
    address
    -
    {0}
    .*", +
    {0}
    .*", core.state().change_addresses[0], ), ); @@ -699,7 +699,7 @@ inscriptions: format!("/inscription/{}", output.inscriptions[2].id), ".*
    address
    -
    bc1pxwww0ct9ue7e8tdnlmug5m2tamfn7q06sahstg39ys4c9f3340qqxrdu9k
    .*", +
    bc1pxwww0ct9ue7e8tdnlmug5m2tamfn7q06sahstg39ys4c9f3340qqxrdu9k
    .*", ); } @@ -1529,7 +1529,7 @@ fn batch_can_etch_rune() { ord.assert_response_regex( "/rune/AAAAAAAAAAAAA", format!( - r".*\s*
    turbo
    \s*
    false
    .*
    parent
    \s*
    {parent}
    .*" + r".*\s*
    turbo
    \s*
    false
    .*
    parent
    \s*
    {parent}
    .*" ), ); @@ -1630,7 +1630,7 @@ fn batch_can_etch_turbo_rune() { ord.assert_response_regex( "/rune/AAAAAAAAAAAAA", format!( - r".*\s*
    turbo
    \s*
    true
    .*
    parent
    \s*
    {parent}
    .*" + r".*\s*
    turbo
    \s*
    true
    .*
    parent
    \s*
    {parent}
    .*" ), ); } @@ -1692,7 +1692,7 @@ fn batch_can_etch_rune_without_premine() { ord.assert_response_regex( "/rune/AAAAAAAAAAAAA", format!( - r".*
    parent
    \s*
    {parent}
    .*" + r".*
    parent
    \s*
    {parent}
    .*" ), ); @@ -1778,7 +1778,7 @@ fn batch_inscribe_can_etch_rune_with_offset() { ord.assert_response_regex( "/rune/AAAAAAAAAAAAA", format!( - r".*
    parent
    \s*
    {parent}
    .*" + r".*
    parent
    \s*
    {parent}
    .*" ), ); @@ -1852,7 +1852,7 @@ fn batch_inscribe_can_etch_rune_with_height() { ord.assert_response_regex( "/rune/AAAAAAAAAAAAA", format!( - r".*
    parent
    \s*
    {parent}
    .*" + r".*
    parent
    \s*
    {parent}
    .*" ), ); @@ -2616,7 +2616,7 @@ fn oversize_runestone_error() { ) .core(&core) .ord(&ord) - .expected_stderr("error: runestone greater than maximum OP_RETURN size: 104 > 82\n") + .expected_stderr("error: runestone greater than maximum OP_RETURN size: 104 > 83\n") .expected_exit_code(1) .run_and_extract_stdout(); } diff --git a/tests/wallet/burn.rs b/tests/wallet/burn.rs index 140a27dc21..07f355d3e6 100644 --- a/tests/wallet/burn.rs +++ b/tests/wallet/burn.rs @@ -32,6 +32,8 @@ fn inscriptions_can_be_burned() {
    🔥
    +
    value
    +
    9922
    .*
    content length
    3 bytes
    @@ -212,3 +214,190 @@ fn cannot_burn_with_excess_postage() { .expected_exit_code(1) .run_and_extract_stdout(); } + +#[test] +fn json_metadata_can_be_included_when_burning() { + let core = mockcore::spawn(); + + let ord = TestServer::spawn_with_server_args(&core, &[], &[]); + + create_wallet(&core, &ord); + + core.mine_blocks(1); + + let (inscription, _) = inscribe(&core, &ord); + + core.mine_blocks(1); + + let output = CommandBuilder::new(format!( + "wallet burn --fee-rate 1 {inscription} --json-metadata metadata.json" + )) + .core(&core) + .ord(&ord) + .write("metadata.json", r#"{"foo": "bar", "baz": 1}"#) + .stdout_regex(r".*") + .run_and_deserialize_output::(); + + let txid = core.mempool()[0].compute_txid(); + assert_eq!(txid, output.txid); + + core.mine_blocks(1); + + let script_pubkey = script::Builder::new() + .push_opcode(opcodes::all::OP_RETURN) + .push_slice([ + 0xA2, 0x63, b'f', b'o', b'o', 0x63, b'b', b'a', b'r', 0x63, b'b', b'a', b'z', 0x01, + ]) + .into_script(); + + ord.assert_html( + format!("/inscription/{inscription}"), + Chain::Mainnet, + InscriptionHtml { + charms: Charm::Burned.flag(), + fee: 138, + id: inscription, + output: Some(TxOut { + value: Amount::from_sat(9907), + script_pubkey, + }), + height: 3, + inscription: Inscription { + content_type: Some("text/plain;charset=utf-8".as_bytes().into()), + body: Some("foo".as_bytes().into()), + ..default() + }, + satpoint: SatPoint { + outpoint: OutPoint { + txid: output.txid, + vout: 0, + }, + offset: 0, + }, + timestamp: "1970-01-01 00:00:03+00:00" + .parse::>() + .unwrap(), + ..default() + }, + ); +} + +#[test] +fn cbor_metadata_can_be_included_when_burning() { + let core = mockcore::spawn(); + + let ord = TestServer::spawn_with_server_args(&core, &[], &[]); + + create_wallet(&core, &ord); + + core.mine_blocks(1); + + let (inscription, _) = inscribe(&core, &ord); + + core.mine_blocks(1); + + let metadata = [ + 0xA2, 0x63, b'f', b'o', b'o', 0x63, b'b', b'a', b'r', 0x63, b'b', b'a', b'z', 0x01, + ]; + + let output = CommandBuilder::new(format!( + "wallet burn --fee-rate 1 {inscription} --cbor-metadata metadata.cbor" + )) + .core(&core) + .ord(&ord) + .write("metadata.cbor", metadata) + .stdout_regex(r".*") + .run_and_deserialize_output::(); + + let txid = core.mempool()[0].compute_txid(); + assert_eq!(txid, output.txid); + + core.mine_blocks(1); + + let script_pubkey = script::Builder::new() + .push_opcode(opcodes::all::OP_RETURN) + .push_slice(metadata) + .into_script(); + + ord.assert_html( + format!("/inscription/{inscription}"), + Chain::Mainnet, + InscriptionHtml { + charms: Charm::Burned.flag(), + fee: 138, + id: inscription, + output: Some(TxOut { + value: Amount::from_sat(9907), + script_pubkey, + }), + height: 3, + inscription: Inscription { + content_type: Some("text/plain;charset=utf-8".as_bytes().into()), + body: Some("foo".as_bytes().into()), + ..default() + }, + satpoint: SatPoint { + outpoint: OutPoint { + txid: output.txid, + vout: 0, + }, + offset: 0, + }, + timestamp: "1970-01-01 00:00:03+00:00" + .parse::>() + .unwrap(), + ..default() + }, + ); +} + +#[test] +fn cbor_and_json_metadata_flags_conflict() { + let core = mockcore::spawn(); + + let ord = TestServer::spawn_with_server_args(&core, &[], &[]); + + create_wallet(&core, &ord); + + core.mine_blocks(1); + + let (inscription, _) = inscribe(&core, &ord); + + core.mine_blocks(1); + + CommandBuilder::new(format!( + "wallet burn --fee-rate 1 {inscription} --cbor-metadata foo --json-metadata bar" + )) + .core(&core) + .ord(&ord) + .stderr_regex( + "error: the argument '--cbor-metadata ' cannot be used with '--json-metadata '.*", + ) + .expected_exit_code(2) + .run_and_extract_stdout(); +} + +#[test] +fn oversize_metadata_requires_no_limit_flag() { + let core = mockcore::spawn(); + + let ord = TestServer::spawn_with_server_args(&core, &[], &[]); + + create_wallet(&core, &ord); + + core.mine_blocks(1); + + let (inscription, _) = inscribe(&core, &ord); + + core.mine_blocks(1); + + CommandBuilder::new(format!( + "wallet burn --fee-rate 1 {inscription} --json-metadata metadata.json" + )) + .core(&core) + .ord(&ord) + .write("metadata.json", format!("\"{}\"", "0".repeat(79))) + .stderr_regex("error: OP_RETURN with metadata larger than maximum: 84 > 83\n") + .expected_exit_code(1) + .run_and_extract_stdout(); +} diff --git a/tests/wallet/outputs.rs b/tests/wallet/outputs.rs index 57646e9a0f..f7c108dc16 100644 --- a/tests/wallet/outputs.rs +++ b/tests/wallet/outputs.rs @@ -94,3 +94,72 @@ fn outputs_includes_sat_ranges() { Some(vec!["5000000000-5001000000".to_string()]) ); } + +#[test] +fn outputs_includes_runes_and_inscriptions() { + let core = mockcore::builder().network(Network::Regtest).build(); + + let ord = TestServer::spawn_with_server_args(&core, &["--regtest", "--index-runes"], &[]); + + create_wallet(&core, &ord); + + let rune = Rune(RUNE); + + let etched = batch( + &core, + &ord, + batch::File { + etching: Some(batch::Etching { + divisibility: 3, + premine: "1.111".parse().unwrap(), + rune: SpacedRune { rune, spacers: 1 }, + supply: "2.222".parse().unwrap(), + symbol: '¢', + terms: Some(batch::Terms { + amount: "1.111".parse().unwrap(), + cap: 1, + ..default() + }), + turbo: false, + }), + inscriptions: vec![batch::Entry { + file: Some("inscription.jpeg".into()), + ..default() + }], + ..default() + }, + ); + + let output = CommandBuilder::new("--regtest --index-runes wallet outputs") + .core(&core) + .ord(&ord) + .run_and_deserialize_output::>(); + + assert!(output.contains(&Output { + output: etched.output.rune.clone().unwrap().location.unwrap(), + address: etched.output.rune.unwrap().destination, + amount: 10000, + inscriptions: Some(Vec::new()), + runes: Some( + vec![( + SpacedRune { rune, spacers: 1 }, + ord::decimal::Decimal { + value: 1111, + scale: 3, + } + )] + .into_iter() + .collect() + ), + sat_ranges: None, + })); + + assert!(output.contains(&Output { + output: etched.output.inscriptions[0].location.outpoint, + address: Some(etched.output.inscriptions[0].destination.clone()), + amount: 10000, + inscriptions: Some(vec![etched.output.inscriptions[0].id]), + runes: Some(BTreeMap::new()), + sat_ranges: None, + })); +} diff --git a/tests/wallet/restore.rs b/tests/wallet/restore.rs index 66b594e580..abf8c8c968 100644 --- a/tests/wallet/restore.rs +++ b/tests/wallet/restore.rs @@ -5,10 +5,24 @@ fn restore_generates_same_descriptors() { let (mnemonic, descriptors) = { let core = mockcore::spawn(); + let ord = TestServer::spawn(&core); + let create::Output { mnemonic, .. } = CommandBuilder::new("wallet create") .core(&core) .run_and_deserialize_output(); + let output = CommandBuilder::new("wallet dump") + .core(&core) + .ord(&ord) + .stderr_regex(".*THIS STRING CONTAINS YOUR PRIVATE KEYS.*") + .run_and_deserialize_output::(); + + // new descriptors are created with timestamp `now` + assert!(output + .descriptors + .iter() + .all(|descriptor| descriptor.timestamp == bitcoincore_rpc::json::Timestamp::Now)); + (mnemonic, core.descriptors()) }; @@ -19,6 +33,20 @@ fn restore_generates_same_descriptors() { .core(&core) .run_and_extract_stdout(); + let ord = TestServer::spawn(&core); + + let output = CommandBuilder::new("wallet dump") + .core(&core) + .ord(&ord) + .stderr_regex(".*THIS STRING CONTAINS YOUR PRIVATE KEYS.*") + .run_and_deserialize_output::(); + + // restored descriptors are created with timestamp `0` + assert!(output + .descriptors + .iter() + .all(|descriptor| descriptor.timestamp == bitcoincore_rpc::json::Timestamp::Time(0))); + assert_eq!(core.descriptors(), descriptors); } @@ -194,3 +222,149 @@ fn passphrase_conflicts_with_descriptor() { .expected_stderr("error: descriptor does not take a passphrase\n") .run_and_extract_stdout(); } + +#[test] +fn timestamp_conflicts_with_descriptor() { + let core = mockcore::spawn(); + let ord = TestServer::spawn(&core); + + CommandBuilder::new([ + "wallet", + "restore", + "--from", + "descriptor", + "--timestamp", + "now", + ]) + .stdin("".into()) + .core(&core) + .ord(&ord) + .expected_exit_code(1) + .expected_stderr("error: descriptor does not take a timestamp\n") + .run_and_extract_stdout(); +} + +#[test] +fn restore_with_now_timestamp() { + let mnemonic = { + let core = mockcore::spawn(); + + let create::Output { mnemonic, .. } = CommandBuilder::new(["wallet", "create"]) + .core(&core) + .run_and_deserialize_output(); + + mnemonic + }; + + let core = mockcore::spawn(); + let ord = TestServer::spawn(&core); + + CommandBuilder::new([ + "wallet", + "restore", + "--from", + "mnemonic", + "--timestamp", + "now", + ]) + .stdin(mnemonic.to_string().into()) + .core(&core) + .run_and_extract_stdout(); + + let output = CommandBuilder::new("wallet dump") + .core(&core) + .ord(&ord) + .stderr_regex(".*") + .run_and_deserialize_output::(); + + assert!(output + .descriptors + .iter() + .all(|descriptor| match descriptor.timestamp { + bitcoincore_rpc::json::Timestamp::Now => true, + bitcoincore_rpc::json::Timestamp::Time(time) => + time.abs_diff( + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs() + ) <= 5, + })); +} + +#[test] +fn restore_with_no_timestamp_defaults_to_0() { + let mnemonic = { + let core = mockcore::spawn(); + + let create::Output { mnemonic, .. } = CommandBuilder::new(["wallet", "create"]) + .core(&core) + .run_and_deserialize_output(); + + mnemonic + }; + + let core = mockcore::spawn(); + let ord = TestServer::spawn(&core); + + CommandBuilder::new(["wallet", "restore", "--from", "mnemonic"]) + .stdin(mnemonic.to_string().into()) + .core(&core) + .run_and_extract_stdout(); + + let output = CommandBuilder::new("wallet dump") + .core(&core) + .ord(&ord) + .stderr_regex(".*") + .run_and_deserialize_output::(); + + assert!(output + .descriptors + .iter() + .all(|descriptor| match descriptor.timestamp { + bitcoincore_rpc::json::Timestamp::Now => false, + bitcoincore_rpc::json::Timestamp::Time(time) => time == 0, + })); +} + +#[test] +fn restore_with_timestamp() { + let mnemonic = { + let core = mockcore::spawn(); + + let create::Output { mnemonic, .. } = CommandBuilder::new(["wallet", "create"]) + .core(&core) + .run_and_deserialize_output(); + + mnemonic + }; + + let core = mockcore::spawn(); + let ord = TestServer::spawn(&core); + + CommandBuilder::new([ + "wallet", + "restore", + "--from", + "mnemonic", + "--timestamp", + "123456789", + ]) + .stdin(mnemonic.to_string().into()) + .core(&core) + .run_and_extract_stdout(); + + let output = CommandBuilder::new("wallet dump") + .core(&core) + .ord(&ord) + .stderr_regex(".*") + .run_and_deserialize_output::(); + + assert!(output + .descriptors + .iter() + .all(|descriptor| match descriptor.timestamp { + bitcoincore_rpc::json::Timestamp::Now => false, + bitcoincore_rpc::json::Timestamp::Time(time) => time == 123456789, + })); +} diff --git a/tests/wallet/selection.rs b/tests/wallet/selection.rs index 4b16e15204..8f8c9313d3 100644 --- a/tests/wallet/selection.rs +++ b/tests/wallet/selection.rs @@ -210,3 +210,65 @@ fn sending_rune_does_not_send_inscription() { .expected_stderr("error: not enough cardinal utxos\n") .run_and_extract_stdout(); } + +#[test] +fn split_does_not_select_inscribed_or_runic_utxos() { + let core = mockcore::builder().network(Network::Regtest).build(); + + let ord = TestServer::spawn_with_server_args(&core, &["--regtest", "--index-runes"], &[]); + + create_wallet(&core, &ord); + + let rune = Rune(RUNE); + + etch(&core, &ord, rune); + + etch(&core, &ord, Rune(RUNE + 1)); + + drain(&core, &ord); + + pretty_assert_eq!( + CommandBuilder::new("--regtest wallet balance") + .core(&core) + .ord(&ord) + .run_and_deserialize_output::(), + Balance { + cardinal: 0, + ordinal: 20000, + runic: Some(20000), + runes: Some( + [ + (SpacedRune { rune, spacers: 0 }, "1000".parse().unwrap()), + ( + SpacedRune { + rune: Rune(RUNE + 1), + spacers: 0 + }, + "1000".parse().unwrap() + ), + ] + .into() + ), + total: 40000, + } + ); + + CommandBuilder::new("--regtest wallet split --fee-rate 0 --splits splits.yaml") + .core(&core) + .ord(&ord) + .write( + "splits.yaml", + format!( + " +outputs: +- address: bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw + value: 20000 sat + runes: + {rune}: 1000 +" + ), + ) + .expected_exit_code(1) + .expected_stderr("error: not enough cardinal utxos\n") + .run_and_extract_stdout(); +} diff --git a/tests/wallet/send.rs b/tests/wallet/send.rs index 630a08d0dd..46ef3ec635 100644 --- a/tests/wallet/send.rs +++ b/tests/wallet/send.rs @@ -39,7 +39,7 @@ fn inscriptions_can_be_sent() {
    text/plain;charset=utf-8
    .*
    location
    -
    {send_txid}:0:0
    +
    {send_txid}:0:0
    .* .*", @@ -153,7 +153,7 @@ fn send_inscription_by_sat() { ord.assert_response_regex( format!("/inscription/{inscription}"), format!( - ".*

    Inscription 0

    .*
    address
    .*
    {address}
    .*
    location
    .*
    {send_txid}:0:0
    .*", + ".*

    Inscription 0

    .*
    address
    .*
    {address}
    .*
    location
    .*
    {send_txid}:0:0
    .*", ), ); } @@ -345,6 +345,10 @@ inscriptions: output_json, api::Output { address: Some(destination.clone()), + outpoint: OutPoint { + txid: reveal_txid, + vout: 0 + }, inscriptions: vec![ InscriptionId { txid: reveal_txid, @@ -816,9 +820,9 @@ fn sending_rune_with_change_works() { pretty_assert_eq!( balances, ord::subcommand::balances::Output { - runes: vec![( + runes: [( SpacedRune::new(Rune(RUNE), 0), - vec![ + [ ( OutPoint { txid: output.txid, @@ -842,11 +846,155 @@ fn sending_rune_with_change_works() { }, ) ] - .into_iter() - .collect() + .into() )] - .into_iter() - .collect(), + .into() + } + ); +} + +#[test] +fn sending_rune_creates_change_output_for_non_outgoing_runes() { + let core = mockcore::builder().network(Network::Regtest).build(); + + let ord = TestServer::spawn_with_server_args(&core, &["--index-runes", "--regtest"], &[]); + + create_wallet(&core, &ord); + + let a = etch(&core, &ord, Rune(RUNE)); + let b = etch(&core, &ord, Rune(RUNE + 1)); + + let (a_block, a_tx) = core.tx_index(a.output.reveal); + let (b_block, b_tx) = core.tx_index(b.output.reveal); + + core.mine_blocks(1); + + let address = CommandBuilder::new("--regtest wallet receive") + .core(&core) + .ord(&ord) + .run_and_deserialize_output::() + .addresses + .into_iter() + .next() + .unwrap(); + + let merge = core.broadcast_tx(TransactionTemplate { + inputs: &[(a_block, a_tx, 1, default()), (b_block, b_tx, 1, default())], + recipient: Some(address.require_network(Network::Regtest).unwrap()), + ..default() + }); + + core.mine_blocks(1); + + let balances = CommandBuilder::new("--regtest --index-runes balances") + .core(&core) + .ord(&ord) + .run_and_deserialize_output::(); + + pretty_assert_eq!( + balances, + ord::subcommand::balances::Output { + runes: [ + ( + SpacedRune::new(Rune(RUNE), 0), + [( + OutPoint { + txid: merge, + vout: 0 + }, + Pile { + amount: 1000, + divisibility: 0, + symbol: Some('¢') + }, + )] + .into() + ), + ( + SpacedRune::new(Rune(RUNE + 1), 0), + [( + OutPoint { + txid: merge, + vout: 0 + }, + Pile { + amount: 1000, + divisibility: 0, + symbol: Some('¢') + }, + )] + .into() + ), + ] + .into() + } + ); + + let output = CommandBuilder::new(format!( + "--chain regtest --index-runes wallet send --fee-rate 1 bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw 1000:{}", + Rune(RUNE) + )) + .core(&core) + .ord(&ord) + .run_and_deserialize_output::(); + + core.mine_blocks(1); + + let balances = CommandBuilder::new("--regtest --index-runes balances") + .core(&core) + .ord(&ord) + .run_and_deserialize_output::(); + + pretty_assert_eq!( + balances, + ord::subcommand::balances::Output { + runes: [ + ( + SpacedRune::new(Rune(RUNE), 0), + [( + OutPoint { + txid: output.txid, + vout: 2 + }, + Pile { + amount: 1000, + divisibility: 0, + symbol: Some('¢') + }, + )] + .into() + ), + ( + SpacedRune::new(Rune(RUNE + 1), 0), + [( + OutPoint { + txid: output.txid, + vout: 1 + }, + Pile { + amount: 1000, + divisibility: 0, + symbol: Some('¢') + }, + )] + .into() + ) + ] + .into() + } + ); + + pretty_assert_eq!( + CommandBuilder::new("--regtest --index-runes wallet balance") + .core(&core) + .ord(&ord) + .run_and_deserialize_output::(), + Balance { + cardinal: 84999960160, + ordinal: 20000, + runes: Some([(SpacedRune::new(Rune(RUNE + 1), 0), "1000".parse().unwrap())].into()), + runic: Some(10000), + total: 84999990160, } ); } diff --git a/tests/wallet/sign.rs b/tests/wallet/sign.rs new file mode 100644 index 0000000000..d777213d82 --- /dev/null +++ b/tests/wallet/sign.rs @@ -0,0 +1,96 @@ +use { + super::*, + ord::subcommand::wallet::{addresses::Output as AddressesOutput, sign::Output as SignOutput}, +}; + +#[test] +fn sign() { + let core = mockcore::spawn(); + + let ord = TestServer::spawn_with_server_args(&core, &[], &[]); + + create_wallet(&core, &ord); + + core.mine_blocks(1); + + let addresses = CommandBuilder::new("wallet addresses") + .core(&core) + .ord(&ord) + .run_and_deserialize_output::, Vec>>(); + + let address = addresses.first_key_value().unwrap().0; + + let message = "HelloWorld"; + + let sign = CommandBuilder::new(format!( + "wallet sign --address {} --message {message}", + address.clone().assume_checked(), + )) + .core(&core) + .ord(&ord) + .run_and_deserialize_output::(); + + assert_eq!(address, &sign.address); + assert_eq!(message, &sign.message.unwrap()); + + CommandBuilder::new(format!( + "verify --address {} --message {message} --witness {}", + address.clone().assume_checked(), + sign.witness, + )) + .core(&core) + .ord(&ord) + .run_and_extract_stdout(); +} + +#[test] +fn sign_file() { + let core = mockcore::spawn(); + + let ord = TestServer::spawn_with_server_args(&core, &[], &[]); + + create_wallet(&core, &ord); + + core.mine_blocks(1); + + let addresses = CommandBuilder::new("wallet addresses") + .core(&core) + .ord(&ord) + .run_and_deserialize_output::, Vec>>(); + + let address = addresses.first_key_value().unwrap().0; + + let sign = CommandBuilder::new(format!( + "wallet sign --address {} --file hello.txt", + address.clone().assume_checked(), + )) + .write("hello.txt", "Hello World") + .core(&core) + .ord(&ord) + .run_and_deserialize_output::(); + + assert_eq!(address, &sign.address); + assert!(sign.message.is_none()); + + CommandBuilder::new(format!( + "verify --address {} --file hello.txt --witness {}", + address.clone().assume_checked(), + sign.witness, + )) + .write("hello.txt", "Hello World") + .core(&core) + .ord(&ord) + .run_and_extract_stdout(); + + CommandBuilder::new(format!( + "verify --address {} --file hello.txt --witness {}", + address.clone().assume_checked(), + sign.witness, + )) + .write("hello.txt", "FAIL") + .core(&core) + .ord(&ord) + .expected_exit_code(1) + .stderr_regex("error: Invalid signature.*") + .run_and_extract_stdout(); +} diff --git a/tests/wallet/split.rs b/tests/wallet/split.rs new file mode 100644 index 0000000000..63793fc8c9 --- /dev/null +++ b/tests/wallet/split.rs @@ -0,0 +1,293 @@ +use super::*; + +#[test] +fn requires_rune_index() { + let core = mockcore::spawn(); + + let ord = TestServer::spawn_with_server_args(&core, &[], &[]); + + create_wallet(&core, &ord); + + CommandBuilder::new("wallet split --fee-rate 1 --splits splits.yaml") + .core(&core) + .ord(&ord) + .expected_stderr("error: `ord wallet split` requires index created with `--index-runes`\n") + .expected_exit_code(1) + .run_and_extract_stdout(); +} + +#[test] +fn unrecognized_fields_are_forbidden() { + let core = mockcore::spawn(); + + let ord = TestServer::spawn_with_server_args(&core, &["--index-runes"], &[]); + + create_wallet(&core, &ord); + + CommandBuilder::new("wallet split --fee-rate 1 --splits splits.yaml") + .core(&core) + .ord(&ord) + .write( + "splits.yaml", + " +foo: +outputs: +", + ) + .stderr_regex("error: unknown field `foo`.*") + .expected_exit_code(1) + .run_and_extract_stdout(); + + CommandBuilder::new("wallet split --fee-rate 1 --splits splits.yaml") + .core(&core) + .ord(&ord) + .write( + "splits.yaml", + " +outputs: +- address: bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 + runes: + foo: +", + ) + .stderr_regex(r"error: outputs\[0\]: unknown field `foo`.*") + .expected_exit_code(1) + .run_and_extract_stdout(); +} + +#[test] +fn cannot_split_un_etched_runes() { + let core = mockcore::builder().network(Network::Regtest).build(); + + let ord = TestServer::spawn_with_server_args(&core, &["--regtest", "--index-runes"], &[]); + + create_wallet(&core, &ord); + + let rune = Rune(RUNE); + + CommandBuilder::new("--regtest wallet split --fee-rate 1 --splits splits.yaml") + .core(&core) + .ord(&ord) + .write( + "splits.yaml", + format!( + " +outputs: +- address: bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw + runes: + {rune}: 500 +" + ), + ) + .expected_stderr("error: rune `AAAAAAAAAAAAA` has not been etched\n") + .expected_exit_code(1) + .run_and_extract_stdout(); +} + +#[test] +fn simple_split() { + let core = mockcore::builder().network(Network::Regtest).build(); + + let ord = TestServer::spawn_with_server_args(&core, &["--regtest", "--index-runes"], &[]); + + create_wallet(&core, &ord); + + let rune = Rune(RUNE); + let spaced_rune = SpacedRune { rune, spacers: 1 }; + + batch( + &core, + &ord, + batch::File { + etching: Some(batch::Etching { + supply: "100.0".parse().unwrap(), + divisibility: 1, + terms: None, + premine: "100.0".parse().unwrap(), + rune: SpacedRune { rune, spacers: 1 }, + symbol: '¢', + turbo: false, + }), + inscriptions: vec![batch::Entry { + file: Some("inscription.jpeg".into()), + ..default() + }], + ..default() + }, + ); + + pretty_assert_eq!( + CommandBuilder::new("--regtest wallet balance") + .core(&core) + .ord(&ord) + .run_and_deserialize_output::(), + Balance { + cardinal: 7 * 50 * COIN_VALUE - 20000, + ordinal: 10000, + runic: Some(10000), + runes: Some([(spaced_rune, "100.0".parse().unwrap())].into()), + total: 7 * 50 * COIN_VALUE, + } + ); + + let output = CommandBuilder::new( + "--regtest wallet split --fee-rate 10 --postage 666sat --splits splits.yaml", + ) + .core(&core) + .ord(&ord) + .write( + "splits.yaml", + format!( + " +outputs: +- address: bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw + runes: + {spaced_rune}: 50.1 +" + ), + ) + .run_and_deserialize_output::(); + + assert_eq!(output.fee, 2030); + + core.mine_blocks_with_subsidy(1, 0); + + pretty_assert_eq!( + CommandBuilder::new("--regtest wallet balance") + .core(&core) + .ord(&ord) + .run_and_deserialize_output::(), + Balance { + cardinal: 7 * 50 * COIN_VALUE - 10960, + ordinal: 10000, + runic: Some(666), + runes: Some([(spaced_rune, "49.9".parse().unwrap())].into()), + total: 7 * 50 * COIN_VALUE - 294, + } + ); + + pretty_assert_eq!( + CommandBuilder::new("--regtest --index-runes balances") + .core(&core) + .ord(&ord) + .run_and_deserialize_output::(), + Balances { + runes: [( + spaced_rune, + [ + ( + OutPoint { + txid: output.txid, + vout: 1 + }, + Pile { + amount: 499, + divisibility: 1, + symbol: Some('¢'), + } + ), + ( + OutPoint { + txid: output.txid, + vout: 2 + }, + Pile { + amount: 501, + divisibility: 1, + symbol: Some('¢'), + } + ) + ] + .into() + ),] + .into(), + } + ); +} + +#[test] +fn oversize_op_returns_are_allowed_with_flag() { + let core = mockcore::builder().network(Network::Regtest).build(); + + let ord = TestServer::spawn_with_server_args(&core, &["--regtest", "--index-runes"], &[]); + + create_wallet(&core, &ord); + + let rune = Rune(RUNE); + + let spaced_rune = SpacedRune { rune, spacers: 1 }; + + batch( + &core, + &ord, + batch::File { + etching: Some(batch::Etching { + supply: "10000000000".parse().unwrap(), + divisibility: 0, + terms: None, + premine: "10000000000".parse().unwrap(), + rune: SpacedRune { rune, spacers: 1 }, + symbol: '¢', + turbo: false, + }), + inscriptions: vec![batch::Entry { + file: Some("inscription.jpeg".into()), + ..default() + }], + ..default() + }, + ); + + let mut splitfile = String::from("outputs:\n"); + + for _ in 0..10 { + splitfile.push_str( + "\n- address: bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw + runes: + AAAAAAAAAAAAA: 1000000000", + ); + } + + CommandBuilder::new("--regtest wallet split --fee-rate 0 --splits splits.yaml") + .core(&core) + .ord(&ord) + .write("splits.yaml", &splitfile) + .expected_stderr("error: runestone size 85 over maximum standard OP_RETURN size 83\n") + .expected_exit_code(1) + .run_and_extract_stdout(); + + let output = + CommandBuilder::new("--regtest wallet split --fee-rate 0 --splits splits.yaml --no-limit") + .core(&core) + .ord(&ord) + .write("splits.yaml", &splitfile) + .run_and_deserialize_output::(); + + core.mine_blocks(1); + + pretty_assert_eq!( + CommandBuilder::new("--regtest --index-runes balances") + .core(&core) + .ord(&ord) + .run_and_deserialize_output::(), + Balances { + runes: [( + spaced_rune, + (0..10) + .map(|i| ( + OutPoint { + txid: output.txid, + vout: 1 + i, + }, + Pile { + amount: 1000000000, + divisibility: 0, + symbol: Some('¢'), + } + ),) + .collect() + )] + .into(), + } + ); +}
    - {{ outpoint }} + + {{ outpoint }} + {{ balance }}