diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 80efcaa9..28e04107 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -57,7 +57,7 @@ jobs: strategy: fail-fast: false matrix: - toolchain: [ nightly, beta, stable, 1.76.0 ] + toolchain: [ nightly, beta, stable, 1.81.0 ] steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9821d00e..b56e1765 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -36,4 +36,4 @@ jobs: - name: Add wasm32 target run: rustup target add wasm32-unknown-unknown - name: Test in headless Chrome - run: wasm-pack test --headless --chrome + run: RUSTFLAGS='--cfg getrandom_backend="wasm_js"' wasm-pack test --headless --chrome diff --git a/.rustfmt.toml b/.rustfmt.toml index 9017cd7d..6d14899a 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -1,5 +1,5 @@ edition = "2021" -version = "Two" +style_edition = "2021" max_width = 100 array_width = 100 diff --git a/Cargo.lock b/Cargo.lock index 1e4aa19b..2e3014dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,17 +2,26 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "aluvm" -version = "0.11.0-beta.8" +version = "0.11.1-alpha.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db04c1d697d7f5b86d935bfe06cfd0310fd8a6c491b043118bec228597dcede9" +checksum = "6035110db4d2f0a0e6df87e7b61f1c397828ed127ed97d46896371a258888403" dependencies = [ "amplify", "ascii-armor", "baid64", "blake3", - "getrandom", + "getrandom 0.3.3", "half", "paste", "ripemd", @@ -25,16 +34,16 @@ dependencies = [ [[package]] name = "amplify" -version = "4.7.0" +version = "4.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7147b742325842988dd6c793d55f58df3ae36bccf7d9b6e07db10ab035be343d" +checksum = "3a9d7cb29f1d4c6ec8650abbee35948b8bdefb7f0750a26445ff593eb9bf7fcf" dependencies = [ "amplify_apfloat", "amplify_derive", "amplify_num", "amplify_syn", "ascii", - "rand", + "rand 0.8.5", "serde", "stringly_conversions", "wasm-bindgen", @@ -47,7 +56,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "695e433882668b55b3d7fb0ba22bf9be66a91abe30d7ca1f1a774f8b90b4db4c" dependencies = [ "amplify_num", - "bitflags 2.6.0", + "bitflags", "wasm-bindgen", ] @@ -101,9 +110,9 @@ dependencies = [ [[package]] name = "arrayref" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" @@ -122,9 +131,9 @@ dependencies = [ [[package]] name = "ascii-armor" -version = "0.7.2" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4966ac403dc4a666d8131dfe4df684f45acc68d4c7e768db89c463aa5617910" +checksum = "0269eb842ec952b027df0fc33184b6a0dea5ea473160b36992274eb53758461e" dependencies = [ "amplify", "baid64", @@ -135,15 +144,38 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "aws-lc-rs" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fcc8f365936c834db5514fc45aee5b1202d677e6b40e48468aaaa8183ca8c7" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "61b1d86e7705efe1be1b569bab41d4fa1e14e220b60a160f78de2db687add079" +dependencies = [ + "bindgen", + "cc", + "cmake", + "dunce", + "fs_extra", +] [[package]] name = "baid64" -version = "0.2.2" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95dabc2759e01e2c382968639868a701f384a18890934f9e75d4feb4d6623794" +checksum = "6cb4a8b2f1afee4ef00a190b260ad871842b93206177b59631fecd325d48d538" dependencies = [ "amplify", "base64", @@ -173,28 +205,55 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" [[package]] -name = "bitcoin-private" -version = "0.1.0" +name = "bindgen" +version = "0.69.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.101", + "which", +] + +[[package]] +name = "bitcoin-io" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73290177011694f38ec25e165d0387ab7ea749a4b81cd4c80dae5988229f7a57" +checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" [[package]] -name = "bitflags" -version = "1.3.2" +name = "bitcoin_hashes" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" +dependencies = [ + "bitcoin-io", + "hex-conservative", +] [[package]] name = "bitflags" -version = "2.6.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "blake3" -version = "1.5.4" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d82033247fd8e890df8f740e407ad4d038debb9eb1f40533fffb32e7d17dc6f7" +checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" dependencies = [ "arrayref", "arrayvec", @@ -212,11 +271,17 @@ dependencies = [ "generic-array", ] +[[package]] +name = "borrow-or-share" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eeab4423108c5d7c744f4d234de88d18d636100093ae04caf4825134b9c3a32" + [[package]] name = "bp-consensus" -version = "0.11.0-beta.8" +version = "0.11.1-alpha.2+unreviewed" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae3a99a46063d23d20a3177a04923652b245f31c2a04a6d0c47d5a93dc201a80" +checksum = "2d473a0cea358746ab5ef820b62f2530ce6516cb59226c9a3736a8e60c4943d9" dependencies = [ "amplify", "chrono", @@ -229,16 +294,17 @@ dependencies = [ [[package]] name = "bp-core" -version = "0.11.0-beta.8" +version = "0.11.1-alpha.2+unreviewed" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b8caf04291e2703ce267b1f8baf14f03879a6d1a5afe76e011ada489f172f9" +checksum = "d248df4e1ab41de97556bb092891c8c5208604f471a20c4129dc357fd9eade39" dependencies = [ "amplify", "bp-consensus", "bp-dbc", "bp-seals", "commit_verify", - "getrandom", + "getrandom 0.2.16", + "getrandom 0.3.3", "serde", "single_use_seals", "strict_encoding", @@ -248,9 +314,9 @@ dependencies = [ [[package]] name = "bp-dbc" -version = "0.11.0-beta.8" +version = "0.11.1-alpha.2+unreviewed" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11fc4081db2147411381b9650765ce683e5065559f1125508696f79cc4cbfedf" +checksum = "2670bd384743ba75ca6d8cf821bcdcb74bfd5715e8798e545331dc00652f612c" dependencies = [ "amplify", "base85", @@ -261,11 +327,46 @@ dependencies = [ "strict_encoding", ] +[[package]] +name = "bp-electrum" +version = "0.11.1-alpha.2+unreviewed" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "752903a7ec4bd5a2dc7709f4eb9fc86abe52a702a0f6699bebb79f021f506ec7" +dependencies = [ + "amplify", + "bp-core", + "byteorder", + "libc", + "log", + "rustls 0.23.27", + "serde", + "serde_json", + "sha2", + "webpki-roots 0.26.11", + "winapi", +] + +[[package]] +name = "bp-esplora" +version = "0.11.1-alpha.2+unreviewed" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faa310a99982cd5e5a51c070a0a0a4fb9ad4db85767febd476e10bbaf4b52ad1" +dependencies = [ + "amplify", + "bp-core", + "bp-invoice", + "log", + "minreq", + "serde", + "serde_with", + "sha2", +] + [[package]] name = "bp-invoice" -version = "0.11.0-beta.8" +version = "0.11.1-alpha.2+unreviewed" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29c4f672b4b82ce7a218b2900254ac8e4b15b0022ed13abffa722571e73d375" +checksum = "d2a2365fc099e265705f31f018d4fb9309546cc3fdb6f855da6d2a81e9700bd2" dependencies = [ "amplify", "bech32", @@ -275,16 +376,16 @@ dependencies = [ [[package]] name = "bp-seals" -version = "0.11.0-beta.8" +version = "0.11.1-alpha.2+unreviewed" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d607238c2bf2c34d048d14cd798a6365306e0fb6b02211235f3ccad0bc7fa8f1" +checksum = "3aab0c94862b08721f7a60eb0d13502605147ebc2fc3cfe0ceacdb6d58aeb43c" dependencies = [ "amplify", "baid64", "bp-consensus", "bp-dbc", "commit_verify", - "rand", + "rand 0.9.1", "serde", "single_use_seals", "strict_encoding", @@ -292,9 +393,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "byteorder" @@ -304,13 +405,24 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.1.16" +version = "1.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9d013ecb737093c0e86b151a7b837993cf9ec6c502946cfb44bedc392421e0b" +checksum = "16595d3be041c03b09d08d0858631facccee9221e579704070e6e9e4915d3bc7" dependencies = [ + "jobserver", + "libc", "shlex", ] +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -319,9 +431,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" dependencies = [ "android-tzdata", "iana-time-zone", @@ -329,14 +441,34 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets", + "windows-link", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", ] [[package]] name = "commit_encoding_derive" -version = "0.11.0-beta.8" +version = "0.11.1-alpha.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea07c5ad73a637276dc4f8a957f8285764018d45bdefef35eb9137f32d0e3c81" +checksum = "d12f2e05ae7d81bc49d9f0856ff97968da750bf09f145043155e9c7f13ce4ace" dependencies = [ "amplify", "amplify_syn", @@ -347,13 +479,13 @@ dependencies = [ [[package]] name = "commit_verify" -version = "0.11.0-beta.8" +version = "0.11.1-alpha.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82a1982dc6c54d2dcfa2bf4398d97e4e80a93f24d2537e58d6110b2b272cff0c" +checksum = "171940b95b456f7c8906c78cd548c1dcf30310c4dc2e2a5ba352ac183bf163b3" dependencies = [ "amplify", "commit_encoding_derive", - "rand", + "rand 0.9.1", "ripemd", "serde", "sha2", @@ -362,16 +494,6 @@ dependencies = [ "vesper-lang", ] -[[package]] -name = "console_error_panic_hook" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" -dependencies = [ - "cfg-if", - "wasm-bindgen", -] - [[package]] name = "constant_time_eq" version = "0.3.1" @@ -386,18 +508,18 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.13" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] [[package]] name = "crunchy" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" [[package]] name = "crypto-common" @@ -409,6 +531,51 @@ dependencies = [ "typenum", ] +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.101", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "deranged" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +dependencies = [ + "powerfmt", + "serde", +] + [[package]] name = "digest" version = "0.10.7" @@ -419,27 +586,62 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] [[package]] name = "fast32" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27ea9bdb2356e5a92403cf23ac493f9b43bd71e4ffd0f800862b841dd723994c" +checksum = "a35a73237400bde66c82e38387343f90d7182a2f2f22729e096a2abd57d75db9" [[package]] name = "fluent-uri" -version = "0.1.4" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17c704e9dbe1ddd863da1e6ff3567795087b1eb201ce80d8fa81162e1516500d" +checksum = "1918b65d96df47d3591bed19c5cca17e3fa5d0707318e4b5ef2eae01764df7e5" dependencies = [ - "bitflags 1.3.2", + "borrow-or-share", + "ref-cast", ] +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "generic-array" version = "0.14.7" @@ -452,22 +654,42 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "js-sys", "libc", - "wasi", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", "wasm-bindgen", ] +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + [[package]] name = "half" -version = "2.4.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" dependencies = [ "cfg-if", "crunchy", @@ -475,9 +697,15 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" [[package]] name = "heck" @@ -485,16 +713,41 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-conservative" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "iana-time-zone" -version = "0.1.60" +version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", + "log", "wasm-bindgen", "windows-core", ] @@ -508,42 +761,108 @@ dependencies = [ "cc", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "indexmap" -version = "2.5.0" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.15.3", + "serde", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", ] [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jobserver" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +dependencies = [ + "getrandom 0.3.3", + "libc", +] [[package]] name = "js-sys" -version = "0.3.70" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" -version = "0.2.158" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + +[[package]] +name = "libloading" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c" +dependencies = [ + "cfg-if", + "windows-targets 0.53.0", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "log" -version = "0.4.22" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "memchr" @@ -559,29 +878,66 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "minicov" -version = "0.3.5" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c71e683cd655513b99affab7d317deb690528255a0d5f717f1024093c12b169" +checksum = "f27fe9f1cc3c22e1687f9446c2083c4c5fc7f0bcf1c7a86bdbded14985895b4b" dependencies = [ "cc", "walkdir", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "minreq" +version = "2.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d2aaba477837b46ec1289588180fabfccf0c3b1d1a0c6b1866240cd6cd5ce9" +dependencies = [ + "base64", + "log", + "rustls 0.21.12", + "rustls-webpki 0.101.7", + "serde", + "serde_json", + "webpki-roots 0.25.4", +] + [[package]] name = "mnemonic" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2b8f3a258db515d5e91a904ce4ae3f73e091149b90cadbdb93d210bee07f63b" +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "nonasync" -version = "0.1.0" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94a84b7c873630913f738950f17412b9d5b24cad6866b98b802253f8cbbefabb" +checksum = "d81f7335a3fa37124e24461d6164485760d4c056dd92a81a70c23fc28c2b63c8" dependencies = [ "amplify", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-traits" version = "0.2.19" @@ -593,9 +949,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "paste" @@ -609,33 +965,55 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ "zerocopy", ] +[[package]] +name = "prettyplease" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6" +dependencies = [ + "proc-macro2", + "syn 2.0.101", +] + [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.37" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "rand" version = "0.8.5" @@ -643,18 +1021,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", ] [[package]] -name = "rand_chacha" -version = "0.3.1" +name = "rand" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" dependencies = [ - "ppv-lite86", - "rand_core", + "rand_chacha 0.9.0", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", ] [[package]] @@ -663,14 +1061,72 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.16", ] +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", +] + +[[package]] +name = "ref-cast" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + [[package]] name = "rgb-core" -version = "0.11.0-beta.8" +version = "0.11.1-alpha.3+unreviewed" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43dc70212f5eff8189f3cdfef2d11f53f7be4c4128db9839b5d56a0f9ef60c98" +checksum = "9ee6739c9704151ca82f9346f73b1d80189637c8291565866fee6859511e2008" dependencies = [ "aluvm", "amplify", @@ -678,9 +1134,9 @@ dependencies = [ "bp-core", "chrono", "commit_verify", - "getrandom", + "getrandom 0.3.3", "mime", - "secp256k1-zkp", + "secp256k1", "serde", "single_use_seals", "strict_encoding", @@ -690,7 +1146,7 @@ dependencies = [ [[package]] name = "rgb-invoice" -version = "0.11.0-beta.8" +version = "0.11.1-alpha.3+unreviewed" dependencies = [ "amplify", "baid64", @@ -698,9 +1154,9 @@ dependencies = [ "bp-invoice", "fast32", "fluent-uri", - "indexmap", + "indexmap 2.9.0", "percent-encoding", - "rand", + "rand 0.9.1", "rgb-core", "serde", "strict_encoding", @@ -709,7 +1165,7 @@ dependencies = [ [[package]] name = "rgb-std" -version = "0.11.0-beta.8" +version = "0.11.1-alpha.3+unreviewed" dependencies = [ "aluvm", "amplify", @@ -717,12 +1173,14 @@ dependencies = [ "baid64", "base85", "bp-core", + "bp-electrum", + "bp-esplora", "chrono", "commit_verify", - "getrandom", - "indexmap", + "getrandom 0.3.3", + "indexmap 2.9.0", "nonasync", - "rand", + "rand 0.9.1", "rgb-core", "rgb-invoice", "serde", @@ -734,7 +1192,7 @@ dependencies = [ [[package]] name = "rgb-stl" -version = "0.11.0-beta.8" +version = "0.11.1-alpha.3+unreviewed" dependencies = [ "amplify", "commit_verify", @@ -742,6 +1200,20 @@ dependencies = [ "strict_types", ] +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "ripemd" version = "0.1.3" @@ -752,94 +1224,159 @@ dependencies = [ ] [[package]] -name = "ryu" -version = "1.0.18" +name = "rustc-hash" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] -name = "same-file" -version = "1.0.6" +name = "rustix" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "winapi-util", + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", ] [[package]] -name = "scoped-tls" -version = "1.0.1" +name = "rustls" +version = "0.21.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring", + "rustls-webpki 0.101.7", + "sct", +] [[package]] -name = "secp256k1" -version = "0.29.0" +name = "rustls" +version = "0.23.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e0cc0f1cf93f4969faf3ea1c7d8a9faed25918d96affa959720823dfe86d4f3" +checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" dependencies = [ - "rand", - "secp256k1-sys", - "serde", + "aws-lc-rs", + "log", + "once_cell", + "rustls-pki-types", + "rustls-webpki 0.103.3", + "subtle", + "zeroize", ] [[package]] -name = "secp256k1-sys" -version = "0.10.0" +name = "rustls-pki-types" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1433bd67156263443f14d603720b082dd3121779323fce20cba2aa07b874bc1b" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" dependencies = [ - "cc", + "zeroize", ] [[package]] -name = "secp256k1-zkp" -version = "0.11.0" +name = "rustls-webpki" +version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52a44aed3002b5ae975f8624c5df3a949cfbf00479e18778b6058fcd213b76e3" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "bitcoin-private", - "rand", - "secp256k1", - "secp256k1-zkp-sys", + "ring", + "untrusted", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "secp256k1" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" +dependencies = [ + "bitcoin_hashes", + "rand 0.8.5", + "secp256k1-sys", "serde", ] [[package]] -name = "secp256k1-zkp-sys" -version = "0.10.0" +name = "secp256k1-sys" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c6eea7919e0cab992510edfbf40bd9342c0a3c2bb910f2c51355c2cb2d69839" +checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" dependencies = [ "cc", - "secp256k1-sys", ] [[package]] name = "serde" -version = "1.0.209" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.209" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.101", ] [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "itoa", "memchr", @@ -849,9 +1386,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] @@ -866,13 +1403,43 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_with" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" +dependencies = [ + "base64", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.9.0", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "serde_yaml" version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap", + "indexmap 2.9.0", "itoa", "ryu", "serde", @@ -881,9 +1448,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -898,21 +1465,20 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "single_use_seals" -version = "0.11.0-beta.8" +version = "0.11.1-alpha.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1a4c51f21507cf63984c367507f281215073e85b08711ed7da4fc63dbd709e0" +checksum = "8c36139c6f642d05f2d74501a8f84ccfb5833caeb7c8cde1e6b811261cd526bd" dependencies = [ "amplify_derive", ] [[package]] name = "strict_encoding" -version = "2.7.0" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d69b4893cf054e129d5288a565102124520d7b94eb9589d1e78202abc7e2092d" +checksum = "8553c0321466c11aa1e33f082c9190194f380efd8824bf5ce4fa56b64b875be9" dependencies = [ "amplify", - "half", "serde", "strict_encoding_derive", "wasm-bindgen", @@ -920,9 +1486,9 @@ dependencies = [ [[package]] name = "strict_encoding_derive" -version = "2.7.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d4f9b678862372f8e439bcaafc27df7610ea93b06d2deb6244dec0af4259ce6" +checksum = "34e3bc6e4a2450420b4dbfb6929d9ce005e8c36cf73bf215db99f0d09c9fa79f" dependencies = [ "amplify_syn", "heck", @@ -933,15 +1499,14 @@ dependencies = [ [[package]] name = "strict_types" -version = "2.7.0" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f16e8855a575633815f01482ac927ebaca3d2485aec8e17226c6826de29154e" +checksum = "07dd1bdf4bfce0a1ff3eec041e7d4d20b06d22ca2aaf71338726dd9609e57a5e" dependencies = [ "amplify", "ascii-armor", "baid64", - "half", - "indexmap", + "indexmap 2.9.0", "serde", "serde_json", "serde_yaml", @@ -962,6 +1527,18 @@ dependencies = [ "serde_str_helpers", ] +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "1.0.109" @@ -975,9 +1552,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", @@ -986,29 +1563,60 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.101", +] + +[[package]] +name = "time" +version = "0.3.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" + +[[package]] +name = "time-macros" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +dependencies = [ + "num-conv", + "time-core", ] [[package]] name = "toml" -version = "0.8.19" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" dependencies = [ "serde", "serde_spanned", @@ -1018,37 +1626,44 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.20" +version = "0.22.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ - "indexmap", + "indexmap 2.9.0", "serde", "serde_spanned", "toml_datetime", + "toml_write", "winnow", ] +[[package]] +name = "toml_write" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" + [[package]] name = "typenum" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unsafe-libyaml" @@ -1056,6 +1671,12 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "version_check" version = "0.9.5" @@ -1064,9 +1685,9 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "vesper-lang" -version = "0.1.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f72ebd3b32f16ee8ace2bd3058c2bfa0f4820992bd4ea86e73ba228bb13dd2b0" +checksum = "cd2b7e3e27aeb0524204e58042f6e4531a720745d1b1a3978d3a084f1885f63d" dependencies = [ "amplify", "strict_encoding", @@ -1088,49 +1709,59 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasm-bindgen" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.101", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.43" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1138,33 +1769,34 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.101", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasm-bindgen-test" -version = "0.3.43" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68497a05fb21143a08a7d24fc81763384a3072ee43c44e86aad1744d6adef9d9" +checksum = "66c8d5e33ca3b6d9fa3b4676d774c5778031d27a578c2b007f905acf816152c3" dependencies = [ - "console_error_panic_hook", "js-sys", "minicov", - "scoped-tls", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-test-macro", @@ -1172,41 +1804,158 @@ dependencies = [ [[package]] name = "wasm-bindgen-test-macro" -version = "0.3.43" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8220be1fa9e4c889b30fd207d4906657e7e90b12e0e6b0c8b8d8709f5de021" +checksum = "17d5042cc5fa009658f9a7333ef24291b1291a25b6382dd68862a7f3b969f69b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.101", ] [[package]] name = "web-sys" -version = "0.3.70" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", ] +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.0", +] + +[[package]] +name = "webpki-roots" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + [[package]] name = "winapi-util" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys", + "windows-sys 0.59.0", ] +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1215,7 +1964,7 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1224,14 +1973,30 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", ] [[package]] @@ -1240,74 +2005,136 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "winnow" -version = "0.6.18" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" dependencies = [ "memchr", ] +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags", +] + [[package]] name = "zerocopy" -version = "0.7.35" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" dependencies = [ - "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.35" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.101", ] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/Cargo.toml b/Cargo.toml index b4715406..853bd336 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,27 +11,29 @@ default-members = [ resolver = "2" [workspace.package] -version = "0.11.0-beta.8" +version = "0.11.1-alpha.3+unreviewed" authors = ["Dr Maxim Orlovsky "] homepage = "https://github.com/RGB-WG" repository = "https://github.com/RGB-WG/rgb-std" keywords = ["bitcoin", "lightning", "rgb", "smart-contracts", "lnp-bp"] categories = ["cryptography::cryptocurrencies"] -rust-version = "1.76.0" # Due to use of `inspect_err` +rust-version = "1.81.0" edition = "2021" license = "Apache-2.0" [workspace.dependencies] -amplify = "4.7.0" +amplify = "4.8.0" nonasync = "0.1.0" -ascii-armor = "0.7.2" -baid64 = "0.2.2" -strict_encoding = "2.7.0" -strict_types = "2.7.0" -commit_verify = { version = "0.11.0-beta.8", features = ["stl"] } -bp-core = { version = "0.11.0-beta.8", features = ["stl"] } -bp-invoice = { version = "0.11.0-beta.8" } -rgb-core = { version = "0.11.0-beta.8", features = ["stl"] } +ascii-armor = "0.9.0" +baid64 = "0.4.1" +bp-electrum = "0.11.1-alpha.2" +bp-esplora = { version = "0.11.1-alpha.2", default-features = false } +strict_encoding = "2.8.2" +strict_types = "2.8.3" +commit_verify = { version = "0.11.1-alpha.2", features = ["stl"] } +bp-core = { version = "0.11.1-alpha.2", features = ["stl"] } +bp-invoice = { version = "0.11.1-alpha.2" } +rgb-core = { version = "0.11.1-alpha.3", features = ["stl"] } indexmap = "2.4.0" serde_crate = { package = "serde", version = "1", features = ["derive"] } @@ -58,22 +60,28 @@ amplify = { workspace = true } nonasync = { workspace = true } ascii-armor = { workspace = true } baid64 = { workspace = true } +bp-electrum = { workspace = true, optional = true } +bp-esplora = { workspace = true, optional = true } strict_encoding = { workspace = true } strict_types = { workspace = true } commit_verify = { workspace = true } bp-core = { workspace = true } rgb-core = { workspace = true } -rgb-invoice = { version = "0.11.0-beta.6", path = "invoice" } -aluvm = "0.11.0-beta.7" +rgb-invoice = { version = "0.11.1-alpha.2", path = "invoice" } +aluvm = "0.11.1-alpha.2" base85 = "=2.0.0" chrono = "0.4.38" indexmap = { workspace = true } serde_crate = { workspace = true, optional = true } -rand = "0.8.5" +rand = "0.9.1" [features] default = [] -all = ["fs", "serde"] +all = ["esplora_blocking", "electrum_blocking", "mempool_blocking", "fs", "serde"] +esplora_blocking = ["bp-esplora", "bp-esplora/blocking", "bp-esplora/blocking-https"] +esplora_blocking-wasm = ["bp-esplora", "bp-esplora/blocking-wasm"] +electrum_blocking = ["bp-electrum"] +mempool_blocking = ["esplora_blocking"] serde = [ "serde_crate", "chrono/serde", @@ -89,8 +97,8 @@ fs = [] [target.'cfg(target_arch = "wasm32")'.dependencies] wasm-bindgen = "0.2" -rand = { version = "0.8.4", optional = true } -getrandom = { version = "0.2", features = ["js"] } +rand = { version = "0.9.1", optional = true } +getrandom = { version = "0.3", features = ["wasm_js"] } [target.'cfg(target_arch = "wasm32")'.dev-dependencies] wasm-bindgen-test = "0.3" diff --git a/MANIFEST.yml b/MANIFEST.yml index c5f31a0d..aa9bf5b9 100644 --- a/MANIFEST.yml +++ b/MANIFEST.yml @@ -3,7 +3,7 @@ Type: Library Kind: Free software License: Apache-2.0 Language: Rust -Compiler: 1.76 +Compiler: 1.81 Author: Maxim Orlovsky Maintained: LNP/BP Standards Association, Switzerland Maintainers: diff --git a/asset/armored_contract.default b/asset/armored_contract.default index 4944ab6e..fcf76d6e 100644 --- a/asset/armored_contract.default +++ b/asset/armored_contract.default @@ -1,13 +1,12 @@ -----BEGIN RGB CONSIGNMENT----- -Id: rgb:csg:mK5KaBwW-ht!iR1o-5qi3fe7-gIHhBiL-t80Tud5-Wi2Jo4A#bikini-binary-table -Version: 2 +Id: rgb:csg:7EPeHd3A-SpVC4OT-DQqNfRq-XQJlWQQ-SSZh7ZL-RNjGI8A#monkey-chemist-join +Version: 0 Type: contract -Contract: rgb:5M7hTCP5-or5y2Bp-xPPIYez-WEsey5D-e2GhCpV-HlsK7jI -Schema: rgb:sch:CyqM42yAdM1moWyNZPQedAYt73BM$k9z$dKLUXY1voA#cello-global-deluxe -Check-SHA256: 181748dae0c83cbb44f6ccfdaddf6faca0bc4122a9f35fef47bab9aea023e4a1 +Contract: rgb:ekxv8ZNU-206FIlW-XN6ZxMq-OcOErkI-Qu3w06i-~4JCKxQ +Schema: rgb:sch:jF0BoXFm8GGMSV0q9CsY0jmpVkwn811VmU2o7m0lKGo#hammer-citrus-time +Check-SHA256: 753ab0d7bee40de37936bf5e60cebfb15f57627d86cd509f1f82869b9775fce1 -0ssI2000000000000000000000000000000000000000000000000000000D0CRI`I$>^aZh38Qb#nj! -0000000000000000000000d59ZDjxe00000000dDb8~4rVQz13d2MfXa{vGU00000000000000000000 -0000000000000 +0000000000000000000000000000000000000000000000000000000000dDb8~4rVQz13d2MfXa{>SW +000000000000d59ZDjxe000000000000000 -----END RGB CONSIGNMENT----- diff --git a/asset/armored_kit.default b/asset/armored_kit.default index 7a10425d..0ad40729 100644 --- a/asset/armored_kit.default +++ b/asset/armored_kit.default @@ -1,9 +1,9 @@ -----BEGIN RGB KIT----- -Id: rgb:kit:e1jW6Rgc-2$JzXDg-XmR8XRJ-v!q$Dzf-yImkPjD-t8EjfvI -Version: 2 -Type-System: sts:8Vb$sM1F-5MsQc20-HEixf55-gJR37FM-0zRKfpY-SwIp35w#design-farmer-camel -Check-SHA256: 5563cc1568e244183804e0db3cec6ff9bf577f4a403924096177bf4a586160da +Id: rgb:kit:jXOeJYkD-NlOgJoP-_zT1Hvl-0fP71Zo-b2mAh2C-i5ISROo +Version: 0 +Type-System: sts:8Vb~sM1F-5MsQc20-HEixf55-gJR37FM-0zRKfpY-SwIp35w#design-farmer-camel +Check-SHA256: 837885c8f8091aeaeb9ec3c3f85a6ff470a415e610b8ba3e49f9b33c9cf9d619 -0ssI2000000000 +000000000 -----END RGB KIT----- diff --git a/asset/armored_transfer.default b/asset/armored_transfer.default index e1cb27e8..863d7a1e 100644 --- a/asset/armored_transfer.default +++ b/asset/armored_transfer.default @@ -1,13 +1,12 @@ -----BEGIN RGB CONSIGNMENT----- -Id: rgb:csg:H58AX9qS-4IFd5aQ-tx9Exr$-DzCvDDW-luMVTFJ-GqEnZ3k#avenue-educate-finland -Version: 2 +Id: rgb:csg:lBZui4Kc-TXlFr5L-~s7hdIj-VT7hOG_-8vs2vkr-Sfoey1I#blonde-pandora-public +Version: 0 Type: transfer -Contract: rgb:5M7hTCP5-or5y2Bp-xPPIYez-WEsey5D-e2GhCpV-HlsK7jI -Schema: rgb:sch:CyqM42yAdM1moWyNZPQedAYt73BM$k9z$dKLUXY1voA#cello-global-deluxe -Check-SHA256: 562a944631243e23a8de1d2aa2a5621be13351fc6f4d9aa8127c12ac4fb54d97 +Contract: rgb:ekxv8ZNU-206FIlW-XN6ZxMq-OcOErkI-Qu3w06i-~4JCKxQ +Schema: rgb:sch:jF0BoXFm8GGMSV0q9CsY0jmpVkwn811VmU2o7m0lKGo#hammer-citrus-time +Check-SHA256: 0906488190013e3373e9a4dd53fc62f3b25d51e5378154830f657e9bfff25079 -0s#O3000000000000000000000000000000000000000000000000000000D0CRI`I$>^aZh38Qb#nj! -0000000000000000000000d59ZDjxe00000000dDb8~4rVQz13d2MfXa{vGU00000000000000000000 -0000000000000 +0096100000000000000000000000000000000000000000000000000000dDb8~4rVQz13d2MfXa{>SW +000000000000d59ZDjxe000000000000000 -----END RGB CONSIGNMENT----- diff --git a/asset/contract.default b/asset/contract.default index 33e2bd71..18f3e14f 100644 Binary files a/asset/contract.default and b/asset/contract.default differ diff --git a/asset/kit.default b/asset/kit.default index 67d729f7..e19e3f90 100644 Binary files a/asset/kit.default and b/asset/kit.default differ diff --git a/asset/transfer.default b/asset/transfer.default index 5685f5e9..f3fe4bef 100644 Binary files a/asset/transfer.default and b/asset/transfer.default differ diff --git a/invoice/Cargo.toml b/invoice/Cargo.toml index b0cfb329..11b00883 100644 --- a/invoice/Cargo.toml +++ b/invoice/Cargo.toml @@ -18,19 +18,18 @@ name = "rgbinvoice" [dependencies] amplify = { workspace = true } baid64 = { workspace = true } -fast32 = "1.0.2" +fast32 = "1.0.3" strict_encoding = { workspace = true } strict_types = { workspace = true } bp-core = { workspace = true } bp-invoice = { workspace = true } rgb-core = { workspace = true } indexmap = { workspace = true } -fluent-uri = "0.1.4" +fluent-uri = "0.3.2" percent-encoding = "2.3.1" serde_crate = { workspace = true, optional = true } -rand = "0.8.5" +rand = "0.9.1" [features] default = [] serde = ["serde_crate"] -# TODO: Separate URL with a feature gate diff --git a/invoice/src/amount.rs b/invoice/src/amount.rs index 9007f085..b065e620 100644 --- a/invoice/src/amount.rs +++ b/invoice/src/amount.rs @@ -63,7 +63,7 @@ impl StrictSerialize for Amount {} impl StrictDeserialize for Amount {} impl From for Amount { - fn from(value: RevealedValue) -> Self { Amount(value.value.as_u64()) } + fn from(value: RevealedValue) -> Self { Amount(value.as_u64()) } } impl From for Amount { @@ -74,6 +74,10 @@ impl From for FungibleState { fn from(amount: Amount) -> Self { FungibleState::Bits64(amount.0) } } +impl From for RevealedValue { + fn from(amount: Amount) -> Self { RevealedValue::from(FungibleState::Bits64(amount.0)) } +} + impl Amount { pub const ZERO: Self = Amount(0); diff --git a/invoice/src/builder.rs b/invoice/src/builder.rs index 17f2da31..7af5fc56 100644 --- a/invoice/src/builder.rs +++ b/invoice/src/builder.rs @@ -21,8 +21,8 @@ use std::str::FromStr; -use rgb::ContractId; -use strict_encoding::{FieldName, TypeName}; +use rgb::{ContractId, SchemaId}; +use strict_types::FieldName; use crate::invoice::{Beneficiary, InvoiceState, RgbInvoice, RgbTransport, XChainNet}; use crate::{Allocation, Amount, CoinAmount, NonFungible, Precision, TransportParseError}; @@ -36,11 +36,10 @@ impl RgbInvoiceBuilder { Self(RgbInvoice { transports: vec![RgbTransport::UnspecifiedMeans], contract: None, - iface: None, - operation: None, - assignment: None, + schema: None, + assignment_name: None, + assignment_state: None, beneficiary: beneficiary.into(), - owned_state: InvoiceState::Void, expiry: None, unknown_query: none!(), }) @@ -50,36 +49,28 @@ impl RgbInvoiceBuilder { Self::new(beneficiary).set_contract(contract_id) } - pub fn rgb20(contract_id: ContractId, beneficiary: impl Into>) -> Self { - Self::with(contract_id, beneficiary).set_interface("RGB20") - } - - pub fn rgb20_anything(beneficiary: impl Into>) -> Self { - Self::new(beneficiary).set_interface("RGB20") - } - pub fn set_contract(mut self, contract_id: ContractId) -> Self { self.0.contract = Some(contract_id); self } - pub fn set_interface(mut self, name: impl Into) -> Self { - self.0.iface = Some(name.into()); + pub fn set_schema(mut self, schema_id: SchemaId) -> Self { + self.0.schema = Some(schema_id); self } - pub fn set_operation(mut self, name: impl Into) -> Self { - self.0.operation = Some(name.into()); + pub fn set_assignment_name(mut self, assignment_name: impl Into) -> Self { + self.0.assignment_name = Some(assignment_name.into()); self } - pub fn set_assignment(mut self, name: impl Into) -> Self { - self.0.assignment = Some(name.into()); + pub fn set_void(mut self) -> Self { + self.0.assignment_state = Some(InvoiceState::Void); self } pub fn set_amount_raw(mut self, amount: impl Into) -> Self { - self.0.owned_state = InvoiceState::Amount(amount.into()); + self.0.assignment_state = Some(InvoiceState::Amount(amount.into())); self } @@ -94,12 +85,13 @@ impl RgbInvoiceBuilder { Err(_) => return Err(self), } .to_amount_unchecked(); - self.0.owned_state = InvoiceState::Amount(amount); + self.0.assignment_state = Some(InvoiceState::Amount(amount)); Ok(self) } pub fn set_allocation_raw(mut self, allocation: impl Into) -> Self { - self.0.owned_state = InvoiceState::Data(NonFungible::RGB21(allocation.into())); + self.0.assignment_state = + Some(InvoiceState::Data(NonFungible::FractionedToken(allocation.into()))); self } diff --git a/invoice/src/data.rs b/invoice/src/data.rs index 7eaa93f7..0022efc8 100644 --- a/invoice/src/data.rs +++ b/invoice/src/data.rs @@ -21,7 +21,7 @@ use std::str::FromStr; -use rgb::{DataState, RevealedData}; +use rgb::RevealedData; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use strict_encoding::{StrictDeserialize, StrictSerialize}; @@ -37,14 +37,14 @@ use crate::LIB_NAME_RGB_CONTRACT; )] pub enum NonFungible { #[display(inner)] - RGB21(Allocation), + FractionedToken(Allocation), } impl FromStr for NonFungible { type Err = AllocationParseError; fn from_str(s: &str) -> Result { let allocation = Allocation::from_str(s)?; - Ok(NonFungible::RGB21(allocation)) + Ok(NonFungible::FractionedToken(allocation)) } } @@ -158,19 +158,13 @@ impl StrictDeserialize for Allocation {} impl From for Allocation { fn from(data: RevealedData) -> Self { - Allocation::from_strict_serialized(data.value.into()).expect("invalid allocation data") + Allocation::from_strict_serialized(data.into()).expect("invalid allocation data") } } -impl From for Allocation { - fn from(state: DataState) -> Self { - Allocation::from_strict_serialized(state.into()).expect("invalid allocation data") - } -} - -impl From for DataState { +impl From for RevealedData { fn from(allocation: Allocation) -> Self { - DataState::from( + RevealedData::from( allocation .to_strict_serialized() .expect("invalid allocation data"), diff --git a/invoice/src/invoice.rs b/invoice/src/invoice.rs index 853bd702..3cee7e28 100644 --- a/invoice/src/invoice.rs +++ b/invoice/src/invoice.rs @@ -19,15 +19,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::ops::Deref; use std::str::FromStr; use amplify::{ByteArray, Bytes32}; -use bp::seals::txout::CloseMethod; -use bp::{InvalidPubkey, OutputPk, PubkeyHash, ScriptHash, WPubkeyHash, WScriptHash}; +use bp::{InternalPk, InvalidPubkey, OutputPk, PubkeyHash, ScriptHash, WPubkeyHash, WScriptHash}; use indexmap::IndexMap; use invoice::{AddressNetwork, AddressPayload, Network}; -use rgb::{AttachId, ContractId, Layer1, SecretSeal}; -use strict_encoding::{FieldName, TypeName}; +use rgb::{ChainNet, ContractId, Layer1, SchemaId, SecretSeal, StateType}; +use strict_types::FieldName; use crate::{Amount, NonFungible}; @@ -57,8 +57,6 @@ pub enum InvoiceState { Amount(Amount), #[display(inner)] Data(NonFungible), - #[display(inner)] - Attach(AttachId), } impl FromStr for InvoiceState { @@ -70,60 +68,18 @@ impl FromStr for InvoiceState { Ok(InvoiceState::Amount(amount)) } else if let Ok(data) = NonFungible::from_str(s) { Ok(InvoiceState::Data(data)) - } else if let Ok(attach) = AttachId::from_str(s) { - Ok(InvoiceState::Attach(attach)) } else { Err(InvoiceStateError::ParseError(s.to_owned())) } } } -#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)] -#[non_exhaustive] -pub enum ChainNet { - #[display("bc")] - BitcoinMainnet, - #[display("tb")] - BitcoinTestnet, - #[display("sb")] - BitcoinSignet, - #[display("bcrt")] - BitcoinRegtest, - #[display("lq")] - LiquidMainnet, - #[display("tl")] - LiquidTestnet, -} - -impl ChainNet { - pub fn layer1(&self) -> Layer1 { - match self { - ChainNet::BitcoinMainnet - | ChainNet::BitcoinTestnet - | ChainNet::BitcoinSignet - | ChainNet::BitcoinRegtest => Layer1::Bitcoin, - ChainNet::LiquidMainnet | ChainNet::LiquidTestnet => Layer1::Liquid, - } - } - - pub fn is_prod(&self) -> bool { - match self { - ChainNet::BitcoinMainnet | ChainNet::LiquidMainnet => true, - - ChainNet::BitcoinTestnet - | ChainNet::BitcoinSignet - | ChainNet::BitcoinRegtest - | ChainNet::LiquidTestnet => false, - } - } - - pub fn address_network(&self) -> AddressNetwork { - match self { - ChainNet::BitcoinMainnet => AddressNetwork::Mainnet, - ChainNet::BitcoinTestnet | ChainNet::BitcoinSignet => AddressNetwork::Testnet, - ChainNet::BitcoinRegtest => AddressNetwork::Regtest, - ChainNet::LiquidMainnet => AddressNetwork::Mainnet, - ChainNet::LiquidTestnet => AddressNetwork::Testnet, +impl From for StateType { + fn from(val: InvoiceState) -> Self { + match val { + InvoiceState::Void => StateType::Void, + InvoiceState::Amount(_) => StateType::Fungible, + InvoiceState::Data(_) => StateType::Structured, } } } @@ -132,7 +88,8 @@ impl ChainNet { #[non_exhaustive] pub enum XChainNet { BitcoinMainnet(T), - BitcoinTestnet(T), + BitcoinTestnet3(T), + BitcoinTestnet4(T), BitcoinSignet(T), BitcoinRegtest(T), LiquidMainnet(T), @@ -143,7 +100,8 @@ impl XChainNet { pub fn with(cn: ChainNet, data: T) -> Self { match cn { ChainNet::BitcoinMainnet => XChainNet::BitcoinMainnet(data), - ChainNet::BitcoinTestnet => XChainNet::BitcoinTestnet(data), + ChainNet::BitcoinTestnet3 => XChainNet::BitcoinTestnet3(data), + ChainNet::BitcoinTestnet4 => XChainNet::BitcoinTestnet4(data), ChainNet::BitcoinSignet => XChainNet::BitcoinSignet(data), ChainNet::BitcoinRegtest => XChainNet::BitcoinRegtest(data), ChainNet::LiquidMainnet => XChainNet::LiquidMainnet(data), @@ -154,8 +112,8 @@ impl XChainNet { pub fn bitcoin(network: Network, data: T) -> Self { match network { Network::Mainnet => Self::BitcoinMainnet(data), - Network::Testnet3 => Self::BitcoinTestnet(data), - Network::Testnet4 => Self::BitcoinTestnet(data), + Network::Testnet3 => Self::BitcoinTestnet3(data), + Network::Testnet4 => Self::BitcoinTestnet4(data), Network::Signet => Self::BitcoinSignet(data), Network::Regtest => Self::BitcoinRegtest(data), } @@ -164,7 +122,8 @@ impl XChainNet { pub fn chain_network(&self) -> ChainNet { match self { XChainNet::BitcoinMainnet(_) => ChainNet::BitcoinMainnet, - XChainNet::BitcoinTestnet(_) => ChainNet::BitcoinTestnet, + XChainNet::BitcoinTestnet3(_) => ChainNet::BitcoinTestnet3, + XChainNet::BitcoinTestnet4(_) => ChainNet::BitcoinTestnet4, XChainNet::BitcoinSignet(_) => ChainNet::BitcoinSignet, XChainNet::BitcoinRegtest(_) => ChainNet::BitcoinRegtest, XChainNet::LiquidMainnet(_) => ChainNet::LiquidMainnet, @@ -175,7 +134,8 @@ impl XChainNet { pub fn into_inner(self) -> T { match self { XChainNet::BitcoinMainnet(inner) - | XChainNet::BitcoinTestnet(inner) + | XChainNet::BitcoinTestnet3(inner) + | XChainNet::BitcoinTestnet4(inner) | XChainNet::BitcoinSignet(inner) | XChainNet::BitcoinRegtest(inner) | XChainNet::LiquidMainnet(inner) @@ -184,15 +144,23 @@ impl XChainNet { } pub fn layer1(&self) -> Layer1 { self.chain_network().layer1() } - pub fn address_network(&self) -> AddressNetwork { self.chain_network().address_network() } - pub fn is_prod(&self) -> bool { self.chain_network().is_prod() } + + pub fn address_network(&self) -> AddressNetwork { + match self.chain_network() { + ChainNet::BitcoinMainnet => AddressNetwork::Mainnet, + ChainNet::BitcoinTestnet3 | ChainNet::BitcoinTestnet4 | ChainNet::BitcoinSignet => { + AddressNetwork::Testnet + } + ChainNet::BitcoinRegtest => AddressNetwork::Regtest, + ChainNet::LiquidMainnet => AddressNetwork::Mainnet, + ChainNet::LiquidTestnet => AddressNetwork::Testnet, + } + } } #[derive(Copy, Clone, Eq, PartialEq, Debug, Display, Error)] #[display(doc_comments)] pub enum Pay2VoutError { - /// invalid close method byte {0:#04x}. - InvalidMethod(u8), /// unexpected address type byte {0:#04x}. InvalidAddressType(u8), /// invalid taproot output key; specifically {0}. @@ -200,9 +168,16 @@ pub enum Pay2VoutError { } #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, From)] -pub struct Pay2Vout { - pub method: CloseMethod, - pub address: AddressPayload, +pub struct Pay2Vout(AddressPayload); + +impl Pay2Vout { + pub fn new(address_payload: AddressPayload) -> Self { Pay2Vout(address_payload) } +} + +impl Deref for Pay2Vout { + type Target = AddressPayload; + + fn deref(&self) -> &'_ Self::Target { &self.0 } } impl Pay2Vout { @@ -213,24 +188,22 @@ impl Pay2Vout { pub(crate) const P2TR: u8 = 5; } -impl TryFrom<[u8; 34]> for Pay2Vout { +impl TryFrom<[u8; 33]> for Pay2Vout { type Error = Pay2VoutError; - fn try_from(data: [u8; 34]) -> Result { - let method = - CloseMethod::try_from(data[0]).map_err(|e| Pay2VoutError::InvalidMethod(e.1))?; - let address = match data[1] { - Self::P2PKH => AddressPayload::Pkh(PubkeyHash::from_slice_unsafe(&data[2..22])), - Self::P2SH => AddressPayload::Sh(ScriptHash::from_slice_unsafe(&data[2..22])), - Self::P2WPKH => AddressPayload::Wpkh(WPubkeyHash::from_slice_unsafe(&data[2..22])), - Self::P2WSH => AddressPayload::Wsh(WScriptHash::from_slice_unsafe(&data[2..])), + fn try_from(data: [u8; 33]) -> Result { + let address = match data[0] { + Self::P2PKH => AddressPayload::Pkh(PubkeyHash::from_slice_unsafe(&data[1..21])), + Self::P2SH => AddressPayload::Sh(ScriptHash::from_slice_unsafe(&data[1..21])), + Self::P2WPKH => AddressPayload::Wpkh(WPubkeyHash::from_slice_unsafe(&data[1..21])), + Self::P2WSH => AddressPayload::Wsh(WScriptHash::from_slice_unsafe(&data[1..])), Self::P2TR => AddressPayload::Tr( - OutputPk::from_byte_array(Bytes32::from_slice_unsafe(&data[2..34]).to_byte_array()) + OutputPk::from_byte_array(Bytes32::from_slice_unsafe(&data[1..33]).to_byte_array()) .map_err(Pay2VoutError::InvalidTapkey)?, ), wrong => return Err(Pay2VoutError::InvalidAddressType(wrong)), }; - Ok(Self { method, address }) + Ok(Pay2Vout(address)) } } @@ -238,8 +211,7 @@ impl TryFrom<[u8; 34]> for Pay2Vout { pub enum Beneficiary { #[from] BlindedSeal(SecretSeal), - #[from] - WitnessVout(Pay2Vout), + WitnessVout(Pay2Vout, Option), } #[derive(Clone, Eq, PartialEq, Debug)] @@ -247,11 +219,10 @@ pub enum Beneficiary { pub struct RgbInvoice { pub transports: Vec, pub contract: Option, - pub iface: Option, - pub operation: Option, - pub assignment: Option, + pub schema: Option, + pub assignment_name: Option, + pub assignment_state: Option, pub beneficiary: XChainNet, - pub owned_state: InvoiceState, /// UTC unix timestamp pub expiry: Option, pub unknown_query: IndexMap, @@ -261,5 +232,4 @@ impl RgbInvoice { pub fn chain_network(&self) -> ChainNet { self.beneficiary.chain_network() } pub fn address_network(&self) -> AddressNetwork { self.beneficiary.address_network() } pub fn layer1(&self) -> Layer1 { self.beneficiary.layer1() } - pub fn is_prod(&self) -> bool { self.beneficiary.is_prod() } } diff --git a/invoice/src/lib.rs b/invoice/src/lib.rs index a798caeb..7c8132bf 100644 --- a/invoice/src/lib.rs +++ b/invoice/src/lib.rs @@ -43,8 +43,7 @@ pub use data::{Allocation, NonFungible, OwnedFraction, TokenIndex}; pub use parse::{InvoiceParseError, TransportParseError}; pub use crate::invoice::{ - Beneficiary, ChainNet, InvoiceState, Pay2Vout, Pay2VoutError, RgbInvoice, RgbTransport, - XChainNet, + Beneficiary, InvoiceState, Pay2Vout, Pay2VoutError, RgbInvoice, RgbTransport, XChainNet, }; pub const LIB_NAME_RGB_CONTRACT: &str = "RGBContract"; diff --git a/invoice/src/parse.rs b/invoice/src/parse.rs index 08e4a37e..c4d66370 100644 --- a/invoice/src/parse.rs +++ b/invoice/src/parse.rs @@ -25,19 +25,20 @@ use std::num::ParseIntError; use std::str::FromStr; use baid64::{Baid64ParseError, DisplayBaid64, FromBaid64Str}; -use fluent_uri::enc::EStr; +use bp::InternalPk; +use fluent_uri::encoding::encoder::Query; +use fluent_uri::encoding::EStr; use fluent_uri::Uri; use indexmap::IndexMap; use invoice::{AddressPayload, UnknownNetwork}; use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS}; -use rgb::{ContractId, SecretSeal}; -use strict_encoding::{InvalidRString, TypeName}; +use rgb::{ChainNet, ContractId, SchemaId, SecretSeal}; +use strict_types::FieldName; -use crate::invoice::{ - Beneficiary, ChainNet, InvoiceState, Pay2Vout, RgbInvoice, RgbTransport, XChainNet, -}; +use crate::invoice::{Beneficiary, InvoiceState, Pay2Vout, RgbInvoice, RgbTransport, XChainNet}; const OMITTED: &str = "~"; +const ASSIGNMENT: &str = "assignment_name"; const EXPIRY: &str = "expiry"; const ENDPOINTS: &str = "endpoints"; const TRANSPORT_SEP: char = ','; @@ -68,10 +69,6 @@ pub enum TransportParseError { #[derive(Debug, Display, Error, From)] #[display(doc_comments)] pub enum InvoiceParseError { - #[from] - #[display(inner)] - Uri(fluent_uri::ParseError), - /// invalid invoice. Invalid, @@ -79,14 +76,17 @@ pub enum InvoiceParseError { /// one. Authority, - /// contract id is missed from the invoice. - ContractMissed, + /// contract id is missing from the invoice. + ContractMissing, - /// interface information is missed from the invoice. - IfaceMissed, + /// schema information is missing from the invoice. + SchemaMissing, - /// assignment data is missed from the invoice. - AssignmentMissed, + /// assignment state is missing from the invoice. + AssignmentStateMissing, + + /// beneficiary is missing from the invoice. + BeneficiaryMissing, /// invalid invoice scheme {0}. InvalidScheme(String), @@ -94,14 +94,17 @@ pub enum InvoiceParseError { /// no invoice transport has been provided. NoTransport, - /// invalid invoice: contract ID present but no contract interface provided. - ContractIdNoIface, - /// invalid contract ID. InvalidContractId(String), - /// invalid interface {0}. - InvalidIface(String), + /// invalid schema {0}. + InvalidSchemaId(String), + + /// invalid assignment state {0}. + InvalidAssignmentState(String), + + /// invalid assignment name {0}. + InvalidAssignmentName(String), /// invalid expiration timestamp {0}. InvalidExpiration(String), @@ -113,10 +116,6 @@ pub enum InvoiceParseError { /// invalid query parameter {0}. InvalidQueryParam(String), - #[from] - #[display(inner)] - Id(baid64::Baid64ParseError), - /// can't recognize beneficiary "{0}": it should be either a bitcoin address /// or a blinded UTXO seal. Beneficiary(String), @@ -125,24 +124,23 @@ pub enum InvoiceParseError { #[display(inner)] Num(ParseIntError), - /// can't recognize amount "{0}": it should be valid rgb21 allocation - /// data. + /// can't recognize amount "{0}": it should be valid allocation data. Data(String), - - #[from] - /// invalid interface name. - IfaceName(InvalidRString), } impl RgbInvoice { fn has_params(&self) -> bool { self.expiry.is_some() + || self.assignment_name.is_some() || self.transports != vec![RgbTransport::UnspecifiedMeans] || !self.unknown_query.is_empty() } fn query_params(&self) -> IndexMap { let mut query_params: IndexMap = IndexMap::new(); + if let Some(ref assignment) = self.assignment_name { + query_params.insert(ASSIGNMENT.to_string(), assignment.to_string()); + } if let Some(expiry) = self.expiry { query_params.insert(EXPIRY.to_string(), expiry.to_string()); } @@ -211,43 +209,33 @@ impl FromStr for RgbTransport { impl Display for XChainNet { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "{}:", self.chain_network())?; + write!(f, "{}:", self.chain_network().prefix())?; match self.into_inner() { Beneficiary::BlindedSeal(seal) => Display::fmt(&seal, f), - Beneficiary::WitnessVout(payload) => payload.fmt_baid64(f), - } - } -} - -impl FromStr for ChainNet { - type Err = InvoiceParseError; - - fn from_str(s: &str) -> Result { - match s.to_lowercase() { - x if ChainNet::BitcoinMainnet.to_string() == x => Ok(ChainNet::BitcoinMainnet), - x if ChainNet::BitcoinTestnet.to_string() == x => Ok(ChainNet::BitcoinTestnet), - x if ChainNet::BitcoinSignet.to_string() == x => Ok(ChainNet::BitcoinSignet), - x if ChainNet::BitcoinRegtest.to_string() == x => Ok(ChainNet::BitcoinRegtest), - x if ChainNet::LiquidMainnet.to_string() == x => Ok(ChainNet::BitcoinMainnet), - x if ChainNet::LiquidTestnet.to_string() == x => Ok(ChainNet::LiquidTestnet), - _ => Err(InvoiceParseError::Beneficiary(s.to_owned())), + Beneficiary::WitnessVout(pay2vout, internal_pk) => { + write!( + f, + "{}{}", + pay2vout.to_baid64_string(), + if let Some(ipk) = internal_pk { format!("+{ipk}") } else { s!("") } + ) + } } } } -impl DisplayBaid64<34> for Pay2Vout { +impl DisplayBaid64<33> for Pay2Vout { const HRI: &'static str = "wvout"; const CHUNKING: bool = true; const PREFIX: bool = true; const EMBED_CHECKSUM: bool = true; const MNEMONIC: bool = false; - fn to_baid64_payload(&self) -> [u8; 34] { - let mut payload = [0u8; 34]; + fn to_baid64_payload(&self) -> [u8; 33] { + let mut payload = [0u8; 33]; // tmp stack array to store the tr payload to resolve lifetime issue let schnorr_pk: [u8; 32]; - payload[0] = self.method as u8; - let (addr_type, spk) = match &self.address { + let (addr_type, spk) = match &**self { AddressPayload::Pkh(pkh) => (Self::P2PKH, pkh.as_ref()), AddressPayload::Sh(sh) => (Self::P2SH, sh.as_ref()), AddressPayload::Wpkh(wpkh) => (Self::P2WPKH, wpkh.as_ref()), @@ -257,8 +245,8 @@ impl DisplayBaid64<34> for Pay2Vout { (Self::P2TR, &schnorr_pk[..]) } }; - payload[1] = addr_type; - Cursor::new(&mut payload[2..]) + payload[0] = addr_type; + Cursor::new(&mut payload[1..]) .write_all(spk) .expect("address payload always less than 32 bytes"); payload @@ -268,7 +256,7 @@ impl DisplayBaid64<34> for Pay2Vout { impl Display for Pay2Vout { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { self.fmt_baid64(f) } } -impl FromBaid64Str<34> for Pay2Vout {} +impl FromBaid64Str<33> for Pay2Vout {} impl FromStr for Pay2Vout { type Err = Baid64ParseError; fn from_str(s: &str) -> Result { Self::from_baid64_str(s) } @@ -279,21 +267,41 @@ impl FromStr for XChainNet { fn from_str(s: &str) -> Result { let Some((cn, beneficiary)) = s.split_once(':') else { - return Err(InvoiceParseError::Beneficiary(s.to_owned())); + return Err(InvoiceParseError::Beneficiary(s!("missing beneficiary HRI"))); }; - let cn = ChainNet::from_str(cn)?; + let cn = + ChainNet::from_str(cn).map_err(|e| InvoiceParseError::Beneficiary(e.to_string()))?; if let Ok(seal) = SecretSeal::from_str(beneficiary) { return Ok(XChainNet::with(cn, Beneficiary::BlindedSeal(seal))); } - let payload = Pay2Vout::from_str(beneficiary)?; - Ok(XChainNet::with(cn, Beneficiary::WitnessVout(payload))) + let (pay2vout, internal_pk) = beneficiary + .split_once("+") + .map(|(p, i)| (p, Some(i))) + .unwrap_or((beneficiary, None)); + + let pay2vout = Pay2Vout::from_str(pay2vout) + .map_err(|e| InvoiceParseError::Beneficiary(e.to_string()))?; + + let internal_pk = match internal_pk { + None => None, + Some(i) => { + if i.is_empty() { + return Err(InvoiceParseError::Beneficiary(s!("missing internal pk"))); + } + Some( + InternalPk::from_str(i) + .map_err(|_| InvoiceParseError::Beneficiary(s!("invalid internal pk")))?, + ) + } + }; + + Ok(XChainNet::with(cn, Beneficiary::WitnessVout(pay2vout, internal_pk))) } } impl Display for RgbInvoice { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let amt = self.owned_state.to_string(); if let Some(contract) = self.contract { let id = if f.alternate() { contract.to_string().replace('-', "") @@ -304,19 +312,17 @@ impl Display for RgbInvoice { } else { write!(f, "rgb:{OMITTED}/")?; } - if let Some(iface) = self.iface.clone() { - write!(f, "{iface}/")?; + if let Some(schema) = self.schema { + let schema_str = format!("{schema:-#}"); + let id = if f.alternate() { schema_str.replace('-', "") } else { schema_str }; + write!(f, "{id}/")?; } else { write!(f, "{OMITTED}/")?; } - if let Some(ref op) = self.operation { - write!(f, "{op}/")?; - } - if let Some(ref assignment_name) = self.assignment { - write!(f, "{assignment_name}/")?; - } - if !amt.is_empty() { - write!(f, "{amt}+")?; + if let Some(ref assignment_state) = self.assignment_state { + write!(f, "{assignment_state}/")?; + } else { + write!(f, "{OMITTED}/")?; } let beneficiary = if f.alternate() { self.beneficiary.to_string().replace('-', "") @@ -352,9 +358,9 @@ impl FromStr for RgbInvoice { type Err = InvoiceParseError; fn from_str(s: &str) -> Result { - let uri = Uri::parse(s)?; + let uri = Uri::parse(s).map_err(|_| InvoiceParseError::Invalid)?; - let scheme = uri.scheme().ok_or(InvoiceParseError::Invalid)?; + let scheme = uri.scheme(); if scheme.as_str() != "rgb" { return Err(InvoiceParseError::InvalidScheme(scheme.to_string())); } @@ -364,10 +370,10 @@ impl FromStr for RgbInvoice { return Err(InvoiceParseError::Authority); } - let mut path = path.segments(); + let mut path = path.split('/'); let Some(contract_id_str) = path.next() else { - return Err(InvoiceParseError::ContractMissed); + return Err(InvoiceParseError::ContractMissing); }; let contract = match ContractId::from_str(contract_id_str.as_str()) { Ok(cid) => Some(cid), @@ -377,37 +383,30 @@ impl FromStr for RgbInvoice { } }; - let Some(iface_str) = path.next() else { - return Err(InvoiceParseError::IfaceMissed); + let Some(schema_str) = path.next() else { + return Err(InvoiceParseError::SchemaMissing); }; - let iface = match TypeName::try_from(iface_str.to_string()) { + let schema = match SchemaId::from_str(schema_str.as_ref()) { Ok(i) => Some(i), - Err(_) if iface_str.as_str() == OMITTED => None, - Err(_) => return Err(InvoiceParseError::InvalidIface(iface_str.to_string())), + Err(_) if schema_str.as_str() == OMITTED => None, + Err(_) => return Err(InvoiceParseError::InvalidSchemaId(schema_str.to_string())), }; - if contract.is_some() && iface.is_none() { - return Err(InvoiceParseError::ContractIdNoIface); - } - let Some(assignment) = path.next() else { - return Err(InvoiceParseError::AssignmentMissed); + let Some(assignment_str) = path.next() else { + return Err(InvoiceParseError::AssignmentStateMissing); }; - let (amount, beneficiary) = assignment - .as_str() - .split_once('+') - .map(|(a, b)| (Some(a), Some(b))) - .unwrap_or((Some(assignment.as_str()), None)); - // TODO: support other state types - let (beneficiary_str, value) = match (beneficiary, amount) { - (Some(b), Some(a)) => ( - b, - InvoiceState::from_str(a).map_err(|_| InvoiceParseError::Data(a.to_string()))?, - ), - (None, Some(b)) => (b, InvoiceState::Void), - _ => unreachable!(), + let assignment_state = match InvoiceState::from_str(assignment_str.as_ref()) { + Ok(i) => Some(i), + Err(_) if assignment_str.as_str() == OMITTED => None, + Err(_) => { + return Err(InvoiceParseError::InvalidAssignmentState(assignment_str.to_string())) + } }; - let beneficiary = XChainNet::::from_str(beneficiary_str)?; + let Some(beneficiary_str) = path.next() else { + return Err(InvoiceParseError::BeneficiaryMissing); + }; + let beneficiary = XChainNet::::from_str(beneficiary_str.as_ref())?; let mut query_params = map_query_params(&uri)?; let transports = if let Some(endpoints) = query_params.shift_remove(ENDPOINTS) { @@ -424,6 +423,13 @@ impl FromStr for RgbInvoice { vec![RgbTransport::UnspecifiedMeans] }; + let mut assignment_name = None; + if let Some(assignment) = query_params.shift_remove(ASSIGNMENT) { + let name = FieldName::try_from(assignment.clone()) + .map_err(|_| InvoiceParseError::InvalidAssignmentName(assignment))?; + assignment_name = Some(name); + } + let mut expiry = None; if let Some(exp) = query_params.shift_remove(EXPIRY) { let timestamp = exp @@ -435,18 +441,17 @@ impl FromStr for RgbInvoice { Ok(RgbInvoice { transports, contract, - iface, - operation: None, - assignment: None, + schema, + assignment_name, beneficiary, - owned_state: value, + assignment_state, expiry, unknown_query: query_params, }) } } -fn percent_decode(estr: &EStr) -> Result { +fn percent_decode(estr: &EStr) -> Result { Ok(estr .decode() .into_string() @@ -472,106 +477,160 @@ fn map_query_params(uri: &Uri<&str>) -> Result, Invoice #[cfg(test)] mod test { use super::*; - use crate::Amount; + use crate::{Allocation, Amount, NonFungible}; #[test] fn parse() { - // rgb20/rgb25 parameters - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/BF+bc:utxob:\ - zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F"; + // nia parameters + let invoice_str = "rgb:eIbQx5Am-XRDjj01-RM~5eo7-rv2nluD-OnBJRAy-S9~Yfts/\ + XvmU3d4_nQQ8S7oagbXi07x5vjMm7P~ERukQNX6SC4M/BF/bc:utxob:\ + 4vm1CX2Z-K8hMo59-e7dgGBS-Jka7mYn-Xe~yP85-yUiHHxr-aVlYa"; let invoice = RgbInvoice::from_str(invoice_str).unwrap(); - assert_eq!(invoice.owned_state, InvoiceState::Amount(Amount::from(100u64))); + assert_eq!(invoice.assignment_state, Some(InvoiceState::Amount(Amount::from(100u64)))); assert_eq!(invoice.to_string(), invoice_str); assert_eq!(format!("{invoice:#}"), invoice_str.replace('-', "")); - // rgb21 parameters - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB21/1@1+bc:\ - utxob:zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F"; + // uda parameters + let invoice_str = "rgb:tx8NOyGe-NkPZex~-U0J_1om-CfrOeoO-7di9xZb-vT3nxyo/\ + XvmU3d4_nQQ8S7oagbXi07x5vjMm7P~ERukQNX6SC4M/1@0/bc:utxob:\ + 4vm1CX2Z-K8hMo59-e7dgGBS-Jka7mYn-Xe~yP85-yUiHHxr-aVlYa"; let invoice = RgbInvoice::from_str(invoice_str).unwrap(); + assert_eq!( + invoice.assignment_state, + Some(InvoiceState::Data(NonFungible::FractionedToken(Allocation::with(0, 1)))) + ); assert_eq!(invoice.to_string(), invoice_str); assert_eq!(format!("{invoice:#}"), invoice_str.replace('-', "")); + // witness vout without internal pk + let invoice_str = "rgb:eIbQx5Am-XRDjj01-RM~5eo7-rv2nluD-OnBJRAy-S9~Yfts/\ + XvmU3d4_nQQ8S7oagbXi07x5vjMm7P~ERukQNX6SC4M/Sa/bc:wvout:\ + A8cJ7Ww3-NIzADo3-Tzp_5aD-7CTBWmA-AAAAAAA-AAAAAAA-ALSQkcw"; + let invoice = RgbInvoice::from_str(invoice_str).unwrap(); + assert_eq!(invoice.to_string(), invoice_str); + + // witness vout with internal pk + let invoice_str = "rgb:eIbQx5Am-XRDjj01-RM~5eo7-rv2nluD-OnBJRAy-S9~Yfts/\ + XvmU3d4_nQQ8S7oagbXi07x5vjMm7P~ERukQNX6SC4M/Sa/bc:wvout:\ + A8cJ7Ww3-NIzADo3-Tzp_5aD-7CTBWmA-AAAAAAA-AAAAAAA-ALSQkcw\ + +750f58bcca0fdb11891e7979d829b8c56e0963dba08c44f54a256cf7dbc09caf"; + let invoice = RgbInvoice::from_str(invoice_str).unwrap(); + assert_eq!(invoice.to_string(), invoice_str); + // no amount - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/bc:utxob:\ - zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F"; + let invoice_str = "rgb:eIbQx5Am-XRDjj01-RM~5eo7-rv2nluD-OnBJRAy-S9~Yfts/\ + XvmU3d4_nQQ8S7oagbXi07x5vjMm7P~ERukQNX6SC4M/~/bc:utxob:\ + 4vm1CX2Z-K8hMo59-e7dgGBS-Jka7mYn-Xe~yP85-yUiHHxr-aVlYa"; let invoice = RgbInvoice::from_str(invoice_str).unwrap(); + assert_eq!(invoice.assignment_state, None); assert_eq!(invoice.to_string(), invoice_str); // no allocation - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB21/bc:utxob:\ - zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F"; + let invoice_str = "rgb:eIbQx5Am-XRDjj01-RM~5eo7-rv2nluD-OnBJRAy-S9~Yfts/\ + V8ujLLtH2k2QSmaDpZI3o06ACIm2UNT0TZl11FiqRuY/~/bc:utxob:\ + 4vm1CX2Z-K8hMo59-e7dgGBS-Jka7mYn-Xe~yP85-yUiHHxr-aVlYa"; let invoice = RgbInvoice::from_str(invoice_str).unwrap(); + assert_eq!(invoice.assignment_state, None); assert_eq!(invoice.to_string(), invoice_str); // no contract ID - let invoice_str = - "rgb:~/RGB20/bc:utxob:zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F"; + let invoice_str = "rgb:~/XvmU3d4_nQQ8S7oagbXi07x5vjMm7P~ERukQNX6SC4M/~/bc:utxob:\ + 4vm1CX2Z-K8hMo59-e7dgGBS-Jka7mYn-Xe~yP85-yUiHHxr-aVlYa"; let invoice = RgbInvoice::from_str(invoice_str).unwrap(); assert_eq!(invoice.to_string(), invoice_str); - // no contract ID nor iface - let invoice_str = "rgb:~/~/bc:utxob:zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F"; + // no contract ID nor schema + let invoice_str = + "rgb:~/~/~/bc:utxob:4vm1CX2Z-K8hMo59-e7dgGBS-Jka7mYn-Xe~yP85-yUiHHxr-aVlYa"; let invoice = RgbInvoice::from_str(invoice_str).unwrap(); assert_eq!(invoice.to_string(), invoice_str); - // contract ID provided but no iface - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/~/bc:utxob:\ - zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F"; - let result = RgbInvoice::from_str(invoice_str); - assert!(matches!(result, Err(InvoiceParseError::ContractIdNoIface))); + // contract ID provided but no schema + let invoice_str = "rgb:eIbQx5Am-XRDjj01-RM~5eo7-rv2nluD-OnBJRAy-S9~Yfts/~/~/bc:utxob:\ + 4vm1CX2Z-K8hMo59-e7dgGBS-Jka7mYn-Xe~yP85-yUiHHxr-aVlYa"; + let invoice = RgbInvoice::from_str(invoice_str).unwrap(); + assert_eq!(invoice.to_string(), invoice_str); // invalid contract ID let invalid_contract_id = "invalid"; let invoice_str = format!( - "rgb:{invalid_contract_id}/RGB20/bc:utxob:\ - zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F" + "rgb:{invalid_contract_id}/XvmU3d4_nQQ8S7oagbXi07x5vjMm7P~ERukQNX6SC4M/bc:utxob:\ + 4vm1CX2Z-K8hMo59-e7dgGBS-Jka7mYn-Xe~yP85-yUiHHxr-aVlYa" ); let result = RgbInvoice::from_str(&invoice_str); assert!(matches!(result, Err(InvoiceParseError::InvalidContractId(c)) if c == invalid_contract_id)); + // with assignment name + let assignment_name = "assetOwner"; + let invoice_str = format!( + "rgb:3NoxsLum-cRPebTV-gTZY8qY-KS20lx7-OqgtBls-t7muan4/\ + XvmU3d4_nQQ8S7oagbXi07x5vjMm7P~ERukQNX6SC4M/BF/bc:utxob:\ + 4vm1CX2Z-K8hMo59-e7dgGBS-Jka7mYn-Xe~yP85-yUiHHxr-aVlYa?{ASSIGNMENT}={assignment_name}" + ); + let invoice = RgbInvoice::from_str(&invoice_str).unwrap(); + assert_eq!(invoice.assignment_name, Some(FieldName::from(assignment_name))); + assert_eq!(invoice.to_string(), invoice_str); + + // bad assignment_name + let assignment_name = ""; + let invoice_str = format!( + "rgb:3NoxsLum-cRPebTV-gTZY8qY-KS20lx7-OqgtBls-t7muan4/\ + XvmU3d4_nQQ8S7oagbXi07x5vjMm7P~ERukQNX6SC4M/BF/bc:utxob:\ + 4vm1CX2Z-K8hMo59-e7dgGBS-Jka7mYn-Xe~yP85-yUiHHxr-aVlYa?{ASSIGNMENT}={assignment_name}" + ); + let result = RgbInvoice::from_str(&invoice_str); + assert!(matches!(result, Err(InvoiceParseError::InvalidAssignmentName(_)))); + // with expiration - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/BF+bc:utxob:\ - zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?\ + let invoice_str = "rgb:3NoxsLum-cRPebTV-gTZY8qY-KS20lx7-OqgtBls-t7muan4/\ + XvmU3d4_nQQ8S7oagbXi07x5vjMm7P~ERukQNX6SC4M/BF/bc:utxob:\ + 4vm1CX2Z-K8hMo59-e7dgGBS-Jka7mYn-Xe~yP85-yUiHHxr-aVlYa?\ expiry=1682086371"; let invoice = RgbInvoice::from_str(invoice_str).unwrap(); assert_eq!(invoice.to_string(), invoice_str); // bad expiration - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/BF+bc:utxob:\ - zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?expiry=six"; + let invoice_str = "rgb:3NoxsLum-cRPebTV-gTZY8qY-KS20lx7-OqgtBls-t7muan4/\ + XvmU3d4_nQQ8S7oagbXi07x5vjMm7P~ERukQNX6SC4M/BF/bc:utxob:\ + 4vm1CX2Z-K8hMo59-e7dgGBS-Jka7mYn-Xe~yP85-yUiHHxr-aVlYa?expiry=six"; let result = RgbInvoice::from_str(invoice_str); assert!(matches!(result, Err(InvoiceParseError::InvalidExpiration(_)))); // with bad query parameter - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/BF+bc:utxob:\ - zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?expiry"; + let invoice_str = "rgb:3NoxsLum-cRPebTV-gTZY8qY-KS20lx7-OqgtBls-t7muan4/\ + XvmU3d4_nQQ8S7oagbXi07x5vjMm7P~ERukQNX6SC4M/BF/bc:utxob:\ + 4vm1CX2Z-K8hMo59-e7dgGBS-Jka7mYn-Xe~yP85-yUiHHxr-aVlYa?expiry"; let result = RgbInvoice::from_str(invoice_str); assert!(matches!(result, Err(InvoiceParseError::InvalidQueryParam(_)))); // with an unknown query parameter - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/BF+bc:utxob:\ - zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?unknown=new"; + let invoice_str = "rgb:3NoxsLum-cRPebTV-gTZY8qY-KS20lx7-OqgtBls-t7muan4/\ + XvmU3d4_nQQ8S7oagbXi07x5vjMm7P~ERukQNX6SC4M/BF/bc:utxob:\ + 4vm1CX2Z-K8hMo59-e7dgGBS-Jka7mYn-Xe~yP85-yUiHHxr-aVlYa?unknown=new"; let invoice = RgbInvoice::from_str(invoice_str).unwrap(); assert_eq!(invoice.to_string(), invoice_str); // with two unknown query parameters - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/BF+bc:utxob:\ - zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?unknown=new&\ + let invoice_str = "rgb:3NoxsLum-cRPebTV-gTZY8qY-KS20lx7-OqgtBls-t7muan4/\ + XvmU3d4_nQQ8S7oagbXi07x5vjMm7P~ERukQNX6SC4M/BF/bc:utxob:\ + 4vm1CX2Z-K8hMo59-e7dgGBS-Jka7mYn-Xe~yP85-yUiHHxr-aVlYa?unknown=new&\ another=new"; let invoice = RgbInvoice::from_str(invoice_str).unwrap(); assert_eq!(invoice.to_string(), invoice_str); // with expiration and an unknown query parameter - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/BF+bc:utxob:\ - zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?\ + let invoice_str = "rgb:3NoxsLum-cRPebTV-gTZY8qY-KS20lx7-OqgtBls-t7muan4/\ + XvmU3d4_nQQ8S7oagbXi07x5vjMm7P~ERukQNX6SC4M/BF/bc:utxob:\ + 4vm1CX2Z-K8hMo59-e7dgGBS-Jka7mYn-Xe~yP85-yUiHHxr-aVlYa?\ expiry=1682086371&unknown=new"; let invoice = RgbInvoice::from_str(invoice_str).unwrap(); assert_eq!(invoice.to_string(), invoice_str); // with an unknown query parameter containing percent-encoded text - let invoice_base = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/BF+bc:\ - utxob:zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?"; + let invoice_base = "rgb:3NoxsLum-cRPebTV-gTZY8qY-KS20lx7-OqgtBls-t7muan4/\ + XvmU3d4_nQQ8S7oagbXi07x5vjMm7P~ERukQNX6SC4M/BF/bc:utxob:\ + 4vm1CX2Z-K8hMo59-e7dgGBS-Jka7mYn-Xe~yP85-yUiHHxr-aVlYa?"; let query_key_encoded = ":@-%20%23"; let query_key_decoded = ":@- #"; let query_val_encoded = "?/.%26%3D"; @@ -587,39 +646,43 @@ mod test { ); // no scheme - let invoice_str = "2WBcas9-yjzEvGufY-9GEgnyMj7-beMNMWA8r-sPHtV1nPU-TMsGMQX/~/bc:utxob:\ - zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F"; + let invoice_str = "eIbQx5Am-XRDjj01-RM~5eo7-rv2nluD-OnBJRAy-S9~Yfts/~/bc:utxob:\ + 4vm1CX2Z-K8hMo59-e7dgGBS-Jka7mYn-Xe~yP85-yUiHHxr-aVlYa"; let result = RgbInvoice::from_str(invoice_str); assert!(matches!(result, Err(InvoiceParseError::Invalid))); // invalid scheme let invoice_str = "bad:2WBcas9-yjzEvGufY-9GEgnyMj7-beMNMWA8r-sPHtV1nPU-TMsGMQX/~/bc:utxob:\ - zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F"; + 4vm1CX2Z-K8hMo59-e7dgGBS-Jka7mYn-Xe~yP85-yUiHHxr-aVlYa"; let result = RgbInvoice::from_str(invoice_str); assert!(matches!(result, Err(InvoiceParseError::InvalidScheme(_)))); // empty transport endpoint specification - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/BF+bc:utxob:\ - zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?endpoints="; + let invoice_str = "rgb:3NoxsLum-cRPebTV-gTZY8qY-KS20lx7-OqgtBls-t7muan4/\ + XvmU3d4_nQQ8S7oagbXi07x5vjMm7P~ERukQNX6SC4M/BF/bc:utxob:\ + 4vm1CX2Z-K8hMo59-e7dgGBS-Jka7mYn-Xe~yP85-yUiHHxr-aVlYa?endpoints="; let result = RgbInvoice::from_str(invoice_str); assert!(matches!(result, Err(InvoiceParseError::InvalidQueryParam(_)))); // invalid transport endpoint specification - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/BF+bc:utxob:\ - zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?endpoints=bad"; + let invoice_str = "rgb:3NoxsLum-cRPebTV-gTZY8qY-KS20lx7-OqgtBls-t7muan4/\ + XvmU3d4_nQQ8S7oagbXi07x5vjMm7P~ERukQNX6SC4M/BF/bc:utxob:\ + 4vm1CX2Z-K8hMo59-e7dgGBS-Jka7mYn-Xe~yP85-yUiHHxr-aVlYa?endpoints=bad"; let result = RgbInvoice::from_str(invoice_str); assert!(matches!(result, Err(InvoiceParseError::InvalidQueryParam(_)))); // invalid transport variant - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/BF+bc:utxob:\ - zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?endpoints=rpca:/\ + let invoice_str = "rgb:3NoxsLum-cRPebTV-gTZY8qY-KS20lx7-OqgtBls-t7muan4/\ + XvmU3d4_nQQ8S7oagbXi07x5vjMm7P~ERukQNX6SC4M/BF/bc:utxob:\ + 4vm1CX2Z-K8hMo59-e7dgGBS-Jka7mYn-Xe~yP85-yUiHHxr-aVlYa?endpoints=rpca:/\ /host.example.com"; let result = RgbInvoice::from_str(invoice_str); assert!(matches!(result, Err(InvoiceParseError::InvalidQueryParam(_)))); // rgb-rpc variant - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/BF+bc:utxob:\ - zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?endpoints=rpc://\ + let invoice_str = "rgb:3NoxsLum-cRPebTV-gTZY8qY-KS20lx7-OqgtBls-t7muan4/\ + XvmU3d4_nQQ8S7oagbXi07x5vjMm7P~ERukQNX6SC4M/BF/bc:utxob:\ + 4vm1CX2Z-K8hMo59-e7dgGBS-Jka7mYn-Xe~yP85-yUiHHxr-aVlYa?endpoints=rpc://\ host.example.com"; let invoice = RgbInvoice::from_str(invoice_str).unwrap(); assert_eq!(invoice.transports, vec![RgbTransport::JsonRpc { @@ -629,8 +692,9 @@ mod test { assert_eq!(invoice.to_string(), invoice_str); // rgb-rpc variant, host containing authentication, "-" characters and port - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/BF+bc:utxob:\ - zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?endpoints=rpcs:/\ + let invoice_str = "rgb:3NoxsLum-cRPebTV-gTZY8qY-KS20lx7-OqgtBls-t7muan4/\ + XvmU3d4_nQQ8S7oagbXi07x5vjMm7P~ERukQNX6SC4M/BF/bc:utxob:\ + 4vm1CX2Z-K8hMo59-e7dgGBS-Jka7mYn-Xe~yP85-yUiHHxr-aVlYa?endpoints=rpcs:/\ /user:pass@host-1.ex-ample.com:1234"; let invoice = RgbInvoice::from_str(invoice_str).unwrap(); assert_eq!(invoice.transports, vec![RgbTransport::JsonRpc { @@ -640,8 +704,9 @@ mod test { assert_eq!(invoice.to_string(), invoice_str); // rgb-rpc variant, IPv6 host - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/BF+bc:utxob:\ - zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?endpoints=rpcs:/\ + let invoice_str = "rgb:3NoxsLum-cRPebTV-gTZY8qY-KS20lx7-OqgtBls-t7muan4/\ + XvmU3d4_nQQ8S7oagbXi07x5vjMm7P~ERukQNX6SC4M/BF/bc:utxob:\ + 4vm1CX2Z-K8hMo59-e7dgGBS-Jka7mYn-Xe~yP85-yUiHHxr-aVlYa?endpoints=rpcs:/\ /%5B2001:db8::1%5D:1234"; let invoice = RgbInvoice::from_str(invoice_str).unwrap(); assert_eq!(invoice.transports, vec![RgbTransport::JsonRpc { @@ -651,29 +716,32 @@ mod test { assert_eq!(invoice.to_string(), invoice_str); // rgb-rpc variant with missing host - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/BF+bc:utxob:\ - zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?endpoints=rpc://"; + let invoice_str = "rgb:3NoxsLum-cRPebTV-gTZY8qY-KS20lx7-OqgtBls-t7muan4/\ + XvmU3d4_nQQ8S7oagbXi07x5vjMm7P~ERukQNX6SC4M/BF/bc:utxob:\ + 4vm1CX2Z-K8hMo59-e7dgGBS-Jka7mYn-Xe~yP85-yUiHHxr-aVlYa?endpoints=rpc://"; let result = RgbInvoice::from_str(invoice_str); assert!(matches!(result, Err(InvoiceParseError::InvalidQueryParam(_)))); // rgb-rpc variant with invalid separator - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/BF+bc:utxob:\ - zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?endpoints=rpc/\ + let invoice_str = "rgb:3NoxsLum-cRPebTV-gTZY8qY-KS20lx7-OqgtBls-t7muan4/\ + XvmU3d4_nQQ8S7oagbXi07x5vjMm7P~ERukQNX6SC4M/BF/bc:utxob:\ + 4vm1CX2Z-K8hMo59-e7dgGBS-Jka7mYn-Xe~yP85-yUiHHxr-aVlYa?endpoints=rpc/\ host.example.com"; let result = RgbInvoice::from_str(invoice_str); assert!(matches!(result, Err(InvoiceParseError::InvalidQueryParam(_)))); // rgb-rpc variant with invalid transport host specification - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/BF+bc:utxob:\ - zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?endpoints=rpc://\ + let invoice_str = "rgb:3NoxsLum-cRPebTV-gTZY8qY-KS20lx7-OqgtBls-t7muan4/\ + XvmU3d4_nQQ8S7oagbXi07x5vjMm7P~ERukQNX6SC4M/BF/bc:utxob:\ + 4vm1CX2Z-K8hMo59-e7dgGBS-Jka7mYn-Xe~yP85-yUiHHxr-aVlYa?endpoints=rpc://\ ho]t"; let result = RgbInvoice::from_str(invoice_str); - assert!(matches!(result, Err(InvoiceParseError::Uri(_)))); + assert!(matches!(result, Err(InvoiceParseError::Invalid))); // rgb+http variant let invoice_str = "rgb:\ - 11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/\ - BF+bc:utxob:zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?endpoints=https://\ + 3NoxsLum-cRPebTV-gTZY8qY-KS20lx7-OqgtBls-t7muan4/XvmU3d4_nQQ8S7oagbXi07x5vjMm7P~ERukQNX6SC4M/\ + BF/bc:utxob:4vm1CX2Z-K8hMo59-e7dgGBS-Jka7mYn-Xe~yP85-yUiHHxr-aVlYa?endpoints=https://\ host.example.com"; let invoice = RgbInvoice::from_str(invoice_str).unwrap(); let transports = vec![RgbTransport::RestHttp { @@ -684,8 +752,9 @@ mod test { assert_eq!(invoice.to_string(), invoice_str); // rgb+ws variant - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/BF+bc:utxob:\ - zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?endpoints=wss://\ + let invoice_str = "rgb:3NoxsLum-cRPebTV-gTZY8qY-KS20lx7-OqgtBls-t7muan4/\ + XvmU3d4_nQQ8S7oagbXi07x5vjMm7P~ERukQNX6SC4M/BF/bc:utxob:\ + 4vm1CX2Z-K8hMo59-e7dgGBS-Jka7mYn-Xe~yP85-yUiHHxr-aVlYa?endpoints=wss://\ host.example.com"; let invoice = RgbInvoice::from_str(invoice_str).unwrap(); let transports = vec![RgbTransport::WebSockets { @@ -695,12 +764,20 @@ mod test { assert_eq!(invoice.transports, transports); assert_eq!(invoice.to_string(), invoice_str); - // TODO: rgb+storm variant + // rgb+storm variant + let invoice_str = "rgb:3NoxsLum-cRPebTV-gTZY8qY-KS20lx7-OqgtBls-t7muan4/\ + XvmU3d4_nQQ8S7oagbXi07x5vjMm7P~ERukQNX6SC4M/BF/bc:utxob:\ + 4vm1CX2Z-K8hMo59-e7dgGBS-Jka7mYn-Xe~yP85-yUiHHxr-aVlYa?endpoints=storm:\ + //_/"; + let invoice = RgbInvoice::from_str(invoice_str).unwrap(); + let transports = vec![RgbTransport::Storm {}]; + assert_eq!(invoice.transports, transports); + assert_eq!(invoice.to_string(), invoice_str); // multiple transports let invoice_str = "rgb:\ - 11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/\ - BF+bc:utxob:zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?endpoints=rpcs://\ + 3NoxsLum-cRPebTV-gTZY8qY-KS20lx7-OqgtBls-t7muan4/XvmU3d4_nQQ8S7oagbXi07x5vjMm7P~ERukQNX6SC4M/\ + BF/bc:utxob:4vm1CX2Z-K8hMo59-e7dgGBS-Jka7mYn-Xe~yP85-yUiHHxr-aVlYa?endpoints=rpcs://\ host1.example.com,http://host2.example.com,ws://host3.example.com"; let invoice = RgbInvoice::from_str(invoice_str).unwrap(); let transports = vec![ @@ -739,45 +816,66 @@ mod test { // rgb-rpc variant with invalid separator parse error let result = RgbTransport::from_str("rpc/host.example.com"); assert!(matches!(result, Err(TransportParseError::InvalidTransport(_)))); + + // invalid witness vout: invalid length of identifier wvout + let invoice_str = "rgb:3NoxsLum-cRPebTV-gTZY8qY-KS20lx7-OqgtBls-t7muan4/\ + XvmU3d4_nQQ8S7oagbXi07x5vjMm7P~ERukQNX6SC4M/BF/bc:wvout:\ + +750f58bcca0fdb11891e7979d829b8c56e0963dba08c44f54a256cf7dbc09caf"; + let result = RgbInvoice::from_str(invoice_str); + assert!(matches!(result, Err(InvoiceParseError::Beneficiary(_)))); + + // invalid witness vout: missing beneficiary HRI + let invoice_str = "rgb:3NoxsLum-cRPebTV-gTZY8qY-KS20lx7-OqgtBls-t7muan4/\ + XvmU3d4_nQQ8S7oagbXi07x5vjMm7P~ERukQNX6SC4M/BF/\ + 750f58bcca0fdb11891e7979d829b8c56e0963dba08c44f54a256cf7dbc09caf"; + let result = RgbInvoice::from_str(invoice_str); + assert!(matches!(result, Err(InvoiceParseError::Beneficiary(_)))); + + // invalid witness vout: invalid chain-network pair + let invoice_str = "rgb:3NoxsLum-cRPebTV-gTZY8qY-KS20lx7-OqgtBls-t7muan4/\ + XvmU3d4_nQQ8S7oagbXi07x5vjMm7P~ERukQNX6SC4M/BF/:\ + +750f58bcca0fdb11891e7979d829b8c56e0963dba08c44f54a256cf7dbc09caf"; + let result = RgbInvoice::from_str(invoice_str); + assert!(matches!(result, Err(InvoiceParseError::Beneficiary(_)))); + + // invalid witness vout: invalid internal pk + let invoice_str = "rgb:3NoxsLum-cRPebTV-gTZY8qY-KS20lx7-OqgtBls-t7muan4/\ + XvmU3d4_nQQ8S7oagbXi07x5vjMm7P~ERukQNX6SC4M/BF/bc:wvout:\ + BYWmQlmL-$i5Co3j-LtTvxSr-53!\ + Brv7-fc7ZntC-ha988ci-jqKOj4Q+750f58bcca0fdb11891e7979"; + let result = RgbInvoice::from_str(invoice_str); + assert!(matches!(result, Err(InvoiceParseError::Beneficiary(_)))); + + // invalid witness vout: missing internal pk + let invoice_str = "rgb:3NoxsLum-cRPebTV-gTZY8qY-KS20lx7-OqgtBls-t7muan4/\ + XvmU3d4_nQQ8S7oagbXi07x5vjMm7P~ERukQNX6SC4M/BF/bc:wvout:\ + BYWmQlmL-$i5Co3j-LtTvxSr-53!Brv7-fc7ZntC-ha988ci-jqKOj4Q+"; + let result = RgbInvoice::from_str(invoice_str); + assert!(matches!(result, Err(InvoiceParseError::Beneficiary(_)))); } #[test] fn pay2vout_parse() { - let p = Pay2Vout { - method: bp::dbc::Method::OpretFirst, - address: AddressPayload::Pkh([0xff; 20].into()), - }; + let p = Pay2Vout::new(AddressPayload::Pkh([0xff; 20].into())); assert_eq!(Pay2Vout::from_str(&p.to_string()).unwrap(), p); - let p = Pay2Vout { - method: bp::dbc::Method::OpretFirst, - address: AddressPayload::Sh([0xff; 20].into()), - }; + let p = Pay2Vout::new(AddressPayload::Sh([0xff; 20].into())); assert_eq!(Pay2Vout::from_str(&p.to_string()).unwrap(), p); - let p = Pay2Vout { - method: bp::dbc::Method::OpretFirst, - address: AddressPayload::Wpkh([0xff; 20].into()), - }; + let p = Pay2Vout::new(AddressPayload::Wpkh([0xff; 20].into())); assert_eq!(Pay2Vout::from_str(&p.to_string()).unwrap(), p); - let p = Pay2Vout { - method: bp::dbc::Method::OpretFirst, - address: AddressPayload::Wsh([0xff; 32].into()), - }; + let p = Pay2Vout::new(AddressPayload::Wsh([0xff; 32].into())); assert_eq!(Pay2Vout::from_str(&p.to_string()).unwrap(), p); - let p = Pay2Vout { - method: bp::dbc::Method::OpretFirst, - address: AddressPayload::Tr( - bp::OutputPk::from_byte_array([ - 0x85, 0xa6, 0x42, 0x59, 0x8b, 0xfe, 0x2e, 0x42, 0xa3, 0x78, 0xcb, 0xb5, 0x3b, - 0xf1, 0x4a, 0xbe, 0x77, 0xf8, 0x1a, 0xef, 0xed, 0xf7, 0x3b, 0x66, 0x7b, 0x42, - 0x85, 0xaf, 0x7c, 0xf1, 0xc8, 0xa3, - ]) - .unwrap(), - ), - }; + let p = Pay2Vout::new(AddressPayload::Tr( + bp::OutputPk::from_byte_array([ + 0x85, 0xa6, 0x42, 0x59, 0x8b, 0xfe, 0x2e, 0x42, 0xa3, 0x78, 0xcb, 0xb5, 0x3b, 0xf1, + 0x4a, 0xbe, 0x77, 0xf8, 0x1a, 0xef, 0xed, 0xf7, 0x3b, 0x66, 0x7b, 0x42, 0x85, 0xaf, + 0x7c, 0xf1, 0xc8, 0xa3, + ]) + .unwrap(), + )); assert_eq!(Pay2Vout::from_str(&p.to_string()).unwrap(), p); } } diff --git a/src/containers/anchors.rs b/src/containers/anchors.rs index d9beb866..490a68a2 100644 --- a/src/containers/anchors.rs +++ b/src/containers/anchors.rs @@ -22,17 +22,33 @@ use std::cmp::Ordering; use amplify::ByteArray; -use bp::dbc::opret::OpretProof; -use bp::dbc::tapret::TapretProof; -use bp::dbc::{anchor, Anchor}; -use bp::{Tx, Txid}; +use bp::dbc::Anchor; +use bp::{dbc, Tx, Txid}; use commit_verify::mpc; -use rgb::validation::DbcProof; -use rgb::{BundleId, DiscloseHash, TransitionBundle, XChain, XWitnessId}; +use rgb::validation::{DbcProof, EAnchor}; +use rgb::{BundleId, DiscloseHash, TransitionBundle}; use strict_encoding::StrictDumb; use crate::{MergeReveal, MergeRevealError, LIB_NAME_RGB_STD}; +/// Error merging two [`SealWitness`]es. +#[derive(Copy, Clone, Eq, PartialEq, Debug, Display, Error, From)] +#[display(doc_comments)] +pub enum SealWitnessMergeError { + /// Error merging two MPC proofs, which are unrelated. + #[display(inner)] + #[from] + MpcMismatch(mpc::MergeError), + + /// Error merging two witness proofs, which are unrelated. + #[display(inner)] + #[from] + WitnessMergeError(MergeRevealError), + + /// seal witnesses can't be merged since they have different DBC proofs. + DbcMismatch, +} + #[derive(Clone, Eq, PartialEq, Debug)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_RGB_STD)] @@ -42,47 +58,77 @@ use crate::{MergeReveal, MergeRevealError, LIB_NAME_RGB_STD}; serde(crate = "serde_crate", rename_all = "camelCase") )] pub struct SealWitness { - pub public: XPubWitness, - pub anchors: AnchorSet, + pub public: PubWitness, + pub merkle_block: mpc::MerkleBlock, + pub dbc_proof: DbcProof, } impl SealWitness { - pub fn new(witness: XPubWitness, anchors: AnchorSet) -> Self { + pub fn new(witness: PubWitness, merkle_block: mpc::MerkleBlock, dbc_proof: DbcProof) -> Self { SealWitness { public: witness, - anchors, + merkle_block, + dbc_proof, } } - pub fn witness_id(&self) -> XWitnessId { self.public.to_witness_id() } -} + pub fn witness_id(&self) -> Txid { self.public.to_witness_id() } -pub type XPubWitness = XChain; + /// Merges two [`SealWitness`]es keeping revealed data. + pub fn merge_reveal(&mut self, other: &Self) -> Result<(), SealWitnessMergeError> { + if self.dbc_proof != other.dbc_proof { + return Err(SealWitnessMergeError::DbcMismatch); + } + self.public.merge_reveal(&other.public)?; + self.merkle_block.merge_reveal(&other.merkle_block)?; + Ok(()) + } + + pub fn known_bundle_ids(&self) -> impl Iterator { + let map = self.merkle_block.to_known_message_map().release(); + map.into_values() + .map(|msg| BundleId::from_byte_array(msg.to_byte_array())) + } +} pub trait ToWitnessId { - fn to_witness_id(&self) -> XWitnessId; + fn to_witness_id(&self) -> Txid; } -impl ToWitnessId for XPubWitness { - fn to_witness_id(&self) -> XWitnessId { self.map_ref(|w| w.txid()) } +impl ToWitnessId for PubWitness { + fn to_witness_id(&self) -> Txid { self.txid() } } -impl MergeReveal for XPubWitness { - fn merge_reveal(self, other: Self) -> Result { - match (self, other) { - (XChain::Bitcoin(one), XChain::Bitcoin(two)) => { - one.merge_reveal(two).map(XChain::Bitcoin) - } - (XChain::Liquid(one), XChain::Liquid(two)) => one.merge_reveal(two).map(XChain::Liquid), - (XChain::Bitcoin(bitcoin), XChain::Liquid(liquid)) - | (XChain::Liquid(liquid), XChain::Bitcoin(bitcoin)) => { - Err(MergeRevealError::ChainMismatch { - bitcoin: bitcoin.txid(), - liquid: liquid.txid(), - }) +impl MergeReveal for PubWitness { + fn merge_reveal(&mut self, other: &Self) -> Result<(), MergeRevealError> { + if self == other { + return Ok(()); + } + if self.txid() != other.txid() { + return Err(MergeRevealError::TxidMismatch(self.txid(), other.txid())); + } + if let Self::Tx(tx2) = other { + if let Self::Tx(tx1) = self { + // Replace each input in tx1 with the one from tx2 if it has more witness or + // sig_script data + for (input1, input2) in tx1.inputs.iter_mut().zip(tx2.inputs.iter()) { + let input1_witness_len: usize = input1.witness.iter().map(|w| w.len()).sum(); + let input2_witness_len: usize = input2.witness.iter().map(|w| w.len()).sum(); + match input1_witness_len.cmp(&input2_witness_len) { + std::cmp::Ordering::Less => *input1 = input2.clone(), + std::cmp::Ordering::Equal => { + if input2.sig_script.len() > input1.sig_script.len() { + *input1 = input2.clone(); + } + } + std::cmp::Ordering::Greater => {} + } + } + } else { + *self = other.clone(); } - _ => unreachable!(), } + Ok(()) } } @@ -98,8 +144,7 @@ pub enum PubWitness { #[strict_type(tag = 0x00)] Txid(Txid), #[strict_type(tag = 0x01)] - Tx(Tx), /* TODO: Consider using `UnsignedTx` here - * TODO: Add SPV as an option here */ + Tx(Tx), } impl PartialEq for PubWitness { @@ -132,21 +177,6 @@ impl PubWitness { PubWitness::Tx(tx) => Some(tx), } } - - pub fn merge_reveal(self, other: Self) -> Result { - match (self, other) { - (Self::Txid(txid1), Self::Txid(txid2)) if txid1 == txid2 => Ok(Self::Txid(txid1)), - (Self::Txid(txid), Self::Tx(tx)) | (Self::Txid(txid), Self::Tx(tx)) - if txid == tx.txid() => - { - Ok(Self::Tx(tx)) - } - // TODO: tx1 and tx2 may differ on their witness data; take the one having most of the - // witness - (Self::Tx(tx1), Self::Tx(tx2)) if tx1.txid() == tx2.txid() => Ok(Self::Tx(tx1)), - (a, b) => Err(MergeRevealError::TxidMismatch(a.txid(), b.txid())), - } - } } #[derive(Clone, Eq, Debug)] @@ -159,110 +189,43 @@ impl PubWitness { )] #[derive(CommitEncode)] #[commit_encode(strategy = strict, id = DiscloseHash)] -pub struct WitnessBundle { - pub pub_witness: XPubWitness, - pub anchor: Anchor, +pub struct WitnessBundle { + pub pub_witness: PubWitness, + pub anchor: Anchor, pub bundle: TransitionBundle, } -impl PartialEq for WitnessBundle

{ +impl PartialEq for WitnessBundle { fn eq(&self, other: &Self) -> bool { self.pub_witness == other.pub_witness } } -impl Ord for WitnessBundle

{ +impl Ord for WitnessBundle { fn cmp(&self, other: &Self) -> Ordering { self.pub_witness.cmp(&other.pub_witness) } } -impl PartialOrd for WitnessBundle

{ +impl PartialOrd for WitnessBundle { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } -impl WitnessBundle { - pub fn witness_id(&self) -> XWitnessId { self.pub_witness.to_witness_id() } -} - -impl WitnessBundle { - pub fn merge_reveal(mut self, other: Self) -> Result { - self.pub_witness = self.pub_witness.merge_reveal(other.pub_witness)?; - if self.anchor != other.anchor { - return Err(MergeRevealError::AnchorsNonEqual(self.bundle.bundle_id())); +impl WitnessBundle +where DbcProof: From +{ + #[inline] + pub fn with(pub_witness: PubWitness, anchor: Anchor, bundle: TransitionBundle) -> Self { + Self { + pub_witness, + anchor, + bundle, } - self.bundle = self.bundle.merge_reveal(other.bundle)?; - Ok(self) - } -} - -#[derive(Clone, PartialEq, Eq, Debug)] -#[derive(StrictType, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD, tags = custom)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub enum AnchorSet { - #[strict_type(tag = 0x01)] - Tapret(Anchor), - #[strict_type(tag = 0x02)] - Opret(Anchor), - #[strict_type(tag = 0x03)] - Double { - tapret: Anchor, - opret: Anchor, - }, -} - -impl StrictDumb for AnchorSet { - fn strict_dumb() -> Self { Self::Opret(strict_dumb!()) } -} - -impl AnchorSet { - pub fn known_bundle_ids(&self) -> impl Iterator { - let map = match self { - AnchorSet::Tapret(tapret) => tapret.mpc_proof.to_known_message_map().release(), - AnchorSet::Opret(opret) => opret.mpc_proof.to_known_message_map().release(), - AnchorSet::Double { tapret, opret } => { - let mut map = tapret.mpc_proof.to_known_message_map().release(); - map.extend(opret.mpc_proof.to_known_message_map().release()); - map - } - }; - map.into_values() - .map(|msg| BundleId::from_byte_array(msg.to_byte_array())) } - pub fn has_tapret(&self) -> bool { matches!(self, Self::Tapret(_) | Self::Double { .. }) } + pub fn witness_id(&self) -> Txid { self.pub_witness.to_witness_id() } - pub fn has_opret(&self) -> bool { matches!(self, Self::Opret(_) | Self::Double { .. }) } + pub fn bundle(&self) -> &TransitionBundle { &self.bundle } - pub fn merge_reveal(self, other: Self) -> Result { - match (self, other) { - (Self::Tapret(anchor), Self::Tapret(a)) => Ok(Self::Tapret(anchor.merge_reveal(a)?)), - (Self::Opret(anchor), Self::Opret(a)) => Ok(Self::Opret(anchor.merge_reveal(a)?)), - (Self::Tapret(tapret), Self::Opret(opret)) - | (Self::Opret(opret), Self::Tapret(tapret)) => Ok(Self::Double { tapret, opret }), + pub fn bundle_mut(&mut self) -> &mut TransitionBundle { &mut self.bundle } - (Self::Double { tapret, opret }, Self::Tapret(t)) - | (Self::Tapret(t), Self::Double { tapret, opret }) => Ok(Self::Double { - tapret: tapret.merge_reveal(t)?, - opret, - }), - - (Self::Double { tapret, opret }, Self::Opret(o)) - | (Self::Opret(o), Self::Double { tapret, opret }) => Ok(Self::Double { - tapret, - opret: opret.merge_reveal(o)?, - }), - ( - Self::Double { tapret, opret }, - Self::Double { - tapret: t, - opret: o, - }, - ) => Ok(Self::Double { - tapret: tapret.merge_reveal(t)?, - opret: opret.merge_reveal(o)?, - }), - } + pub fn eanchor(&self) -> EAnchor { + EAnchor::new(self.anchor.mpc_proof.clone(), self.anchor.dbc_proof.clone().into()) } } diff --git a/src/containers/consignment.rs b/src/containers/consignment.rs index cd91798f..5d9781ce 100644 --- a/src/containers/consignment.rs +++ b/src/containers/consignment.rs @@ -22,35 +22,31 @@ use std::collections::BTreeSet; use std::fmt; use std::fmt::{Display, Formatter}; +use std::num::NonZeroU32; use std::ops::Deref; use std::str::FromStr; use aluvm::library::Lib; -use amplify::confinement::{ - Confined, LargeOrdSet, MediumBlob, SmallOrdMap, SmallOrdSet, TinyOrdMap, TinyOrdSet, -}; +use amplify::confinement::{Confined, LargeOrdSet, SmallOrdMap, SmallOrdSet}; use amplify::{ByteArray, Bytes32}; use armor::{ArmorHeader, AsciiArmor, StrictArmor, StrictArmorError}; use baid64::{Baid64ParseError, DisplayBaid64, FromBaid64Str}; use commit_verify::{CommitEncode, CommitEngine, CommitId, CommitmentId, DigestExt, Sha256}; -use rgb::validation::{ResolveWitness, Validator, Validity, Warning, CONSIGNMENT_MAX_LIBS}; +use rgb::validation::{Failure, ResolveWitness, Validator, Validity, CONSIGNMENT_MAX_LIBS}; use rgb::{ - impl_serde_baid64, validation, AttachId, BundleId, ContractId, Extension, Genesis, GraphSeal, - Operation, Schema, SchemaId, XChain, + impl_serde_baid64, validation, BundleId, ChainNet, ContractId, Genesis, GraphSeal, OpId, + Operation, Schema, SchemaId, Txid, }; use rgbcore::validation::ConsignmentApi; use strict_encoding::{StrictDeserialize, StrictDumb, StrictSerialize}; use strict_types::TypeSystem; use super::{ - ContainerVer, ContentId, ContentSigs, IndexedConsignment, Supplement, WitnessBundle, - ASCII_ARMOR_CONSIGNMENT_TYPE, ASCII_ARMOR_CONTRACT, ASCII_ARMOR_IFACE, ASCII_ARMOR_SCHEMA, - ASCII_ARMOR_TERMINAL, ASCII_ARMOR_VERSION, + ContainerVer, IndexedConsignment, SecretSeals, WitnessBundle, ASCII_ARMOR_CONSIGNMENT_TYPE, + ASCII_ARMOR_CONTRACT, ASCII_ARMOR_SCHEMA, ASCII_ARMOR_TERMINAL, ASCII_ARMOR_VERSION, }; -use crate::interface::{Iface, IfaceImpl}; use crate::persistence::{MemContract, MemContractState}; -use crate::resolvers::ConsignmentResolver; -use crate::{BundleExt, SecretSeal, LIB_NAME_RGB_STD}; +use crate::{SecretSeal, LIB_NAME_RGB_STD}; pub type Transfer = Consignment; pub type Contract = Consignment; @@ -60,7 +56,6 @@ pub trait ConsignmentExt { fn schema_id(&self) -> SchemaId; fn schema(&self) -> &Schema; fn genesis(&self) -> &Genesis; - fn extensions(&self) -> impl Iterator; fn bundled_witnesses(&self) -> impl Iterator; } @@ -77,18 +72,13 @@ impl ConsignmentExt for &C { #[inline] fn genesis(&self) -> &Genesis { (*self).genesis() } - #[inline] - fn extensions(&self) -> impl Iterator { (*self).extensions() } - #[inline] fn bundled_witnesses(&self) -> impl Iterator { (*self).bundled_witnesses() } } -/// Interface identifier. -/// -/// Interface identifier commits to all the interface data. +/// Consignment identifier. #[derive(Wrapper, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)] #[wrapper(Deref, BorrowSlice, Hex, Index, RangeOps)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] @@ -144,6 +134,8 @@ pub struct ValidConsignment { impl ValidConsignment { pub fn validation_status(&self) -> &validation::Status { &self.validation_status } + pub fn validated_opids(&self) -> &BTreeSet { &self.validation_status.validated_opids } + pub fn into_consignment(self) -> Consignment { self.consignment } pub fn into_validation_status(self) -> validation::Status { self.validation_status } @@ -186,14 +178,11 @@ pub struct Consignment { pub transfer: bool, /// Set of secret seals which are history terminals. - pub terminals: SmallOrdMap>, + pub terminals: SmallOrdMap, /// Genesis data. pub genesis: Genesis, - /// All state extensions contained in the consignment. - pub extensions: LargeOrdSet, - /// All bundled state transitions contained in the consignment, together /// with their witness data. pub bundles: LargeOrdSet, @@ -201,27 +190,11 @@ pub struct Consignment { /// Schema (plus root schema, if any) under which contract is issued. pub schema: Schema, - /// Interfaces supported by the contract. - pub ifaces: TinyOrdMap, - - /// Known supplements. - pub supplements: TinyOrdSet, - - /// Type system covering all types used in schema, interfaces and - /// implementations. + /// Type system covering all types used in schema. pub types: TypeSystem, /// Collection of scripts used across consignment. pub scripts: Confined, 0, CONSIGNMENT_MAX_LIBS>, - - /// Data containers coming with this consignment. For the purposes of - /// in-memory consignments we are restricting the size of the containers to - /// 24 bit value (RGB allows containers up to 32-bit values in size). - pub attachments: SmallOrdMap, - - /// Signatures on the pieces of content which are the part of the - /// consignment. - pub signatures: TinyOrdMap, } impl StrictSerialize for Consignment {} @@ -236,27 +209,14 @@ impl CommitEncode for Consignment { e.commit_to_serialized(&self.contract_id()); e.commit_to_serialized(&self.genesis.disclose_hash()); - e.commit_to_set(&TinyOrdSet::from_iter_checked( - self.ifaces.values().map(|iimpl| iimpl.impl_id()), - )); e.commit_to_set(&LargeOrdSet::from_iter_checked( self.bundles.iter().map(WitnessBundle::commit_id), )); - e.commit_to_set(&LargeOrdSet::from_iter_checked( - self.extensions.iter().map(Extension::disclose_hash), - )); e.commit_to_map(&self.terminals); - e.commit_to_set(&SmallOrdSet::from_iter_checked(self.attachments.keys().copied())); - e.commit_to_set(&TinyOrdSet::from_iter_checked( - self.supplements.iter().map(|suppl| suppl.suppl_id()), - )); - e.commit_to_serialized(&self.types.id()); e.commit_to_set(&SmallOrdSet::from_iter_checked(self.scripts.iter().map(|lib| lib.id()))); - - e.commit_to_map(&self.signatures); } } @@ -273,9 +233,6 @@ impl ConsignmentExt for Consignment { #[inline] fn genesis(&self) -> &Genesis { &self.genesis } - #[inline] - fn extensions(&self) -> impl Iterator { self.extensions.iter() } - #[inline] fn bundled_witnesses(&self) -> impl Iterator { self.bundles.iter() } } @@ -289,16 +246,15 @@ impl Consignment { pub fn reveal_terminal_seals( mut self, - f: impl Fn(XChain) -> Result>, E>, + f: impl Fn(SecretSeal) -> Result, E>, ) -> Result { // We need to clone since ordered set does not allow us to mutate members. let mut bundles = LargeOrdSet::with_capacity(self.bundles.len()); for mut witness_bundle in self.bundles { - for (bundle_id, secret) in &self.terminals { - if let Some(seal) = f(*secret)? { - if witness_bundle.bundle.bundle_id() == *bundle_id { - witness_bundle.bundle.reveal_seal(seal); - break; + for (bundle_id, secrets) in &self.terminals { + for secret in secrets { + if let Some(seal) = f(secret)? { + witness_bundle.bundle.reveal_seal(*bundle_id, seal); } } } @@ -313,70 +269,68 @@ impl Consignment { version: self.version, transfer: false, schema: self.schema, - ifaces: self.ifaces, - supplements: self.supplements, types: self.types, genesis: self.genesis, terminals: self.terminals, bundles: self.bundles, - extensions: self.extensions, - attachments: self.attachments, - signatures: self.signatures, scripts: self.scripts, } } + pub fn replace_transitions_input_ops(&self) -> BTreeSet { + self.bundles + .iter() + .flat_map(|b| b.bundle().known_transitions.values()) + .filter(|t| t.transition_type.is_replace()) + .flat_map(|t| t.inputs.iter()) + .filter(|i| i.ty.is_asset()) + .map(|i| i.op) + .collect::>() + } + pub fn validate( self, resolver: &impl ResolveWitness, - // TODO: Add sig validator - //_: &impl SigValidator, - testnet: bool, - ) -> Result, (validation::Status, Consignment)> { + chain_net: ChainNet, + safe_height: Option, + ) -> Result, validation::Status> { + self.validate_with_opids(resolver, chain_net, safe_height, bset![]) + } + + pub fn validate_with_opids( + self, + resolver: &impl ResolveWitness, + chain_net: ChainNet, + safe_height: Option, + trusted_op_seals: BTreeSet, + ) -> Result, validation::Status> { let index = IndexedConsignment::new(&self); - let resolver = ConsignmentResolver { - consignment: &index, - fallback: resolver, - }; let mut status = Validator::, _, _>::validate( &index, &resolver, - testnet, + chain_net, (&self.schema, self.contract_id()), + safe_height, + trusted_op_seals, ); let validity = status.validity(); if self.transfer != TRANSFER { - status.add_warning(Warning::Custom(s!("invalid consignment type"))); - } - // check ifaceid match implementation - for (iface, iimpl) in self.ifaces.iter() { - if iface.iface_id() != iimpl.iface_id { - status.add_warning(Warning::Custom(format!( - "implementation {} targets different interface {} than expected {}", - iimpl.impl_id(), - iimpl.iface_id, - iface.iface_id() - ))); - } + status.add_failure(Failure::Custom(s!("invalid consignment type"))); } // check bundle ids listed in terminals are present in the consignment for bundle_id in self.terminals.keys() { if !index.bundle_ids().any(|id| id == *bundle_id) { - status.add_warning(Warning::Custom(format!( + status.add_failure(Failure::Custom(format!( "terminal bundle id {bundle_id} is not present in the consignment" ))); } } - // TODO: check attach ids from data containers are present in operations - // TODO: validate sigs and remove untrusted - // TODO: Check that all extensions present in the consignment are used by state - // transitions - if validity != Validity::Valid { - Err((status, self)) + if validity == Validity::Invalid { + Err(status) } else { Ok(ValidConsignment { validation_status: status, @@ -384,6 +338,32 @@ impl Consignment { }) } } + + /// Modify a bundle in the consignment if it exists + pub fn modify_bundle(&mut self, witness_id: Txid, modifier: F) -> bool + where F: Fn(&mut WitnessBundle) { + let mut found = false; + let mut modified_bundles = BTreeSet::new(); + + let bundles: Vec<_> = self.bundles.iter().cloned().collect(); + + for bundle in bundles { + if bundle.witness_id() == witness_id { + let mut modified_bundle = bundle.clone(); + modifier(&mut modified_bundle); + modified_bundles.insert(modified_bundle); + found = true; + } else { + modified_bundles.insert(bundle); + } + } + + if found { + self.bundles = Confined::try_from_iter(modified_bundles).unwrap(); + } + + found + } } impl StrictArmor for Consignment { @@ -401,12 +381,6 @@ impl StrictArmor for Consignment { ArmorHeader::new(ASCII_ARMOR_CONTRACT, self.contract_id().to_string()), ArmorHeader::new(ASCII_ARMOR_SCHEMA, self.schema.schema_id().to_string()), ]; - if !self.ifaces.is_empty() { - headers.push(ArmorHeader::with( - ASCII_ARMOR_IFACE, - self.ifaces.keys().map(|iface| iface.name.to_string()), - )); - } if !self.terminals.is_empty() { headers.push(ArmorHeader::with( ASCII_ARMOR_TERMINAL, @@ -416,7 +390,7 @@ impl StrictArmor for Consignment { headers } fn parse_armor_headers(&mut self, headers: Vec) -> Result<(), StrictArmorError> { - // TODO: Check remaining headers - terminals, version, iface, contract, schema + // TODO: Check remaining headers - terminals, version, contract, schema if let Some(header) = headers .iter() .find(|header| header.title == ASCII_ARMOR_CONSIGNMENT_TYPE) @@ -560,23 +534,23 @@ Check-SHA256: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa // Wrong type // TODO: Uncomment once ASCII headers get checked /*assert!(matches!( - Transfer::from_str( - r#"-----BEGIN RGB CONSIGNMENT----- -Id: rgb:csg:9jMKgkmP-alPghZC-bu65ctP-GT5tKgM-cAbaTLT-rhu8xQo#urban-athena-adam -Version: 2 -Type: contract -Contract: rgb:T24t0N1D-eiInTgb-BXlrrXz-$7OgV6n-WJWHPUD-BWNuqZw -Schema: rgb:sch:CyqM42yAdM1moWyNZPQedAYt73BM$k9z$dKLUXY1voA#cello-global-deluxe -Check-SHA256: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - -0s#O3000000000000000000000000000000000000000000000000000000D0CRI`I$>^aZh38Qb#nj! -0000000000000000000000d59ZDjxe00000000dDb8~4rVQz13d2MfXa{vGU00000000000000000000 -0000000000000 - ------END RGB CONSIGNMENT-----"# - ), - Err(ConsignmentParseError::Type) - ));*/ + Transfer::from_str( + r#"-----BEGIN RGB CONSIGNMENT----- + Id: rgb:csg:9jMKgkmP-alPghZC-bu65ctP-GT5tKgM-cAbaTLT-rhu8xQo#urban-athena-adam + Version: 2 + Type: contract + Contract: rgb:T24t0N1D-eiInTgb-BXlrrXz-$7OgV6n-WJWHPUD-BWNuqZw + Schema: rgb:sch:CyqM42yAdM1moWyNZPQedAYt73BM$k9z$dKLUXY1voA#cello-global-deluxe + Check-SHA256: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + + 0s#O3000000000000000000000000000000000000000000000000000000D0CRI`I$>^aZh38Qb#nj! + 0000000000000000000000d59ZDjxe00000000dDb8~4rVQz13d2MfXa{vGU00000000000000000000 + 0000000000000 + + -----END RGB CONSIGNMENT-----"# + ), + Err(ConsignmentParseError::Type) + ));*/ assert!(matches!( Transfer::from_str(include_str!("../../asset/armored_contract.default")), Err(ConsignmentParseError::Type) diff --git a/src/containers/file.rs b/src/containers/file.rs index 4cd722c0..c1a9b4be 100644 --- a/src/containers/file.rs +++ b/src/containers/file.rs @@ -113,9 +113,6 @@ impl FileContent for Transfer { const MAGIC: [u8; MAGIC_LEN] = *b"TFR"; } -// TODO: Add disclosure -// TODO: Add batch and fascia - #[derive(Clone, Debug, From)] #[cfg_attr( feature = "serde", @@ -131,8 +128,6 @@ pub enum UniversalFile { #[from] Transfer(Transfer), - // TODO: Add disclosure - // TODO: Add batch and fascia } impl UniversalFile { @@ -268,41 +263,27 @@ mod test { "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA#distant-history-exotic", ) .unwrap(), - flags: Default::default(), timestamp: Default::default(), issuer: Default::default(), - testnet: Default::default(), - alt_layers1: Default::default(), - asset_tags: Default::default(), + chain_net: Default::default(), + seal_closing_strategy: Default::default(), metadata: Default::default(), globals: Default::default(), assignments: Default::default(), - valencies: Default::default(), - validator: Default::default(), }, - extensions: Default::default(), bundles: Default::default(), schema: rgb::Schema { ffv: Default::default(), - flags: Default::default(), name: strict_encoding::TypeName::from_str("Name").unwrap(), - timestamp: Default::default(), - developer: Default::default(), meta_types: Default::default(), global_types: Default::default(), owned_types: Default::default(), - valency_types: Default::default(), genesis: Default::default(), - extensions: Default::default(), transitions: Default::default(), - reserved: Default::default(), + default_assignment: Default::default(), }, - ifaces: Default::default(), - supplements: Default::default(), types: Default::default(), scripts: Default::default(), - attachments: Default::default(), - signatures: Default::default(), } } @@ -366,41 +347,27 @@ mod test { "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA#distant-history-exotic", ) .unwrap(), - flags: Default::default(), timestamp: Default::default(), issuer: Default::default(), - testnet: Default::default(), - alt_layers1: Default::default(), - asset_tags: Default::default(), + chain_net: Default::default(), + seal_closing_strategy: Default::default(), metadata: Default::default(), globals: Default::default(), assignments: Default::default(), - valencies: Default::default(), - validator: Default::default(), }, - extensions: Default::default(), bundles: Default::default(), schema: rgb::Schema { ffv: Default::default(), - flags: Default::default(), name: strict_encoding::TypeName::from_str("Name").unwrap(), - timestamp: Default::default(), - developer: Default::default(), meta_types: Default::default(), global_types: Default::default(), owned_types: Default::default(), - valency_types: Default::default(), genesis: Default::default(), - extensions: Default::default(), transitions: Default::default(), - reserved: Default::default(), + default_assignment: Default::default(), }, - ifaces: Default::default(), - supplements: Default::default(), types: Default::default(), scripts: Default::default(), - attachments: Default::default(), - signatures: Default::default(), } } diff --git a/src/containers/indexed.rs b/src/containers/indexed.rs index a8e332a5..4e6db785 100644 --- a/src/containers/indexed.rs +++ b/src/containers/indexed.rs @@ -23,12 +23,10 @@ use std::collections::{BTreeMap, BTreeSet}; use std::ops::Deref; use rgb::validation::{ConsignmentApi, EAnchor, OpRef, Scripts}; -use rgb::{ - BundleId, Extension, Genesis, OpId, Operation, Schema, Transition, TransitionBundle, XWitnessId, -}; +use rgb::{BundleId, Genesis, OpId, Operation, Schema, Transition, TransitionBundle, Txid}; use strict_types::TypeSystem; -use super::{Consignment, XPubWitness}; +use super::{Consignment, PubWitness}; use crate::containers::anchors::ToWitnessId; // TODO: Transform consignment into this type instead of composing over it @@ -36,15 +34,14 @@ use crate::containers::anchors::ToWitnessId; pub struct IndexedConsignment<'c, const TRANSFER: bool> { consignment: &'c Consignment, scripts: Scripts, - anchor_idx: BTreeMap, + anchor_idx: BTreeMap, bundle_idx: BTreeMap, - op_witness_idx: BTreeMap, + op_witness_idx: BTreeMap, op_bundle_idx: BTreeMap, - extension_idx: BTreeMap, - witness_idx: BTreeMap, + witness_idx: BTreeMap, } -impl<'c, const TRANSFER: bool> Deref for IndexedConsignment<'c, TRANSFER> { +impl Deref for IndexedConsignment<'_, TRANSFER> { type Target = Consignment; fn deref(&self) -> &Self::Target { self.consignment } @@ -56,24 +53,21 @@ impl<'c, const TRANSFER: bool> IndexedConsignment<'c, TRANSFER> { let mut bundle_idx = BTreeMap::new(); let mut op_witness_idx = BTreeMap::new(); let mut op_bundle_idx = BTreeMap::new(); - let mut extension_idx = BTreeMap::new(); let mut witness_idx = BTreeMap::new(); for witness_bundle in &consignment.bundles { witness_idx .insert(witness_bundle.pub_witness.to_witness_id(), &witness_bundle.pub_witness); - let bundle = &witness_bundle.bundle; - let bundle_id = bundle.bundle_id(); let witness_id = witness_bundle.pub_witness.to_witness_id(); + let anchor = witness_bundle.eanchor(); + let bundle = witness_bundle.bundle(); + let bundle_id = bundle.bundle_id(); bundle_idx.insert(bundle_id, bundle); - anchor_idx.insert(bundle_id, (witness_id, &witness_bundle.anchor)); - for opid in witness_bundle.bundle.known_transitions.keys() { + anchor_idx.insert(bundle_id, (witness_id, anchor)); + for opid in bundle.known_transitions.keys() { op_witness_idx.insert(*opid, witness_id); op_bundle_idx.insert(*opid, bundle_id); } } - for extension in &consignment.extensions { - extension_idx.insert(extension.id(), extension); - } let scripts = Scripts::from_iter_checked( consignment .scripts @@ -87,13 +81,10 @@ impl<'c, const TRANSFER: bool> IndexedConsignment<'c, TRANSFER> { bundle_idx, op_witness_idx, op_bundle_idx, - extension_idx, witness_idx, } } - fn extension(&self, opid: OpId) -> Option<&Extension> { self.extension_idx.get(&opid).copied() } - fn transition(&self, opid: OpId) -> Option<&Transition> { self.op_bundle_idx .get(&opid) @@ -101,12 +92,12 @@ impl<'c, const TRANSFER: bool> IndexedConsignment<'c, TRANSFER> { .and_then(|bundle| bundle.known_transitions.get(&opid)) } - pub fn pub_witness(&self, id: XWitnessId) -> Option<&XPubWitness> { + pub fn pub_witness(&self, id: Txid) -> Option<&PubWitness> { self.witness_idx.get(&id).copied() } } -impl<'c, const TRANSFER: bool> ConsignmentApi for IndexedConsignment<'c, TRANSFER> { +impl ConsignmentApi for IndexedConsignment<'_, TRANSFER> { fn schema(&self) -> &Schema { &self.schema } fn types(&self) -> &TypeSystem { &self.types } @@ -117,9 +108,7 @@ impl<'c, const TRANSFER: bool> ConsignmentApi for IndexedConsignment<'c, TRANSFE if opid == self.genesis.id() { return Some(OpRef::Genesis(&self.genesis)); } - self.transition(opid) - .map(OpRef::Transition) - .or_else(|| self.extension(opid).map(OpRef::Extension)) + self.transition(opid).map(OpRef::Transition) } fn genesis(&self) -> &Genesis { &self.genesis } @@ -136,11 +125,9 @@ impl<'c, const TRANSFER: bool> ConsignmentApi for IndexedConsignment<'c, TRANSFE self.bundle_idx.get(&bundle_id).copied() } - fn anchor(&self, bundle_id: BundleId) -> Option<(XWitnessId, &EAnchor)> { - self.anchor_idx.get(&bundle_id).map(|(id, set)| (*id, *set)) + fn anchor(&self, bundle_id: BundleId) -> Option<(Txid, &EAnchor)> { + self.anchor_idx.get(&bundle_id).map(|(id, set)| (*id, set)) } - fn op_witness_id(&self, opid: OpId) -> Option { - self.op_witness_idx.get(&opid).copied() - } + fn op_witness_id(&self, opid: OpId) -> Option { self.op_witness_idx.get(&opid).copied() } } diff --git a/src/containers/kit.rs b/src/containers/kit.rs index c764aa83..a03c19ea 100644 --- a/src/containers/kit.rs +++ b/src/containers/kit.rs @@ -25,7 +25,7 @@ use std::ops::Deref; use std::str::FromStr; use aluvm::library::Lib; -use amplify::confinement::{SmallOrdSet, TinyOrdMap, TinyOrdSet}; +use amplify::confinement::{SmallOrdSet, TinyOrdSet}; use amplify::{ByteArray, Bytes32}; use armor::{ArmorHeader, AsciiArmor, StrictArmor}; use baid64::{Baid64ParseError, DisplayBaid64, FromBaid64Str}; @@ -34,12 +34,8 @@ use rgb::{validation, Schema}; use strict_encoding::{StrictDeserialize, StrictSerialize}; use strict_types::TypeSystem; -use super::{ - ContentRef, Supplement, ASCII_ARMOR_IFACE, ASCII_ARMOR_IIMPL, ASCII_ARMOR_SCHEMA, - ASCII_ARMOR_SCRIPT, ASCII_ARMOR_TYPE_SYSTEM, ASCII_ARMOR_VERSION, -}; -use crate::containers::{ContainerVer, ContentId, ContentSigs}; -use crate::interface::{Iface, IfaceImpl}; +use super::{ASCII_ARMOR_SCHEMA, ASCII_ARMOR_SCRIPT, ASCII_ARMOR_TYPE_SYSTEM, ASCII_ARMOR_VERSION}; +use crate::containers::ContainerVer; use crate::LIB_NAME_RGB_STD; /// Kit identifier. @@ -123,23 +119,13 @@ pub struct Kit { /// Version. pub version: ContainerVer, - pub ifaces: TinyOrdSet, - pub schemata: TinyOrdSet, - pub iimpls: TinyOrdSet, - - pub supplements: TinyOrdSet, - - /// Type system covering all types used in schema, interfaces and - /// implementations. + /// Type system covering all types used in schema. pub types: TypeSystem, /// Collection of scripts used across kit data. pub scripts: SmallOrdSet, - - /// Signatures on the pieces of content which are the part of the kit. - pub signatures: TinyOrdMap, } impl StrictSerialize for Kit {} @@ -151,23 +137,12 @@ impl CommitEncode for Kit { fn commit_encode(&self, e: &mut CommitEngine) { e.commit_to_serialized(&self.version); - e.commit_to_set(&TinyOrdSet::from_iter_checked( - self.ifaces.iter().map(|iface| iface.iface_id()), - )); e.commit_to_set(&TinyOrdSet::from_iter_checked( self.schemata.iter().map(|schema| schema.schema_id()), )); - e.commit_to_set(&TinyOrdSet::from_iter_checked( - self.iimpls.iter().map(|iimpl| iimpl.impl_id()), - )); - e.commit_to_set(&TinyOrdSet::from_iter_checked( - self.supplements.iter().map(|suppl| suppl.suppl_id()), - )); e.commit_to_serialized(&self.types.id()); e.commit_to_set(&SmallOrdSet::from_iter_checked(self.scripts.iter().map(|lib| lib.id()))); - - e.commit_to_map(&self.signatures); } } @@ -175,17 +150,8 @@ impl Kit { #[inline] pub fn kit_id(&self) -> KitId { self.commit_id() } - pub fn validate( - self, - // TODO: Add sig validator - //_: &impl SigValidator, - ) -> Result { + pub fn validate(self) -> Result { let status = validation::Status::new(); - // TODO: - // - Verify integrity for each interface - // - Verify implementations against interfaces - // - Check schema integrity - // - Validate content sigs and remove untrusted ones Ok(ValidKit { validation_status: status, kit: self, @@ -205,55 +171,6 @@ impl StrictArmor for Kit { let mut header = ArmorHeader::new(ASCII_ARMOR_SCHEMA, schema.name.to_string()); let id = schema.schema_id(); header.params.push((s!("id"), format!("{id:-}"))); - header - .params - .push((s!("dev"), schema.developer.to_string())); - if let Some(suppl) = self - .supplements - .iter() - .find(|s| s.content_id == ContentRef::Schema(id)) - { - header - .params - .push((s!("suppl"), format!("{:-}", suppl.suppl_id()))); - } - headers.push(header); - } - for iface in &self.ifaces { - let mut header = ArmorHeader::new(ASCII_ARMOR_IFACE, iface.name.to_string()); - let id = iface.iface_id(); - header.params.push((s!("id"), format!("{id:-}"))); - header.params.push((s!("dev"), iface.developer.to_string())); - if let Some(suppl) = self - .supplements - .iter() - .find(|s| s.content_id == ContentRef::Iface(id)) - { - header - .params - .push((s!("suppl"), format!("{:-}", suppl.suppl_id()))); - } - headers.push(header); - } - for iimpl in &self.iimpls { - let id = iimpl.impl_id(); - let mut header = ArmorHeader::new(ASCII_ARMOR_IIMPL, format!("{id:-}")); - header - .params - .push((s!("interface"), format!("{:-}", iimpl.iface_id))); - header - .params - .push((s!("schema"), format!("{:-}", iimpl.schema_id))); - header.params.push((s!("dev"), iimpl.developer.to_string())); - if let Some(suppl) = self - .supplements - .iter() - .find(|s| s.content_id == ContentRef::IfaceImpl(id)) - { - header - .params - .push((s!("suppl"), format!("{:-}", suppl.suppl_id()))); - } headers.push(header); } headers.push(ArmorHeader::new(ASCII_ARMOR_TYPE_SYSTEM, self.types.id().to_string())); @@ -289,51 +206,45 @@ mod test { #[test] fn error_kit_strs() { - assert!( - Kit::from_str( - r#"-----BEGIN RGB KIT----- -Id: rgb:kit:e1jW6Rgc-2$JzXDg-XmR8XRJ-v!q$Dzf-yImkPjD-t8EjfvI -Version: 2 -Type-System: sts:8Vb$sM1F-5MsQc20-HEixf55-gJR37FM-0zRKfpY-SwIp35w#design-farmer-camel -Check-SHA256: 5563cc1568e244183804e0db3cec6ff9bf577f4a403924096177bf4a586160da + assert!(Kit::from_str( + r#"-----BEGIN RGB KIT----- +Id: rgb:kit:jXOeJYkD-NlOgJoP-_zT1Hvl-0fP71Zo-b2mAh2C-i5ISROo +Version: 0 +Type-System: sts:8Vb~sM1F-5MsQc20-HEixf55-gJR37FM-0zRKfpY-SwIp35w#design-farmer-camel +Check-SHA256: 837885c8f8091aeaeb9ec3c3f85a6ff470a415e610b8ba3e49f9b33c9cf9d619 -0ssI2000000000 +000000000 -----END RGB KIT-----"# - ) - .is_ok() - ); + ) + .is_ok()); // Wrong Id - assert!( - Kit::from_str( - r#"-----BEGIN RGB KIT----- + assert!(Kit::from_str( + r#"-----BEGIN RGB KIT----- Id: rgb:kit:11111111-2222222-XmR8XRJ-v!q$Dzf-yImkPjD-t8EjfvI -Version: 2 -Type-System: sts:8Vb$sM1F-5MsQc20-HEixf55-gJR37FM-0zRKfpY-SwIp35w#design-farmer-camel -Check-SHA256: 5563cc1568e244183804e0db3cec6ff9bf577f4a403924096177bf4a586160da +Version: 0 +Type-System: sts:8Vb~sM1F-5MsQc20-HEixf55-gJR37FM-0zRKfpY-SwIp35w#design-farmer-camel +Check-SHA256: 837885c8f8091aeaeb9ec3c3f85a6ff470a415e610b8ba3e49f9b33c9cf9d619 -0ssI2000000000 +000000000 -----END RGB KIT-----"# - ) - .is_err() - ); + ) + .is_err()); // wrong checksum - assert!( - Kit::from_str( - r#"-----BEGIN RGB KIT----- -Id: rgb:kit:e1jW6Rgc-2$JzXDg-XmR8XRJ-v!q$Dzf-yImkPjD-t8EjfvI -Version: 2 -Type-System: sts:8Vb$sM1F-5MsQc20-HEixf55-gJR37FM-0zRKfpY-SwIp35w#design-farmer-camel + assert!(Kit::from_str( + r#"-----BEGIN RGB KIT----- +Id: rgb:kit:jXOeJYkD-NlOgJoP-_zT1Hvl-0fP71Zo-b2mAh2C-i5ISROo +Version: 0 +Type-System: sts:8Vb~sM1F-5MsQc20-HEixf55-gJR37FM-0zRKfpY-SwIp35w#design-farmer-camel Check-SHA256: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -0ssI2000000000 +000000000 -----END RGB KIT-----"# - ) - .is_err() - ); + ) + .is_err()); } } diff --git a/src/containers/mod.rs b/src/containers/mod.rs index 277138e2..cf996322 100644 --- a/src/containers/mod.rs +++ b/src/containers/mod.rs @@ -19,56 +19,36 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! RGB containers are data packages which can be transferred between smart -//! contract users. There are two main types of containers: -//! 1. [`Consignment`]s, containing information about partial state of a *single contract*, -//! extending from its genesis up to certain contract endpoints. -//! 2. [`Disclosure`]s, containing extracts from (possibly) independent state transitions and -//! extensions under multiple contracts. Useful fro disclosing the concealed state for some other -//! parties, and also for performing "change" operations on inventory during state transfers. +//! RGB containers are data packages which can be transferred between smart contract users. +//! The main type of container is the [`Consignment`], containing information about partial state +//! of a *single contract*, extending from its genesis up to certain contract endpoints. mod seal; mod anchors; mod consignment; -mod disclosure; mod util; mod partials; mod indexed; mod file; mod kit; -mod suppl; -pub use anchors::{AnchorSet, PubWitness, SealWitness, ToWitnessId, WitnessBundle, XPubWitness}; +pub use anchors::{PubWitness, SealWitness, SealWitnessMergeError, ToWitnessId, WitnessBundle}; pub use consignment::{ Consignment, ConsignmentExt, ConsignmentId, ConsignmentParseError, Contract, Transfer, ValidConsignment, ValidContract, ValidTransfer, }; -pub use disclosure::Disclosure; pub use file::{FileContent, LoadError, UniversalFile}; pub use indexed::IndexedConsignment; pub use kit::{Kit, KitId, ValidKit}; -pub use partials::{ - Batch, BundleDichotomy, CloseMethodSet, Dichotomy, Fascia, TransitionDichotomy, TransitionInfo, - TransitionInfoError, -}; +pub use partials::{Batch, Fascia, TransitionInfo, TransitionInfoError}; pub use seal::{BuilderSeal, VoutSeal}; -pub use suppl::{ - AnnotationName, Annotations, ContentRef, SupplId, SupplItem, SupplMap, SupplSub, Supplement, - TickerSuppl, VelocityHint, SUPPL_ANNOT_IFACE_CLASS, SUPPL_ANNOT_IFACE_FEATURES, - SUPPL_ANNOT_VELOCITY, -}; -pub use util::{ - ContainerVer, ContentId, ContentSigs, DumbValidator, SigBlob, SigValidator, TrustLevel, -}; +pub use util::{ContainerVer, SecretSeals}; pub const ASCII_ARMOR_NAME: &str = "Name"; -pub const ASCII_ARMOR_IFACE: &str = "Interface"; -pub const ASCII_ARMOR_IIMPL: &str = "Implementation"; pub const ASCII_ARMOR_SCHEMA: &str = "Schema"; pub const ASCII_ARMOR_CONTRACT: &str = "Contract"; pub const ASCII_ARMOR_VERSION: &str = "Version"; pub const ASCII_ARMOR_TERMINAL: &str = "Terminal"; -pub const ASCII_ARMOR_SUPPL: &str = "Supplement"; pub const ASCII_ARMOR_SCRIPT: &str = "Alu-Lib"; pub const ASCII_ARMOR_TYPE_SYSTEM: &str = "Type-System"; pub const ASCII_ARMOR_CONSIGNMENT_TYPE: &str = "Type"; diff --git a/src/containers/partials.rs b/src/containers/partials.rs index 28e9ca61..c5a22ff8 100644 --- a/src/containers/partials.rs +++ b/src/containers/partials.rs @@ -22,95 +22,17 @@ use std::cmp::Ordering; use std::collections::BTreeSet; use std::hash::{Hash, Hasher}; -use std::ops::{BitOr, BitOrAssign}; -use std::{iter, vec}; use amplify::confinement::{Confined, NonEmptyOrdMap, U24}; -use bp::seals::txout::CloseMethod; -use rgb::{ - ContractId, OpId, Operation, Transition, TransitionBundle, TxoSeal, XOutpoint, XOutputSeal, - XWitnessId, +use bp::Outpoint; +use rgb::{ContractId, OpId, Operation, OutputSeal, Transition, TransitionBundle, Txid}; +use strict_encoding::{ + StrictDecode, StrictDeserialize, StrictDumb, StrictEncode, StrictSerialize, StrictType, }; -use strict_encoding::{StrictDecode, StrictDeserialize, StrictDumb, StrictEncode, StrictSerialize}; -use crate::containers::{AnchorSet, XPubWitness}; +use super::SealWitness; use crate::LIB_NAME_RGB_STD; -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD, tags = repr, into_u8, try_from_u8)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -#[repr(u8)] -pub enum CloseMethodSet { - #[strict_type(dumb)] - TapretFirst = 0x01, - OpretFirst = 0x02, - Both = 0x03, -} - -impl BitOr> for CloseMethodSet { - type Output = Self; - fn bitor(mut self, rhs: Option) -> Self::Output { - if let Some(m) = rhs { - self |= m - }; - self - } -} - -impl BitOrAssign> for CloseMethodSet { - fn bitor_assign(&mut self, rhs: Option) { - if let Some(m) = rhs { - *self |= m - }; - } -} - -impl BitOr for Option { - type Output = CloseMethodSet; - fn bitor(self, mut rhs: CloseMethodSet) -> Self::Output { - if let Some(m) = self { - rhs |= m - }; - rhs - } -} - -impl BitOrAssign for Option { - fn bitor_assign(&mut self, rhs: CloseMethodSet) { *self = Some(rhs | *self) } -} - -impl> BitOr for CloseMethodSet { - type Output = Self; - fn bitor(self, rhs: T) -> Self::Output { if self == rhs.into() { self } else { Self::Both } } -} - -impl> BitOrAssign for CloseMethodSet { - fn bitor_assign(&mut self, rhs: T) { *self = self.bitor(rhs.into()); } -} - -impl From for CloseMethodSet { - fn from(seal: XOutputSeal) -> Self { seal.method().into() } -} - -impl From for CloseMethodSet { - fn from(method: CloseMethod) -> Self { - match method { - CloseMethod::OpretFirst => CloseMethodSet::OpretFirst, - CloseMethod::TapretFirst => CloseMethodSet::TapretFirst, - } - } -} - -impl CloseMethodSet { - pub fn has_tapret_first(self) -> bool { matches!(self, Self::TapretFirst | Self::Both) } - pub fn has_opret_first(self) -> bool { matches!(self, Self::OpretFirst | Self::Both) } -} - #[derive(Clone, Eq, Debug)] #[derive(StrictType, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_RGB_STD)] @@ -121,9 +43,8 @@ impl CloseMethodSet { )] pub struct TransitionInfo { pub id: OpId, - pub inputs: Confined, 1, U24>, + pub inputs: Confined, 1, U24>, pub transition: Transition, - pub method: CloseMethod, } impl StrictDumb for TransitionInfo { @@ -147,25 +68,24 @@ impl Hash for TransitionInfo { } impl TransitionInfo { + /// # Panics + /// + /// If the number of provided seals is zero. pub fn new( transition: Transition, - seals: impl AsRef<[XOutputSeal]>, + seals: impl AsRef<[OutputSeal]>, ) -> Result { let id = transition.id(); let seals = seals.as_ref(); + assert!(!seals.is_empty(), "empty seals provided to transition info constructor"); let inputs = Confined::, 1, U24>::try_from_iter( - seals.iter().copied().map(XOutpoint::from), + seals.iter().copied().map(Outpoint::from), ) .map_err(|_| TransitionInfoError::TooMany(id))?; - let method = seals.first().expect("one item guaranteed").method(); - if seals.iter().any(|s| s.method() != method) { - return Err(TransitionInfoError::CloseMethodDivergence(id)); - } Ok(TransitionInfo { id, inputs, transition, - method, }) } } @@ -176,10 +96,6 @@ pub enum TransitionInfoError { /// the operation produces too many state transitions which can't fit the /// container requirements. TooMany(OpId), - - /// transition {0} contains inputs with different seal closing methods, - /// which is not allowed. - CloseMethodDivergence(OpId), } /// A batch of state transitions under different contracts which are associated @@ -194,8 +110,8 @@ pub enum TransitionInfoError { serde(crate = "serde_crate", rename_all = "camelCase") )] pub struct Batch { - pub main: TransitionDichotomy, - pub blanks: Confined, 0, { U24 - 1 }>, + pub main: TransitionInfo, + pub extras: Confined, 0, { U24 - 1 }>, } impl StrictSerialize for Batch {} @@ -203,97 +119,21 @@ impl StrictDeserialize for Batch {} impl IntoIterator for Batch { type Item = TransitionInfo; - type IntoIter = iter::FlatMap< - vec::IntoIter>, - vec::IntoIter, - fn(Dichotomy) -> as IntoIterator>::IntoIter, - >; + type IntoIter = std::vec::IntoIter; fn into_iter(self) -> Self::IntoIter { - let mut vec = self.blanks.release(); + let mut vec = self.extras.release(); vec.push(self.main); - vec.into_iter().flat_map(TransitionDichotomy::into_iter) + vec.into_iter() } } impl Batch { - pub fn close_method_set(&self) -> CloseMethodSet { - let mut methods = CloseMethodSet::from(self.main.first.method); - if let Some(info) = &self.main.second { - methods |= info.method; - } - self.blanks.iter().for_each(|i| methods |= i.first.method); - self.blanks - .iter() - .filter_map(|i| i.second.as_ref()) - .for_each(|i| methods |= i.method); - methods - } - pub fn set_priority(&mut self, priority: u64) { - self.main.first.transition.nonce = priority; - if let Some(info) = &mut self.main.second { + self.main.transition.nonce = priority; + for info in &mut self.extras { info.transition.nonce = priority; } - for info in &mut self.blanks { - info.first.transition.nonce = priority; - if let Some(info) = &mut info.second { - info.transition.nonce = priority; - } - } - } -} - -pub type BundleDichotomy = Dichotomy; -pub type TransitionDichotomy = Dichotomy; - -#[derive(Clone, PartialEq, Eq, Hash, Debug)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub struct Dichotomy { - pub first: T, - pub second: Option, -} - -impl FromIterator for Dichotomy { - fn from_iter>(iter: I) -> Self { - let mut iter = iter.into_iter(); - let first = iter.next().expect("iterator must have at least one item"); - let second = iter.next(); - assert!(iter.next().is_none(), "iterator must have at most two items"); - Self { first, second } - } -} - -impl IntoIterator for Dichotomy { - type Item = T; - type IntoIter = vec::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - let mut vec = Vec::with_capacity(2); - vec.push(self.first); - if let Some(s) = self.second { - vec.push(s) - } - vec.into_iter() - } -} - -impl Dichotomy { - pub fn with(first: T, second: Option) -> Self { Self { first, second } } - - pub fn iter(&self) -> vec::IntoIter<&T> { - let mut vec = Vec::with_capacity(2); - vec.push(&self.first); - if let Some(ref s) = self.second { - vec.push(s) - } - vec.into_iter() } } @@ -309,16 +149,14 @@ impl Dichotomy { serde(crate = "serde_crate", rename_all = "camelCase") )] pub struct Fascia { - pub witness: XPubWitness, - pub anchor: AnchorSet, - pub bundles: NonEmptyOrdMap, + pub seal_witness: SealWitness, + pub bundles: NonEmptyOrdMap, } impl StrictDumb for Fascia { fn strict_dumb() -> Self { Fascia { - witness: strict_dumb!(), - anchor: strict_dumb!(), + seal_witness: strict_dumb!(), bundles: NonEmptyOrdMap::with_key_value(strict_dumb!(), strict_dumb!()), } } @@ -327,11 +165,9 @@ impl StrictSerialize for Fascia {} impl StrictDeserialize for Fascia {} impl Fascia { - pub fn witness_id(&self) -> XWitnessId { self.witness.map_ref(|w| w.txid()) } + pub fn witness_id(&self) -> Txid { self.seal_witness.public.txid() } pub fn into_bundles(self) -> impl IntoIterator { - self.bundles - .into_iter() - .flat_map(|(id, d)| d.into_iter().map(move |b| (id, b))) + self.bundles.into_iter() } } diff --git a/src/containers/seal.rs b/src/containers/seal.rs index 49dc3079..ea4e2bdf 100644 --- a/src/containers/seal.rs +++ b/src/containers/seal.rs @@ -24,7 +24,7 @@ use bp::seals::txout::{BlindSeal, CloseMethod, SealTxid}; use bp::secp256k1::rand::{thread_rng, RngCore}; use bp::Vout; -use rgb::{GraphSeal, Layer1, SecretSeal, TxoSeal, XChain}; +use rgb::{GraphSeal, SecretSeal, TxoSeal}; use crate::LIB_NAME_RGB_STD; @@ -102,28 +102,17 @@ impl VoutSeal { } impl From for GraphSeal { - fn from(seal: VoutSeal) -> Self { - Self::with_blinded_vout(seal.method, seal.vout, seal.blinding) - } + fn from(seal: VoutSeal) -> Self { Self::with_blinded_vout(seal.vout, seal.blinding) } } /// Seal used by operation builder which can be either revealed or concealed. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, From)] pub enum BuilderSeal { - Revealed(XChain), + Revealed(Seal), #[from] - Concealed(XChain), -} - -impl From>> for BuilderSeal> { - fn from(seal: XChain>) -> Self { BuilderSeal::Revealed(seal) } + Concealed(SecretSeal), } -impl BuilderSeal { - pub fn layer1(&self) -> Layer1 { - match self { - BuilderSeal::Revealed(x) => x.layer1(), - BuilderSeal::Concealed(x) => x.layer1(), - } - } +impl From> for BuilderSeal> { + fn from(seal: BlindSeal) -> Self { BuilderSeal::Revealed(seal) } } diff --git a/src/containers/suppl.rs b/src/containers/suppl.rs deleted file mode 100644 index 5da0ea8b..00000000 --- a/src/containers/suppl.rs +++ /dev/null @@ -1,355 +0,0 @@ -// RGB standard library for working with smart contracts on Bitcoin & Lightning -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2019-2024 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2019-2024 LNP/BP Standards Association. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::fmt; -use std::fmt::{Display, Formatter}; -use std::str::FromStr; - -use amplify::confinement::{SmallBlob, TinyOrdMap}; -use amplify::{ByteArray, Bytes32}; -use baid64::{Baid64ParseError, DisplayBaid64, FromBaid64Str}; -use chrono::Utc; -use commit_verify::{CommitId, CommitmentId, DigestExt, Sha256}; -use rgb::{AssignmentType, ContractId, GlobalStateType, Identity, SchemaId}; -use strict_encoding::stl::{AlphaCaps, AlphaNumDash}; -use strict_encoding::{ - DeserializeError, FieldName, RString, SerializeError, StrictDeserialize, StrictSerialize, - TypeName, VariantName, -}; -use strict_types::value; - -use crate::interface::{IfaceId, ImplId}; -use crate::LIB_NAME_RGB_STD; - -pub const SUPPL_ANNOT_VELOCITY: &str = "Velocity"; -pub const SUPPL_ANNOT_IFACE_CLASS: &str = "Standard"; -pub const SUPPL_ANNOT_IFACE_FEATURES: &str = "Features"; - -/// Contract supplement identifier. -/// -/// Contract supplement identifier commits to all of the supplement data. -#[derive(Wrapper, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)] -#[wrapper(Deref, BorrowSlice, Hex, Index, RangeOps)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD)] -pub struct SupplId( - #[from] - #[from([u8; 32])] - Bytes32, -); - -impl From for SupplId { - fn from(hasher: Sha256) -> Self { hasher.finish().into() } -} - -impl CommitmentId for SupplId { - const TAG: &'static str = "urn:lnp-bp:rgb:suppl#2024-03-11"; -} - -impl DisplayBaid64 for SupplId { - const HRI: &'static str = "rgb:sup"; - const CHUNKING: bool = false; - const PREFIX: bool = true; - const EMBED_CHECKSUM: bool = false; - const MNEMONIC: bool = false; - fn to_baid64_payload(&self) -> [u8; 32] { self.to_byte_array() } -} -impl FromBaid64Str for SupplId {} -impl FromStr for SupplId { - type Err = Baid64ParseError; - fn from_str(s: &str) -> Result { Self::from_baid64_str(s) } -} -impl Display for SupplId { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { self.fmt_baid64(f) } -} - -impl_serde_baid64!(SupplId); - -impl SupplId { - pub const fn from_array(id: [u8; 32]) -> Self { Self(Bytes32::from_array(id)) } -} - -#[derive(Wrapper, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, From, Display)] -#[wrapper(Deref, FromStr)] -#[display(inner)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", transparent) -)] -pub struct AnnotationName(RString); - -impl From<&'static str> for AnnotationName { - fn from(s: &'static str) -> Self { Self(RString::from(s)) } -} - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display, From)] -#[display(inner)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD, tags = order, dumb = ContentRef::Schema(strict_dumb!()))] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub enum ContentRef { - #[from] - Schema(SchemaId), - #[from] - Genesis(ContractId), - #[from] - Iface(IfaceId), - #[from] - IfaceImpl(ImplId), -} - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)] -#[derive(StrictType, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD, tags = repr, into_u8, try_from_u8)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -#[repr(u8)] -pub enum SupplSub { - #[default] - Itself = 0, - Meta = 1, - Global, - Owned, - Valency, - Assignment, - Genesis, - Transition, - Extension, - Exception, -} - -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)] -#[derive(StrictType, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD, tags = custom)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub enum SupplItem { - #[default] - #[strict_type(tag = 0)] - Default, - #[strict_type(tag = 1)] - TypeNo(u16), - #[strict_type(tag = 0x11)] - TypeName(TypeName), - #[strict_type(tag = 0x12)] - FieldName(FieldName), - #[strict_type(tag = 0x13)] - VariantName(VariantName), -} - -#[derive(Wrapper, WrapperMut, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default, From)] -#[wrapper(Deref)] -#[wrapper_mut(DerefMut)] -#[derive(StrictType, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] -pub struct SupplMap(TinyOrdMap); - -#[derive(Wrapper, WrapperMut, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default, From)] -#[wrapper(Deref)] -#[wrapper_mut(DerefMut)] -#[derive(StrictType, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] -pub struct Annotations(TinyOrdMap); - -/// Contract supplement, providing non-consensus information about standard -/// way of working with the contract data. Each contract can have only a single -/// valid supplement; the supplement is attached to the contract via trusted -/// provider signature (providers are ordered by the priority). -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD)] -#[derive(CommitEncode)] -#[commit_encode(strategy = strict, id = SupplId)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub struct Supplement { - pub content_id: ContentRef, - pub timestamp: i64, - pub creator: Identity, - /// Strict-encoded custom fields. - pub annotations: TinyOrdMap, -} - -impl StrictSerialize for Supplement {} -impl StrictDeserialize for Supplement {} - -impl Supplement { - pub fn suppl_id(&self) -> SupplId { self.commit_id() } - - pub fn new(content: impl Into, creator: impl Into) -> Self { - Supplement { - content_id: content.into(), - timestamp: Utc::now().timestamp(), - creator: creator.into(), - annotations: none!(), - } - } - - pub fn get_default_opt( - &self, - sub: SupplSub, - name: impl Into, - ) -> Option { - self.get_default(sub, name).transpose().ok().flatten() - } - - pub fn get_default( - &self, - sub: SupplSub, - name: impl Into, - ) -> Option> { - let annotation = self - .annotations - .get(&sub)? - .get(&SupplItem::Default)? - .get(&name.into())?; - Some(T::from_strict_serialized(annotation.clone())) - } - - pub fn get( - &self, - sub: SupplSub, - item: SupplItem, - name: impl Into, - ) -> Option> { - let annotation = self.annotations.get(&sub)?.get(&item)?.get(&name.into())?; - Some(T::from_strict_serialized(annotation.clone())) - } - - pub fn annotate_itself( - &mut self, - name: impl Into, - data: &impl StrictSerialize, - ) -> Result { - self.annotate_default(SupplSub::Itself, name, data) - } - - pub fn annotate_default( - &mut self, - sub: SupplSub, - name: impl Into, - data: &impl StrictSerialize, - ) -> Result { - self.annotate(sub, SupplItem::Default, name, data) - } - - pub fn annotate( - &mut self, - sub: SupplSub, - item: SupplItem, - name: impl Into, - data: &impl StrictSerialize, - ) -> Result { - let mut a = self - .annotations - .remove(&sub) - .expect("zero items allowed") - .unwrap_or_default(); - let mut b = a - .remove(&item) - .expect("zero items allowed") - .unwrap_or_default(); - let prev = b.insert(name.into(), data.to_strict_serialized()?)?; - a.insert(item, b)?; - self.annotations.insert(sub, a)?; - Ok(prev.is_some()) - } -} - -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD, tags = custom)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub enum TickerSuppl { - #[strict_type(tag = 0, dumb)] - Absent, - #[strict_type(tag = 1)] - Global(GlobalStateType, value::Path), - #[strict_type(tag = 2)] - Owned(AssignmentType, value::Path), -} - -impl StrictSerialize for TickerSuppl {} -impl StrictDeserialize for TickerSuppl {} - -#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Display, Default)] -#[derive(StrictType, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD, tags = repr, try_from_u8, into_u8)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -#[display(lowercase)] -#[repr(u8)] -pub enum VelocityHint { - #[default] - Unspecified = 0, - /// Should be used for thinks like secondary issuance for tokens which do - /// not inflate very often. - Seldom = 15, - /// Should be used for digital identity revocations. - Episodic = 31, - /// Should be used for digital art, shares, bonds etc. - Regular = 63, - /// Should be used for fungible tokens. - Frequent = 127, - /// Should be used for stablecoins and money. - HighFrequency = 255, -} - -impl StrictSerialize for VelocityHint {} -impl StrictDeserialize for VelocityHint {} - -impl VelocityHint { - pub fn with_value(value: &u8) -> Self { - match *value { - 0 => VelocityHint::Unspecified, - 1..=15 => VelocityHint::Seldom, - 16..=31 => VelocityHint::Episodic, - 32..=63 => VelocityHint::Regular, - 64..=127 => VelocityHint::Frequent, - 128..=255 => VelocityHint::HighFrequency, - } - } -} diff --git a/src/containers/util.rs b/src/containers/util.rs index 92b68043..74602a70 100644 --- a/src/containers/util.rs +++ b/src/containers/util.rs @@ -19,15 +19,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::collections::btree_map; +use std::collections::btree_set; +use std::iter; -use amplify::confinement::{NonEmptyBlob, NonEmptyOrdMap}; -use commit_verify::StrictHash; -use rgb::{ContractId, Identity, SchemaId}; -use strict_encoding::StrictDumb; +use amplify::confinement::{NonEmptyOrdSet, U16}; +use rgb::SecretSeal; -use super::SupplId; -use crate::interface::{IfaceId, ImplId}; use crate::LIB_NAME_RGB_STD; #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, Default)] @@ -41,97 +38,27 @@ use crate::LIB_NAME_RGB_STD; #[non_exhaustive] #[repr(u8)] pub enum ContainerVer { - // V0 and V1 was a previous version before v0.11, currently not supported. #[default] - #[display("v2", alt = "2")] - V2 = 2, + #[display("v0", alt = "0")] + V0 = 0, } -pub trait SigValidator { - fn validate_sig(&self, identity: &Identity, sig: SigBlob) -> bool; -} - -pub struct DumbValidator; -impl SigValidator for DumbValidator { - fn validate_sig(&self, _: &Identity, _: SigBlob) -> bool { false } -} - -#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, Default)] -#[display(lowercase)] -#[derive(StrictType, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD, tags = repr, into_u8, try_from_u8)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -#[repr(u8)] -pub enum TrustLevel { - Malicious = 0x10, - #[default] - Unknown = 0x20, - Untrusted = 0x40, - Trusted = 0x80, - Ultimate = 0xC0, -} - -impl TrustLevel { - pub fn should_accept(self) -> bool { self >= Self::Unknown } - pub fn should_use(self) -> bool { self >= Self::Trusted } - pub fn must_use(self) -> bool { self >= Self::Ultimate } -} - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +/// Non-empty set of secret seals. +#[derive(Wrapper, WrapperMut, Clone, PartialEq, Eq, Hash, Debug, From)] +#[wrapper(Deref)] +#[wrapper_mut(DerefMut)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD, tags = order, dumb = ContentId::Schema(strict_dumb!()))] +#[strict_type(lib = LIB_NAME_RGB_STD, dumb = Self(NonEmptyOrdSet::with(SecretSeal::strict_dumb())))] #[cfg_attr( feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate", rename_all = "camelCase") )] -pub enum ContentId { - Schema(SchemaId), - Genesis(ContractId), - Iface(IfaceId), - IfaceImpl(ImplId), - Suppl(SupplId), -} - -#[derive(Wrapper, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, From, Display)] -#[wrapper(Deref, AsSlice, BorrowSlice, Hex)] -#[display(LowerHex)] -#[derive(StrictType, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD)] -#[derive(CommitEncode)] -#[commit_encode(strategy = strict, id = StrictHash)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", transparent) -)] -pub struct SigBlob(NonEmptyBlob<4096>); - -impl Default for SigBlob { - fn default() -> Self { SigBlob(NonEmptyBlob::with(0)) } -} - -#[derive(Wrapper, WrapperMut, Clone, PartialEq, Eq, Hash, Debug, From)] -#[wrapper(Deref)] -#[wrapper_mut(DerefMut)] -#[derive(StrictType, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] -pub struct ContentSigs(NonEmptyOrdMap); - -impl StrictDumb for ContentSigs { - fn strict_dumb() -> Self { - Self(NonEmptyOrdMap::with_key_value(strict_dumb!(), SigBlob::default())) - } -} +pub struct SecretSeals(NonEmptyOrdSet); -impl IntoIterator for ContentSigs { - type Item = (Identity, SigBlob); - type IntoIter = btree_map::IntoIter; +impl<'a> IntoIterator for &'a SecretSeals { + type Item = SecretSeal; + type IntoIter = iter::Copied>; - fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } + fn into_iter(self) -> Self::IntoIter { self.0.iter().copied() } } diff --git a/src/contract/assignments.rs b/src/contract/assignments.rs index 8d66b25a..5c084ddf 100644 --- a/src/contract/assignments.rs +++ b/src/contract/assignments.rs @@ -20,17 +20,15 @@ // limitations under the License. use std::cmp::Ordering; -use std::collections::HashMap; +use std::collections::{BTreeSet, HashMap}; use std::fmt::Debug; +use std::hash::Hash; -use amplify::confinement::SmallVec; -use commit_verify::Conceal; use invoice::Amount; use rgb::vm::WitnessOrd; use rgb::{ - Assign, AssignAttach, AssignData, AssignFungible, AssignRights, AssignmentType, AttachState, - DataState, ExposedSeal, ExposedState, OpId, Opout, RevealedAttach, RevealedData, RevealedValue, - TypedAssigns, VoidState, XChain, XOutputSeal, XWitnessId, + AssignmentType, BundleId, ExposedSeal, OpId, Opout, OutputSeal, RevealedData, RevealedValue, + Txid, VoidState, }; use strict_encoding::{StrictDecode, StrictDumb, StrictEncode}; @@ -39,18 +37,41 @@ use crate::LIB_NAME_RGB_STD; /// Trait used by contract state. Unlike [`ExposedState`] it doesn't allow /// concealment of the state, i.e. may contain incomplete data without blinding /// factors, asset tags etc. -pub trait KnownState: Debug + StrictDumb + StrictEncode + StrictDecode + Eq + Clone {} +pub trait KnownState: Debug + StrictDumb + StrictEncode + StrictDecode + Eq + Clone + Hash { + const IS_FUNGIBLE: bool; +} + +impl KnownState for () { + const IS_FUNGIBLE: bool = false; +} +impl KnownState for VoidState { + const IS_FUNGIBLE: bool = false; +} +impl KnownState for Amount { + const IS_FUNGIBLE: bool = true; +} +impl KnownState for RevealedValue { + const IS_FUNGIBLE: bool = true; +} +impl KnownState for RevealedData { + const IS_FUNGIBLE: bool = false; +} -impl KnownState for () {} -impl KnownState for VoidState {} -impl KnownState for DataState {} -impl KnownState for Amount {} -impl KnownState for AttachState {} -impl KnownState for RevealedValue {} -impl KnownState for RevealedData {} -impl KnownState for RevealedAttach {} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_RGB_STD)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", rename_all = "camelCase") +)] +pub struct WitnessInfo { + pub id: Txid, + pub ord: WitnessOrd, +} -#[derive(Copy, Clone, Eq, Debug)] +#[allow(clippy::derived_hash_with_manual_eq)] +#[derive(Copy, Clone, Eq, Hash, Debug)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_RGB_STD)] #[cfg_attr( @@ -60,9 +81,10 @@ impl KnownState for RevealedAttach {} )] pub struct OutputAssignment { pub opout: Opout, - pub seal: XOutputSeal, + pub seal: OutputSeal, pub state: State, - pub witness: Option, + pub witness: Option, + pub bundle_id: Option, } impl PartialEq for OutputAssignment { @@ -102,20 +124,19 @@ impl OutputAssignment { /// If the processing is done on invalid stash data, the seal is /// witness-based and the anchor chain doesn't match the seal chain. pub fn with_witness( - seal: XChain, - witness_id: XWitnessId, + seal: Seal, + witness_id: Txid, state: State, + bundle_id: Option, opid: OpId, ty: AssignmentType, no: u16, ) -> Self { OutputAssignment { opout: Opout::new(opid, ty, no), - seal: seal.try_to_output_seal(witness_id).expect( - "processing contract from unverified/invalid stash: witness seal chain doesn't \ - match anchor's chain", - ), + seal: seal.to_output_seal_or_default(witness_id), state, + bundle_id, witness: witness_id.into(), } } @@ -125,8 +146,9 @@ impl OutputAssignment { /// If the processing is done on invalid stash data, the seal is /// witness-based and the anchor chain doesn't match the seal chain. pub fn with_no_witness( - seal: XChain, + seal: Seal, state: State, + bundle_id: Option, opid: OpId, ty: AssignmentType, no: u16, @@ -135,23 +157,26 @@ impl OutputAssignment { opout: Opout::new(opid, ty, no), seal: seal.to_output_seal().expect( "processing contract from unverified/invalid stash: seal must have txid \ - information since it comes from genesis or extension", + information since it comes from genesis", ), state, + bundle_id, witness: None, } } + /// Transmutes output assignment from one form of state to another pub fn transmute>(self) -> OutputAssignment { OutputAssignment { opout: self.opout, seal: self.seal, state: self.state.into(), + bundle_id: self.bundle_id, witness: self.witness, } } - pub fn check_witness(&self, filter: &HashMap) -> bool { + pub fn check_witness(&self, filter: &HashMap) -> bool { match self.witness { None => true, Some(witness_id) => { @@ -159,63 +184,11 @@ impl OutputAssignment { } } } -} - -pub trait TypedAssignsExt { - fn reveal_seal(&mut self, seal: XChain); - - fn filter_revealed_seals(&self) -> Vec>; -} - -impl TypedAssignsExt for TypedAssigns { - fn reveal_seal(&mut self, seal: XChain) { - fn reveal( - vec: &mut SmallVec>, - revealed: XChain, - ) { - for assign in vec.iter_mut() { - match assign { - Assign::ConfidentialSeal { seal, state, lock } - if *seal == revealed.conceal() => - { - *assign = Assign::Revealed { - seal: revealed, - state: state.clone(), - lock: *lock, - } - } - Assign::Confidential { seal, state, lock } if *seal == revealed.conceal() => { - *assign = Assign::ConfidentialState { - seal: revealed, - state: *state, - lock: *lock, - } - } - _ => {} - } - } - } - match self { - TypedAssigns::Declarative(v) => reveal(v, seal), - TypedAssigns::Fungible(v) => reveal(v, seal), - TypedAssigns::Structured(v) => reveal(v, seal), - TypedAssigns::Attachment(v) => reveal(v, seal), - } - } - - fn filter_revealed_seals(&self) -> Vec> { - match self { - TypedAssigns::Declarative(s) => { - s.iter().filter_map(AssignRights::revealed_seal).collect() - } - TypedAssigns::Fungible(s) => { - s.iter().filter_map(AssignFungible::revealed_seal).collect() - } - TypedAssigns::Structured(s) => s.iter().filter_map(AssignData::revealed_seal).collect(), - TypedAssigns::Attachment(s) => { - s.iter().filter_map(AssignAttach::revealed_seal).collect() - } + pub fn check_bundle(&self, invalid_bundles: &BTreeSet) -> bool { + match self.bundle_id { + Some(bundle_id) => !invalid_bundles.contains(&bundle_id), + None => true, } } } diff --git a/src/contract/builder.rs b/src/contract/builder.rs new file mode 100644 index 00000000..5daa9df9 --- /dev/null +++ b/src/contract/builder.rs @@ -0,0 +1,738 @@ +// RGB standard library for working with smart contracts on Bitcoin & Lightning +// +// SPDX-License-Identifier: Apache-2.0 +// +// Written in 2019-2024 by +// Dr Maxim Orlovsky +// +// Copyright (C) 2019-2024 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![allow(clippy::result_large_err)] + +use std::collections::{BTreeMap, HashSet}; + +use amplify::confinement::{Confined, NonEmptyOrdSet, TinyOrdMap, U16}; +use amplify::{confinement, Wrapper}; +use chrono::Utc; +use invoice::Amount; +use rgb::assignments::AssignVec; +use rgb::validation::Scripts; +use rgb::{ + validation, Assign, AssignmentType, Assignments, ChainNet, ContractId, ExposedSeal, + FungibleType, Genesis, GenesisSeal, GlobalState, GraphSeal, Identity, Layer1, MetadataError, + Opout, OwnedStateSchema, RevealedData, RevealedValue, Schema, Transition, TransitionType, + TypedAssigns, +}; +use rgbcore::{GlobalStateSchema, GlobalStateType, MetaType, Metadata}; +use strict_encoding::{FieldName, SerializeError, StrictSerialize}; +use strict_types::{decode, SemId, TypeSystem}; + +use crate::containers::{BuilderSeal, ContainerVer, Contract, ValidConsignment}; +use crate::contract::resolver::DumbResolver; +use crate::contract::AllocatedState; +use crate::persistence::StashInconsistency; + +#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)] +#[display(doc_comments)] +pub enum BuilderError { + #[from] + #[display(inner)] + MetadataInvalid(MetadataError), + + /// unknown owned state name `{0}`. + InvalidStateField(FieldName), + + /// state `{0}` provided to the builder has invalid type. + InvalidStateType(AssignmentType), + + /// {0} is not supported by the contract genesis. + InvalidLayer1(Layer1), + + #[from] + #[display(inner)] + StrictEncode(SerializeError), + + #[from] + #[display(inner)] + Reify(decode::Error), + + #[from] + #[display(inner)] + Confinement(confinement::Error), + + #[from] + #[display(doc_comments)] + Inconsistency(StashInconsistency), + + #[from] + #[display(inner)] + ContractInconsistency(validation::Status), +} + +#[derive(Clone, Debug)] +pub struct ContractBuilder { + builder: OperationBuilder, + scripts: Scripts, + issuer: Identity, + chain_net: ChainNet, +} + +impl ContractBuilder { + pub fn with( + issuer: Identity, + schema: Schema, + types: TypeSystem, + scripts: Scripts, + chain_net: ChainNet, + ) -> Self { + Self { + builder: OperationBuilder::with(schema, types), + scripts, + issuer, + chain_net, + } + } + + pub fn type_system(&self) -> &TypeSystem { self.builder.type_system() } + + #[inline] + pub fn global_type(&self, name: impl Into) -> GlobalStateType { + self.builder.global_type(name) + } + + #[inline] + pub fn meta_name(&self, type_id: MetaType) -> &FieldName { self.builder.meta_name(type_id) } + + #[inline] + pub fn add_metadata( + mut self, + name: impl Into, + value: impl StrictSerialize, + ) -> Result { + self.builder = self.builder.add_metadata(name, value)?; + Ok(self) + } + + #[inline] + pub fn add_metadata_raw( + mut self, + type_id: MetaType, + value: impl StrictSerialize, + ) -> Result { + self.builder = self.builder.add_metadata_raw(type_id, value)?; + Ok(self) + } + + #[inline] + pub fn add_global_state( + mut self, + name: impl Into, + value: impl StrictSerialize, + ) -> Result { + self.builder = self.builder.add_global_state(name, value)?; + Ok(self) + } + + #[inline] + pub fn add_global_state_raw( + mut self, + type_id: GlobalStateType, + value: impl StrictSerialize, + ) -> Result { + self.builder = self.builder.add_global_state_raw(type_id, value)?; + Ok(self) + } + + pub fn add_rights( + mut self, + name: impl Into, + seal: impl Into>, + ) -> Result { + let seal = seal.into(); + self.builder = self.builder.add_rights(name, seal)?; + Ok(self) + } + + pub fn add_rights_raw( + mut self, + type_id: AssignmentType, + seal: impl Into>, + ) -> Result { + let seal = seal.into(); + self.builder = self.builder.add_rights_raw(type_id, seal)?; + Ok(self) + } + + pub fn add_fungible_state( + mut self, + name: impl Into, + seal: impl Into>, + value: impl Into, + ) -> Result { + let seal = seal.into(); + self.builder = self.builder.add_fungible_state(name, seal, value)?; + Ok(self) + } + + pub fn add_fungible_state_raw( + mut self, + type_id: AssignmentType, + seal: impl Into>, + value: impl Into, + ) -> Result { + let state = RevealedValue::new(value.into()); + self.builder = self.builder.add_fungible_state_raw(type_id, seal, state)?; + Ok(self) + } + + pub fn add_data( + mut self, + name: impl Into, + seal: impl Into>, + value: impl StrictSerialize, + ) -> Result { + let seal = seal.into(); + self.builder = self.builder.add_data(name, seal, value)?; + Ok(self) + } + + pub fn add_data_raw( + mut self, + type_id: AssignmentType, + seal: impl Into>, + state: RevealedData, + ) -> Result { + let seal = seal.into(); + self.builder = self.builder.add_data_raw(type_id, seal, state)?; + Ok(self) + } + + pub fn issue_contract(self) -> Result, BuilderError> { + self.issue_contract_raw(Utc::now().timestamp()) + } + + pub fn issue_contract_raw( + self, + timestamp: i64, + ) -> Result, BuilderError> { + let (schema, global, assignments, types, metadata) = self.builder.complete(); + + let genesis = Genesis { + ffv: none!(), + schema_id: schema.schema_id(), + timestamp, + chain_net: self.chain_net, + seal_closing_strategy: Default::default(), + metadata, + globals: global, + assignments, + issuer: self.issuer, + }; + + let scripts = Confined::from_iter_checked(self.scripts.into_values()); + + let contract = Contract { + version: ContainerVer::V0, + transfer: false, + terminals: none!(), + genesis, + bundles: none!(), + schema, + + types, + scripts, + }; + + let valid_contract = contract.validate(&DumbResolver, self.chain_net, None)?; + + Ok(valid_contract) + } +} + +#[derive(Clone, Debug)] +pub struct TransitionBuilder { + contract_id: ContractId, + builder: OperationBuilder, + nonce: u64, + transition_type: TransitionType, + inputs: TinyOrdMap, +} + +impl TransitionBuilder { + pub fn named_transition( + contract_id: ContractId, + schema: Schema, + transition_name: impl Into, + types: TypeSystem, + ) -> Result { + let transition_type = schema.transition_type(transition_name); + Ok(Self::with(contract_id, schema, transition_type, types)) + } + + pub fn with( + contract_id: ContractId, + schema: Schema, + transition_type: TransitionType, + types: TypeSystem, + ) -> Self { + Self { + contract_id, + builder: OperationBuilder::with(schema, types), + nonce: u64::MAX, + transition_type, + inputs: none!(), + } + } + + pub fn type_system(&self) -> &TypeSystem { self.builder.type_system() } + + pub fn transition_type(&self) -> TransitionType { self.transition_type } + + pub fn set_nonce(mut self, nonce: u64) -> Self { + self.nonce = nonce; + self + } + + #[inline] + pub fn add_metadata( + mut self, + name: impl Into, + value: impl StrictSerialize, + ) -> Result { + self.builder = self.builder.add_metadata(name, value)?; + Ok(self) + } + + #[inline] + pub fn add_metadata_raw( + mut self, + type_id: MetaType, + value: impl StrictSerialize, + ) -> Result { + self.builder = self.builder.add_metadata_raw(type_id, value)?; + Ok(self) + } + + #[inline] + pub fn add_global_state( + mut self, + name: impl Into, + value: impl StrictSerialize, + ) -> Result { + self.builder = self.builder.add_global_state(name, value)?; + Ok(self) + } + + pub fn add_input(mut self, opout: Opout, state: AllocatedState) -> Result { + self.inputs.insert(opout, state)?; + Ok(self) + } + + #[inline] + pub fn assignment_type(&self, name: impl Into) -> AssignmentType { + self.builder.assignment_type(name) + } + + #[inline] + pub fn global_type(&self, name: impl Into) -> GlobalStateType { + self.builder.global_type(name) + } + + #[inline] + pub fn meta_name(&self, type_id: MetaType) -> &FieldName { self.builder.meta_name(type_id) } + + pub fn add_owned_state_raw( + mut self, + type_id: AssignmentType, + seal: impl Into>, + state: AllocatedState, + ) -> Result { + self.builder = self.builder.add_owned_state_raw(type_id, seal, state)?; + Ok(self) + } + + pub fn add_rights( + mut self, + name: impl Into, + seal: impl Into>, + ) -> Result { + self.builder = self.builder.add_rights(name, seal)?; + Ok(self) + } + + pub fn add_rights_raw( + mut self, + type_id: AssignmentType, + seal: impl Into>, + ) -> Result { + self.builder = self.builder.add_rights_raw(type_id, seal)?; + Ok(self) + } + + pub fn add_fungible_state( + mut self, + name: impl Into, + seal: impl Into>, + value: impl Into, + ) -> Result { + self.builder = self.builder.add_fungible_state(name, seal, value)?; + Ok(self) + } + + pub fn add_fungible_state_raw( + mut self, + type_id: AssignmentType, + seal: impl Into>, + value: impl Into, + ) -> Result { + let state = RevealedValue::new(value.into()); + self.builder = self.builder.add_fungible_state_raw(type_id, seal, state)?; + Ok(self) + } + + pub fn add_data( + mut self, + name: impl Into, + seal: impl Into>, + value: impl StrictSerialize, + ) -> Result { + self.builder = self.builder.add_data(name, seal, value)?; + Ok(self) + } + + pub fn add_data_raw( + mut self, + type_id: AssignmentType, + seal: impl Into>, + state: RevealedData, + ) -> Result { + self.builder = self.builder.add_data_raw(type_id, seal, state)?; + Ok(self) + } + + pub fn has_inputs(&self) -> bool { !self.inputs.is_empty() } + + pub fn complete_transition(self) -> Result { + let (_, global, assignments, _, metadata) = self.builder.complete(); + + let transition = Transition { + ffv: none!(), + contract_id: self.contract_id, + nonce: self.nonce, + transition_type: self.transition_type, + metadata, + globals: global, + inputs: NonEmptyOrdSet::from_iter_checked(self.inputs.into_keys()).into(), + assignments, + signature: none!(), + }; + + // TODO: Validate against schema + + Ok(transition) + } +} + +#[derive(Clone, Debug)] +pub struct OperationBuilder { + schema: Schema, + + global: GlobalState, + meta: Metadata, + rights: TinyOrdMap>, 1, U16>>, + fungible: + TinyOrdMap, RevealedValue>, 1, U16>>, + data: TinyOrdMap, RevealedData>, 1, U16>>, + types: TypeSystem, +} + +impl OperationBuilder { + fn with(schema: Schema, types: TypeSystem) -> Self { + OperationBuilder { + schema, + + global: none!(), + meta: none!(), + rights: none!(), + fungible: none!(), + data: none!(), + + types, + } + } + + fn type_system(&self) -> &TypeSystem { &self.types } + + fn assignment_type(&self, name: impl Into) -> AssignmentType { + self.schema.assignment_type(name) + } + + fn meta_type(&self, name: impl Into) -> MetaType { self.schema.meta_type(name) } + + fn meta_name(&self, ty: MetaType) -> &FieldName { self.schema.meta_name(ty) } + + fn global_type(&self, name: impl Into) -> GlobalStateType { + self.schema.global_type(name) + } + + #[inline] + fn state_schema(&self, type_id: AssignmentType) -> &OwnedStateSchema { + &self + .schema + .owned_types + .get(&type_id) + .expect("schema should support the assignment type: must be checked by the constructor") + .owned_state_schema + } + + #[inline] + fn meta_schema(&self, type_id: MetaType) -> &SemId { + &self + .schema + .meta_types + .get(&type_id) + .expect("schema should support the meta type: must be checked by the constructor") + .sem_id + } + + #[inline] + fn global_schema(&self, type_id: GlobalStateType) -> &GlobalStateSchema { + &self + .schema + .global_types + .get(&type_id) + .expect( + "schema should support the global state type: must be checked by the constructor", + ) + .global_state_schema + } + + pub fn add_metadata( + self, + name: impl Into, + value: impl StrictSerialize, + ) -> Result { + let type_id = self.meta_type(name); + self.add_metadata_raw(type_id, value) + } + + pub fn add_metadata_raw( + mut self, + type_id: MetaType, + value: impl StrictSerialize, + ) -> Result { + let serialized = value.to_strict_serialized::<{ u16::MAX as usize }>()?; + + let sem_id = self.meta_schema(type_id); + self.types.strict_deserialize_type(*sem_id, &serialized)?; + self.meta.add_value(type_id, serialized.into())?; + Ok(self) + } + + pub fn add_global_state( + self, + name: impl Into, + value: impl StrictSerialize, + ) -> Result { + let type_id = self.global_type(name); + self.add_global_state_raw(type_id, value) + } + + pub fn add_global_state_raw( + mut self, + type_id: GlobalStateType, + value: impl StrictSerialize, + ) -> Result { + let serialized = value.to_strict_serialized::<{ u16::MAX as usize }>()?; + + // Check value matches type requirements + let sem_id = self.global_schema(type_id).sem_id; + self.types.strict_deserialize_type(sem_id, &serialized)?; + + self.global.add_state(type_id, serialized.into())?; + + Ok(self) + } + + fn add_owned_state_raw( + self, + type_id: AssignmentType, + seal: impl Into>, + state: AllocatedState, + ) -> Result { + match state { + AllocatedState::Void => self.add_rights_raw(type_id, seal), + AllocatedState::Amount(value) => self.add_fungible_state_raw(type_id, seal, value), + AllocatedState::Data(data) => self.add_data_raw(type_id, seal, data), + } + } + + fn add_rights( + self, + name: impl Into, + seal: impl Into>, + ) -> Result { + let type_id = self.assignment_type(name); + self.add_rights_raw(type_id, seal) + } + + fn add_rights_raw( + mut self, + type_id: AssignmentType, + seal: impl Into>, + ) -> Result { + let state_schema = self.state_schema(type_id); + if *state_schema != OwnedStateSchema::Declarative { + return Err(BuilderError::InvalidStateType(type_id)); + } + + let seal = seal.into(); + match self.rights.get_mut(&type_id) { + Some(assignments) => { + assignments.push(seal)?; + } + None => { + self.rights.insert(type_id, Confined::with(seal))?; + } + } + + Ok(self) + } + + fn add_fungible_state( + self, + name: impl Into, + seal: impl Into>, + value: impl Into, + ) -> Result { + let type_id = self.assignment_type(name); + let state = RevealedValue::new(value.into()); + self.add_fungible_state_raw(type_id, seal, state) + } + + fn add_fungible_state_raw( + mut self, + type_id: AssignmentType, + seal: impl Into>, + state: RevealedValue, + ) -> Result { + let state_schema = self.state_schema(type_id); + if *state_schema != OwnedStateSchema::Fungible(FungibleType::Unsigned64Bit) { + return Err(BuilderError::InvalidStateType(type_id)); + } + + let seal = seal.into(); + match self.fungible.get_mut(&type_id) { + Some(assignments) => { + assignments.insert(seal, state)?; + } + None => { + self.fungible + .insert(type_id, Confined::with((seal, state)))?; + } + } + + Ok(self) + } + + fn add_data( + self, + name: impl Into, + seal: impl Into>, + value: impl StrictSerialize, + ) -> Result { + let serialized = value.to_strict_serialized::()?; + let state = RevealedData::from(serialized); + + let type_id = self.assignment_type(name); + self.add_data_raw(type_id, seal, state) + } + + fn add_data_raw( + mut self, + type_id: AssignmentType, + seal: impl Into>, + state: RevealedData, + ) -> Result { + let state_schema = self.state_schema(type_id); + if let OwnedStateSchema::Structured(_) = *state_schema { + let seal = seal.into(); + match self.data.get_mut(&type_id) { + Some(assignments) => { + assignments.insert(seal, state)?; + } + None => { + self.data.insert(type_id, Confined::with((seal, state)))?; + } + } + } else { + return Err(BuilderError::InvalidStateType(type_id)); + } + Ok(self) + } + + fn complete(self) -> (Schema, GlobalState, Assignments, TypeSystem, Metadata) { + let owned_state = self.fungible.into_iter().map(|(id, vec)| { + let vec = vec + .into_iter() + .map(|(seal, value)| match seal { + BuilderSeal::Revealed(seal) => Assign::Revealed { seal, state: value }, + BuilderSeal::Concealed(seal) => Assign::ConfidentialSeal { seal, state: value }, + }) + .collect::>(); + let state = Confined::try_from_iter(vec).expect("at least one element"); + let state = TypedAssigns::Fungible(AssignVec::with(state)); + (id, state) + }); + let owned_data = self.data.into_iter().map(|(id, vec)| { + let vec_data = vec.into_iter().map(|(seal, value)| match seal { + BuilderSeal::Revealed(seal) => Assign::Revealed { seal, state: value }, + BuilderSeal::Concealed(seal) => Assign::ConfidentialSeal { seal, state: value }, + }); + let state_data = Confined::try_from_iter(vec_data).expect("at least one element"); + let state_data = TypedAssigns::Structured(AssignVec::with(state_data)); + (id, state_data) + }); + let owned_rights = self.rights.into_iter().map(|(id, vec)| { + let vec_data = vec.into_iter().map(|seal| match seal { + BuilderSeal::Revealed(seal) => Assign::Revealed { + seal, + state: none!(), + }, + BuilderSeal::Concealed(seal) => Assign::ConfidentialSeal { + seal, + state: none!(), + }, + }); + let state_data = Confined::try_from_iter(vec_data).expect("at least one element"); + let state_data = TypedAssigns::Declarative(AssignVec::with(state_data)); + (id, state_data) + }); + + let owned_state = Confined::try_from_iter(owned_state).expect("same size"); + let owned_data = Confined::try_from_iter(owned_data).expect("same size"); + let owned_rights = Confined::try_from_iter(owned_rights).expect("same size"); + + let mut assignments = Assignments::from_inner(owned_state); + assignments + .extend(Assignments::from_inner(owned_data).into_inner()) + .expect("too many assignments"); + assignments + .extend(Assignments::from_inner(owned_rights).into_inner()) + .expect("too many assignments"); + + (self.schema, self.global, assignments, self.types, self.meta) + } +} diff --git a/src/contract/bundle.rs b/src/contract/bundle.rs deleted file mode 100644 index ef9788ca..00000000 --- a/src/contract/bundle.rs +++ /dev/null @@ -1,68 +0,0 @@ -// RGB standard library for working with smart contracts on Bitcoin & Lightning -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2019-2024 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2019-2024 LNP/BP Standards Association. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use rgb::{GraphSeal, OpId, Operation, Transition, TransitionBundle, XChain}; - -use crate::contract::TypedAssignsExt; - -#[derive(Clone, Eq, PartialEq, Debug, Display, Error)] -#[display(doc_comments)] -pub enum RevealError { - /// the provided state transition is not a part of the bundle - UnrelatedTransition(OpId, Transition), -} - -pub trait BundleExt { - /// Ensures that the seal is revealed inside the bundle. - fn reveal_seal(&mut self, seal: XChain); - - /// Ensures that the transition is revealed inside the bundle. - /// - /// # Returns - /// - /// `true` if the transition was previously concealed; `false` if it was - /// already revealed; error if the transition is unrelated to the bundle. - fn reveal_transition(&mut self, transition: Transition) -> Result; -} - -impl BundleExt for TransitionBundle { - fn reveal_seal(&mut self, seal: XChain) { - for (_, transition) in self.known_transitions.keyed_values_mut() { - for (_, assign) in transition.assignments.keyed_values_mut() { - assign.reveal_seal(seal) - } - } - } - - fn reveal_transition(&mut self, transition: Transition) -> Result { - let opid = transition.id(); - if self.input_map.values().all(|id| id != &opid) { - return Err(RevealError::UnrelatedTransition(opid, transition)); - } - if self.known_transitions.contains_key(&opid) { - return Ok(false); - } - self.known_transitions - .insert(opid, transition) - .expect("same size as input map"); - Ok(true) - } -} diff --git a/src/contract/data.rs b/src/contract/data.rs new file mode 100644 index 00000000..750a1d8c --- /dev/null +++ b/src/contract/data.rs @@ -0,0 +1,521 @@ +// RGB standard library for working with smart contracts on Bitcoin & Lightning +// +// SPDX-License-Identifier: Apache-2.0 +// +// Written in 2019-2024 by +// Dr Maxim Orlovsky +// +// Copyright (C) 2019-2024 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::borrow::Borrow; +use std::collections::{BTreeSet, HashMap, HashSet}; + +use bp::Outpoint; +use invoice::{Allocation, Amount}; +use rgb::{ + AssignmentType, ContractId, GlobalStateType, OpId, OutputSeal, RevealedData, RevealedValue, + Schema, Txid, VoidState, +}; +use strict_encoding::{FieldName, StrictDecode, StrictDumb, StrictEncode}; +use strict_types::{StrictVal, TypeSystem}; + +use crate::contract::{AssignmentsFilter, KnownState, OutputAssignment, WitnessInfo}; +use crate::info::ContractInfo; +use crate::persistence::ContractStateRead; +use crate::LIB_NAME_RGB_STD; + +#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)] +#[display(doc_comments)] +pub enum ContractError { + /// field name {0} is unknown to the contract schema + FieldNameUnknown(FieldName), +} + +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display, From)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_RGB_STD, tags = custom)] +#[display(inner)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", rename_all = "camelCase") +)] +pub enum AllocatedState { + #[from(())] + #[from(VoidState)] + #[display("~")] + #[strict_type(tag = 0, dumb)] + Void, + + #[from] + #[from(Amount)] + #[strict_type(tag = 1)] + Amount(RevealedValue), + + #[from] + #[from(Allocation)] + #[strict_type(tag = 2)] + Data(RevealedData), +} + +impl KnownState for AllocatedState { + const IS_FUNGIBLE: bool = false; +} + +impl AllocatedState { + fn unwrap_fungible(&self) -> Amount { + match self { + AllocatedState::Amount(revealed_value) => (*revealed_value).into(), + _ => panic!("unwrapping non-fungible state"), + } + } +} + +pub type OwnedAllocation = OutputAssignment; +pub type RightsAllocation = OutputAssignment; +pub type FungibleAllocation = OutputAssignment; +pub type DataAllocation = OutputAssignment; + +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Display)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", rename_all = "camelCase") +)] +#[display(lowercase)] +pub enum OpDirection { + Issued, + Received, + Sent, +} + +#[derive(Clone, Eq, PartialEq, Hash, Debug)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", rename_all = "camelCase", tag = "type") +)] +pub struct ContractOp { + pub direction: OpDirection, + pub ty: AssignmentType, + pub opids: BTreeSet, + pub state: AllocatedState, + pub to: BTreeSet, + pub witness: Option, +} + +fn reduce_to_ty(allocations: impl IntoIterator) -> AssignmentType { + allocations + .into_iter() + .map(|a| a.opout.ty) + .reduce(|ty1, ty2| { + assert_eq!(ty1, ty2); + ty1 + }) + .expect("empty list of allocations") +} + +impl ContractOp { + fn non_fungible_genesis( + our_allocations: HashSet, + ) -> impl ExactSizeIterator { + our_allocations.into_iter().map(|a| Self { + direction: OpDirection::Issued, + ty: a.opout.ty, + opids: bset![a.opout.op], + state: a.state, + to: bset![a.seal], + witness: None, + }) + } + + fn non_fungible_sent( + witness: WitnessInfo, + ext_allocations: HashSet, + ) -> impl ExactSizeIterator { + ext_allocations.into_iter().map(move |a| Self { + direction: OpDirection::Sent, + ty: a.opout.ty, + opids: bset![a.opout.op], + state: a.state, + to: bset![a.seal], + witness: Some(witness), + }) + } + + fn non_fungible_received( + witness: WitnessInfo, + our_allocations: HashSet, + ) -> impl ExactSizeIterator { + our_allocations.into_iter().map(move |a| Self { + direction: OpDirection::Received, + ty: a.opout.ty, + opids: bset![a.opout.op], + state: a.state, + to: bset![a.seal], + witness: Some(witness), + }) + } + + fn fungible_genesis(our_allocations: HashSet) -> Self { + let to = our_allocations.iter().map(|a| a.seal).collect(); + let opids = our_allocations.iter().map(|a| a.opout.op).collect(); + let issued: Amount = our_allocations + .iter() + .map(|a| a.state.unwrap_fungible()) + .sum(); + Self { + direction: OpDirection::Issued, + ty: reduce_to_ty(our_allocations), + opids, + state: AllocatedState::Amount(issued.into()), + to, + witness: None, + } + } + + fn fungible_sent(witness: WitnessInfo, ext_allocations: HashSet) -> Self { + let opids = ext_allocations.iter().map(|a| a.opout.op).collect(); + let to = ext_allocations.iter().map(|a| a.seal).collect(); + let amount: Amount = ext_allocations + .iter() + .map(|a| a.state.unwrap_fungible()) + .sum(); + Self { + direction: OpDirection::Sent, + ty: reduce_to_ty(ext_allocations), + opids, + state: AllocatedState::Amount(amount.into()), + to, + witness: Some(witness), + } + } + + fn fungible_received(witness: WitnessInfo, our_allocations: HashSet) -> Self { + let opids = our_allocations.iter().map(|a| a.opout.op).collect(); + let to = our_allocations.iter().map(|a| a.seal).collect(); + let amount: Amount = our_allocations + .iter() + .map(|a| a.state.unwrap_fungible()) + .sum(); + Self { + direction: OpDirection::Received, + ty: reduce_to_ty(our_allocations), + opids, + state: AllocatedState::Amount(amount.into()), + to, + witness: Some(witness), + } + } +} + +/// Data of a contract. +#[derive(Clone, Eq, PartialEq, Debug)] +pub struct ContractData { + pub state: S, + pub schema: Schema, + pub types: TypeSystem, + pub info: ContractInfo, +} + +impl ContractData { + pub fn contract_id(&self) -> ContractId { self.state.contract_id() } + + /// # Panics + /// + /// If data is corrupted. + pub fn global(&self, name: impl Into) -> impl Iterator + '_ { + self.global_raw(self.schema.global_type(name)) + } + + /// # Panics + /// + /// If data is corrupted. + pub fn global_raw(&self, type_id: GlobalStateType) -> impl Iterator + '_ { + let global_details = self + .schema + .global_types + .get(&type_id) + .expect("cannot find type ID in schema global types"); + self.state + .global(type_id) + .expect("cannot find type ID in global state") + .map(|data| { + self.types + .strict_deserialize_type( + global_details.global_state_schema.sem_id, + data.borrow().as_slice(), + ) + .expect("unvalidated contract data in stash") + .unbox() + }) + } + + fn extract_state<'c, A, U>( + &'c self, + state: impl IntoIterator> + 'c, + type_id: AssignmentType, + filter: impl AssignmentsFilter + 'c, + ) -> Result> + 'c, ContractError> + where + A: Clone + KnownState + 'c, + U: From + KnownState + 'c, + { + Ok(self + .extract_state_unfiltered(state, type_id)? + .filter(move |outp| filter.should_include(outp.seal, outp.witness))) + } + + fn extract_state_unfiltered<'c, A, U>( + &'c self, + state: impl IntoIterator> + 'c, + type_id: AssignmentType, + ) -> Result> + 'c, ContractError> + where + A: Clone + KnownState + 'c, + U: From + KnownState + 'c, + { + Ok(state + .into_iter() + .filter(move |outp| outp.opout.ty == type_id) + .cloned() + .map(OutputAssignment::::transmute)) + } + + pub fn rights<'c>( + &'c self, + name: impl Into, + filter: impl AssignmentsFilter + 'c, + ) -> Result + 'c, ContractError> { + let type_id = self.schema.assignment_type(name); + self.rights_raw(type_id, filter) + } + + pub fn rights_raw<'c>( + &'c self, + type_id: AssignmentType, + filter: impl AssignmentsFilter + 'c, + ) -> Result + 'c, ContractError> { + self.extract_state(self.state.rights_all(), type_id, filter) + } + + pub fn fungible<'c>( + &'c self, + name: impl Into, + filter: impl AssignmentsFilter + 'c, + ) -> Result + 'c, ContractError> { + let type_id = self.schema.assignment_type(name); + self.fungible_raw(type_id, filter) + } + + pub fn fungible_raw<'c>( + &'c self, + type_id: AssignmentType, + filter: impl AssignmentsFilter + 'c, + ) -> Result + 'c, ContractError> { + self.extract_state(self.state.fungible_all(), type_id, filter) + } + + pub fn data<'c>( + &'c self, + name: impl Into, + filter: impl AssignmentsFilter + 'c, + ) -> Result + 'c, ContractError> { + let type_id = self.schema.assignment_type(name); + self.data_raw(type_id, filter) + } + + pub fn data_raw<'c>( + &'c self, + type_id: AssignmentType, + filter: impl AssignmentsFilter + 'c, + ) -> Result + 'c, ContractError> { + self.extract_state(self.state.data_all(), type_id, filter) + } + + pub fn allocations<'c>( + &'c self, + filter: impl AssignmentsFilter + Copy + 'c, + ) -> impl Iterator + 'c { + fn f<'a, S, U>( + filter: impl AssignmentsFilter + 'a, + state: impl IntoIterator> + 'a, + ) -> impl Iterator> + 'a + where + S: Clone + KnownState + 'a, + U: From + KnownState + 'a, + { + state + .into_iter() + .filter(move |outp| filter.should_include(outp.seal, outp.witness)) + .cloned() + .map(OutputAssignment::::transmute) + } + + f(filter, self.state.rights_all()) + .chain(f(filter, self.state.fungible_all())) + .chain(f(filter, self.state.data_all())) + } + + pub fn outpoint_allocations( + &self, + outpoint: Outpoint, + ) -> impl Iterator + '_ { + self.allocations(outpoint) + } + + pub fn history( + &self, + filter_outpoints: impl AssignmentsFilter + Clone, + filter_witnesses: impl AssignmentsFilter + Clone, + ) -> Vec { + self.history_fungible(filter_outpoints.clone(), filter_witnesses.clone()) + .into_iter() + .chain(self.history_rights(filter_outpoints.clone(), filter_witnesses.clone())) + .chain(self.history_data(filter_outpoints.clone(), filter_witnesses.clone())) + .collect() + } + + fn operations<'c, T: KnownState + 'c, I: Iterator>>( + &'c self, + state: impl Fn(&'c S) -> I, + filter_outpoints: impl AssignmentsFilter, + filter_witnesses: impl AssignmentsFilter, + ) -> Vec + where + AllocatedState: From, + { + // get all allocations which ever belonged to this wallet and store them by witness id + let mut allocations_our_outpoint = state(&self.state) + .filter(move |outp| filter_outpoints.should_include(outp.seal, outp.witness)) + .fold(HashMap::<_, HashSet<_>>::new(), |mut map, a| { + map.entry(a.witness) + .or_default() + .insert(a.clone().transmute::()); + map + }); + // get all allocations which has a witness transaction belonging to this wallet + let mut allocations_our_witness = state(&self.state) + .filter(move |outp| filter_witnesses.should_include(outp.seal, outp.witness)) + .fold(HashMap::<_, HashSet<_>>::new(), |mut map, a| { + let witness = a.witness.expect( + "all empty witnesses must be already filtered out by wallet.filter_witness()", + ); + map.entry(witness) + .or_default() + .insert(a.clone().transmute::()); + map + }); + + // gather all witnesses from both sets + let mut witness_ids = allocations_our_witness + .keys() + .cloned() + .collect::>(); + witness_ids.extend(allocations_our_outpoint.keys().filter_map(|x| *x)); + + // reconstruct contract history from the wallet perspective + let mut ops = Vec::with_capacity(witness_ids.len() + 1); + // add allocations with no witness to the beginning of the history + if let Some(genesis_allocations) = allocations_our_outpoint.remove(&None) { + if T::IS_FUNGIBLE { + ops.push(ContractOp::fungible_genesis(genesis_allocations)); + } else { + ops.extend(ContractOp::non_fungible_genesis(genesis_allocations)); + } + } + for witness_id in witness_ids { + let our_outpoint = allocations_our_outpoint.remove(&Some(witness_id)); + let our_witness = allocations_our_witness.remove(&witness_id); + let witness_info = self.witness_info(witness_id).expect( + "witness id was returned from the contract state above, so it must be there", + ); + match (our_outpoint, our_witness) { + // we own both allocation and witness transaction: these allocations are changes and + // outgoing payments. The difference between the change and the payments are whether + // a specific allocation is listed in the first tuple pattern field. + (Some(our_allocations), Some(all_allocations)) => { + // all_allocations - our_allocations = external payments + let ext_allocations = all_allocations + .difference(&our_allocations) + .cloned() + .collect::>(); + // This was an extra state transition with no external payment + if ext_allocations.is_empty() { + continue; + } + if T::IS_FUNGIBLE { + ops.push(ContractOp::fungible_sent(witness_info, ext_allocations)) + } else { + ops.extend(ContractOp::non_fungible_sent(witness_info, ext_allocations)) + } + } + // the same as above, but the payment has no change + (None, Some(ext_allocations)) => { + if T::IS_FUNGIBLE { + ops.push(ContractOp::fungible_sent(witness_info, ext_allocations)) + } else { + ops.extend(ContractOp::non_fungible_sent(witness_info, ext_allocations)) + } + } + // we own allocation but the witness transaction was made by other wallet: + // this is an incoming payment to us. + (Some(our_allocations), None) => { + if T::IS_FUNGIBLE { + ops.push(ContractOp::fungible_received(witness_info, our_allocations)) + } else { + ops.extend(ContractOp::non_fungible_received(witness_info, our_allocations)) + } + } + // these can't get into the `witness_ids` due to the used filters + (None, None) => unreachable!("broken allocation filters"), + }; + } + + ops + } + + pub fn history_fungible( + &self, + filter_outpoints: impl AssignmentsFilter, + filter_witnesses: impl AssignmentsFilter, + ) -> Vec { + self.operations(|state| state.fungible_all(), filter_outpoints, filter_witnesses) + } + + pub fn history_rights( + &self, + filter_outpoints: impl AssignmentsFilter, + filter_witnesses: impl AssignmentsFilter, + ) -> Vec { + self.operations(|state| state.rights_all(), filter_outpoints, filter_witnesses) + } + + pub fn history_data( + &self, + filter_outpoints: impl AssignmentsFilter, + filter_witnesses: impl AssignmentsFilter, + ) -> Vec { + self.operations(|state| state.data_all(), filter_outpoints, filter_witnesses) + } + + pub fn witness_info(&self, witness_id: Txid) -> Option { + let ord = self.state.witness_ord(witness_id)?; + Some(WitnessInfo { + id: witness_id, + ord, + }) + } +} diff --git a/src/contract/filter.rs b/src/contract/filter.rs new file mode 100644 index 00000000..55cd28ca --- /dev/null +++ b/src/contract/filter.rs @@ -0,0 +1,112 @@ +// RGB standard library for working with smart contracts on Bitcoin & Lightning +// +// SPDX-License-Identifier: Apache-2.0 +// +// Written in 2019-2024 by +// Dr Maxim Orlovsky +// +// Copyright (C) 2019-2024 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; +use std::ops::Deref; + +use bp::{Outpoint, Txid}; + +pub trait AssignmentsFilter { + fn should_include(&self, outpoint: impl Into, witness_id: Option) -> bool; +} + +pub struct FilterIncludeAll; +pub struct FilterExclude(pub T); + +impl AssignmentsFilter for FilterIncludeAll { + fn should_include(&self, _: impl Into, _: Option) -> bool { true } +} + +impl AssignmentsFilter for FilterExclude { + fn should_include(&self, outpoint: impl Into, witness_id: Option) -> bool { + !self.0.should_include(outpoint.into(), witness_id) + } +} + +impl AssignmentsFilter for &T { + fn should_include(&self, outpoint: impl Into, witness_id: Option) -> bool { + (*self).should_include(outpoint, witness_id) + } +} + +impl AssignmentsFilter for &mut T { + fn should_include(&self, outpoint: impl Into, witness_id: Option) -> bool { + self.deref().should_include(outpoint, witness_id) + } +} + +impl AssignmentsFilter for Option { + fn should_include(&self, outpoint: impl Into, witness_id: Option) -> bool { + self.as_ref() + .map(|filter| filter.should_include(outpoint, witness_id)) + .unwrap_or(true) + } +} + +impl AssignmentsFilter for Outpoint { + fn should_include(&self, outpoint: impl Into, _: Option) -> bool { + *self == outpoint.into() + } +} + +impl AssignmentsFilter for [Outpoint; LEN] { + fn should_include(&self, outpoint: impl Into, _: Option) -> bool { + self.contains(&outpoint.into()) + } +} + +impl AssignmentsFilter for &[Outpoint] { + fn should_include(&self, outpoint: impl Into, _: Option) -> bool { + self.contains(&outpoint.into()) + } +} + +impl AssignmentsFilter for Vec { + fn should_include(&self, outpoint: impl Into, _: Option) -> bool { + self.contains(&outpoint.into()) + } +} + +impl AssignmentsFilter for HashSet { + fn should_include(&self, outpoint: impl Into, _: Option) -> bool { + self.contains(&outpoint.into()) + } +} + +impl AssignmentsFilter for BTreeSet { + fn should_include(&self, outpoint: impl Into, _: Option) -> bool { + self.contains(&outpoint.into()) + } +} + +impl AssignmentsFilter for HashMap { + fn should_include(&self, outpoint: impl Into, _: Option) -> bool { + let outpoint = outpoint.into(); + self.keys().any(|o| *o == outpoint) + } +} + +impl AssignmentsFilter for BTreeMap { + fn should_include(&self, outpoint: impl Into, _: Option) -> bool { + let outpoint = outpoint.into(); + self.keys().any(|o| *o == outpoint) + } +} diff --git a/src/contract/merge_reveal.rs b/src/contract/merge_reveal.rs index b662f5a5..3d4a64e0 100644 --- a/src/contract/merge_reveal.rs +++ b/src/contract/merge_reveal.rs @@ -19,25 +19,28 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::collections::BTreeMap; - use amplify::confinement::Confined; use amplify::Wrapper; use bp::Txid; use commit_verify::{mpc, Conceal}; use rgb::{ - Assign, Assignments, BundleId, ExposedSeal, ExposedState, Extension, Genesis, OpId, Operation, - Transition, TransitionBundle, TypedAssigns, + Assign, Assignments, BundleId, ExposedSeal, ExposedState, Genesis, OpId, Operation, Transition, + TransitionBundle, TypedAssigns, }; #[derive(Copy, Clone, Eq, PartialEq, Debug, Display, Error, From)] #[display(doc_comments)] pub enum MergeRevealError { - /// operations {0} and {1} has different commitment ids and can't be + /// operations {0} and {1} have different commitment ids and can't be /// merge-revealed. This usually means internal application business logic /// error which should be reported to the software vendor. OperationMismatch(OpId, OpId), + /// operations with ID {0} have different signatures and can't be merge-revealed. + /// This usually means internal application business logic + /// error which should be reported to the software vendor. + SignatureMismatch(OpId), + /// mismatch in anchor chains: one grip references bitcoin transaction /// {bitcoin} and the other merged part references liquid transaction /// {liquid}. @@ -49,8 +52,8 @@ pub enum MergeRevealError { /// anchors in anchored bundle are not equal for bundle {0}. AnchorsNonEqual(BundleId), - /// the merged bundles contain more transitions than inputs. - InsufficientInputs, + /// assignments have different keys. + AssignmentsDifferentKeys, /// contract id provided for the merge-reveal operation doesn't match /// multiprotocol commitment. @@ -74,188 +77,80 @@ pub enum MergeRevealError { /// merge(ConfidentialSeal, ConfidentialAmount) => Revealed /// merge(ConfidentialAmount, ConfidentialSeal) => Revealed /// merge(Confidential, Anything) => Anything -pub trait MergeReveal: Sized { - // TODO: Take self by mut ref instead of consuming (will remove clones in - // Stash::consume operation). - fn merge_reveal(self, other: Self) -> Result; -} - -/* -pub trait MergeRevealContract: Sized { - fn merge_reveal_contract( - self, - other: Self, - contract_id: ContractId, - ) -> Result; +pub trait MergeReveal { + fn merge_reveal(&mut self, other: &Self) -> Result<(), MergeRevealError>; } - */ impl MergeReveal for Assign { - fn merge_reveal(self, other: Self) -> Result { + fn merge_reveal(&mut self, other: &Self) -> Result<(), MergeRevealError> { debug_assert_eq!(self.conceal(), other.conceal()); - match (self, other) { - // Anything + Revealed = Revealed - (_, state @ Assign::Revealed { .. }) | (state @ Assign::Revealed { .. }, _) => { - Ok(state) - } - - // ConfidentialAmount + ConfidentialSeal = Revealed - ( - Assign::ConfidentialSeal { - state, lock: lock1, .. - }, - Assign::ConfidentialState { - seal, lock: lock2, .. - }, - ) => { - debug_assert_eq!(lock1, lock2); - Ok(Assign::Revealed { - seal, - state, - lock: lock1, - }) - } - - // ConfidentialSeal + ConfidentialAmount = Revealed - ( - Assign::ConfidentialState { - seal, lock: lock1, .. - }, - Assign::ConfidentialSeal { - state, lock: lock2, .. - }, - ) => { - debug_assert_eq!(lock1, lock2); - Ok(Assign::Revealed { - seal, - state, - lock: lock1, - }) - } - - // if self and other is of same variant return self - (state @ Assign::ConfidentialState { .. }, Assign::ConfidentialState { .. }) => { - Ok(state) - } - (state @ Assign::ConfidentialSeal { .. }, Assign::ConfidentialSeal { .. }) => Ok(state), - - // Anything + Confidential = Anything - (state, Assign::Confidential { .. }) | (Assign::Confidential { .. }, state) => { - Ok(state) - } + // Anything + Revealed = Revealed + if let Assign::Revealed { .. } = other { + *self = other.clone(); } + Ok(()) } } impl MergeReveal for TypedAssigns { - fn merge_reveal(self, other: Self) -> Result { + fn merge_reveal(&mut self, other: &Self) -> Result<(), MergeRevealError> { match (self, other) { (TypedAssigns::Declarative(first_vec), TypedAssigns::Declarative(second_vec)) => { - let mut result = Vec::with_capacity(first_vec.len()); - for (first, second) in first_vec.into_iter().zip(second_vec.into_iter()) { - result.push(first.merge_reveal(second)?); + for (first, second) in first_vec.iter_mut().zip(second_vec.as_ref()) { + first.merge_reveal(second)?; } - Ok(TypedAssigns::Declarative( - Confined::try_from(result).expect("collection of the same size"), - )) } (TypedAssigns::Fungible(first_vec), TypedAssigns::Fungible(second_vec)) => { - let mut result = Vec::with_capacity(first_vec.len()); - for (first, second) in first_vec.into_iter().zip(second_vec.into_iter()) { - result.push(first.merge_reveal(second)?); + for (first, second) in first_vec.iter_mut().zip(second_vec.as_ref()) { + first.merge_reveal(second)?; } - Ok(TypedAssigns::Fungible( - Confined::try_from(result).expect("collection of the same size"), - )) } (TypedAssigns::Structured(first_vec), TypedAssigns::Structured(second_vec)) => { - let mut result = Vec::with_capacity(first_vec.len()); - for (first, second) in first_vec.into_iter().zip(second_vec.into_iter()) { - result.push(first.merge_reveal(second)?); + for (first, second) in first_vec.iter_mut().zip(second_vec.as_ref()) { + first.merge_reveal(second)?; } - Ok(TypedAssigns::Structured( - Confined::try_from(result).expect("collection of the same size"), - )) } - (TypedAssigns::Attachment(first_vec), TypedAssigns::Attachment(second_vec)) => { - let mut result = Vec::with_capacity(first_vec.len()); - for (first, second) in first_vec.into_iter().zip(second_vec.into_iter()) { - result.push(first.merge_reveal(second)?); - } - Ok(TypedAssigns::Attachment( - Confined::try_from(result).expect("collection of the same size"), - )) - } // No other patterns possible, should not reach here _ => { unreachable!("Assignments::consensus_commitments is broken") } - } + }; + Ok(()) } } impl MergeReveal for Assignments { - fn merge_reveal(self, other: Self) -> Result { - let mut result = BTreeMap::new(); - for (first, second) in self - .into_inner() - .into_iter() - .zip(other.into_inner().into_iter()) - { - debug_assert_eq!(first.0, second.0); - result.insert(first.0, first.1.merge_reveal(second.1)?); + fn merge_reveal(&mut self, other: &Self) -> Result<(), MergeRevealError> { + for (ass_type, other_typed_assigns) in other.as_inner().iter() { + let typed_assigns = self + .get_mut(ass_type) + .ok_or(MergeRevealError::AssignmentsDifferentKeys)?; + typed_assigns.merge_reveal(other_typed_assigns)?; } - Ok(Assignments::from_inner( - Confined::try_from(result).expect("collection of the same size"), - )) + Ok(()) } } impl MergeReveal for TransitionBundle { - fn merge_reveal(mut self, other: Self) -> Result { + fn merge_reveal(&mut self, other: &Self) -> Result<(), MergeRevealError> { debug_assert_eq!(self.bundle_id(), other.bundle_id()); - let mut self_transitions = self.known_transitions.release(); - for (opid, other_transition) in other.known_transitions { - if let Some(mut transition) = self_transitions.remove(&opid) { - transition = transition.merge_reveal(other_transition)?; - self_transitions.insert(opid, transition); + let mut self_transitions = self.known_transitions.to_unconfined(); + for (opid, other_transition) in &other.known_transitions { + if let Some(transition) = self_transitions.get_mut(opid) { + transition.merge_reveal(other_transition)?; } } self.known_transitions = Confined::from_checked(self_transitions); - - if self.input_map.len() < self.known_transitions.len() { - return Err(MergeRevealError::InsufficientInputs); - } - Ok(self) - } -} - -/* -impl MergeRevealContract for AnchoredBundle { - fn merge_reveal_contract( - self, - other: Self, - contract_id: ContractId, - ) -> Result { - let bundle_id = self.bundle_id(); - let anchor1 = self.anchor.into_merkle_block(contract_id, bundle_id)?; - let anchor2 = other.anchor.into_merkle_block(contract_id, bundle_id)?; - Ok(AnchoredBundle { - anchor: anchor1 - .merge_reveal(anchor2)? - .into_merkle_proof(contract_id)?, - bundle: self.bundle.merge_reveal(other.bundle)?, - }) + Ok(()) } } - */ impl MergeReveal for Genesis { - fn merge_reveal(mut self, other: Self) -> Result { + fn merge_reveal(&mut self, other: &Self) -> Result<(), MergeRevealError> { let self_id = self.id(); let other_id = other.id(); if self_id != other_id { @@ -264,31 +159,34 @@ impl MergeReveal for Genesis { OpId::from_inner(other_id.into_inner()), )); } - self.assignments = self.assignments.merge_reveal(other.assignments)?; - Ok(self) + self.assignments.merge_reveal(&other.assignments)?; + Ok(()) } } impl MergeReveal for Transition { - fn merge_reveal(mut self, other: Self) -> Result { - let self_id = self.id(); - let other_id = other.id(); - if self_id != other_id { - return Err(MergeRevealError::OperationMismatch(self_id, other_id)); - } - self.assignments = self.assignments.merge_reveal(other.assignments)?; - Ok(self) - } -} - -impl MergeReveal for Extension { - fn merge_reveal(mut self, other: Self) -> Result { + fn merge_reveal(&mut self, other: &Self) -> Result<(), MergeRevealError> { let self_id = self.id(); let other_id = other.id(); if self_id != other_id { return Err(MergeRevealError::OperationMismatch(self_id, other_id)); } - self.assignments = self.assignments.merge_reveal(other.assignments)?; - Ok(self) + self.assignments.merge_reveal(&other.assignments)?; + match (self.signature.take(), other.signature.as_ref()) { + (None, None) => {} + (Some(sig), None) => { + self.signature = Some(sig); + } + (None, Some(sig)) => { + self.signature = Some(sig.clone()); + } + (Some(sig1), Some(sig2)) if sig1 == *sig2 => { + self.signature = Some(sig1); + } + _ => { + return Err(MergeRevealError::SignatureMismatch(self_id)); + } + }; + Ok(()) } } diff --git a/src/contract/mod.rs b/src/contract/mod.rs index b7905e89..067833f5 100644 --- a/src/contract/mod.rs +++ b/src/contract/mod.rs @@ -20,14 +20,24 @@ // limitations under the License. mod assignments; -mod bundle; +mod builder; +mod data; +mod filter; mod merge_reveal; +mod schema; +pub(crate) mod resolver; -pub use assignments::{KnownState, OutputAssignment, TypedAssignsExt}; -pub use bundle::{BundleExt, RevealError}; +pub use assignments::{KnownState, OutputAssignment, WitnessInfo}; +pub use builder::{BuilderError, ContractBuilder, TransitionBuilder}; +pub use data::{ + AllocatedState, ContractData, ContractError, ContractOp, DataAllocation, FungibleAllocation, + OpDirection, OwnedAllocation, RightsAllocation, +}; +pub use filter::{AssignmentsFilter, FilterExclude, FilterIncludeAll}; pub use merge_reveal::{MergeReveal, MergeRevealError}; use rgb::vm::OrdOpRef; -use rgb::{ExtensionType, OpId, TransitionType, XWitnessId}; +use rgb::{OpId, TransitionType, Txid}; +pub use schema::{IssuerWrapper, SchemaWrapper}; use crate::LIB_NAME_RGB_STD; @@ -42,8 +52,7 @@ use crate::LIB_NAME_RGB_STD; pub enum OpWitness { #[strict_type(dumb)] Genesis, - Transition(XWitnessId, TransitionType), - Extension(XWitnessId, ExtensionType), + Transition(Txid, TransitionType), } impl From> for OpWitness { @@ -53,21 +62,16 @@ impl From> for OpWitness { OrdOpRef::Transition(op, witness_id, ..) => { OpWitness::Transition(witness_id, op.transition_type) } - OrdOpRef::Extension(op, witness_id, ..) => { - OpWitness::Extension(witness_id, op.extension_type) - } } } } impl OpWitness { #[inline] - pub fn witness_id(&self) -> Option { + pub fn witness_id(&self) -> Option { match self { OpWitness::Genesis => None, - OpWitness::Transition(witness_id, _) | OpWitness::Extension(witness_id, _) => { - Some(*witness_id) - } + OpWitness::Transition(witness_id, _) => Some(*witness_id), } } } @@ -81,13 +85,13 @@ impl OpWitness { serde(crate = "serde_crate", rename_all = "camelCase") )] pub struct GlobalOut { - pub opid: OpId, - pub nonce: u64, pub index: u16, pub op_witness: OpWitness, + pub nonce: u64, + pub opid: OpId, } impl GlobalOut { #[inline] - pub fn witness_id(&self) -> Option { self.op_witness.witness_id() } + pub fn witness_id(&self) -> Option { self.op_witness.witness_id() } } diff --git a/src/interface/resolver.rs b/src/contract/resolver.rs similarity index 74% rename from src/interface/resolver.rs rename to src/contract/resolver.rs index 7f563820..d396ec80 100644 --- a/src/interface/resolver.rs +++ b/src/contract/resolver.rs @@ -19,20 +19,22 @@ // See the License for the specific language governing permissions and // limitations under the License. +use bp::{Tx, Txid}; use rgb::validation::{ResolveWitness, WitnessResolverError}; -use rgb::vm::{WitnessOrd, XWitnessTx}; +use rgb::vm::WitnessOrd; +use rgb::ChainNet; use strict_encoding::StrictDumb; -use crate::XWitnessId; - pub(crate) struct DumbResolver; impl ResolveWitness for DumbResolver { - fn resolve_pub_witness(&self, _: XWitnessId) -> Result { - Ok(XWitnessTx::strict_dumb()) + fn resolve_pub_witness(&self, _: Txid) -> Result { + Ok(Tx::strict_dumb()) } - fn resolve_pub_witness_ord(&self, _: XWitnessId) -> Result { + fn resolve_pub_witness_ord(&self, _: Txid) -> Result { Ok(WitnessOrd::strict_dumb()) } + + fn check_chain_net(&self, _: ChainNet) -> Result<(), WitnessResolverError> { Ok(()) } } diff --git a/src/contract/schema.rs b/src/contract/schema.rs new file mode 100644 index 00000000..18206c15 --- /dev/null +++ b/src/contract/schema.rs @@ -0,0 +1,53 @@ +// RGB standard library for working with smart contracts on Bitcoin & Lightning +// +// SPDX-License-Identifier: Apache-2.0 +// +// Written in 2019-2024 by +// Dr Maxim Orlovsky +// +// Copyright (C) 2019-2024 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use amplify::confinement::Confined; +use strict_types::TypeSystem; + +use crate::containers::{ContainerVer, Kit, ValidKit}; +use crate::contract::ContractData; +use crate::persistence::ContractStateRead; +use crate::validation::Scripts; +use crate::Schema; + +/// The instances implementing this trait are used as wrappers around [`ContractData`] object, +/// allowing a simple API matching the schema requirements. +pub trait SchemaWrapper { + fn with(data: ContractData) -> Self; +} + +pub trait IssuerWrapper { + type Wrapper: SchemaWrapper; + + fn schema() -> Schema; + fn types() -> TypeSystem; + fn scripts() -> Scripts; + + fn kit() -> ValidKit { + let kit = Kit { + version: ContainerVer::V0, + schemata: tiny_bset![Self::schema()], + types: Self::types(), + scripts: Confined::from_iter_checked(Self::scripts().release().into_values()), + }; + kit.validate().expect("invalid construction") + } +} diff --git a/src/indexers/any.rs b/src/indexers/any.rs new file mode 100644 index 00000000..9526d40d --- /dev/null +++ b/src/indexers/any.rs @@ -0,0 +1,128 @@ +// RGB standard library for working with smart contracts on Bitcoin & Lightning +// +// SPDX-License-Identifier: Apache-2.0 +// +// Written in 2024 by +// Zoe Faltibà +// Rewritten in 2024 by +// Dr Maxim Orlovsky +// +// Copyright (C) 2024 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::collections::HashMap; + +use bp::{Tx, Txid}; +use rgbcore::validation::{ResolveWitness, WitnessResolverError}; +use rgbcore::vm::WitnessOrd; +use rgbcore::ChainNet; + +use crate::containers::Consignment; + +// We need to repeat methods of `WitnessResolve` trait here to avoid making +// wrappers around resolver types. TODO: Use wrappers instead +pub trait RgbResolver: Send { + fn check_chain_net(&self, chain_net: ChainNet) -> Result<(), String>; + fn resolve_pub_witness(&self, txid: Txid) -> Result, String>; + fn resolve_pub_witness_ord(&self, txid: Txid) -> Result; +} + +/// Type that contains any of the [`Resolver`] types defined by the library +#[derive(From)] +#[non_exhaustive] +pub struct AnyResolver { + inner: Box, + consignment_txes: HashMap, +} + +impl AnyResolver { + #[cfg(feature = "electrum_blocking")] + pub fn electrum_blocking(url: &str, config: Option) -> Result { + Ok(AnyResolver { + inner: Box::new( + electrum::Client::from_config(url, config.unwrap_or_default()) + .map_err(|e| e.to_string())?, + ), + consignment_txes: Default::default(), + }) + } + + #[cfg(feature = "esplora_blocking")] + pub fn esplora_blocking(url: &str, config: Option) -> Result { + Ok(AnyResolver { + inner: Box::new( + esplora::BlockingClient::from_config(url, config.unwrap_or_default()) + .map_err(|e| e.to_string())?, + ), + consignment_txes: Default::default(), + }) + } + + #[cfg(feature = "mempool_blocking")] + pub fn mempool_blocking(url: &str, config: Option) -> Result { + Ok(AnyResolver { + inner: Box::new(super::mempool_blocking::MemPoolClient::new( + url, + config.unwrap_or_default(), + )?), + consignment_txes: Default::default(), + }) + } + + /// Add to the resolver the TXs found in the consignment bundles. Those TXs + /// will not be resolved by an indexer and will be considered tentative. + /// Use with caution, this could allow accepting a consignment containing TXs that have not + /// been broadcasted. + pub fn add_consignment_txes(&mut self, consignment: &Consignment) { + self.consignment_txes.extend( + consignment + .bundles + .iter() + .filter_map(|bw| bw.pub_witness.tx().cloned()) + .map(|tx| (tx.txid(), tx)), + ); + } +} + +impl ResolveWitness for AnyResolver { + fn resolve_pub_witness(&self, witness_id: Txid) -> Result { + if let Some(tx) = self.consignment_txes.get(&witness_id) { + return Ok(tx.clone()); + } + + self.inner + .resolve_pub_witness(witness_id) + .map_err(|e| WitnessResolverError::Other(witness_id, e)) + .and_then(|r| r.ok_or(WitnessResolverError::Unknown(witness_id))) + } + + fn resolve_pub_witness_ord( + &self, + witness_id: Txid, + ) -> Result { + if self.consignment_txes.contains_key(&witness_id) { + return Ok(WitnessOrd::Tentative); + } + + self.inner + .resolve_pub_witness_ord(witness_id) + .map_err(|e| WitnessResolverError::Other(witness_id, e)) + } + + fn check_chain_net(&self, chain_net: ChainNet) -> Result<(), WitnessResolverError> { + self.inner + .check_chain_net(chain_net) + .map_err(|_| WitnessResolverError::WrongChainNet) + } +} diff --git a/src/indexers/electrum_blocking.rs b/src/indexers/electrum_blocking.rs new file mode 100644 index 00000000..b0181ed5 --- /dev/null +++ b/src/indexers/electrum_blocking.rs @@ -0,0 +1,158 @@ +// RGB standard library for working with smart contracts on Bitcoin & Lightning +// +// SPDX-License-Identifier: Apache-2.0 +// +// Written in 2024 by +// Zoe Faltibà +// Rewritten in 2024 by +// Dr Maxim Orlovsky +// +// Copyright (C) 2024 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::iter; +use std::num::NonZeroU32; + +use bp::{ConsensusDecode, Tx, Txid}; +use electrum::{Client, ElectrumApi, Param}; +pub use electrum::{Config, ConfigBuilder, Error, Socks5Config}; +use rgbcore::vm::{WitnessOrd, WitnessPos}; +use rgbcore::ChainNet; + +use super::RgbResolver; + +macro_rules! check { + ($e:expr) => { + $e.map_err(|e| e.to_string())? + }; +} + +impl RgbResolver for Client { + fn check_chain_net(&self, chain_net: ChainNet) -> Result<(), String> { + // check the electrum server is for the correct network + let block_hash = check!(self.block_header(0)).block_hash(); + if chain_net.genesis_block_hash() != block_hash { + return Err(s!("resolver is for a network different from the wallet's one")); + } + // check the electrum server has the required functionality (verbose + // transactions) + let txid = match chain_net { + ChainNet::BitcoinMainnet => { + "33e794d097969002ee05d336686fc03c9e15a597c1b9827669460fac98799036" + } + ChainNet::BitcoinTestnet3 => { + "5e6560fd518aadbed67ee4a55bdc09f19e619544f5511e9343ebba66d2f62653" + } + ChainNet::BitcoinTestnet4 => { + "7aa0a7ae1e223414cb807e40cd57e667b718e42aaf9306db9102fe28912b7b4e" + } + ChainNet::BitcoinSignet => { + "8153034f45e695453250a8fb7225a5e545144071d8ed7b0d3211efa1f3c92ad8" + } + ChainNet::BitcoinRegtest => { + "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b" + } + _ => return Err(s!("only bitcoin is supported")), + }; + if let Err(e) = self.raw_call("blockchain.transaction.get", vec![ + Param::String(txid.to_string()), + Param::Bool(true), + ]) { + if !e + .to_string() + .contains("genesis block coinbase is not considered an ordinary transaction") + { + return Err(s!( + "verbose transactions are unsupported by the provided electrum service" + )); + } + } + Ok(()) + } + + fn resolve_pub_witness_ord(&self, txid: Txid) -> Result { + // We get the height of the tip of blockchain + let header = check!(self.block_headers_subscribe()); + + // Now we get and parse transaction information to get the number of + // confirmations + let tx_details = match self.raw_call("blockchain.transaction.get", vec![ + Param::String(txid.to_string()), + Param::Bool(true), + ]) { + Err(e) + if e.to_string() + .contains("No such mempool or blockchain transaction") => + { + return Ok(WitnessOrd::Archived); + } + Err(e) => return Err(e.to_string()), + Ok(v) => v, + }; + let forward = iter::from_fn(|| self.block_headers_pop().ok().flatten()).count() as isize; + + let Some(confirmations) = tx_details.get("confirmations") else { + return Ok(WitnessOrd::Tentative); + }; + let confirmations = check!(confirmations + .as_u64() + .and_then(|x| u32::try_from(x).ok()) + .ok_or(Error::InvalidResponse(tx_details.clone()))); + if confirmations == 0 { + return Ok(WitnessOrd::Tentative); + } + let block_time = check!(tx_details + .get("blocktime") + .and_then(|v| v.as_i64()) + .ok_or(Error::InvalidResponse(tx_details.clone()))); + + let tip_height = u32::try_from(header.height).map_err(|_| s!("impossible height value"))?; + let height: isize = (tip_height - confirmations) as isize; + const SAFETY_MARGIN: isize = 1; + // first check from expected min to max height + let get_merkle_res = (1..=forward + 1) + // we need this under assumption that electrum was lying due to "DB desynchronization" + // since this have a very low probability we do that after everything else + .chain((1..=SAFETY_MARGIN).flat_map(|i| [i + forward + 1, 1 - i])) + .find_map(|offset| self.transaction_get_merkle(&txid, (height + offset) as usize).ok()) + .ok_or_else(|| s!("transaction can't be located in the blockchain"))?; + + let tx_height = u32::try_from(get_merkle_res.block_height) + .map_err(|_| s!("impossible height value"))?; + + let height = + check!(NonZeroU32::new(tx_height).ok_or(Error::InvalidResponse(tx_details.clone()))); + let pos = check!(WitnessPos::bitcoin(height, block_time) + .ok_or(Error::InvalidResponse(tx_details.clone()))); + + Ok(WitnessOrd::Mined(pos)) + } + + fn resolve_pub_witness(&self, txid: Txid) -> Result, String> { + self.transaction_get_raw(&txid) + .map_err(|e| e.to_string()) + .and_then(|raw_tx| { + Tx::consensus_deserialize(raw_tx) + .map_err(|e| format!("cannot deserialize raw TX - {e}")) + }) + .map(Some) + .or_else(|e| { + if e.contains("No such mempool or blockchain transaction") { + Ok(None) + } else { + Err(e) + } + }) + } +} diff --git a/src/indexers/esplora_blocking.rs b/src/indexers/esplora_blocking.rs new file mode 100644 index 00000000..8b7d0046 --- /dev/null +++ b/src/indexers/esplora_blocking.rs @@ -0,0 +1,68 @@ +// RGB standard library for working with smart contracts on Bitcoin & Lightning +// +// SPDX-License-Identifier: Apache-2.0 +// +// Written in 2019-2023 by +// Dr Maxim Orlovsky +// +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::num::NonZeroU32; + +use bp::{Tx, Txid}; +use esplora::BlockingClient; +pub use esplora::{Builder, Config, Error}; +use rgbcore::vm::{WitnessOrd, WitnessPos}; +use rgbcore::ChainNet; + +use super::RgbResolver; + +impl RgbResolver for BlockingClient { + fn check_chain_net(&self, chain_net: ChainNet) -> Result<(), String> { + // check the esplora server is for the correct network + let block_hash = self.block_hash(0)?; + if chain_net.genesis_block_hash() != block_hash { + return Err(s!("resolver is for a network different from the wallet's one")); + } + Ok(()) + } + + fn resolve_pub_witness_ord(&self, txid: Txid) -> Result { + if self.tx(&txid)?.is_none() { + return Ok(WitnessOrd::Archived); + } + let status = self.tx_status(&txid)?; + let ord = match status + .block_height + .and_then(|h| status.block_time.map(|t| (h, t))) + { + Some((h, t)) => { + let height = NonZeroU32::new(h).ok_or(Error::InvalidServerData)?; + WitnessOrd::Mined( + WitnessPos::bitcoin(height, t as i64).ok_or(Error::InvalidServerData)?, + ) + } + None => WitnessOrd::Tentative, + }; + Ok(ord) + } + + fn resolve_pub_witness(&self, txid: Txid) -> Result, String> { + self.tx(&txid).or_else(|e| match e { + Error::TransactionNotFound(_) => Ok(None), + e => Err(e.to_string()), + }) + } +} diff --git a/src/indexers/mempool_blocking.rs b/src/indexers/mempool_blocking.rs new file mode 100644 index 00000000..279e18f6 --- /dev/null +++ b/src/indexers/mempool_blocking.rs @@ -0,0 +1,131 @@ +// RGB standard library for working with smart contracts on Bitcoin & Lightning +// +// SPDX-License-Identifier: Apache-2.0 +// +// Written in 2019-2023 by +// Dr Maxim Orlovsky +// +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use bp::{Tx, Txid}; +use esplora::{BlockingClient, Config, Error}; +use rgbcore::vm::WitnessOrd; +use rgbcore::ChainNet; + +use super::RgbResolver; + +#[derive(Clone, Debug)] +/// Represents a client for interacting with a mempool. +// Currently, this client is wrapping an `esplora::BlockingClient` instance. +// If the mempool service changes in the future and is not compatible with +// esplora::BlockingClient, Only the internal implementation needs to be +// modified +pub struct MemPoolClient { + inner: BlockingClient, +} + +impl MemPoolClient { + /// Creates a new `MemPoolClient` instance. + /// + /// # Arguments + /// + /// * `url` - The URL of the mempool server. + /// * `config` - The configuration for the mempool client. + /// + /// # Returns + /// + /// Returns a `Result` containing the `MemPoolClient` instance if + /// successful, or an `Error` if an error occurred. + #[allow(clippy::result_large_err)] + pub fn new(url: &str, config: Config) -> Result { + let inner = BlockingClient::from_config(url, config)?; + Ok(MemPoolClient { inner }) + } +} + +impl RgbResolver for MemPoolClient { + fn check_chain_net(&self, chain_net: ChainNet) -> Result<(), String> { + self.inner.check_chain_net(chain_net) + } + + fn resolve_pub_witness_ord(&self, txid: Txid) -> Result { + self.inner.resolve_pub_witness_ord(txid) + } + + fn resolve_pub_witness(&self, txid: Txid) -> Result, String> { + self.inner.resolve_pub_witness(txid) + } +} + +#[cfg(test)] +mod test { + use esplora::Config; + #[test] + fn test_mempool_client_mainnet_tx() { + let client = super::MemPoolClient::new("https://mempool.space/api", Config::default()) + .expect("Failed to create client"); + let txid = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b" + .parse() + .unwrap(); + let status = client.inner.tx_status(&txid).unwrap(); + assert_eq!(status.block_height, Some(0)); + assert_eq!(status.block_time, Some(1231006505)); + } + + #[test] + fn test_mempool_client_testnet_tx() { + let client = + super::MemPoolClient::new("https://mempool.space/testnet/api", Config::default()) + .expect("Failed to create client"); + + let txid = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b" + .parse() + .unwrap(); + let status = client.inner.tx_status(&txid).unwrap(); + assert_eq!(status.block_height, Some(0)); + assert_eq!(status.block_time, Some(1296688602)); + } + + #[test] + fn test_mempool_client_testnet4_tx() { + let client = + super::MemPoolClient::new("https://mempool.space/testnet4/api", Config::default()) + .expect("Failed to create client"); + let txid = "7aa0a7ae1e223414cb807e40cd57e667b718e42aaf9306db9102fe28912b7b4e" + .parse() + .unwrap(); + let status = client.inner.tx_status(&txid).unwrap(); + assert_eq!(status.block_height, Some(0)); + assert_eq!(status.block_time, Some(1714777860)); + } + + #[test] + fn test_mempool_client_testnet4_tx_detail() { + let client = + super::MemPoolClient::new("https://mempool.space/testnet4/api", Config::default()) + .expect("Failed to create client"); + let txid = "7aa0a7ae1e223414cb807e40cd57e667b718e42aaf9306db9102fe28912b7b4e" + .parse() + .unwrap(); + let tx = client + .inner + .tx(&txid) + .expect("Failed to get tx") + .expect("Tx not found"); + assert!(!tx.inputs.is_empty()); + assert!(!tx.outputs.is_empty()); + assert_eq!(tx.outputs[0].value, 5_000_000_000); + } +} diff --git a/src/containers/disclosure.rs b/src/indexers/mod.rs similarity index 69% rename from src/containers/disclosure.rs rename to src/indexers/mod.rs index b81f83c9..9c108bba 100644 --- a/src/containers/disclosure.rs +++ b/src/indexers/mod.rs @@ -2,10 +2,10 @@ // // SPDX-License-Identifier: Apache-2.0 // -// Written in 2019-2024 by +// Written in 2019-2023 by // Dr Maxim Orlovsky // -// Copyright (C) 2019-2024 LNP/BP Standards Association. All rights reserved. +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -19,5 +19,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -// TODO: Implement disclosures -pub struct Disclosure; +mod any; +#[cfg(feature = "esplora_blocking")] +pub mod esplora_blocking; +#[cfg(feature = "electrum_blocking")] +pub mod electrum_blocking; + +#[cfg(feature = "mempool_blocking")] +pub mod mempool_blocking; + +pub use any::{AnyResolver, RgbResolver}; diff --git a/src/info.rs b/src/info.rs index 261d890d..3c877849 100644 --- a/src/info.rs +++ b/src/info.rs @@ -19,171 +19,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::collections::HashMap; -use std::fmt::{self, Debug, Display, Formatter, Write}; -use std::str::FromStr; +use std::fmt::{self, Debug, Display, Formatter}; -use amplify::confinement::TinyOrdSet; use chrono::{DateTime, TimeZone, Utc}; -use rgb::{AltLayer1Set, ContractId, Genesis, Identity, Operation, SchemaId}; -use strict_encoding::stl::{AlphaCapsLodash, AlphaNumLodash}; -use strict_encoding::{FieldName, RString, StrictDeserialize, StrictSerialize, TypeName}; - -use crate::containers::{ - SupplSub, Supplement, SUPPL_ANNOT_IFACE_CLASS, SUPPL_ANNOT_IFACE_FEATURES, -}; -use crate::interface::{Iface, IfaceId, IfaceImpl, IfaceRef, ImplId, VerNo}; -use crate::persistence::SchemaIfaces; -use crate::LIB_NAME_RGB_STD; - -#[derive(Wrapper, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, From)] -#[wrapper(Deref, Display, FromStr)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", transparent) -)] -pub struct IfaceClassName(RString); - -impl_ident_type!(IfaceClassName); -impl_ident_subtype!(IfaceClassName); -impl_strict_newtype!(IfaceClassName, LIB_NAME_RGB_STD); - -impl StrictSerialize for IfaceClassName {} -impl StrictDeserialize for IfaceClassName {} - -#[derive(Wrapper, WrapperMut, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default, From)] -#[wrapper(Deref)] -#[wrapper_mut(DerefMut)] -#[derive(StrictType, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] -pub struct FeatureList(TinyOrdSet); - -impl StrictSerialize for FeatureList {} -impl StrictDeserialize for FeatureList {} - -#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Debug)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub struct IfaceInfo { - pub id: IfaceId, - pub version: VerNo, - pub name: TypeName, - pub standard: Option, - pub features: FeatureList, - pub developer: Identity, - pub created_at: DateTime, - pub inherits: Vec, - pub default_op: Option, -} - -impl IfaceInfo { - pub fn new( - iface: &Iface, - names: &HashMap, - suppl: Option<&Supplement>, - ) -> Self { - let mut standard = None; - let mut features = none!(); - if let Some(suppl) = suppl { - standard = - suppl.get_default_opt::(SupplSub::Itself, SUPPL_ANNOT_IFACE_CLASS); - if let Some(list) = - suppl.get_default_opt::(SupplSub::Itself, SUPPL_ANNOT_IFACE_FEATURES) - { - features = list - }; - } - Self::with(iface, standard, features, names) - } - - pub fn with( - iface: &Iface, - standard: Option, - features: FeatureList, - names: &HashMap, - ) -> Self { - IfaceInfo { - id: iface.iface_id(), - version: iface.version, - name: iface.name.clone(), - standard, - features, - developer: iface.developer.clone(), - created_at: Utc - .timestamp_opt(iface.timestamp, 0) - .single() - .unwrap_or_else(Utc::now), - inherits: iface - .inherits - .iter() - .map(|id| { - names - .get(id) - .cloned() - .map(IfaceRef::Name) - .unwrap_or(IfaceRef::Id(*id)) - }) - .collect(), - default_op: iface.default_operation.clone(), - } - } -} - -impl Display for IfaceInfo { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!( - f, - "{}\t", - self.standard - .as_ref() - .map(IfaceClassName::to_string) - .unwrap_or_else(|| s!("~")) - )?; - write!(f, "{: <40}\t", self.name.to_string())?; - write!(f, "{}\t", self.created_at.format("%Y-%m-%d"))?; - write!(f, "{}\t", self.version)?; - writeln!(f, "{}", self.id)?; - - writeln!( - f, - " Features: {}", - self.features - .iter() - .map(FieldName::to_string) - .collect::>() - .join(", ") - )?; - - writeln!( - f, - " Defaults to: {}", - self.default_op - .as_ref() - .map(FieldName::to_string) - .unwrap_or_else(|| s!("~")) - )?; - - writeln!(f, " Developer: {}", self.developer)?; - - writeln!( - f, - " Inherits: {}", - self.inherits - .iter() - .map(|f| format!("{:#}", f)) - .collect::>() - .chunks(5) - .map(|chunk| chunk.join(", ")) - .collect::>() - .join("\n ") - ) - } -} +use rgb::{ChainNet, ContractId, Genesis, Identity, Operation, Schema, SchemaId}; +use strict_encoding::TypeName; #[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Debug)] #[cfg_attr( @@ -194,27 +34,13 @@ impl Display for IfaceInfo { pub struct SchemaInfo { pub id: SchemaId, pub name: TypeName, - pub developer: Identity, - pub created_at: DateTime, - pub implements: Vec, } impl SchemaInfo { - pub fn with(schema_ifaces: &SchemaIfaces) -> Self { - let schema = &schema_ifaces.schema; + pub fn with(schema: &Schema) -> Self { SchemaInfo { id: schema.schema_id(), name: schema.name.clone(), - developer: schema.developer.clone(), - created_at: Utc - .timestamp_opt(schema.timestamp, 0) - .single() - .unwrap_or_else(Utc::now), - implements: schema_ifaces - .iimpls - .iter() - .map(|(name, iimpl)| ImplInfo::with(name.clone(), iimpl)) - .collect(), } } } @@ -223,53 +49,10 @@ impl Display for SchemaInfo { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "{: <24}", self.name.to_string())?; write!(f, "\t{: <80}", self.id.to_string())?; - write!(f, "\t{}", self.created_at.format("%Y-%m-%d"))?; - writeln!(f, "\t{}", self.developer)?; - for info in &self.implements { - write!(f, " {info}")?; - } Ok(()) } } -#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Debug)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub struct ImplInfo { - pub id: ImplId, - pub iface_id: IfaceId, - pub iface_name: TypeName, - pub developer: Identity, - pub created_at: DateTime, -} - -impl ImplInfo { - pub fn with(iface_name: TypeName, iimpl: &IfaceImpl) -> Self { - ImplInfo { - id: iimpl.impl_id(), - iface_id: iimpl.iface_id, - iface_name, - developer: iimpl.developer.clone(), - created_at: Utc - .timestamp_opt(iimpl.timestamp, 0) - .single() - .unwrap_or_else(Utc::now), - } - } -} - -impl Display for ImplInfo { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "{: <24}", self.iface_name.to_string())?; - write!(f, "\t{: <80}", self.id.to_string())?; - write!(f, "\t{}", self.created_at.format("%Y-%m-%d"))?; - writeln!(f, "\t{}", self.developer) - } -} - #[derive(Clone, Eq, PartialEq, Hash, Debug)] #[cfg_attr( feature = "serde", @@ -281,8 +64,7 @@ pub struct ContractInfo { pub schema_id: SchemaId, pub issuer: Identity, pub issued_at: DateTime, - pub testnet: bool, - pub alt_layers1: AltLayer1Set, + pub chain_net: ChainNet, } impl ContractInfo { @@ -295,8 +77,7 @@ impl ContractInfo { .timestamp_opt(genesis.timestamp, 0) .single() .unwrap_or_else(Utc::now), - testnet: genesis.testnet, - alt_layers1: genesis.alt_layers1.clone(), + chain_net: genesis.chain_net, } } } @@ -304,14 +85,7 @@ impl ContractInfo { impl Display for ContractInfo { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "{}", self.id)?; - write!( - f, - "\tbitcoin{: <12}", - self.alt_layers1.iter().fold(s!(""), |mut acc, layer| { - write!(acc, ", {layer}").ok(); - acc - }) - )?; + write!(f, "\t{}", self.chain_net)?; write!(f, "\t{}", self.issued_at.format("%Y-%m-%d"))?; writeln!(f, "\t{: <80}", self.schema_id.to_string())?; writeln!(f, " Developer: {}", self.issuer) diff --git a/src/interface/builder.rs b/src/interface/builder.rs deleted file mode 100644 index eb8a01c4..00000000 --- a/src/interface/builder.rs +++ /dev/null @@ -1,1448 +0,0 @@ -// RGB standard library for working with smart contracts on Bitcoin & Lightning -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2019-2024 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2019-2024 LNP/BP Standards Association. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#![allow(clippy::result_large_err)] - -use std::collections::{BTreeMap, HashSet}; - -use amplify::confinement::{Confined, SmallOrdSet, TinyOrdMap, U16}; -use amplify::{confinement, Wrapper}; -use chrono::Utc; -use invoice::{Allocation, Amount}; -use rgb::validation::Scripts; -use rgb::{ - validation, AltLayer1, AltLayer1Set, AssetTag, AssetTags, Assign, AssignmentType, Assignments, - AttachState, BlindingFactor, ContractId, DataState, ExposedSeal, FungibleType, Genesis, - GenesisSeal, GlobalState, GraphSeal, Identity, Input, Layer1, MetadataError, Opout, - OwnedStateSchema, RevealedAttach, RevealedData, RevealedValue, Schema, Transition, - TransitionType, TypedAssigns, XChain, XOutpoint, -}; -use rgbcore::{GlobalStateSchema, GlobalStateType, MetaType, Metadata, ValencyType}; -use strict_encoding::{FieldName, SerializeError, StrictSerialize}; -use strict_types::{decode, SemId, TypeSystem}; - -use crate::containers::{BuilderSeal, ContainerVer, Contract, ValidConsignment}; -use crate::interface::resolver::DumbResolver; -use crate::interface::{Iface, IfaceImpl, TransitionIface}; -use crate::persistence::PersistedState; -use crate::Outpoint; - -#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)] -#[display(doc_comments)] -pub enum BuilderError { - /// contract already has too many layers1. - TooManyLayers1, - - /// metadata `{0}` are not known to the schema - MetadataNotFound(FieldName), - - #[from] - #[display(inner)] - MetadataInvalid(MetadataError), - - /// global state `{0}` is not known to the schema. - GlobalNotFound(FieldName), - - /// assignment `{0}` is not known to the schema. - AssignmentNotFound(FieldName), - - /// transition `{0}` is not known to the schema. - TransitionNotFound(FieldName), - - /// unknown owned state name `{0}`. - InvalidStateField(FieldName), - - /// state `{0}` provided to the builder has invalid type. - InvalidStateType(AssignmentType), - - /// asset tag for state `{0}` must be added before any fungible state of - /// the same type. - AssetTagMissed(AssignmentType), - - /// asset tag for state `{0}` was already automatically created. Please call - /// `add_asset_tag` before adding any fungible state to the builder. - AssetTagAutomatic(AssignmentType), - - /// state data for state type `{0}` are invalid: asset tag doesn't match the - /// tag defined by the contract. - AssetTagInvalid(AssignmentType), - - /// interface doesn't specifies default operation name, thus an explicit - /// operation type must be provided with `set_operation_type` method. - NoOperationSubtype, - - /// interface doesn't have a default assignment type. - NoDefaultAssignment, - - /// {0} is not supported by the contract genesis. - InvalidLayer1(Layer1), - - #[from] - #[display(inner)] - StrictEncode(SerializeError), - - #[from] - #[display(inner)] - Reify(decode::Error), - - #[from] - #[display(inner)] - Confinement(confinement::Error), - - #[from] - #[display(inner)] - ContractInconsistency(validation::Status), -} - -mod private { - pub trait Sealed {} -} - -pub trait TxOutpoint: Copy + Eq + private::Sealed { - fn is_liquid(&self) -> bool; - fn is_bitcoin(&self) -> bool; - fn map_to_xchain(self, f: impl FnOnce(Outpoint) -> U) -> XChain; -} - -impl private::Sealed for Outpoint {} -impl private::Sealed for XOutpoint {} -impl TxOutpoint for Outpoint { - fn is_liquid(&self) -> bool { false } - fn is_bitcoin(&self) -> bool { true } - fn map_to_xchain(self, f: impl FnOnce(Outpoint) -> U) -> XChain { - XChain::Bitcoin(f(self)) - } -} -impl TxOutpoint for XOutpoint { - fn is_liquid(&self) -> bool { XChain::is_liquid(self) } - fn is_bitcoin(&self) -> bool { XChain::is_bitcoin(self) } - fn map_to_xchain(self, f: impl FnOnce(Outpoint) -> U) -> XChain { self.map(f) } -} - -#[derive(Clone, Debug)] -pub struct ContractBuilder { - builder: OperationBuilder, - testnet: bool, - alt_layers1: AltLayer1Set, - scripts: Scripts, - issuer: Identity, -} - -impl ContractBuilder { - pub fn with( - issuer: Identity, - iface: Iface, - schema: Schema, - iimpl: IfaceImpl, - types: TypeSystem, - scripts: Scripts, - ) -> Self { - Self { - builder: OperationBuilder::with(iface, schema, iimpl, types), - testnet: true, - alt_layers1: none!(), - scripts, - issuer, - } - } - - pub fn deterministic( - issuer: Identity, - iface: Iface, - schema: Schema, - iimpl: IfaceImpl, - types: TypeSystem, - scripts: Scripts, - ) -> Self { - Self { - builder: OperationBuilder::deterministic(iface, schema, iimpl, types), - testnet: true, - alt_layers1: none!(), - scripts, - issuer, - } - } - - pub fn type_system(&self) -> &TypeSystem { self.builder.type_system() } - - pub fn set_mainnet(mut self) -> Self { - self.testnet = false; - self - } - - pub fn has_layer1(&self, layer1: Layer1) -> bool { - match layer1 { - Layer1::Bitcoin => true, - Layer1::Liquid => self.alt_layers1.contains(&AltLayer1::Liquid), - } - } - pub fn check_layer1(&self, layer1: Layer1) -> Result<(), BuilderError> { - if !self.has_layer1(layer1) { - return Err(BuilderError::InvalidLayer1(layer1)); - } - Ok(()) - } - - pub fn add_layer1(mut self, layer1: AltLayer1) -> Result { - self.alt_layers1 - .push(layer1) - .map_err(|_| BuilderError::TooManyLayers1)?; - Ok(self) - } - - #[inline] - pub fn asset_tag(&self, name: impl Into) -> Result { - self.builder.asset_tag(name) - } - - #[inline] - pub fn add_asset_tag( - mut self, - name: impl Into, - asset_tag: AssetTag, - ) -> Result { - self.builder = self.builder.add_asset_tag(name, asset_tag)?; - Ok(self) - } - - #[inline] - pub fn global_type(&self, name: &FieldName) -> Option { - self.builder.global_type(name) - } - - #[inline] - pub fn valency_type(&self, name: &FieldName) -> Option { - self.builder.valency_type(name) - } - - #[inline] - pub fn valency_name(&self, type_id: ValencyType) -> &FieldName { - self.builder.valency_name(type_id) - } - - #[inline] - pub fn meta_name(&self, type_id: MetaType) -> &FieldName { self.builder.meta_name(type_id) } - - #[inline] - pub fn add_metadata( - mut self, - name: impl Into, - value: impl StrictSerialize, - ) -> Result { - self.builder = self.builder.add_metadata(name, value)?; - Ok(self) - } - - #[inline] - pub fn add_global_state( - mut self, - name: impl Into, - value: impl StrictSerialize, - ) -> Result { - self.builder = self.builder.add_global_state(name, value)?; - Ok(self) - } - - pub fn add_owned_state_det( - mut self, - name: impl Into, - seal: impl Into>, - state: PersistedState, - ) -> Result { - let seal = seal.into(); - self.check_layer1(seal.layer1())?; - self.builder = self.builder.add_owned_state_det(name, seal, state)?; - Ok(self) - } - - pub fn add_rights( - mut self, - name: impl Into, - seal: impl Into>, - ) -> Result { - let seal = seal.into(); - self.check_layer1(seal.layer1())?; - self.builder = self.builder.add_rights(name, seal)?; - Ok(self) - } - - pub fn add_fungible_state( - mut self, - name: impl Into, - seal: impl Into>, - value: impl Into, - ) -> Result { - let name = name.into(); - let seal = seal.into(); - self.check_layer1(seal.layer1())?; - self.builder.init_asset_tag(name.clone())?; - self.builder = self.builder.add_fungible_state(name, seal, value)?; - Ok(self) - } - - pub fn add_fungible_state_det( - mut self, - name: impl Into, - seal: impl Into>, - value: impl Into, - blinding: BlindingFactor, - ) -> Result { - let name = name.into(); - let seal = seal.into(); - self.check_layer1(seal.layer1())?; - let tag = self.builder.init_asset_tag(name.clone())?; - let state = RevealedValue::with_blinding(value.into(), blinding, tag); - self.builder = self.builder.add_fungible_state_det(name, seal, state)?; - Ok(self) - } - - pub fn add_data( - mut self, - name: impl Into, - seal: impl Into>, - value: impl StrictSerialize, - ) -> Result { - let seal = seal.into(); - self.check_layer1(seal.layer1())?; - self.builder = self.builder.add_data(name, seal, value)?; - Ok(self) - } - - pub fn add_data_det( - mut self, - name: impl Into, - seal: impl Into>, - data: RevealedData, - ) -> Result { - let seal = seal.into(); - self.check_layer1(seal.layer1())?; - self.builder = self.builder.add_data_det(name, seal, data)?; - Ok(self) - } - - pub fn add_attachment( - mut self, - name: impl Into, - seal: impl Into>, - attachment: AttachState, - ) -> Result { - let seal = seal.into(); - self.check_layer1(seal.layer1())?; - self.builder = self.builder.add_attachment(name, seal, attachment)?; - Ok(self) - } - - pub fn add_attachment_det( - mut self, - name: impl Into, - seal: impl Into>, - attachment: RevealedAttach, - ) -> Result { - let seal = seal.into(); - self.check_layer1(seal.layer1())?; - self.builder = self.builder.add_attachment_det(name, seal, attachment)?; - Ok(self) - } - - pub fn issue_contract(self) -> Result, BuilderError> { - debug_assert!( - !self.builder.deterministic, - "for issuing deterministic contracts please use issue_contract_det method" - ); - self.issue_contract_raw(Utc::now().timestamp()) - } - - pub fn issue_contract_det( - self, - timestamp: i64, - ) -> Result, BuilderError> { - debug_assert!( - self.builder.deterministic, - "for issuing deterministic contracts please use deterministic constructor" - ); - self.issue_contract_raw(timestamp) - } - - fn issue_contract_raw(self, timestamp: i64) -> Result, BuilderError> { - let (schema, iface, iimpl, global, assignments, types, asset_tags) = - self.builder.complete(None); - - let genesis = Genesis { - ffv: none!(), - schema_id: schema.schema_id(), - flags: none!(), - timestamp, - testnet: self.testnet, - alt_layers1: self.alt_layers1, - asset_tags, - metadata: empty!(), - globals: global, - assignments, - valencies: none!(), - issuer: self.issuer, - validator: none!(), - }; - - let ifaces = tiny_bmap! { iface => iimpl }; - let scripts = Confined::from_iter_checked(self.scripts.into_values()); - - let contract = Contract { - version: ContainerVer::V2, - transfer: false, - terminals: none!(), - genesis, - extensions: none!(), - bundles: none!(), - schema, - ifaces, - attachments: none!(), // TODO: Add support for attachment files - - types, - scripts, - - supplements: none!(), // TODO: Add supplements - signatures: none!(), // TODO: Add signatures - }; - - let valid_contract = contract - .validate(&DumbResolver, self.testnet) - .map_err(|(status, _)| status)?; - - Ok(valid_contract) - } -} - -#[derive(Clone, Debug)] -pub struct TransitionBuilder { - contract_id: ContractId, - builder: OperationBuilder, - nonce: u64, - transition_type: TransitionType, - inputs: TinyOrdMap, -} - -impl TransitionBuilder { - pub fn blank_transition( - contract_id: ContractId, - iface: Iface, - schema: Schema, - iimpl: IfaceImpl, - types: TypeSystem, - ) -> Self { - Self::with(contract_id, iface, schema, iimpl, TransitionType::BLANK, types) - } - - pub fn blank_transition_det( - contract_id: ContractId, - iface: Iface, - schema: Schema, - iimpl: IfaceImpl, - types: TypeSystem, - ) -> Self { - Self::deterministic(contract_id, iface, schema, iimpl, TransitionType::BLANK, types) - } - - pub fn default_transition( - contract_id: ContractId, - iface: Iface, - schema: Schema, - iimpl: IfaceImpl, - types: TypeSystem, - ) -> Result { - let transition_type = iface - .default_operation - .as_ref() - .and_then(|name| iimpl.transition_type(name)) - .ok_or(BuilderError::NoOperationSubtype)?; - Ok(Self::with(contract_id, iface, schema, iimpl, transition_type, types)) - } - - pub fn default_transition_det( - contract_id: ContractId, - iface: Iface, - schema: Schema, - iimpl: IfaceImpl, - types: TypeSystem, - ) -> Result { - let transition_type = iface - .default_operation - .as_ref() - .and_then(|name| iimpl.transition_type(name)) - .ok_or(BuilderError::NoOperationSubtype)?; - Ok(Self::deterministic(contract_id, iface, schema, iimpl, transition_type, types)) - } - - pub fn named_transition( - contract_id: ContractId, - iface: Iface, - schema: Schema, - iimpl: IfaceImpl, - transition_name: impl Into, - types: TypeSystem, - ) -> Result { - let transition_name = transition_name.into(); - let transition_type = iimpl - .transition_type(&transition_name) - .ok_or(BuilderError::TransitionNotFound(transition_name))?; - Ok(Self::with(contract_id, iface, schema, iimpl, transition_type, types)) - } - - pub fn named_transition_det( - contract_id: ContractId, - iface: Iface, - schema: Schema, - iimpl: IfaceImpl, - transition_name: impl Into, - types: TypeSystem, - ) -> Result { - let transition_name = transition_name.into(); - let transition_type = iimpl - .transition_type(&transition_name) - .ok_or(BuilderError::TransitionNotFound(transition_name))?; - Ok(Self::deterministic(contract_id, iface, schema, iimpl, transition_type, types)) - } - - fn with( - contract_id: ContractId, - iface: Iface, - schema: Schema, - iimpl: IfaceImpl, - transition_type: TransitionType, - types: TypeSystem, - ) -> Self { - Self { - contract_id, - builder: OperationBuilder::with(iface, schema, iimpl, types), - nonce: u64::MAX, - transition_type, - inputs: none!(), - } - } - - fn deterministic( - contract_id: ContractId, - iface: Iface, - schema: Schema, - iimpl: IfaceImpl, - transition_type: TransitionType, - types: TypeSystem, - ) -> Self { - Self { - contract_id, - builder: OperationBuilder::deterministic(iface, schema, iimpl, types), - nonce: u64::MAX, - transition_type, - inputs: none!(), - } - } - - pub fn type_system(&self) -> &TypeSystem { self.builder.type_system() } - - pub fn transition_type(&self) -> TransitionType { self.transition_type } - - pub fn set_nonce(mut self, nonce: u64) -> Self { - self.nonce = nonce; - self - } - - #[inline] - pub fn asset_tag(&self, name: impl Into) -> Result { - self.builder.asset_tag(name) - } - - #[inline] - pub fn add_asset_tag( - mut self, - name: impl Into, - asset_tag: AssetTag, - ) -> Result { - self.builder = self.builder.add_asset_tag(name, asset_tag)?; - Ok(self) - } - - #[inline] - pub fn add_asset_tag_raw( - mut self, - type_id: AssignmentType, - asset_tag: AssetTag, - ) -> Result { - self.builder = self.builder.add_asset_tag_raw(type_id, asset_tag)?; - Ok(self) - } - - #[inline] - pub fn add_metadata( - mut self, - name: impl Into, - value: impl StrictSerialize, - ) -> Result { - self.builder = self.builder.add_metadata(name, value)?; - Ok(self) - } - - #[inline] - pub fn add_global_state( - mut self, - name: impl Into, - value: impl StrictSerialize, - ) -> Result { - self.builder = self.builder.add_global_state(name, value)?; - Ok(self) - } - - pub fn add_input(mut self, opout: Opout, state: PersistedState) -> Result { - self.inputs.insert(Input::with(opout), state)?; - Ok(self) - } - - pub fn default_assignment(&self) -> Result<&FieldName, BuilderError> { - self.builder - .transition_iface(self.transition_type) - .default_assignment - .as_ref() - .ok_or(BuilderError::NoDefaultAssignment) - } - - #[inline] - pub fn assignments_type(&self, name: &FieldName) -> Option { - self.builder.assignments_type(name) - } - - #[inline] - pub fn global_type(&self, name: &FieldName) -> Option { - self.builder.global_type(name) - } - - #[inline] - pub fn valency_type(&self, name: &FieldName) -> Option { - self.builder.valency_type(name) - } - - pub fn valency_name(&self, type_id: ValencyType) -> &FieldName { - self.builder.valency_name(type_id) - } - - pub fn meta_name(&self, type_id: MetaType) -> &FieldName { self.builder.meta_name(type_id) } - - pub fn add_owned_state_det( - mut self, - name: impl Into, - seal: impl Into>, - state: PersistedState, - ) -> Result { - self.builder = self.builder.add_owned_state_det(name, seal, state)?; - Ok(self) - } - - pub fn add_owned_state_raw( - mut self, - type_id: AssignmentType, - seal: impl Into>, - state: PersistedState, - ) -> Result { - if matches!(state, PersistedState::Amount(_, _, tag) if self.builder.asset_tag_raw(type_id)? != tag) - { - return Err(BuilderError::AssetTagInvalid(type_id)); - } - self.builder = self.builder.add_owned_state_raw(type_id, seal, state)?; - Ok(self) - } - - pub fn add_rights( - mut self, - name: impl Into, - seal: impl Into>, - ) -> Result { - self.builder = self.builder.add_rights(name, seal)?; - Ok(self) - } - - pub fn add_fungible_default_state( - self, - seal: impl Into>, - value: u64, - ) -> Result { - let assignment_name = self.default_assignment()?.clone(); - self.add_fungible_state(assignment_name, seal.into(), value) - } - - pub fn add_fungible_default_state_det( - self, - seal: impl Into>, - value: u64, - blinding: BlindingFactor, - ) -> Result { - let assignment_name = self.default_assignment()?.clone(); - self.add_fungible_state_det(assignment_name, seal.into(), value, blinding) - } - - pub fn add_fungible_state( - mut self, - name: impl Into, - seal: impl Into>, - value: impl Into, - ) -> Result { - self.builder = self.builder.add_fungible_state(name.into(), seal, value)?; - Ok(self) - } - - pub fn add_fungible_state_det( - mut self, - name: impl Into, - seal: impl Into>, - value: impl Into, - blinding: BlindingFactor, - ) -> Result { - let name = name.into(); - let type_id = self - .builder - .assignments_type(&name) - .ok_or(BuilderError::AssignmentNotFound(name.clone()))?; - let tag = self.builder.asset_tag_raw(type_id)?; - let state = RevealedValue::with_blinding(value.into(), blinding, tag); - - self.builder = self.builder.add_fungible_state_det(name, seal, state)?; - Ok(self) - } - - pub fn add_fungible_state_raw( - mut self, - type_id: AssignmentType, - seal: impl Into>, - value: impl Into, - blinding: BlindingFactor, - ) -> Result { - let tag = self.builder.asset_tag_raw(type_id)?; - let state = RevealedValue::with_blinding(value.into(), blinding, tag); - self.builder = self.builder.add_fungible_state_raw(type_id, seal, state)?; - Ok(self) - } - - pub fn add_data( - mut self, - name: impl Into, - seal: impl Into>, - value: impl StrictSerialize, - ) -> Result { - self.builder = self.builder.add_data(name, seal, value)?; - Ok(self) - } - - pub fn add_data_det( - mut self, - name: impl Into, - seal: impl Into>, - data: RevealedData, - ) -> Result { - self.builder = self.builder.add_data_det(name, seal, data)?; - Ok(self) - } - - pub fn add_data_raw( - mut self, - type_id: AssignmentType, - seal: impl Into>, - allocation: impl Into, - blinding: u64, - ) -> Result { - let revealed_state = RevealedData::with_salt(allocation.into(), blinding.into()); - self.builder = self.builder.add_data_raw(type_id, seal, revealed_state)?; - Ok(self) - } - - pub fn add_data_default( - self, - seal: impl Into>, - value: impl StrictSerialize, - ) -> Result { - let assignment_name = self.default_assignment()?.clone(); - self.add_data(assignment_name, seal.into(), value) - } - - pub fn add_attachment( - mut self, - name: impl Into, - seal: impl Into>, - attachment: AttachState, - ) -> Result { - self.builder = self.builder.add_attachment(name, seal, attachment)?; - Ok(self) - } - - pub fn add_attachment_det( - mut self, - name: impl Into, - seal: impl Into>, - attachment: RevealedAttach, - ) -> Result { - self.builder = self.builder.add_attachment_det(name, seal, attachment)?; - Ok(self) - } - - pub fn has_inputs(&self) -> bool { !self.inputs.is_empty() } - - pub fn complete_transition(self) -> Result { - let (_, _, _, global, assignments, _, _) = self.builder.complete(Some(&self.inputs)); - - let transition = Transition { - ffv: none!(), - contract_id: self.contract_id, - nonce: self.nonce, - transition_type: self.transition_type, - metadata: empty!(), - globals: global, - inputs: SmallOrdSet::from_iter_checked(self.inputs.into_keys()).into(), - assignments, - valencies: none!(), - witness: none!(), - validator: none!(), - }; - - // TODO: Validate against schema - - Ok(transition) - } -} - -#[derive(Clone, Debug)] -pub struct OperationBuilder { - // TODO: use references instead of owned values - schema: Schema, - iface: Iface, - iimpl: IfaceImpl, - asset_tags: AssetTags, - deterministic: bool, - - global: GlobalState, - meta: Metadata, - rights: TinyOrdMap>, 1, U16>>, - fungible: - TinyOrdMap, RevealedValue>, 1, U16>>, - data: TinyOrdMap, RevealedData>, 1, U16>>, - attachments: - TinyOrdMap, RevealedAttach>, 1, U16>>, - // TODO: add valencies - types: TypeSystem, -} - -impl OperationBuilder { - fn with(iface: Iface, schema: Schema, iimpl: IfaceImpl, types: TypeSystem) -> Self { - OperationBuilder { - schema, - iface, - iimpl, - asset_tags: none!(), - deterministic: false, - - global: none!(), - meta: none!(), - rights: none!(), - fungible: none!(), - attachments: none!(), - data: none!(), - - types, - } - } - - fn deterministic(iface: Iface, schema: Schema, iimpl: IfaceImpl, types: TypeSystem) -> Self { - OperationBuilder { - schema, - iface, - iimpl, - asset_tags: none!(), - deterministic: true, - - global: none!(), - meta: none!(), - rights: none!(), - fungible: none!(), - attachments: none!(), - data: none!(), - - types, - } - } - - fn type_system(&self) -> &TypeSystem { &self.types } - - fn transition_iface(&self, ty: TransitionType) -> &TransitionIface { - let transition_name = self.iimpl.transition_name(ty).expect("reverse type"); - self.iface - .transitions - .get(transition_name) - .expect("internal inconsistency") - } - - fn assignments_type(&self, name: &FieldName) -> Option { - self.iimpl.assignments_type(name) - } - - fn meta_type(&self, name: &FieldName) -> Option { self.iimpl.meta_type(name) } - - fn meta_name(&self, ty: MetaType) -> &FieldName { - self.iimpl.meta_name(ty).expect("internal inconsistency") - } - - fn global_type(&self, name: &FieldName) -> Option { - self.iimpl.global_type(name) - } - - fn valency_type(&self, name: &FieldName) -> Option { - self.iimpl.valency_type(name) - } - - fn valency_name(&self, ty: ValencyType) -> &FieldName { - self.iimpl.valency_name(ty).expect("internal inconsistency") - } - - #[inline] - fn state_schema(&self, type_id: AssignmentType) -> &OwnedStateSchema { - self.schema - .owned_types - .get(&type_id) - .expect("schema should match interface: must be checked by the constructor") - } - - #[inline] - fn meta_schema(&self, type_id: MetaType) -> &SemId { - self.schema - .meta_types - .get(&type_id) - .expect("schema should match interface: must be checked by the constructor") - } - - #[inline] - fn global_schema(&self, type_id: GlobalStateType) -> &GlobalStateSchema { - self.schema - .global_types - .get(&type_id) - .expect("schema should match interface: must be checked by the constructor") - } - - pub fn asset_tag(&self, name: impl Into) -> Result { - let name = name.into(); - let type_id = self - .assignments_type(&name) - .ok_or(BuilderError::AssignmentNotFound(name.clone()))?; - self.asset_tag_raw(type_id) - } - - #[inline] - fn asset_tag_raw(&self, type_id: AssignmentType) -> Result { - self.asset_tags - .get(&type_id) - .ok_or(BuilderError::AssetTagMissed(type_id)) - .copied() - } - - #[inline] - pub fn add_asset_tag( - self, - name: impl Into, - asset_tag: AssetTag, - ) -> Result { - let name = name.into(); - let type_id = self - .assignments_type(&name) - .ok_or(BuilderError::AssignmentNotFound(name))?; - - self.add_asset_tag_raw(type_id, asset_tag) - } - - #[inline] - pub fn add_asset_tag_raw( - mut self, - type_id: AssignmentType, - asset_tag: AssetTag, - ) -> Result { - if self.fungible.contains_key(&type_id) { - return Err(BuilderError::AssetTagAutomatic(type_id)); - } - - self.asset_tags.insert(type_id, asset_tag)?; - Ok(self) - } - - pub fn init_asset_tag(&mut self, name: impl Into) -> Result { - let name = name.into(); - let type_id = self - .assignments_type(&name) - .ok_or(BuilderError::AssignmentNotFound(name))?; - - if let Some(tag) = self.asset_tags.get(&type_id) { - Ok(*tag) - } else { - let asset_tag = AssetTag::new_random( - format!("{}/{}", self.schema.schema_id(), self.iface.iface_id()), - type_id, - ); - self.asset_tags.insert(type_id, asset_tag)?; - Ok(asset_tag) - } - } - - pub fn add_metadata( - mut self, - name: impl Into, - value: impl StrictSerialize, - ) -> Result { - let name = name.into(); - let serialized = value.to_strict_serialized::<{ u16::MAX as usize }>()?; - - let Some(type_id) = self.meta_type(&name) else { - return Err(BuilderError::MetadataNotFound(name)); - }; - - let sem_id = self.meta_schema(type_id); - self.types.strict_deserialize_type(*sem_id, &serialized)?; - self.meta.add_value(type_id, serialized.into())?; - Ok(self) - } - - pub fn add_global_state( - mut self, - name: impl Into, - value: impl StrictSerialize, - ) -> Result { - let name = name.into(); - let serialized = value.to_strict_serialized::<{ u16::MAX as usize }>()?; - - // Check value matches type requirements - let Some(type_id) = self.global_type(&name) else { - return Err(BuilderError::GlobalNotFound(name)); - }; - let sem_id = self.global_schema(type_id).sem_id; - self.types.strict_deserialize_type(sem_id, &serialized)?; - - self.global.add_state(type_id, serialized.into())?; - - Ok(self) - } - - fn add_owned_state_det( - self, - name: impl Into, - seal: impl Into>, - state: PersistedState, - ) -> Result { - debug_assert!( - self.deterministic, - "to add owned state in deterministic way the builder has to be created using \ - deterministic constructor" - ); - let name = name.into(); - let type_id = self - .assignments_type(&name) - .ok_or(BuilderError::AssignmentNotFound(name.clone()))?; - self.add_owned_state_raw(type_id, seal, state) - } - - fn add_owned_state_raw( - self, - type_id: AssignmentType, - seal: impl Into>, - state: PersistedState, - ) -> Result { - match state { - PersistedState::Void => self.add_rights_raw(type_id, seal), - PersistedState::Amount(value, blinding, tag) => { - if self.asset_tag_raw(type_id)? != tag { - return Err(BuilderError::AssetTagInvalid(type_id)); - } - - self.add_fungible_state_raw( - type_id, - seal, - RevealedValue::with_blinding(value, blinding, tag), - ) - } - PersistedState::Data(data, salt) => { - self.add_data_raw(type_id, seal, RevealedData::with_salt(data, salt)) - } - PersistedState::Attachment(attach, salt) => self.add_attachment_raw( - type_id, - seal, - RevealedAttach::with_salt(attach.id, attach.media_type, salt), - ), - } - } - - fn add_rights( - self, - name: impl Into, - seal: impl Into>, - ) -> Result { - let name = name.into(); - - let type_id = self - .assignments_type(&name) - .ok_or(BuilderError::AssignmentNotFound(name))?; - - self.add_rights_raw(type_id, seal) - } - - fn add_rights_raw( - mut self, - type_id: AssignmentType, - seal: impl Into>, - ) -> Result { - let state_schema = self.state_schema(type_id); - if *state_schema != OwnedStateSchema::Declarative { - return Err(BuilderError::InvalidStateType(type_id)); - } - - let seal = seal.into(); - match self.rights.get_mut(&type_id) { - Some(assignments) => { - assignments.push(seal)?; - } - None => { - self.rights.insert(type_id, Confined::with(seal))?; - } - } - - Ok(self) - } - - fn add_fungible_state( - self, - name: impl Into, - seal: impl Into>, - value: impl Into, - ) -> Result { - debug_assert!( - !self.deterministic, - "for adding state to deterministic contracts you have to use add_*_det methods" - ); - - let name = name.into(); - - let type_id = self - .assignments_type(&name) - .ok_or(BuilderError::AssignmentNotFound(name))?; - let tag = self.asset_tag_raw(type_id)?; - - let state = RevealedValue::new_random_blinding(value.into(), tag); - self.add_fungible_state_raw(type_id, seal, state) - } - - fn add_fungible_state_det( - self, - name: impl Into, - seal: impl Into>, - state: RevealedValue, - ) -> Result { - debug_assert!( - self.deterministic, - "to add owned state in deterministic way the builder has to be created using \ - deterministic constructor" - ); - - let name = name.into(); - - let type_id = self - .assignments_type(&name) - .ok_or(BuilderError::AssignmentNotFound(name))?; - self.add_fungible_state_raw(type_id, seal, state) - } - - fn add_fungible_state_raw( - mut self, - type_id: AssignmentType, - seal: impl Into>, - state: RevealedValue, - ) -> Result { - let state_schema = self.state_schema(type_id); - if *state_schema != OwnedStateSchema::Fungible(FungibleType::Unsigned64Bit) { - return Err(BuilderError::InvalidStateType(type_id)); - } - - let seal = seal.into(); - match self.fungible.get_mut(&type_id) { - Some(assignments) => { - assignments.insert(seal, state)?; - } - None => { - self.fungible - .insert(type_id, Confined::with((seal, state)))?; - } - } - - Ok(self) - } - - fn add_data( - self, - name: impl Into, - seal: impl Into>, - value: impl StrictSerialize, - ) -> Result { - debug_assert!( - !self.deterministic, - "for adding state to deterministic contracts you have to use add_*_det methods" - ); - - let name = name.into(); - let serialized = value.to_strict_serialized::()?; - let state = DataState::from(serialized); - - let type_id = self - .assignments_type(&name) - .ok_or(BuilderError::AssignmentNotFound(name))?; - - self.add_data_raw(type_id, seal, RevealedData::new_random_salt(state)) - } - - fn add_data_det( - self, - name: impl Into, - seal: impl Into>, - state: RevealedData, - ) -> Result { - debug_assert!( - self.deterministic, - "to add owned state in deterministic way the builder has to be created using \ - deterministic constructor" - ); - - let name = name.into(); - let type_id = self - .assignments_type(&name) - .ok_or(BuilderError::AssignmentNotFound(name))?; - - self.add_data_raw(type_id, seal, state) - } - - fn add_data_raw( - mut self, - type_id: AssignmentType, - seal: impl Into>, - state: RevealedData, - ) -> Result { - let state_schema = self.state_schema(type_id); - if let OwnedStateSchema::Structured(_) = *state_schema { - let seal = seal.into(); - match self.data.get_mut(&type_id) { - Some(assignments) => { - assignments.insert(seal, state)?; - } - None => { - self.data.insert(type_id, Confined::with((seal, state)))?; - } - } - } else { - return Err(BuilderError::InvalidStateType(type_id)); - } - Ok(self) - } - - fn add_attachment( - self, - name: impl Into, - seal: impl Into>, - state: AttachState, - ) -> Result { - debug_assert!( - !self.deterministic, - "for adding state to deterministic contracts you have to use add_*_det methods" - ); - - let name = name.into(); - - let type_id = self - .assignments_type(&name) - .ok_or(BuilderError::AssignmentNotFound(name))?; - - self.add_attachment_raw( - type_id, - seal, - RevealedAttach::new_random_salt(state.id, state.media_type), - ) - } - - fn add_attachment_det( - self, - name: impl Into, - seal: impl Into>, - state: RevealedAttach, - ) -> Result { - debug_assert!( - self.deterministic, - "to add owned state in deterministic way the builder has to be created using \ - deterministic constructor" - ); - - let name = name.into(); - - let type_id = self - .assignments_type(&name) - .ok_or(BuilderError::AssignmentNotFound(name))?; - - self.add_attachment_raw(type_id, seal, state) - } - - fn add_attachment_raw( - mut self, - type_id: AssignmentType, - seal: impl Into>, - state: RevealedAttach, - ) -> Result { - let state_schema = self.state_schema(type_id); - if let OwnedStateSchema::Attachment(_) = *state_schema { - let seal = seal.into(); - match self.attachments.get_mut(&type_id) { - Some(assignments) => { - assignments.insert(seal, state)?; - } - None => { - self.attachments - .insert(type_id, Confined::with((seal, state)))?; - } - } - } else { - return Err(BuilderError::InvalidStateType(type_id)); - } - Ok(self) - } - - fn complete( - self, - inputs: Option<&TinyOrdMap>, - ) -> (Schema, Iface, IfaceImpl, GlobalState, Assignments, TypeSystem, AssetTags) { - let owned_state = self.fungible.into_iter().map(|(id, vec)| { - let mut blindings = Vec::with_capacity(vec.len()); - let mut vec = vec - .into_iter() - .map(|(seal, value)| { - blindings.push(value.blinding); - match seal { - BuilderSeal::Revealed(seal) => Assign::Revealed { - seal, - state: value, - lock: none!(), - }, - BuilderSeal::Concealed(seal) => Assign::ConfidentialSeal { - seal, - state: value, - lock: none!(), - }, - } - }) - .collect::>(); - if let Some(assignment) = vec.last_mut() { - blindings.pop(); - let state = assignment - .as_revealed_state_mut() - .expect("builder always operates revealed state"); - let mut inputs = inputs - .map(|i| { - i.iter() - .filter(|(out, _)| out.prev_out.ty == id) - .map(|(_, ts)| match ts { - PersistedState::Amount(_, blinding, _) => *blinding, - _ => panic!("previous state has invalid type"), - }) - .collect::>() - }) - .unwrap_or_default(); - if inputs.is_empty() { - inputs = vec![BlindingFactor::EMPTY]; - } - state.blinding = BlindingFactor::zero_balanced(inputs, blindings).expect( - "malformed set of blinding factors; probably random generator is broken", - ); - } - let state = Confined::try_from_iter(vec).expect("at least one element"); - let state = TypedAssigns::Fungible(state); - (id, state) - }); - let owned_data = self.data.into_iter().map(|(id, vec)| { - let vec_data = vec.into_iter().map(|(seal, value)| match seal { - BuilderSeal::Revealed(seal) => Assign::Revealed { - seal, - state: value, - lock: none!(), - }, - BuilderSeal::Concealed(seal) => Assign::ConfidentialSeal { - seal, - state: value, - lock: none!(), - }, - }); - let state_data = Confined::try_from_iter(vec_data).expect("at least one element"); - let state_data = TypedAssigns::Structured(state_data); - (id, state_data) - }); - let owned_rights = self.rights.into_iter().map(|(id, vec)| { - let vec_data = vec.into_iter().map(|seal| match seal { - BuilderSeal::Revealed(seal) => Assign::Revealed { - seal, - state: none!(), - lock: none!(), - }, - BuilderSeal::Concealed(seal) => Assign::ConfidentialSeal { - seal, - state: none!(), - lock: none!(), - }, - }); - let state_data = Confined::try_from_iter(vec_data).expect("at least one element"); - let state_data = TypedAssigns::Declarative(state_data); - (id, state_data) - }); - let owned_attachments = self.attachments.into_iter().map(|(id, vec)| { - let vec_data = vec.into_iter().map(|(seal, value)| match seal { - BuilderSeal::Revealed(seal) => Assign::Revealed { - seal, - state: value, - lock: none!(), - }, - BuilderSeal::Concealed(seal) => Assign::ConfidentialSeal { - seal, - state: value, - lock: none!(), - }, - }); - let state_data = Confined::try_from_iter(vec_data).expect("at least one element"); - let state_data = TypedAssigns::Attachment(state_data); - (id, state_data) - }); - - let owned_state = Confined::try_from_iter(owned_state).expect("same size"); - let owned_data = Confined::try_from_iter(owned_data).expect("same size"); - let owned_rights = Confined::try_from_iter(owned_rights).expect("same size"); - let owned_attachments = Confined::try_from_iter(owned_attachments).expect("same size"); - - let mut assignments = Assignments::from_inner(owned_state); - assignments - .extend(Assignments::from_inner(owned_data).into_inner()) - .expect("too many assignments"); - assignments - .extend(Assignments::from_inner(owned_rights).into_inner()) - .expect("too many assignments"); - assignments - .extend(Assignments::from_inner(owned_attachments).into_inner()) - .expect("too many assignments"); - - (self.schema, self.iface, self.iimpl, self.global, assignments, self.types, self.asset_tags) - } -} diff --git a/src/interface/contract.rs b/src/interface/contract.rs deleted file mode 100644 index b54e45d2..00000000 --- a/src/interface/contract.rs +++ /dev/null @@ -1,477 +0,0 @@ -// RGB standard library for working with smart contracts on Bitcoin & Lightning -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2019-2024 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2019-2024 LNP/BP Standards Association. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::borrow::Borrow; -use std::cmp::Ordering; -use std::collections::HashMap; - -use amplify::confinement::SmallOrdSet; -use invoice::{Allocation, Amount}; -use rgb::{ - AttachState, ContractId, DataState, OpId, RevealedAttach, RevealedData, RevealedValue, Schema, - VoidState, XOutpoint, XOutputSeal, XWitnessId, -}; -use strict_encoding::{FieldName, StrictDecode, StrictDumb, StrictEncode}; -use strict_types::{StrictVal, TypeSystem}; - -use crate::contract::{KnownState, OutputAssignment}; -use crate::info::ContractInfo; -use crate::interface::{IfaceImpl, OutpointFilter}; -use crate::persistence::ContractStateRead; -use crate::LIB_NAME_RGB_STD; - -#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)] -#[display(doc_comments)] -pub enum ContractError { - /// field name {0} is unknown to the contract interface - FieldNameUnknown(FieldName), -} - -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display, From)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD, tags = custom)] -#[display(inner)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub enum AllocatedState { - #[from(())] - #[from(VoidState)] - #[display("~")] - #[strict_type(tag = 0, dumb)] - Void, - - #[from] - #[from(RevealedValue)] - #[strict_type(tag = 1)] - Amount(Amount), - - #[from] - #[from(RevealedData)] - #[from(Allocation)] - #[strict_type(tag = 2)] - Data(DataState), - - #[from] - #[from(RevealedAttach)] - #[strict_type(tag = 3)] - Attachment(AttachState), -} - -impl KnownState for AllocatedState {} - -pub type OwnedAllocation = OutputAssignment; -pub type RightsAllocation = OutputAssignment; -pub type FungibleAllocation = OutputAssignment; -pub type DataAllocation = OutputAssignment; -pub type AttachAllocation = OutputAssignment; - -pub trait StateChange: Clone + Eq + StrictDumb + StrictEncode + StrictDecode { - type State: KnownState; - fn from_spent(state: Self::State) -> Self; - fn from_received(state: Self::State) -> Self; - fn merge_spent(&mut self, state: Self::State); - fn merge_received(&mut self, state: Self::State); -} - -#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD, tags = custom)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub enum AmountChange { - #[display("-{0}")] - #[strict_type(tag = 0xFF)] - Dec(Amount), - - #[display("0")] - #[strict_type(tag = 0, dumb)] - Zero, - - #[display("+{0}")] - #[strict_type(tag = 0x01)] - Inc(Amount), -} - -impl StateChange for AmountChange { - type State = Amount; - - fn from_spent(state: Self::State) -> Self { AmountChange::Dec(state) } - - fn from_received(state: Self::State) -> Self { AmountChange::Inc(state) } - - fn merge_spent(&mut self, sub: Self::State) { - *self = match self { - AmountChange::Dec(neg) => AmountChange::Dec(*neg + sub), - AmountChange::Zero => AmountChange::Dec(sub), - AmountChange::Inc(pos) => match sub.cmp(pos) { - Ordering::Less => AmountChange::Inc(*pos - sub), - Ordering::Equal => AmountChange::Zero, - Ordering::Greater => AmountChange::Dec(sub - *pos), - }, - }; - } - - fn merge_received(&mut self, add: Self::State) { - *self = match self { - AmountChange::Inc(pos) => AmountChange::Inc(*pos + add), - AmountChange::Zero => AmountChange::Inc(add), - AmountChange::Dec(neg) => match add.cmp(neg) { - Ordering::Less => AmountChange::Dec(*neg - add), - Ordering::Equal => AmountChange::Zero, - Ordering::Greater => AmountChange::Inc(add - *neg), - }, - }; - } -} - -#[derive(Clone, Eq, PartialEq, Hash, Debug)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub struct IfaceOp { - pub opids: SmallOrdSet, // may come from multiple bundles - pub inputs: SmallOrdSet, // may come from multiple bundles - pub state_change: S, - pub payers: SmallOrdSet, - pub beneficiaries: SmallOrdSet, -} - -impl IfaceOp { - fn from_spent(alloc: OutputAssignment) -> Self { - Self { - opids: none!(), - inputs: small_bset![alloc.opout.op], - state_change: C::from_spent(alloc.state), - payers: none!(), - // TODO: Do something with beneficiary info - beneficiaries: none!(), - } - } - fn from_received(alloc: OutputAssignment) -> Self { - Self { - opids: small_bset![alloc.opout.op], - inputs: none!(), - state_change: C::from_received(alloc.state), - // TODO: Do something with payer info - payers: none!(), - beneficiaries: none!(), - } - } - fn merge_spent(&mut self, alloc: OutputAssignment) { - self.inputs - .push(alloc.opout.op) - .expect("internal inconsistency of stash data"); - self.state_change.merge_spent(alloc.state); - // TODO: Do something with beneficiary info - } - fn merge_received(&mut self, alloc: OutputAssignment) { - self.opids - .push(alloc.opout.op) - .expect("internal inconsistency of stash data"); - self.state_change.merge_received(alloc.state); - // TODO: Do something with payer info - } -} - -/// Contract state is an in-memory structure providing API to read structured -/// data from the [`rgb::ContractHistory`]. -#[derive(Clone, Eq, PartialEq, Debug)] -pub struct ContractIface { - pub state: S, - pub schema: Schema, - pub iface: IfaceImpl, - pub types: TypeSystem, - pub info: ContractInfo, -} - -impl ContractIface { - pub fn contract_id(&self) -> ContractId { self.state.contract_id() } - - /// # Panics - /// - /// If data are corrupted and contract schema doesn't match interface - /// implementations. - pub fn global( - &self, - name: impl Into, - ) -> Result + '_, ContractError> { - let name = name.into(); - let type_id = self - .iface - .global_type(&name) - .ok_or(ContractError::FieldNameUnknown(name))?; - let global_schema = self - .schema - .global_types - .get(&type_id) - .expect("schema doesn't match interface"); - Ok(self - .state - .global(type_id) - .expect("schema doesn't match interface") - .map(|data| { - self.types - .strict_deserialize_type(global_schema.sem_id, data.borrow().as_slice()) - .expect("unvalidated contract data in stash") - .unbox() - })) - } - - fn extract_state<'c, A, U>( - &'c self, - state: impl IntoIterator> + 'c, - name: impl Into, - filter: impl OutpointFilter + 'c, - ) -> Result> + 'c, ContractError> - where - A: Clone + KnownState + 'c, - U: From + KnownState + 'c, - { - let name = name.into(); - let type_id = self - .iface - .assignments_type(&name) - .ok_or(ContractError::FieldNameUnknown(name))?; - Ok(state - .into_iter() - .filter(move |outp| outp.opout.ty == type_id) - .filter(move |outp| filter.include_outpoint(outp.seal)) - .cloned() - .map(OutputAssignment::::transmute)) - } - - pub fn rights<'c>( - &'c self, - name: impl Into, - filter: impl OutpointFilter + 'c, - ) -> Result + 'c, ContractError> { - self.extract_state(self.state.rights_all(), name, filter) - } - - pub fn rights_all<'c>( - &'c self, - name: impl Into, - filter: impl OutpointFilter + 'c, - ) -> Result, ContractError> { - Ok(self - .extract_state(self.state.rights_all(), name, filter)? - .collect()) - } - - pub fn fungible<'c>( - &'c self, - name: impl Into, - filter: impl OutpointFilter + 'c, - ) -> Result + 'c, ContractError> { - self.extract_state(self.state.fungible_all(), name, filter) - } - - pub fn fungible_all<'c>( - &'c self, - name: impl Into, - filter: impl OutpointFilter + 'c, - ) -> Result, ContractError> { - Ok(self - .extract_state(self.state.fungible_all(), name, filter)? - .collect()) - } - - pub fn data<'c>( - &'c self, - name: impl Into, - filter: impl OutpointFilter + 'c, - ) -> Result + 'c, ContractError> { - self.extract_state(self.state.data_all(), name, filter) - } - - pub fn data_all<'c>( - &'c self, - name: impl Into, - filter: impl OutpointFilter + 'c, - ) -> Result, ContractError> { - Ok(self - .extract_state(self.state.data_all(), name, filter)? - .collect()) - } - - pub fn attachments<'c>( - &'c self, - name: impl Into, - filter: impl OutpointFilter + 'c, - ) -> Result + 'c, ContractError> { - self.extract_state(self.state.attach_all(), name, filter) - } - - pub fn attachments_all<'c>( - &'c self, - name: impl Into, - filter: impl OutpointFilter + 'c, - ) -> Result, ContractError> { - Ok(self - .extract_state(self.state.attach_all(), name, filter)? - .collect()) - } - - pub fn allocations<'c>( - &'c self, - filter: impl OutpointFilter + Copy + 'c, - ) -> impl Iterator + 'c { - fn f<'a, S, U>( - filter: impl OutpointFilter + 'a, - state: impl IntoIterator> + 'a, - ) -> impl Iterator> + 'a - where - S: Clone + KnownState + 'a, - U: From + KnownState + 'a, - { - state - .into_iter() - .filter(move |outp| filter.include_outpoint(outp.seal)) - .cloned() - .map(OutputAssignment::::transmute) - } - - f(filter, self.state.rights_all()) - .map(OwnedAllocation::from) - .chain(f(filter, self.state.fungible_all()).map(OwnedAllocation::from)) - .chain(f(filter, self.state.data_all()).map(OwnedAllocation::from)) - .chain(f(filter, self.state.attach_all()).map(OwnedAllocation::from)) - } - - pub fn outpoint_allocations( - &self, - outpoint: XOutpoint, - ) -> impl Iterator + '_ { - self.allocations(outpoint) - } - - // TODO: Ignore blank state transition - fn operations<'c, C: StateChange>( - &'c self, - state: impl IntoIterator> + 'c, - allocations: impl Iterator> + 'c, - ) -> HashMap> - where - C::State: 'c, - { - fn f<'a, S, U>( - state: impl IntoIterator> + 'a, - ) -> impl Iterator> + 'a - where - S: Clone + KnownState + 'a, - U: From + KnownState + 'a, - { - state.into_iter().map(OutputAssignment::::transmute) - } - - let spent = f::<_, C::State>(state).map(OutputAssignment::from); - let mut ops = HashMap::>::new(); - for alloc in spent { - let Some(witness_id) = alloc.witness else { - continue; - }; - if let Some(op) = ops.get_mut(&witness_id) { - op.merge_spent(alloc); - } else { - ops.insert(witness_id, IfaceOp::from_spent(alloc)); - } - } - - for alloc in allocations { - let Some(witness_id) = alloc.witness else { - continue; - }; - if let Some(op) = ops.get_mut(&witness_id) { - op.merge_received(alloc); - } else { - ops.insert(witness_id, IfaceOp::from_received(alloc)); - } - } - - ops - } - - pub fn fungible_ops<'c, C: StateChange>( - &'c self, - name: impl Into, - outpoint_filter: impl OutpointFilter + Copy + 'c, - ) -> Result>, ContractError> { - Ok(self.operations( - self.state - .fungible_all() - .copied() - .map(OutputAssignment::transmute), - self.fungible(name, outpoint_filter)?, - )) - } - - pub fn data_ops<'c, C: StateChange>( - &'c self, - name: impl Into, - outpoint_filter: impl OutpointFilter + Copy + 'c, - ) -> Result>, ContractError> { - Ok(self.operations( - self.state - .data_all() - .cloned() - .map(OutputAssignment::transmute), - self.data(name, outpoint_filter)?, - )) - } - - pub fn rights_ops<'c, C: StateChange>( - &'c self, - name: impl Into, - outpoint_filter: impl OutpointFilter + Copy + 'c, - ) -> Result>, ContractError> { - Ok(self.operations( - self.state - .rights_all() - .copied() - .map(OutputAssignment::transmute), - self.rights(name, outpoint_filter)?, - )) - } - - pub fn attachment_ops<'c, C: StateChange>( - &'c self, - name: impl Into, - outpoint_filter: impl OutpointFilter + Copy + 'c, - ) -> Result>, ContractError> { - Ok(self.operations( - self.state - .attach_all() - .cloned() - .map(OutputAssignment::transmute), - self.attachments(name, outpoint_filter)?, - )) - } -} diff --git a/src/interface/contractum.rs b/src/interface/contractum.rs deleted file mode 100644 index 01cbdbe6..00000000 --- a/src/interface/contractum.rs +++ /dev/null @@ -1,347 +0,0 @@ -// RGB standard library for working with smart contracts on Bitcoin & Lightning -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2019-2024 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2019-2024 LNP/BP Standards Association. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::collections::HashMap; -use std::fmt; -use std::fmt::{Display, Formatter}; - -use amplify::confinement::TinyOrdSet; -use rgb::Occurrences; -use strict_encoding::{FieldName, TypeName, VariantName}; -use strict_types::{SemId, SymbolicSys}; - -use super::{ - ArgMap, ExtensionIface, GenesisIface, Iface, IfaceId, Modifier, OwnedIface, TransitionIface, -}; - -struct ArgMapDisplay<'a>(&'a ArgMap); - -impl<'a> Display for ArgMapDisplay<'a> { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - for (i, (name, occ)) in self.0.iter().enumerate() { - if i > 0 { - f.write_str(", ")? - } - write!(f, "{name}")?; - match occ { - Occurrences::Once => Ok(()), - Occurrences::NoneOrOnce => write!(f, "(?)"), - Occurrences::NoneOrMore => write!(f, "(*)"), - Occurrences::OnceOrMore => write!(f, "(+)"), - Occurrences::NoneOrUpTo(to) => write!(f, "(..{to})"), - Occurrences::OnceOrUpTo(to) => write!(f, "(1..{to})"), - Occurrences::Exactly(v) => write!(f, "({v})"), - Occurrences::Range(r) => write!(f, "({}..{})", r.start(), r.end()), - }?; - } - Ok(()) - } -} - -struct OpIfaceDisplay<'a> { - metadata: &'a TinyOrdSet, - globals: &'a ArgMap, - assignments: &'a ArgMap, - valencies: &'a TinyOrdSet, - errors: &'a TinyOrdSet, -} - -impl<'a> OpIfaceDisplay<'a> { - fn genesis(op: &'a GenesisIface) -> Self { - Self { - metadata: &op.metadata, - globals: &op.globals, - assignments: &op.assignments, - valencies: &op.valencies, - errors: &op.errors, - } - } - - fn transition(op: &'a TransitionIface) -> Self { - Self { - metadata: &op.metadata, - globals: &op.globals, - assignments: &op.assignments, - valencies: &op.valencies, - errors: &op.errors, - } - } - - fn extension(op: &'a ExtensionIface) -> Self { - Self { - metadata: &op.metadata, - globals: &op.globals, - assignments: &op.assignments, - valencies: &op.valencies, - errors: &op.errors, - } - } -} - -impl<'a> Display for OpIfaceDisplay<'a> { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - if !self.errors.is_empty() { - write!(f, "\t\terrors: ")?; - for (i, name) in self.errors.iter().enumerate() { - if i > 0 { - f.write_str(", ")?; - } - write!(f, "{name}")?; - } - writeln!(f)?; - } - - if !self.metadata.is_empty() { - write!(f, "\t\tmeta: ")?; - for (i, meta) in self.metadata.iter().enumerate() { - if i > 0 { - write!(f, ", ")?; - } - write!(f, "{meta}")?; - } - writeln!(f)?; - } - if !self.globals.is_empty() { - writeln!(f, "\t\tglobals: {}", ArgMapDisplay(self.globals))?; - } - if !self.valencies.is_empty() { - write!(f, "\t\tvalencies: ")?; - for (i, name) in self.valencies.iter().enumerate() { - if i > 0 { - f.write_str(", ")? - } - write!(f, "{name}")?; - } - writeln!(f)?; - } - if !self.assignments.is_empty() { - writeln!(f, "\t\tassigns: {}", ArgMapDisplay(self.assignments))?; - } - Ok(()) - } -} - -pub struct IfaceDisplay<'a> { - iface: &'a Iface, - externals: &'a HashMap, - types: &'a SymbolicSys, -} - -impl<'a> IfaceDisplay<'a> { - pub fn new( - iface: &'a Iface, - externals: &'a HashMap, - types: &'a SymbolicSys, - ) -> Self { - Self { - iface, - types, - externals, - } - } -} - -impl<'a> Display for IfaceDisplay<'a> { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - fn sugar(f: &mut Formatter<'_>, required: bool, multiple: bool) -> fmt::Result { - match (required, multiple) { - (true, true) => write!(f, "(+)"), - (false, false) => write!(f, "(?)"), - (false, true) => write!(f, "(*)"), - _ => Ok(()), - } - } - fn resolve(f: &mut Formatter<'_>, types: &SymbolicSys, id: SemId) -> fmt::Result { - match types.lookup(id) { - Some(fqn) => write!(f, "{fqn}"), - None => write!(f, "{id:-} -- type name unknown"), - } - } - fn opsugar( - f: &mut Formatter<'_>, - pred: &str, - name: Option<&FieldName>, - modifier: Modifier, - optional: bool, - default: bool, - ) -> fmt::Result { - write!(f, "\t{pred}")?; - if let Some(name) = name { - write!(f, " {name}")?; - } - let mut modifiers = vec![]; - if !optional { - modifiers.push("required"); - } - if default { - modifiers.push("default"); - } - match modifier { - Modifier::Final => modifiers.push("final"), - Modifier::Abstract => modifiers.push("abstract"), - Modifier::Override => modifiers.push("override"), - } - - if !modifiers.is_empty() { - f.write_str(": ")?; - } - for (i, name) in modifiers.into_iter().enumerate() { - if i > 0 { - f.write_str(", ")?; - } - f.write_str(name)?; - } - writeln!(f) - } - - writeln!(f, "@version({:#})", self.iface.version)?; - writeln!(f, "@id({})", self.iface.iface_id())?; - if !self.iface.developer.is_anonymous() { - writeln!(f, "@developer(\"{}\")", self.iface.developer)?; - } - writeln!(f, "@timestamp({})", self.iface.timestamp)?; - write!(f, "interface {}", self.iface.name)?; - if !self.iface.inherits.is_empty() { - f.write_str(": ")?; - for (index, id) in self.iface.inherits.iter().enumerate() { - if index > 0 { - f.write_str(", ")?; - } - match self.externals.get(id) { - Some(name) => write!(f, "{name}")?, - None => writeln!(f, "{id:-}")?, - } - } - } - writeln!(f)?; - - for (fname, semid) in &self.iface.metadata { - write!(f, "\tmeta {fname}: ")?; - match self.types.lookup(*semid) { - Some(fqn) => write!(f, "{fqn}"), - None => write!(f, "{semid} -- type name is unknown"), - }?; - writeln!(f)?; - } - if !self.iface.metadata.is_empty() { - writeln!(f)?; - } - - for (fname, g) in &self.iface.global_state { - write!(f, "\tglobal {fname}")?; - sugar(f, g.required, g.multiple)?; - write!(f, ": ")?; - match g.sem_id { - Some(id) => resolve(f, self.types, id)?, - None => write!(f, "Any")?, - } - writeln!(f)?; - } - writeln!(f)?; - - for (fname, a) in &self.iface.assignments { - write!(f, "\t")?; - match a.public { - true => write!(f, "public ")?, - false => write!(f, "owned ")?, - } - write!(f, "{fname}")?; - sugar(f, a.required, a.multiple)?; - f.write_str(": ")?; - match a.owned_state { - OwnedIface::Any => write!(f, "AnyType")?, - OwnedIface::Amount => write!(f, "Zk64")?, - OwnedIface::AnyData => write!(f, "Any")?, - OwnedIface::AnyAttach => write!(f, "AnyAttachment")?, - OwnedIface::Rights => write!(f, "Rights")?, - OwnedIface::Data(id) => resolve(f, self.types, id)?, - } - writeln!(f)?; - } - if !self.iface.assignments.is_empty() { - writeln!(f)?; - } - - for (fname, v) in &self.iface.valencies { - write!(f, "\tvalency {fname}")?; - if !v.required { - write!(f, "(?)")?; - } - writeln!(f)?; - } - if !self.iface.valencies.is_empty() { - writeln!(f)?; - } - - for (name, descr) in &self.iface.errors { - writeln!(f, "\terror {name}")?; - writeln!(f, "\t\t\"{descr}\"")?; - } - if !self.iface.errors.is_empty() { - writeln!(f)?; - } - - let op = OpIfaceDisplay::genesis(&self.iface.genesis); - opsugar(f, "genesis", None, self.iface.genesis.modifier, true, false)?; - writeln!(f, "{op}")?; - - for (name, t) in &self.iface.transitions { - let default = self.iface.default_operation.as_ref() == Some(name); - opsugar(f, "transition", Some(name), t.modifier, t.optional, default)?; - - let op = OpIfaceDisplay::transition(t); - write!(f, "{op}")?; - - if let Some(ref d) = t.default_assignment { - writeln!(f, "\t\tdefault: {d}")?; - } - - writeln!(f, "\t\tinputs: {}", ArgMapDisplay(&t.inputs))?; - - writeln!(f)?; - } - - for (name, e) in &self.iface.extensions { - let default = self.iface.default_operation.as_ref() == Some(name); - opsugar(f, "extension", Some(name), e.modifier, e.optional, default)?; - - let op = OpIfaceDisplay::extension(e); - write!(f, "{op}")?; - - if let Some(ref d) = e.default_assignment { - writeln!(f, "\t\tdefault: {d}")?; - } - - write!(f, "\t\tredeems: ")?; - for (i, name) in e.redeems.iter().enumerate() { - if i > 0 { - f.write_str(", ")? - } - write!(f, "{name}")?; - } - writeln!(f)?; - - writeln!(f)?; - } - - Ok(()) - } -} diff --git a/src/interface/filter.rs b/src/interface/filter.rs deleted file mode 100644 index 5e802fff..00000000 --- a/src/interface/filter.rs +++ /dev/null @@ -1,110 +0,0 @@ -// RGB standard library for working with smart contracts on Bitcoin & Lightning -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2019-2024 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2019-2024 LNP/BP Standards Association. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; -use std::ops::Deref; - -use rgb::XOutpoint; - -pub trait OutpointFilter { - fn include_outpoint(&self, outpoint: impl Into) -> bool; -} - -pub struct FilterIncludeAll; -pub struct FilterExclude(pub T); - -impl OutpointFilter for FilterIncludeAll { - fn include_outpoint(&self, _: impl Into) -> bool { true } -} - -impl OutpointFilter for FilterExclude { - fn include_outpoint(&self, outpoint: impl Into) -> bool { - !self.0.include_outpoint(outpoint.into()) - } -} - -impl OutpointFilter for &T { - fn include_outpoint(&self, outpoint: impl Into) -> bool { - (*self).include_outpoint(outpoint) - } -} - -impl OutpointFilter for &mut T { - fn include_outpoint(&self, outpoint: impl Into) -> bool { - self.deref().include_outpoint(outpoint) - } -} - -impl OutpointFilter for Option { - fn include_outpoint(&self, outpoint: impl Into) -> bool { - self.as_ref() - .map(|filter| filter.include_outpoint(outpoint)) - .unwrap_or(true) - } -} - -impl OutpointFilter for XOutpoint { - fn include_outpoint(&self, outpoint: impl Into) -> bool { *self == outpoint.into() } -} - -impl OutpointFilter for [XOutpoint; LEN] { - fn include_outpoint(&self, outpoint: impl Into) -> bool { - self.contains(&outpoint.into()) - } -} - -impl OutpointFilter for &[XOutpoint] { - fn include_outpoint(&self, outpoint: impl Into) -> bool { - self.contains(&outpoint.into()) - } -} - -impl OutpointFilter for Vec { - fn include_outpoint(&self, outpoint: impl Into) -> bool { - self.contains(&outpoint.into()) - } -} - -impl OutpointFilter for HashSet { - fn include_outpoint(&self, outpoint: impl Into) -> bool { - self.contains(&outpoint.into()) - } -} - -impl OutpointFilter for BTreeSet { - fn include_outpoint(&self, outpoint: impl Into) -> bool { - self.contains(&outpoint.into()) - } -} - -impl OutpointFilter for HashMap { - fn include_outpoint(&self, outpoint: impl Into) -> bool { - let outpoint = outpoint.into(); - self.keys().any(|o| *o == outpoint) - } -} - -impl OutpointFilter for BTreeMap { - fn include_outpoint(&self, outpoint: impl Into) -> bool { - let outpoint = outpoint.into(); - self.keys().any(|o| *o == outpoint) - } -} diff --git a/src/interface/iface.rs b/src/interface/iface.rs deleted file mode 100644 index c6cbfaaf..00000000 --- a/src/interface/iface.rs +++ /dev/null @@ -1,677 +0,0 @@ -// RGB standard library for working with smart contracts on Bitcoin & Lightning -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2019-2024 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2019-2024 LNP/BP Standards Association. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::cmp::Ordering; -use std::collections::HashMap; -use std::fmt::{self, Debug, Display, Formatter}; -use std::hash::{Hash, Hasher}; -use std::str::FromStr; - -use amplify::confinement::{TinyOrdMap, TinyOrdSet, TinyString, TinyVec}; -use amplify::{ByteArray, Bytes32}; -use baid64::{Baid64ParseError, DisplayBaid64, FromBaid64Str}; -use chrono::{DateTime, TimeZone, Utc}; -use commit_verify::{CommitId, CommitmentId, DigestExt, Sha256}; -use rgb::{Identity, Occurrences}; -use strict_encoding::{ - FieldName, StrictDecode, StrictDeserialize, StrictDumb, StrictEncode, StrictSerialize, - StrictType, TypeName, VariantName, -}; -use strict_types::{SemId, SymbolicSys, TypeLib}; - -use crate::interface::{ContractIface, IfaceDisplay, IfaceImpl, VerNo}; -use crate::persistence::{ContractStateRead, SchemaIfaces}; -use crate::LIB_NAME_RGB_STD; - -/// Interface identifier. -/// -/// Interface identifier commits to all the interface data. -#[derive(Wrapper, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)] -#[wrapper(Deref, BorrowSlice, Hex, Index, RangeOps)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD)] -pub struct IfaceId( - #[from] - #[from([u8; 32])] - Bytes32, -); - -impl From for IfaceId { - fn from(hasher: Sha256) -> Self { hasher.finish().into() } -} - -impl CommitmentId for IfaceId { - const TAG: &'static str = "urn:lnp-bp:rgb:interface#2024-02-04"; -} - -impl DisplayBaid64 for IfaceId { - const HRI: &'static str = "rgb:ifc"; - const CHUNKING: bool = true; - const PREFIX: bool = true; - const EMBED_CHECKSUM: bool = false; - const MNEMONIC: bool = true; - fn to_baid64_payload(&self) -> [u8; 32] { self.to_byte_array() } -} -impl FromBaid64Str for IfaceId {} -impl FromStr for IfaceId { - type Err = Baid64ParseError; - fn from_str(s: &str) -> Result { Self::from_baid64_str(s) } -} -impl Display for IfaceId { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { self.fmt_baid64(f) } -} - -impl_serde_baid64!(IfaceId); - -impl IfaceId { - pub const fn from_array(id: [u8; 32]) -> Self { IfaceId(Bytes32::from_array(id)) } -} - -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, From)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub enum IfaceRef { - #[from] - #[from(&'static str)] - Name(TypeName), - #[from] - Id(IfaceId), -} - -impl Display for IfaceRef { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - IfaceRef::Name(name) => f.write_str(name.as_str()), - IfaceRef::Id(id) => { - if f.alternate() { - write!(f, "{}", id.to_baid64_mnemonic()) - } else { - write!(f, "{}", id) - } - } - } - } -} - -#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] -pub enum Req { - Optional, - Required, - NoneOrMore, - OneOrMore, -} - -impl Req { - pub fn is_required(self) -> bool { self == Req::Required || self == Req::OneOrMore } - pub fn is_multiple(self) -> bool { self == Req::NoneOrMore || self == Req::OneOrMore } -} - -#[derive(Clone, PartialEq, Eq, Hash, Debug)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub struct ValencyIface { - pub required: bool, -} - -#[derive(Clone, PartialEq, Eq, Hash, Debug)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub struct GlobalIface { - pub sem_id: Option, - pub required: bool, - pub multiple: bool, -} - -impl GlobalIface { - pub fn any(req: Req) -> Self { - GlobalIface { - sem_id: None, - required: req.is_required(), - multiple: req.is_multiple(), - } - } - pub fn optional(sem_id: SemId) -> Self { - GlobalIface { - sem_id: Some(sem_id), - required: false, - multiple: false, - } - } - pub fn required(sem_id: SemId) -> Self { - GlobalIface { - sem_id: Some(sem_id), - required: true, - multiple: false, - } - } - pub fn none_or_many(sem_id: SemId) -> Self { - GlobalIface { - sem_id: Some(sem_id), - required: false, - multiple: true, - } - } - pub fn one_or_many(sem_id: SemId) -> Self { - GlobalIface { - sem_id: Some(sem_id), - required: true, - multiple: true, - } - } -} - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD, tags = order)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub struct AssignIface { - pub owned_state: OwnedIface, - pub public: bool, - pub required: bool, - pub multiple: bool, -} - -impl AssignIface { - pub fn public(owned_state: OwnedIface, req: Req) -> Self { - AssignIface { - owned_state, - public: true, - required: req.is_required(), - multiple: req.is_multiple(), - } - } - - pub fn private(owned_state: OwnedIface, req: Req) -> Self { - AssignIface { - owned_state, - public: false, - required: req.is_required(), - multiple: req.is_multiple(), - } - } -} - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD, tags = order)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub enum OwnedIface { - #[strict_type(dumb)] - Any, - Rights, - Amount, - AnyData, - AnyAttach, - Data(SemId), -} - -impl OwnedIface { - pub fn sem_id(&self) -> Option { - if let Self::Data(id) = self { Some(*id) } else { None } - } -} - -pub type ArgMap = TinyOrdMap; - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display, Default)] -#[derive(StrictType, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD, into_u8, try_from_u8, tags = repr)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -#[display(lowercase)] -#[repr(u8)] -pub enum Modifier { - Abstract = 0, - Override = 1, - #[default] - Final = 0xFF, -} - -#[derive(Clone, PartialEq, Eq, Hash, Debug)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub struct GenesisIface { - pub modifier: Modifier, - pub metadata: TinyOrdSet, - pub globals: ArgMap, - pub assignments: ArgMap, - pub valencies: TinyOrdSet, - pub errors: TinyOrdSet, -} - -#[derive(Clone, PartialEq, Eq, Hash, Debug)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub struct ExtensionIface { - pub modifier: Modifier, - /// Defines whence schema may omit providing this operation. - pub optional: bool, - pub metadata: TinyOrdSet, - pub globals: ArgMap, - pub assignments: ArgMap, - pub redeems: TinyOrdSet, - pub valencies: TinyOrdSet, - pub errors: TinyOrdSet, - pub default_assignment: Option, -} - -#[derive(Clone, PartialEq, Eq, Hash, Debug)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub struct TransitionIface { - pub modifier: Modifier, - /// Defines whence schema may omit providing this operation. - pub optional: bool, - pub metadata: TinyOrdSet, - pub globals: ArgMap, - pub inputs: ArgMap, - pub assignments: ArgMap, - pub valencies: TinyOrdSet, - pub errors: TinyOrdSet, - pub default_assignment: Option, -} - -/// A class of interfaces: one or several interfaces inheriting from each other. -/// -/// Interface standards like RGB20, RGB21 and RGB25 are actually interface -/// classes. -pub trait IfaceClass: Clone + Default { - const IFACE_NAME: &'static str; - const IFACE_IDS: &'static [IfaceId]; - - type Wrapper: IfaceWrapper; - - fn stl(&self) -> TypeLib; - fn iface(&self) -> Iface; - fn iface_id(&self) -> IfaceId; -} - -/// The instances implementing this trait are used as wrappers around -/// [`ContractIface`] object, allowing a simple API matching the interface class -/// requirements. -pub trait IfaceWrapper { - /// Object which represent concise summary about a contract; - type Info: Clone + Eq + Debug; - - fn with(iface: ContractIface) -> Self; - - /// Constructs information object describing a specific class in terms of - /// the interface class. - fn info(&self) -> Self::Info; -} - -/// Interface definition. -#[derive(Clone, Eq, Debug)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD)] -#[derive(CommitEncode)] -#[commit_encode(strategy = strict, id = IfaceId)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub struct Iface { - pub version: VerNo, - pub name: TypeName, - pub inherits: TinyVec, // TODO: Replace with TinyIndexSet - pub timestamp: i64, - pub metadata: TinyOrdMap, - pub global_state: TinyOrdMap, - pub assignments: TinyOrdMap, - pub valencies: TinyOrdMap, - pub genesis: GenesisIface, - pub transitions: TinyOrdMap, - pub extensions: TinyOrdMap, - pub default_operation: Option, - pub errors: TinyOrdMap, - pub developer: Identity, -} - -impl PartialEq for Iface { - fn eq(&self, other: &Self) -> bool { self.iface_id() == other.iface_id() } -} - -impl Ord for Iface { - fn cmp(&self, other: &Self) -> Ordering { self.iface_id().cmp(&other.iface_id()) } -} - -impl PartialOrd for Iface { - fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } -} - -impl Hash for Iface { - fn hash(&self, state: &mut H) { state.write(self.iface_id().as_slice()) } -} - -impl StrictSerialize for Iface {} -impl StrictDeserialize for Iface {} - -impl Iface { - #[inline] - pub fn iface_id(&self) -> IfaceId { self.commit_id() } - - pub fn display<'a>( - &'a self, - externals: &'a HashMap, - sys: &'a SymbolicSys, - ) -> IfaceDisplay<'a> { - IfaceDisplay::new(self, externals, sys) - } - - pub fn types(&self) -> impl Iterator + '_ { - self.metadata - .values() - .copied() - .chain(self.global_state.values().filter_map(|i| i.sem_id)) - .chain( - self.assignments - .values() - .filter_map(|i| i.owned_state.sem_id()), - ) - } - - pub fn find_abstractable_impl<'a>( - &self, - schema_ifaces: &'a SchemaIfaces, - ) -> Option<&'a IfaceImpl> { - schema_ifaces.get(self.iface_id()).or_else(|| { - self.inherits - .iter() - .rev() - .find_map(move |parent| schema_ifaces.get(*parent)) - }) - } - - pub fn check(&self) -> Result<(), Vec> { - let proc_globals = |op_name: &OpName, - globals: &ArgMap, - errors: &mut Vec| { - for (name, occ) in globals { - if let Some(g) = self.global_state.get(name) { - if occ.min_value() > 1 && !g.multiple { - errors.push(IfaceInconsistency::MultipleGlobal( - op_name.clone(), - name.clone(), - )); - } - } else { - errors.push(IfaceInconsistency::UnknownGlobal(op_name.clone(), name.clone())); - } - } - }; - let proc_assignments = - |op_name: &OpName, assignments: &ArgMap, errors: &mut Vec| { - for (name, occ) in assignments { - if let Some(a) = self.assignments.get(name) { - if occ.min_value() > 1 && !a.multiple { - errors.push(IfaceInconsistency::MultipleAssignment( - op_name.clone(), - name.clone(), - )); - } - } else { - errors.push(IfaceInconsistency::UnknownAssignment( - op_name.clone(), - name.clone(), - )); - } - } - }; - let proc_valencies = |op_name: &OpName, - valencies: &TinyOrdSet, - errors: &mut Vec| { - for name in valencies { - if self.valencies.get(name).is_none() { - errors.push(IfaceInconsistency::UnknownValency(op_name.clone(), name.clone())); - } - } - }; - let proc_errors = |op_name: &OpName, - errs: &TinyOrdSet, - errors: &mut Vec| { - for name in errs { - if !self.errors.contains_key(name) { - errors.push(IfaceInconsistency::UnknownError(op_name.clone(), name.clone())); - } - } - }; - - let mut errors = vec![]; - - let now = Utc::now(); - match Utc.timestamp_opt(self.timestamp, 0).single() { - Some(ts) if ts > now => errors.push(IfaceInconsistency::FutureTimestamp(ts)), - None => errors.push(IfaceInconsistency::InvalidTimestamp(self.timestamp)), - _ => {} - } - - for name in &self.genesis.metadata { - if !self.metadata.contains_key(name) { - errors.push(IfaceInconsistency::UnknownMetadata(OpName::Genesis, name.clone())); - } - } - proc_globals(&OpName::Genesis, &self.genesis.globals, &mut errors); - proc_assignments(&OpName::Genesis, &self.genesis.assignments, &mut errors); - proc_valencies(&OpName::Genesis, &self.genesis.valencies, &mut errors); - proc_errors(&OpName::Genesis, &self.genesis.errors, &mut errors); - - for (name, t) in &self.transitions { - let op_name = OpName::Transition(name.clone()); - - for name in &t.metadata { - if !self.metadata.contains_key(name) { - errors.push(IfaceInconsistency::UnknownMetadata(op_name.clone(), name.clone())); - } - } - proc_globals(&op_name, &t.globals, &mut errors); - proc_assignments(&op_name, &t.assignments, &mut errors); - proc_valencies(&op_name, &t.valencies, &mut errors); - proc_errors(&op_name, &t.errors, &mut errors); - - for (name, occ) in &t.inputs { - if let Some(a) = self.assignments.get(name) { - if occ.min_value() > 1 && !a.multiple { - errors.push(IfaceInconsistency::MultipleInputs( - op_name.clone(), - name.clone(), - )); - } - } else { - errors.push(IfaceInconsistency::UnknownInput(op_name.clone(), name.clone())); - } - } - if let Some(ref name) = t.default_assignment { - if t.assignments.get(name).is_none() { - errors - .push(IfaceInconsistency::UnknownDefaultAssignment(op_name, name.clone())); - } - } - } - - for (name, e) in &self.extensions { - let op_name = OpName::Extension(name.clone()); - - for name in &e.metadata { - if !self.metadata.contains_key(name) { - errors.push(IfaceInconsistency::UnknownMetadata(op_name.clone(), name.clone())); - } - } - proc_globals(&op_name, &e.globals, &mut errors); - proc_assignments(&op_name, &e.assignments, &mut errors); - proc_valencies(&op_name, &e.valencies, &mut errors); - proc_errors(&op_name, &e.errors, &mut errors); - - for name in &e.redeems { - if self.valencies.get(name).is_none() { - errors.push(IfaceInconsistency::UnknownRedeem(op_name.clone(), name.clone())); - } - } - if let Some(ref name) = e.default_assignment { - if e.assignments.get(name).is_none() { - errors - .push(IfaceInconsistency::UnknownDefaultAssignment(op_name, name.clone())); - } - } - } - - for name in self.transitions.keys() { - if self.extensions.contains_key(name) { - errors.push(IfaceInconsistency::RepeatedOperationName(name.clone())); - } - } - - if let Some(ref name) = self.default_operation { - if self.transitions.get(name).is_none() && self.extensions.get(name).is_none() { - errors.push(IfaceInconsistency::UnknownDefaultOp(name.clone())); - } - } - - for (name, g) in &self.global_state { - if g.required && self.genesis.globals.get(name).is_none() { - errors.push(IfaceInconsistency::RequiredGlobalAbsent(name.clone())); - } - } - for (name, a) in &self.assignments { - if a.required && self.genesis.assignments.get(name).is_none() { - errors.push(IfaceInconsistency::RequiredAssignmentAbsent(name.clone())); - } - } - for (name, v) in &self.valencies { - if v.required && self.genesis.valencies.get(name).is_none() { - errors.push(IfaceInconsistency::RequiredValencyAbsent(name.clone())); - } - } - - if errors.is_empty() { Ok(()) } else { Err(errors) } - } - - // TODO: Implement checking interface inheritance. - /* - pub fn check_inheritance<'a>(&self, ifaces: impl IntoIterator) -> Result<(), Vec> { - // check for the depth - } - */ - - // TODO: Implement checking types against presence in a type system. - /* - pub fn check_types(&self, sys: &TypeSystem) -> Result<(), Vec> { - for g in self.global_state.values() { - if let Some(id) = g.sem_id { - - } - } - } - */ -} - -#[derive(Clone, Eq, PartialEq, Hash, Debug, Display)] -pub enum OpName { - #[display("genesis")] - Genesis, - #[display("transition '{0}'")] - Transition(FieldName), - #[display("extension '{0}'")] - Extension(FieldName), -} - -#[derive(Clone, Eq, PartialEq, Hash, Debug, Display, Error)] -#[display(doc_comments)] -pub enum IfaceInconsistency { - /// timestamp is invalid ({0}). - InvalidTimestamp(i64), - /// timestamp in the future ({0}). - FutureTimestamp(DateTime), - /// unknown global state '{1}' referenced from {0}. - UnknownGlobal(OpName, FieldName), - /// unknown valency '{1}' referenced from {0}. - UnknownValency(OpName, FieldName), - /// unknown input '{1}' referenced from {0}. - UnknownRedeem(OpName, FieldName), - /// unknown assignment '{1}' referenced from {0}. - UnknownAssignment(OpName, FieldName), - /// unknown input '{1}' referenced from {0}. - UnknownInput(OpName, FieldName), - /// unknown error '{1}' referenced from {0}. - UnknownError(OpName, VariantName), - /// unknown default assignment '{1}' referenced from {0}. - UnknownDefaultAssignment(OpName, FieldName), - /// unknown default operation '{0}'. - UnknownDefaultOp(FieldName), - /// unknown metadata '{1}' in {0}. - UnknownMetadata(OpName, FieldName), - /// global state '{1}' must have a unique single value, but operation {0} - /// defines multiple global state of this type. - MultipleGlobal(OpName, FieldName), - /// assignment '{1}' must be unique, but operation {0} defines multiple - /// assignments of this type. - MultipleAssignment(OpName, FieldName), - /// assignment '{1}' is unique, but operation {0} defines multiple inputs of - /// this type, which is not possible. - MultipleInputs(OpName, FieldName), - /// operation name '{0}' is used by both state transition and extension. - RepeatedOperationName(FieldName), - /// global state '{0}' is required, but genesis doesn't define it. - RequiredGlobalAbsent(FieldName), - /// assignment '{0}' is required, but genesis doesn't define it. - RequiredAssignmentAbsent(FieldName), - /// valency '{0}' is required, but genesis doesn't define it. - RequiredValencyAbsent(FieldName), -} diff --git a/src/interface/iimpl.rs b/src/interface/iimpl.rs deleted file mode 100644 index bad1a1c6..00000000 --- a/src/interface/iimpl.rs +++ /dev/null @@ -1,559 +0,0 @@ -// RGB standard library for working with smart contracts on Bitcoin & Lightning -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2019-2024 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2019-2024 LNP/BP Standards Association. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::collections::HashMap; -use std::fmt::{self, Display, Formatter}; -use std::str::FromStr; - -use amplify::confinement::TinyOrdSet; -use amplify::{ByteArray, Bytes32}; -use baid64::{Baid64ParseError, DisplayBaid64, FromBaid64Str}; -use chrono::{DateTime, TimeZone, Utc}; -use commit_verify::{CommitId, CommitmentId, DigestExt, Sha256}; -use rgb::{ - impl_serde_baid64, AssignmentType, ExtensionType, GlobalStateType, Identity, MetaType, Schema, - SchemaId, TransitionType, ValencyType, -}; -use strict_encoding::{FieldName, StrictDumb, VariantName}; -use strict_types::encoding::{StrictDecode, StrictEncode, StrictType}; - -use crate::interface::iface::IfaceId; -use crate::interface::{Iface, VerNo}; -use crate::{ReservedBytes, LIB_NAME_RGB_STD}; - -pub trait SchemaTypeIndex: - Copy + Eq + Ord + StrictType + StrictDumb + StrictEncode + StrictDecode -{ -} -impl SchemaTypeIndex for u8 {} // Error types -impl SchemaTypeIndex for MetaType {} -impl SchemaTypeIndex for GlobalStateType {} -impl SchemaTypeIndex for AssignmentType {} -impl SchemaTypeIndex for ValencyType {} -impl SchemaTypeIndex for ExtensionType {} -impl SchemaTypeIndex for TransitionType {} - -/// Interface identifier. -/// -/// Interface identifier commits to all the interface data. -#[derive(Wrapper, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)] -#[wrapper(Deref, BorrowSlice, Hex, Index, RangeOps)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD)] -pub struct ImplId( - #[from] - #[from([u8; 32])] - Bytes32, -); - -impl From for ImplId { - fn from(hasher: Sha256) -> Self { hasher.finish().into() } -} - -impl CommitmentId for ImplId { - const TAG: &'static str = "urn:lnp-bp:rgb:iface-impl#2024-02-04"; -} - -impl DisplayBaid64 for ImplId { - const HRI: &'static str = "rgb:imp"; - const CHUNKING: bool = true; - const PREFIX: bool = true; - const EMBED_CHECKSUM: bool = false; - const MNEMONIC: bool = true; - fn to_baid64_payload(&self) -> [u8; 32] { self.to_byte_array() } -} -impl FromBaid64Str for ImplId {} -impl FromStr for ImplId { - type Err = Baid64ParseError; - fn from_str(s: &str) -> Result { Self::from_baid64_str(s) } -} -impl Display for ImplId { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { self.fmt_baid64(f) } -} - -impl_serde_baid64!(ImplId); - -impl ImplId { - pub const fn from_array(id: [u8; 32]) -> Self { ImplId(Bytes32::from_array(id)) } -} - -/// Maps certain form of type id (global or owned state or a valency) to a -/// human-readable name. -/// -/// Two distinct [`NamedField`] objects must always have both different state -/// ids and names. -#[derive(Clone, Eq, PartialOrd, Ord, Debug)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub struct NamedField { - pub id: T, - pub name: FieldName, - /// Reserved bytes for storing information about value transformation - /// procedures - pub reserved: ReservedBytes<4usize>, -} - -impl PartialEq for NamedField -where T: SchemaTypeIndex -{ - fn eq(&self, other: &Self) -> bool { self.id == other.id || self.name == other.name } -} - -impl NamedField { - pub fn with(id: T, name: FieldName) -> NamedField { - NamedField { - id, - name, - reserved: default!(), - } - } -} - -/// Maps certain form of type id (global or owned state or a valency) to a -/// human-readable name. -/// -/// Two distinct [`crate::interface::NamedField`] objects must always have both -/// different state ids and names. -#[derive(Clone, Eq, PartialOrd, Ord, Debug)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub struct NamedVariant { - pub id: T, - pub name: VariantName, - /// Reserved bytes for storing information about value transformation - /// procedures - pub reserved: ReservedBytes<4usize>, -} - -impl PartialEq for NamedVariant -where T: SchemaTypeIndex -{ - fn eq(&self, other: &Self) -> bool { self.id == other.id || self.name == other.name } -} - -impl NamedVariant { - pub fn with(id: T, name: VariantName) -> NamedVariant { - NamedVariant { - id, - name, - reserved: default!(), - } - } -} - -/// Maps operation numeric type id to a human-readable name. -/// -/// Two distinct [`NamedType`] objects must always have both different state -/// ids and names. -#[derive(Clone, Eq, PartialOrd, Ord, Debug)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub struct NamedType { - pub id: T, - pub name: FieldName, - /// Reserved bytes for storing information about adaptor procedures - pub reserved: ReservedBytes<0, 4>, -} - -impl PartialEq for NamedType -where T: SchemaTypeIndex -{ - fn eq(&self, other: &Self) -> bool { self.id == other.id || self.name == other.name } -} - -impl NamedType { - pub fn with(id: T, name: impl Into) -> NamedType { - NamedType { - id, - name: name.into(), - reserved: default!(), - } - } -} - -/// Interface implementation for some specific schema. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD)] -#[derive(CommitEncode)] -#[commit_encode(strategy = strict, id = ImplId)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub struct IfaceImpl { - pub version: VerNo, - pub schema_id: SchemaId, - pub iface_id: IfaceId, - pub timestamp: i64, - pub metadata: TinyOrdSet>, - pub global_state: TinyOrdSet>, - pub assignments: TinyOrdSet>, - pub valencies: TinyOrdSet>, - pub transitions: TinyOrdSet>, - pub extensions: TinyOrdSet>, - pub errors: TinyOrdSet>, - pub developer: Identity, -} - -impl IfaceImpl { - #[inline] - pub fn impl_id(&self) -> ImplId { self.commit_id() } - - pub fn meta_name(&self, id: MetaType) -> Option<&FieldName> { - self.metadata - .iter() - .find(|nt| nt.id == id) - .map(|nt| &nt.name) - } - - pub fn meta_type(&self, name: &FieldName) -> Option { - self.metadata - .iter() - .find(|nt| &nt.name == name) - .map(|nt| nt.id) - } - - pub fn global_type(&self, name: &FieldName) -> Option { - self.global_state - .iter() - .find(|nt| &nt.name == name) - .map(|nt| nt.id) - } - - pub fn assignments_type(&self, name: &FieldName) -> Option { - self.assignments - .iter() - .find(|nt| &nt.name == name) - .map(|nt| nt.id) - } - - pub fn transition_type(&self, name: &FieldName) -> Option { - self.transitions - .iter() - .find(|nt| &nt.name == name) - .map(|nt| nt.id) - } - - pub fn extension_name(&self, id: ExtensionType) -> Option<&FieldName> { - self.extensions - .iter() - .find(|nt| nt.id == id) - .map(|nt| &nt.name) - } - - pub fn extension_type(&self, name: &FieldName) -> Option { - self.extensions - .iter() - .find(|nt| &nt.name == name) - .map(|nt| nt.id) - } - - pub fn valency_type(&self, name: &FieldName) -> Option { - self.valencies - .iter() - .find(|nt| &nt.name == name) - .map(|nt| nt.id) - } - - pub fn valency_name(&self, id: ValencyType) -> Option<&FieldName> { - self.valencies - .iter() - .find(|nt| nt.id == id) - .map(|nt| &nt.name) - } - pub fn global_name(&self, id: GlobalStateType) -> Option<&FieldName> { - self.global_state - .iter() - .find(|nt| nt.id == id) - .map(|nt| &nt.name) - } - - pub fn assignment_name(&self, id: AssignmentType) -> Option<&FieldName> { - self.assignments - .iter() - .find(|nt| nt.id == id) - .map(|nt| &nt.name) - } - - pub fn transition_name(&self, id: TransitionType) -> Option<&FieldName> { - self.transitions - .iter() - .find(|nt| nt.id == id) - .map(|nt| &nt.name) - } - - pub fn error_name(&self, errno: u8) -> Option<&VariantName> { - self.errors - .iter() - .find(|nt| nt.id == errno) - .map(|nt| &nt.name) - } -} - -#[derive(Clone, Eq, PartialEq, Hash, Debug, Display, Error)] -#[display(doc_comments)] -pub enum ImplInconsistency { - /// timestamp is invalid ({0}). - InvalidTimestamp(i64), - /// timestamp in the future ({0}). - FutureTimestamp(DateTime), - - /// interface metadata field '{0}' is not resolved by the implementation. - IfaceMetaAbsent(FieldName), - /// implementation metadata field '{0}' maps to an unknown schema metadata - /// type {1}. - SchemaMetaAbsent(FieldName, MetaType), - - /// interface global state field '{0}' is not resolved by the - /// implementation. - IfaceGlobalAbsent(FieldName), - /// implementation global state field '{0}' maps to an unknown schema global - /// state type {1}. - SchemaGlobalAbsent(FieldName, GlobalStateType), - - /// interface owned state field '{0}' is not resolved by the - /// implementation. - IfaceAssignmentAbsent(FieldName), - /// implementation owned state field '{0}' maps to an unknown schema owned - /// state type {1}. - SchemaAssignmentAbsent(FieldName, AssignmentType), - - /// interface valency field '{0}' is not resolved by the implementation. - IfaceValencyAbsent(FieldName), - /// implementation valency field '{0}' maps to an unknown schema valency - /// {1}. - SchemaValencyAbsent(FieldName, ValencyType), - - /// interface state transition name '{0}' is not resolved by the - /// implementation. - IfaceTransitionAbsent(FieldName), - /// implementation state transition name '{0}' maps to an unknown schema - /// state transition type {1}. - SchemaTransitionAbsent(FieldName, TransitionType), - - /// interface state extension name '{0}' is not resolved by the - /// implementation. - IfaceExtensionAbsent(FieldName), - /// implementation state extension name '{0}' maps to an unknown schema - /// state extension type {1}. - SchemaExtensionAbsent(FieldName, ExtensionType), - - /// implementation references unknown interface error '{0}'. - IfaceErrorAbsent(VariantName), - - /// metadata field '{0}' is repeated {1} times - RepeatedMetaData(FieldName, i32), - - /// global state field '{0}' is repeated {1} times - RepeatedGlobalState(FieldName, i32), - - /// assignments field '{0}' is repeated {1} times - RepeatedAssignments(FieldName, i32), - - /// valencies field '{0}' is repeated {1} times - RepeatedValencies(FieldName, i32), - - /// transition field '{0}' is repeated {1} times - RepeatedTransitions(FieldName, i32), - - /// extension field '{0}' is repeated {1} times - RepeatedExtensions(FieldName, i32), -} - -impl IfaceImpl { - pub fn check(&self, iface: &Iface, schema: &Schema) -> Result<(), Vec> { - let mut errors = vec![]; - let now = Utc::now(); - let mut dup_metadata = HashMap::new(); - let mut dup_global_state = HashMap::new(); - let mut dup_assignments = HashMap::new(); - let mut dup_valencies = HashMap::new(); - let mut dup_transitions = HashMap::new(); - let mut dup_extensions = HashMap::new(); - - match Utc.timestamp_opt(self.timestamp, 0).single() { - Some(ts) if ts > now => errors.push(ImplInconsistency::FutureTimestamp(ts)), - None => errors.push(ImplInconsistency::InvalidTimestamp(self.timestamp)), - _ => {} - } - - for name in iface.metadata.keys() { - if self.metadata.iter().all(|field| &field.name != name) { - errors.push(ImplInconsistency::IfaceMetaAbsent(name.clone())); - } - } - for field in &self.metadata { - dup_metadata - .entry(field.name.clone()) - .and_modify(|counter| *counter += 1) - .or_insert(0); - if !schema.meta_types.contains_key(&field.id) { - errors.push(ImplInconsistency::SchemaMetaAbsent(field.name.clone(), field.id)); - } - } - - dup_metadata - .iter() - .filter(|(_, &count)| count > 1) - .for_each(|(field_name, &count)| { - errors.push(ImplInconsistency::RepeatedMetaData(field_name.clone(), count)); - }); - - for name in iface.global_state.keys() { - if self.global_state.iter().all(|field| &field.name != name) { - errors.push(ImplInconsistency::IfaceGlobalAbsent(name.clone())); - } - } - for field in &self.global_state { - dup_global_state - .entry(field.name.clone()) - .and_modify(|counter| *counter += 1) - .or_insert(0); - if !schema.global_types.contains_key(&field.id) { - errors.push(ImplInconsistency::SchemaGlobalAbsent(field.name.clone(), field.id)); - } - } - - dup_global_state - .iter() - .filter(|(_, &count)| count > 1) - .for_each(|(field_name, &count)| { - errors.push(ImplInconsistency::RepeatedGlobalState(field_name.clone(), count)); - }); - - for name in iface.assignments.keys() { - if self.assignments.iter().all(|field| &field.name != name) { - errors.push(ImplInconsistency::IfaceAssignmentAbsent(name.clone())); - } - } - for field in &self.assignments { - dup_assignments - .entry(field.name.clone()) - .and_modify(|counter| *counter += 1) - .or_insert(0); - if !schema.owned_types.contains_key(&field.id) { - errors - .push(ImplInconsistency::SchemaAssignmentAbsent(field.name.clone(), field.id)); - } - } - - dup_assignments - .iter() - .filter(|(_, &count)| count > 1) - .for_each(|(field_name, &count)| { - errors.push(ImplInconsistency::RepeatedAssignments(field_name.clone(), count)); - }); - - for name in iface.valencies.keys() { - if self.valencies.iter().all(|field| &field.name != name) { - errors.push(ImplInconsistency::IfaceValencyAbsent(name.clone())); - } - } - for field in &self.valencies { - dup_valencies - .entry(field.name.clone()) - .and_modify(|counter| *counter += 1) - .or_insert(0); - - if !schema.valency_types.contains(&field.id) { - errors.push(ImplInconsistency::SchemaValencyAbsent(field.name.clone(), field.id)); - } - } - dup_valencies - .iter() - .filter(|(_, &count)| count > 1) - .for_each(|(field_name, &count)| { - errors.push(ImplInconsistency::RepeatedValencies(field_name.clone(), count)); - }); - - for name in iface.transitions.keys() { - if self.transitions.iter().all(|field| &field.name != name) { - errors.push(ImplInconsistency::IfaceTransitionAbsent(name.clone())); - } - } - for field in &self.transitions { - dup_transitions - .entry(field.name.clone()) - .and_modify(|counter| *counter += 1) - .or_insert(0); - - if !schema.transitions.contains_key(&field.id) { - errors - .push(ImplInconsistency::SchemaTransitionAbsent(field.name.clone(), field.id)); - } - } - - dup_transitions - .iter() - .filter(|(_, &count)| count > 1) - .for_each(|(field_name, &count)| { - errors.push(ImplInconsistency::RepeatedTransitions(field_name.clone(), count)); - }); - - for name in iface.extensions.keys() { - if self.extensions.iter().all(|field| &field.name != name) { - errors.push(ImplInconsistency::IfaceExtensionAbsent(name.clone())); - } - } - for field in &self.extensions { - dup_extensions - .entry(field.name.clone()) - .and_modify(|counter| *counter += 1) - .or_insert(0); - - if !schema.extensions.contains_key(&field.id) { - errors.push(ImplInconsistency::SchemaExtensionAbsent(field.name.clone(), field.id)); - } - } - - dup_extensions - .iter() - .filter(|(_, &count)| count > 1) - .for_each(|(field_name, &count)| { - errors.push(ImplInconsistency::RepeatedExtensions(field_name.clone(), count)); - }); - - for var in &self.errors { - if iface.errors.keys().all(|name| name != &var.name) { - errors.push(ImplInconsistency::IfaceErrorAbsent(var.name.clone())); - } - } - - if errors.is_empty() { Ok(()) } else { Err(errors) } - } -} diff --git a/src/interface/inheritance.rs b/src/interface/inheritance.rs deleted file mode 100644 index 8a1f945b..00000000 --- a/src/interface/inheritance.rs +++ /dev/null @@ -1,713 +0,0 @@ -// RGB standard library for working with smart contracts on Bitcoin & Lightning -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2019-2024 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2019-2024 LNP/BP Standards Association. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use amplify::confinement::{Confined, TinyOrdMap, TinyOrdSet}; -use rgb::{ - AssignmentType, ExtensionType, GlobalStateType, Occurrences, OpFullType, OpSchema, Schema, - TransitionType, ValencyType, -}; -use strict_encoding::{FieldName, TypeName}; - -use crate::interface::{ - ExtensionIface, GenesisIface, Iface, IfaceImpl, Modifier, OpName, OwnedIface, TransitionIface, -}; - -#[derive(Clone, PartialEq, Eq, Debug, Display, From)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -#[display(doc_comments)] -pub enum InheritanceFailure { - /// invalid schema - no match with root schema requirements for global state - /// type #{0}. - GlobalStateMismatch(GlobalStateType), - /// invalid schema - no match with root schema requirements for assignment - /// type #{0}. - AssignmentTypeMismatch(AssignmentType), - /// invalid schema - no match with root schema requirements for valency - /// type #{0}. - ValencyTypeMismatch(ValencyType), - /// invalid schema - no match with root schema requirements for transition - /// type #{0}. - TransitionTypeMismatch(TransitionType), - /// invalid schema - no match with root schema requirements for extension - /// type #{0}. - ExtensionTypeMismatch(ExtensionType), - - /// invalid schema - no match with root schema requirements for global state - /// type #{1} used in {0}. - OpGlobalStateMismatch(OpFullType, GlobalStateType), - /// invalid schema - no match with root schema requirements for input - /// type #{1} used in {0}. - OpInputMismatch(OpFullType, AssignmentType), - /// invalid schema - no match with root schema requirements for redeem - /// type #{1} used in {0}. - OpRedeemMismatch(OpFullType, ValencyType), - /// invalid schema - no match with root schema requirements for assignment - /// type #{1} used in {0}. - OpAssignmentsMismatch(OpFullType, AssignmentType), - /// invalid schema - no match with root schema requirements for valency - /// type #{1} used in {0}. - OpValencyMismatch(OpFullType, ValencyType), -} - -pub trait CheckInheritance { - fn check_inheritance(&self, root: &Self) -> Result<(), Vec>; -} - -impl CheckInheritance for Schema { - fn check_inheritance(&self, root: &Schema) -> Result<(), Vec> { - let mut status = vec![]; - - for (global_type, data_format) in &self.global_types { - match root.global_types.get(global_type) { - None => status.push(InheritanceFailure::GlobalStateMismatch(*global_type)), - Some(root_data_format) if root_data_format != data_format => { - status.push(InheritanceFailure::GlobalStateMismatch(*global_type)) - } - _ => {} - }; - } - - for (assignments_type, state_schema) in &self.owned_types { - match root.owned_types.get(assignments_type) { - None => status.push(InheritanceFailure::AssignmentTypeMismatch(*assignments_type)), - Some(root_state_schema) if root_state_schema != state_schema => { - status.push(InheritanceFailure::AssignmentTypeMismatch(*assignments_type)) - } - _ => {} - }; - } - - for valencies_type in &self.valency_types { - if !root.valency_types.contains(valencies_type) { - status.push(InheritanceFailure::ValencyTypeMismatch(*valencies_type)); - } - } - - self.genesis - .check_schema_op_inheritance(OpFullType::Genesis, &root.genesis) - .map_err(|e| status.extend(e)) - .ok(); - - for (type_id, transition_schema) in &self.transitions { - if let Some(root_transition_schema) = root.transitions.get(type_id) { - transition_schema - .check_schema_op_inheritance( - OpFullType::StateTransition(*type_id), - root_transition_schema, - ) - .map_err(|e| status.extend(e)) - .ok(); - } else { - status.push(InheritanceFailure::TransitionTypeMismatch(*type_id)); - } - } - for (type_id, extension_schema) in &self.extensions { - if let Some(root_extension_schema) = root.extensions.get(type_id) { - extension_schema - .check_schema_op_inheritance( - OpFullType::StateExtension(*type_id), - root_extension_schema, - ) - .map_err(|e| status.extend(e)) - .ok(); - } else { - status.push(InheritanceFailure::ExtensionTypeMismatch(*type_id)); - } - } - - if status.is_empty() { Ok(()) } else { Err(status) } - } -} - -/// Trait used for internal schema validation against some root schema -pub(crate) trait CheckSchemaOpInheritance { - fn check_schema_op_inheritance( - &self, - op_type: OpFullType, - root: &Self, - ) -> Result<(), Vec>; -} - -impl CheckSchemaOpInheritance for T -where T: OpSchema -{ - fn check_schema_op_inheritance( - &self, - op_type: OpFullType, - root: &Self, - ) -> Result<(), Vec> { - let mut status = vec![]; - - for (type_id, occ) in self.globals() { - match root.globals().get(type_id) { - None => status.push(InheritanceFailure::OpGlobalStateMismatch(op_type, *type_id)), - Some(root_occ) if occ != root_occ => { - status.push(InheritanceFailure::OpGlobalStateMismatch(op_type, *type_id)) - } - _ => {} - }; - } - - if let Some(inputs) = self.inputs() { - let root_inputs = root.inputs().expect("generic guarantees"); - for (type_id, occ) in inputs { - match root_inputs.get(type_id) { - None => status.push(InheritanceFailure::OpInputMismatch(op_type, *type_id)), - Some(root_occ) if occ != root_occ => { - status.push(InheritanceFailure::OpInputMismatch(op_type, *type_id)) - } - _ => {} - }; - } - } - - for (type_id, occ) in self.assignments() { - match root.assignments().get(type_id) { - None => status.push(InheritanceFailure::OpAssignmentsMismatch(op_type, *type_id)), - Some(root_occ) if occ != root_occ => { - status.push(InheritanceFailure::OpAssignmentsMismatch(op_type, *type_id)) - } - _ => {} - }; - } - - if let Some(redeems) = self.redeems() { - let root_redeems = root.redeems().expect("generic guarantees"); - for type_id in redeems { - if !root_redeems.contains(type_id) { - status.push(InheritanceFailure::OpRedeemMismatch(op_type, *type_id)); - } - } - } - - for type_id in self.valencies() { - if !root.valencies().contains(type_id) { - status.push(InheritanceFailure::OpValencyMismatch(op_type, *type_id)); - } - } - - if status.is_empty() { Ok(()) } else { Err(status) } - } -} - -#[derive(Clone, Eq, PartialEq, Hash, Debug, Display, Error)] -#[display("{iface} {err}")] -pub struct InheritError { - err: ExtensionError, - iface: TypeName, -} - -#[derive(Clone, Eq, PartialEq, Hash, Debug, Display, Error)] -#[display(doc_comments)] -pub enum ExtensionError { - /// too many metadata fields defined. - MetaOverflow, - /// too many global state types defined. - GlobalOverflow, - /// global state '{0}' has different data type from the parent interface. - GlobalType(FieldName), - /// global state '{0}' has fewer occurrences than in the parent interface. - GlobalOcc(FieldName), - /// too many assignment types defined. - AssignmentOverflow, - /// assignment '{0}' has different data type from the parent interface. - AssignmentType(FieldName), - /// assignment '{0}' has fewer occurrences than in the parent interface. - AssignmentOcc(FieldName), - /// global state '{0}' has lower visibility than in the parent interface. - AssignmentPublic(FieldName), - /// too many valency types defined. - ValencyOverflow, - /// valency '{0}' has fewer occurrences than in the parent interface. - ValencyOcc(FieldName), - /// too many state transitions. - TransitionOverflow, - /// too many state extensions. - ExtensionOverflow, - /// too many error types defined. - ErrorOverflow, - /// inherited interface tries to override the parent default operation. - DefaultOverride, - /// {0} in the parent interface is final and can't be overridden. - OpFinal(OpName), - /// {0} must use `override` keyword to modify the parent version. - OpNoOverride(OpName), - /// too many {1} types defined in {0}. - OpOverflow(OpName, &'static str), - /// {0} tries to override the parent default assignment. - OpDefaultOverride(OpName), - /// {0} tries to override '{2}' {1}. - OpOcc(OpName, &'static str, FieldName), - /// too deep inheritance; it is not allowed for any interface to have more - /// than 255 parents it inherits from, including all grandparents. - InheritanceOverflow, -} - -impl OwnedIface { - pub fn is_superset(self, other: OwnedIface) -> bool { - if self == Self::Any { - return true; - } - if self == Self::AnyData && matches!(other, Self::Data(_)) { - return true; - } - self == other - } -} - -impl Modifier { - pub fn is_final(self) -> bool { self == Self::Final } - pub fn can_be_overridden_by(self, other: Modifier) -> bool { - matches!((self, other), (Self::Abstract | Self::Override, Self::Override | Self::Final)) - } -} - -impl Iface { - pub fn expect_inherit( - name: impl Into, - ifaces: impl IntoIterator, - ) -> Iface { - let name = name.into(); - match Self::inherit(name.clone(), ifaces) { - Ok(iface) => iface, - Err(msgs) => { - eprintln!("Unable to construct interface {name}:"); - for msg in msgs { - eprintln!("- {msg}") - } - panic!(); - } - } - } - - pub fn inherit( - name: impl Into, - ifaces: impl IntoIterator, - ) -> Result> { - let name = name.into(); - let mut iter = ifaces.into_iter(); - let mut iface = iter - .next() - .expect("at least one interface must be provided for the inheritance"); - for ext in iter { - let ext_name = ext.name.clone(); - iface = iface.extended(ext, name.clone()).map_err(|err| { - err.into_iter() - .map(|e| InheritError { - err: e, - iface: ext_name.clone(), - }) - .collect::>() - })?; - } - Ok(iface) - } - - pub fn expect_extended(self, ext: Iface, name: impl Into) -> Iface { - let prev_name = self.name.clone(); - let ext_name = ext.name.clone(); - match self.extended(ext, name) { - Ok(iface) => iface, - Err(msgs) => { - eprintln!("Unable to extend {prev_name} with {ext_name}:"); - for msg in msgs { - eprintln!("- {msg}") - } - panic!(); - } - } - } - - pub fn extended( - mut self, - ext: Iface, - name: impl Into, - ) -> Result> { - let orig_id = self.iface_id(); - let ext_id = ext.iface_id(); - let name = name.into(); - - let mut errors = vec![]; - - self.metadata - .extend(ext.metadata) - .map_err(|_| errors.push(ExtensionError::MetaOverflow)) - .ok(); - - for (name, e) in ext.global_state { - match self.global_state.get_mut(&name) { - None => { - if self - .global_state - .insert(name, e) - .map_err(|_| errors.push(ExtensionError::GlobalOverflow)) - .is_err() - { - break; - } - } - Some(orig) => { - if orig.sem_id.is_some() && e.sem_id != orig.sem_id { - errors.push(ExtensionError::GlobalType(name)); - } else if orig.required & !e.required { - errors.push(ExtensionError::GlobalOcc(name)); - } else { - *orig = e; - } - } - } - } - - for (name, e) in ext.assignments { - match self.assignments.get_mut(&name) { - None => { - if self - .assignments - .insert(name, e) - .map_err(|_| errors.push(ExtensionError::AssignmentOverflow)) - .is_err() - { - break; - } - } - Some(orig) => { - if !orig.owned_state.is_superset(e.owned_state) { - errors.push(ExtensionError::AssignmentType(name)); - } else if orig.required & !e.required { - errors.push(ExtensionError::AssignmentOcc(name)); - } else if orig.public & !e.public { - errors.push(ExtensionError::AssignmentPublic(name)); - } else { - *orig = e; - } - } - } - } - - for (name, e) in ext.valencies { - match self.valencies.get_mut(&name) { - None => { - if self - .valencies - .insert(name, e) - .map_err(|_| errors.push(ExtensionError::ValencyOverflow)) - .is_err() - { - break; - } - } - Some(orig) => { - if orig.required & !e.required { - errors.push(ExtensionError::ValencyOcc(name)); - } else { - *orig = e; - } - } - } - } - - self.clone() - .genesis - .extended(ext.genesis) - .map(|genesis| self.genesis = genesis) - .map_err(|errs| errors.extend(errs)) - .ok(); - - for (name, op) in ext.transitions { - match self.transitions.remove(&name) { - Ok(None) if op.optional => continue, - Ok(None) => { - if self - .transitions - .insert(name, op) - .map_err(|_| errors.push(ExtensionError::TransitionOverflow)) - .is_err() - { - break; - } - } - Ok(Some(orig)) => { - orig.extended(op, name.clone()) - .map(|op| self.transitions.insert(name, op).expect("same size")) - .map_err(|errs| errors.extend(errs)) - .ok(); - } - Err(_) => unreachable!(), - } - } - - for (name, op) in ext.extensions { - match self.extensions.remove(&name) { - Ok(None) if op.optional => continue, - Ok(None) => { - if self - .extensions - .insert(name, op) - .map_err(|_| errors.push(ExtensionError::TransitionOverflow)) - .is_err() - { - break; - } - } - Ok(Some(orig)) => { - orig.extended(op, name.clone()) - .map(|op| self.extensions.insert(name, op).expect("same size")) - .map_err(|errs| errors.extend(errs)) - .ok(); - } - Err(_) => unreachable!(), - } - } - - // We allow replacing error messages - self.errors - .extend(ext.errors) - .map_err(|_| errors.push(ExtensionError::ErrorOverflow)) - .ok(); - - if ext.default_operation.is_some() { - if self.default_operation.is_some() && self.default_operation != ext.default_operation { - errors.push(ExtensionError::DefaultOverride); - } else { - self.default_operation = ext.default_operation - } - } - - self.name = name; - self.inherits - .push(orig_id) - .and_then(|_| self.inherits.extend(ext.inherits)) - .and_then(|_| self.inherits.push(ext_id)) - .map_err(|_| errors.push(ExtensionError::InheritanceOverflow)) - .ok(); - - if errors.is_empty() { Ok(self) } else { Err(errors) } - } -} - -fn check_occs( - orig: &mut TinyOrdMap, - ext: impl IntoIterator, - op: OpName, - state: &'static str, - errors: &mut Vec, -) { - for (name, occ) in ext { - match orig.get_mut(&name) { - None => { - if orig - .insert(name, occ) - .map_err(|_| errors.push(ExtensionError::OpOverflow(op.clone(), state))) - .is_err() - { - break; - } - } - Some(orig) => { - if orig.min_value() > occ.min_value() { - errors.push(ExtensionError::OpOcc(op.clone(), state, name)); - } else { - *orig = occ; - } - } - } - } -} - -fn check_presence( - orig: &mut TinyOrdSet, - ext: impl IntoIterator, - op: OpName, - state: &'static str, - errors: &mut Vec, -) { - for name in ext { - if orig - .push(name) - .map_err(|_| errors.push(ExtensionError::OpOverflow(op.clone(), state))) - .is_err() - { - break; - } - } -} - -impl GenesisIface { - pub fn extended(mut self, ext: Self) -> Result> { - let mut errors = vec![]; - - let op = OpName::Genesis; - if self.modifier.is_final() { - errors.push(ExtensionError::OpFinal(op.clone())); - } else if !self.modifier.can_be_overridden_by(ext.modifier) { - errors.push(ExtensionError::OpNoOverride(op.clone())); - } - - self.metadata - .extend(ext.metadata) - .map_err(|_| errors.push(ExtensionError::OpOverflow(OpName::Genesis, "metadata"))) - .ok(); - check_occs(&mut self.globals, ext.globals, op.clone(), "global", &mut errors); - check_occs(&mut self.assignments, ext.assignments, op.clone(), "assignment", &mut errors); - - check_presence(&mut self.valencies, ext.valencies, op.clone(), "valency", &mut errors); - check_presence(&mut self.errors, ext.errors, op.clone(), "error", &mut errors); - - if errors.is_empty() { Ok(self) } else { Err(errors) } - } -} - -impl TransitionIface { - pub fn extended(mut self, ext: Self, op_name: FieldName) -> Result> { - let mut errors = vec![]; - - let op = OpName::Transition(op_name); - if self.modifier.is_final() { - errors.push(ExtensionError::OpFinal(op.clone())); - } else if !self.modifier.can_be_overridden_by(ext.modifier) { - errors.push(ExtensionError::OpNoOverride(op.clone())); - } - self.optional = self.optional.max(ext.optional); - - self.metadata - .extend(ext.metadata) - .map_err(|_| errors.push(ExtensionError::OpOverflow(op.clone(), "metadata"))) - .ok(); - check_occs(&mut self.globals, ext.globals, op.clone(), "global", &mut errors); - check_occs(&mut self.assignments, ext.assignments, op.clone(), "assignment", &mut errors); - check_occs(&mut self.inputs, ext.inputs, op.clone(), "input", &mut errors); - - check_presence(&mut self.valencies, ext.valencies, op.clone(), "valency", &mut errors); - check_presence(&mut self.errors, ext.errors, op.clone(), "error", &mut errors); - - if ext.default_assignment.is_some() { - if self.default_assignment.is_some() - && self.default_assignment != ext.default_assignment - { - errors.push(ExtensionError::OpDefaultOverride(op.clone())); - } else { - self.default_assignment = ext.default_assignment - } - } - - if errors.is_empty() { Ok(self) } else { Err(errors) } - } -} - -impl ExtensionIface { - pub fn extended(mut self, ext: Self, op_name: FieldName) -> Result> { - let mut errors = vec![]; - - let op = OpName::Transition(op_name); - if self.modifier.is_final() { - errors.push(ExtensionError::OpFinal(op.clone())); - } else if !self.modifier.can_be_overridden_by(ext.modifier) { - errors.push(ExtensionError::OpNoOverride(op.clone())); - } - self.optional = self.optional.max(ext.optional); - - self.metadata - .extend(ext.metadata) - .map_err(|_| errors.push(ExtensionError::OpOverflow(op.clone(), "metadata"))) - .ok(); - check_occs(&mut self.globals, ext.globals, op.clone(), "global", &mut errors); - check_occs(&mut self.assignments, ext.assignments, op.clone(), "assignment", &mut errors); - - check_presence(&mut self.valencies, ext.valencies, op.clone(), "valency", &mut errors); - check_presence(&mut self.redeems, ext.redeems, op.clone(), "input", &mut errors); - check_presence(&mut self.errors, ext.errors, op.clone(), "error", &mut errors); - - if ext.default_assignment.is_some() { - if self.default_assignment.is_some() - && self.default_assignment != ext.default_assignment - { - errors.push(ExtensionError::OpDefaultOverride(op.clone())); - } else { - self.default_assignment = ext.default_assignment - } - } - - if errors.is_empty() { Ok(self) } else { Err(errors) } - } -} - -impl IfaceImpl { - pub fn abstracted(mut self, base: &Iface, parent: &Iface) -> Option { - assert_eq!(self.iface_id, base.iface_id()); - let parent_id = parent.iface_id(); - if !base.inherits.contains(&parent_id) { - return None; - } - - self.metadata = Confined::from_iter_checked(base.metadata.keys().filter_map(|name| { - self.metadata - .iter() - .find(|i| parent.metadata.contains_key(name) && &i.name == name) - .cloned() - })); - - self.global_state = - Confined::from_iter_checked(base.global_state.keys().filter_map(|name| { - self.global_state - .iter() - .find(|i| parent.global_state.contains_key(name) && &i.name == name) - .cloned() - })); - - self.assignments = - Confined::from_iter_checked(base.assignments.keys().filter_map(|name| { - self.assignments - .iter() - .find(|i| parent.assignments.contains_key(name) && &i.name == name) - .cloned() - })); - - self.valencies = Confined::from_iter_checked(base.assignments.keys().filter_map(|name| { - self.valencies - .iter() - .find(|i| parent.valencies.contains_key(name) && &i.name == name) - .cloned() - })); - - self.transitions = - Confined::from_iter_checked(base.transitions.keys().filter_map(|name| { - self.transitions - .iter() - .find(|i| parent.transitions.contains_key(name) && &i.name == name) - .cloned() - })); - - self.extensions = Confined::from_iter_checked(base.extensions.keys().filter_map(|name| { - self.extensions - .iter() - .find(|i| parent.extensions.contains_key(name) && &i.name == name) - .cloned() - })); - - self.iface_id = parent_id; - - Some(self) - } -} diff --git a/src/interface/mod.rs b/src/interface/mod.rs deleted file mode 100644 index ea5ef2d9..00000000 --- a/src/interface/mod.rs +++ /dev/null @@ -1,67 +0,0 @@ -// RGB standard library for working with smart contracts on Bitcoin & Lightning -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2019-2024 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2019-2024 LNP/BP Standards Association. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! RGB contract interface provides a mapping between identifiers of RGB schema- -//! defined contract state and operation types to a human-readable and -//! standardized wallet APIs. - -mod iface; -mod iimpl; -mod contract; -mod builder; -mod filter; -pub(crate) mod resolver; -mod contractum; -mod inheritance; - -pub use builder::{BuilderError, ContractBuilder, TransitionBuilder, TxOutpoint}; -pub use contract::{ - AllocatedState, AmountChange, AttachAllocation, ContractError, ContractIface, DataAllocation, - FungibleAllocation, IfaceOp, OwnedAllocation, RightsAllocation, StateChange, -}; -pub use contractum::IfaceDisplay; -pub use filter::{FilterExclude, FilterIncludeAll, OutpointFilter}; -pub use iface::{ - ArgMap, AssignIface, ExtensionIface, GenesisIface, GlobalIface, Iface, IfaceClass, IfaceId, - IfaceInconsistency, IfaceRef, IfaceWrapper, Modifier, OpName, OwnedIface, Req, TransitionIface, - ValencyIface, -}; -pub use iimpl::{IfaceImpl, ImplId, NamedField, NamedType, NamedVariant, SchemaTypeIndex}; -pub use inheritance::{CheckInheritance, ExtensionError, InheritanceFailure}; - -#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, Default)] -#[derive(StrictType, StrictEncode, StrictDecode)] -#[strict_type(lib = crate::LIB_NAME_RGB_STD, tags = repr, into_u8, try_from_u8)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -#[repr(u8)] -#[non_exhaustive] -pub enum VerNo { - #[display("v0", alt = "0")] - V0 = 0, - - #[default] - #[display("v1", alt = "1")] - V1 = 1, -} diff --git a/src/lib.rs b/src/lib.rs index 5adfce94..b61f5748 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,18 +38,14 @@ extern crate serde_crate as serde; pub extern crate rgbinvoice as invoice; pub mod stl; -pub mod interface; pub mod containers; pub mod persistence; -pub mod resolvers; -mod contract; +pub mod contract; +pub mod indexers; pub mod info; pub use bp::{Outpoint, Txid}; -pub use contract::{ - BundleExt, KnownState, MergeReveal, MergeRevealError, OutputAssignment, RevealError, - TypedAssignsExt, -}; +pub use contract::{KnownState, MergeReveal, MergeRevealError, OutputAssignment, WitnessInfo}; pub use invoice::{Allocation, Amount, CoinAmount, OwnedFraction, Precision, TokenIndex}; pub use rgb::prelude::*; pub use rgb::rgbasm; diff --git a/src/persistence/index.rs b/src/persistence/index.rs index a047d7d0..a72b683c 100644 --- a/src/persistence/index.rs +++ b/src/persistence/index.rs @@ -23,12 +23,12 @@ use std::collections::BTreeSet; use std::error::Error; use std::fmt::Debug; -use amplify::confinement; +use amplify::confinement::{self, SmallOrdSet}; +use bp::{Outpoint, Txid}; use nonasync::persistence::{CloneNoPersistence, Persisting}; use rgb::{ - Assign, AssignmentType, BundleId, ContractId, ExposedState, Extension, Genesis, GenesisSeal, - GraphSeal, OpId, Operation, Opout, TransitionBundle, TypedAssigns, XChain, XOutputSeal, - XWitnessId, + Assign, AssignmentType, BundleId, ContractId, ExposedState, Genesis, GenesisSeal, GraphSeal, + OpId, Operation, Opout, TransitionBundle, TypedAssigns, }; use crate::containers::{ConsignmentExt, ToWitnessId, WitnessBundle}; @@ -99,7 +99,7 @@ pub enum IndexInconsistency { BundleAbsent(OpId), /// outpoint {0} is not part of the contract {1}. - OutpointUnknown(XOutputSeal, ContractId), + OutpointUnknown(Outpoint, ContractId), /// index already contains information about bundle {bundle_id} which /// specifies contract {present} instead of contract {expected}. @@ -166,13 +166,10 @@ impl Index

{ .register_contract(contract_id) .map_err(IndexError::WriteProvider)?; self.index_genesis(contract_id, consignment.genesis())?; - for extension in consignment.extensions() { - self.index_extension(contract_id, extension)?; - } for WitnessBundle { pub_witness, - bundle, anchor: _, + bundle, } in consignment.bundled_witnesses() { let witness_id = pub_witness.to_witness_id(); @@ -198,39 +195,6 @@ impl Index

{ self.provider .index_genesis_assignments(id, vec, opid, *type_id)?; } - TypedAssigns::Attachment(vec) => { - self.provider - .index_genesis_assignments(id, vec, opid, *type_id)?; - } - } - } - Ok(()) - } - - fn index_extension( - &mut self, - id: ContractId, - extension: &Extension, - ) -> Result<(), IndexError

> { - let opid = extension.id(); - for (type_id, assign) in extension.assignments.iter() { - match assign { - TypedAssigns::Declarative(vec) => { - self.provider - .index_genesis_assignments(id, vec, opid, *type_id)?; - } - TypedAssigns::Fungible(vec) => { - self.provider - .index_genesis_assignments(id, vec, opid, *type_id)?; - } - TypedAssigns::Structured(vec) => { - self.provider - .index_genesis_assignments(id, vec, opid, *type_id)?; - } - TypedAssigns::Attachment(vec) => { - self.provider - .index_genesis_assignments(id, vec, opid, *type_id)?; - } } } Ok(()) @@ -240,7 +204,7 @@ impl Index

{ &mut self, contract_id: ContractId, bundle: &TransitionBundle, - witness_id: XWitnessId, + witness_id: Txid, ) -> Result<(), IndexError

> { let bundle_id = bundle.bundle_id(); @@ -249,6 +213,9 @@ impl Index

{ for (opid, transition) in &bundle.known_transitions { self.provider.register_operation(*opid, bundle_id)?; + for input in &transition.inputs { + self.provider.register_spending(input.op, bundle_id)?; + } for (type_id, assign) in transition.assignments.iter() { match assign { TypedAssigns::Declarative(vec) => { @@ -278,15 +245,6 @@ impl Index

{ witness_id, )?; } - TypedAssigns::Attachment(vec) => { - self.provider.index_transition_assignments( - contract_id, - vec, - *opid, - *type_id, - witness_id, - )?; - } } } } @@ -296,7 +254,7 @@ impl Index

{ pub(super) fn contracts_assigning( &self, - outputs: BTreeSet, + outputs: BTreeSet, ) -> Result + '_, IndexError

> { self.provider .contracts_assigning(outputs) @@ -313,14 +271,14 @@ impl Index

{ pub(super) fn opouts_by_outputs( &self, contract_id: ContractId, - outputs: impl IntoIterator>, + outputs: impl IntoIterator>, ) -> Result, IndexError

> { Ok(self.provider.opouts_by_outputs(contract_id, outputs)?) } pub(super) fn opouts_by_terminals( &self, - terminals: impl IntoIterator>, + terminals: impl IntoIterator, ) -> Result, IndexError

> { self.provider .opouts_by_terminals(terminals) @@ -331,10 +289,17 @@ impl Index

{ Ok(self.provider.bundle_id_for_op(opid)?) } + pub(super) fn bundle_ids_children_of_op( + &self, + opid: OpId, + ) -> Result, IndexError

> { + Ok(self.provider.bundle_ids_children_of_op(opid)?) + } + pub(super) fn bundle_info( &self, bundle_id: BundleId, - ) -> Result<(impl Iterator + '_, ContractId), IndexError

> { + ) -> Result<(impl Iterator + '_, ContractId), IndexError

> { Ok(self.provider.bundle_info(bundle_id)?) } } @@ -367,7 +332,7 @@ pub trait IndexReadProvider { fn contracts_assigning( &self, - outputs: BTreeSet, + outputs: BTreeSet, ) -> Result + '_, Self::Error>; fn public_opouts( @@ -378,20 +343,25 @@ pub trait IndexReadProvider { fn opouts_by_outputs( &self, contract_id: ContractId, - outputs: impl IntoIterator>, + outputs: impl IntoIterator>, ) -> Result, IndexReadError>; fn opouts_by_terminals( &self, - terminals: impl IntoIterator>, + terminals: impl IntoIterator, ) -> Result, Self::Error>; fn bundle_id_for_op(&self, opid: OpId) -> Result>; + fn bundle_ids_children_of_op( + &self, + opid: OpId, + ) -> Result, IndexReadError>; + fn bundle_info( &self, bundle_id: BundleId, - ) -> Result<(impl Iterator, ContractId), IndexReadError>; + ) -> Result<(impl Iterator, ContractId), IndexReadError>; } pub trait IndexWriteProvider: StoreTransaction { @@ -402,7 +372,7 @@ pub trait IndexWriteProvider: StoreTransaction { fn register_bundle( &mut self, bundle_id: BundleId, - witness_id: XWitnessId, + witness_id: Txid, contract_id: ContractId, ) -> Result>; @@ -412,6 +382,12 @@ pub trait IndexWriteProvider: StoreTransaction { bundle_id: BundleId, ) -> Result>; + fn register_spending( + &mut self, + opid: OpId, + bundle_id: BundleId, + ) -> Result>; + fn index_genesis_assignments( &mut self, contract_id: ContractId, @@ -426,6 +402,6 @@ pub trait IndexWriteProvider: StoreTransaction { vec: &[Assign], opid: OpId, type_id: AssignmentType, - witness_id: XWitnessId, + witness_id: Txid, ) -> Result<(), IndexWriteError>; } diff --git a/src/persistence/memory.rs b/src/persistence/memory.rs index 7d4ce810..88a27d30 100644 --- a/src/persistence/memory.rs +++ b/src/persistence/memory.rs @@ -24,45 +24,39 @@ use std::cmp::Ordering; use std::collections::{BTreeMap, BTreeSet, HashMap}; use std::convert::Infallible; use std::fmt::{Debug, Formatter}; -use std::num::NonZeroU32; use std::{iter, mem}; use aluvm::library::{Lib, LibId}; use amplify::confinement::{ - self, Confined, LargeOrdMap, LargeOrdSet, MediumBlob, MediumOrdMap, MediumOrdSet, SmallOrdMap, - TinyOrdMap, TinyOrdSet, + self, LargeOrdMap, LargeOrdSet, MediumOrdSet, SmallOrdMap, SmallOrdSet, TinyOrdMap, }; use amplify::num::u24; use bp::dbc::tapret::TapretCommitment; +use bp::{Outpoint, Txid}; use commit_verify::{CommitId, Conceal}; use nonasync::persistence::{CloneNoPersistence, Persistence, PersistenceError, Persisting}; -use rgb::validation::ResolveWitness; +use rgb::validation::DbcProof; use rgb::vm::{ ContractStateAccess, ContractStateEvolve, GlobalContractState, GlobalOrd, GlobalStateIter, OrdOpRef, UnknownGlobalStateType, WitnessOrd, }; use rgb::{ - Assign, AssignmentType, Assignments, AssignmentsRef, AttachId, AttachState, BundleId, - ContractId, DataState, ExposedSeal, ExposedState, Extension, FungibleState, Genesis, - GenesisSeal, GlobalStateType, GraphSeal, Identity, OpId, Operation, Opout, RevealedAttach, - RevealedData, RevealedValue, Schema, SchemaId, SecretSeal, Transition, TransitionBundle, - TypedAssigns, VoidState, XChain, XOutpoint, XOutputSeal, XWitnessId, + Assign, AssignmentType, Assignments, AssignmentsRef, BundleId, ContractId, ExposedSeal, + ExposedState, FungibleState, Genesis, GenesisSeal, GlobalStateType, GraphSeal, OpId, Operation, + Opout, OutputSeal, RevealedData, RevealedValue, Schema, SchemaId, SecretSeal, Transition, + TransitionBundle, TypedAssigns, VoidState, }; use strict_encoding::{StrictDeserialize, StrictSerialize}; use strict_types::TypeSystem; use super::{ - ContractIfaceError, ContractStateRead, ContractStateWrite, IndexInconsistency, IndexProvider, - IndexReadError, IndexReadProvider, IndexWriteError, IndexWriteProvider, SchemaIfaces, - StashInconsistency, StashProvider, StashProviderError, StashReadProvider, StashWriteProvider, - StateInconsistency, StateProvider, StateReadProvider, StateWriteProvider, StoreTransaction, - UpdateRes, -}; -use crate::containers::{ - AnchorSet, ContentId, ContentRef, ContentSigs, SealWitness, SigBlob, Supplement, TrustLevel, + ContractStateRead, ContractStateWrite, IndexInconsistency, IndexProvider, IndexReadError, + IndexReadProvider, IndexWriteError, IndexWriteProvider, StashInconsistency, StashProvider, + StashProviderError, StashReadProvider, StashWriteProvider, StateInconsistency, StateProvider, + StateReadProvider, StateWriteProvider, StoreTransaction, }; +use crate::containers::SealWitness; use crate::contract::{GlobalOut, KnownState, OpWitness, OutputAssignment}; -use crate::interface::{Iface, IfaceClass, IfaceId, IfaceImpl, IfaceRef}; use crate::LIB_NAME_RGB_STORAGE; #[derive(Debug, Display, Error, From)] @@ -89,19 +83,13 @@ pub struct MemStash { #[strict_type(skip)] persistence: Option>, - schemata: TinyOrdMap, - ifaces: TinyOrdMap, - geneses: TinyOrdMap, - suppl: TinyOrdMap>, + schemata: TinyOrdMap, + geneses: SmallOrdMap, bundles: LargeOrdMap, - extensions: LargeOrdMap, - witnesses: LargeOrdMap, - attachments: SmallOrdMap, - secret_seals: MediumOrdSet>, + witnesses: LargeOrdMap, + secret_seals: LargeOrdSet, type_system: TypeSystem, - identities: SmallOrdMap, libs: SmallOrdMap, - sigs: SmallOrdMap, } impl StrictSerialize for MemStash {} @@ -112,18 +100,12 @@ impl MemStash { Self { persistence: none!(), schemata: empty!(), - ifaces: empty!(), geneses: empty!(), - suppl: empty!(), bundles: empty!(), - extensions: empty!(), witnesses: empty!(), - attachments: empty!(), secret_seals: empty!(), type_system: none!(), - identities: empty!(), libs: empty!(), - sigs: empty!(), } } } @@ -133,18 +115,12 @@ impl CloneNoPersistence for MemStash { Self { persistence: None, schemata: self.schemata.clone(), - ifaces: self.ifaces.clone(), geneses: self.geneses.clone(), - suppl: self.suppl.clone(), bundles: self.bundles.clone(), - extensions: self.extensions.clone(), witnesses: self.witnesses.clone(), - attachments: self.attachments.clone(), secret_seals: self.secret_seals.clone(), type_system: self.type_system.clone(), - identities: self.identities.clone(), libs: self.libs.clone(), - sigs: self.sigs.clone(), } } } @@ -185,99 +161,20 @@ impl StashReadProvider for MemStash { .ok_or_else(|| StashInconsistency::LibAbsent(id).into()) } - fn ifaces(&self) -> Result, Self::Error> { - Ok(self.ifaces.values()) - } - - fn iface(&self, iface: impl Into) -> Result<&Iface, StashProviderError> { - let iref = iface.into(); - match iref { - IfaceRef::Name(ref name) => self.ifaces.values().find(|iface| &iface.name == name), - IfaceRef::Id(ref id) => self.ifaces.get(id), - } - .ok_or_else(|| StashInconsistency::IfaceAbsent(iref).into()) - } - - fn schemata(&self) -> Result, Self::Error> { + fn schemata(&self) -> Result, Self::Error> { Ok(self.schemata.values()) } - fn schemata_by( - &self, - ) -> Result, Self::Error> { - Ok(self - .schemata - .values() - .filter(|schema_ifaces| self.impl_for::(schema_ifaces).is_ok())) - } - - fn impl_for<'a, C: IfaceClass + 'a>( - &'a self, - schema_ifaces: &'a SchemaIfaces, - ) -> Result<&'a IfaceImpl, StashProviderError> { - schema_ifaces - .iimpls - .values() - .find(|iimpl| C::IFACE_IDS.contains(&iimpl.iface_id)) - .or_else(|| { - C::IFACE_IDS.iter().find_map(|id| { - let iface = self.iface(*id).ok()?; - iface.find_abstractable_impl(schema_ifaces) - }) - }) - .ok_or_else(move || { - ContractIfaceError::NoAbstractImpl( - C::IFACE_IDS[0], - schema_ifaces.schema.schema_id(), - ) - .into() - }) - } - fn schema( - &self, - schema_id: SchemaId, - ) -> Result<&SchemaIfaces, StashProviderError> { + fn schema(&self, schema_id: SchemaId) -> Result<&Schema, StashProviderError> { self.schemata .get(&schema_id) .ok_or_else(|| StashInconsistency::SchemaAbsent(schema_id).into()) } - fn get_trust(&self, identity: &Identity) -> Result { - Ok(self.identities.get(identity).copied().unwrap_or_default()) - } - - fn supplement(&self, content_ref: ContentRef) -> Result, Self::Error> { - Ok(self.suppl.get(&content_ref).and_then(|s| s.first())) - } - - fn supplements( - &self, - content_ref: ContentRef, - ) -> Result, Self::Error> { - Ok(self - .suppl - .get(&content_ref) - .cloned() - .unwrap_or_default() - .into_iter()) - } - - fn sigs_for(&self, content_id: &ContentId) -> Result, Self::Error> { - Ok(self.sigs.get(content_id)) - } - fn geneses(&self) -> Result, Self::Error> { Ok(self.geneses.values()) } - fn geneses_by(&self) -> Result, Self::Error> { - Ok(self.schemata_by::()?.flat_map(|schema_ifaces| { - self.geneses - .values() - .filter(|genesis| schema_ifaces.schema.schema_id() == genesis.schema_id) - })) - } - fn genesis( &self, contract_id: ContractId, @@ -287,7 +184,7 @@ impl StashReadProvider for MemStash { .ok_or(StashInconsistency::ContractAbsent(contract_id).into()) } - fn witness_ids(&self) -> Result, Self::Error> { + fn witness_ids(&self) -> Result, Self::Error> { Ok(self.witnesses.keys().copied()) } @@ -304,46 +201,26 @@ impl StashReadProvider for MemStash { .ok_or(StashInconsistency::BundleAbsent(bundle_id).into()) } - fn extension_ids(&self) -> Result, Self::Error> { - Ok(self.extensions.keys().copied()) - } - - fn extension(&self, op_id: OpId) -> Result<&Extension, StashProviderError> { - self.extensions - .get(&op_id) - .ok_or(StashInconsistency::OperationAbsent(op_id).into()) - } - - fn witness( - &self, - witness_id: XWitnessId, - ) -> Result<&SealWitness, StashProviderError> { + fn witness(&self, witness_id: Txid) -> Result<&SealWitness, StashProviderError> { self.witnesses .get(&witness_id) .ok_or(StashInconsistency::WitnessAbsent(witness_id).into()) } - fn taprets(&self) -> Result, Self::Error> { + fn taprets(&self) -> Result, Self::Error> { Ok(self .witnesses .iter() - .filter_map(|(witness_id, witness)| match &witness.anchors { - AnchorSet::Tapret(anchor) - | AnchorSet::Double { - tapret: anchor, - opret: _, - } => Some((*witness_id, TapretCommitment { - mpc: anchor.mpc_proof.commit_id(), - nonce: anchor.dbc_proof.path_proof.nonce(), + .filter_map(|(witness_id, witness)| match &witness.dbc_proof { + DbcProof::Tapret(tapret_proof) => Some((*witness_id, TapretCommitment { + mpc: witness.merkle_block.commit_id(), + nonce: tapret_proof.path_proof.nonce(), })), _ => None, })) } - fn seal_secret( - &self, - secret: XChain, - ) -> Result>, Self::Error> { + fn seal_secret(&self, secret: SecretSeal) -> Result, Self::Error> { Ok(self .secret_seals .iter() @@ -351,7 +228,7 @@ impl StashReadProvider for MemStash { .copied()) } - fn secret_seals(&self) -> Result>, Self::Error> { + fn secret_seals(&self) -> Result, Self::Error> { Ok(self.secret_seals.iter().copied()) } } @@ -362,64 +239,18 @@ impl StashWriteProvider for MemStash { fn replace_schema(&mut self, schema: Schema) -> Result { let schema_id = schema.schema_id(); if !self.schemata.contains_key(&schema_id) { - self.schemata.insert(schema_id, SchemaIfaces::new(schema))?; + self.schemata.insert(schema_id, schema)?; return Ok(true); } Ok(false) } - fn replace_iface(&mut self, iface: Iface) -> Result { - let iface_id = iface.iface_id(); - if !self.ifaces.contains_key(&iface_id) { - self.ifaces.insert(iface_id, iface)?; - return Ok(true); - } - Ok(false) - } - - fn replace_iimpl(&mut self, iimpl: IfaceImpl) -> Result { - let schema_ifaces = self - .schemata - .get_mut(&iimpl.schema_id) - .expect("unknown schema"); - let iface = self.ifaces.get(&iimpl.iface_id).expect("unknown interface"); - let iface_name = iface.name.clone(); - let present = schema_ifaces.iimpls.contains_key(&iface_name); - schema_ifaces.iimpls.insert(iface_name, iimpl)?; - Ok(!present) - } - - fn set_trust( - &mut self, - identity: Identity, - trust: TrustLevel, - ) -> Result<(), confinement::Error> { - self.identities.insert(identity, trust)?; - Ok(()) - } - - fn add_supplement(&mut self, suppl: Supplement) -> Result<(), Self::Error> { - match self.suppl.get_mut(&suppl.content_id) { - None => { - self.suppl.insert(suppl.content_id, tiny_bset![suppl])?; - } - Some(suppls) => suppls.push(suppl)?, - } - Ok(()) - } - fn replace_genesis(&mut self, genesis: Genesis) -> Result { let contract_id = genesis.contract_id(); let present = self.geneses.insert(contract_id, genesis)?.is_some(); Ok(!present) } - fn replace_extension(&mut self, extension: Extension) -> Result { - let opid = extension.id(); - let present = self.extensions.insert(opid, extension)?.is_some(); - Ok(!present) - } - fn replace_bundle(&mut self, bundle: TransitionBundle) -> Result { let bundle_id = bundle.bundle_id(); let present = self.bundles.insert(bundle_id, bundle)?.is_some(); @@ -432,15 +263,6 @@ impl StashWriteProvider for MemStash { Ok(!present) } - fn replace_attachment( - &mut self, - id: AttachId, - attach: MediumBlob, - ) -> Result { - let present = self.attachments.insert(id, attach)?.is_some(); - Ok(!present) - } - fn consume_types(&mut self, types: TypeSystem) -> Result<(), Self::Error> { Ok(self.type_system.extend(types)?) } @@ -450,30 +272,7 @@ impl StashWriteProvider for MemStash { Ok(!present) } - fn import_sigs(&mut self, content_id: ContentId, sigs: I) -> Result<(), Self::Error> - where I: IntoIterator { - let sigs = sigs.into_iter().filter(|(id, _)| { - match self.identities.get(id) { - Some(level) => *level, - None => { - let level = TrustLevel::default(); - // We ignore if the identities are full - self.identities.insert(id.clone(), level).ok(); - level - } - } - .should_accept() - }); - if let Some(prev_sigs) = self.sigs.get_mut(&content_id) { - prev_sigs.extend(sigs)?; - } else { - let sigs = Confined::try_from_iter(sigs)?; - self.sigs.insert(content_id, ContentSigs::from(sigs)).ok(); - } - Ok(()) - } - - fn add_secret_seal(&mut self, seal: XChain) -> Result { + fn add_secret_seal(&mut self, seal: GraphSeal) -> Result { let present = self.secret_seals.contains(&seal); self.secret_seals.push(seal)?; Ok(!present) @@ -493,8 +292,9 @@ pub struct MemState { #[strict_type(skip)] persistence: Option>, - witnesses: LargeOrdMap, - contracts: TinyOrdMap, + witnesses: LargeOrdMap, + invalid_bundles: LargeOrdSet, + contracts: SmallOrdMap, } impl StrictSerialize for MemState {} @@ -505,6 +305,7 @@ impl MemState { Self { persistence: none!(), witnesses: empty!(), + invalid_bundles: empty!(), contracts: empty!(), } } @@ -515,6 +316,7 @@ impl CloneNoPersistence for MemState { Self { persistence: None, witnesses: self.witnesses.clone(), + invalid_bundles: empty!(), contracts: self.contracts.clone(), } } @@ -569,20 +371,19 @@ impl StateReadProvider for MemState { || unfiltered.rights.iter().any(|a| a.witness == id) || unfiltered.fungibles.iter().any(|a| a.witness == id) || unfiltered.data.iter().any(|a| a.witness == id) - || unfiltered.attach.iter().any(|a| a.witness == id) }) .map(|(id, ord)| (*id, *ord)) .collect(); - Ok(MemContract { filter, unfiltered }) + Ok(MemContract { + filter, + invalid_bundles: self.invalid_bundles.clone().release(), + unfiltered, + }) } - fn is_valid_witness(&self, witness_id: XWitnessId) -> Result { - let ord = self - .witnesses - .get(&witness_id) - .ok_or(StateInconsistency::AbsentValidWitness)?; - Ok(ord.is_valid()) - } + fn witnesses(&self) -> LargeOrdMap { self.witnesses.clone() } + + fn invalid_bundles(&self) -> LargeOrdSet { self.invalid_bundles.clone() } } impl StateWriteProvider for MemState { @@ -610,7 +411,7 @@ impl StateWriteProvider for MemState { }; let mut writer = MemContractWriter { writer: Box::new( - |witness_id: XWitnessId, ord: WitnessOrd| -> Result<(), confinement::Error> { + |witness_id: Txid, ord: WitnessOrd| -> Result<(), confinement::Error> { // NB: We do not check the existence of the witness since we have a newer // version anyway and even if it is known we have to replace it self.witnesses.insert(witness_id, ord)?; @@ -635,7 +436,7 @@ impl StateWriteProvider for MemState { // We can't move this constructor to a dedicated method due to the rust borrower // checker writer: Box::new( - |witness_id: XWitnessId, ord: WitnessOrd| -> Result<(), confinement::Error> { + |witness_id: Txid, ord: WitnessOrd| -> Result<(), confinement::Error> { // NB: We do not check the existence of the witness since we have a newer // version anyway and even if it is known we have to replace // it @@ -647,35 +448,22 @@ impl StateWriteProvider for MemState { })) } - fn update_witnesses( + fn upsert_witness( &mut self, - resolver: impl ResolveWitness, - after_height: u32, - ) -> Result { - let after_height = NonZeroU32::new(after_height).unwrap_or(NonZeroU32::MIN); - let mut succeeded = 0; - let mut failed = map![]; - self.begin_transaction()?; - let mut witnesses = LargeOrdMap::new(); - mem::swap(&mut self.witnesses, &mut witnesses); - let mut witnesses = witnesses.release(); - for (id, ord) in &mut witnesses { - if matches!(ord, WitnessOrd::Mined(pos) if pos.height() < after_height) { - continue; - } - match resolver.resolve_pub_witness_ord(*id) { - Ok(new) => *ord = new, - Err(err) => { - failed.insert(*id, err.to_string()); - } - } - succeeded += 1; + witness_id: Txid, + witness_ord: WitnessOrd, + ) -> Result<(), Self::Error> { + self.witnesses.insert(witness_id, witness_ord)?; + Ok(()) + } + + fn update_bundle(&mut self, bundle_id: BundleId, valid: bool) -> Result<(), Self::Error> { + if valid { + self.invalid_bundles.remove(&bundle_id)?; + } else { + self.invalid_bundles.push(bundle_id)?; } - let mut witnesses = - LargeOrdMap::try_from(witnesses).inspect_err(|_| self.rollback_transaction())?; - mem::swap(&mut self.witnesses, &mut witnesses); - self.commit_transaction()?; - Ok(UpdateRes { succeeded, failed }) + Ok(()) } } @@ -684,7 +472,7 @@ impl StateWriteProvider for MemState { #[strict_type(lib = LIB_NAME_RGB_STORAGE)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] pub struct MemGlobalState { - known: LargeOrdMap, + known: LargeOrdMap, limit: u24, } @@ -724,7 +512,6 @@ pub struct MemContractState { rights: LargeOrdSet>, fungibles: LargeOrdSet>, data: LargeOrdSet>, - attach: LargeOrdSet>, } impl MemContractState { @@ -733,7 +520,7 @@ impl MemContractState { schema .global_types .iter() - .map(|(ty, glob)| (*ty, MemGlobalState::new(glob.max_items))), + .map(|(ty, glob)| (*ty, MemGlobalState::new(glob.global_state_schema.max_items))), ); MemContractState { schema_id: schema.schema_id(), @@ -742,7 +529,6 @@ impl MemContractState { rights: empty!(), fungibles: empty!(), data: empty!(), - attach: empty!(), } } @@ -756,10 +542,10 @@ impl MemContractState { .expect("global map must be initialized from the schema"); for (idx, s) in state.iter().enumerate() { let out = GlobalOut { - opid, - nonce: op.nonce(), index: idx as u16, op_witness: OpWitness::from(op), + nonce: op.nonce(), + opid, }; map.known .insert(out, s.clone()) @@ -767,62 +553,32 @@ impl MemContractState { } } - // We skip removing of invalidated state for the cases of re-orgs or unmined - // witness transactions committing to the new state. - // TODO: Expose an API to prune historic state by witness txid - /* - // Remove invalidated state - for input in &op.inputs() { - if let Some(o) = self.rights.iter().find(|r| r.opout == input.prev_out) { - let o = o.clone(); // need this b/c of borrow checker - self.rights - .remove(&o) - .expect("collection allows zero elements"); - } - if let Some(o) = self.fungibles.iter().find(|r| r.opout == input.prev_out) { - let o = o.clone(); - self.fungibles - .remove(&o) - .expect("collection allows zero elements"); - } - if let Some(o) = self.data.iter().find(|r| r.opout == input.prev_out) { - let o = o.clone(); - self.data - .remove(&o) - .expect("collection allows zero elements"); - } - if let Some(o) = self.attach.iter().find(|r| r.opout == input.prev_out) { - let o = o.clone(); - self.attach - .remove(&o) - .expect("collection allows zero elements"); - } - } - */ - + let bundle_id = op.bundle_id(); let witness_id = op.witness_id(); match op.assignments() { AssignmentsRef::Genesis(assignments) => { - self.add_assignments(witness_id, opid, assignments) + self.add_assignments(bundle_id, witness_id, opid, assignments) } AssignmentsRef::Graph(assignments) => { - self.add_assignments(witness_id, opid, assignments) + self.add_assignments(bundle_id, witness_id, opid, assignments) } } } fn add_assignments( &mut self, - witness_id: Option, + bundle_id: Option, + witness_id: Option, opid: OpId, assignments: &Assignments, ) { fn process( contract_state: &mut LargeOrdSet>, assignments: &[Assign], + bundle_id: Option, opid: OpId, ty: AssignmentType, - witness_id: Option, + witness_id: Option, ) { for (no, seal, state) in assignments .iter() @@ -830,10 +586,12 @@ impl MemContractState { .filter_map(|(n, a)| a.to_revealed().map(|(seal, state)| (n, seal, state))) { let assigned_state = match witness_id { - Some(witness_id) => { - OutputAssignment::with_witness(seal, witness_id, state, opid, ty, no as u16) - } - None => OutputAssignment::with_no_witness(seal, state, opid, ty, no as u16), + Some(witness_id) => OutputAssignment::with_witness( + seal, witness_id, state, bundle_id, opid, ty, no as u16, + ), + None => OutputAssignment::with_no_witness( + seal, state, bundle_id, opid, ty, no as u16, + ), }; contract_state .push(assigned_state) @@ -844,16 +602,13 @@ impl MemContractState { for (ty, assignments) in assignments.iter() { match assignments { TypedAssigns::Declarative(assignments) => { - process(&mut self.rights, assignments, opid, *ty, witness_id) + process(&mut self.rights, assignments, bundle_id, opid, *ty, witness_id) } TypedAssigns::Fungible(assignments) => { - process(&mut self.fungibles, assignments, opid, *ty, witness_id) + process(&mut self.fungibles, assignments, bundle_id, opid, *ty, witness_id) } TypedAssigns::Structured(assignments) => { - process(&mut self.data, assignments, opid, *ty, witness_id) - } - TypedAssigns::Attachment(assignments) => { - process(&mut self.attach, assignments, opid, *ty, witness_id) + process(&mut self.data, assignments, bundle_id, opid, *ty, witness_id) } } } @@ -861,7 +616,8 @@ impl MemContractState { } pub struct MemContract = MemContractState> { - filter: HashMap, + filter: HashMap, + invalid_bundles: BTreeSet, unfiltered: M, } @@ -876,12 +632,12 @@ impl> ContractStateAccess for MemContract { &self, ty: GlobalStateType, ) -> Result, UnknownGlobalStateType> { - type Src<'a> = &'a BTreeMap; - type FilteredIter<'a> = Box + 'a>; + type Src<'a> = &'a BTreeMap; + type FilteredIter<'a> = Box + 'a>; struct Iter<'a> { src: Src<'a>, iter: FilteredIter<'a>, - last: Option<(GlobalOrd, &'a DataState)>, + last: Option<(GlobalOrd, &'a RevealedData)>, depth: u24, constructor: Box) -> FilteredIter<'a> + 'a>, } @@ -893,7 +649,7 @@ impl> ContractStateAccess for MemContract { } } impl<'a> GlobalStateIter for Iter<'a> { - type Data = &'a DataState; + type Data = &'a RevealedData; fn size(&mut self) -> u24 { let iter = self.swap(); // TODO: Consuming iterator just to count items is highly inefficient, but I do @@ -944,10 +700,6 @@ impl> ContractStateAccess for MemContract { let ord = self.filter.get(&id)?; GlobalOrd::transition(out.opid, out.index, ty, out.nonce, *ord) } - OpWitness::Extension(id, ty) => { - let ord = self.filter.get(&id)?; - GlobalOrd::extension(out.opid, out.index, ty, out.nonce, *ord) - } }; Some((ord, data)) }) @@ -964,7 +716,7 @@ impl> ContractStateAccess for MemContract { Ok(GlobalContractState::new(iter)) } - fn rights(&self, outpoint: XOutpoint, ty: AssignmentType) -> u32 { + fn rights(&self, outpoint: Outpoint, ty: AssignmentType) -> u32 { self.unfiltered .borrow() .rights @@ -973,12 +725,13 @@ impl> ContractStateAccess for MemContract { assignment.seal.to_outpoint() == outpoint && assignment.opout.ty == ty }) .filter(|assignment| assignment.check_witness(&self.filter)) + .filter(|assignment| assignment.check_bundle(&self.invalid_bundles)) .count() as u32 } fn fungible( &self, - outpoint: XOutpoint, + outpoint: Outpoint, ty: AssignmentType, ) -> impl DoubleEndedIterator { self.unfiltered @@ -989,14 +742,15 @@ impl> ContractStateAccess for MemContract { assignment.seal.to_outpoint() == outpoint && assignment.opout.ty == ty }) .filter(|assignment| assignment.check_witness(&self.filter)) - .map(|assignment| assignment.state.value) + .filter(|assignment| assignment.check_bundle(&self.invalid_bundles)) + .map(|assignment| assignment.state.into()) } fn data( &self, - outpoint: XOutpoint, + outpoint: Outpoint, ty: AssignmentType, - ) -> impl DoubleEndedIterator> { + ) -> impl DoubleEndedIterator> { self.unfiltered .borrow() .data @@ -1005,41 +759,28 @@ impl> ContractStateAccess for MemContract { assignment.seal.to_outpoint() == outpoint && assignment.opout.ty == ty }) .filter(|assignment| assignment.check_witness(&self.filter)) - .map(|assignment| &assignment.state.value) - } - - fn attach( - &self, - outpoint: XOutpoint, - ty: AssignmentType, - ) -> impl DoubleEndedIterator> { - self.unfiltered - .borrow() - .attach - .iter() - .filter(move |assignment| { - assignment.seal.to_outpoint() == outpoint && assignment.opout.ty == ty - }) - .filter(|assignment| assignment.check_witness(&self.filter)) - .map(|assignment| &assignment.state.file) + .filter(|assignment| assignment.check_bundle(&self.invalid_bundles)) + .map(|assignment| &assignment.state) } } impl ContractStateEvolve for MemContract { type Context<'ctx> = (&'ctx Schema, ContractId); + type Error = MemError; fn init(context: Self::Context<'_>) -> Self { Self { filter: empty!(), + invalid_bundles: empty!(), unfiltered: MemContractState::new(context.0, context.1), } } - fn evolve_state(&mut self, op: OrdOpRef) -> Result<(), confinement::Error> { + fn evolve_state(&mut self, op: OrdOpRef) -> Result<(), Self::Error> { fn writer(me: &mut MemContract) -> MemContractWriter { MemContractWriter { writer: Box::new( - |witness_id: XWitnessId, ord: WitnessOrd| -> Result<(), confinement::Error> { + |witness_id: Txid, ord: WitnessOrd| -> Result<(), confinement::Error> { // NB: We do not check the existence of the witness since we have a // newer version anyway and even if it is // known we have to replace it @@ -1055,22 +796,11 @@ impl ContractStateEvolve for MemContract { let mut writer = writer(self); writer.add_genesis(genesis) } - OrdOpRef::Transition(transition, witness_id, ord) => { + OrdOpRef::Transition(transition, witness_id, ord, bundle_id) => { let mut writer = writer(self); - writer.add_transition(transition, witness_id, ord) + writer.add_transition(transition, witness_id, ord, bundle_id) } - OrdOpRef::Extension(extension, witness_id, ord) => { - let mut writer = writer(self); - writer.add_extension(extension, witness_id, ord) - } - } - .map_err(|err| { - // TODO: remove once evolve_state would accept arbitrary errors - match err { - MemError::Persistence(_) => unreachable!("only confinement errors are possible"), - MemError::Confinement(e) => e, - } - })?; + }?; Ok(()) } } @@ -1082,6 +812,11 @@ impl> ContractStateRead for MemContract { #[inline] fn schema_id(&self) -> SchemaId { self.unfiltered.borrow().schema_id } + #[inline] + fn witness_ord(&self, witness_id: Txid) -> Option { + self.filter.get(&witness_id).copied() + } + #[inline] fn rights_all(&self) -> impl Iterator> { self.unfiltered @@ -1089,6 +824,7 @@ impl> ContractStateRead for MemContract { .rights .iter() .filter(|assignment| assignment.check_witness(&self.filter)) + .filter(|assignment| assignment.check_bundle(&self.invalid_bundles)) } #[inline] @@ -1098,6 +834,7 @@ impl> ContractStateRead for MemContract { .fungibles .iter() .filter(|assignment| assignment.check_witness(&self.filter)) + .filter(|assignment| assignment.check_bundle(&self.invalid_bundles)) } #[inline] @@ -1107,24 +844,16 @@ impl> ContractStateRead for MemContract { .data .iter() .filter(|assignment| assignment.check_witness(&self.filter)) - } - - #[inline] - fn attach_all(&self) -> impl Iterator> { - self.unfiltered - .borrow() - .attach - .iter() - .filter(|assignment| assignment.check_witness(&self.filter)) + .filter(|assignment| assignment.check_bundle(&self.invalid_bundles)) } } pub struct MemContractWriter<'mem> { - writer: Box Result<(), confinement::Error> + 'mem>, + writer: Box Result<(), confinement::Error> + 'mem>, contract: &'mem mut MemContractState, } -impl<'mem> ContractStateWrite for MemContractWriter<'mem> { +impl ContractStateWrite for MemContractWriter<'_> { type Error = MemError; /// # Panics @@ -1143,28 +872,13 @@ impl<'mem> ContractStateWrite for MemContractWriter<'mem> { fn add_transition( &mut self, transition: &Transition, - witness_id: XWitnessId, - ord: WitnessOrd, - ) -> Result<(), Self::Error> { - (self.writer)(witness_id, ord)?; - self.contract - .add_operation(OrdOpRef::Transition(transition, witness_id, ord)); - Ok(()) - } - - /// # Panics - /// - /// If state extension violates RGB consensus rules and wasn't checked - /// against the schema before adding to the history. - fn add_extension( - &mut self, - extension: &Extension, - witness_id: XWitnessId, + witness_id: Txid, ord: WitnessOrd, + bundle_id: BundleId, ) -> Result<(), Self::Error> { (self.writer)(witness_id, ord)?; self.contract - .add_operation(OrdOpRef::Extension(extension, witness_id, ord)); + .add_operation(OrdOpRef::Transition(transition, witness_id, ord, bundle_id)); Ok(()) } } @@ -1190,8 +904,8 @@ impl From for IndexWriteError { serde(crate = "serde_crate", rename_all = "camelCase") )] pub struct ContractIndex { - public_opouts: MediumOrdSet, - outpoint_opouts: MediumOrdMap>, + public_opouts: LargeOrdSet, + outpoint_opouts: LargeOrdMap>, } #[derive(Getters, Debug)] @@ -1203,11 +917,12 @@ pub struct MemIndex { #[strict_type(skip)] persistence: Option>, - op_bundle_index: MediumOrdMap, - bundle_contract_index: MediumOrdMap, - bundle_witness_index: MediumOrdMap>, - contract_index: TinyOrdMap, - terminal_index: MediumOrdMap, TinyOrdSet>, + op_bundle_children_index: LargeOrdMap>, + op_bundle_index: LargeOrdMap, + bundle_contract_index: LargeOrdMap, + bundle_witness_index: LargeOrdMap>, + contract_index: SmallOrdMap, + terminal_index: LargeOrdMap>, } impl StrictSerialize for MemIndex {} @@ -1217,6 +932,7 @@ impl MemIndex { pub fn in_memory() -> Self { Self { persistence: None, + op_bundle_children_index: empty!(), op_bundle_index: empty!(), bundle_contract_index: empty!(), bundle_witness_index: empty!(), @@ -1230,6 +946,7 @@ impl CloneNoPersistence for MemIndex { fn clone_no_persistence(&self) -> Self { Self { persistence: None, + op_bundle_children_index: self.op_bundle_children_index.clone(), op_bundle_index: self.op_bundle_index.clone(), bundle_contract_index: self.bundle_contract_index.clone(), bundle_witness_index: self.bundle_witness_index.clone(), @@ -1268,14 +985,18 @@ impl IndexReadProvider for MemIndex { fn contracts_assigning( &self, - outputs: BTreeSet, + outpoints: BTreeSet, ) -> Result + '_, Self::Error> { Ok(self .contract_index .iter() .flat_map(move |(contract_id, index)| { - outputs.clone().into_iter().filter_map(|outpoint| { - if index.outpoint_opouts.contains_key(&outpoint) { + outpoints.clone().into_iter().filter_map(|outpoint| { + if index + .outpoint_opouts + .keys() + .any(|seal| seal.to_outpoint() == outpoint) + { Some(*contract_id) } else { None @@ -1298,17 +1019,19 @@ impl IndexReadProvider for MemIndex { fn opouts_by_outputs( &self, contract_id: ContractId, - outputs: impl IntoIterator>, + outpoints: impl IntoIterator>, ) -> Result, IndexReadError> { let index = self .contract_index .get(&contract_id) .ok_or(IndexInconsistency::ContractAbsent(contract_id))?; let mut opouts = BTreeSet::new(); - for output in outputs.into_iter().map(|o| o.into()) { + for output in outpoints.into_iter().map(|o| o.into()) { let set = index .outpoint_opouts - .get(&output) + .iter() + .find(|(seal, _)| seal.to_outpoint() == output) + .map(|(_, set)| set.to_unconfined()) .ok_or(IndexInconsistency::OutpointUnknown(output, contract_id))?; opouts.extend(set) } @@ -1317,7 +1040,7 @@ impl IndexReadProvider for MemIndex { fn opouts_by_terminals( &self, - terminals: impl IntoIterator>, + terminals: impl IntoIterator, ) -> Result, Self::Error> { let terminals = terminals.into_iter().collect::>(); Ok(self @@ -1336,11 +1059,21 @@ impl IndexReadProvider for MemIndex { .ok_or(IndexInconsistency::BundleAbsent(opid).into()) } + fn bundle_ids_children_of_op( + &self, + opid: OpId, + ) -> Result, IndexReadError> { + self.op_bundle_children_index + .get(&opid) + .ok_or(IndexInconsistency::BundleAbsent(opid).into()) + .cloned() + } + fn bundle_info( &self, bundle_id: BundleId, - ) -> Result<(impl Iterator, ContractId), IndexReadError> { - let witness_ids = self + ) -> Result<(impl Iterator, ContractId), IndexReadError> { + let witness_id = self .bundle_witness_index .get(&bundle_id) .ok_or(IndexInconsistency::BundleWitnessUnknown(bundle_id))?; @@ -1348,7 +1081,7 @@ impl IndexReadProvider for MemIndex { .bundle_contract_index .get(&bundle_id) .ok_or(IndexInconsistency::BundleContractUnknown(bundle_id))?; - Ok((witness_ids.iter().copied(), *contract_id)) + Ok((witness_id.iter().cloned(), *contract_id)) } } @@ -1367,7 +1100,7 @@ impl IndexWriteProvider for MemIndex { fn register_bundle( &mut self, bundle_id: BundleId, - witness_id: XWitnessId, + witness_id: Txid, contract_id: ContractId, ) -> Result> { if let Some(alt) = self @@ -1382,12 +1115,10 @@ impl IndexWriteProvider for MemIndex { } .into()); } - let mut set = self - .bundle_witness_index - .remove(&bundle_id)? - .unwrap_or_default(); - set.push(witness_id)?; - self.bundle_witness_index.insert(bundle_id, set)?; + self.bundle_witness_index + .entry(bundle_id)? + .or_default() + .push(witness_id)?; let present2 = self .bundle_contract_index .insert(bundle_id, contract_id)? @@ -1416,6 +1147,25 @@ impl IndexWriteProvider for MemIndex { Ok(!present) } + fn register_spending( + &mut self, + opid: OpId, + bundle_id: BundleId, + ) -> Result> { + let mut present = false; + match self.op_bundle_children_index.get_mut(&opid) { + Some(opids) => { + present = true; + opids.push(bundle_id)?; + } + None => { + self.op_bundle_children_index + .insert(opid, small_bset!(bundle_id))?; + } + } + Ok(present) + } + fn index_genesis_assignments( &mut self, contract_id: ContractId, @@ -1430,7 +1180,7 @@ impl IndexWriteProvider for MemIndex { for (no, assign) in vec.iter().enumerate() { let opout = Opout::new(opid, type_id, no as u16); - if let Assign::ConfidentialState { seal, .. } | Assign::Revealed { seal, .. } = assign { + if let Assign::Revealed { seal, .. } = assign { let output = seal .to_output_seal() .expect("genesis seals always have outpoint"); @@ -1455,7 +1205,7 @@ impl IndexWriteProvider for MemIndex { vec: &[Assign], opid: OpId, type_id: AssignmentType, - witness_id: XWitnessId, + witness_id: Txid, ) -> Result<(), IndexWriteError> { let index = self .contract_index @@ -1464,14 +1214,8 @@ impl IndexWriteProvider for MemIndex { for (no, assign) in vec.iter().enumerate() { let opout = Opout::new(opid, type_id, no as u16); - if let Assign::ConfidentialState { seal, .. } | Assign::Revealed { seal, .. } = assign { - let output = seal.try_to_output_seal(witness_id).unwrap_or_else(|_| { - panic!( - "chain mismatch between assignment vout seal ({}) and witness transaction \ - ({})", - seal, witness_id - ) - }); + if let Assign::Revealed { seal, .. } = assign { + let output = seal.to_output_seal_or_default(witness_id); match index.outpoint_opouts.get_mut(&output) { Some(opouts) => { opouts.push(opout)?; @@ -1497,9 +1241,7 @@ impl MemIndex { ) -> Result<(), IndexWriteError> { for (no, assign) in vec.iter().enumerate() { let opout = Opout::new(opid, type_id, no as u16); - if let Assign::Confidential { seal, .. } | Assign::ConfidentialSeal { seal, .. } = - assign - { + if let Assign::ConfidentialSeal { seal, .. } = assign { self.add_terminal(*seal, opout)?; } } @@ -1508,7 +1250,7 @@ impl MemIndex { fn add_terminal( &mut self, - seal: XChain, + seal: SecretSeal, opout: Opout, ) -> Result<(), IndexWriteError> { match self @@ -1516,9 +1258,12 @@ impl MemIndex { .remove(&seal) .expect("can have zero elements") { - Some(mut existing_opouts) => existing_opouts.push(opout)?, + Some(mut existing_opouts) => { + existing_opouts.push(opout)?; + let _ = self.terminal_index.insert(seal, existing_opouts); + } None => { - self.terminal_index.insert(seal, tiny_bset![opout])?; + self.terminal_index.insert(seal, medium_bset![opout])?; } } Ok(()) diff --git a/src/persistence/mod.rs b/src/persistence/mod.rs index 321f7b14..75c164fc 100644 --- a/src/persistence/mod.rs +++ b/src/persistence/mod.rs @@ -47,16 +47,16 @@ pub use memory::{ MemContract, MemContractState, MemError, MemGlobalState, MemIndex, MemStash, MemState, }; pub use stash::{ - ProviderError as StashProviderError, SchemaIfaces, Stash, StashDataError, StashError, - StashInconsistency, StashProvider, StashReadProvider, StashWriteProvider, + ProviderError as StashProviderError, Stash, StashDataError, StashError, StashInconsistency, + StashProvider, StashReadProvider, StashWriteProvider, }; pub use state::{ - ContractStateRead, ContractStateWrite, PersistedState, State, StateError, StateInconsistency, - StateProvider, StateReadProvider, StateWriteProvider, + ContractStateRead, ContractStateWrite, State, StateError, StateInconsistency, StateProvider, + StateReadProvider, StateWriteProvider, }; pub use stock::{ - ComposeError, ConsignError, ContractIfaceError, FasciaError, InputError as StockInputError, - Stock, StockError, StockErrorAll, StockErrorMem, UpdateRes, + ComposeError, ConsignError, FasciaError, InputError as StockInputError, Stock, StockError, + StockErrorAll, StockErrorMem, UpdateRes, }; pub trait StoreTransaction { diff --git a/src/persistence/stash.rs b/src/persistence/stash.rs index 0924fdd0..ae406457 100644 --- a/src/persistence/stash.rs +++ b/src/persistence/stash.rs @@ -19,38 +19,31 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::collections::{BTreeMap, BTreeSet}; +use std::collections::BTreeMap; use std::error::Error; use std::fmt::Debug; use aluvm::library::{Lib, LibId}; -use amplify::confinement::{Confined, MediumBlob, TinyOrdMap}; -use amplify::{confinement, ByteArray}; -use bp::dbc::anchor::MergeError; +use amplify::confinement::Confined; +use amplify::ByteArray; use bp::dbc::tapret::TapretCommitment; -use bp::dbc::Anchor; use bp::seals::txout::CloseMethod; -use commit_verify::mpc; -use commit_verify::mpc::MerkleBlock; +use commit_verify::mpc::{self, MerkleBlock}; use nonasync::persistence::{CloneNoPersistence, Persisting}; -use rgb::validation::{DbcProof, Scripts}; +use rgb::validation::Scripts; use rgb::{ - AttachId, BundleId, ContractId, Extension, Genesis, GraphSeal, Identity, OpId, Operation, - Schema, SchemaId, TransitionBundle, XChain, XWitnessId, + BundleId, ChainNet, ContractId, Genesis, GraphSeal, Identity, OpId, Schema, SchemaId, + TransitionBundle, TransitionType, Txid, }; -use strict_encoding::{FieldName, TypeName}; use strict_types::typesys::UnknownType; -use strict_types::TypeSystem; +use strict_types::{FieldName, TypeSystem}; use crate::containers::{ - AnchorSet, Consignment, ConsignmentExt, ContentId, ContentRef, ContentSigs, Kit, SealWitness, - SigBlob, Supplement, TrustLevel, WitnessBundle, + Consignment, ConsignmentExt, Kit, SealWitness, SealWitnessMergeError, WitnessBundle, }; -use crate::interface::{ - ContractBuilder, Iface, IfaceClass, IfaceId, IfaceImpl, IfaceRef, TransitionBuilder, -}; -use crate::persistence::{ContractIfaceError, StoreTransaction}; -use crate::{MergeReveal, MergeRevealError, SecretSeal, LIB_NAME_RGB_STD}; +use crate::contract::{ContractBuilder, TransitionBuilder}; +use crate::persistence::StoreTransaction; +use crate::{MergeReveal, MergeRevealError, SecretSeal}; #[derive(Debug, Display, Error, From)] #[display(inner)] @@ -71,7 +64,7 @@ pub enum StashError { /// Errors caused by invalid input arguments. #[from] #[from(UnknownType)] - #[from(MergeError)] + #[from(SealWitnessMergeError)] #[from(MergeRevealError)] #[from(mpc::InvalidProof)] Data(StashDataError), @@ -82,8 +75,6 @@ pub enum StashError { pub enum ProviderError { #[from] Inconsistency(StashInconsistency), - #[from] - Iface(ContractIfaceError), Connectivity(E), } @@ -92,7 +83,6 @@ impl From::Error>> for match err { ProviderError::Inconsistency(e) => StashError::Inconsistency(e), ProviderError::Connectivity(e) => StashError::ReadProvider(e), - ProviderError::Iface(e) => StashError::Data(StashDataError::NoAbstractIface(e)), } } } @@ -103,26 +93,20 @@ pub enum StashInconsistency { /// library {0} is unknown; perhaps you need to import it first. LibAbsent(LibId), - /// interface {0} is unknown; perhaps you need to import it first. - IfaceAbsent(IfaceRef), - /// contract {0} is unknown. Probably you haven't imported the contract yet. ContractAbsent(ContractId), /// schema {0} is unknown. SchemaAbsent(SchemaId), - /// interface {0::<0} is not implemented for the schema {1::<0}. - IfaceImplAbsent(IfaceId, SchemaId), - /// transition {0} is absent. OperationAbsent(OpId), /// information about witness {0} is absent. - WitnessAbsent(XWitnessId), + WitnessAbsent(Txid), /// witness {0} for the bundle {1} misses contract {2} information in {3} anchor. - WitnessMissesContract(XWitnessId, BundleId, ContractId, CloseMethod), + WitnessMissesContract(Txid, BundleId, ContractId, CloseMethod), /// bundle {0} is absent. BundleAbsent(BundleId), @@ -135,7 +119,7 @@ pub enum StashInconsistency { #[derive(Clone, PartialEq, Eq, Debug, Display, Error, From)] #[display(doc_comments)] pub enum StashDataError { - /// schema {0} and related interfaces use too many AluVM libraries. + /// schema {0} uses too many AluVM libraries. TooManyLibs(SchemaId), #[from] @@ -148,48 +132,11 @@ pub enum StashDataError { #[from] #[display(inner)] - Merge(MergeError), + Merge(SealWitnessMergeError), #[from] #[display(inner)] MergeReveal(MergeRevealError), - - /// schema {0} doesn't implement interface {1}. - NoIfaceImpl(SchemaId, IfaceId), - - #[from] - #[display(inner)] - NoAbstractIface(ContractIfaceError), -} - -#[derive(Clone, Eq, PartialEq, Debug)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_STD)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub struct SchemaIfaces { - pub schema: Schema, - pub iimpls: TinyOrdMap, -} - -impl SchemaIfaces { - pub fn new(schema: Schema) -> Self { - SchemaIfaces { - schema, - iimpls: none!(), - } - } - - pub fn get(&self, id: IfaceId) -> Option<&IfaceImpl> { - self.iimpls.values().find(|iimpl| iimpl.iface_id == id) - } - - pub fn contains(&self, id: IfaceId) -> bool { - self.iimpls.values().any(|iimpl| iimpl.iface_id == id) - } } #[derive(Debug)] @@ -224,82 +171,28 @@ impl Stash

{ #[doc(hidden)] pub(super) fn as_provider_mut(&mut self) -> &mut P { &mut self.provider } - pub(super) fn ifaces(&self) -> Result + '_, StashError

> { - self.provider.ifaces().map_err(StashError::ReadProvider) - } - pub(super) fn iface(&self, iface: impl Into) -> Result<&Iface, StashError

> { - Ok(self.provider.iface(iface)?) - } - pub(super) fn schemata( - &self, - ) -> Result + '_, StashError

> { + pub(super) fn schemata(&self) -> Result + '_, StashError

> { self.provider.schemata().map_err(StashError::ReadProvider) } - pub(super) fn schema(&self, schema_id: SchemaId) -> Result<&SchemaIfaces, StashError

> { + pub(super) fn schema(&self, schema_id: SchemaId) -> Result<&Schema, StashError

> { Ok(self.provider.schema(schema_id)?) } - pub(super) fn impl_for<'a, C: IfaceClass + 'a>( - &'a self, - schema_ifaces: &'a SchemaIfaces, - ) -> Result<&'a IfaceImpl, StashError

> { - Ok(self.provider.impl_for::(schema_ifaces)?) - } pub(super) fn geneses(&self) -> Result + '_, StashError

> { self.provider.geneses().map_err(StashError::ReadProvider) } - pub(super) fn geneses_by<'a, C: IfaceClass + 'a>( - &'a self, - ) -> Result + 'a, StashError

> { - self.provider - .geneses_by::() - .map_err(StashError::ReadProvider) - } pub(super) fn genesis(&self, contract_id: ContractId) -> Result<&Genesis, StashError

> { Ok(self.provider.genesis(contract_id)?) } pub(super) fn bundle(&self, bundle_id: BundleId) -> Result<&TransitionBundle, StashError

> { Ok(self.provider.bundle(bundle_id)?) } - pub(super) fn witness(&self, witness_id: XWitnessId) -> Result<&SealWitness, StashError

> { + pub(super) fn witness(&self, witness_id: Txid) -> Result<&SealWitness, StashError

> { Ok(self.provider.witness(witness_id)?) } - pub(super) fn supplements( - &self, - content_ref: ContentRef, - ) -> Result + '_, StashError

> { - self.provider - .supplements(content_ref) - .map_err(StashError::ReadProvider) - } - - pub(super) fn sigs_for( - &self, - content_id: &ContentId, - ) -> Result, StashError

> { - self.provider - .sigs_for(content_id) - .map_err(StashError::ReadProvider) - } - - pub(super) fn supplement( - &self, - content_ref: ContentRef, - ) -> Result, StashError

> { - self.provider - .supplement(content_ref) - .map_err(StashError::ReadProvider) - } - - pub(super) fn extract<'a>( - &self, - schema: &Schema, - ifaces: impl IntoIterator, - ) -> Result<(TypeSystem, Scripts), StashError

> { - let type_iter = schema - .types() - .chain(ifaces.into_iter().flat_map(Iface::types)); + pub(super) fn extract(&self, schema: &Schema) -> Result<(TypeSystem, Scripts), StashError

> { + let type_iter = schema.types(); let types = self .provider .type_system() @@ -321,114 +214,40 @@ impl Stash

{ &self, issuer: Identity, schema_id: SchemaId, - iface: impl Into, + chain_net: ChainNet, ) -> Result> { - let schema_ifaces = self.schema(schema_id)?; - let iface = self.iface(iface)?; - let iface_id = iface.iface_id(); - let iimpl = schema_ifaces - .get(iface_id) - .ok_or(StashDataError::NoIfaceImpl(schema_id, iface_id))?; - - let (types, scripts) = self.extract(&schema_ifaces.schema, [iface])?; - - let builder = ContractBuilder::with( - issuer, - iface.clone(), - schema_ifaces.schema.clone(), - iimpl.clone(), - types, - scripts, - ); + let schema = self.schema(schema_id)?; + + let (types, scripts) = self.extract(schema)?; + + let builder = ContractBuilder::with(issuer, schema.clone(), types, scripts, chain_net); Ok(builder) } pub(super) fn transition_builder( &self, contract_id: ContractId, - iface: impl Into, - transition_name: Option>, + transition_name: impl Into, ) -> Result> { - let schema_ifaces = self.provider.contract_schema(contract_id)?; - let iface = self.iface(iface)?; - let schema = &schema_ifaces.schema; - let iimpl = schema_ifaces - .get(iface.iface_id()) - .ok_or(StashDataError::NoIfaceImpl(schema.schema_id(), iface.iface_id()))?; - let genesis = self.provider.genesis(contract_id)?; - - let (types, _) = self.extract(&schema_ifaces.schema, [iface])?; - - let mut builder = if let Some(transition_name) = transition_name { - TransitionBuilder::named_transition( - contract_id, - iface.clone(), - schema.clone(), - iimpl.clone(), - transition_name.into(), - types, - ) - } else { - TransitionBuilder::default_transition( - contract_id, - iface.clone(), - schema.clone(), - iimpl.clone(), - types, - ) - } - .expect("internal inconsistency"); + let schema = self.provider.contract_schema(contract_id)?; + let (types, _) = self.extract(schema)?; - for (assignment_type, asset_tag) in genesis.asset_tags.iter() { - builder = builder - .add_asset_tag_raw(*assignment_type, *asset_tag) - .expect("tags are in bset and must not repeat"); - } + let transition_type = schema.transition_type(transition_name); + let builder = TransitionBuilder::with(contract_id, schema.clone(), transition_type, types); Ok(builder) } - pub(super) fn blank_builder( + pub(super) fn transition_builder_raw( &self, contract_id: ContractId, - iface: impl Into, + transition_type: TransitionType, ) -> Result> { - let schema_ifaces = self.provider.contract_schema(contract_id)?; - let iface = self.iface(iface)?; - let schema = &schema_ifaces.schema; - if schema_ifaces.iimpls.is_empty() { - return Err(StashDataError::NoIfaceImpl(schema.schema_id(), iface.iface_id()).into()); - } - let genesis = self.provider.genesis(contract_id)?; - - let (types, _) = self.extract(&schema_ifaces.schema, [iface])?; - - let mut builder = if let Some(iimpl) = schema_ifaces.get(iface.iface_id()) { - TransitionBuilder::blank_transition( - contract_id, - iface.clone(), - schema.clone(), - iimpl.clone(), - types, - ) - } else { - let (default_iface_name, default_iimpl) = - schema_ifaces.iimpls.first_key_value().unwrap(); - let default_iface = self.iface(default_iface_name.clone())?; - - TransitionBuilder::blank_transition( - contract_id, - default_iface.clone(), - schema.clone(), - default_iimpl.clone(), - types, - ) - }; - for (assignment_type, asset_tag) in genesis.asset_tags.iter() { - builder = builder - .add_asset_tag_raw(*assignment_type, *asset_tag) - .expect("tags are in bset and must not repeat"); - } + let schema = self.provider.contract_schema(contract_id)?; + + let (types, _) = self.extract(schema)?; + + let builder = TransitionBuilder::with(contract_id, schema.clone(), transition_type, types); Ok(builder) } @@ -443,35 +262,11 @@ impl Stash

{ .map_err(StashError::WriteProvider)?; } - // TODO: filter most trusted signers for schema in kit.schemata { self.provider .replace_schema(schema) .map_err(StashError::WriteProvider)?; } - for iface in kit.ifaces { - self.provider - .replace_iface(iface) - .map_err(StashError::WriteProvider)?; - } - for iimpl in kit.iimpls { - self.provider - .replace_iimpl(iimpl) - .map_err(StashError::WriteProvider)?; - } - - // TODO: filter out non-trusted signers - for suppl in kit.supplements { - self.provider - .add_supplement(suppl) - .map_err(StashError::WriteProvider)?; - } - - for (content_id, sigs) in kit.signatures { - // TODO: Filter sigs by trust level - // Do not bother if we can't import all the sigs - self.provider.import_sigs(content_id, sigs).ok(); - } Ok(()) } @@ -495,96 +290,62 @@ impl Stash

{ let contract_id = consignment.contract_id(); let genesis = match self.genesis(contract_id) { - Ok(g) => g.clone().merge_reveal(consignment.genesis)?, + Ok(g) => { + let mut g = g.clone(); + g.merge_reveal(&consignment.genesis)?; + g + } Err(_) => consignment.genesis, }; self.provider .replace_genesis(genesis) .map_err(StashError::WriteProvider)?; - for extension in consignment.extensions { - let opid = extension.id(); - let extension = match self.provider.extension(opid) { - Ok(e) => e.clone().merge_reveal(extension)?, - Err(_) => extension, - }; - self.provider - .replace_extension(extension) - .map_err(StashError::WriteProvider)?; - } - - for bw in consignment.bundles { - self.consume_bundled_witness(contract_id, bw)?; - } - - for (id, attach) in consignment.attachments { - self.provider - .replace_attachment(id, attach) - .map_err(StashError::WriteProvider)?; + for witness_bundles in consignment.bundles { + self.consume_witness_bundle(contract_id, witness_bundles)?; } - let (ifaces, iimpls): (BTreeSet<_>, BTreeSet<_>) = consignment - .ifaces - .release() - .into_iter() - .fold((bset!(), bset!()), |(mut keys, mut values), (k, v)| { - keys.insert(k); - values.insert(v); - (keys, values) - }); - self.consume_kit(Kit { version: consignment.version, - ifaces: Confined::from_checked(ifaces), schemata: tiny_bset![consignment.schema], - iimpls: Confined::from_checked(iimpls), - supplements: consignment.supplements, types: consignment.types, scripts: Confined::from_checked(consignment.scripts.release()), - signatures: consignment.signatures, }) } - fn consume_bundled_witness( + fn consume_witness_bundle( &mut self, contract_id: ContractId, - bundled_witness: WitnessBundle, + witness_bundle: WitnessBundle, ) -> Result<(), StashError

> { - let WitnessBundle { - pub_witness, - bundle, - anchor, - } = bundled_witness; - - // TODO: Save pub witness transaction and SPVs + let bundle = witness_bundle.bundle(); + let eanchor = witness_bundle.eanchor(); let bundle_id = bundle.bundle_id(); - self.consume_bundle(bundle)?; + self.consume_bundle(bundle.clone())?; let proto = mpc::ProtocolId::from_byte_array(contract_id.to_byte_array()); let msg = mpc::Message::from_byte_array(bundle_id.to_byte_array()); - let merkle_block = MerkleBlock::with(&anchor.mpc_proof, proto, msg)?; - let anchors = match anchor.dbc_proof { - DbcProof::Tapret(tapret) => AnchorSet::Tapret(Anchor::new(merkle_block, tapret)), - DbcProof::Opret(opret) => AnchorSet::Opret(Anchor::new(merkle_block, opret)), - }; + let merkle_block = MerkleBlock::with(&eanchor.mpc_proof, proto, msg)?; + let witness = SealWitness { - public: pub_witness.clone(), - anchors, + public: witness_bundle.pub_witness.clone(), + merkle_block, + dbc_proof: eanchor.dbc_proof, }; - self.consume_witness(witness)?; + self.consume_witness(&witness)?; Ok(()) } - pub(crate) fn consume_witness(&mut self, witness: SealWitness) -> Result> { + pub(crate) fn consume_witness(&mut self, witness: &SealWitness) -> Result> { let witness = match self.provider.witness(witness.witness_id()).cloned() { - Ok(mut w) => { - w.public = w.public.clone().merge_reveal(witness.public)?; - w.anchors = w.anchors.clone().merge_reveal(witness.anchors)?; + Ok(w) => { + let mut w = w.clone(); + w.merge_reveal(witness)?; w } - Err(_) => witness, + Err(_) => witness.clone(), }; self.provider @@ -597,7 +358,11 @@ impl Stash

{ bundle: TransitionBundle, ) -> Result> { let bundle = match self.provider.bundle(bundle.bundle_id()).cloned() { - Ok(b) => b.merge_reveal(bundle)?, + Ok(b) => { + let mut b = b.clone(); + b.merge_reveal(&bundle)?; + b + } Err(_) => bundle, }; self.provider @@ -605,10 +370,7 @@ impl Stash

{ .map_err(StashError::WriteProvider) } - pub(crate) fn store_secret_seal( - &mut self, - seal: XChain, - ) -> Result> { + pub(crate) fn store_secret_seal(&mut self, seal: GraphSeal) -> Result> { self.begin_transaction()?; let seal = self .provider @@ -650,75 +412,39 @@ pub trait StashReadProvider { fn type_system(&self) -> Result<&TypeSystem, Self::Error>; fn lib(&self, id: LibId) -> Result<&Lib, ProviderError>; - fn ifaces(&self) -> Result, Self::Error>; - fn iface(&self, iface: impl Into) -> Result<&Iface, ProviderError>; - fn schemata(&self) -> Result, Self::Error>; - fn schema(&self, schema_id: SchemaId) -> Result<&SchemaIfaces, ProviderError>; - fn schemata_by( - &self, - ) -> Result, Self::Error>; - fn impl_for<'a, C: IfaceClass + 'a>( - &'a self, - schema_ifaces: &'a SchemaIfaces, - ) -> Result<&'a IfaceImpl, ProviderError>; + fn schemata(&self) -> Result, Self::Error>; + fn schema(&self, schema_id: SchemaId) -> Result<&Schema, ProviderError>; fn geneses(&self) -> Result, Self::Error>; - fn geneses_by(&self) -> Result, Self::Error>; fn genesis(&self, contract_id: ContractId) -> Result<&Genesis, ProviderError>; fn contract_schema( &self, contract_id: ContractId, - ) -> Result<&SchemaIfaces, ProviderError> { + ) -> Result<&Schema, ProviderError> { let genesis = self.genesis(contract_id)?; self.schema(genesis.schema_id) } - fn get_trust(&self, identity: &Identity) -> Result; - fn supplement(&self, content_ref: ContentRef) -> Result, Self::Error>; - fn supplements( - &self, - content_ref: ContentRef, - ) -> Result, Self::Error>; - - fn sigs_for(&self, content_id: &ContentId) -> Result, Self::Error>; - fn witness_ids(&self) -> Result, Self::Error>; + fn witness_ids(&self) -> Result, Self::Error>; fn bundle_ids(&self) -> Result, Self::Error>; fn bundle(&self, bundle_id: BundleId) -> Result<&TransitionBundle, ProviderError>; - fn extension_ids(&self) -> Result, Self::Error>; - fn extension(&self, op_id: OpId) -> Result<&Extension, ProviderError>; - fn witness(&self, witness_id: XWitnessId) -> Result<&SealWitness, ProviderError>; + fn witness(&self, witness_id: Txid) -> Result<&SealWitness, ProviderError>; - fn taprets(&self) -> Result, Self::Error>; - fn seal_secret( - &self, - secret: XChain, - ) -> Result>, Self::Error>; - fn secret_seals(&self) -> Result>, Self::Error>; + fn taprets(&self) -> Result, Self::Error>; + fn seal_secret(&self, secret: SecretSeal) -> Result, Self::Error>; + fn secret_seals(&self) -> Result, Self::Error>; } pub trait StashWriteProvider: StoreTransaction { type Error: Error; fn replace_schema(&mut self, schema: Schema) -> Result; - fn replace_iface(&mut self, iface: Iface) -> Result; - fn replace_iimpl(&mut self, iimpl: IfaceImpl) -> Result; fn replace_genesis(&mut self, genesis: Genesis) -> Result; - fn replace_extension(&mut self, extension: Extension) -> Result; fn replace_bundle(&mut self, bundle: TransitionBundle) -> Result; fn replace_witness(&mut self, witness: SealWitness) -> Result; - fn replace_attachment(&mut self, id: AttachId, attach: MediumBlob) - -> Result; fn replace_lib(&mut self, lib: Lib) -> Result; fn consume_types(&mut self, types: TypeSystem) -> Result<(), Self::Error>; - fn set_trust( - &mut self, - identity: Identity, - trust: TrustLevel, - ) -> Result<(), confinement::Error>; - fn add_supplement(&mut self, suppl: Supplement) -> Result<(), Self::Error>; - fn import_sigs(&mut self, content_id: ContentId, sigs: I) -> Result<(), Self::Error> - where I: IntoIterator; - - fn add_secret_seal(&mut self, seal: XChain) -> Result; + + fn add_secret_seal(&mut self, seal: GraphSeal) -> Result; } diff --git a/src/persistence/state.rs b/src/persistence/state.rs index 621ec2eb..2a66ae3e 100644 --- a/src/persistence/state.rs +++ b/src/persistence/state.rs @@ -20,24 +20,21 @@ // limitations under the License. use std::borrow::Borrow; -use std::collections::BTreeMap; use std::error::Error; use std::fmt::Debug; -use std::iter; -use invoice::Amount; +use amplify::confinement::{LargeOrdMap, LargeOrdSet}; use nonasync::persistence::{CloneNoPersistence, Persisting}; use rgb::validation::{ResolveWitness, WitnessResolverError}; use rgb::vm::{ContractStateAccess, WitnessOrd}; use rgb::{ - AssetTag, AttachState, BlindingFactor, ContractId, DataState, Extension, Genesis, Operation, - RevealedAttach, RevealedData, RevealedValue, Schema, SchemaId, Transition, TransitionBundle, - VoidState, XWitnessId, + BundleId, ContractId, Genesis, RevealedData, RevealedValue, Schema, SchemaId, Transition, + TransitionBundle, Txid, VoidState, }; use crate::containers::{ConsignmentExt, ToWitnessId}; use crate::contract::OutputAssignment; -use crate::persistence::{StoreTransaction, UpdateRes}; +use crate::persistence::StoreTransaction; #[derive(Debug, Display, Error, From)] #[display(inner)] @@ -50,7 +47,11 @@ pub enum StateError { /// witness {0} can't be resolved: {1} #[display(doc_comments)] - Resolver(XWitnessId, WitnessResolverError), + Resolver(Txid, WitnessResolverError), + + /// valid (non-archived) witness is absent in the list of witnesses for a + /// state transition bundle. + AbsentValidWitness, /// {0} /// @@ -66,30 +67,8 @@ pub enum StateError { pub enum StateInconsistency { /// contract state {0} is not known. UnknownContract(ContractId), - /// valid (non-archived) witness is absent in the list of witnesses for a - /// state transition bundle. - AbsentValidWitness, -} - -#[derive(Clone, Eq, PartialEq, Debug, Hash)] -pub enum PersistedState { - Void, - Amount(Amount, BlindingFactor, AssetTag), - // TODO: Use RevealedData - Data(DataState, u128), - // TODO: Use RevealedAttach - Attachment(AttachState, u64), -} - -impl PersistedState { - pub(crate) fn update_blinding(&mut self, blinding: BlindingFactor) { - match self { - PersistedState::Void => {} - PersistedState::Amount(_, b, _) => *b = blinding, - PersistedState::Data(_, _) => {} - PersistedState::Attachment(_, _) => {} - } - } + /// a witness {0} is absent from the state data. + AbsentWitness(Txid), } #[derive(Debug)] @@ -136,26 +115,35 @@ impl State

{ pub fn select_valid_witness( &self, - witness_ids: impl IntoIterator>, - ) -> Result> { - for witness_id in witness_ids { - let witness_id = *witness_id.borrow(); - if self - .provider - .is_valid_witness(witness_id) - .map_err(StateError::ReadProvider)? - { - return Ok(witness_id); - } + witness_ids: impl IntoIterator>, + ) -> Result<(Txid, WitnessOrd), StateError

> { + let witnesses = self.as_provider().witnesses(); + let mut best_candidate = None; + for id in witness_ids { + let id = *id.borrow(); + let Some(&ord) = witnesses.get(&id) else { + return Err(StateError::Inconsistency(StateInconsistency::AbsentWitness(id))); + }; + best_candidate = match best_candidate { + Some((_, curr_ord)) if ord < curr_ord => Some((id, ord)), + None => Some((id, ord)), + _ => best_candidate, + }; + } + + let (best_id, best_ord) = best_candidate.expect("one witness ID should always be there"); + if best_ord == WitnessOrd::Archived { + Err(StateError::AbsentValidWitness) + } else { + Ok((best_id, best_ord)) } - Err(StateError::Inconsistency(StateInconsistency::AbsentValidWitness)) } pub fn update_from_bundle( &mut self, contract_id: ContractId, bundle: &TransitionBundle, - witness_id: XWitnessId, + witness_id: Txid, resolver: R, ) -> Result<(), StateError

> { let mut updater = self @@ -163,12 +151,13 @@ impl State

{ .update_contract(contract_id) .map_err(StateError::WriteProvider)? .ok_or(StateInconsistency::UnknownContract(contract_id))?; + let bundle_id = bundle.bundle_id(); for transition in bundle.known_transitions.values() { let ord = resolver .resolve_pub_witness_ord(witness_id) .map_err(|e| StateError::Resolver(witness_id, e))?; updater - .add_transition(transition, witness_id, ord) + .add_transition(transition, witness_id, ord, bundle_id) .map_err(StateError::WriteProvider)?; } Ok(()) @@ -183,62 +172,37 @@ impl State

{ .as_provider_mut() .register_contract(consignment.schema(), consignment.genesis()) .map_err(StateError::WriteProvider)?; - let mut extension_idx = consignment - .extensions() - .map(Extension::id) - .zip(iter::repeat(false)) - .collect::>(); - let mut ordered_extensions = BTreeMap::new(); for witness_bundle in consignment.bundled_witnesses() { - for transition in witness_bundle.bundle.known_transitions.values() { + let bundle = witness_bundle.bundle(); + let bundle_id = bundle.bundle_id(); + for (_, transition) in &bundle.known_transitions { let witness_id = witness_bundle.pub_witness.to_witness_id(); let witness_ord = resolver .resolve_pub_witness_ord(witness_id) .map_err(|e| StateError::Resolver(witness_id, e))?; state - .add_transition(transition, witness_id, witness_ord) - .map_err(StateError::WriteProvider)?; - for (id, used) in &mut extension_idx { - if *used { - continue; - } - for input in &transition.inputs { - if input.prev_out.op == *id { - *used = true; - if let Some((_, witness_ord2)) = ordered_extensions.get_mut(id) { - if *witness_ord2 < witness_ord { - *witness_ord2 = witness_ord; - } - } else { - ordered_extensions.insert(*id, (witness_id, witness_ord)); - } - } - } - } - } - } - for extension in consignment.extensions() { - if let Some((witness_id, witness_ord)) = ordered_extensions.get(&extension.id()) { - state - .add_extension(extension, *witness_id, *witness_ord) + .add_transition(transition, witness_id, witness_ord, bundle_id) .map_err(StateError::WriteProvider)?; } - // Otherwise consignment includes state extensions which are not - // used in transaction graph. This must not be the case for the - // validated consignments. } Ok(()) } - pub fn update_witnesses( + pub fn upsert_witness( &mut self, - resolver: impl ResolveWitness, - after_height: u32, - ) -> Result> { + witness_id: Txid, + witness_ord: WitnessOrd, + ) -> Result<(), StateError

> { + self.provider + .upsert_witness(witness_id, witness_ord) + .map_err(StateError::WriteProvider) + } + + pub fn update_bundle(&mut self, bundle_id: BundleId, valid: bool) -> Result<(), StateError

> { self.provider - .update_witnesses(resolver, after_height) + .update_bundle(bundle_id, valid) .map_err(StateError::WriteProvider) } } @@ -276,7 +240,9 @@ pub trait StateReadProvider { contract_id: ContractId, ) -> Result, Self::Error>; - fn is_valid_witness(&self, witness_id: XWitnessId) -> Result; + fn witnesses(&self) -> LargeOrdMap; + + fn invalid_bundles(&self) -> LargeOrdSet; } pub trait StateWriteProvider: StoreTransaction { @@ -295,20 +261,22 @@ pub trait StateWriteProvider: StoreTransaction { contract_id: ContractId, ) -> Result>, Self::Error>; - fn update_witnesses( + fn upsert_witness( &mut self, - resolver: impl ResolveWitness, - after_height: u32, - ) -> Result; + witness_id: Txid, + witness_ord: WitnessOrd, + ) -> Result<(), Self::Error>; + + fn update_bundle(&mut self, bundle_id: BundleId, valid: bool) -> Result<(), Self::Error>; } pub trait ContractStateRead: ContractStateAccess { fn contract_id(&self) -> ContractId; fn schema_id(&self) -> SchemaId; + fn witness_ord(&self, witness_id: Txid) -> Option; fn rights_all(&self) -> impl Iterator>; fn fungible_all(&self) -> impl Iterator>; fn data_all(&self) -> impl Iterator>; - fn attach_all(&self) -> impl Iterator>; } pub trait ContractStateWrite { @@ -319,14 +287,8 @@ pub trait ContractStateWrite { fn add_transition( &mut self, transition: &Transition, - witness_id: XWitnessId, - witness_ord: WitnessOrd, - ) -> Result<(), Self::Error>; - - fn add_extension( - &mut self, - extension: &Extension, - witness_id: XWitnessId, + witness_id: Txid, witness_ord: WitnessOrd, + bundle_id: BundleId, ) -> Result<(), Self::Error>; } diff --git a/src/persistence/stock.rs b/src/persistence/stock.rs index 3f2e1849..443c84fa 100644 --- a/src/persistence/stock.rs +++ b/src/persistence/stock.rs @@ -24,44 +24,39 @@ use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::convert::Infallible; use std::error::Error; use std::fmt::Debug; +use std::num::NonZeroU32; -use amplify::confinement::{Confined, U24}; -use amplify::Wrapper; -use bp::dbc::{Anchor, Method}; -use bp::seals::txout::CloseMethod; -use bp::Vout; -use chrono::Utc; -use invoice::{Amount, Beneficiary, InvoiceState, NonFungible, RgbInvoice}; +use amplify::confinement::{Confined, LargeOrdSet}; +use bp::dbc::{Anchor, Proof}; +use bp::{Outpoint, Txid}; use nonasync::persistence::{CloneNoPersistence, PersistenceError, PersistenceProvider}; -use rgb::validation::{DbcProof, ResolveWitness, WitnessResolverError}; +use rgb::validation::{ResolveWitness, UnsafeHistoryMap, WitnessResolverError}; +use rgb::vm::WitnessOrd; use rgb::{ - validation, AssignmentType, BlindingFactor, BundleId, ContractId, DataState, GraphSeal, - Identity, OpId, Operation, Opout, SchemaId, SecretSeal, Transition, TxoSeal, XChain, XOutpoint, - XOutputSeal, XWitnessId, + validation, AssignmentType, BundleId, ChainNet, ContractId, GraphSeal, Identity, OpId, + Operation, Opout, OutputSeal, Schema, SchemaId, SecretSeal, Transition, TransitionType, + UnrelatedTransition, }; -use strict_encoding::FieldName; +use strict_types::FieldName; use super::{ ContractStateRead, Index, IndexError, IndexInconsistency, IndexProvider, IndexReadProvider, - IndexWriteProvider, MemIndex, MemStash, MemState, PersistedState, SchemaIfaces, Stash, - StashDataError, StashError, StashInconsistency, StashProvider, StashReadProvider, - StashWriteProvider, State, StateError, StateInconsistency, StateProvider, StateReadProvider, - StateWriteProvider, StoreTransaction, + IndexWriteProvider, MemIndex, MemStash, MemState, Stash, StashDataError, StashError, + StashInconsistency, StashProvider, StashReadProvider, StashWriteProvider, State, StateError, + StateInconsistency, StateProvider, StateReadProvider, StateWriteProvider, StoreTransaction, }; use crate::containers::{ - AnchorSet, Batch, BuilderSeal, Consignment, ContainerVer, ContentId, ContentRef, Contract, - Fascia, Kit, SealWitness, SupplItem, SupplSub, Transfer, TransitionDichotomy, TransitionInfo, - TransitionInfoError, ValidConsignment, ValidContract, ValidKit, ValidTransfer, VelocityHint, - WitnessBundle, SUPPL_ANNOT_VELOCITY, + Consignment, ContainerVer, Contract, Fascia, Kit, SecretSeals, Transfer, TransitionInfoError, + ValidConsignment, ValidContract, ValidKit, ValidTransfer, WitnessBundle, }; -use crate::info::{ContractInfo, IfaceInfo, SchemaInfo}; -use crate::interface::{ - BuilderError, ContractBuilder, ContractIface, Iface, IfaceClass, IfaceId, IfaceRef, - IfaceWrapper, TransitionBuilder, +use crate::contract::{ + AllocatedState, BuilderError, ContractBuilder, ContractData, IssuerWrapper, SchemaWrapper, + TransitionBuilder, }; -use crate::{BundleExt, MergeRevealError, RevealError}; +use crate::info::{ContractInfo, SchemaInfo}; +use crate::MergeRevealError; -pub type ContractAssignments = HashMap>; +pub type ContractAssignments = HashMap>; #[derive(Debug, Display, Error, From)] #[display(inner)] @@ -107,8 +102,12 @@ pub enum StockError< #[from] StashData(StashDataError), + /// valid (non-archived) witness is absent in the list of witnesses for a + /// state transition bundle. + AbsentValidWitness, + /// witness {0} can't be resolved: {1} - WitnessUnresolved(XWitnessId, WitnessResolverError), + WitnessUnresolved(Txid, WitnessResolverError), } impl From> @@ -133,6 +132,7 @@ impl From Self::StateWrite(err), StateError::Inconsistency(e) => Self::StateInconsistency(e), StateError::Resolver(id, e) => Self::WitnessUnresolved(id, e), + StateError::AbsentValidWitness => Self::AbsentValidWitness, } } } @@ -151,14 +151,12 @@ impl From From Self { Self::InvalidInput(err.into()) } } -impl From +impl From for StockError { - fn from(err: RevealError) -> Self { Self::InvalidInput(err.into()) } + fn from(err: UnrelatedTransition) -> Self { Self::InvalidInput(err.into()) } } #[derive(Clone, PartialEq, Eq, Debug, Display, Error, From)] #[display(doc_comments)] pub enum ComposeError { - /// no outputs available to store state of type {1} with velocity class - /// '{0}'. - NoBlankOrChange(VelocityHint, AssignmentType), + /// no outputs available to store state of type {0} + NoExtraOrChange(AssignmentType), /// the provided PSBT doesn't pay any sats to the RGB beneficiary address. NoBeneficiaryOutput, @@ -212,9 +209,6 @@ pub enum ComposeError { /// the invoice contains no contract information. NoContract, - /// the invoice contains no interface information. - NoIface, - /// the invoice requirements can't be fulfilled using available assets or /// smart contract state. InsufficientState, @@ -227,9 +221,9 @@ pub enum ComposeError { #[display(inner)] Transition(TransitionInfoError), - /// the operation produces too many blank state transitions which can't fit + /// the operation produces too many extra state transitions which can't fit /// the container requirements. - TooManyBlanks, + TooManyExtras, #[from] #[display(inner)] @@ -261,20 +255,6 @@ impl From fn from(err: FasciaError) -> Self { Self::InvalidInput(err) } } -#[derive(Clone, PartialEq, Eq, Debug, Display, Error, From)] -#[display(doc_comments)] -pub enum ContractIfaceError { - /// no known implementations of {0::<0} parent interfaces for - /// the schema {1::<0}. - NoAbstractImpl(IfaceId, SchemaId), -} - -impl From - for StockError -{ - fn from(err: ContractIfaceError) -> Self { Self::InvalidInput(err) } -} - #[derive(Clone, PartialEq, Eq, Debug, Display, Error, From)] #[display(inner)] pub enum InputError { @@ -284,8 +264,6 @@ pub enum InputError { Consign(ConsignError), #[from] Fascia(FasciaError), - #[from] - ContractIface(ContractIfaceError), } macro_rules! stock_err_conv { @@ -303,6 +281,7 @@ macro_rules! stock_err_conv { StockError::IndexWrite(e) => StockError::IndexWrite(e), StockError::StateRead(e) => StockError::StateRead(e), StockError::StateWrite(e) => StockError::StateWrite(e), + StockError::AbsentValidWitness => StockError::AbsentValidWitness, StockError::StashData(e) => StockError::StashData(e), StockError::StashInconsistency(e) => StockError::StashInconsistency(e), StockError::StateInconsistency(e) => StockError::StateInconsistency(e), @@ -326,19 +305,14 @@ impl From for ConsignError { impl From for FasciaError { fn from(_: Infallible) -> Self { unreachable!() } } -impl From for ContractIfaceError { - fn from(_: Infallible) -> Self { unreachable!() } -} stock_err_conv!(Infallible, ComposeError); stock_err_conv!(Infallible, ConsignError); stock_err_conv!(Infallible, FasciaError); -stock_err_conv!(Infallible, ContractIfaceError); stock_err_conv!(Infallible, InputError); stock_err_conv!(ComposeError, InputError); stock_err_conv!(ConsignError, InputError); stock_err_conv!(FasciaError, InputError); -stock_err_conv!(ContractIfaceError, InputError); pub type StockErrorMem = StockError; pub type StockErrorAll = StockError; @@ -457,28 +431,10 @@ impl Stock { #[doc(hidden)] pub fn as_index_provider_mut(&mut self) -> &mut P { self.index.as_provider_mut() } - pub fn ifaces(&self) -> Result + '_, StockError> { - let names = self - .stash - .ifaces()? - .map(|iface| (iface.iface_id(), iface.name.clone())) - .collect::>(); - Ok(self.stash.ifaces()?.map(move |iface| { - let suppl = self - .stash - .supplement(ContentRef::Iface(iface.iface_id())) - .ok() - .flatten(); - IfaceInfo::new(iface, &names, suppl) - })) - } - pub fn iface(&self, iface: impl Into) -> Result<&Iface, StockError> { - Ok(self.stash.iface(iface)?) - } pub fn schemata(&self) -> Result + '_, StockError> { Ok(self.stash.schemata()?.map(SchemaInfo::with)) } - pub fn schema(&self, schema_id: SchemaId) -> Result<&SchemaIfaces, StockError> { + pub fn schema(&self, schema_id: SchemaId) -> Result<&Schema, StockError> { Ok(self.stash.schema(schema_id)?) } @@ -488,28 +444,11 @@ impl Stock { Ok(self.stash.geneses()?.map(ContractInfo::with)) } - #[allow(clippy::multiple_bound_locations)] - pub fn contracts_by<'a, C: IfaceClass + 'a>( - &'a self, - ) -> Result< - impl Iterator< - Item = > as IfaceWrapper>>::Info, - > + 'a, - StockError, - > { - Ok(self.stash.geneses_by::()?.filter_map(|genesis| { - self.contract_iface_class::(genesis.contract_id()) - .as_ref() - .map(> as IfaceWrapper>>::info) - .ok() - })) - } - /// Iterates over ids of all contract assigning state to the provided set of /// output seals. pub fn contracts_assigning( &self, - outputs: impl IntoIterator>, + outputs: impl IntoIterator>, ) -> Result + '_, StockError> { let outputs = outputs .into_iter() @@ -522,12 +461,18 @@ impl Stock { fn contract_raw( &self, contract_id: ContractId, - ) -> Result<(&SchemaIfaces, H::ContractRead<'_>, ContractInfo), StockError> { + ) -> Result<(&Schema, H::ContractRead<'_>, ContractInfo), StockError> { let state = self.state.contract_state(contract_id)?; let schema_id = state.schema_id(); - let schema_ifaces = self.stash.schema(schema_id)?; - let info = ContractInfo::with(self.stash.genesis(contract_id)?); - Ok((schema_ifaces, state, info)) + let schema = self.stash.schema(schema_id)?; + Ok((schema, state, self.contract_info(contract_id)?)) + } + + pub fn contract_info( + &self, + contract_id: ContractId, + ) -> Result> { + Ok(ContractInfo::with(self.stash.genesis(contract_id)?)) } pub fn contract_state( @@ -539,46 +484,26 @@ impl Stock { .map_err(StockError::from) } - #[allow(clippy::multiple_bound_locations, clippy::type_complexity)] - pub fn contract_iface_class( + pub fn contract_wrapper( &self, contract_id: ContractId, - ) -> Result>, StockError> { - let (schema_ifaces, state, info) = self.contract_raw(contract_id)?; - let iimpl = self.stash.impl_for::(schema_ifaces)?; - - let iface = self.stash.iface(iimpl.iface_id)?; - let (types, _) = self.stash.extract(&schema_ifaces.schema, [iface])?; - - Ok(C::Wrapper::with(ContractIface { - state, - schema: schema_ifaces.schema.clone(), - iface: iimpl.clone(), - types, - info, - })) + ) -> Result>, StockError> { + let contract_data = self.contract_data(contract_id)?; + Ok(C::Wrapper::with(contract_data)) } - /// Returns the best matching abstract interface to a contract. - pub fn contract_iface( + /// Returns the contract data for the given contract ID + pub fn contract_data( &self, contract_id: ContractId, - iface: impl Into, - ) -> Result>, StockError> { - let (schema_ifaces, state, info) = self.contract_raw(contract_id)?; - let iface = self.stash.iface(iface)?; - let iface_id = iface.iface_id(); - - let iimpl = iface.find_abstractable_impl(schema_ifaces).ok_or_else(|| { - ContractIfaceError::NoAbstractImpl(iface_id, schema_ifaces.schema.schema_id()) - })?; + ) -> Result>, StockError> { + let (schema, state, info) = self.contract_raw(contract_id)?; - let (types, _) = self.stash.extract(&schema_ifaces.schema, [iface])?; + let (types, _) = self.stash.extract(schema)?; - Ok(ContractIface { + Ok(ContractData { state, - schema: schema_ifaces.schema.clone(), - iface: iimpl.clone(), + schema: schema.clone(), types, info, }) @@ -587,55 +512,39 @@ impl Stock { pub fn contract_assignments_for( &self, contract_id: ContractId, - outpoints: impl IntoIterator>, + outpoints: impl IntoIterator>, ) -> Result> { - let outputs: BTreeSet = outpoints.into_iter().map(|o| o.into()).collect(); + let outputs: BTreeSet = outpoints.into_iter().map(|o| o.into()).collect(); let state = self.contract_state(contract_id)?; let mut res = - HashMap::>::with_capacity(outputs.len()); + HashMap::>::with_capacity(outputs.len()); for item in state.fungible_all() { let outpoint = item.seal.into(); - if outputs.contains::(&outpoint) { - res.entry(item.seal).or_default().insert( - item.opout, - PersistedState::Amount( - item.state.value.into(), - item.state.blinding, - item.state.tag, - ), - ); + if outputs.contains::(&outpoint) { + res.entry(item.seal) + .or_default() + .insert(item.opout, AllocatedState::Amount(item.state)); } } for item in state.data_all() { let outpoint = item.seal.into(); - if outputs.contains::(&outpoint) { - res.entry(item.seal).or_default().insert( - item.opout, - PersistedState::Data(item.state.value.clone(), item.state.salt), - ); + if outputs.contains::(&outpoint) { + res.entry(item.seal) + .or_default() + .insert(item.opout, AllocatedState::Data(item.state.clone())); } } for item in state.rights_all() { let outpoint = item.seal.into(); - if outputs.contains::(&outpoint) { + if outputs.contains::(&outpoint) { res.entry(item.seal) .or_default() - .insert(item.opout, PersistedState::Void); - } - } - - for item in state.attach_all() { - let outpoint = item.seal.into(); - if outputs.contains::(&outpoint) { - res.entry(item.seal).or_default().insert( - item.opout, - PersistedState::Attachment(item.state.clone().into(), item.state.salt), - ); + .insert(item.opout, AllocatedState::Void); } } @@ -646,46 +555,38 @@ impl Stock { &self, issuer: impl Into, schema_id: SchemaId, - iface: impl Into, + chain_net: ChainNet, ) -> Result> { Ok(self .stash - .contract_builder(issuer.into(), schema_id, iface)?) + .contract_builder(issuer.into(), schema_id, chain_net)?) } pub fn transition_builder( &self, contract_id: ContractId, - iface: impl Into, - transition_name: Option>, + transition_name: impl Into, ) -> Result> { Ok(self .stash - .transition_builder(contract_id, iface, transition_name)?) + .transition_builder(contract_id, transition_name)?) } - pub fn blank_builder( + pub fn transition_builder_raw( &self, contract_id: ContractId, - iface: impl Into, + transition_type: TransitionType, ) -> Result> { - Ok(self.stash.blank_builder(contract_id, iface)?) + Ok(self + .stash + .transition_builder_raw(contract_id, transition_type)?) } pub fn export_schema(&self, schema_id: SchemaId) -> Result> { let mut kit = Kit::default(); - let schema_ifaces = self.schema(schema_id)?; - kit.schemata - .push(schema_ifaces.schema.clone()) - .expect("single item"); - for name in schema_ifaces.iimpls.keys() { - let iface = self.stash.iface(name.clone())?; - kit.ifaces.push(iface.clone()).expect("type guarantees"); - } - kit.iimpls - .extend(schema_ifaces.iimpls.values().cloned()) - .expect("type guarantees"); - let (types, scripts) = self.stash.extract(&schema_ifaces.schema, &kit.ifaces)?; + let schema = self.schema(schema_id)?; + kit.schemata.push(schema.clone()).expect("single item"); + let (types, scripts) = self.stash.extract(schema)?; kit.scripts .extend(scripts.into_values()) .expect("type guarantees"); @@ -697,42 +598,31 @@ impl Stock { &self, contract_id: ContractId, ) -> Result> { - let consignment = self.consign::(contract_id, [], None)?; + let consignment = self.consign::(contract_id, [], vec![], None)?; Ok(consignment) } pub fn transfer( &self, contract_id: ContractId, - outputs: impl AsRef<[XOutputSeal]>, - secret_seal: Option>, + outputs: impl AsRef<[OutputSeal]>, + secret_seals: impl AsRef<[SecretSeal]>, + witness_id: Option, ) -> Result> { - let consignment = self.consign(contract_id, outputs, secret_seal)?; + let consignment = self.consign(contract_id, outputs, secret_seals, witness_id)?; Ok(consignment) } fn consign( &self, contract_id: ContractId, - outputs: impl AsRef<[XOutputSeal]>, - secret_seal: Option>, + outputs: impl AsRef<[OutputSeal]>, + secret_seals: impl AsRef<[SecretSeal]>, + witness_id: Option, ) -> Result, StockError> { let outputs = outputs.as_ref(); + let secret_seals = secret_seals.as_ref(); - // Initialize supplements with btree set - let mut supplements = bset![]; - // Initialize signatures with btree map - let mut signatures = bmap! {}; - // Get genesis signature by contract id - self.stash - .sigs_for(&ContentId::Genesis(contract_id))? - .map(|genesis_sig| { - signatures.insert(ContentId::Genesis(contract_id), genesis_sig.clone()) - }); - // Get genesis supplement by contract id - self.stash - .supplement(ContentRef::Genesis(contract_id))? - .map(|genesis_suppl| supplements.insert(genesis_suppl.clone())); // 1. Collect initial set of anchored bundles // 1.1. Get all public outputs let mut opouts = self.index.public_opouts(contract_id)?; @@ -742,52 +632,71 @@ impl Stock { self.index .opouts_by_outputs(contract_id, outputs.iter().copied())?, ); - opouts.extend(self.index.opouts_by_terminals(secret_seal.into_iter())?); + opouts.extend( + self.index + .opouts_by_terminals(secret_seals.iter().copied())?, + ); // 1.3. Collect all state transitions assigning state to the provided outpoints - let mut witness_bundles = BTreeMap::::new(); + let mut bundles = BTreeMap::::new(); let mut transitions = BTreeMap::::new(); - let mut terminals = BTreeMap::>::new(); + let mut bundle_sec_seals: BTreeMap> = BTreeMap::new(); for opout in opouts { if opout.op == contract_id { continue; // we skip genesis since it will be present anywhere } let transition = self.transition(opout.op)?; - transitions.insert(opout.op, transition.clone()); let bundle_id = self.index.bundle_id_for_op(transition.id())?; - // 2. Collect secret seals from terminal transitions to add to the consignment terminals + + // skip bundles not associated to the terminals witness + if let Some(witness_id) = witness_id { + let (mut witness_ids, _) = self.index.bundle_info(bundle_id)?; + if !witness_ids.any(|w| w == witness_id) { + continue; + } + } + + transitions.insert(opout.op, transition.clone()); + + // 1.4. Collect secret seals for this bundle to add to the consignment terminals for typed_assignments in transition.assignments.values() { for index in 0..typed_assignments.len_u16() { let seal = typed_assignments.to_confidential_seals()[index as usize]; - if secret_seal == Some(seal) { - let res = terminals.insert(bundle_id, seal); - assert_eq!(res, None); + if secret_seals.contains(&seal) { + bundle_sec_seals.entry(bundle_id).or_default().insert(seal); } } } - if let Entry::Vacant(entry) = witness_bundles.entry(bundle_id) { - let bw = self.witness_bundle(bundle_id)?; - entry.insert(bw); + if let Entry::Vacant(entry) = bundles.entry(bundle_id) { + entry.insert(self.witness_bundle(bundle_id)?); } } + let is_asset_replacement = + |tt: TransitionType, at: AssignmentType| -> bool { tt.is_replace() && at.is_asset() }; + // 2. Collect all state transitions between terminals and genesis let mut ids = vec![]; for transition in transitions.values() { - ids.extend(transition.inputs().iter().map(|input| input.prev_out.op)); + ids.extend(transition.inputs().iter().map(|input| { + (input.op, is_asset_replacement(transition.transition_type, input.ty)) + })); } - while let Some(id) = ids.pop() { + while let Some((id, asset_replacement)) = ids.pop() { if id == contract_id { continue; // we skip genesis since it will be present anywhere } let transition = self.transition(id)?; - ids.extend(transition.inputs().iter().map(|input| input.prev_out.op)); - transitions.insert(id, transition.clone()); + if !asset_replacement { + ids.extend(transition.inputs().iter().map(|input| { + (input.op, is_asset_replacement(transition.transition_type, input.ty)) + })); + } let bundle_id = self.index.bundle_id_for_op(transition.id())?; - witness_bundles + bundles .entry(bundle_id) .or_insert(self.witness_bundle(bundle_id)?.clone()) .bundle @@ -795,434 +704,41 @@ impl Stock { } let genesis = self.stash.genesis(contract_id)?.clone(); - // Get schema signature by schema id - self.stash - .sigs_for(&ContentId::Schema(genesis.schema_id))? - .map(|schema_signature| { - signatures.insert(ContentId::Schema(genesis.schema_id), schema_signature.clone()) - }); - // Get schema supplement by schema id - self.stash - .supplement(ContentRef::Schema(genesis.schema_id))? - .map(|schema_suppl| supplements.insert(schema_suppl.clone())); - - let schema_ifaces = self.stash.schema(genesis.schema_id)?.clone(); - let mut ifaces = BTreeMap::new(); - for (iface_id, iimpl) in schema_ifaces.iimpls { - let iface = self.stash.iface(iface_id)?; - // Get iface and iimpl signatures by iface id and iimpl id - self.stash - .sigs_for(&ContentId::Iface(iface.iface_id()))? - .map(|iface_signature| { - signatures.insert(ContentId::Iface(iface.iface_id()), iface_signature.clone()) - }); - self.stash - .sigs_for(&ContentId::IfaceImpl(iimpl.impl_id()))? - .map(|iimpl_signature| { - signatures - .insert(ContentId::IfaceImpl(iimpl.impl_id()), iimpl_signature.clone()) - }); - // Get iface and iimpl supplement by iface id and iimpl id - self.stash - .supplement(ContentRef::Iface(iface.iface_id()))? - .map(|iface_suppl| supplements.insert(iface_suppl.clone())); - - self.stash - .supplement(ContentRef::IfaceImpl(iimpl.impl_id()))? - .map(|iimpl_suppl| supplements.insert(iimpl_suppl.clone())); - - ifaces.insert(iface.clone(), iimpl); - } - let ifaces = Confined::from_checked(ifaces); - - let mut bundles = BTreeMap::::new(); - for witness_bundle in witness_bundles.into_values() { - let witness_id = witness_bundle.witness_id(); - match bundles.get_mut(&witness_id) { - Some(prev) => { - *prev = prev.clone().merge_reveal(witness_bundle)?; - } - None => { - bundles.insert(witness_id, witness_bundle); - } - } - } + + let schema = self.stash.schema(genesis.schema_id)?.clone(); + let bundles = Confined::try_from_iter(bundles.into_values()) .map_err(|_| ConsignError::TooManyBundles)?; - let terminals = - Confined::try_from(terminals).map_err(|_| ConsignError::TooManyTerminals)?; + let terminals = Confined::try_from( + bundle_sec_seals + .into_iter() + .map(|(bundle_id, seals)| { + Confined::try_from(seals) + .map(|confined| (bundle_id, SecretSeals::from(confined))) + .map_err(|_| ConsignError::InvalidSecretSealsNumber) + }) + .collect::, _>>()?, + ) + .map_err(|_| ConsignError::TooManyTerminals)?; - let (types, scripts) = self.stash.extract(&schema_ifaces.schema, ifaces.keys())?; + let (types, scripts) = self.stash.extract(&schema)?; let scripts = Confined::from_iter_checked(scripts.into_values()); - let supplements = - Confined::try_from(supplements).map_err(|_| ConsignError::TooManySupplements)?; - let signatures = - Confined::try_from(signatures).map_err(|_| ConsignError::TooManySignatures)?; // TODO: Conceal everything we do not need - // TODO: Add known sigs to the consignment Ok(Consignment { - version: ContainerVer::V2, + version: ContainerVer::V0, transfer: TRANSFER, - schema: schema_ifaces.schema, - ifaces, + schema, genesis, terminals, bundles, - extensions: none!(), - attachments: none!(), - signatures, - supplements, types, scripts, }) } - /// Composes a batch of state transitions updating state for the provided - /// set of previous outputs, satisfying requirements of the invoice, paying - /// the change back and including the necessary blank state transitions. - #[allow(clippy::result_large_err)] - pub fn compose( - &self, - invoice: &RgbInvoice, - prev_outputs: impl IntoIterator>, - method: CloseMethod, - beneficiary_vout: Option>, - allocator: impl Fn(ContractId, AssignmentType, VelocityHint) -> Option, - ) -> Result> { - self.compose_deterministic( - invoice, - prev_outputs, - method, - beneficiary_vout, - u64::MAX, - allocator, - |_, _| BlindingFactor::random(), - |_, _| rand::random(), - ) - } - - /// Composes a batch of state transitions updating state for the provided - /// set of previous outputs, satisfying requirements of the invoice, paying - /// the change back and including the necessary blank state transitions. - #[allow(clippy::too_many_arguments, clippy::result_large_err)] - pub fn compose_deterministic( - &self, - invoice: &RgbInvoice, - prev_outputs: impl IntoIterator>, - method: CloseMethod, - beneficiary_vout: Option>, - priority: u64, - allocator: impl Fn(ContractId, AssignmentType, VelocityHint) -> Option, - pedersen_blinder: impl Fn(ContractId, AssignmentType) -> BlindingFactor, - seal_blinder: impl Fn(ContractId, AssignmentType) -> u64, - ) -> Result> { - let layer1 = invoice.layer1(); - let prev_outputs = prev_outputs - .into_iter() - .map(|o| o.into()) - .collect::>(); - - #[allow(clippy::type_complexity)] - let output_for_assignment = - |id: ContractId, - assignment_type: AssignmentType| - -> Result, StockError> { - let mut suppl = self.stash.supplements(ContentRef::Genesis(id))?; - let velocity = suppl - .next() - .and_then(|suppl| { - suppl - .get( - SupplSub::Assignment, - SupplItem::TypeNo(assignment_type.to_inner()), - SUPPL_ANNOT_VELOCITY, - ) - .transpose() - .ok() - .flatten() - }) - .unwrap_or_default(); - let vout = allocator(id, assignment_type, velocity) - .ok_or(ComposeError::NoBlankOrChange(velocity, assignment_type))?; - let seal = - GraphSeal::with_blinded_vout(method, vout, seal_blinder(id, assignment_type)); - Ok(BuilderSeal::Revealed(XChain::with(layer1, seal))) - }; - - // 1. Prepare the data - if let Some(expiry) = invoice.expiry { - if expiry < Utc::now().timestamp() { - return Err(ComposeError::InvoiceExpired.into()); - } - } - let contract_id = invoice.contract.ok_or(ComposeError::NoContract)?; - let iface = invoice.iface.as_ref().ok_or(ComposeError::NoIface)?; - let mut main_builder = - self.transition_builder(contract_id, iface.clone(), invoice.operation.clone())?; - let assignment_name = invoice - .assignment - .as_ref() - .or_else(|| main_builder.default_assignment().ok()) - .ok_or(BuilderError::NoDefaultAssignment)? - .clone(); - let assignment_id = main_builder - .assignments_type(&assignment_name) - .ok_or(BuilderError::InvalidStateField(assignment_name.clone()))?; - - // If there are inputs which are using different seal closing method from our - // wallet (and thus main state transition) we need to put them aside and - // allocate a different state transition spending them as a change. - let mut alt_builder = - self.transition_builder(contract_id, iface.clone(), invoice.operation.clone())?; - let mut alt_inputs = Vec::::new(); - - let layer1 = invoice.beneficiary.chain_network().layer1(); - let beneficiary = match (invoice.beneficiary.into_inner(), beneficiary_vout) { - (Beneficiary::BlindedSeal(seal), None) => { - BuilderSeal::Concealed(XChain::with(layer1, seal)) - } - (Beneficiary::BlindedSeal(_), Some(_)) => { - return Err(ComposeError::BeneficiaryVout.into()); - } - (Beneficiary::WitnessVout(payload), Some(vout)) => { - let blinding = seal_blinder(contract_id, assignment_id); - let seal = GraphSeal::with_blinded_vout(payload.method, vout, blinding); - BuilderSeal::Revealed(XChain::with(layer1, seal)) - } - (Beneficiary::WitnessVout(_), None) => { - return Err(ComposeError::NoBeneficiaryOutput.into()); - } - }; - - // 2. Prepare transition - let mut main_inputs = Vec::::new(); - let mut sum_inputs = Amount::ZERO; - let mut sum_alt = Amount::ZERO; - let mut data_inputs = vec![]; - let mut data_main = true; - let lookup_state = - if let InvoiceState::Data(NonFungible::RGB21(allocation)) = &invoice.owned_state { - Some(DataState::from(*allocation)) - } else { - None - }; - - for (output, list) in - self.contract_assignments_for(contract_id, prev_outputs.iter().copied())? - { - if output.method() == method { - main_inputs.push(output) - } else { - alt_inputs.push(output) - }; - for (opout, mut state) in list { - if output.method() == method { - main_builder = main_builder.add_input(opout, state.clone())?; - } else { - alt_builder = alt_builder.add_input(opout, state.clone())?; - } - if opout.ty != assignment_id { - let seal = output_for_assignment(contract_id, opout.ty)?; - state.update_blinding(pedersen_blinder(contract_id, assignment_id)); - if output.method() == method { - main_builder = main_builder.add_owned_state_raw(opout.ty, seal, state)?; - } else { - alt_builder = alt_builder.add_owned_state_raw(opout.ty, seal, state)?; - } - } else if let PersistedState::Amount(value, _, _) = state { - sum_inputs += value; - if output.method() != method { - sum_alt += value; - } - } else if let PersistedState::Data(value, _) = state { - if lookup_state.as_ref() == Some(&value) && output.method() != method { - data_main = false; - } - data_inputs.push(value); - } - } - } - // Add payments to beneficiary and change - match invoice.owned_state.clone() { - InvoiceState::Amount(amt) => { - // Pay beneficiary - if sum_inputs < amt { - return Err(ComposeError::InsufficientState.into()); - } - - let sum_main = sum_inputs - sum_alt; - let (paid_main, paid_alt) = - if sum_main < amt { (sum_main, amt - sum_main) } else { (amt, Amount::ZERO) }; - let blinding_beneficiary = pedersen_blinder(contract_id, assignment_id); - - if paid_main > Amount::ZERO { - main_builder = main_builder.add_fungible_state_raw( - assignment_id, - beneficiary, - paid_main, - blinding_beneficiary, - )?; - } - if paid_alt > Amount::ZERO { - alt_builder = alt_builder.add_fungible_state_raw( - assignment_id, - beneficiary, - paid_alt, - blinding_beneficiary, - )?; - } - - let blinding_change = pedersen_blinder(contract_id, assignment_id); - let change_seal = output_for_assignment(contract_id, assignment_id)?; - - // Pay change - if sum_main > paid_main { - main_builder = main_builder.add_fungible_state_raw( - assignment_id, - change_seal, - sum_main - paid_main, - blinding_change, - )?; - } - if sum_alt > paid_alt { - alt_builder = alt_builder.add_fungible_state_raw( - assignment_id, - change_seal, - sum_alt - paid_alt, - blinding_change, - )?; - } - } - InvoiceState::Data(data) => match data { - NonFungible::RGB21(allocation) => { - let lookup_state = DataState::from(allocation); - if !data_inputs.into_iter().any(|x| x == lookup_state) { - return Err(ComposeError::InsufficientState.into()); - } - - let seal = seal_blinder(contract_id, assignment_id); - if data_main { - main_builder = main_builder.add_data_raw( - assignment_id, - beneficiary, - allocation, - seal, - )?; - } else { - alt_builder = alt_builder.add_data_raw( - assignment_id, - beneficiary, - allocation, - seal, - )?; - } - } - }, - _ => { - todo!( - "only PersistedState::Amount and PersistedState::Allocation are currently \ - supported" - ) - } - } - - // 3. Prepare other transitions - // Enumerate state - let mut spent_state = - HashMap::>>::new(); - for id in self.contracts_assigning(prev_outputs.iter().copied())? { - // Skip current contract - if id == contract_id { - continue; - } - let state = self.contract_assignments_for(id, prev_outputs.iter().copied())?; - let entry = spent_state.entry(id).or_default(); - for (seal, assigns) in state { - entry.entry(seal).or_default().extend(assigns); - } - } - - // Construct blank transitions - let mut blanks = Confined::, 0, { U24 - 1 }>::with_capacity(spent_state.len()); - for (id, list) in spent_state { - let mut blank_builder_tapret = self.blank_builder(id, iface.clone())?; - let mut blank_builder_opret = self.blank_builder(id, iface.clone())?; - let mut outputs_tapret = Vec::with_capacity(list.len()); - let mut outputs_opret = Vec::with_capacity(list.len()); - for (output, assigns) in list { - match output.method() { - Method::TapretFirst => outputs_tapret.push(output), - Method::OpretFirst => outputs_opret.push(output), - } - for (opout, state) in assigns { - let seal = output_for_assignment(id, opout.ty)?; - match output.method() { - Method::TapretFirst => { - blank_builder_tapret = blank_builder_tapret - .add_input(opout, state.clone())? - .add_owned_state_raw(opout.ty, seal, state)? - } - Method::OpretFirst => { - blank_builder_opret = blank_builder_opret - .add_input(opout, state.clone())? - .add_owned_state_raw(opout.ty, seal, state)? - } - } - } - } - - let mut dicho = vec![]; - for (blank_builder, outputs) in - [(blank_builder_tapret, outputs_tapret), (blank_builder_opret, outputs_opret)] - { - if !blank_builder.has_inputs() { - continue; - } - let transition = blank_builder.complete_transition()?; - let info = TransitionInfo::new(transition, outputs).map_err(|e| { - debug_assert!(!matches!(e, TransitionInfoError::CloseMethodDivergence(_))); - ComposeError::TooManyInputs - })?; - dicho.push(info); - } - blanks - .push(TransitionDichotomy::from_iter(dicho)) - .map_err(|_| ComposeError::TooManyBlanks)?; - } - - let (first_builder, second_builder) = - match (main_builder.has_inputs(), alt_builder.has_inputs()) { - (true, true) => (main_builder, Some(alt_builder)), - (true, false) => (main_builder, None), - (false, true) => (alt_builder, None), - (false, false) => return Err(ComposeError::InsufficientState.into()), - }; - let first = TransitionInfo::new(first_builder.complete_transition()?, main_inputs) - .map_err(|e| { - debug_assert!(!matches!(e, TransitionInfoError::CloseMethodDivergence(_))); - ComposeError::TooManyInputs - })?; - let second = if let Some(second_builder) = second_builder { - Some(TransitionInfo::new(second_builder.complete_transition()?, alt_inputs).map_err( - |e| { - debug_assert!(!matches!(e, TransitionInfoError::CloseMethodDivergence(_))); - ComposeError::TooManyInputs - }, - )?) - } else { - None - }; - let mut batch = Batch { - main: TransitionDichotomy::with(first, second), - blanks, - }; - batch.set_priority(priority); - Ok(batch) - } - fn store_transaction( &mut self, f: impl FnOnce( @@ -1309,8 +825,7 @@ impl Stock { ) -> Result<(), StockError> { self.store_transaction(move |stash, state, index| { let witness_id = fascia.witness_id(); - stash - .consume_witness(SealWitness::new(fascia.witness.clone(), fascia.anchor.clone()))?; + stash.consume_witness(&fascia.seal_witness)?; for (contract_id, bundle) in fascia.into_bundles() { let ids1 = bundle @@ -1318,7 +833,11 @@ impl Stock { .keys() .copied() .collect::>(); - let ids2 = bundle.input_map.values().copied().collect::>(); + let ids2 = bundle + .input_map + .values() + .flat_map(|opids| opids.to_unconfined()) + .collect::>(); if !ids1.is_subset(&ids2) { return Err(FasciaError::InvalidBundle(contract_id, bundle.bundle_id()).into()); } @@ -1342,74 +861,318 @@ impl Stock { fn witness_bundle(&self, bundle_id: BundleId) -> Result> { let (witness_ids, contract_id) = self.index.bundle_info(bundle_id)?; - let bundle = self.stash.bundle(bundle_id)?.clone(); - let witness_id = self.state.select_valid_witness(witness_ids)?; + let (witness_id, _) = self.state.select_valid_witness(witness_ids)?; let witness = self.stash.witness(witness_id)?; - let (merkle_block, dbc) = match (bundle.close_method, &witness.anchors) { - ( - CloseMethod::TapretFirst, - AnchorSet::Tapret(tapret) | AnchorSet::Double { tapret, .. }, - ) => (&tapret.mpc_proof, DbcProof::Tapret(tapret.dbc_proof.clone())), - ( - CloseMethod::OpretFirst, - AnchorSet::Opret(opret) | AnchorSet::Double { opret, .. }, - ) => (&opret.mpc_proof, DbcProof::Opret(opret.dbc_proof)), - _ => { - return Err( - StashInconsistency::BundleMissedInAnchors(bundle_id, contract_id).into() - ); - } - }; - let Ok(mpc_proof) = merkle_block.to_merkle_proof(contract_id.into()) else { + let pub_witness = witness.public.clone(); + let Ok(mpc_proof) = witness.merkle_block.to_merkle_proof(contract_id.into()) else { return Err(StashInconsistency::WitnessMissesContract( witness_id, bundle_id, contract_id, - CloseMethod::OpretFirst, + witness.dbc_proof.method(), ) .into()); }; - let anchor = Anchor::new(mpc_proof, dbc); + let anchor = Anchor::new(mpc_proof, witness.dbc_proof.clone()); // TODO: Conceal all transitions except the one we need - Ok(WitnessBundle { - pub_witness: witness.public.clone(), - bundle, - anchor, - }) + Ok(WitnessBundle::with(pub_witness, anchor, bundle)) } - pub fn store_secret_seal( + pub fn store_secret_seal(&mut self, seal: GraphSeal) -> Result> { + Ok(self.stash.store_secret_seal(seal)?) + } + + fn set_bundles_as_invalid(&mut self, bundle_id: &BundleId) -> Result<(), StockError> { + // add bundle to set of invalid bundles + self.state.update_bundle(*bundle_id, false)?; + let bundle = self.stash.bundle(*bundle_id)?.clone(); + // recursively set all bundle descendants as invalid + for opid in bundle.known_transitions.keys() { + let children_bundle_ids = match self.index.bundle_ids_children_of_op(*opid) { + Ok(bundle_ids) => bundle_ids, + Err(IndexError::Inconsistency(IndexInconsistency::BundleAbsent(_))) => { + // this transition has no children yet + return Ok(()); + } + Err(e) => return Err(e.into()), + }; + + for child_bundle_id in children_bundle_ids { + self.set_bundles_as_invalid(&child_bundle_id)?; + } + } + Ok(()) + } + + fn maybe_update_bundles_as_valid( &mut self, - seal: XChain, + bundle_id: &BundleId, + invalid_bundles: &mut LargeOrdSet, + maybe_became_valid_bundle_ids: &mut BTreeSet, ) -> Result> { - Ok(self.stash.store_secret_seal(seal)?) + let bundle = self.stash.bundle(*bundle_id)?.clone(); + let mut valid = true; + // recursively visit bundle ancestors + for transition in bundle.known_transitions.values() { + for input in &transition.inputs { + let input_opid = input.op; + let input_bundle_id = match self.index.bundle_id_for_op(input_opid) { + Ok(id) => Some(id), + Err(IndexError::Inconsistency(IndexInconsistency::BundleAbsent(_))) => { + // reached genesis + None + } + Err(e) => return Err(e.into()), + }; + + if let Some(input_bundle_id) = input_bundle_id { + // process parent first if its status is also uncertain + if maybe_became_valid_bundle_ids.contains(&input_bundle_id) { + valid = self.maybe_update_bundles_as_valid( + &input_bundle_id, + invalid_bundles, + maybe_became_valid_bundle_ids, + )?; + // a single invalid parent is enough to consider the bundle as invalid + } else if invalid_bundles.contains(&input_bundle_id) { + valid = false; + break; + } + } + } + } + + // remove bundle since at this point we are sure about its status + maybe_became_valid_bundle_ids.remove(bundle_id); + + if valid { + // remove bundle from set of invalid bundles + self.state.update_bundle(*bundle_id, true)?; + invalid_bundles.remove(bundle_id).unwrap(); + // recursively visit bundle descendants to check if they became valid as well + for (opid, _transition) in bundle.known_transitions { + let children_bundle_ids = match self.index.bundle_ids_children_of_op(opid) { + Ok(bundle_ids) => bundle_ids, + Err(IndexError::Inconsistency(IndexInconsistency::BundleAbsent(_))) => { + // this transition has no children yet + small_bset![] + } + Err(e) => return Err(e.into()), + }; + for child_bundle_id in children_bundle_ids { + self.maybe_update_bundles_as_valid( + &child_bundle_id, + invalid_bundles, + maybe_became_valid_bundle_ids, + )?; + } + } + } + + Ok(valid) + } + + fn update_witness_ord( + &mut self, + resolver: impl ResolveWitness, + id: &Txid, + ord: &mut WitnessOrd, + became_invalid_witnesses: &mut BTreeMap>, + became_valid_witnesses: &mut BTreeMap>, + ) -> Result<(), StockError> { + let new = resolver + .resolve_pub_witness_ord(*id) + .map_err(|e| StockError::WitnessUnresolved(*id, e))?; + let changed = *ord != new; + if changed { + let bundle_valid = match (*ord, new) { + (WitnessOrd::Archived, _) => Some(true), + (_, WitnessOrd::Archived) => Some(false), + _ => None, + }; + // save witnesses that became valid or invalid + if let Some(valid) = bundle_valid { + let seal_witness = self.stash.witness(*id)?; + let bundle_ids: BTreeSet<_> = seal_witness.known_bundle_ids().collect(); + if valid { + became_valid_witnesses.insert(*id, bundle_ids); + } else { + became_invalid_witnesses.insert(*id, bundle_ids); + } + } + // save the changed witness ord + self.state.upsert_witness(*id, new)?; + *ord = new + } + Ok(()) } pub fn update_witnesses( &mut self, resolver: impl ResolveWitness, after_height: u32, + force_witnesses: Vec, ) -> Result> { - Ok(self.state.update_witnesses(resolver, after_height)?) + let after_height = NonZeroU32::new(after_height).unwrap_or(NonZeroU32::MIN); + let mut succeeded = 0; + let mut failed = map![]; + self.state.begin_transaction()?; + let witnesses = self.as_state_provider().witnesses(); + let mut witnesses = witnesses.release(); + let mut became_invalid_witnesses = bmap!(); + let mut became_valid_witnesses = bmap!(); + // 1. update witness ord of all witnesses + for (id, ord) in &mut witnesses { + if matches!(ord, WitnessOrd::Ignored) && !force_witnesses.contains(id) { + continue; + } + if matches!(ord, WitnessOrd::Mined(pos) if pos.height() < after_height) { + continue; + } + match self.update_witness_ord( + &resolver, + id, + ord, + &mut became_invalid_witnesses, + &mut became_valid_witnesses, + ) { + Ok(()) => { + succeeded += 1; + } + Err(err) => { + failed.insert(*id, err.to_string()); + } + } + } + + // 2. set invalidity of bundles + for bundle_ids in became_invalid_witnesses.values() { + for bundle_id in bundle_ids { + let bundle_witness_ids: BTreeSet = + self.index.bundle_info(*bundle_id)?.0.collect(); + // set bundle as invalid only if there are no valid witnesses associated to it + if bundle_witness_ids + .iter() + .all(|id| !witnesses.get(id).unwrap().is_valid()) + { + // set this bundle and all its descendants as invalid + self.set_bundles_as_invalid(bundle_id)?; + } + } + } + + // 3. set validity of bundles + let mut maybe_became_valid_bundle_ids = bset!(); + // get all bundles that became invalid and ones that were already invalid + let mut invalid_bundles_pre = self.as_state_provider().invalid_bundles(); + for bundle_ids in became_valid_witnesses.values() { + // store bundles that may become valid (to be sure its ancestors are checked) + maybe_became_valid_bundle_ids.extend(bundle_ids); + } + for bundle_ids in became_valid_witnesses.values() { + for bundle_id in bundle_ids { + // check if this bundle and its descendants are now valid + self.maybe_update_bundles_as_valid( + bundle_id, + &mut invalid_bundles_pre, + &mut maybe_became_valid_bundle_ids, + )?; + } + } + + self.state.commit_transaction()?; + Ok(UpdateRes { succeeded, failed }) + } + + fn _check_bundle_history( + &self, + bundle_id: &BundleId, + safe_height: NonZeroU32, + contract_history: &mut HashMap>>, + ) -> Result<(), StockError> { + let (bundle_witness_ids, contract_id) = self.index.bundle_info(*bundle_id)?; + let (witness_id, ord) = self.state.select_valid_witness(bundle_witness_ids)?; + match ord { + WitnessOrd::Mined(witness_pos) => { + let witness_height = witness_pos.height(); + if witness_height > safe_height { + contract_history + .entry(contract_id) + .or_default() + .entry(witness_height.into()) + .or_default() + .insert(witness_id); + } + } + WitnessOrd::Tentative | WitnessOrd::Ignored | WitnessOrd::Archived => { + contract_history + .entry(contract_id) + .or_default() + .entry(0) + .or_default() + .insert(witness_id); + } + } + + // recursively check bundle ancestors + let bundle = self.stash.bundle(*bundle_id)?.clone(); + for transition in bundle.known_transitions.values() { + for input in &transition.inputs { + let input_opid = input.op; + let input_bundle_id = match self.index.bundle_id_for_op(input_opid) { + Ok(id) => Some(id), + Err(IndexError::Inconsistency(IndexInconsistency::BundleAbsent(_))) => { + // reached genesis + None + } + Err(e) => return Err(e.into()), + }; + + if let Some(input_bundle_id) = input_bundle_id { + self._check_bundle_history(&input_bundle_id, safe_height, contract_history)?; + } + } + } + + Ok(()) + } + + pub fn get_outpoint_unsafe_history( + &self, + outpoint: Outpoint, + safe_height: NonZeroU32, + ) -> Result, StockError> { + let mut contract_history: HashMap>> = HashMap::new(); + + for id in self.contracts_assigning([outpoint])? { + let state = self.contract_assignments_for(id, [outpoint])?; + for opid in state + .iter() + .flat_map(|(_, assigns)| assigns.keys().map(|opout| opout.op)) + { + let bundle_id = self.index.bundle_id_for_op(opid)?; + self._check_bundle_history(&bundle_id, safe_height, &mut contract_history)?; + } + } + + Ok(contract_history) } } #[derive(Clone, Eq, PartialEq, Debug)] pub struct UpdateRes { pub succeeded: usize, - pub failed: HashMap, + pub failed: HashMap, } #[cfg(test)] mod test { - use std::str::FromStr; - use baid64::FromBaid64Str; + use bp::Vout; use commit_verify::{Conceal, DigestExt, Sha256}; - use strict_encoding::TypeName; use super::*; use crate::containers::ConsignmentExt; @@ -1417,18 +1180,15 @@ mod test { #[test] fn test_consign() { let mut stock = Stock::in_memory(); - let seal = XChain::with( - rgbcore::Layer1::Bitcoin, - GraphSeal::new_random_vout(bp::dbc::Method::OpretFirst, Vout::from_u32(0)), - ); + let seal = GraphSeal::new_random_vout(Vout::from_u32(0)); let secret_seal = seal.conceal(); stock.store_secret_seal(seal).unwrap(); let contract_id = ContractId::from_baid64_str("rgb:qFuT6DN8-9AuO95M-7R8R8Mc-AZvs7zG-obum1Va-BRnweKk") .unwrap(); - if let Ok(transfer) = stock.consign::(contract_id, [], Some(secret_seal)) { - println!("{:?}", transfer.supplements) + if let Ok(transfer) = stock.consign::(contract_id, [], vec![secret_seal], None) { + println!("{:?}", transfer) } } @@ -1453,45 +1213,15 @@ mod test { } } - #[test] - fn test_blank_builder_ifaceid() { - let stock = Stock::in_memory(); - let hasher = Sha256::default(); - let iface_id = IfaceId::from(hasher.clone()); - let bytes_hash = hasher.finish(); - let contract_id = ContractId::copy_from_slice(bytes_hash).unwrap(); - if let Ok(builder) = stock.blank_builder(contract_id, IfaceRef::Id(iface_id)) { - println!("{:?}", builder.transition_type()) - } - } - - #[test] - fn test_blank_builder_ifacename() { - let stock = Stock::in_memory(); - let hasher = Sha256::default(); - let bytes_hash = hasher.finish(); - let contract_id = ContractId::copy_from_slice(bytes_hash).unwrap(); - if let Ok(builder) = - stock.blank_builder(contract_id, IfaceRef::Name(TypeName::from_str("RGB20").unwrap())) - { - println!("{:?}", builder.transition_type()) - } - } - #[test] fn test_transition_builder() { let stock = Stock::in_memory(); let hasher = Sha256::default(); - let iface_id = IfaceId::from(hasher.clone()); let bytes_hash = hasher.finish(); let contract_id = ContractId::copy_from_slice(bytes_hash).unwrap(); - if let Ok(builder) = stock.transition_builder( - contract_id, - IfaceRef::Id(iface_id), - Some(FieldName::from_str("transfer").unwrap()), - ) { + if let Ok(builder) = stock.transition_builder(contract_id, "transfer") { println!("{:?}", builder.transition_type()) } } diff --git a/src/resolvers.rs b/src/resolvers.rs deleted file mode 100644 index 76dc5e7b..00000000 --- a/src/resolvers.rs +++ /dev/null @@ -1,54 +0,0 @@ -// RGB standard library for working with smart contracts on Bitcoin & Lightning -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2019-2024 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2019-2024 LNP/BP Standards Association. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use rgb::validation::{ResolveWitness, WitnessResolverError}; -use rgb::vm::{WitnessOrd, XWitnessId, XWitnessTx}; - -use crate::containers::IndexedConsignment; - -// TODO: Implement caching witness resolver - -pub(crate) struct ConsignmentResolver<'cons, R: ResolveWitness, const TRANSFER: bool> { - pub consignment: &'cons IndexedConsignment<'cons, TRANSFER>, - pub fallback: R, -} - -impl<'cons, R: ResolveWitness, const TRANSFER: bool> ResolveWitness - for ConsignmentResolver<'cons, R, TRANSFER> -{ - fn resolve_pub_witness( - &self, - witness_id: XWitnessId, - ) -> Result { - self.consignment - .pub_witness(witness_id) - .and_then(|p| p.map_ref(|pw| pw.tx().cloned()).transpose()) - .ok_or(WitnessResolverError::Unknown(witness_id)) - .or_else(|_| self.fallback.resolve_pub_witness(witness_id)) - } - - fn resolve_pub_witness_ord( - &self, - witness_id: XWitnessId, - ) -> Result { - self.fallback.resolve_pub_witness_ord(witness_id) - } -} diff --git a/src/stl/mod.rs b/src/stl/mod.rs index d2d84c82..b1fb70f2 100644 --- a/src/stl/mod.rs +++ b/src/stl/mod.rs @@ -31,13 +31,14 @@ use error::Error; pub use invoice::LIB_NAME_RGB_CONTRACT; pub use mime::{MediaRegName, MediaType}; pub use specs::{ - Article, AssetSpec, Attachment, BurnMeta, ContractSpec, ContractTerms, Details, IssueMeta, - Name, RicardianContract, Ticker, + Article, AssetSpec, Attachment, AttachmentName, AttachmentType, BurnMeta, ContractSpec, + ContractTerms, Details, EmbeddedMedia, IssueMeta, Name, OpidRejectUrl, RicardianContract, + Ticker, TokenData, }; pub use stl::{ - aluvm_stl, bp_core_stl, bp_tx_stl, commit_verify_stl, rgb_commit_stl, rgb_contract_stl, - rgb_logic_stl, rgb_std_stl, rgb_storage_stl, StandardTypes, LIB_ID_RGB_COMMIT, - LIB_ID_RGB_CONTRACT, LIB_ID_RGB_LOGIC, LIB_ID_RGB_STD, LIB_ID_RGB_STORAGE, + aluvm_stl, bp_consensus_stl, bp_core_stl, bp_tx_stl, commit_verify_stl, rgb_commit_stl, + rgb_contract_stl, rgb_logic_stl, rgb_std_stl, rgb_storage_stl, StandardTypes, + LIB_ID_RGB_COMMIT, LIB_ID_RGB_CONTRACT, LIB_ID_RGB_LOGIC, LIB_ID_RGB_STD, LIB_ID_RGB_STORAGE, }; pub const LIB_NAME_RGB_STD: &str = "RGBStd"; diff --git a/src/stl/specs.rs b/src/stl/specs.rs index f51afc5d..3db573f8 100644 --- a/src/stl/specs.rs +++ b/src/stl/specs.rs @@ -21,17 +21,21 @@ #![allow(unused_braces)] // caused by rustc unable to understand strict_dumb +use std::collections::BTreeMap; use std::fmt::{self, Debug, Formatter}; use std::hash::{Hash, Hasher}; use std::str::FromStr; -use amplify::confinement::{Confined, NonEmptyString, SmallOrdSet, SmallString, U8}; +use amplify::ascii::AsciiString; +use amplify::confinement::{ + Confined, NonEmptyString, NonEmptyVec, SmallBlob, SmallOrdSet, SmallString, U8, +}; use amplify::Bytes32; -use invoice::Precision; +use invoice::{Precision, TokenIndex}; use strict_encoding::stl::{Alpha, AlphaNum, AsciiPrintable}; use strict_encoding::{ InvalidRString, RString, StrictDeserialize, StrictDumb, StrictEncode, StrictSerialize, - StrictType, + StrictType, TypedWrite, }; use strict_types::StrictVal; @@ -408,3 +412,209 @@ impl ContractTerms { Self { text, media } } } + +#[derive(Clone, Eq, PartialEq, Hash, Debug)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_RGB_CONTRACT)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", rename_all = "camelCase") +)] +pub struct EmbeddedMedia { + #[strict_type(rename = "type")] + #[cfg_attr(feature = "serde", serde(rename = "type"))] + pub ty: MediaType, + pub data: SmallBlob, +} + +impl EmbeddedMedia { + pub fn from_strict_val_unchecked(value: &StrictVal) -> Self { + let ty = MediaType::from_strict_val_unchecked(value.unwrap_struct("type")); + let data = SmallBlob::from_iter_checked( + value.unwrap_struct("data").unwrap_bytes().iter().copied(), + ); + + Self { ty, data } + } +} + +#[derive(Clone, Eq, PartialEq, Hash, Debug)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_RGB_CONTRACT, dumb = { AttachmentType::with(0, "dumb") })] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", rename_all = "camelCase") +)] +pub struct AttachmentType { + pub id: u8, + pub name: AttachmentName, +} + +impl AttachmentType { + pub fn with(id: u8, name: &'static str) -> AttachmentType { + AttachmentType { + id, + name: AttachmentName::from(name), + } + } +} + +#[derive(Wrapper, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, From)] +#[wrapper(Deref, Display)] +#[derive(StrictType, StrictDumb, StrictDecode)] +#[strict_type(lib = LIB_NAME_RGB_CONTRACT, dumb = { AttachmentName::from("dumb") })] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", transparent) +)] +pub struct AttachmentName(Confined); +impl StrictEncode for AttachmentName { + fn strict_encode(&self, writer: W) -> std::io::Result { + let iter = self + .0 + .as_bytes() + .iter() + .map(|c| AsciiPrintable::try_from(*c).unwrap()); + writer + .write_newtype::(&NonEmptyVec::::try_from_iter(iter).unwrap()) + } +} + +// TODO: Ensure all constructors filter invalid characters +impl FromStr for AttachmentName { + type Err = InvalidRString; + + fn from_str(s: &str) -> Result { + let s = AsciiString::from_ascii(s.as_bytes())?; + let s = Confined::try_from_iter(s.chars())?; + Ok(Self(s)) + } +} + +impl From<&'static str> for AttachmentName { + fn from(s: &'static str) -> Self { Self::from_str(s).expect("invalid attachment name") } +} + +impl TryFrom for AttachmentName { + type Error = InvalidRString; + + fn try_from(name: String) -> Result { + let name = AsciiString::from_ascii(name.as_bytes())?; + let s = Confined::try_from(name)?; + Ok(Self(s)) + } +} + +impl Debug for AttachmentName { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_tuple("AttachmentName") + .field(&self.as_str()) + .finish() + } +} + +#[derive(Clone, Eq, PartialEq, Hash, Debug, Default)] +#[derive(StrictType, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_RGB_CONTRACT)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", rename_all = "camelCase") +)] +pub struct TokenData { + pub index: TokenIndex, + pub ticker: Option, + pub name: Option, + pub details: Option

, + pub preview: Option, + pub media: Option, + pub attachments: Confined, 0, 20>, + pub reserves: Option, +} + +impl StrictSerialize for TokenData {} +impl StrictDeserialize for TokenData {} + +impl TokenData { + pub fn from_strict_val_unchecked(value: &StrictVal) -> Self { + let index = TokenIndex::from( + value + .unwrap_struct("index") + .unwrap_num() + .unwrap_uint::(), + ); + let ticker = value + .unwrap_struct("ticker") + .unwrap_option() + .map(|x| Ticker::from_str(&x.unwrap_string()).expect("invalid uda ticker")); + + let name = value + .unwrap_struct("name") + .unwrap_option() + .map(|x| Name::from_str(&x.unwrap_string()).expect("invalid uda name")); + + let details = value + .unwrap_struct("details") + .unwrap_option() + .map(|x| Details::from_str(&x.unwrap_string()).expect("invalid uda details")); + + let preview = value + .unwrap_struct("preview") + .unwrap_option() + .map(EmbeddedMedia::from_strict_val_unchecked); + let media = value + .unwrap_struct("media") + .unwrap_option() + .map(Attachment::from_strict_val_unchecked); + + let attachments = if let StrictVal::Map(list) = value.unwrap_struct("attachments") { + Confined::from_iter_checked( + list.iter() + .map(|(k, v)| (k.unwrap_uint(), Attachment::from_strict_val_unchecked(v))), + ) + } else { + Confined::default() + }; + + let reserves = value + .unwrap_struct("reserves") + .unwrap_option() + .map(ProofOfReserves::from_strict_val_unchecked); + Self { + index, + ticker, + name, + details, + preview, + media, + attachments, + reserves, + } + } +} + +#[derive(Wrapper, Clone, Eq, PartialEq, Hash, From)] +#[wrapper(Deref, Display, FromStr)] +#[derive(StrictDumb, StrictType, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_RGB_CONTRACT)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", transparent) +)] +pub struct OpidRejectUrl(RString); + +impl StrictSerialize for OpidRejectUrl {} +impl StrictDeserialize for OpidRejectUrl {} + +impl_ident_type!(OpidRejectUrl); +impl_ident_subtype!(OpidRejectUrl); + +impl OpidRejectUrl { + pub fn from_strict_val_unchecked(value: &StrictVal) -> Self { + OpidRejectUrl::from_str(&value.unwrap_string()).unwrap() + } +} diff --git a/src/stl/stl.rs b/src/stl/stl.rs index 2aeb050b..cc8a5790 100644 --- a/src/stl/stl.rs +++ b/src/stl/stl.rs @@ -19,19 +19,20 @@ // See the License for the specific language governing permissions and // limitations under the License. -pub use bp::bc::stl::bp_tx_stl; +pub use bp::bc::stl::{bp_consensus_stl, bp_tx_stl}; pub use bp::stl::bp_core_stl; #[allow(unused_imports)] pub use commit_verify::stl::{commit_verify_stl, LIB_ID_COMMIT_VERIFY}; use invoice::{Allocation, Amount}; pub use rgb::stl::{aluvm_stl, rgb_commit_stl, rgb_logic_stl, LIB_ID_RGB_COMMIT, LIB_ID_RGB_LOGIC}; +use rgb::Schema; use strict_types::stl::{std_stl, strict_types_stl}; use strict_types::typesys::SystemBuilder; use strict_types::{CompileError, LibBuilder, SemId, SymbolicSys, TypeLib, TypeSystem}; use super::{ - AssetSpec, BurnMeta, ContractSpec, ContractTerms, Error, IssueMeta, MediaType, - LIB_NAME_RGB_CONTRACT, LIB_NAME_RGB_STORAGE, + AssetSpec, AttachmentType, BurnMeta, ContractSpec, ContractTerms, EmbeddedMedia, Error, + IssueMeta, MediaType, OpidRejectUrl, TokenData, LIB_NAME_RGB_CONTRACT, LIB_NAME_RGB_STORAGE, }; use crate::containers::{Contract, Kit, Transfer}; use crate::persistence::{MemIndex, MemStash, MemState}; @@ -41,39 +42,41 @@ use crate::LIB_NAME_RGB_STD; /// Strict types id for the library providing standard data types which may be /// used in RGB smart contracts. pub const LIB_ID_RGB_STORAGE: &str = - "stl:YBgLtVmT-FuzNBPe-1fxIqay-JW4Evd5-Za7fm9w-GLF6JAg#transit-xray-giraffe"; + "stl:zmXtAwkp-kSXdrCC-VwhgOsx-xaqYeeN-83jRn4_-iANSAwg#salon-explain-teacher"; /// Strict types id for the library providing standard data types which may be /// used in RGB smart contracts. pub const LIB_ID_RGB_CONTRACT: &str = - "stl:!r5yXt4a-v3XXv0M-E9Z6eoh-BFZweik-fxS6CB4-8AaO!MM#rover-annual-disney"; + "stl:1uyMC~lT-xPK57Lr-IgIhB0r-WxYd9io-2wZav_s-6TbR4LY#nuclear-liquid-sonic"; /// Strict types id for the library representing of RGB StdLib data types. pub const LIB_ID_RGB_STD: &str = - "stl:H1HLPfyC-5YLlAk!-KWhcUo1-0vtex!9-ODxSmIA-zj1J$qc#western-craft-bogart"; + "stl:Nn~mJpFH-NvnJbK4-KygN4WE-gZ2qLty-s_BnUXh-HhkGA6I#reverse-diana-mirage"; -fn _rgb_std_stl() -> Result { - LibBuilder::new(libname!(LIB_NAME_RGB_STD), tiny_bset! { +fn _rgb_std_stl() -> Result> { + // TODO: wait for fix in strict_types to use LibBuilder::with + #[allow(deprecated)] + Ok(LibBuilder::new(libname!(LIB_NAME_RGB_STD), [ std_stl().to_dependency(), strict_types_stl().to_dependency(), commit_verify_stl().to_dependency(), - bp_tx_stl().to_dependency(), + bp_consensus_stl().to_dependency(), bp_core_stl().to_dependency(), aluvm_stl().to_dependency(), rgb_commit_stl().to_dependency(), rgb_logic_stl().to_dependency(), - }) + ]) .transpile::() .transpile::() .transpile::() - .compile() + .compile()?) } -fn _rgb_contract_stl() -> Result { - LibBuilder::new(libname!(LIB_NAME_RGB_CONTRACT), tiny_bset! { - std_stl().to_dependency(), - bp_tx_stl().to_dependency() - }) +fn _rgb_contract_stl() -> Result> { + Ok(LibBuilder::with(libname!(LIB_NAME_RGB_CONTRACT), [ + std_stl().to_dependency_types(), + bp_consensus_stl().to_dependency_types(), + ]) .transpile::() .transpile::() .transpile::() @@ -83,11 +86,17 @@ fn _rgb_contract_stl() -> Result { .transpile::() .transpile::() .transpile::() - .compile() + .transpile::() + .transpile::() + .transpile::() + .transpile::() + .compile()?) } -fn _rgb_storage_stl() -> Result { - LibBuilder::new(libname!(LIB_NAME_RGB_STORAGE), tiny_bset! { +fn _rgb_storage_stl() -> Result> { + // TODO: wait for fix in strict_types to use LibBuilder::with + #[allow(deprecated)] + Ok(LibBuilder::new(libname!(LIB_NAME_RGB_STORAGE), [ std_stl().to_dependency(), strict_types_stl().to_dependency(), commit_verify_stl().to_dependency(), @@ -96,12 +105,12 @@ fn _rgb_storage_stl() -> Result { aluvm_stl().to_dependency(), rgb_commit_stl().to_dependency(), rgb_logic_stl().to_dependency(), - rgb_std_stl().to_dependency() - }) + rgb_std_stl().to_dependency(), + ]) .transpile::() .transpile::() .transpile::() - .compile() + .compile()?) } /// Generates strict type library representation of RGB StdLib data types. @@ -122,18 +131,9 @@ pub fn rgb_storage_stl() -> TypeLib { #[derive(Debug)] pub struct StandardTypes(SymbolicSys); -impl Default for StandardTypes { - fn default() -> Self { StandardTypes::new() } -} - impl StandardTypes { - pub fn new() -> Self { - Self::try_with([std_stl(), bp_tx_stl(), rgb_contract_stl()]) - .expect("error in standard RGBContract type system") - } - pub fn with(lib: TypeLib) -> Self { - Self::try_with([std_stl(), bp_tx_stl(), rgb_contract_stl(), lib]) + Self::try_with([std_stl(), bp_consensus_stl(), rgb_contract_stl(), lib]) .expect("error in standard RGBContract type system") } @@ -147,7 +147,9 @@ impl StandardTypes { Ok(Self(sys)) } - pub fn type_system(&self) -> TypeSystem { self.0.as_types().clone() } + pub fn type_system(&self, schema: Schema) -> TypeSystem { + self.0.as_types().extract(schema.types()).unwrap() + } pub fn get(&self, name: &'static str) -> SemId { *self.0.resolve(name).unwrap_or_else(|| { diff --git a/stl/Cargo.toml b/stl/Cargo.toml index 801bf9f5..caf699a0 100644 --- a/stl/Cargo.toml +++ b/stl/Cargo.toml @@ -20,4 +20,4 @@ path = "src/main.rs" amplify = { workspace = true } strict_types = { workspace = true } commit_verify = { workspace = true } -rgb-std = { version = "0.11.0-beta.4", path = ".." } +rgb-std = { version = "0.11.1-alpha.1", path = ".." } diff --git a/stl/IfaceStd.con b/stl/IfaceStd.con deleted file mode 100644 index 37380a20..00000000 --- a/stl/IfaceStd.con +++ /dev/null @@ -1,252 +0,0 @@ -@version(1) -@timestamp(1711405444) -interface NamedAsset - global spec: RGBContract.AssetSpec - global terms: RGBContract.AssetTerms - - genesis: abstract - globals: spec, terms - - -@version(1) -@timestamp(1711405444) -interface RenameableAsset - - public updateRight: Rights - - genesis: override - assigns: updateRight - - transition rename: required, final - globals: spec - assigns: updateRight(?) - default: updateRight - inputs: updateRight - - -@version(1) -@timestamp(1711405444) -interface FungibleAsset - global issuedSupply: RGBContract.Amount - - owned assetOwner(*): Zk64 - - error nonEqualAmounts - "the sum of spent assets doesn't equal to the sum of assets in outputs" - error supplyMismatch - "supply specified as a global parameter doesn't match the issued supply allocated to the asset owners" - - genesis: override - errors: insufficientReserves, invalidProof, supplyMismatch - globals: issuedSupply - assigns: assetOwner(*) - - transition transfer: required, default, abstract - errors: nonEqualAmounts - assigns: assetOwner(+) - default: assetOwner - inputs: assetOwner(+) - - -@version(1) -@timestamp(1711405444) -interface FixedAsset - - owned assetOwner(+): Zk64 - - genesis: override - errors: insufficientReserves, invalidProof, supplyMismatch - assigns: assetOwner(+) - - -@version(1) -@timestamp(1711405444) -interface BurnableAsset - global burnedSupply(*): RGBContract.Amount - - public burnRight(+): Rights - - error insufficientCoverage - "the claimed amount of burned assets is not covered by the assets in the operation inputs" - - genesis: override - assigns: burnRight(+) - - transition burn: required, final - errors: insufficientCoverage, invalidProof, supplyMismatch - meta: RGBContract.BurnMeta - globals: burnedSupply - assigns: burnRight(*) - inputs: burnRight - - -@version(1) -@timestamp(1711405444) -interface InflatableAsset - global issuedSupply(+): RGBContract.Amount - - public inflationAllowance(*): Zk64 - - error issueExceedsAllowance - "you try to issue more assets than allowed by the contract terms" - - genesis: override - assigns: inflationAllowance(+) - - transition issue: required, abstract - errors: issueExceedsAllowance, supplyMismatch - globals: issuedSupply - assigns: assetOwner(*), inflationAllowance(*) - default: assetOwner - inputs: inflationAllowance(+) - - -@version(1) -@timestamp(1711405444) -interface ReplaceableAsset - global burnedSupply(*): RGBContract.Amount - global replacedSupply(*): RGBContract.Amount - - public burnEpoch(+): Rights - public burnRight(*): Rights - - error insufficientCoverage - "the claimed amount of burned assets is not covered by the assets in the operation inputs" - - genesis: override - assigns: burnEpoch - - transition burn: required, final - errors: insufficientCoverage, invalidProof, supplyMismatch - meta: RGBContract.BurnMeta - globals: burnedSupply - assigns: burnRight(?) - inputs: burnRight - - transition openEpoch: required, final - assigns: burnEpoch(?), burnRight - default: burnRight - inputs: burnEpoch - - transition replace: required, final - errors: insufficientCoverage, invalidProof, nonEqualAmounts, supplyMismatch - meta: RGBContract.BurnMeta - globals: replacedSupply - assigns: assetOwner(*), burnRight(?) - default: assetOwner - inputs: burnRight - - -@version(1) -@timestamp(1711405444) -interface ReservableAsset - - error insufficientReserves - "reserve is insufficient to cover the issued assets" - error invalidProof - "the provided proof is invalid" - - genesis: override - errors: insufficientReserves, invalidProof - meta: RGBContract.IssueMeta - - transition issue: override - errors: insufficientReserves, invalidProof - meta: RGBContract.IssueMeta - default: assetOwner - inputs: - - -@version(1) -@timestamp(1711405444) -interface NonFungibleToken - global attachmentTypes(*): RGB21.AttachmentType - global tokens(*): RGB21.TokenData - - owned assetOwner(*): RGB21.Allocation - - error fractionOverflow - "the amount of fractional token in outputs exceeds 1" - error invalidAttachmentType - "attachment has a type which is not allowed for the token" - error nonEqualValues - "the sum of spent token fractions doesn't equal to the sum of token fractions in outputs" - error nonFractionalToken - "attempt to transfer a fraction of non-fractionable token" - - genesis: override - errors: fractionOverflow, invalidAttachmentType - globals: attachmentTypes(*), tokens(*) - assigns: assetOwner(*) - - transition transfer: required, default, final - errors: fractionOverflow, nonEqualValues, nonFractionalToken - assigns: assetOwner(+) - default: assetOwner - inputs: assetOwner(+) - - -@version(1) -@timestamp(1711405444) -interface EngravableNft - global engravings(*): RGB21.EngravingData - - error nonEngravableToken - "attempt to engrave on a token which prohibit engraving" - - genesis: override - - transition engrave: required, final - errors: fractionOverflow, nonEngravableToken, nonEqualValues, nonFractionalToken - globals: engravings - assigns: assetOwner(+) - default: assetOwner - inputs: assetOwner(+) - - -@version(1) -@timestamp(1711405444) -interface UniqueNft - global attachmentTypes: RGB21.AttachmentType - global tokens: RGB21.TokenData - - owned assetOwner(+): RGB21.Allocation - - genesis: override - globals: attachmentTypes, tokens - assigns: assetOwner(+) - - -@version(1) -@timestamp(1711405444) -interface LimitedNft - global attachmentTypes(+): RGB21.AttachmentType - global tokens(+): RGB21.TokenData - - owned assetOwner(+): RGB21.Allocation - - genesis: override - globals: attachmentTypes(+), tokens(+) - assigns: assetOwner(+) - - -@version(1) -@timestamp(1711405444) -interface IssuableNft - - public inflationAllowance(+): RGB21.ItemsCount - - error issueExceedsAllowance - "you try to issue more assets than allowed by the contract terms" - - genesis: override - assigns: inflationAllowance(+) - - transition issue: required, abstract - errors: fractionOverflow, insufficientReserves, invalidAttachmentType, invalidProof, issueExceedsAllowance - globals: attachmentTypes(*), tokens(*) - assigns: assetOwner(*), inflationAllowance(*) - default: assetOwner - inputs: inflationAllowance(+) - - diff --git a/stl/RGBContract@0.11.0.sta b/stl/RGBContract@0.11.0.sta index 50e247d3..26537f19 100644 --- a/stl/RGBContract@0.11.0.sta +++ b/stl/RGBContract@0.11.0.sta @@ -1,52 +1,66 @@ -----BEGIN STRICT TYPE LIB----- -Id: stl:!r5yXt4a-v3XXv0M-E9Z6eoh-BFZweik-fxS6CB4-8AaO!MM#rover-annual-disney +Id: stl:1uyMC~lT-xPK57Lr-IgIhB0r-WxYd9io-2wZav_s-6TbR4LY#nuclear-liquid-sonic Name: RGBContract Dependencies: - Std#ralph-blue-lucky, - Bitcoin#signal-color-cipher -Check-SHA256: 6347d5f0a4c36a074fd3b6abb98041f64798a4a233e1742a352edc330dddbf3d + Std#delete-roman-hair, + Bitcoin#quiz-patent-exit +Check-SHA256: cd08b8b1d553bb24f87554499a11ec5f7b9268f8ea1835e9243c54e2864ebb4a -3sOfyLvL<$a$#e10?I5NZ-bfLFbqC#o>4E?M+l67UG^w8*<_XZ#%uyqCj(P-Wc6$lVk7oBr%DNv+($;q -`HHK!gIHa)*%m(-e#9sm3I{@IbYpL6ZUP5FX>?<6X>J1mA>%$n#j0HLDJN5-IKgM_J7b(p+0MPGk2Gl) +3sOfyLvL<$a$#e10)mO_O%DrjRIhYP1?a)oog)LLTw}}6rDvG=`c^zKY6DYrWWh(oW+jcb^C>NgzlT_m +3wFmfDB@ZlUF_{Gi(YuS4F^JLbYpL6ZUP5FX>?<6X>J1mA>%$n#j0HLDJN5-IKgM_J7b(p+0MPGk2Gl) y2(Rz1Xgc#bfbbo^UK%K(4i9Ajp1M~R@C@!4#dQE#lUD;OiKi1Rs>XdX=LbXK+Rkw`Mu(V|7oQWGN(Z+ AyvH&RuaL#N1_Q*>km07$+g7b@t4MVjY>G@u4Q3HlB(d+LiLJm-R=h;`?dxDG*c V`*tna%paKVPb4$UtT8V#RWVYMMP0s#-L?ApehHE`!Nx1aisd$7U5G>2tjOcXkkuuZGt0!^mXvL0b>G|!UaKWaA;xq7YGF1t^|4b)vt7`JJJH?>OpeZskt`?6&l-r#0;SdL2Phn -VN-2kY-|(&3PEgaZ)0I}X>V=?0s&*k#ka7f`< -Z^7s3P_GJP!FFFM4E? -M+l67UG^w8*<_XZ#%uyqCxRn@^mXvL0b>G|!T<;Y$}AplgPGkh3_fq3 -Q7_j=2#kPT_9!;lWR>~GYywm#UtT8V#RWVYMMP0s#-L?ApehHE`!Nx1aisd$7U5G>00000000009{>OV +VN-2kY-||-3PEgaZ)0I}X>V=?0s&*k#ka7f`< +Z^7s3P_GJP!FFFML0b>G|!T<;Yf{E)*4-0TquXIZV +=)u>WBLk*fW6RH_XPEi=Ry;9kUtT8V#RWVYMMP0s#-L?ApehHE`!Nx1aisd$7U5G>00000000009{>OV 000002|;snWpq<;Wn%^e26Sm-Yh`i)TX!suv0v9m0LPL+_79KRH|I99{YEOV7tT#ZPWpkW1a4t%WdVR* #k^Az$U%@qU7>2Bz={du0O&G0&#r1CLJBFZ06hm}WprU_Y;ynv0ssVVZ*FA(00035b8l^B00jX7KPz&# #IG7-47St%2#c>Z5R>jkTb_MKDq#SE1Nsg8a>I`c#0nSFF19TD^=GS9xE uuG0V@n0eeL3DIsV`yzV!Z000000RR600000001QKKZgg^CV{}t+Wn%^e2Vrt_X=7|<00aU61a5C`WdHyG0R(ezZDjxj0ReXZ -@I5NQHT;&p7LREnfs~VQpmrfL_JCQxeEQkVIXfYN5c23F83hGCI$$Y9m4l -DXjoK2V`Y*VQFl000aU61a5C`WdHyG0R(ezZDjxj0RcZNa<{~<8ep?nYaleMc%`0D|O6 -*W?Lsa%E#_b7^mG0bK*c7mcZoem^?%L*to!bRZoO^d~aUzM`;8jz95VA`L@tZgg^CV{}wya&2=40t9qr -cys|6%am^tlg}6qop{{FTg974FaQ3n`}K{nn9PGH_DcZ;ZDnL>VE_aI00eGtZe;)f009JZZ*64&1pxs= -s(;1y^<+=wqOM*VsgOd=>xYy=<4kft^@4w~Gv0~^L}hegX>4-^0RRX90RaF2000000RI300000000~KR -b9H4+WprT%0SIzsb7gXNWpe-t0S?j{I~j%e^V!Z000000RR60 -0000001QoKWNBeiWoJ%dZDj=k00ja9$}AplgPGkh3_fq3Q7_j=2#kPT_9!;lWR>~GYywm#`4m;t2@#H=ITLm*{QiV2NfFIf5Z%-00;p*(W0Hqt(%d1CNN)#sHFQL7%(bMbH%I*cn#*O0A6zd00000 -0000#0000000009O=VnH0sEektM3d!V}qV`yP= -b7gb@1OfmAZf|a7000011aog~WdH>M0UWS(jugTGj1K^e=F-$2o*6gc%@3Ir#hQL8;m&)Yy9iBbZDm7f -VR8d41Z8+*Y#{__VRL9B24rt+Y+-UF17U4&CIoP7b#p5OWMOk?Edyk4bS?yXWpZyY18;6+F#~jWZ!!gR -XmVv`GX!RDb#gQWW@&b1H3M^Lcs2!dWp-t5Hw9&BXJ~Xd1a4_=WjO_7VRB`3UIuJ$WMOk?UjboZ0b*hS -V`BkiWC3Mm0cK_aXJ-LuXaQ+y0cvUiYij{)YyoX;0d8&qZ*Ku`Z~<{~0djHyb8`W7bOCjB0d{r)cXt7J -cma8N0eX4_PGN0j1pxpB0s_h`9&dx0-7pM3Z=O*v*GCA9fL-<|HrZsA`NnJlR3}KjBNr;@ghiU?gEXK9 -KMDE{F?;HZBRuDVqlk6qmbd^20?I5NZ-bfLFbqC#o>4E?M+l67UG^w8*<_XZ#%uyqCrG{{7b@t4MVjY> -G@u4Q3HlB(d+LiLJm-R=h;`?dxBvhE0000004D$d0000001Z!fZe?Ufa$#e1X>V=?0RR992~cunV`+0~ -Z*Bt<3u$g-X?AIIX<}?;00d-ZV`%{eV`Xl1X#xdpX>4q10|{hhV`)ukY;0)+3S(t%bZJd#Y;0)-1#M|# -a&HC+WMyM%O=)9tZwCrvWo~q7O=)9tZwLf#VQy~;2xMhrX-;8oZwd)xWo~q7PGN3u3j}a!V{Z%yWMyM% -P-$at4GCjqZggo-X=85=1!iS!bZ-v{WMyM%MrCbuZx9M&Wo~q7MrCbuZxIAxbaZbL4^VP%Z)Q(sQe|^x -a&~2N1_A_iba-z9^=uPjBlbC`N(qzPM@Gr{imSMTSY5T*7C#t%#3&jH=xRXCTqXIv;)MTcr4cfxK`S9u -y$)6q!N22#m0-mN1#oh2Z)N}p002M$0000000030{{R300000HQfXsha%5>?ZbNTwbaG*1bOiwb2mk>9 -0000000030{{R3000006RB2;tWpV`p00ja9$}AplgPGkh3_fq3Q7_j=2#kPT_9!;lWR>~GYywm#f+K+R -b@1)9wcJs8k=}EVt)knrbu3HWBLk*fW6RH_XPEi=Ry;9kNWLQ% +D(Hkon&*Qwpawq)`VKLB>Wd>h=Ype%b?2720RR91000006aWAK000004ncHuVPj}*Wo~p-d2nS00s?7d +00035ZeeX@0Zy@TnA*;6t215lmk3S~(t;6Y7Z=sJCn}C_bsn*7Z3seja&Ap!bYTVo3SxC~ZcuV>Z)S4< +2>}k$8ao+<`1M~J|HmdBRUd1sOY#QI#7O)0BZUJ2b#21aJj($Hn^F!mAeRLol +5%ecA&%UCtOO8MBUm^`dZ*FvQVPkYuWpZtE1_A_hWq5P}7|WDz5|hsu4xM=1##_akM=$^Wt^4(jv6#$* +DE3PM1#M+yX<+~a0ssVVZ*FA(00035b8l^B00jX7L#ltp_Vr{><)W@$8mW*%&FhDhLgP$wKlOrtu`}L^ +2SjCbVQFl01pxpE00992000000093000000000d|ZDM6)WMyPcWn^h#1_A_hd2nR`u?^n-fFP~dpvnj- +AyBKaJW)+{-ce}5$#DguerIN21Y}`!VE_mK06+i$000000096000000000R|b8~fNO=Wap1_20iWpib6 +c4cz_2>}k$8ao+<`1M~J|HmdBRUd1sOY#QI#7OJ{2V-bqa&u*L00aU61a5C`WdHyG +0R(ezZDjxj0RbGab&eFm0E`a+jONnR9-bLD;mr@36vdi;DdEn0pt}f7X>DagXkl^#Ed*tFV{9P=Xkl|` +BL-w|Y;0k2Bm-e>a3%zBY;|)h1Y}`zXe|R|Z*(pMdS!BNFavLHWibPEcW*KUbZByAWite3Z*_7s1ZHV= +Wi0dH>saBu-}aRG920dsQ!baVlAbpdvE0e5!+cz6MMc>#KQ1WsXXWd#8M +1p)$siR(=d3vg7gbV~*3!PlK51EySK%g?1}nECovJTYoWz9SbZ=!8X@=Yuq$20sb<4l#S`iz7Vef}@Ca +=a#qt2m*qM>rD>}a8$2!O9kk`*PSB+rd(so&!uOW`TABoF=|M@BNr;@ghiU?gEXK9KMDE{F?;HZBRuDV +qlk6qmbd@_00000001Wd00000000e7aA{;xWol((bX9U}1pxpB0s?}G>rD>}a8$2!O9kk`*PSB+rd(so +&!uOW`TABoF=|M@BNr;@ghiU?gEXK9KMDE{F?;HZBRuDVqlk6qmbd^20)mO_O%DrjRIhYP1?a)oog)LL +Tw}}6rDvG=`c^zKYDm5#7b@t4MVjY>G@u4Q3HlB(d+LiLJm-R=h;`?dxBvhE0000006!l90000001Z!f +Ze?Ufa$#e1X>V=?0RR992~cunV`+0~Z*Bt<3u$g-X?AIIX<}?;00d-ZV`%{eV`Xl1X#xdpX>4q10|{hh +V`)ukY;0)+3S(t%bZJd#Y;0)-1#M|#a&HC+WMyM%O=)9tZwCrvWo~q7O=)9tZwLf#VQy~;2xMhrX-;8o +Zwd)xWo~q7PGN3u3j}a!V{Z%yWMyM%P-$at4GCjqZggo-X=85=1!iS!bZ-v{WMyM%MrCbuZx9M&Wo~q7 +MrCbuZxIAxbaZbL4^VP%Z)Q(sQe|^xa&~2N1_A_iba-z9!AHbqC5^W8DJ_b>hggsccE>d+;#wbF?CmX! +UU<0;=xRXCTqXIv;)MTcr4cfxK`S9uy$)6q!N22#m0-mN1#oh2Z)N}p002M$0000000030{{R300000H +QfXsha%5>?ZbNTwbaG*1bOiwb2mk>90000000030{{R3000006RB2;tWpV`p00ja9f{E)*4-0TquXIZV +=)u>WBLk*fW6RH_XPEi=Ry;9kf+K+Rb@1)9wcJs8k=}EVt)knrbu3HMd?cmZVrRzu`SjPv&tGy!?nCFm&fz)So=%sVIc1y9;Ha~%eBX=7_; +asUJZ00eGtZe;)f009JZZ*64&1pxtDcPx&vU)M(f$C5$z50Bb6=QgwbMk=ru&P_#5`hlthZeeX@00aU6 +1a5C`WdHyG0R(ezZDjxj0Rezs#k^Az$U%@qU7>2Bz={du0O&G0&#r1CLJBFZ06hm}WprU_Y;ynv0ssVV +Z*FA(00035b8l^B00jX7KPz&##IG7-47St%2#c>Z5R>jkTb_MKDq#SE<)W@$8mW*%&FhDhLgP$wKlOrtu`}L^00000000006aWAK000002y$g}WpZ|9a{vSa00eGtZe;)f +009JZZ*64&1pxsL(i%G%h4}Sf8vn;89aSG|t4s0*&BRFk%a^yrND0jfRBvl#Zb@!rWq1Vv000C -----END STRICT TYPE LIB----- diff --git a/stl/RGBContract@0.11.0.stl b/stl/RGBContract@0.11.0.stl index e8872884..f07eafa0 100644 Binary files a/stl/RGBContract@0.11.0.stl and b/stl/RGBContract@0.11.0.stl differ diff --git a/stl/RGBContract@0.11.0.sty b/stl/RGBContract@0.11.0.sty index 54324362..800a17ec 100644 --- a/stl/RGBContract@0.11.0.sty +++ b/stl/RGBContract@0.11.0.sty @@ -1,8 +1,8 @@ {- - Id: stl:!r5yXt4a-v3XXv0M-E9Z6eoh-BFZweik-fxS6CB4-8AaO!MM#rover-annual-disney + Id: stl:1uyMC~lT-xPK57Lr-IgIhB0r-WxYd9io-2wZav_s-6TbR4LY#nuclear-liquid-sonic Name: RGBContract Version: 0.11.0 - Description: Types for writing RGB contracts and interfaces + Description: Types for writing RGB schemata Author: Dr Maxim Orlovsky Copyright (C) 2023-2024 LNP/BP Standards Association. All rights reserved. License: Apache-2.0 @@ -11,13 +11,13 @@ @context typelib RGBContract -import Std#ralph-blue-lucky +import Std#delete-roman-hair use AsciiPrintable#ultra-sunset-format use AlphaNum#window-tractor-alamo use Alpha#citizen-bicycle-stretch use AlphaSmall#magnum-martin-soviet -import Bitcoin#signal-color-cipher +import Bitcoin#quiz-patent-exit use Vout#brush-gloria-heroic use Txid#shallow-light-reverse use Outpoint#logo-alamo-madam @@ -41,6 +41,12 @@ data AssetSpec : ticker Ticker @mnemonic(mono-bagel-falcon) data Attachment : type MediaType, digest [Byte ^ 32] +@mnemonic(resume-telex-tower) +data AttachmentName : [Std.AsciiPrintable ^ 1..0x14] + +@mnemonic(brave-costume-percent) +data AttachmentType : id U8, name AttachmentName + @mnemonic(ivory-speed-finish) data BurnMeta : burnProofs {ProofOfReserves} @@ -56,6 +62,9 @@ data ContractTerms : text RicardianContract, media Attachment? @mnemonic(trivial-halt-nobody) data Details : [Unicode ^ 1..0xff] +@mnemonic(mega-mayday-diana) +data EmbeddedMedia : type MediaType, data [Byte] + @mnemonic(alias-analog-icon) data IssueMeta : reserves {ProofOfReserves} @@ -85,6 +94,9 @@ data MimeChar : excl#33 | hash#35 | dollar | amp#38 @mnemonic(lexicon-monitor-madonna) data Name : Std.AsciiPrintable, [Std.AsciiPrintable ^ ..0x27] +@mnemonic(hair-laser-peru) +data OpidRejectUrl : Std.AsciiPrintable, [Std.AsciiPrintable ^ ..0x1f3f] + @mnemonic(mission-person-armor) data OwnedFraction : U64 @@ -105,6 +117,16 @@ data RicardianContract : [Unicode] @mnemonic(newton-corona-aloha) data Ticker : Std.Alpha, [Std.AlphaNum ^ ..0x7] +@mnemonic(sofia-cement-citrus) +data TokenData : index TokenIndex + , ticker Ticker? + , name Name? + , details Details? + , preview EmbeddedMedia? + , media Attachment? + , attachments {U8 -> ^ ..0x14 Attachment} + , reserves ProofOfReserves? + @mnemonic(giraffe-correct-modest) data TokenIndex : U32 diff --git a/stl/RGBStd@0.11.0.sta b/stl/RGBStd@0.11.0.sta index bec475b7..72588fef 100644 --- a/stl/RGBStd@0.11.0.sta +++ b/stl/RGBStd@0.11.0.sta @@ -1,306 +1,139 @@ -----BEGIN STRICT TYPE LIB----- -Id: stl:H1HLPfyC-5YLlAk!-KWhcUo1-0vtex!9-ODxSmIA-zj1J$qc#western-craft-bogart +Id: stl:Nn~mJpFH-NvnJbK4-KygN4WE-gZ2qLty-s_BnUXh-HhkGA6I#reverse-diana-mirage Name: RGBStd Dependencies: - StrictTypes#century-comrade-chess, - AluVM#congo-archive-folio, - RGBCommit#tuna-safari-design, - RGBLogic#explain-marvin-bless, - CommitVerify#miller-pancake-elastic, - BPCore#totem-holiday-helena, - Std#ralph-blue-lucky, - Bitcoin#signal-color-cipher -Check-SHA256: 04b8598c3e334bc5ad1ed6d7f4d5b7aaec4e86a6205329e4b40d3a089dbc918f + BPCore#juliet-super-dominic, + CommitVerify#violet-panther-herbert, + RGBCommit#support-iris-depend, + Std#delete-roman-hair, + AluVM#jargon-gorilla-poetic, + RGBLogic#colombo-famous-erosion, + Bitcoin#quiz-patent-exit, + StrictTypes#henry-heart-survive +Check-SHA256: 1227f7f69888f4f758f1f7ea8ca55d6a9174193ab97d64777f04aee4fea1e185 -22w{tQ*>kqMe3tp+xFv-0Xp&G?S=|}9rRaeU`~uMrbA>C`}q*r3sZD*X=8L$d2nTOVsJHoA?4$swuZp1 -Wc+9AOf`(TIbyKWjTy4WkGaM+1wm|eR!wBY)d@|xKsr716mTQmaB}ROZ@Dgs2ia_2=g^?i+KdTOM?yny -ZEb0EZYmQ7jMFx!r5s@rJ-O*H1;I5Y*Jq{lbOS_Kbz-c)2vSEvOmAmtV|oJ}-)Viz@~C%8KNS}Z0aQ3s -^SOqbBwN-D{wl@OCJaMwZEb0ER%LQ&W_hMrvQRIBF~gy)hQg^4yxce6i-HaxmCGKABZpBR?$8E8P(yEW -Wy&lbZ-bfLFbqC#o>4E?M+l67UG^w8*<_XZ#%uyqCj(P-Wc6$lVk7oBr%DNv+($;q`HHK!gIHa)*%m(- -e#9sm3I{@IbYpL6ZU_ZIY;{&m1^^NSr?vtRe2PRb^)}W8ZdqCQlr&gKJ5X<}{fp(f$H +22w{tQ*>kqJ?lFX3Sj7m#S0l<6lWWUs|%1)CkG9>cz4~pa1P{=20~CnZ*pZ!WfpKF*Z0wZ5-ly2voTT) +QcLg9hvFSzGHuQ>ceug~LvL+uX>?X)a%pCHUATk^%RR@{#>rD>}a8$2!O9kk`*PSB+rd(so&!uOW`TABoF=_)-bYz<NgzlT_m3wFmfDB@ZlUF_{Gi(YuS4F^JLbYpL6Zt0$q=Q}``f02HLt~iCiD@{1Jw0_*8A_pi$)ov?1 +PzzIZa%p39RC#b^a|i`NY;{&m1^^NSr?vtRe2PRb^)}W8ZdqCQlr&gKJ5X<}{fp(f$H b>aU=OZ$bvG|>z)+>9PT;Au-7)~D;-++hbyX<}1pbY+s|KgxOTbXgHlB~91)qbQqD?1jgn N8blYb74+lZDpr0RRS&fT*&Z=qeY@Wmfle*z!SF)@h8|JkU^FEQwjx4X<|uavkfUR&BN*@p#4(ibP}+p taQ)Oq;;uu>eaRuf?``QO_k(5tm>UaGoca9LuK7Ij+X8IutaRAF#(Wpqw&WMxoca&&HGau2p2m_Hir +P(yEWWeNZRdtm>UaGoca9LuK7Ij+X8IutaRAF#(Wpqw&WMxoca&&HGau2p2m_Hir tB!lh<{Yi-S-!KI0_27BH<@sVme~^s3=33YaB^jIP;zf?W)s9yQf4Q+L?w(nXY|a%e*XOAC%4aD5B-6U -FMfO2d=FG%aB^jIP+@dvP;zf?W-hl7CAn^87TS9h9ibhaZ&^Bcn*B*;w|~I;-PD|t>jq6_bZBp6I6q=8 -aZ}RBA(1@GcO9QSWZ!o3C{DXc_Cg)w39@m$R6qOEzWQ+NTC@=;{OM>iY* -&c?b?aZznm(1lyi>kUF|X>MdwWnpYocxhy@ZeT05|jA;vvYupWm6Q)O{ZZweWZ*HiKem1Z9kJM|+< -C6E3~$lVDiq#NV}y^f+rssvPcNp2zIK10Q-T=FR=Q=>S+XYD&bY*ifyRPVjiFd`Y2QhLn&64&owka*miGSR> --o?7a>3`V^RAF#VZ)9aiVRL9T+8q@+Aa1+e+@!-jhcW8%o2S}z-#y5JARJB>wYeM!OmAarRB3HxICTWE -OMDJSZAYFLM|~u8B!Bn=Wb8dls`ok|_d#@P2~%ljQ)6;zaCBd+*=^-NPQ?`2v5jYd+6t@dEhY>7H!Y*U -dZb-BpG^u(WnpGhV{&P5bdWn_aCwA}8zxgKpy7|rEn>a@Jg9&ldILR+=b-aAzAVr?5I2ooM2Ulry -Ze??Gqk=;7%h%D+p%U7S;b1RT)c9`>#Kd;Rz-U=aO9W+B1XOrwWTLLzo&{8RR%LRjg@kugo@o29zwXDHA;ech!BqJAy+4@X(~&*rw>NkSNp5sya&BR4P;0g`38@&r -wvr8Q$XKK#ha*N>X+Ls92fzOv*E(~7PRR#MWnpGkWpcj!9{gsd8U18ZYC02#KAOx^Y=h@ -bX*KmV{&P5bWn9-Yh`)Fa%+!|DA9Vsm&hHC4WXN2M4aZ(WL^Hp>3BS~hw-BaLV0v$Q*?60dm);?_c?BI -Mu4qFRxf<)p=@qHCf(fs{C;c$=G;UARCwrWK+Rkw`Mu(V|7oQWGN(Z+AyvH&RuaL#N4?X)a%pCH1^_soLxv|6 -1vo|i^jdP;% -w2}rc(F;vwa%*g5P;zf?W|Q||cyL4!ji%3ykIJtwI*nu%&Q~z;Vl^%5w -S6(#;{6ajG64wDPk{-(rQe|^xa&~28LV0v$b2R>f7D?ZDzCQez5c=X9w<(f6`q$DH-G17V_XV{1(H;&` -Wpib6c4cHjd30rSI0;fmLPKwDZE18w00U?O%&6V>N}#h955#ht!=;R2Lj=uo+MI7C_W0!u+z&-~bY*UH -X>V>*V`yb|`fdzI -Y;R&=Y*t}xb!BrDB|MH$#iox7(epK^GJZz3uq*Cb2l>R6Lh9EroO>_{O=WapR$**)WfhrcWXrXyKnGOw -A#t$mH2bG7pQ)aE=^FQF!@KkQhzLn;aCLM|VQ?5o)zidWvABmX&uCxQ{9vUAsn@)h(<^=)@3p(i4FwHH -Wo~72X>(I!Xk~3-Bv(?{Wq|OU%4#DwR1!oWV0@!2f9}lj6c7M!3JEHV3_)ykOksItaxqh7bR|V}zQMU~ -Cv3(oCX8r!*SiR9zOp;)>$$b(q=dpw@(@pVZe?UsbYXO5Q)6glZDA=T4hF%Q&3qd{UvF)tP|M@Vc@bh1 -|A(%Z=^thBTg(YTb8}^MRAFaxF0!Tm7r-z?Fqq(6n;Tke)*kJ44PoBPfF{#q^A_Q|307}uWK(oubY(K0 -R#67|O%*Grnxkw0HI;&$`LH+T3zWkAaKFZV1ceDiVRT_rbYXO5G*S<)6P6lYy(#<=BR_>s@(?%#f7ArN --=Rj?7Ns(14peesZgXjLX>V>+d2nSm!8D=zpn(&o-7tVWUa<1Q{n`|;)uYyv!)~4rGOBqCPh(?sa&l#E -V`XzWC-dJ*Ygad93@i9pCb+uV$agN<27ESr7(9FG*~&H@L349yXKq$+X=GD$VRU6eY-w&}Q)OXnRCrKy -ayffohRF9oualB}P71SaJfwx=uHX^JIK`}x&T2C;PAKlCCveQ{N4udSh#@3Dqj&&J9b8~5DZf#|5baO&% -X>MdwWnpYocu;h5M(yUq2ps*m=2xUDT;RqCgn#@WzFu~@adfH5^@&-|3szxlWo~16RC#b^NWB_v7yE`g -7JPmsUNvXifRDiWpZ<6ZbNTvZE19EWo~pzXnF^bIJ{KZXc;Wm98lWo=MdwWnpYocxhx>Z)vk7 -zTZV$_{ZYwZsV-}v4k`%cR+ywTd+-_+35lqRC#b^WI=OtX=iS8LTqVnWK(5fY*ct@WLl4N2_;P0H1c!=^sIJb7^O8Qe}2!VQgh&L}7GcLTqVnWK(5fY*ct@WMS$T_RVyy866uW -6(UoUJMwlSx^W#RES3wGAo{t~Y8XLtb7^O8ZDnqBb3$xsZe&wsVQf@*X=G$|9zv-Vp*%wog4O?q)g04A -aHEjnO6;Ie%sNwVNZt)acywiMb7^mGRC#b^ZA8ZOFKPukLlqCE=E5w*=z8TWl=ueJ45i$M_H~V*5K?7! -WnpY(WJYyvXK7+=WpE8;5VC@SZy&ckV>*V`yb+BFjcr)z~Z#|7V!q4uVJ{nYcaAjmcb8~5D -ZgWCxX>MdwWnpYocu;h5fgb0V4v@cHO73HjlbgFm42mCs2<+~e+;O=m63^mMAVG6;X=iRyWp-s@Y-MCb -VRT_aY-w&}Q)OXnRCrKya)JyEuWS7@0e2{bYWv?No0k%_$#~gq^1qCzduE|50q|r -gTt*-ZIkqGqXDRHN7@cTY-w&}X>MmmVPkY}a)xYW^+v~7{W03rq(;firJ6j(!OVQFqcY-w&}Q)OXnRCrKy -a+46efUz`Mi!Z}iQtl5;XwV(E`Zdd&WRj~^37YhpmjzF7Z*_E&z?57PmRE;x*JyQZ??N1%-?X%h&UrKU%$CX5lEZwTb6rTk%m8vvBgJ_74J{Jehz7Np5g;baSek)I|~ROXFB3|9;oFKZ_7pLug@XZcue%S7~%^Wpi_{jhE2@R4ADY@XOb3WHJm&VktwD1&R~J8Qi15 -W{8UrRB~Z%b7^#GZ*D?$Ze(m_w&;L{94K`ndk%K5+?9Jv$dw7jc}U5p5@2#$kUJ%u2uWmRZggpMdB|&m -dkb29#*qXha^)f?kI>J>8dqqbOFybHKpQ-MBMCulbWCA+WpXjekD95&21^?K{bw7Oyej@6CZq~drvB7pz2;cIXd9lO&nf5a*AnfQld~-bdb4MdwWnpYocu;h5+M7`mSQb`xkca!3_>4e9YQ#rfba;u!+d5tm#=h2RwFD4YLug@XZc}Ara%FT=WnpaHg=PS6VPp{$?vC--s`v@B8YHl) -C#jpVFzBk!DMw8SR$**qZewX>bKWD7Yo@G%*b#-tU^&3KX?w7l?~*Sh8@1jRRblZzybDKcZ(?C=Q*>c; -W#7-Kk@bh=O+>c=6Nvv2!%yAASLvLGVedrqOkAVG6;X=iRyWp-s@Y-MCtVQh6}LTqVnWK(5fY*ct@WC&76LQHRGX=4EZ -4!$0V0pzZFIpSn)xsYw`yZK|qY&x6i)e}5MB-6;q2t;CIP;zf?W&=}nWC#E!gwc#^4#qsMUl{*1zNe>I -^CwqAYJB+ZKALhJOg5MaL2PhnVMAeXb4b1;7b@t4MVjY>G@u4Q3HlB(d+LiLJm-R=h;`?dxDG*cV`*tn -a%paKVPb4$VTK~nd#>@6JWikDr@YkEAs#9)9JJvRH-Qc7Q2s%Kf+=VCy -OABEU3kOtrQ)O*QWJFFoaz*WZZ5##rf6bm&7qffY6*N`B##bI~HzDmr7z##dWo%?qWo=1hQx*t>6v={g -sJ=SZlTl1iF5eQ8IAl(q%E@>So406W2vm7+WlmvjWn_%h53p;7sgGx&z)8&prN#D&cR=tS@df05SQ3Z* -PZCvbZeeX@WJYOaY-Dp&Wo=1hYXqYdo~D%m7H6OD0<^0n_2##VWXRdjy=DB@qgYOj1yf~hNo0M=LMPN> -0NEy%g(UCHeUkYj{YR7-159lqXIqm$}teZO^;JC(UrZ-Ks{e+7M1*ZDnLeX=Q9=b5mt)No1EHgR0RS -PeIWLGZ_*YTjUMn3>33lep74@i%V@}#Ze4JZgp)|VRC6;+#Z*Ep$ -a%o|1baPW>ZAoPPfv$so3kRF1PV2}fOp_vjQ6FdFHId|V>$VQpmv0RRO80?I5NZ-bfLFbqC#o>4E?M+l67UG^w8*<_XZ#%uyqCnto_jB5_YJg;9E|1`d*r&;qS -S3+uh`0YNLave-Im;eX@$}AplgPGkh3_fq3Q7_j=2#kPT_9!;lWR>~GYywm#cxiZMvTM3tQ2*(p5s~Z{ -6V3QiK&W#-F~+s6raGiL0000000000{r~^~000003qfvfZ**aFX>V?G1pxpG0WnM89|TAhYfrNblQVQ` -LpZXKteHopy#|_NJkG`K5da7P06+i$00000009600000000000000000093000000000X?b8~5DZb@cg -V`T;e3U7CAWn@!yVRU5y-Vzs+-~y(u)KQ?3g=q&>T!Ej;9TxQjc0+10Fg2)p25@y^Y-wWx$}AplgPGkh -3_fq3Q7_j=2#kPT_9!;lWR>~GYywm#VTK~nd#>C -WCF@89&dx0-7pM3Z=O*v*GCA9fL-<|HrZsA`NnJlR3~AEBGG%U@MZ$v=XJ?|;InIPy66cFfOYp#JM2r7 -_Du+Fb!>ELaBO7)$}AplgPGkh3_fq3Q7_j=2#kPT_9!;lWR>~GYywm#VTK~nd#>V=;l-_zLn%tmAe68Jly{Qj?Kss0ny)C8x -zFm#1PkyMk2y}8`ZgXa3astXM9&dx0-7pM3Z=O*v*GCA9fL-<|HrZsA`NnJlR3~AEBGG%U@MZ$v=XJ?| -;InIPy66cFfOYp#JM2r7_DuKtpQ8M_qJyiO1VIUJ=H=)@ii_0000000000|Nj60000002WMq&WpinB0%XM12~D{` -Iz96ga3kGta_pUNxh{zZ*=%3u(4f-VjO?=JcE6dyPJT+tk%Iz|R3_dTDo~ZL;TN>NvV?G00{zQ#MKE+xj;HS^AvC+-Eea3oo~4=i3iziU+2)E(%Otur}OFoDdEE8rbT!M3y4gMJ*2_uUvGVL -lsE)B`jpK80000000030|Ns9000007Vs&n0Y-Mu*2?0Hl)<>d4I}(*_lNXA@iYQk^RnB@|WjyHfhANam -kKF(O0000000960|Nj60000JaV`ybV!Z000000RI300000001I<Z4!V -_T!KNI`QJ|h6;Zj^jB$MPK+?7Lu3>C`4HLtfv$so3kRF1PV2}fOp_vjQ6FdFHId|lr&gK9B000000000400000000YNbaY{3Xl-R~baMa-0%XM12~D{`Iz96ga3kGta_pUNxh{zZ*=%3u -(4f-VjD&FwlPpg3!?y@aX^XIja4CK{WF&t@k=WXUZP9(YH~bairNa{vkf;?xyT5z&Ua+M@}mOiDpYxh>^^GknUxTJ!XL#OUcE0frb5ENEw7 -&f?o%+)B!ZpG}K!%4G?I4vp$|ttu*CMF0Q*000000RI300000001rcNZgXj8Zf#|5baZlcWd;ogc4cyN -X>V=;l-_zLn%tmAe68Jly{Qj?Kss0ny)C8xzFm#1PkyMk2y}8`ZgXa3astXM9&dx0-7pM3Z=O*v*GCA9 -fL-<|HrZsA`NnJlR3~AEBGG%U@MZ$v=XJ?|;InIPy66cFfOYp#JM2r7_DuKtpQ8M_qJyiO1VIUJ=H=)@ii_00000 -00000|Nj60000002WMq&WpinB0%XM12~D{`Iz96ga3kGta_pUNxh{zZ*=%3u(4f-VjO?=JcE6dyPJT+t -k%Iz|R3_dTDo~ZL;TN>NvV?G00{zQ#MKE+xj;HS^AvC+-Eea3oo~4=i3iziU+2)E -(%Otur}OFoDdEE8rbT!M3y4gMJ*2_uUvGVLlsE)B`jpK80000000030|Ns9000007Vs&n0Y-Mu*2?0Hl -)<>d4I}(*_lNXA@iYQk^RnB@|WjyHfhANamkKF(O0000000960|Nj60000JaV`ybV!Z000000RI300000001I<Z4!V_T!KNI`QJ|h6;Zj^jB$MPK+?7Lu3>C`4HLtfv$so3kRF1 -PV2}fOp_vjQ6FdFHId|lr&gK9B000000000400000000YNbaY{3Xl-R~baMa- -0%XM12~D{`Iz96ga3kGta_pUNxh{zZ*=%3u(4f-VjD&FwlPpg3!?y@aX^XIja4CK{WF&t@k=WXUZP9(Y -H~bairNa{vkf;?xyT5z&Ua+M@}m -OiDpYxh>^^GknUxTJ!XL#OUcE0frb5ENEw7&f?o%+)B!ZpG}K!%4G?I4vp$|ttu*CMF0Q*000000RI30 -0000001QKKZggR3Ze?;-WpV=n0(LS22}5sgbY*UINn`{C00whoXk~3-00jX8WW?18O}RiiJ@XWBBi(Rv -?4579E{O-(Y+vWlpwilmlv2~%1FNg3QJ<&wKF}2F)J=UcKm7gx`duV?R0NO^0S9MgZe??6a{vVa0%XM1 -2~D{`Iz96ga3kGta_pUNxh{zZ*=%3u(4f-VjGqWBNjk^^qPoT1+zTRnAg`3vXv9d*8d@RXy~6c6G6Dr@ -W?^Gx00jX7JIcU;0|?p#+${pKVqYC0|{wnVPj=UZE$P=1pxt8$PakD#zGc4 -+eY|aXXwx;YM0QXyiqR;Jsw2Z*{J&j1#@+9aBKht0Rd++hrkGM>lK-W|y2ahx3nF|Vuawki#7NH?S|Q-Q!u2{b0tIPiVPjm(ZiUQ7;QU9z@vL -sQU{;Z*FvDZgf*=XLAJs015(R#MKE+xj;HS^AvC+-Eea3oo~4=i3iziU+2)E(%Ou+=zxYCD0L!x4tB5H -m3vFbl?lapNXe%XU~*fKJ0+X}A;%YO&?-P3O4E?M+l67UG^w8*<_XZ#%uyqCt-#n(R;4&W&+>mb;*F>vukd;=m`yg -b@x#_>`RmOO$cpebYWy+bYTDq0!8YhU)%QMkO4aJ;_ZeCe;xE!X<$x_Fs4If6Z`oP*&DQ20rFt3ZOHs7 -0;T-agdg$OP=xIp;K4#IcLF!~asU7T000000RI300000000(DmZ(?C=a{vkgMe3tp+xFv-0Xp&G?S=|} -9rRaeU`~uMrbA>C`}q*r8?;yf@?frQ$owe+rTo-{AMw{vgzX#P!9p!}0yp?_0%XM12~D{`Iz96ga3kGt -a_pUNxh{zZ*=%3u(4f-Vj5fhEq57bK6Q|uUfIMEX^1}Vv6tLB!)|10-o)0prc>n+a000000RI3000000 -01IJrb7^O8ZDnqBa{vkgMe3tp+xFv-0Xp&G?S=|}9rRaeU`~uMrbA>C`}q*r8?;yf@?frQ$owe+rTo-{ -AMw{vgzX#P!9p!}0yp?_0%XM12~D{`Iz96ga3kGta_pUNxh{zZ*=%3u(4f-Vj5fhEq57bK6Q|uUfIMEX -^1}Vv6tLB!)|10-o)0prc>n+a000000RI300000000(kqWMyS-a{vhfMe3tp+xFv-0Xp&G?S=|}9rRae -U`~uMrbA>C`}q*r8?;yf@?frQ$owe+rTo-{AMw{vgzX#P!9p!}0yp?_0000000000{{R300000033g#@ -Wo~0>Wpe-t0!8YhU)%QMkO4aJ;_ZeCe;xE!X<$x_Fs4If6Z`oP*&DQ20rFt3ZOHs70;T-agdg$OP=xIp -;K4#IcLF!~asU7T000000RI300000000w1pa&K~T00{y`>Z4!V_T!KNI`QJ|h6;Zj^jB$MPK+?7Lu3>C -`4HI&hQW&>`ZdvNB=ndTz*X~v;Uq>`<)y^XImOPdju4Lk0000000030000000000HWMyVyb!>D&b8~5D -Zf#|5bN~bb00eGtZe;)f009JZZ*64&1pxv@>Z4!V_T!KNI`QJ|h6;Zj^jB$MPK+?7Lu3>C`4HI~v{(W1 -V6JV*{3!yZ{M3XW@z+pZODNcTE%yi#yB_`&o2yJC_VPs)+VE_pNMe3tp+xFv-0Xp&G?S=|}9rRaeU`~uMrbA>C`}q*r -8?;yf@?frQ$owe+rTo-{AMw{vgzX#P!9p!}0yp?_0000000000{{R30000002WM<=Vqt7^015&{>Z4!V -_T!KNI`QJ|h6;Zj^jB$MPK+?7Lu3>C`4HI~v{(W1V6JV*{3!yZ{M3XW@z+pGEG00000 -00000{{R30000003t@9}X=iS2Wo~qH015&{>Z4!V_T!KNI`QJ|h6;Zj^jB$MPK+?7Lu3>C`4HI~v{(W1 -V6JV*{3!yZ{M3XW@z+pGEG0000000000{{R300000033g#@Wo~0>Wpe-t0!8YhU)%QM -kO4aJ;_ZeCe;xE!X<$x_Fs4If6Z`oP*&DQ20rFt3ZOHs70;T-agdg$OP=xIp;K4#IcLF!~asU7T00000 -0RI300000000w1pa&K~T00{y`>Z4!V_T!KNI`QJ|h6;Zj^jB$MPK+?7Lu3>C`4HI&hQW&>`ZdvNB=ndT -z*X~v;Uq>`<)y^XImOPdju4Lk0000000030000000000BM{I9mVQfieVPjM0!8YhU)%QMkO4aJ;_ZeCe;xE!X<$x_Fs4If6Z`oP*=q!&6rQG)02XJT?*g=|B=zRE -ie$*y(7k2+*P~cYjRuJC38 --{*D7fZ(%hZo23R4S;p`Q9JBQllDyrZFOvPX>e?10?I5NZ-bfLFbqC#o>4E?M+l67UG^w8*<_XZ#%uyq -Ct-#n(R;4&W&+>mb;*F>vukd;=m`ygb@x#_>`RmOO$AA2VPjC`}q*rQx*t>6v={g -sJ=SZlTl1iF5eQ8IAl(q%E@>So406W2x)F;WpZhBa{vedJIcU;0|?p#+${p -KVqYC0000000000{{R300000033O>~Wpi|4ZEyepNC<6ZbYWy+bYTDr0!8YhU)%QMkO4aJ;_ZeCe;xE! -X<$x_Fs4If6Z`oP*&DQ20rFt3ZOHs70;T-agdg$OP=xIp;K4#IcLF!~asox_qhH(hioJpYH;+t0eX2w~A!Q+0eaZ{MVycPK^Kn000000093000000000YT -Y;R&=Y*Tb$bY%bv0!8YhU)%QMkO4aJ;_ZeCe;xE!X<$x_Fs4If6Z`oP*&DQ20rFt3ZOHs70;T-agdg$O -P=xIp;K4#IcLF!~askQ+=8&Hpw3LVJZG0TWlikxJMmHEDQne=0G(X}F$%Fs^000000093000000000YN -b8~5DZf#|5baMa-0!8YhU)%QMkO4aJ;_ZeCe;xE!X<$x_Fs4If6Z`oP*&DQ20rFt3ZOHs70;T-agdg$O -P=xIp;K4#IcLF!~asf-J$cU-b<11?Ur~I=y495{S&B3%8Yyg?DnxLGM`ZE9k000000093000000000Sg -VQgh?V`*h`015&{>Z4!V_T!KNI`QJ|h6;Zj^jB$MPK+?7Lu3>C`4HI~v{(W1V6JV*{3!yZ{M3XW@z+p< -?Hl01LM?X!H~4Y^K5OyylhJC`}q*r8?;yf@?frQ$owe+rTo-{AMw{vgzX#P!9p!}0yp?_0XGgC8a;P^F9!TQ -ys`YZF3(w8EA1?b(;%Z(R5QEQf&c&j000000RI3000000019PzbY*UHX>V?G015&{>Z4!V_T!KNI`QJ| -h6;Zj^jB$MPK+?7Lu3>C`4HI~v{(W1V6JV*{3!yZ{M3XW@z+p&VRUJ4ZU6)V00eGtZe;)f -009JZZ*64&1pxv@>Z4!V_T!KNI`QJ|h6;Zj^jB$MPK+?7Lu3>C`4HI~v{(W1V6JV*{3!yZ{M3XW@z+p< -?Hl01LM?X!H~4Y}WpZ+Fa&rI*0!8YhU)%QMkO4aJ;_ZeCe;xE!X<$x_Fs4If6Z`oP*$IZhiz50p(P||0 -m=?fQ^Mv6fMp@;h#Lzj#&aRFSj{pb&0RR91000000RI300000000000000000RI3000000010Gec4cgD -aAk4=WW?18O}RiiJ@XWBBi(Rv?4579E{O-(Y+vWlpwilmw&;L{94K`ndk%K5+?9Jv$dw7jc}U5p5@2#$ -kUJ%u2T5jOV`WKX1pxpD002NB00~KEVPj=UZE$P`3n8fQnYaJ?>kL6XG(3esa0W5QyJn#ohg24a)0000000930 -00000000YTY;R&=Y*Tb$bY%bu0daC2M$y&U#EU!@hie?UNS!6hQhW-W8INxxf{Fuzg#Z8m000000RI30 -0000001IJrb7^O8ZDnqBa{vhenIb5$QI^$V6PNW$vdMt6d#0>Rmk*`=dQ)K)k7d+w0000000000{{R30 -0000033g#@Wo~0>Wpe-t0ak~ln%Z2n%R^9PBgQXo1&n-R?HR4hpUd;mfGub-hyVZp000000RI3000000 -01I?-VQzD2bZKvHa{vheI6k{n`X+XC81LasyqR+($`>jwnD57lV5q8m)(2RS0000000000{{R3000000 -3T1e7Wo~n6Z*Fq{2?1%uWwlwnKfCTqC!TnpV`xO%(e*mXP$JGS1-q69d*1*6000000093000000000JM -a&m8Sa{vhe!)N7;Jv;(oC!o$&iP#xB8s<*^%!GF?$0)U9@BFJ?0000000000{{R300000031nq0{baMa+0b@PWiLgsaRw~c9&Ny{YCK_TCaeS`x+X~XLW@}|U -wEzGB000000RI300000000ne;aAk7>Me3tp+xFv-0Xp&G?S=|}9rRaeU`~uMrbA>C`}q*r{eiB7ehUYi -s7~w1CQOqefKeZ3;Wd%uopqe!>_vj92XkX`X>fFN00{zOa5aA+<>R2XhQO_4{AcS-HH^7AVzASV8M4NY -xyCjU1gEwF5PXV6FZDLo1#Vec_~kix7WfVQ#Sd|CM9$^_0000000030{{R300000Ab7^O8VRUtJWpe-u -0pipZP!Z9Fy4s@&s7y*hO1UlNfirx{z*_V4e8lMKApwRM5G-hCV9w&(UffE`hM!G~aLQ!~gAR@AcC9KZ -Uqt`_000000093000000000P0Z)9m^X=QQ)0|;Sab98cHV{`xrZ+2yJa%p5`0R?7hZeeWy7*1hrWn@Na -Wo%?Yb8~5DZf#|5bX0k8Wd;KRX=DOq#MKE+xj;HS^AvC+-Eea3oo~4=i3iziU+2)E(%OuN{^Dg=h-~N_ -zJ`Red1EINWrM}GXaQb}6c#qIM2EQqZeeX@0!8YhU)%QMkO4aJ;_ZeCe;xE!X<$x_Fs4If6Z`oP*&DQ2 -0rFt3ZOHs70;T-agdg$OP=xIp;K4#IcLF!~atLx|b7gXNWn=<+10COKearHwcS=7M7YzYaI8*bvhMOc? -)(rkC#nUG5>JtwI*nu%&Q~z;Vl^%5wS6(#;{6ajG64wDPk{-(!PGN0jWJYOaY-B}vbY*UHX>V>+d2nS0 -0|IGe0%XM12~D{`Iz96ga3kGta_pUNxh{zZ*=%3u(4f-VjAV5lLa7y@JVOzJ)&GXo9MeQ_qmbcB?4VH0 -I#X{*-UM!8ZDj&Q>Z4!V_T!KNI`QJ|h6;Zj^jB$MPK+?7Lu3>C`4HI~v{(W1V6JV*{3!yZ{M3XW@z+p< -?Hl01LM?X!H~4Z0a%FR6a&~280(t`--)Viz@~C%8KNS}Z0aQ3s^SOqbBwN-D{wl@OChzJK4+YqPF=12x -aaxrgbrDxyH3xis%LHy=ZDj&Q>Z4!V -_T!KNI`QJ|h6;Zj^jB$MPK+?7Lu3>C`4HI~v{(W1V6JV*{3!yZ{M3XW@z+pC -`}q*r8?;yf@?frQ$owe+rTo-{AMw{vgzX#P!9p!}0yp?_2y$g}WpZ|9WCD5v9p7nv%krpqNV>+d2nS00|IGe0%XM12~D{`Iz96ga3kGta_pUNxh{zZ*=%3u(4f-Vj5JaYt`n9TUcD*&5hFi^PVx{q -1b@^7zTcrn*%qZTXasIyZDj&Q>Z4!V_T!KNI`QJ|h6;Zj^jB$MPK+?7Lu3>C`4HI~v{(W1V6JV*{3!yZ -{M3XW@z+pZ4!V_T!KNI`QJ|h6;Zj^jB$MPK+?7Lu3>C`4HI~v{(W1V6JV*{3!yZ{M3XW@z+ppG1a4t%WdcR&qhH(he1kloHngE|MP03Qu=#Wn@WaVPjZ4!V_T!KNI`QJ|h6;Zj^jB$MPK+?7Lu3>C`4HJ_1fvw5rj-B|XP@r^w5ufb=C_Ju$l1`nW&GEpSWb-! -P<3KgX>@L7b8`d&00eY+X=DHe0Rr`G6JjIwIj2eqliWu}$@z+_xPw?-wb>Rw7=FYk8VaL=Li5Yl(a@n1 -+Ku60FILp}Zw|!7cE!MGSxid=WmW+Kba(&-0Rr`G6JjIwIj2eqliWu}$@z+_xPw?-wb>Rw7=FYk8VbdG -A)3GUIc{=BfUQMVFMRBwY;Hd$-Q55DeryBg+(ZXcX=g%gZ(;=j00;m8Kmh;%00000000mG0000000&cb -aByr%WCZ~L2LJ#-AOHzdb#QQONpxjx1O)&GWMyVyb!>D100037ba`-PPHzAO0RR935eRg7aAi(mZDjxj -0RlzpqhH(hOi(W05|TJ%PM*ricn_Pm -Xk-!zW@%+?WKLmiWdH>M0!8YhU)%QMkO4aJ;_ZeCe;xE!X<$x_Fs4If6Z`oP*&DQ20rFt3ZOHs70;T-a -gdg$OP=xIp;K4#IcLF!~auW-7VRC6NdhVC3z)=0wst0Y0}#&o=rIt`?a&Fa))1(#TPD!jC~PR_M88ajEx^RR910000000RI3000000 -00>icaByr>bz%bw25EG2Wo%{u1Z`z>VF3nbY;R&=Yyt&ucWz~50|$0tY-Mg^c?1e!b8~5DZf#|5bOi@z -Wo~72X>$e&baG*Cb7^#GZ*B((Wq5RDZgXjGZU_lwcw=R7bZKvH2?|qnaBys8ZDnqB1_TLXZ*FvDZgfdx -0S1_@x7s+uExGllhUte$e$Op^sMk_Bzn7+|3$axzr3rLtZDn(GVQp{#07wU8a%Ew3Z*l@;#MKE+xj;HS -^AvC+-Eea3oo~4=i3iziU+2)E(%Ou+=zxYCD0L!x4tB5Hm3vFbl?lapNXe%XU~*fKJ0+Y8VQy}3bYXO9 -Z*Fq{3ISO55nb$VTQ?X>xA?XXJF{2H^dT~zWT)b=0OBT1J2L^)|BtqCIH`QK6F1|v)Z${_U85pQj^zm^ -DU~vi8uS+c0000000030000000000FRB~Z%b7^#GZ*ECuVPj4E?M+l67UG^w8*<_XZ#%uyqCt-#n -(R;4&W&+>mb;*F>vukd;=m`ygb@x#_>`RmOO$cpebYWy+bYTDq0!8YhU)%QMkO4aJ;_ZeCe;xE!X<$x_ -Fs4If6Z`oP*&DQ20rFt3ZOHs70;T-agdg$OP=xIp;K4#IcLF!~asU7T000000RI300000000(DmZ(?C= -a{vkgMe3tp+xFv-0Xp&G?S=|}9rRaeU`~uMrbA>C`}q*r8?;yf@?frQ$owe+rTo-{AMw{vgzX#P!9p!} -0yp?_0%XM12~D{`Iz96ga3kGta_pUNxh{zZ*=%3u(4f-Vj5fhEq57bK6Q|uUfIMEX^1}Vv6tLB!)|10- -o)0prc>n+a000000RI300000000wDpaCLNZ015&{>Z4!V_T!KNI`QJ|h6;Zj^jB$MPK+?7Lu3>C`4HI~ -v{(W1V6JV*{3!yZ{M3XW@z+pGEG0000000000{{R30000003t@9}X=iS2Wo~qH015&{ ->Z4!V_T!KNI`QJ|h6;Zj^jB$MPK+?7Lu3>C`4HI~v{(W1V6JV*{3!yZ{M3XW@z+pGEG -0000000000{{R300000033g#@Wo~0>Wpe-t0!8YhU)%QMkO4aJ;_ZeCe;xE!X<$x_Fs4If6Z`oP*&DQ2 -0rFt3ZOHs70;T-agdg$OP=xIp;K4#IcLF!~asU7T000000RI300000000w1pa&K~T00{y`>Z4!V_T!KN -I`QJ|h6;Zj^jB$MPK+?7Lu3>C`4HI&hQW&>`ZdvNB=ndTz*X~v;Uq>`<)y^XImOPdju4Lk0000000030 -000000000HWMyVyb!>D&b8~5DZf#|5bN~bb00eGtZe;)f009JZZ*64&1pxv@>Z4!V_T!KNI`QJ|h6;Zj -^jB$MPK+?7Lu3>C`4HI~v{(W1V6JV*{3!yZ{M3XW@z+p~GYywm#VTK~nd#>Ze%hHN@6KPlLd+s#TneA -z-EYx +FMfO2d=FG%aB^jIP+@dvP;zf?W-J4ZNJ~fc1UzTDb_LBRjrSXj?;k=^zm6d~Uh7!wPz^$CX>MdwWnpYo +cxhxfKVmL%Q_{#Gkvz+H9iKg9-*)mSRaq_gMnjYqO>G4cRAF#(Wpq+$XJ~Xna$#;`Xh%-ZT+rxDK6vW; +JU&?LxLM72H?wDC1Zo}=N}D)4mkLjCa%FT-a&K>DQJz*lU&edk{G8WCh%}junc6$5v!Tz0lK7`GY>kS^ +4nb~XXm4^vVq;KpZ*OL38SA{&vly$FvzVnzHf7z~rv`86=_Ka^V5yX|y#`JSQ)OdvWpq%$n#j0HLDJN5-IKgM_J7b(p+0MPGk2Gl) +y2(Rz1Xgc#bS10xxe^o?x}!PNUwajGr*TW+dUY6G&@nZ7)X6RBh6__;a%pgMLV0v$b1}QF=!A)P#jpo4 +axu-4_As_7EzOC4+`8Vyy2R;!*$Y%*a87SzWk_LjXf@g$6*(YoyWQNR!##&F>hhbX+H~JN$bujoP8PMf +90*KrV{24tZDlxh1hGqe4n}Q9o)<@bBy=Qy_yc6@Jxi+hIw1E!bZZGyX=hVoa%pgMU#!_}aHwX>fFqJ7jQqgpV60Q!3=n#-@oxpi@}5@PW%Fv%B~$o;&je +RCrHybeHwl212eXGm<4cs7@Wu#FOK{KGSirhjWHCPRxjcYXnnabaS0mldQV=&ET6jM)-pXanm@-FK%_b +eB&TRo~t++rXB}ZX>@L7b91ADLi5Yl(a@n1+Ku60FILp}Zw|!7cE!MGSxid=WmW`Kcxhy)e8zcXXXRJd +MCHk1I^Yb;mDw5%F9Y9nz;zN&zQ>*gRCrcpa;b%ccT=8d`>?<6$C@F;S3|*6`1-v+nBdcqJ?FPKcnV2w +bY*gGVQf%qwlfK-7{9iX4Q|L-q$GzUMp|h#f#zWg5iW~CYZWKwE66NffESNfJhg}MqX$BH +bY)X?a>aWgn!oosZgNI|twmNZeC(lYZa*g7-2eQ3Yy;-pL;_TJ=xRXCTqXIv;)MTcr4cfxK`S9uy$)6q +!N22#m0-mN2v2o%aBpdDbo`>HD!!5a&4Q@0n2=*4!cKOosx|T?(Q^f3pcpQQSqE58Zfto_YYangZEb0E +R%LQ&W_bbtIG#g>Clv)aMjKgwAH@`bu1x<7g|G$};xvA~n-$_S3Qc8lYiwmmVRLAO(LK3V&vL%&i(~ao +9rExlGMgPx_&tqtqVlwo1}@PHO=WUxY-Lb#Z*OJ^Qb$5VZ*6U9bT$A3dy}<28ig(gSpg+?&9*`C2(3=% +09avzwZKZf-~wC-LUnFrY-LGg3_{(hxZY2n*~L)h`g1`5+Y6Wcy3*3FV40R@$PzKkAVG6;X=iRyWp-s@ +Y-MCbVRT_aY-w&}Q)OXnRCrKyau2r4C}<;1JnIz8xNJV~(^X1K`bf%-u8H4;5Rd<+X4P{pQ?3(@m6 +s4{*=wy-PiS_k>Wl|t&*Fr0fZ2~A~mVOC*mb!8S#O0=mQ@MdwWnpYocu;h57Y44uDeu(+mSpNyvVU;?Wpj0tETEt$u{ByCey89j8dQ03 +Wn@8fb7^O8b3$xsZe&wsVQf@*P;_!2@=#VHk7myNk^1UqykExfX@6CZd7@2Wi;=emnEZ^0vCdd$@LOV-czo1g)3{ojBg}iEK@em(;z`} +b7^O8Qe}2!VQgh&R$**)WkPIeZe&wsVQf@*X=FAAOOsb2BHT!z`XR@Y;NLiQXyctw^aAjmcb8~5DZgWCxX>MdwWnpYocxhxfvU_4@XKMa?TsAlK-}9~&K4&!} +B_Q_~{-JMMpvoC2L349yXKq$yV?lFsX=iRyWp-s@Y-MCbVRT_aY-w&}Q)OXnRCsA*LLFzFqNeWfC|Bzc +W)C&G2_(cU)%eA9tG|}D;6vKd3RH4oZgXjLX>V>s!tI8CO};e1CqOk7zVRUtJWkxi8r5^2Q3|{+hFQ&wA>Dj|l?}?5nvXn-qjmDr}rVK}HZ(?C=R$**)Wphq!%9@}OZ_-bc +51ndy0w1NGl_wIgUQD>ylx`L>RgMiuWo~72X>(I!Xk~3-TA8sVI1lHZPjU1lqsG~bOtk&)HFq4^xslxt +&yd4j96@t)X=iR$Z)s#xbYXO5LTqVnWK(5fY*ct@WM^aw-?LlPQjTl%(2ntadXH-EO|kM)2Suh4M{I9mVQf=$VRU6vV`ybZoQPE-kW*z2&v&{< +UdS6)_{YXS3Ma>jb_z*uaCLM~aA{<7gc#?E`%W7KsO97JuYbDvlzH(zmRs?SoJt!M#ot>t3r%HoVMJwg +VQFl0hyLPaScq)s9KMExvw34D6J>+NwrBxfixd_%u|$Wt4ncEsX=iS2Wo~p-d2nTmu2d>~5inUu`9yv& +8fat=S8rs<8`txcI~e&`_$v(otsF+cqN0Qy}ddQ=3E5C~Ia +Xk~3-No1AC=6W7=VqesjRYGc!>wZFzp>JB4@xD;^wu&SY_r(NHa7kpA7yOE2lZ8~neJqmb=e!%6GT&j$ +T6TU?+!P_HIBQ)dL349yXKq$yV?lFsX=iR$Z)s#xbYXO5LTqVnWK(5fY*ctqbaIwtP5eoR)-wgn>b=na +L2?WP;yphdRtnMIcPCTiyJR0hb8~5DZc=4-WnpY(WJFc;Wu}ZU +kw;6)I>KU%$CX5lEZwTb6rTk%m84<>f2kfhA@=uGO=Jtx#~n~$5JW2|a}~7vfDWQsGAeQqRB~Z%b7^#GZ*Eg#Xk~3- +tsXy=AYX8dDQEQRO5D(yMKm~r|3!X*hx(?lDDx}U2t#OLX>LwsbgiQFz^ko6x1)SL)KNJF?yfBScz9?Y +SqBl8)jS}SY$`!>b7^O8R%K&Bb8~5DZc=4-WnpY(WL9Bpb!9?qX>MdwWnpYocu;h5w&;L{94K`ndk%K5 ++?9Jv$dw7jc}U5p5@2#$kUJ%u2uWmRZggpMdAEq7KH8Joz6zOTm|U)Lm9Rr0Amv$$1lYlz$wHX7(J4W5 +b7^O8R%K&Bb8~5DZc=4-WnpY(WJFf*E{)ae8NT&o8 +22*2bWo=>7Z(864p8Ouwzj`dQ1n3V{mIVn=ti@|(ytFb|&pdtyM`dnhb7^zc?vf5kh_h+&YE#h%O8d1V +_{UOl9{V;uR#^q%T!0{hf1BB`=sqDirMEm%rl}EHag_fA3Wp< +AwhFv2!% +kp04IdejB5_YJg;9E|1`d*r&;qSS3+uh`0YNLave-ImojDN{iR+YxT1qeJQTRI%yh?{ +hxxA$L2PhnVMAeXb4+h!VRLBFJq*Jt8?Abrta^#~Iw-!oZ%zqO(A&rh^vGm~tg_w^L2PhnVN-2kY-~(# +WMOk?3sZD*X=8L$d2nTO4*&^LN2X=Q9=Q)O*QWK$LhgcQkwbf~^M){{|8P%hsRk~m~ep32F1 +51Y4WWC&DwaAi(mZDnMP)DN(0hN+Kdp}ZAoN($wDX8VgT7DmW3qm%zcviBmGB|7z0dg +BIJ4&sCG^VR$+2!VQzGR(<~&{!{{>E!(#o&^pB98KZhv1GEPn8Orhb4n;8ZMQ)zl>ZfBCy0{K32d-H~a +`3x8b375ImR&CF_#3#*gz1^xtuG$bzVQpn(MrmbiWOGwxZAoO8A%m*X98W>f2s0TH8C&EH;|vtDTYgh) +4~t7}WW`YoMQ(L%R$+2!VQzGDn938Qb#DiI%LhXtBc@pg0t!L7$2{bU&sPXOO(dS=5LRJwX<=@3Np5Cu +Q)O*QWc?UbbJ9Xwr}~3wv^yxa@v}v^+kiGSR2X#8M$tG2GZIy9X>V>;VRC6_vj93RHP;Wm9=`bY*P^01!iOZgXj8Zf#|5bY@{}b7cky2XCKHIoQI!*iVO+VNst{;wk@CbBrVQzC~WpV<7iR(=d3vg7gbV~*3!PlK51EySK +%g?1}nECovJTYovh9c2>uJC38-{*D7fZ(%hZo23R4S;p`Q9JBQllDysbY*gFX>MU`a{vkgUATk^%RRrzR5b&Jq7pCHn+BAwE2THpkp{2tZ6dMvX9=nqwv1qo2B#cO4}v@%)GJbnjab#7#AWpe-t0j!@J ++1z|)_nc4T)9jZsjvvUbyZ`_I000000RR90{{R3000whoXk~3-0$sR-3ClgkT&R7@ +Fp+vLr8o)eQb~Pvi_kTnAkwoUoz9Fk$<9<_tH9#zCbfD!eC0p3ZB1|7AO43pNl2#z6a{p7aAk7>>7J73 +J3yCzk$#1)IEB9}O*pr-e%zuW2Pj0_vj92XkX` +X>fFN00{z{Dm~KXV7O_2K9yF<>+h;;t~i_0*|LWuHI04?{jxC<1gEwF5PXV6FZDLo1#Vec_~kix7WfVQ +#Sd|CM9$^_00000000001ONa40000FLvL<#X=iS2Wo~qIa&=_}2nTj$a&u{KZUMGnCZ)bsi%sy +BtF}?M>WBLk*fW6RH_XPEi=Ry;9kVTK~n +d#>{Y;yn#0$sR-3ClgkT&R7@Fp+vLr8o)eQb~Pv +i_kTnAkwoUodbK5wLKbzE(ciwC3nrXLGTEzPUiqvVS}~6O1$TyxP%GIJ;q$9eabMAdM>3n3F}fxeRYe_HJ>2T +vm%|+Z(864p8Ouwzj`dQ1n3V{mIVn=ti@|(ytFb|&pdtyVs&n0Y-Mu*2?4C18`<1^X7`*=rzR5b&Jq7 +pCHn+BAw2RHObCYVynR7?IyK)J$&Uqwrx#s+#mjjI7vvS1QZ2yd2nTO0_mQT=Q}``f02HLt~iCiD@{1J +w0_*8A_pi$)ov?1Q2l|fgMJGKo2X9f$Rbsi%syBtF}?M>rzR5b&Jq7pCHn+BAw2RHObCYVynR7 +?IyK)J$&Uqwrx#s+#mjjI7vvS1QY-O000000093000000000Gad2nTO0_mQT=Q}``f02HLt~iCiD@{1J +w0_*8A_pi$)ov?1Q2l|fgMJGKo2X9f$Rcz4~pa1P{=Xc_Cg)w39@m$R6qOEzWQ+NTC@=;cz4~pa1P{=QJz*lU&edk{G8WCh%}junc6$5 +v!Tz0lK7`GY>kS^24Zz?WNc*uUATk^%RR ^ ..0xff [Byte]} - -@mnemonic(baby-infant-cake) -data AssignIface : ownedState OwnedIface - , public Std.Bool - , required Std.Bool - , multiple Std.Bool -@mnemonic(tango-hotel-jamaica) +@mnemonic(editor-comedy-dialog) data Consignmentfalse : version ContainerVer , transfer Std.Bool - , terminals {RGBCommit.BundleId -> RGBCommit.XChainSecretSeal} + , terminals {RGBCommit.BundleId -> SecretSeals} , genesis RGBCommit.Genesis - , extensions {RGBCommit.Extension ^ ..0xffffffff} , bundles {WitnessBundle ^ ..0xffffffff} , schema RGBCommit.Schema - , ifaces {Iface -> ^ ..0xff IfaceImpl} - , supplements {Supplement ^ ..0xff} , types StrictTypes.TypeSystem , scripts {AluVM.Lib ^ ..0x400} - , attachments {RGBCommit.AttachId -> [Byte ^ ..0xffffff]} - , signatures {ContentId -> ^ ..0xff ContentSigs} -@mnemonic(postage-canary-oxygen) +@mnemonic(charm-picasso-trapeze) data Consignmenttrue : version ContainerVer , transfer Std.Bool - , terminals {RGBCommit.BundleId -> RGBCommit.XChainSecretSeal} + , terminals {RGBCommit.BundleId -> SecretSeals} , genesis RGBCommit.Genesis - , extensions {RGBCommit.Extension ^ ..0xffffffff} , bundles {WitnessBundle ^ ..0xffffffff} , schema RGBCommit.Schema - , ifaces {Iface -> ^ ..0xff IfaceImpl} - , supplements {Supplement ^ ..0xff} , types StrictTypes.TypeSystem , scripts {AluVM.Lib ^ ..0x400} - , attachments {RGBCommit.AttachId -> [Byte ^ ..0xffffff]} - , signatures {ContentId -> ^ ..0xff ContentSigs} - -@mnemonic(giant-bravo-jacket) -data ContainerVer : v2#2 - - -@mnemonic(dispute-senator-parody) -data ContentId : schema RGBCommit.SchemaId - | genesis RGBCommit.ContractId - | iface IfaceId - | ifaceImpl ImplId - | suppl SupplId - -@mnemonic(reward-sharp-orca) -data ContentRef : schema RGBCommit.SchemaId - | genesis RGBCommit.ContractId - | iface IfaceId - | ifaceImpl ImplId - -@mnemonic(apropos-horizon-couple) -data ContentSigs : {RGBCommit.Identity -> ^ 1..0xa SigBlob} - -@mnemonic(corner-reptile-pagoda) -data ExtensionIface : modifier Modifier - , optional Std.Bool - , metadata {StrictTypes.FieldName ^ ..0xff} - , globals {StrictTypes.FieldName -> ^ ..0xff RGBCommit.Occurrences} - , assignments {StrictTypes.FieldName -> ^ ..0xff RGBCommit.Occurrences} - , redeems {StrictTypes.FieldName ^ ..0xff} - , valencies {StrictTypes.FieldName ^ ..0xff} - , errors {StrictTypes.VariantName ^ ..0xff} - , defaultAssignment StrictTypes.FieldName? -@mnemonic(oregano-virus-ringo) -data GenesisIface : modifier Modifier - , metadata {StrictTypes.FieldName ^ ..0xff} - , globals {StrictTypes.FieldName -> ^ ..0xff RGBCommit.Occurrences} - , assignments {StrictTypes.FieldName -> ^ ..0xff RGBCommit.Occurrences} - , valencies {StrictTypes.FieldName ^ ..0xff} - , errors {StrictTypes.VariantName ^ ..0xff} +@mnemonic(option-crimson-joseph) +data ContainerVer : v0 | (|) -@mnemonic(concert-combat-charm) -data GlobalIface : semId StrictTypes.SemId? - , required Std.Bool - , multiple Std.Bool -@mnemonic(exodus-rider-garcia) -data Iface : version VerNo - , name StrictTypes.TypeName - , inherits [IfaceId ^ ..0xff] - , timestamp I64 - , metadata {StrictTypes.FieldName -> ^ ..0xff StrictTypes.SemId} - , globalState {StrictTypes.FieldName -> ^ ..0xff GlobalIface} - , assignments {StrictTypes.FieldName -> ^ ..0xff AssignIface} - , valencies {StrictTypes.FieldName -> ^ ..0xff ValencyIface} - , genesis GenesisIface - , transitions {StrictTypes.FieldName -> ^ ..0xff TransitionIface} - , extensions {StrictTypes.FieldName -> ^ ..0xff ExtensionIface} - , defaultOperation StrictTypes.FieldName? - , errors {StrictTypes.VariantName -> ^ ..0xff [Unicode ^ ..0xff]} - , developer RGBCommit.Identity - -@mnemonic(nova-cola-carbon) -data IfaceId : [Byte ^ 32] - -@mnemonic(chris-earth-pony) -data IfaceImpl : version VerNo - , schemaId RGBCommit.SchemaId - , ifaceId IfaceId - , timestamp I64 - , metadata {NamedFieldMetaType ^ ..0xff} - , globalState {NamedFieldGlobalStateType ^ ..0xff} - , assignments {NamedFieldAssignmentType ^ ..0xff} - , valencies {NamedFieldValencyType ^ ..0xff} - , transitions {NamedFieldTransitionType ^ ..0xff} - , extensions {NamedFieldExtensionType ^ ..0xff} - , errors {NamedVariantu8 ^ ..0xff} - , developer RGBCommit.Identity - -@mnemonic(seminar-data-table) -data ImplId : [Byte ^ 32] - -@mnemonic(sultan-dexter-lotus) +@mnemonic(postal-sinatra-disco) data Kit : version ContainerVer - , ifaces {Iface ^ ..0xff} , schemata {RGBCommit.Schema ^ ..0xff} - , iimpls {IfaceImpl ^ ..0xff} - , supplements {Supplement ^ ..0xff} , types StrictTypes.TypeSystem , scripts {AluVM.Lib} - , signatures {ContentId -> ^ ..0xff ContentSigs} - -@mnemonic(saturn-escort-jordan) -data Modifier : abstract | override | final#255 - - -@mnemonic(origin-caramel-flipper) -data NamedFieldAssignmentType : id RGBCommit.AssignmentType - , name StrictTypes.FieldName - , reserved CommitVerify.ReservedBytes4 - -@mnemonic(tuna-archer-melon) -data NamedFieldExtensionType : id RGBCommit.ExtensionType - , name StrictTypes.FieldName - , reserved CommitVerify.ReservedBytes4 - -@mnemonic(museum-ohio-arizona) -data NamedFieldGlobalStateType : id RGBCommit.GlobalStateType - , name StrictTypes.FieldName - , reserved CommitVerify.ReservedBytes4 - -@mnemonic(prefix-carmen-artist) -data NamedFieldMetaType : id RGBCommit.MetaType - , name StrictTypes.FieldName - , reserved CommitVerify.ReservedBytes4 - -@mnemonic(express-brush-desire) -data NamedFieldTransitionType : id RGBCommit.TransitionType - , name StrictTypes.FieldName - , reserved CommitVerify.ReservedBytes4 - -@mnemonic(invest-apollo-inca) -data NamedFieldValencyType : id RGBCommit.ValencyType - , name StrictTypes.FieldName - , reserved CommitVerify.ReservedBytes4 - -@mnemonic(star-pilgrim-pilgrim) -data NamedVariantu8 : id U8 - , name StrictTypes.VariantName - , reserved CommitVerify.ReservedBytes4 - -@mnemonic(delphi-athlete-fresh) -data OwnedIface : any () - | rights () - | amount () - | anyData () - | anyAttach () - | data StrictTypes.SemId @mnemonic(paper-visa-storm) data PubWitness : txid Bitcoin.Txid | tx Bitcoin.Tx -@mnemonic(insect-cello-avalon) -data SigBlob : [Byte ^ 1..0x1000] - -@mnemonic(pilot-claudia-minute) -data SupplId : [Byte ^ 32] - -@mnemonic(jargon-orchid-forget) -data SupplItem : default () - | typeNo U16 - | typeName#17 StrictTypes.TypeName - | fieldName StrictTypes.FieldName - | variantName StrictTypes.VariantName - -@mnemonic(phone-claudia-kiwi) -data SupplMap : {SupplItem -> ^ ..0xff Annotations} - -@mnemonic(canoe-denmark-short) -data SupplSub : itself | meta | global | owned - | valency | assignment | genesis | transition - | extension | exception - - -@mnemonic(lobster-traffic-flame) -data Supplement : contentId ContentRef - , timestamp I64 - , creator RGBCommit.Identity - , annotations {SupplSub -> ^ ..0xff SupplMap} - -@mnemonic(sigma-rose-cubic) -data TransitionIface : modifier Modifier - , optional Std.Bool - , metadata {StrictTypes.FieldName ^ ..0xff} - , globals {StrictTypes.FieldName -> ^ ..0xff RGBCommit.Occurrences} - , inputs {StrictTypes.FieldName -> ^ ..0xff RGBCommit.Occurrences} - , assignments {StrictTypes.FieldName -> ^ ..0xff RGBCommit.Occurrences} - , valencies {StrictTypes.FieldName ^ ..0xff} - , errors {StrictTypes.VariantName ^ ..0xff} - , defaultAssignment StrictTypes.FieldName? - -@mnemonic(buzzer-holiday-fiber) -data ValencyIface : required Std.Bool - -@mnemonic(textile-next-stretch) -data VerNo : v0 | v1 - +@mnemonic(rainbow-sunday-exodus) +data SecretSeals : {BPCore.SecretSeal ^ 1..} -@mnemonic(special-almond-anatomy) -data WitnessBundle : pubWitness RGBCommit.XChainPubWitness - , anchor BPCore.AnchorMerkleProofDbcProof +@mnemonic(vortex-genesis-risk) +data WitnessBundle : pubWitness PubWitness + , anchor BPCore.AnchorDbcProof , bundle RGBCommit.TransitionBundle diff --git a/stl/RGBStorage@0.11.0.sta b/stl/RGBStorage@0.11.0.sta index 85b7610d..031e6440 100644 --- a/stl/RGBStorage@0.11.0.sta +++ b/stl/RGBStorage@0.11.0.sta @@ -1,223 +1,172 @@ -----BEGIN STRICT TYPE LIB----- -Id: stl:YBgLtVmT-FuzNBPe-1fxIqay-JW4Evd5-Za7fm9w-GLF6JAg#transit-xray-giraffe +Id: stl:zmXtAwkp-kSXdrCC-VwhgOsx-xaqYeeN-83jRn4_-iANSAwg#salon-explain-teacher Name: RGBStorage Dependencies: - RGBStd#western-craft-bogart, - StrictTypes#century-comrade-chess, - AluVM#congo-archive-folio, - RGBCommit#tuna-safari-design, - RGBLogic#explain-marvin-bless, - CommitVerify#miller-pancake-elastic, - BPCore#totem-holiday-helena, - Std#ralph-blue-lucky, + RGBStd#reverse-diana-mirage, + BPCore#juliet-super-dominic, + CommitVerify#violet-panther-herbert, + RGBCommit#support-iris-depend, + Std#delete-roman-hair, + AluVM#jargon-gorilla-poetic, + RGBLogic#colombo-famous-erosion, + StrictTypes#henry-heart-survive, Bitcoin#signal-color-cipher -Check-SHA256: d9ad4313afd685354c6846af7c3cc13a907b553af88f8e584482c6541807b6dd +Check-SHA256: 6a97e8ad10d29e32f334e8090d1b1ef2f1bdbd4960762dfc70ddc7c6d560a596 -3Q|WxQ*>`~VP|CtA5qIa{DS3zlS>Z4!V_T!KNI`QJ| -h6;Zj^jB$MPK+?7Lu3>C`4HI)Q*?4^V{}w`aAk91a5aA+<>R2XhQO_4{AcS-HH^7AVzASV8M4NYxyCjH -L2PwaO=QH?2~D{`Iz96ga3kGta_pUNxh{zZ*=%3u(4f-Vj0sXlLPKwDZE19FDia2b(>AB29AOeYx#=(k -!8In=XQlLX14LMLVywRiQb$5eZ)a&^dIKHbX?@G`sCP;~6&DQwR5(-fxrUo0Th*%N~j&hfyEy&;~+KLvM0r$}AplgPGkh3_fq3Q7_j=2#kPT -_9!;lWR>~GYywm#15`~VP|CtHh<lK>pKz(VCaX%3mIP& +XB&sB3y@ML2MxM-cip;h4&;#rLQq3*a%D_q7H}ii_tAk8EiIL^F;WduOYhK!;vHZzZO$@xxWWuWZ*6U9 +bXH|@X=Zs{xP%GIJ;q$9eabMAdM>3n3F}fxeRYe_HJ>2Tvm%`dQb$5VZ*6U9bb^WNO%DrjRIhYP1?a)o +og)LLTw}}6rDvG=`c^zKY6DYrWSc5I(&k{eX@5SIR>|w{s%)+}o6^~`ha@$PehdAwF$F7J73J3yCzk$#1)IEB9}O*pr-e%zuW2Pj0< +ZYw-c3sZD*X=8L$d2nTO^=uPjBlbC`N(qzPM@Gr{imSMTSY5T*7C#t%#3&jH2SRCdV{d702?arHbyiIV 01^bJwgM1*ibOBDT%6aZ|SrJerP1pIOD57`7Ol|-ogQ6Pjg~y>s-v>!^VNPLfWv4Jz0xkJm$nc4y MWR2J-cc#Q6SofWC)gp7L6!Sc3I$AQVo7AP4Jk3r!|EoW{Zi$060oMN(jLzPuNboiNpoRS -WoOdj7+WWC1?EKi+6QrwlvP$nl8x-M691f9z+~t)>6ivgX<}1lX9hx0LvM0r4FCar2VDR_OBR)w8yCZ2 -EylR&t_^>1Sz?kFbz0>alMxYAVQ_L~bWU$%Wl&*qbZ%vG1JyK;>qK>mX$cszrKCL=@F5H{a;)B(Tlt4r -og*WC5Jh-!Y-wX@bW>$vY*ct@WDm98Wft0d6dj=*oo`t> -c$)o5X19O9`rXu=lIsRdWprq7WH>)!E^$-R$RUwD%Xb~0J!Ic@@+ehVE%-)5lom~G1rbzXaB^jIQfX&s -bV71rZewUhC#Wt^wA&hNfbvI8l{tqo-}{|djZ8YAkJtUQVz<=}LTqVnWK(5fY*ctqbaF>d&s@;xOg?z( -`#e5a?6_IYcQ>qF5t# -xf?-lV`y)3O=WUxY-K`hZ)0muaB^jIP;zf?W@s7fyw$T9tCzEwrAszt-P)%HZ|LbH=L2A=l(W4CP6|_H -V{&D5Q)OXnu*Pw&hI`xNV4B0;>oUbhHyi-Y#=22)QEgSwgbZe&wsVQf@*X=JhGI5`vwIKJ?2 -8qdBQV5M*2;q-lY2q<~K(fZR6A>9R3cu;h52SRCdV{d706aWDkZG|bw_S!^E6;6$ujJ=)@jfnzUJFt-< -#ywK79)|@}WpPe#3K@{sQ}POxW*-wf^&?6pkN!)@-3ce88{`DNj-p1Y1XOrQZXx47L&d6G@+l`%qd385 -?K@+fP1(-9sgE>i7rMzqbp%##b#x`G^|=xh7rLW4)L(lQb*FJl;d*r#UC=Q#deq4+>4pnaV{&P5bV7M_ -WpgpRuIPk`cg3&=F>*1@lJ+pRDJ{*3f84s>#k$1lf7uIEVQ@}wWMxQUb7(c%9ThnsZoA#wq{BUjG3xT0 -r`mMiJ;;I}98MOsxf}>gZ)0mzX>DaVbp)|Xd=5r!N1hi)eI#@wfA|Ar>^)1W_c|c=L3C>gQ)y>YV{&P5 -bYHC5ZRI~s#T41Gjc0(`3ajfaCJX&HEu+ACq+L0mO$tn9VP;cfa%pgMkUL~>d4!J}CQ~Zp&c>#RM4(ex -xbT6?CbPTvEuK5`1yp!Xb##~Y*9JnaDl?KLJE%?_&cu`BzdqA(v4?YpHBQWkf@=g*VRUnyRgqGWM}19*F@#XVmjaq%az$04KD-Vbij2IPrk>V1yp!eWpb&7gm+V(X#23g?#G%T#8*SXRQUS6 -KbYXtkv-?PH+Tw3ZggdGZeeUtYqm29sTjYuk_~RiSfnI}BSut4!A4H=_ds@ZTntlVa%pgMP<3K!WqHJMYmbj8(R#s`$Q_Oip_^hvoaWGE -UH-@Ecs#X-@uLSqd30q{baKUeA)3GUIc{=BfUQMVFMRBwY;Hd$-Q55DeryBg+(ZIYc<5?C&0HnY62ZUYgq2{$1_)1ebZ~EJZgl*jrz*aY{>_4@v6zr!BEn8~s;V{eB++vPW1tu=h*<|% -Pi|~^P-_fBZ*6U9bXH|@X=Zr|05Fw)<{e=)S-S-YQ3WPbltNdpi4*91)SI!>2Tf&jb75y?IG#g>Clv)aMjKgwAH@`bu1x<7g|G$} -;xvA~n-$_S3Qc8lYiwmmVRL9)&E@`k{z!7(TI@Y9B6!g4sB24Po?(Fc@vtHNfU4XJO=WUxY-K`hZ)0nd -_h5K%L=laq&yA1JoJ^{7>oKLkF4~iax8KK|47hp@Qe|^xa&~28LV0v$b1}=fEj#9D^K)f#Cf|Xn@L3mU -0Z2&n-dr?jcD1Ll0RawDWpib6c4cHjd30rSGT61bmh)A>-9G+(AKhLw+s!eDmlO2>&}_PPHjCBJR|r&c -Wo1rpWM%K_6AuO0fiYoI|8ZKC9(55{UNs2(LOhfb*8wh)9?K3=Wpib6c4cHjd30rSH2#7XN#A(BKKz&v -`r;e6DUv<<*U}c>*a184%wsNMHUptBVZ#B!U% -rHo-i1kG~VoNp!e_~i}U4@G!%Wo~n6Z*Eg#Xk~3-1ACLTJsO2B2U!6ncg?mz@CdC==Kxq?gSEg)z2E{| -2tsvkWNc+gWC^x^65s1c;WeUUgB8EM+V1n+T -F3m?Yd1DC`X&GvUv9(-1>WQHGZVX3kZ(?C=R$**)WpflIJdRMsrjHBJ^EIe4enz&iEACnc`NWk%>en!w -doT%2WprUyVQh6}6`5yb%eAXO2UPPRaj@((`=>9Tsh)f38uw_!yYu^q2uW^mb#zT(a2QC{)5Kh{xQ8## -XkXX-V5JAC*Swe0D}EgBwY$m<1r0}KZe??6b5mnzWo=<3S5nwzfbg8kY9lvP5=0XL2PtPVR>b8F;iu9B}H_;!MSfIY{o4njA(e*y9jN*vODbSxwYq{gu+hp5Knh*Wn@!yVRU6vV`yb< -VJRgJ2Em!ld>cVuZ*8Se%j3y;5n>eohpw0DA7$}d%n3nrb7gc?VP|tLvZekPz%WEGnBZKS8(M7E9_@Aw -VcyGtCevi|7U8=IR&Qx!Q*>c;Wip;tQ3m-<6)UHjqig^*m4co5us7ukl*0UQzs7w8g$YDqbYW9;VRU6Q -QV*^ZmKt8YDf|&5KZQ>65I6*X)C9iYp+?yjr7~y^RB~Z%b7^#GZ*Ek1aAh{ZG@<&SffJ|QFn~N>u=2wF -+7z(Wqt=tdZk`V^s(A}fV`Fu4a%FB~WpgMdwWnpYocu;h5IeTD+$oD|6lahT-3bSoIq=nQXM0|*v-OPCjO=V)bO0J^ce(PcW1p-OKH=(^)qT -gDk?v){Y2{bs0f(b7^O8ZDnqBb3$xsZe&wsVQf@*P;_!e?dHP>9R0ZFSEMRj;Km4qfBYZ5UUs>0bg9bq -iCNAIR$**qZew{=d2nS&y&7&8`-VFfe10WfHD}v`L+>hHy6d9F3e5?wo>3YSP-SFga&u*FLvL+uX>@I6 -Zgfg$dIyj=yj0m~TwLF91B-Lz;+I~gZmLfZ*F5{VQgh&Ms;pyX<}?;QxVNWsw`2k$dAVY -YJsYG;e2<6^ZE|08NgypNP%NA5%VQKM}UDECiLhAbd;J)AwhFi#4d2nT9L349yXKr&s -Y-w&}Q)OXnRCsA*T90!HB~2q+D9Z7_cLRiBQrIV5qn*4?Y6;!|pLWveA3<|-X=iRyWp-s@Y-MCbVRT_a -Y-w&}Q)OXnRCsA*Vd@w5&2+699UCMSB2$w)@^&J+aUCZtmJ634`nl9<7(sJ$X=iS2Wo~qHLTqVnWK(5f -Y*ct@WMp+7La7y@JVOzJ)&GXo9MeQ_qmbcB?4VH0I#X{*-VH@~bY*UHX>V>+d2nTIM8@PY!h`6Z9%SYt5l1;p48RB~Z%b7^#GZ*Eg#Xk~3-d$KicKz74!90ND1i-DNy><~nF -Gwl#>J&iiT&+eT*8dQ03Wn@8fb7^O8b3$xsZe&wsVQf@*P;_#E9_N=1kiT6@?qiUXo4Z8}iXVUo?CzP| -ak(fG&*D)aL349yXKqquc4c8~Wn@HQbYVhlX>MdwWnpYocu;h5f(#9>YyC6dKSj6?lx}!~!>v+nlk{(+0jO?A+73c&X>Md`Zf8beV{~tFhyLPaScq)s9KMEx -vw34D6J>+NwrBxfixd_%u|$Wt4ncEsX=iS2Wo~p-d2nTkYgi@C#*klFTE}3hP#3Wmki}o*nL&Ed10e7t -M;q}9SVL%GX>L?_X=IPP!sthuPUKDEU2%WC`V+X+(UG)mk--2W1{>juaWxcJLug@XZbEEnZe&wsVQf@* -P;_#W5WIk~G+K)5%bR49t5yk`^qQ9iPjGK_bd$i8ToRU7hj7c;WkPIeZe&wsVQf@*X=IdA)7t~9tEf?*r}jS36zkMYeK9}${s8)2 -BzjZ?kPrw{V`yb-W|y2ahx -3nF|Vuawki#7NH?S|Q-Q!u2{b3PW#hbaG*1bV+2Uj4+W$OUgRJVvNU?M2#%ns>Kwa1v8ba_B>T#2Nxy{ -Ms;pyX<}?;Q*>c;WvQZiSChz_$|Xx}eRkFNAr%^eLl(1e@}~9=0-ijXfCfo!aCLNZuZ@?{0aPfN4Did> -Ze%hHN@6KPlLd+s#TneAz-EYx5L9wuZgXjLX>V>qb#7#AWwz*mh8!q$B6|*YuiTY;OURW8#d%1{rxIXt -TaY^?oCrx|Wo~q7ba}{Yf_n>Eea4XlBy!~v3=AX`3Ri2tjjmWpq?wXVGUDy+ac4_6daU{%%bk3j+fu`A*2Y1(Gbp$uTFE -suoy7Xklq?MR;&*X=7=0Q)OXnRCsA*)s0^W44Z@6CZq~drvB7pz2;cIXd9lO&nf5a*AnfQld~-bdb4MdwWnpYocu;h5+M7`mSQb`xkca!3_>4e9YQ#rfba;u!+d5tm#=h2RwFD4YLug@XZc}Ara%FT=WnpaHg=PS6VPp{$?vC-- -s`v@B8YHl)C#jpVFzBk!DMw8SR$**qZewX>bKWD7Yo@G%*b#-tU^&3KX?w7l?~*Sh8@1jRRblZzybDKc -Z(?C=Q*>c;W#7-Kk@bh=O+>c=6Nvv2!%cWh5H`};;G4e`yboldn7 -SVL%GX>L$;VpnN&Ze??G{4_<~U(XE-|Ev|Hdb$N7;9H9;8!%;3hl7uME$faw4?}NmV`X7%Wn@8gbYWv? -|7c^tcv66A`G>fIP}NCm0qyW47Umu?kmdbZ%vHb5L({_6sAL7mGp}OcA!CL~}i)2Slu_c$oJJbQGPZ -GGuje3Rh`#Ze??GPjX}iQb$5lbYv<320KfVtdv3E%6>Cqk43jf-YC|}(kgS&RQe`%bQtg9 -OuU(MB+3^mE|~AfiD0OzeAWk8kQh#3ZDnLeX=Q9=RB~Z%b7^#GZ*Ek1aAi1aE_h*Lbg9zSQXNeF+Qu*8 -L^1@@Y8ulZ0qRRZb7l@jcywiMb7^mGNoHYVWjV(hK7J55&$qsubbafuzL1-^j%|=cN>I>nnK4))Pz6b5 -VPj=G%D{mG2;nQMTOnwNgyXhzrB~SH04;UKo5i(1Vxw^fNoHYVWl3Z{Yw`7y(SHDgMM6vX0;lo=W-SML -KDgmdc1Y|2T5mrLR$**qZew{#W?^GxKDS8EHu@2+7MVFP1hs6^$We8|k3le2=(%KZsrQgo3qfvfZ**aF -X>V?GOR30+snp{uYWb)9vpEdM5jV}jv>9vwnX#InoRj)93qf;pX=iRpW?^GxOmsqKkxP5xCLpf?kur-g -(F6&@HI9dWN1yNftOldQ1y*HpPH$F+rJCAYw97+KWFy8eZUu~dV(l5N%b&~h(10yyF^CmTVQpn(Mrmbi -WL9BpWo~16RC#b^R>%){yv9NnI@?D0UT5ggCu*0_qr6cs3q2l0*x9K221#vjY)NET_7Pp|Zd*4POSky8 -4?DA0%Jd;JpJb=vumIvFO*=CPQ+04~Y*Te&UNH8E>xBrIwS50cCx~Y4>vpbO6eBrP2Wxp8;c>|H3sYrb -Y*%S?Ze??GU_W_(!uc?s(BakzdJ7?EF9^9Gyf?ad??x_c(9*_N98Yz0aCLM+b8~5DZf#|5bW&w@WnpY( -WJFFkgU`2UB%$aBN9rX=%!lt4v-5=I26&L{nTGW3do8V16r+p^9tR;mq;f9#3_2aCLM+ -b8~5DZf#|5bW&w@WnpY(WI=RvVPj}%#AUTvyg$3{N++IppJQl5+tKwp$xtHBFa^7o2YcTaPGN0jWJYOa -Y-B}vbY*UHX>V>+d2nTFC(1LJY0Wqjnq=?-uFQU?YI{u_&s!j~X7+MC!AG3V -Hn6J~&pv~A7-khwT~%sgOe`@BM`dnhb7^x)W?^GxgjZ$<5FZnjcuT6B5B6)POqpHCTrHl4#QtZa;zni7 -2}x#QV`WKgaBPMc5G-hCV9w&(UffE`hM!G~aLQ!~gAR@AcC9KZUquT;Z*FvDZgf*=XLE+Y-hdqXPWBsL -cVYo9B4AF|J)gHXb;e-i?Yrhg(m4rJb#QQONpxjxlpOD6#%EY0CLclTa6hZC<#>ZODNcTE%yi#yB_`&o -2u*KfX=Z6cbOl5XuY`uYqVp0y6#Opn49V(cQq7+-P0pRHy9#PwIZe?UiW?^Gx;?xyT5z&Ua+M@}mOiDpYxh>^^GknUxTJ!XL -#OUcE2}5sgbY*UINo3<=9kcvVUUjCQt9$#kE#Vw&S@S1XLTY^Y?LL}v9ZWWu2|;XdXkkNPaC1n$BNr;@ghiU?gEXK9KMDE{F?;HZBRuDVqlk6q -mbeZmb;*F>vukd;=m`ygb@x#_>`RmOO$0)3Z)|2*aM+Gq(Fu_0 -Ocz)^+@GUUoV7w&pu=F9->y0X3z7m=HF#-wX0mI#UQqw(qY;tp7Zc6+Qb4G4KrzO(t)@DpIt)Q@6JWikDr@YkEAs#9)9JJvRH-Qc7Q2s%Kf+=VCyOABEU3kOtrQ)O*QWJFFoaz*WZ -Z5##rf6bm&7qffY6*N`B##bI~HzDmr7z##dWo%?qWo=1hQx*t>6v={gsJ=SZlTl1iF5eQ8IAl(q%E@>S -o406W2vm7+WlmvjWn_%h53p;7sgGx&z)8&prN#D&cR=tS@df05SQ3Z*PZCvbZeeX@WJYOaY-Dp&Wo=1h -YXqYdo~D%m7H6OD0<^0n_2##VWXRdjy=DB@qgYOj1yf~hNo0M=LMPN>0NEy%g(UCHeUkYj{YR7-159lq -XIqm$}teZO^;JC(UrZ-Ks{e+7M1*ZDnLeX=Q9=b5mt)No1EHgR0RSPeIWLGZ_*YTjUMn3>33lep74@ -i%V@}#Ze4JZgp)|VRC6;+#Z*Ep$a%o|1baPW>ZAoPPfv$so3kRF1 -PV2}fOp_vjQ6FdFHId|V -a{vhfWW?18O}RiiJ@XWBBi(Rv?4579E{O-(Y+vWlpwilmlMuXsu{2tXFT+?;?hj39&>gq>HOrf1lB-q; -n)I5N0000000000|Ns90000004sUgIaBpdDbWd<^b#!w83Ib%r)d@|xKsr716mTQmaB}ROZ@Dgs2ia_2 -=g^?i+Kka>7rjFg@b(FW?*48~9t#5lC;3juy9JUg#K|!ymZ|^=0%XM12~D{`Iz96ga3kGta_pUNxh{zZ -*=%3u(4f-VjFS+&fUz`Mi!Z}iQtl5;XwV(E`Zdd&WRj~^37YhpmjD0&000000RR90000000000000000 -0RR900000001!=OZ9{KvbaG*1bW?O;bY%ty2yJmcWTI{*Lx000000RI300000000wetXJ~YD00{ygQOiC2g5`qc -0#Axs7ZjsiFSYD`^x*MIVgSxPN&ctwsMp|!ND`2%gc_Nxrh2qVwA%Us#V;Pkm;mDO$Szy}0000000030 -|Ns9000009W_507X<}?;a{vhfA5qIa{DS3z$bhcTl`&GYj0000000000|NsC0000001Y}`!VE_pNA5qIa{DS3z6r)`)wd{WM;PFag0M0#0{-c;Wd;HTYi@6MZU71bA5qIa{DS3z00Rh3Wo=1rWMy~;1r2X-LUnFrY-LGqWMy~&3Ib%r)d@|xKsr716mTQmaB}ROZ@Dgs2ia_2 -=g^?i+KiRR=6W7=VqesjRYGc!>wZFzp>JB4@xD;^wu&SY_r(Ha#MKE+xj;HS^AvC+-Eea3oo~4=i3izi -U+2)E(%Oszdy}<28ig(gSpg+?&9*`C2(3=%09avzwZKZf-~wC#0000000030|Nj600000JVs&n0Y-K}l -Zgg^CV{}PwWMy~&3Ib%r)d@|xKsr716mTQmaB}ROZ@Dgs2ia_2=g^?i+KdBxleIk>g)RqK0VQ|Mwn6X+ -txo3vSYd;;z)HQ~0$c)Q#MKE+xj;HS^AvC+-Eea3oo~4=i3iziU+2)E(%Oul2rNlD$O59e#ogQsB77jP -l+h5j^*S;D0000000030|Nj600000IVs&n0Y-LwzbZ%vHb4hMwWq1Gz0%XM12~D{`Iz96g -a3kGta_pUNxh{zZ*=%3u(4f-Vj01aUXjT --wZT0^&Yy7N)`YB000000093000000000eiWpZt4ZeeUmZe(S6015(R#MKE+xj;HS^AvC+-Eea3oo~4= -i3iziU+2)E(%OvMidq_i6cBYN^7xEELu$lFU37Sf$J;ty5yrmOX|)6Z2?Auq)d@|xKsr716mTQmaB}RO -Z@Dgs2ia_2=g^?i+KiJBynwMZT8l5kSW@l}O=!>^xB4~9n`Dx!RtcK)nwJ0o00000009300000000000 -0000000960{{R30000P0Wo=V*VRL8(4G42%Xk~3-bYTDr0%XM12~D{`Iz96ga3kGta_pUNxh{zZ*=%3u -(4f-VjFeK-+XJhss8OG%_CC-Q>(otsF+cqN0Qy}ddQ=3E5CR`j%RT&p<$~n`Pl{R>6r)`)wd{WM;PFag -0M0#0{-@G+FLmd)8^C0+InTyb%?WTG%$DZ$m;bAR)ya#VGE@Kn000000093000000000JQW?^Gxa{vkg -A5qIa{DS3zp#+${pKVqYC -0v}P!J^X^@g5?5Fidq*Gqg^kx?0)p%@k(L<&OJ%~r#Z(OK7J55&$qsubbafuzL1-^j%|=cN>I>nnK4)) -Pyhe`000000RI300000000(DfZe??2a{vkgWW?18O}RiiJ@XWBBi(Rv?4579E{O-(Y+vWlpwilmp9m~T -I>-W|y2ahx3nF|Vuawki#7NH?S|Q-Q!u2{b0%XM12~D{`Iz96ga3kGta_pUNxh{zZ*=%3u(4f-VjO?=J -cE6dyPJT+tk%Iz|R3_dTDo~ZL;TN>Nv6r)`)wd{WM;PFag0M0#0{-*|*t+(1Z!Y#S=r-tc=NPf>PeW=$`IKP*ssSB}HE2RJl0v}P!J^X^@ -g5?5Fidq*Gqg^kx?0)p%@k(L<&OJ%~r(-?SiLgsaRw~c9&Ny{YCK_TCaeS`x+X~XLW@}|UwEzGB00000 -0RI300000000000000000RI300000000&}qZe(m_a{vkgWW?18O}RiiJ@XWBBi(Rv?4579E{O-(Y+vWl -pwilm1ACLTJsO2B2U!6ncg?mz@CdC==Kxq?gSEg)z2E{|0%XM12~D{`Iz96ga3kGta_pUNxh{zZ*=%3u -(4f-VjIWKC(E(H_nGEpD*KTAo3`$}tLz4xH6U7V?G -015(R#MKE+xj;HS^AvC+-Eea3oo~4=i3iziU+2)E(%Oub$mV(;bz)!CmQ_M(k?Vd!kfCo{nDM?)_qK{8 -68FUdWW?18O}RiiJ@XWBBi(Rv?4579E{O-(Y+vWlpwilmQ>XLl0V(0al>0fVsbCfs) -I{K8&0000000000|NsC00000033q99Ze??GWpe-u0%XM12~D{`Iz96ga3kGta_pUNxh{zZ*=%3u(4f-V -jEQSlCC$c=UszhlV5m?Ru@{iVU*wrVdeH+Q@FPbX@d6)F%RT&p<$~n`Pl{R>6r)`)wd{WM;PFag0M0#0 -{-<6r_K53+2$;2e|4Ao^X6@^Cu3Qu&Ia3E~c^u(!$n*dJ0000000960|Nj60000YNbaY{3Xl-R~baMa- -0%XM12~D{`Iz96ga3kGta_pUNxh{zZ*=%3u(4f-VjD&FwlPpg3!?y@aX^XIja4CK{WF&t@k=WXUZP9(Y -H~bW>$vY;yn!0%XM12~D{`Iz96g -a3kGta_pUNxh{zZ*=%3u(4f-VjE}p*=tr7Pr6F_xjAC6(~TLj#*ewiHWCD6r)`)wd{WM;PFag0M0#0{-=f*5G-hCV9w&(UffE` -hM!G~aLQ!~gAR@AcC9KZUqt`_000000096000000000P0Wo=V*VRU5%0tt6%bZ%vHb7gY?3Ib%r)d@|x -Ksr716mTQmaB}ROZ@Dgs2ia_2=g^?i+Kh>7SS8KIkY89@$6%;X7qJ(R#b4x^L3+^xAn+qc8}R~eDia2b -(>AB29AOeYx#=(k!8In=XQlLX14LMLVywUR3nQ8ri$WPp5w@a4b3LR7M69fMnD+{F6rHCsWOZ=>00000 -00030|Ns9000009V{dMBa$#e1a{vkgWW?18O}RiiJ@XWBBi(Rv?4579E{O-(Y+vWlpwilmp9m~TI>-W| -y2ahx3nF|Vuawki#7NH?S|Q-Q!u2{b0d|^F*?W9|>m72;^XjNjCf456piKdvUO#1@>k1Zy`v3p{00000 -0RI300000000 +WoOdj7+WWC1?EKi+6QrwlvP$nl8x-M691f9z+~t)>6ivgX<}1lX9hx0LvM0r3IG9o2VDR_OBR)w8yCZ2 +EylR&t_^>1Sz?kFbz0>alMxYAVQ_L~bWU$%Wl&*qbZ%vG54IneKN{_;j(f`H9IfkFzO$PGD6U0+eW+%HuC5$^~^vuG3{`}-8x6fV={eh1!etXz_4^&}ra%FT-VRUFva&K>D +ECY>5OGottJZHLg1Ujv+c;>sahi4MJ>bZe&wsVQf@*X=FG*VlHu0(#Ro^Jj-_-pFL#X +cJe4ySuOZRLzEUxZ3PijVQ_L~bW&+&XmmnyVQyn+M^4XN(CAD)cag{ +3QuryWpq$-Z*OL38SA{&vly$FvzVnzHf7z~rv`86=_Ka^V5yX|y#`JSQ)OdvWpq@h$CD;?(VA_yLjW`fRcyMfKV`+3#WnpYocxhy(sPef35>JJaHJF7}X$37Y21E`BySDf% +$MYb{T~|g9LTqVnWK(5fY*ctqbaJufI5`vwIKJ?28qdBQV5M*2;q-lY2q<~K(fZR6A>9R3cu;h52SRCd +V{d706aWDkZG|bw_S!^E6;6$ujJ=)@jfnzUJFt-<#ywK79)|@}WpPe#3K@{sQ}POxW*-wf^&?6pkN!)@ +-3ce88{`DNj-p1Y1XOrQZXx47L&d6G@+l`%qd385?K@+fP1(-9sgE>i7rMzqbp%##b#x`G^|=xh7rLW4 +)L(lQb*FJl;d*r#UC=Q#deq4+>4pnaV{&P5bV7M_WpgpRuIPk`cg3&=F>*1@lJ+pRDJ{*3f84s>#k$1l +f7uIEVQ@}wWMxQUb7(c%9ThnsZoA#wq{BUjG3xT0r`mMiJ;;I}98MOsxf}>gZ)0mzX>DaVbp)|Xd=5r! +N1hi)eI#@wfA|Ar>^)1W_c|c=L3C>gQ)y>YV{&P5bYHC5ZRI~s#T41Gjc0(`3ajfaCJX&HEu+ACq+L0m +O$tn9VP;cfa%pgMkUL~>d4!J}CQ~Zp&c>#RM4(exxbT6?CbPTvEuK5`1yp!Xb##~Y*9JnaDl?KLJE%?_ +&cu`BzdqA(v4?YpHBQWkf@=g*VRUnyRgqGWM}19*F@#XVmjaq%az$04KD-Vbij2I +Prk>V1yp!eWpb&7gm+V(X#23g?#G%T#8*SXRQUS6KbYXtkv-?PH+Tw3ZggdGZeeUtYqm29sTjYuk_~Ri +SfnI}BSut4!A4H=_ds@ZTntlV +a%pgMP<3K!WqHJMYmbj8(R#s`$Q_Oip_^hvoaWGEUH-@Ecs#X-@uLSqd30q{baKUeA)3GUIc{=BfUQMV +FMRBwY;Hd$-Q55DeryBg+(ZIYc<5?C&0HnY62ZUYgq2{$1_)1ebZ~EJZgl*j +rz*aY{>_4@v6zr!BEn8~s;V{eB++vPW1tu=h*<|%Pi|~^P-_fBZ*6U9bXH|@X=Zr^05Fw)<{e=)S-S-Y +Q3WPbltNdpi4*91)SI!> +2Tf&jb75y?IG#g>Clv)aMjKgwAH@`bu1x<7g|G$};xvA~n-$_S3Qc8lYiwmmVRL9)&E@`k{z!7(TI@Y9 +B6!g4sB24Po?(Fc@vtHNfU4XJO=WUxY-K`hZ)0oNwYiq_Rlwao{(T?aUNqayF^88E^#IUpx^^~;)zDW6 +RB~lyPH$vo2~tNwLvL+uX>>LK1ACLTJsO2B2U!6ncg?mz@CdC==Kxq?gSEg)z2E{|2tsvkWNc+gWDG*x +skq)xpV`Gw<@$3$0NV?f{JPT8u3(v#XUGyU%pgH?b7^O8Qe}2!VQgh&L}7GcLTqVnWK(5fY*ctqbaD^2 +%P43gO+4!q%eZVl@Y7XFOZrI4j;@LJR>mVbPjd}NY;R&=Y(!;rVQFl05mk4XX5>{BgqY!%B9ne2;GPx* +vyoN#D~xyn2O`xG5Knh*Wn@!yVRU6vV`ybj|MdwWnpYocu;h5 +Ao5UFB9CUy{E_wOY}Ov_ +b`4?P%YY`+Wb+o`y9rirX=GD$VRU6QQV*^ZmKt8YDf|&5KZQ>65I6*X)C9iYp+?yjr7~y^RB~Z%b7^#G +Z*Ek1aAh>_o|h$~m;x7qi^=s8P2N+kb%iTyz>IGsVJuTN&(k15b8~5DZc=4-WnpY(WL9Bpb!9?qX>Mdw +WnpYocxhxd21}DyAtKyJp!y-llj5_1lAj$w(~~K8Zkuu=2wF ++7z(Wqt=tdZk`V^s(A}fV`Fu4a%FB~Wpg*1^P0j`PyFcKl0@oC_m?3Ve+y;&po9u3K1f-Y=e!wId2nT9 +L349yXKr&sY-w&}Q)OXnRCsA*II??UXJ=~udt5d*^xyNY6+UM*BPAgB7yhAdTcFArC_!^`X=iR$Wn)2e +b7^O8Qe}2!VQgh&L}7GcLTqVnWK(5fY*ct@WI`Qhoua1h@F-X75M~cGx(Ou2EYc;WkPIe +Ze&wsVQf@*X=G<)3g5G2eU+-LJ4R_90-H=oQq8@u`N?Dm8EmJT^zaZ=a$#@6CZbEf#WNc-1*YiE5tb$Y2vUw~^w5s+eR_{-?oF}s +QU^t*5l3uqVqt7kbYXO5Q)6glZDE4?v{pIxoScYNB9K#M4$pVGM_$MqSNO-qKME(uiFOJ}Zg6#UPjG2u +bA%Y@iTh3)1gPcX^{;=r`ILF_J(gSXjhspw6vf|LHVaK_2Ed=W5NNclv5FdArN4p(nv$Q#%5lsg#tSokXs89{S% +X=iS2Wo~qHLTqVnWK(5fY*ctqbaImrynwMZT8l5kSW@l}O=!>^xB4~9n`Dx!RtcK)nwJGnaBp>VlRC}c +diNl+Np+sim8IKY2NL?#G1fp*MV^LGtQvy0DM52{X=iR$Wn)2eb7^O8Qe}2!VQgh&R$**)WkPIeZe&ws +VQf@*X=IdA)7t~9tEf?*r}jS36zkMYeK9}${s8)2BzjZ?kPrw{V`ybF%vyGSQrr|FsW@w0CP8y^X=iR$Wn)2e +b7^O8R&Qx!Q*>c;WkPIeZe&wsVQf@*P;_#ZWKH}@ht@L%%<8?+|3PvL1mZnE9aaj_-*+cdMdwWnpYocxhysE!4oj;3`<37v2Z4^tv`QLvAn=wSY4a%d)dfQpu?kQ)OXn +Lu_wzX>MmzbaG*IWoLPx2rNlD$O59e#ogQsB77jPl+h5j^*S;NLvL<$a$#e1No1lk$>=Cf +%ypRj207<6(8~ghODx=kkv^H(szyE})=~>cY;R&=Y*Tb$bY-TDFp)<~$~wYgjK`HkjV#@&#T1_fGnK3M +JXK)_7bXoxb#7;AVr*qobYXO5r?zUHR4te2V>s +WprU_Y;&t+cznDDD1^I$Y^N^U(vj;W#J@gj^sY5z(hn_6P=XObb8~5DZf#|5bVOxzVQFl0tbeHV>*V`ybPpga1W- +frt90uqg8@)(AsrVQFqoWpu5g^uVjFLARrPJ=9S-1@5ja{CIe19$5zwmeo8Ulx!+Nb8~5DZdPSuL349y +XKqquc4c8~Wn@-iY;|QqY-w&}Q)OXnRCrKya<=Gzh8!q$B6|*YuiTY;OURW8#d%1{rxIXtTaY^?oCrx| +Wo~q7ba}UkqCVP_+P(^zWtd#9ah0$`At2>hiv-xgp2MdwWnpYocu;h5%sU`)39H8lI7A^rifZh_?ICzyC$UZalW1NfPXalR7(sJ$X=iS2Wo~qHLTqVn +WK(5fY*ct@WX_B=$<9<_tH9#zCbfD!eC0p3ZB1|7AO43pNl2#z6b4gcXk~3-(r;Se1fKjJ)xUZyvjpf5 +Rh9(_P^`skWxTX9SMp{wIfQLx1JC|lRtv}2|vJ# +Rj;5&YI0c6F%?4p4N_%xWnpY(WL9Bpb!GK>O*mP8`7qV21dnrCyk{{a-lF$FG0V5TNAc?Tc{K=4WprU= +VRT{lTw_^8WdM??RZ;ab079^O9n9I5E1y8~f0E6BLSf$}L349yXKq$yV?lFsX=iR$Z)s#xbYXO5LTqVn +WK(5fY*ct@WcSVA$Leu*(}zl(oBO2gDT>+WIm|Pn7B)KM(;qzK2_Zpqb7^O8Qe}2!VQgh&R$**)WkPIe +Ze&wsVQf@*P;_$n^sESGu0eNZ)cp(*eFU-DRQ(QTUJ^TE1nY56>E%WYMs;pyX<}?;RC#b^2vSEvOmAmt +V*~&Wz8;AIDT-**L6)$-r+lRyXoI{GIo*U3C +NI7Emc=xo*$N+#63Rh`#Ze??GP;Ya2#Mns)Y&M6LqDeqU-jv95KHlThh+c{38p;h*Y8ns*OksItaxstV +VFCP7wPq#=<|G-oQP|YlF^@fu07kb?cmYX}g@OuKX>@L7b8}B}WCl`4LQ`~P2LJ{;OOmXVLEy~oQ-C|E +J{=;K=5mqowm^*AJFBo!g5e5Kbz)a(bZ%vHb5FsfyU6^!Am>*UnF~ugt;tRB34tClkb#0P@Kl(bS{+Yy +bZ~WaL349yXKrm}Zgf&*c4c8~Wn@-iY;|R4z!w}!_*fFX9u23x2B)@U$sQVATFdKeto{kO7k*y}PjFXh +bZ%vHbDeh}Dl%U><|oit;<)Mmc#PrnMmBmzDete!zLOlP=NwOUbZ~WaL349yXKrm}Zgf&*c4c8~Wn@HQ +bYZQUDoPxXm+POS?Zkdao(?NvR4VY;R&=Y)^G`!^wVq=f*JR(GpTfNL}lr+43(S +;gS+vl4Z-(f9Jy@8BcX|aCLM+b8~5DZf#|5bXIR^WK(oubY=3%=@L7b8`bzbYuqrCxp?AYYxUduU{DdG`^>&S@S1XLTY^Y?LL}v9ZWWu2|;XdXkkNP +aC1n$BNr;@ghiU?gEXK9KMDE{F?;HZBRuDVqlk6qmbeZ|1Bk8zGh-IHIi*o-3_)ygXkkNPaC1&| +ZI#2l$xQ-a`EhCyJoZT~T}~sIjxz)>1`ZdvNB=ndTz*X~v;Uq>`<)y^XImOPdju4Lv +R$+2!VQzFzVQpm_v{(W1V6JV*{3!yZ{M3XW@z+pX9Jt +zktHWiJ@1L)babHELfN$u@7k>`Uy~SX>DnAX?A5X{h;vIo29B#Zbv)THgnzJqzni;K&ISmvLd)pN>Rl&wr9&I-v?L-&~MrmbiWK(5rNn}$N2!s^Lf^?|9I@Xg> +Oi(W05|TJ%PM*ricn_PmXk-Xfd2nS;VQpn(jMNXXYlf+hXQ9AJ%?72#_KJ5v@E-96x! +ZDnLeX=Q9=b5mt)Nn~pTqZFQ|l>ioJpYH;+t0eX2w~A!Q+0eaZ{MVycPK^aqWo=1heaS*6)M5bHCYFUH +@63IY`6K;Dlo$g{Z6f4)7N~Yk2UcNnX<=@3fzvD`*Td*C*~4P}$n=kpoj->tyfRKrOAiJKV)22*Kz +X>Mnd(*pTEa(nZJgZT^?2ML$C)mClKyTm8WaJ}8CMy}crPGN0jWJYOaY-Dp&Wo=1hmm!0y(Hu`f(Fijc +5*b_M4dVsY!8b|ZDhq!3`K5rZB}7&X<=@3bC}8#qjhfwd&>tyAtR<)2LcK~xyL-@iqBUFK20Q^ +G*lRL(MHiY2Qv~?ZfS3BR$+2!VQzGD +Q)O*QWc`7zgMJGKo2X9f$RrzR5b&Jq7pCHn+BAt^EynwMZT8l5kSW@l} +O=!>^xB4~9n`Dx!RtcK)nwJ0o0000000960|Nj60000heb#!oVX>N2+aBp>Va{vkgJ?lFX3Sj7m#S0l< +6lWWUs|%1)CkG9>cz4~pa1P{=pge*1_9$GZBsdB}fMG<}rv*w^M*=0-6Wn0hkV%a=00{zJxP%GIJ;q$9 +eabMAdM>3n3F}fxeRYe_HJ>2Tvm%|75WIk~G+K)5%bR49t5yk`^qQ9d0000000030 +|Nj60000000000000030|Ns900000GO=WFEZ*FvQVPkYtbYXO51_lUoV`ybbaG*1bV+0a +UATk^%RR-W|y2ahx3nF|Vuawki#7NH?S|Q-Q!u2{b +24`$rzR5b&Jq7pCHn+BAwOlk`76TvuW{aQ_%-X`?VwZ$5L?~ +`!+pRSq0(b70UrvE7rsqBHk#fJ+ZFZf!BafnCGSE(FlGoV#P-WF=PJ#00000000300000000006a%pF1 +baMa+0yclc;Wd;HTYi@6MZU71bHh<en_4UD`8YGkyt}EpMh{<^eb^)0$sR-3ClgkT&R7@Fp+vLr8o)eQb~Pvi_kTn +AkwoUogng1Rw9pP&is-3>Sw%P#_(xeJx6(SSwF1NH_CEIWdHyG000000RR90{{R3000nGmZE17>00Rh3 +Wo=1rWMy~;1{H5`LUnFrY-K}eX>4S2Wo}7sWMy~&3IbiYgbB+%#$2d<$}o|7E~PjL>rzR5b&Jq7pCHn+ +BAu1U=6W7=VqesjRYGc!>wZFzp>JB4@xD;^wu&SY_r(AS0$sR-3ClgkT&R7@Fp+vLr8o)eQb~Pvi_kTn +AkwoUodbK5wLKbzE(ciwC3nrXLGTEzPUiqvVS}~6O1rzR5b&Jq7pCHn+BAu1U=6W7=Vqesj +RYGc!>wZFzp>JB4@xD;^wu&SY_r(HTxP%GIJ;q$9eabMAdM>3n3F}fxeRYe_HJ>2Tvm%`Xdy}<28ig(g +Spg+?&9*`C2(3=%09avzwZKZf-~wC#0000000030|Ns900000JVs&n0Y-K}lZgg^CV{}PwWMy~&3IbiY +gbB+%#$2d<$}o|7E~PjL>rzR5b&Jq7pCHn+BAo+!leIk>g)RqK0VQ|Mwn6X+txo3vSYd;;z)HQ~0$c)J +xP%GIJ;q$9eabMAdM>3n3F}fxeRYe_HJ>2Tvm%|J2rNlD$O59e#ogQsB77jPl+h5j^*S;D +0000000030|Ns900000IVs&n0Y-LwzbZ%vHb4hMwWq1Gz0$sR-3ClgkT&R7@Fp+vLr8o)eQb~Pvi_kTn +AkwoUodbK5wLKbzE(ciwC3nrXLGTEzPUiqvVS}~6O1#Kd;Rz-U=aO9W+B0000000000|NsC0000000000000000 +|NsC0000004P$R^baG*1bV+VxWq1Gz0$sR-3ClgkT&R7@Fp+vLr8o)eQb~Pvi_kTnAkwoUou3FSNjk^^ +qPoT1+zTRnAg`3vXv9d*8d@RXy~6c6G64kxGba`;79(Hi^QCjpnbefDtxpKz(VCaX%3mIP&XB&sB3y@ML2MxM-cip;h4&;$&8SA{& +vly$FvzVnzHf7z~rv`86=_Ka^V5yX|y#`JI2?AZXgbB+%#$2d<$}o|7E~PjL>rzR5b&Jq7pCHn+BAt^E +ynwMZT8l5kSW@l}O=!>^xB4~9n`Dx!RtcK)nwJ0o0000000960{{R30000000000000960|Nj60000P0 +Wo=V*VRL8(2MBXxXk~3-bYTDr0$sR-3ClgkT&R7@Fp+vLr8o)eQb~Pvi_kTnAkwoUos?43+XJhss8OG% +_CC-Q>(otsF+cqN0Qy}ddQ=3E5CUDegbB+%#$2d<$}o|7E~PjL>rzR5b&Jq7pCHn+BAw2RHObCYVynR7 +?IyK)J$&Uqwrx#s+#mjjI7vvS1QY-O000000093000000000MPWo~72Wpe-u0$sR-3ClgkT&R7@Fp+vL +r8o)eQb~Pvi_kTnAkwoUou3FSNjk^^qPoT1+zTRnAg`3vXv9d*8d@RXy~6c6G6G$=gbB+%#$2d<$}o|7 +E~PjL>rzR5b&Jq7pCHn+BAwE2THpkp{2tZ6dMvX9=nqwv1qo2B#cO4}v@%)GJbnNG000000096000000 +000MKb#7#AWpe-u0$sR-3ClgkT&R7@Fp+vLr8o)eQb~Pvi_kTnAkwoUodbK5wLKbzE(ciwC3nrXLGTEz +PUiqvVS}~6O1rzR5b&Jq7pCHn+BAsVs3g5G2eU+-LJ4R_90-H=o +Qq8@u`N?Dm8EmJT^zZ-x0000000960|Nj60000ShX>@L7b8}^L015*2Y!hN5_Bp3Y36tDMM#=e#tGI($ +UA5U3KNx<*C>jc*f- +a%FT=WnpY{00{y;>pKz(VCaX%3mIP&XB&sB3y@ML2MxM-cip;h4&;%isPef35>JJaHJF7}X$37Y21E`B +ySDf%$MYb{T~|f`0000000030|Ns900000Aba`-PQ+acAWo-iKo|5M~K$m}!eub_$g}*CJIJdNZ+@c}} +C`8q6D?CvBfv$so3kRF1PV2}fOp_vjQ6FdFHId|+h;; +t~i_0*|LWuHI04?{jxEqFjWFA`CQ2GiK9iLKbGE6DZmrA4)G`0A&^0p`%?-6n<_oh=3uyKe?FB~$?NZ` +Y_2$)(%G_yBsGnG3;nV&5(KBV0uX$PL@)I=)&*`^S@`8Scoz5#{lyP)a751L0000000000|Nj6000000 +2u)>eQ*>c;Wd;KYcWHEPWpi_7a{vkg^=uPjBlbC`N(qzPM@Gr{imSMTSY5T*7C#t%#3&jHqk=;7%h%D+ +p%U7S;b1RT)c9`>#Kd;Rz-U=aO9W+B0;-8nrZYj|nJFZS6xP`-jES%TjD&(ihCCQ0ZEXBf&c&j000000RR90{{R3001jzxc42I3WI}arWNc+~00{zJxP%GI +J;q$9eabMAdM>3n3F}fxeRYe_HJ>2Tvm%`Xdy}<28ig(gSpg+?&9*`C2(3=%09avzwZKZf-~wC#00000 +00030|Ns9000009V{dMBa$#e1a{vkgUATk^%RR-W| +y2ahx3nF|Vuawki#7NH?S|Q-Q!u2{b0n9D`_KFAY2phA?>-!eGJLd1CM2xPn1?{~t^y ^ ..0xffffff {RGBCommit.Opout ^ ..0xffffff}} +@mnemonic(organic-sweet-karate) +data ContractIndex : publicOpouts {RGBCommit.Opout ^ ..0xffffffff}, outpointOpouts {BPCore.ExplicitSealTxid -> ^ ..0xffffffff {RGBCommit.Opout ^ ..0xffffff}} -@mnemonic(shake-square-wizard) +@mnemonic(laura-eric-planet) data MemContractState : schemaId RGBCommit.SchemaId , contractId RGBCommit.ContractId , global {RGBCommit.GlobalStateType -> ^ ..0xff MemGlobalState} , rights {RGBStd.OutputAssignmentVoidState ^ ..0xffffffff} , fungibles {RGBStd.OutputAssignmentRevealedValue ^ ..0xffffffff} , data {RGBStd.OutputAssignmentRevealedData ^ ..0xffffffff} - , attach {RGBStd.OutputAssignmentRevealedAttach ^ ..0xffffffff} -@mnemonic(gilbert-torpedo-digital) -data MemGlobalState : known {RGBStd.GlobalOut -> ^ ..0xffffffff RGBCommit.DataState}, limit U24 +@mnemonic(electra-before-cobra) +data MemGlobalState : known {RGBStd.GlobalOut -> ^ ..0xffffffff RGBCommit.RevealedData}, limit U24 -@mnemonic(savage-joshua-clone) -data MemIndex : opBundleIndex {RGBCommit.OpId -> ^ ..0xffffff RGBCommit.BundleId} - , bundleContractIndex {RGBCommit.BundleId -> ^ ..0xffffff RGBCommit.ContractId} - , bundleWitnessIndex {RGBCommit.BundleId -> ^ ..0xffffff {RGBCommit.XChainTxid ^ ..0xff}} - , contractIndex {RGBCommit.ContractId -> ^ ..0xff ContractIndex} - , terminalIndex {RGBCommit.XChainSecretSeal -> ^ ..0xffffff {RGBCommit.Opout ^ ..0xff}} +@mnemonic(canyon-sherman-nurse) +data MemIndex : opBundleChildrenIndex {RGBCommit.OpId -> ^ ..0xffffffff {RGBCommit.BundleId}} + , opBundleIndex {RGBCommit.OpId -> ^ ..0xffffffff RGBCommit.BundleId} + , bundleContractIndex {RGBCommit.BundleId -> ^ ..0xffffffff RGBCommit.ContractId} + , bundleWitnessIndex {RGBCommit.BundleId -> ^ ..0xffffffff {Bitcoin.Txid ^ ..0xffffffff}} + , contractIndex {RGBCommit.ContractId -> ContractIndex} + , terminalIndex {BPCore.SecretSeal -> ^ ..0xffffffff {RGBCommit.Opout ^ ..0xffffff}} -@mnemonic(ultra-sweden-limbo) -data MemStash : schemata {RGBCommit.SchemaId -> ^ ..0xff RGBStd.SchemaIfaces} - , ifaces {RGBStd.IfaceId -> ^ ..0xff RGBStd.Iface} - , geneses {RGBCommit.ContractId -> ^ ..0xff RGBCommit.Genesis} - , suppl {RGBStd.ContentRef -> ^ ..0xff {RGBStd.Supplement ^ ..0xff}} +@mnemonic(nylon-studio-singer) +data MemStash : schemata {RGBCommit.SchemaId -> ^ ..0xff RGBCommit.Schema} + , geneses {RGBCommit.ContractId -> RGBCommit.Genesis} , bundles {RGBCommit.BundleId -> ^ ..0xffffffff RGBCommit.TransitionBundle} - , extensions {RGBCommit.OpId -> ^ ..0xffffffff RGBCommit.Extension} - , witnesses {RGBCommit.XChainTxid -> ^ ..0xffffffff RGBStd.SealWitness} - , attachments {RGBCommit.AttachId -> [Byte ^ ..0xffffff]} - , secretSeals {RGBCommit.XChainBlindSealTxPtr ^ ..0xffffff} + , witnesses {Bitcoin.Txid -> ^ ..0xffffffff RGBStd.SealWitness} + , secretSeals {BPCore.BlindSealTxPtr ^ ..0xffffffff} , typeSystem StrictTypes.TypeSystem - , identities {RGBCommit.Identity -> RGBStd.TrustLevel} , libs {AluVM.LibId -> AluVM.Lib} - , sigs {RGBStd.ContentId -> RGBStd.ContentSigs} -@mnemonic(opinion-romeo-hunter) -data MemState : witnesses {RGBCommit.XChainTxid -> ^ ..0xffffffff RGBLogic.WitnessOrd}, contracts {RGBCommit.ContractId -> ^ ..0xff MemContractState} +@mnemonic(eddie-prague-trilogy) +data MemState : witnesses {Bitcoin.Txid -> ^ ..0xffffffff RGBLogic.WitnessOrd} + , invalidBundles {RGBCommit.BundleId ^ ..0xffffffff} + , contracts {RGBCommit.ContractId -> MemContractState} diff --git a/stl/Transfer.vesper b/stl/Transfer.vesper index 737079e6..3cdaaf47 100644 --- a/stl/Transfer.vesper +++ b/stl/Transfer.vesper @@ -7,1048 +7,279 @@ Seals vesper lexicon=types+commitments -ConsignmentId commitment hasher=SHA256 tagged=urn:lnp-bp:rgb:consignment#2024-03-11 - ContainerVer serialized - Bool serialized - ContractId serialized - DiscloseHash serialized - ImplId set len=0..MAX8 - ImplId element - DiscloseHash set len=0..MAX32 - DiscloseHash element - DiscloseHash set len=0..MAX32 - DiscloseHash element - XChainSecretSeal map len=0..MAX16 - BundleId mapKey - XChainSecretSeal mapValue - AttachId set len=0..MAX16 - AttachId element - SupplId set len=0..MAX8 - SupplId element - TypeSysId serialized - LibId set len=0..MAX16 - LibId element - ContentSigs map len=0..MAX8 - ContentId mapKey - ContentSigs mapValue +commitment ConsignmentId, hasher SHA256, tagged urn:lnp-bp:rgb:consignment#2024-03-11 + serialized ContainerVer + serialized Bool + serialized ContractId + serialized DiscloseHash + set DiscloseHash, len 0..MAX32 + element DiscloseHash + map SecretSeals, len 0..MAX16 + mapKey BundleId + mapValue SecretSeals + serialized TypeSysId + set LibId, len 0..MAX16 + element LibId -Consignmenttrue rec - version enum ContainerVer v2=2 - transfer enum Bool false=0 true=1 - terminals map len=0..MAX16 - value union XChainSecretSeal - bitcoin bytes len=32 wrapped aka=SecretSeal tag=0 - liquid bytes len=32 wrapped aka=SecretSeal tag=1 - genesis rec Genesis - ffv is U16 aka=Ffv - schemaId bytes len=32 aka=SchemaId - flags bytes len=1 aka=ReservedBytes1 - timestamp is I64 - issuer ascii aka=Identity first=AsciiPrintable rest=AsciiPrintable len=1..4096 - testnet enum Bool false=0 true=1 - altLayers1 set len=0..MAX8 aka=AltLayer1Set - AltLayer1 enum liquid=1 - assetTags map len=0..MAX8 aka=AssetTags - key is U16 aka=AssignmentType - value bytes len=32 aka=AssetTag - metadata map len=0..MAX8 aka=Metadata - key is U16 aka=MetaType - value bytes len=0..MAX16 aka=MetaValue - globals map len=0..MAX8 aka=GlobalState - key is U16 aka=GlobalStateType - value list len=1..MAX16 aka=GlobalValues - element bytes len=0..MAX16 aka=DataState - assignments map len=0..MAX8 aka=AssignmentsBlindSealTxid - key is U16 aka=AssignmentType - value union TypedAssignsBlindSealTxid - declarative list len=0..MAX16 wrapped tag=0 - AssignVoidStateBlindSealTxid union - confidential rec tag=0 - seal union XChainSecretSeal - bitcoin bytes len=32 wrapped aka=SecretSeal tag=0 - liquid bytes len=32 wrapped aka=SecretSeal tag=1 - state is Unit aka=VoidState - lock bytes len=2 aka=ReservedBytes2 - confidentialState rec tag=1 - seal union XChainBlindSealTxid - bitcoin rec BlindSealTxid wrapped tag=0 - method enum Method opretFirst=0 tapretFirst=1 - txid bytes len=32 aka=Txid - vout is U32 aka=Vout - blinding is U64 - liquid rec BlindSealTxid wrapped tag=1 - method enum Method opretFirst=0 tapretFirst=1 - txid bytes len=32 aka=Txid - vout is U32 aka=Vout - blinding is U64 - state is Unit aka=VoidState - lock bytes len=2 aka=ReservedBytes2 - confidentialSeal rec tag=2 - seal union XChainSecretSeal - bitcoin bytes len=32 wrapped aka=SecretSeal tag=0 - liquid bytes len=32 wrapped aka=SecretSeal tag=1 - state is Unit aka=VoidState - lock bytes len=2 aka=ReservedBytes2 - revealed rec tag=3 - seal union XChainBlindSealTxid - bitcoin rec BlindSealTxid wrapped tag=0 - method enum Method opretFirst=0 tapretFirst=1 - txid bytes len=32 aka=Txid - vout is U32 aka=Vout - blinding is U64 - liquid rec BlindSealTxid wrapped tag=1 - method enum Method opretFirst=0 tapretFirst=1 - txid bytes len=32 aka=Txid - vout is U32 aka=Vout - blinding is U64 - state is Unit aka=VoidState - lock bytes len=2 aka=ReservedBytes2 - fungible list len=0..MAX16 wrapped tag=1 - AssignRevealedValueBlindSealTxid union - confidential rec tag=0 - seal union XChainSecretSeal - bitcoin bytes len=32 wrapped aka=SecretSeal tag=0 - liquid bytes len=32 wrapped aka=SecretSeal tag=1 - state rec ConcealedFungible - commitment bytes len=33 aka=PedersenCommitment - rangeProof bytes len=33 aka=PedersenCommitment - lock bytes len=2 aka=ReservedBytes2 - confidentialState rec tag=1 - seal union XChainBlindSealTxid - bitcoin rec BlindSealTxid wrapped tag=0 - method enum Method opretFirst=0 tapretFirst=1 - txid bytes len=32 aka=Txid - vout is U32 aka=Vout - blinding is U64 - liquid rec BlindSealTxid wrapped tag=1 - method enum Method opretFirst=0 tapretFirst=1 - txid bytes len=32 aka=Txid - vout is U32 aka=Vout - blinding is U64 - state rec ConcealedFungible - commitment bytes len=33 aka=PedersenCommitment - rangeProof bytes len=33 aka=PedersenCommitment - lock bytes len=2 aka=ReservedBytes2 - confidentialSeal rec tag=2 - seal union XChainSecretSeal - bitcoin bytes len=32 wrapped aka=SecretSeal tag=0 - liquid bytes len=32 wrapped aka=SecretSeal tag=1 - state rec RevealedFungible - value union FungibleState - bits64 is U64 wrapped tag=0 - blinding bytes len=32 aka=BlindingFactor - tag bytes len=32 aka=AssetTag - lock bytes len=2 aka=ReservedBytes2 - revealed rec tag=3 - seal union XChainBlindSealTxid - bitcoin rec BlindSealTxid wrapped tag=0 - method enum Method opretFirst=0 tapretFirst=1 - txid bytes len=32 aka=Txid - vout is U32 aka=Vout - blinding is U64 - liquid rec BlindSealTxid wrapped tag=1 - method enum Method opretFirst=0 tapretFirst=1 - txid bytes len=32 aka=Txid - vout is U32 aka=Vout - blinding is U64 - state rec RevealedFungible - value union FungibleState - bits64 is U64 wrapped tag=0 - blinding bytes len=32 aka=BlindingFactor - tag bytes len=32 aka=AssetTag - lock bytes len=2 aka=ReservedBytes2 - structured list len=0..MAX16 wrapped tag=2 - AssignRevealedDataBlindSealTxid union - confidential rec tag=0 - seal union XChainSecretSeal - bitcoin bytes len=32 wrapped aka=SecretSeal tag=0 - liquid bytes len=32 wrapped aka=SecretSeal tag=1 - state bytes len=32 aka=ConcealedData - lock bytes len=2 aka=ReservedBytes2 - confidentialState rec tag=1 - seal union XChainBlindSealTxid - bitcoin rec BlindSealTxid wrapped tag=0 - method enum Method opretFirst=0 tapretFirst=1 - txid bytes len=32 aka=Txid - vout is U32 aka=Vout - blinding is U64 - liquid rec BlindSealTxid wrapped tag=1 - method enum Method opretFirst=0 tapretFirst=1 - txid bytes len=32 aka=Txid - vout is U32 aka=Vout - blinding is U64 - state bytes len=32 aka=ConcealedData - lock bytes len=2 aka=ReservedBytes2 - confidentialSeal rec tag=2 - seal union XChainSecretSeal - bitcoin bytes len=32 wrapped aka=SecretSeal tag=0 - liquid bytes len=32 wrapped aka=SecretSeal tag=1 - state rec RevealedData - value bytes len=0..MAX16 aka=DataState - salt is U128 - lock bytes len=2 aka=ReservedBytes2 - revealed rec tag=3 - seal union XChainBlindSealTxid - bitcoin rec BlindSealTxid wrapped tag=0 - method enum Method opretFirst=0 tapretFirst=1 - txid bytes len=32 aka=Txid - vout is U32 aka=Vout - blinding is U64 - liquid rec BlindSealTxid wrapped tag=1 - method enum Method opretFirst=0 tapretFirst=1 - txid bytes len=32 aka=Txid - vout is U32 aka=Vout - blinding is U64 - state rec RevealedData - value bytes len=0..MAX16 aka=DataState - salt is U128 - lock bytes len=2 aka=ReservedBytes2 - attachment list len=0..MAX16 wrapped tag=3 - AssignRevealedAttachBlindSealTxid union - confidential rec tag=0 - seal union XChainSecretSeal - bitcoin bytes len=32 wrapped aka=SecretSeal tag=0 - liquid bytes len=32 wrapped aka=SecretSeal tag=1 - state bytes len=32 aka=ConcealedAttach - lock bytes len=2 aka=ReservedBytes2 - confidentialState rec tag=1 - seal union XChainBlindSealTxid - bitcoin rec BlindSealTxid wrapped tag=0 - method enum Method opretFirst=0 tapretFirst=1 - txid bytes len=32 aka=Txid - vout is U32 aka=Vout - blinding is U64 - liquid rec BlindSealTxid wrapped tag=1 - method enum Method opretFirst=0 tapretFirst=1 - txid bytes len=32 aka=Txid - vout is U32 aka=Vout - blinding is U64 - state bytes len=32 aka=ConcealedAttach - lock bytes len=2 aka=ReservedBytes2 - confidentialSeal rec tag=2 - seal union XChainSecretSeal - bitcoin bytes len=32 wrapped aka=SecretSeal tag=0 - liquid bytes len=32 wrapped aka=SecretSeal tag=1 - state rec RevealedAttach - file rec AttachState - id bytes len=32 aka=AttachId - mediaType enum MediaType any=255 - salt is U64 - lock bytes len=2 aka=ReservedBytes2 - revealed rec tag=3 - seal union XChainBlindSealTxid - bitcoin rec BlindSealTxid wrapped tag=0 - method enum Method opretFirst=0 tapretFirst=1 - txid bytes len=32 aka=Txid - vout is U32 aka=Vout - blinding is U64 - liquid rec BlindSealTxid wrapped tag=1 - method enum Method opretFirst=0 tapretFirst=1 - txid bytes len=32 aka=Txid - vout is U32 aka=Vout - blinding is U64 - state rec RevealedAttach - file rec AttachState - id bytes len=32 aka=AttachId - mediaType enum MediaType any=255 - salt is U64 - lock bytes len=2 aka=ReservedBytes2 - valencies set len=0..MAX8 aka=Valencies - element is U16 aka=ValencyType - validator bytes len=1 aka=ReservedBytes1 - extensions set len=0..MAX32 - Extension rec - ffv is U16 aka=Ffv - contractId bytes len=32 aka=ContractId - nonce is U64 - extensionType is U16 aka=ExtensionType - metadata map len=0..MAX8 aka=Metadata - key is U16 aka=MetaType - value bytes len=0..MAX16 aka=MetaValue - globals map len=0..MAX8 aka=GlobalState - key is U16 aka=GlobalStateType - value list len=1..MAX16 aka=GlobalValues - element bytes len=0..MAX16 aka=DataState - assignments map len=0..MAX8 aka=AssignmentsBlindSealTxid - key is U16 aka=AssignmentType - value union TypedAssignsBlindSealTxid - declarative list len=0..MAX16 wrapped tag=0 - AssignVoidStateBlindSealTxid union - confidential rec tag=0 - seal union XChainSecretSeal - bitcoin bytes len=32 wrapped aka=SecretSeal tag=0 - liquid bytes len=32 wrapped aka=SecretSeal tag=1 - state is Unit aka=VoidState - lock bytes len=2 aka=ReservedBytes2 - confidentialState rec tag=1 - seal union XChainBlindSealTxid - bitcoin rec BlindSealTxid wrapped tag=0 - method enum Method opretFirst=0 tapretFirst=1 - txid bytes len=32 aka=Txid - vout is U32 aka=Vout - blinding is U64 - liquid rec BlindSealTxid wrapped tag=1 - method enum Method opretFirst=0 tapretFirst=1 - txid bytes len=32 aka=Txid - vout is U32 aka=Vout - blinding is U64 - state is Unit aka=VoidState - lock bytes len=2 aka=ReservedBytes2 - confidentialSeal rec tag=2 - seal union XChainSecretSeal - bitcoin bytes len=32 wrapped aka=SecretSeal tag=0 - liquid bytes len=32 wrapped aka=SecretSeal tag=1 - state is Unit aka=VoidState - lock bytes len=2 aka=ReservedBytes2 - revealed rec tag=3 - seal union XChainBlindSealTxid - bitcoin rec BlindSealTxid wrapped tag=0 - method enum Method opretFirst=0 tapretFirst=1 - txid bytes len=32 aka=Txid - vout is U32 aka=Vout - blinding is U64 - liquid rec BlindSealTxid wrapped tag=1 - method enum Method opretFirst=0 tapretFirst=1 - txid bytes len=32 aka=Txid - vout is U32 aka=Vout - blinding is U64 - state is Unit aka=VoidState - lock bytes len=2 aka=ReservedBytes2 - fungible list len=0..MAX16 wrapped tag=1 - AssignRevealedValueBlindSealTxid union - confidential rec tag=0 - seal union XChainSecretSeal - bitcoin bytes len=32 wrapped aka=SecretSeal tag=0 - liquid bytes len=32 wrapped aka=SecretSeal tag=1 - state rec ConcealedFungible - commitment bytes len=33 aka=PedersenCommitment - rangeProof bytes len=33 aka=PedersenCommitment - lock bytes len=2 aka=ReservedBytes2 - confidentialState rec tag=1 - seal union XChainBlindSealTxid - bitcoin rec BlindSealTxid wrapped tag=0 - method enum Method opretFirst=0 tapretFirst=1 - txid bytes len=32 aka=Txid - vout is U32 aka=Vout - blinding is U64 - liquid rec BlindSealTxid wrapped tag=1 - method enum Method opretFirst=0 tapretFirst=1 - txid bytes len=32 aka=Txid - vout is U32 aka=Vout - blinding is U64 - state rec ConcealedFungible - commitment bytes len=33 aka=PedersenCommitment - rangeProof bytes len=33 aka=PedersenCommitment - lock bytes len=2 aka=ReservedBytes2 - confidentialSeal rec tag=2 - seal union XChainSecretSeal - bitcoin bytes len=32 wrapped aka=SecretSeal tag=0 - liquid bytes len=32 wrapped aka=SecretSeal tag=1 - state rec RevealedFungible - value union FungibleState - bits64 is U64 wrapped tag=0 - blinding bytes len=32 aka=BlindingFactor - tag bytes len=32 aka=AssetTag - lock bytes len=2 aka=ReservedBytes2 - revealed rec tag=3 - seal union XChainBlindSealTxid - bitcoin rec BlindSealTxid wrapped tag=0 - method enum Method opretFirst=0 tapretFirst=1 - txid bytes len=32 aka=Txid - vout is U32 aka=Vout - blinding is U64 - liquid rec BlindSealTxid wrapped tag=1 - method enum Method opretFirst=0 tapretFirst=1 - txid bytes len=32 aka=Txid - vout is U32 aka=Vout - blinding is U64 - state rec RevealedFungible - value union FungibleState - bits64 is U64 wrapped tag=0 - blinding bytes len=32 aka=BlindingFactor - tag bytes len=32 aka=AssetTag - lock bytes len=2 aka=ReservedBytes2 - structured list len=0..MAX16 wrapped tag=2 - AssignRevealedDataBlindSealTxid union - confidential rec tag=0 - seal union XChainSecretSeal - bitcoin bytes len=32 wrapped aka=SecretSeal tag=0 - liquid bytes len=32 wrapped aka=SecretSeal tag=1 - state bytes len=32 aka=ConcealedData - lock bytes len=2 aka=ReservedBytes2 - confidentialState rec tag=1 - seal union XChainBlindSealTxid - bitcoin rec BlindSealTxid wrapped tag=0 - method enum Method opretFirst=0 tapretFirst=1 - txid bytes len=32 aka=Txid - vout is U32 aka=Vout - blinding is U64 - liquid rec BlindSealTxid wrapped tag=1 - method enum Method opretFirst=0 tapretFirst=1 - txid bytes len=32 aka=Txid - vout is U32 aka=Vout - blinding is U64 - state bytes len=32 aka=ConcealedData - lock bytes len=2 aka=ReservedBytes2 - confidentialSeal rec tag=2 - seal union XChainSecretSeal - bitcoin bytes len=32 wrapped aka=SecretSeal tag=0 - liquid bytes len=32 wrapped aka=SecretSeal tag=1 - state rec RevealedData - value bytes len=0..MAX16 aka=DataState - salt is U128 - lock bytes len=2 aka=ReservedBytes2 - revealed rec tag=3 - seal union XChainBlindSealTxid - bitcoin rec BlindSealTxid wrapped tag=0 - method enum Method opretFirst=0 tapretFirst=1 - txid bytes len=32 aka=Txid - vout is U32 aka=Vout - blinding is U64 - liquid rec BlindSealTxid wrapped tag=1 - method enum Method opretFirst=0 tapretFirst=1 - txid bytes len=32 aka=Txid - vout is U32 aka=Vout - blinding is U64 - state rec RevealedData - value bytes len=0..MAX16 aka=DataState - salt is U128 - lock bytes len=2 aka=ReservedBytes2 - attachment list len=0..MAX16 wrapped tag=3 - AssignRevealedAttachBlindSealTxid union - confidential rec tag=0 - seal union XChainSecretSeal - bitcoin bytes len=32 wrapped aka=SecretSeal tag=0 - liquid bytes len=32 wrapped aka=SecretSeal tag=1 - state bytes len=32 aka=ConcealedAttach - lock bytes len=2 aka=ReservedBytes2 - confidentialState rec tag=1 - seal union XChainBlindSealTxid - bitcoin rec BlindSealTxid wrapped tag=0 - method enum Method opretFirst=0 tapretFirst=1 - txid bytes len=32 aka=Txid - vout is U32 aka=Vout - blinding is U64 - liquid rec BlindSealTxid wrapped tag=1 - method enum Method opretFirst=0 tapretFirst=1 - txid bytes len=32 aka=Txid - vout is U32 aka=Vout - blinding is U64 - state bytes len=32 aka=ConcealedAttach - lock bytes len=2 aka=ReservedBytes2 - confidentialSeal rec tag=2 - seal union XChainSecretSeal - bitcoin bytes len=32 wrapped aka=SecretSeal tag=0 - liquid bytes len=32 wrapped aka=SecretSeal tag=1 - state rec RevealedAttach - file rec AttachState - id bytes len=32 aka=AttachId - mediaType enum MediaType any=255 - salt is U64 - lock bytes len=2 aka=ReservedBytes2 - revealed rec tag=3 - seal union XChainBlindSealTxid - bitcoin rec BlindSealTxid wrapped tag=0 - method enum Method opretFirst=0 tapretFirst=1 - txid bytes len=32 aka=Txid - vout is U32 aka=Vout - blinding is U64 - liquid rec BlindSealTxid wrapped tag=1 - method enum Method opretFirst=0 tapretFirst=1 - txid bytes len=32 aka=Txid - vout is U32 aka=Vout - blinding is U64 - state rec RevealedAttach - file rec AttachState - id bytes len=32 aka=AttachId - mediaType enum MediaType any=255 - salt is U64 - lock bytes len=2 aka=ReservedBytes2 - redeemed map len=0..MAX8 aka=Redeemed - key is U16 aka=ValencyType - value bytes len=32 aka=OpId - valencies set len=0..MAX8 aka=Valencies - element is U16 aka=ValencyType - validator bytes len=1 aka=ReservedBytes1 - witness bytes len=2 aka=ReservedBytes2 - bundles set len=0..MAX32 - WitnessBundle rec - bundle rec TransitionBundle - closeMethod enum Method opretFirst=0 tapretFirst=1 - inputMap map len=1..MAX16 aka=InputMap - key is U32 aka=Vout - value bytes len=32 aka=OpId - knownTransitions map len=1..MAX16 - key bytes len=32 aka=OpId - value rec Transition - ffv is U16 aka=Ffv - contractId bytes len=32 aka=ContractId - nonce is U64 - transitionType is U16 aka=TransitionType - metadata map len=0..MAX8 aka=Metadata - key is U16 aka=MetaType - value bytes len=0..MAX16 aka=MetaValue - globals map len=0..MAX8 aka=GlobalState - key is U16 aka=GlobalStateType - value list len=1..MAX16 aka=GlobalValues - element bytes len=0..MAX16 aka=DataState - inputs set len=0..MAX16 aka=Inputs - Input rec - prevOut rec Opout - op bytes len=32 aka=OpId - ty is U16 aka=AssignmentType - no is U16 - reserved bytes len=2 aka=ReservedBytes2 - assignments map len=0..MAX8 aka=AssignmentsBlindSealTxPtr - key is U16 aka=AssignmentType - value union TypedAssignsBlindSealTxPtr - declarative list len=0..MAX16 wrapped tag=0 - AssignVoidStateBlindSealTxPtr union - confidential rec tag=0 - seal union XChainSecretSeal - bitcoin bytes len=32 wrapped aka=SecretSeal tag=0 - liquid bytes len=32 wrapped aka=SecretSeal tag=1 - state is Unit aka=VoidState - lock bytes len=2 aka=ReservedBytes2 - confidentialState rec tag=1 - seal union XChainBlindSealTxPtr - bitcoin rec BlindSealTxPtr wrapped tag=0 - method enum Method opretFirst=0 tapretFirst=1 - txid union TxPtr - witnessTx is Unit tag=0 - txid bytes len=32 wrapped aka=Txid tag=1 - vout is U32 aka=Vout - blinding is U64 - liquid rec BlindSealTxPtr wrapped tag=1 - method enum Method opretFirst=0 tapretFirst=1 - txid union TxPtr - witnessTx is Unit tag=0 - txid bytes len=32 wrapped aka=Txid tag=1 - vout is U32 aka=Vout - blinding is U64 - state is Unit aka=VoidState - lock bytes len=2 aka=ReservedBytes2 - confidentialSeal rec tag=2 - seal union XChainSecretSeal - bitcoin bytes len=32 wrapped aka=SecretSeal tag=0 - liquid bytes len=32 wrapped aka=SecretSeal tag=1 - state is Unit aka=VoidState - lock bytes len=2 aka=ReservedBytes2 - revealed rec tag=3 - seal union XChainBlindSealTxPtr - bitcoin rec BlindSealTxPtr wrapped tag=0 - method enum Method opretFirst=0 tapretFirst=1 - txid union TxPtr - witnessTx is Unit tag=0 - txid bytes len=32 wrapped aka=Txid tag=1 - vout is U32 aka=Vout - blinding is U64 - liquid rec BlindSealTxPtr wrapped tag=1 - method enum Method opretFirst=0 tapretFirst=1 - txid union TxPtr - witnessTx is Unit tag=0 - txid bytes len=32 wrapped aka=Txid tag=1 - vout is U32 aka=Vout - blinding is U64 - state is Unit aka=VoidState - lock bytes len=2 aka=ReservedBytes2 - fungible list len=0..MAX16 wrapped tag=1 - AssignRevealedValueBlindSealTxPtr union - confidential rec tag=0 - seal union XChainSecretSeal - bitcoin bytes len=32 wrapped aka=SecretSeal tag=0 - liquid bytes len=32 wrapped aka=SecretSeal tag=1 - state rec ConcealedFungible - commitment bytes len=33 aka=PedersenCommitment - rangeProof bytes len=33 aka=PedersenCommitment - lock bytes len=2 aka=ReservedBytes2 - confidentialState rec tag=1 - seal union XChainBlindSealTxPtr - bitcoin rec BlindSealTxPtr wrapped tag=0 - method enum Method opretFirst=0 tapretFirst=1 - txid union TxPtr - witnessTx is Unit tag=0 - txid bytes len=32 wrapped aka=Txid tag=1 - vout is U32 aka=Vout - blinding is U64 - liquid rec BlindSealTxPtr wrapped tag=1 - method enum Method opretFirst=0 tapretFirst=1 - txid union TxPtr - witnessTx is Unit tag=0 - txid bytes len=32 wrapped aka=Txid tag=1 - vout is U32 aka=Vout - blinding is U64 - state rec ConcealedFungible - commitment bytes len=33 aka=PedersenCommitment - rangeProof bytes len=33 aka=PedersenCommitment - lock bytes len=2 aka=ReservedBytes2 - confidentialSeal rec tag=2 - seal union XChainSecretSeal - bitcoin bytes len=32 wrapped aka=SecretSeal tag=0 - liquid bytes len=32 wrapped aka=SecretSeal tag=1 - state rec RevealedFungible - value union FungibleState - bits64 is U64 wrapped tag=0 - blinding bytes len=32 aka=BlindingFactor - tag bytes len=32 aka=AssetTag - lock bytes len=2 aka=ReservedBytes2 - revealed rec tag=3 - seal union XChainBlindSealTxPtr - bitcoin rec BlindSealTxPtr wrapped tag=0 - method enum Method opretFirst=0 tapretFirst=1 - txid union TxPtr - witnessTx is Unit tag=0 - txid bytes len=32 wrapped aka=Txid tag=1 - vout is U32 aka=Vout - blinding is U64 - liquid rec BlindSealTxPtr wrapped tag=1 - method enum Method opretFirst=0 tapretFirst=1 - txid union TxPtr - witnessTx is Unit tag=0 - txid bytes len=32 wrapped aka=Txid tag=1 - vout is U32 aka=Vout - blinding is U64 - state rec RevealedFungible - value union FungibleState - bits64 is U64 wrapped tag=0 - blinding bytes len=32 aka=BlindingFactor - tag bytes len=32 aka=AssetTag - lock bytes len=2 aka=ReservedBytes2 - structured list len=0..MAX16 wrapped tag=2 - AssignRevealedDataBlindSealTxPtr union - confidential rec tag=0 - seal union XChainSecretSeal - bitcoin bytes len=32 wrapped aka=SecretSeal tag=0 - liquid bytes len=32 wrapped aka=SecretSeal tag=1 - state bytes len=32 aka=ConcealedData - lock bytes len=2 aka=ReservedBytes2 - confidentialState rec tag=1 - seal union XChainBlindSealTxPtr - bitcoin rec BlindSealTxPtr wrapped tag=0 - method enum Method opretFirst=0 tapretFirst=1 - txid union TxPtr - witnessTx is Unit tag=0 - txid bytes len=32 wrapped aka=Txid tag=1 - vout is U32 aka=Vout - blinding is U64 - liquid rec BlindSealTxPtr wrapped tag=1 - method enum Method opretFirst=0 tapretFirst=1 - txid union TxPtr - witnessTx is Unit tag=0 - txid bytes len=32 wrapped aka=Txid tag=1 - vout is U32 aka=Vout - blinding is U64 - state bytes len=32 aka=ConcealedData - lock bytes len=2 aka=ReservedBytes2 - confidentialSeal rec tag=2 - seal union XChainSecretSeal - bitcoin bytes len=32 wrapped aka=SecretSeal tag=0 - liquid bytes len=32 wrapped aka=SecretSeal tag=1 - state rec RevealedData - value bytes len=0..MAX16 aka=DataState - salt is U128 - lock bytes len=2 aka=ReservedBytes2 - revealed rec tag=3 - seal union XChainBlindSealTxPtr - bitcoin rec BlindSealTxPtr wrapped tag=0 - method enum Method opretFirst=0 tapretFirst=1 - txid union TxPtr - witnessTx is Unit tag=0 - txid bytes len=32 wrapped aka=Txid tag=1 - vout is U32 aka=Vout - blinding is U64 - liquid rec BlindSealTxPtr wrapped tag=1 - method enum Method opretFirst=0 tapretFirst=1 - txid union TxPtr - witnessTx is Unit tag=0 - txid bytes len=32 wrapped aka=Txid tag=1 - vout is U32 aka=Vout - blinding is U64 - state rec RevealedData - value bytes len=0..MAX16 aka=DataState - salt is U128 - lock bytes len=2 aka=ReservedBytes2 - attachment list len=0..MAX16 wrapped tag=3 - AssignRevealedAttachBlindSealTxPtr union - confidential rec tag=0 - seal union XChainSecretSeal - bitcoin bytes len=32 wrapped aka=SecretSeal tag=0 - liquid bytes len=32 wrapped aka=SecretSeal tag=1 - state bytes len=32 aka=ConcealedAttach - lock bytes len=2 aka=ReservedBytes2 - confidentialState rec tag=1 - seal union XChainBlindSealTxPtr - bitcoin rec BlindSealTxPtr wrapped tag=0 - method enum Method opretFirst=0 tapretFirst=1 - txid union TxPtr - witnessTx is Unit tag=0 - txid bytes len=32 wrapped aka=Txid tag=1 - vout is U32 aka=Vout - blinding is U64 - liquid rec BlindSealTxPtr wrapped tag=1 - method enum Method opretFirst=0 tapretFirst=1 - txid union TxPtr - witnessTx is Unit tag=0 - txid bytes len=32 wrapped aka=Txid tag=1 - vout is U32 aka=Vout - blinding is U64 - state bytes len=32 aka=ConcealedAttach - lock bytes len=2 aka=ReservedBytes2 - confidentialSeal rec tag=2 - seal union XChainSecretSeal - bitcoin bytes len=32 wrapped aka=SecretSeal tag=0 - liquid bytes len=32 wrapped aka=SecretSeal tag=1 - state rec RevealedAttach - file rec AttachState - id bytes len=32 aka=AttachId - mediaType enum MediaType any=255 - salt is U64 - lock bytes len=2 aka=ReservedBytes2 - revealed rec tag=3 - seal union XChainBlindSealTxPtr - bitcoin rec BlindSealTxPtr wrapped tag=0 - method enum Method opretFirst=0 tapretFirst=1 - txid union TxPtr - witnessTx is Unit tag=0 - txid bytes len=32 wrapped aka=Txid tag=1 - vout is U32 aka=Vout - blinding is U64 - liquid rec BlindSealTxPtr wrapped tag=1 - method enum Method opretFirst=0 tapretFirst=1 - txid union TxPtr - witnessTx is Unit tag=0 - txid bytes len=32 wrapped aka=Txid tag=1 - vout is U32 aka=Vout - blinding is U64 - state rec RevealedAttach - file rec AttachState - id bytes len=32 aka=AttachId - mediaType enum MediaType any=255 - salt is U64 - lock bytes len=2 aka=ReservedBytes2 - valencies set len=0..MAX8 aka=Valencies - element is U16 aka=ValencyType - validator bytes len=1 aka=ReservedBytes1 - witness bytes len=2 aka=ReservedBytes2 - schema rec Schema - ffv is U16 aka=Ffv - flags bytes len=1 aka=ReservedBytes1 - name ascii aka=TypeName first=AlphaCapsLodash rest=AlphaNumLodash len=1..100 - timestamp is I64 - developer ascii aka=Identity first=AsciiPrintable rest=AsciiPrintable len=1..4096 - metaTypes map len=0..MAX8 - key is U16 aka=MetaType - value bytes len=32 aka=SemId - globalTypes map len=0..MAX8 - key is U16 aka=GlobalStateType - value rec GlobalStateSchema - reserved bytes len=1 aka=ReservedBytes1 - semId bytes len=32 aka=SemId - maxItems is U24 - ownedTypes map len=0..MAX8 - key is U16 aka=AssignmentType - value union OwnedStateSchema - declarative is Unit tag=0 - fungible enum FungibleType wrapped unsigned64Bit=8 tag=1 - structured bytes len=32 wrapped aka=SemId tag=2 - attachment enum MediaType wrapped any=255 tag=3 - valencyTypes set len=0..MAX8 - element is U16 aka=ValencyType - genesis rec GenesisSchema - metadata set len=0..MAX8 - element is U16 aka=MetaType - globals map len=0..MAX8 - key is U16 aka=GlobalStateType - value rec Occurrences - min is U16 - max is U16 - assignments map len=0..MAX8 - key is U16 aka=AssignmentType - value rec Occurrences - min is U16 - max is U16 - valencies set len=0..MAX8 - element is U16 aka=ValencyType - some rec LibSite option wrapped tag=1 - lib bytes len=32 aka=LibId - pos is U16 - extensions map len=0..MAX8 - key is U16 aka=ExtensionType - value rec ExtensionSchema - metadata set len=0..MAX8 - element is U16 aka=MetaType - globals map len=0..MAX8 - key is U16 aka=GlobalStateType - value rec Occurrences - min is U16 - max is U16 - redeems set len=0..MAX8 - element is U16 aka=ValencyType - assignments map len=0..MAX8 - key is U16 aka=AssignmentType - value rec Occurrences - min is U16 - max is U16 - valencies set len=0..MAX8 - element is U16 aka=ValencyType - some rec LibSite option wrapped tag=1 - lib bytes len=32 aka=LibId - pos is U16 - transitions map len=0..MAX8 - key is U16 aka=TransitionType - value rec TransitionSchema - metadata set len=0..MAX8 - element is U16 aka=MetaType - globals map len=0..MAX8 - key is U16 aka=GlobalStateType - value rec Occurrences - min is U16 - max is U16 - inputs map len=0..MAX8 - key is U16 aka=AssignmentType - value rec Occurrences - min is U16 - max is U16 - assignments map len=0..MAX8 - key is U16 aka=AssignmentType - value rec Occurrences - min is U16 - max is U16 - valencies set len=0..MAX8 - element is U16 aka=ValencyType - some rec LibSite option wrapped tag=1 - lib bytes len=32 aka=LibId - pos is U16 - ifaces map len=0..MAX8 - key rec Iface - version enum VerNo v0=0 v1=1 - name ascii aka=TypeName first=AlphaCapsLodash rest=AlphaNumLodash len=1..100 - inherits list len=0..MAX8 - element bytes len=32 aka=IfaceId - timestamp is I64 - metadata map len=0..MAX8 - key ascii aka=FieldName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 - value bytes len=32 aka=SemId - globalState map len=0..MAX8 - key ascii aka=FieldName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 - value rec GlobalIface - some bytes len=32 option wrapped aka=SemId tag=1 - required enum Bool false=0 true=1 - multiple enum Bool false=0 true=1 - assignments map len=0..MAX8 - key ascii aka=FieldName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 - value rec AssignIface - ownedState union OwnedIface - any is Unit tag=0 - rights is Unit tag=1 - amount is Unit tag=2 - anyData is Unit tag=3 - anyAttach is Unit tag=4 - data bytes len=32 wrapped aka=SemId tag=5 - public enum Bool false=0 true=1 - required enum Bool false=0 true=1 - multiple enum Bool false=0 true=1 - valencies map len=0..MAX8 - key ascii aka=FieldName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 - value rec ValencyIface - required enum Bool false=0 true=1 - genesis rec GenesisIface - modifier enum Modifier abstract=0 override=1 final=255 - metadata set len=0..MAX8 - element ascii aka=FieldName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 - globals map len=0..MAX8 - key ascii aka=FieldName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 - value rec Occurrences - min is U16 - max is U16 - assignments map len=0..MAX8 - key ascii aka=FieldName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 - value rec Occurrences - min is U16 - max is U16 - valencies set len=0..MAX8 - element ascii aka=FieldName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 - errors set len=0..MAX8 - element ascii aka=VariantName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 - transitions map len=0..MAX8 - key ascii aka=FieldName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 - value rec TransitionIface - modifier enum Modifier abstract=0 override=1 final=255 - optional enum Bool false=0 true=1 - metadata set len=0..MAX8 - element ascii aka=FieldName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 - globals map len=0..MAX8 - key ascii aka=FieldName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 - value rec Occurrences - min is U16 - max is U16 - inputs map len=0..MAX8 - key ascii aka=FieldName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 - value rec Occurrences - min is U16 - max is U16 - assignments map len=0..MAX8 - key ascii aka=FieldName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 - value rec Occurrences - min is U16 - max is U16 - valencies set len=0..MAX8 - element ascii aka=FieldName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 - errors set len=0..MAX8 - element ascii aka=VariantName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 - some ascii option wrapped aka=FieldName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 tag=1 - extensions map len=0..MAX8 - key ascii aka=FieldName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 - value rec ExtensionIface - modifier enum Modifier abstract=0 override=1 final=255 - optional enum Bool false=0 true=1 - metadata set len=0..MAX8 - element ascii aka=FieldName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 - globals map len=0..MAX8 - key ascii aka=FieldName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 - value rec Occurrences - min is U16 - max is U16 - assignments map len=0..MAX8 - key ascii aka=FieldName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 - value rec Occurrences - min is U16 - max is U16 - redeems set len=0..MAX8 - element ascii aka=FieldName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 - valencies set len=0..MAX8 - element ascii aka=FieldName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 - errors set len=0..MAX8 - element ascii aka=VariantName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 - some ascii option wrapped aka=FieldName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 tag=1 - some ascii option wrapped aka=FieldName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 tag=1 - errors map len=0..MAX8 - key ascii aka=VariantName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 - value str len=0..MAX8 - developer ascii aka=Identity first=AsciiPrintable rest=AsciiPrintable len=1..4096 - value rec IfaceImpl - version enum VerNo v0=0 v1=1 - schemaId bytes len=32 aka=SchemaId - ifaceId bytes len=32 aka=IfaceId - timestamp is I64 - metadata set len=0..MAX8 - NamedFieldMetaType rec - id is U16 aka=MetaType - name ascii aka=FieldName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 - reserved bytes len=4 aka=ReservedBytes4 - globalState set len=0..MAX8 - NamedFieldGlobalStateType rec - id is U16 aka=GlobalStateType - name ascii aka=FieldName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 - reserved bytes len=4 aka=ReservedBytes4 - assignments set len=0..MAX8 - NamedFieldAssignmentType rec - id is U16 aka=AssignmentType - name ascii aka=FieldName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 - reserved bytes len=4 aka=ReservedBytes4 - valencies set len=0..MAX8 - NamedFieldValencyType rec - id is U16 aka=ValencyType - name ascii aka=FieldName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 - reserved bytes len=4 aka=ReservedBytes4 - transitions set len=0..MAX8 - NamedFieldTransitionType rec - id is U16 aka=TransitionType - name ascii aka=FieldName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 - reserved bytes len=4 aka=ReservedBytes4 - extensions set len=0..MAX8 - NamedFieldExtensionType rec - id is U16 aka=ExtensionType - name ascii aka=FieldName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 - reserved bytes len=4 aka=ReservedBytes4 - errors set len=0..MAX8 - NamedVariantu8 rec - id is U8 - name ascii aka=VariantName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 - reserved bytes len=4 aka=ReservedBytes4 - developer ascii aka=Identity first=AsciiPrintable rest=AsciiPrintable len=1..4096 - supplements set len=0..MAX8 - Supplement rec - contentId union ContentRef - schema bytes len=32 wrapped aka=SchemaId tag=0 - genesis bytes len=32 wrapped aka=ContractId tag=1 - iface bytes len=32 wrapped aka=IfaceId tag=2 - ifaceImpl bytes len=32 wrapped aka=ImplId tag=3 - timestamp is I64 - creator ascii aka=Identity first=AsciiPrintable rest=AsciiPrintable len=1..4096 - annotations map len=0..MAX8 - key enum { - SupplSub itself=0 meta=1 global=2 owned=3 valency=4 assignment=5 genesis=6 transition=7 - extension=8 exception=9 - } - value map len=0..MAX8 aka=SupplMap - key union SupplItem - default is Unit tag=0 - typeNo is U16 wrapped tag=1 - typeName ascii wrapped aka=TypeName first=AlphaCapsLodash rest=AlphaNumLodash len=1..100 tag=2 - fieldName ascii wrapped aka=FieldName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 tag=3 - variantName ascii wrapped aka=VariantName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 tag=4 - value map len=0..MAX8 aka=Annotations - key ascii aka=AnnotationName first=AlphaCaps rest=AlphaNumDash len=1..MAX8 - value bytes len=0..MAX16 - types map len=0..MAX24 aka=TypeSystem - key bytes len=32 aka=SemId - value union TySemId - primitive is U8 wrapped aka=Primitive tag=0 - unicode is Unit tag=1 - enum set len=1..MAX8 wrapped aka=EnumVariants tag=2 - Variant rec - name ascii aka=VariantName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 - tag is U8 - union map len=0..MAX8 wrapped aka=UnionVariantsSemId tag=3 - key is U8 - value rec VariantInfoSemId - name ascii aka=VariantName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 - ty bytes len=32 aka=SemId - tuple list len=1..MAX8 wrapped aka=UnnamedFieldsSemId tag=4 - element bytes len=32 aka=SemId - struct list len=1..MAX8 wrapped aka=NamedFieldsSemId tag=5 - FieldSemId rec - name ascii aka=FieldName first=AlphaSmallLodash rest=AlphaNumLodash len=1..100 - ty bytes len=32 aka=SemId - array tuple tag=6 - _ bytes len=32 aka=SemId - _ is U16 - list tuple tag=7 - _ bytes len=32 aka=SemId - Sizing rec - min is U64 - max is U64 - set tuple tag=8 - _ bytes len=32 aka=SemId - Sizing rec - min is U64 - max is U64 - map tuple tag=9 - _ bytes len=32 aka=SemId - _ bytes len=32 aka=SemId - Sizing rec - min is U64 - max is U64 - scripts set len=0..1024 - Lib rec - isae set len=0..64 aka=IsaSeg - element ascii aka=IsaName first=AlphaCaps rest=AlphaCapsNum len=2..8 - code bytes len=0..MAX16 - data bytes len=0..MAX16 - libs set len=0..MAX8 aka=LibSeg - element bytes len=32 aka=LibId - attachments map len=0..MAX16 - key bytes len=32 aka=AttachId - value bytes len=0..MAX24 - signatures map len=0..MAX8 - key union ContentId - schema bytes len=32 wrapped aka=SchemaId tag=0 - genesis bytes len=32 wrapped aka=ContractId tag=1 - iface bytes len=32 wrapped aka=IfaceId tag=2 - ifaceImpl bytes len=32 wrapped aka=ImplId tag=3 - suppl bytes len=32 wrapped aka=SupplId tag=4 - value map len=1..10 aka=ContentSigs - key ascii aka=Identity first=AsciiPrintable rest=AsciiPrintable len=1..4096 - value bytes len=1..4096 aka=SigBlob +rec Consignmenttrue + enum version, ContainerVer, v0 0 + enum transfer, Bool, false 0, true 1 + map terminals, len 0..MAX16 + bytes key, len 32, aka BundleId + set value, len 1..MAX16, aka SecretSeals + bytes element, len 32, aka SecretSeal + rec genesis, Genesis + is ffv, U16, aka Ffv + bytes schemaId, len 32, aka SchemaId + is timestamp, I64 + ascii issuer, aka Identity, first AsciiPrintable, rest AsciiPrintable, len 1..4096 + enum chainNet, ChainNet, bitcoinMainnet 0, bitcoinTestnet3 1, bitcoinTestnet4 2, bitcoinSignet 3, bitcoinRegtest 4, liquidMainnet 5, liquidTestnet 6 + enum sealClosingStrategy, SealClosingStrategy, firstOpretOrTapret 0 + map metadata, len 0..MAX8, aka Metadata + is key, U16, aka MetaType + bytes value, len 0..MAX16, aka MetaValue + map globals, len 0..MAX8, aka GlobalState + is key, U16, aka GlobalStateType + list value, len 1..MAX16, aka GlobalValues + bytes element, len 0..MAX16, aka RevealedData + map assignments, len 0..MAX16, aka AssignmentsBlindSealTxid + is key, U16, aka AssignmentType + union value, TypedAssignsBlindSealTxid + list declarative, len 1..MAX16, wrapped, aka AssignVecAssignVoidStateBlindSealTxid, tag 0 + union AssignVoidStateBlindSealTxid + rec revealed, tag 0 + rec seal, BlindSealTxid + bytes txid, len 32, aka Txid + is vout, U32, aka Vout + is blinding, U64 + is state, Unit, aka VoidState + rec confidentialSeal, tag 1 + bytes seal, len 32, aka SecretSeal + is state, Unit, aka VoidState + list fungible, len 1..MAX16, wrapped, aka AssignVecAssignRevealedValueBlindSealTxid, tag 1 + union AssignRevealedValueBlindSealTxid + rec revealed, tag 0 + rec seal, BlindSealTxid + bytes txid, len 32, aka Txid + is vout, U32, aka Vout + is blinding, U64 + union state, FungibleState, aka RevealedValue + is bits64, U64, wrapped, tag 0 + rec confidentialSeal, tag 1 + bytes seal, len 32, aka SecretSeal + union state, FungibleState, aka RevealedValue + is bits64, U64, wrapped, tag 0 + list structured, len 1..MAX16, wrapped, aka AssignVecAssignRevealedDataBlindSealTxid, tag 2 + union AssignRevealedDataBlindSealTxid + rec revealed, tag 0 + rec seal, BlindSealTxid + bytes txid, len 32, aka Txid + is vout, U32, aka Vout + is blinding, U64 + bytes state, len 0..MAX16, aka RevealedData + rec confidentialSeal, tag 1 + bytes seal, len 32, aka SecretSeal + bytes state, len 0..MAX16, aka RevealedData + set bundles, len 0..MAX32 + rec WitnessBundle + union pubWitness, PubWitness + bytes txid, len 32, wrapped, aka Txid, tag 0 + rec tx, Tx, wrapped, tag 1 + is version, I32, aka TxVer + list inputs, len 0..MAX32 + rec TxIn + rec prevOutput, Outpoint + bytes txid, len 32, aka Txid + is vout, U32, aka Vout + bytes sigScript, len 0..MAX32, aka SigScript, aka ScriptBytes + is sequence, U32, aka SeqNo + list witness, len 0..MAX32, aka Witness + bytes element, len 0..MAX32, aka ByteStr + list outputs, len 0..MAX32 + rec TxOut + is value, U64, aka Sats + bytes scriptPubkey, len 0..MAX32, aka ScriptPubkey, aka ScriptBytes + is lockTime, U32, aka LockTime + rec bundle, TransitionBundle + map inputMap, len 1..MAX16 + is key, U32, aka Vout + set value, len 1..MAX16, aka InputOpids + bytes element, len 32, aka OpId + map knownTransitions, len 1..MAX16 + bytes key, len 32, aka OpId + rec value, Transition + is ffv, U16, aka Ffv + bytes contractId, len 32, aka ContractId + is nonce, U64 + is transitionType, U16, aka TransitionType + map metadata, len 0..MAX8, aka Metadata + is key, U16, aka MetaType + bytes value, len 0..MAX16, aka MetaValue + map globals, len 0..MAX8, aka GlobalState + is key, U16, aka GlobalStateType + list value, len 1..MAX16, aka GlobalValues + bytes element, len 0..MAX16, aka RevealedData + set inputs, len 1..MAX16, aka Inputs + rec Opout + bytes op, len 32, aka OpId + is ty, U16, aka AssignmentType + is no, U16 + map assignments, len 0..MAX16, aka AssignmentsBlindSealTxPtr + is key, U16, aka AssignmentType + union value, TypedAssignsBlindSealTxPtr + list declarative, len 1..MAX16, wrapped, aka AssignVecAssignVoidStateBlindSealTxPtr, tag 0 + union AssignVoidStateBlindSealTxPtr + rec revealed, tag 0 + rec seal, BlindSealTxPtr + union txid, TxPtr + is witnessTx, Unit, tag 0 + bytes txid, len 32, wrapped, aka Txid, tag 1 + is vout, U32, aka Vout + is blinding, U64 + is state, Unit, aka VoidState + rec confidentialSeal, tag 1 + bytes seal, len 32, aka SecretSeal + is state, Unit, aka VoidState + list fungible, len 1..MAX16, wrapped, aka AssignVecAssignRevealedValueBlindSealTxPtr, tag 1 + union AssignRevealedValueBlindSealTxPtr + rec revealed, tag 0 + rec seal, BlindSealTxPtr + union txid, TxPtr + is witnessTx, Unit, tag 0 + bytes txid, len 32, wrapped, aka Txid, tag 1 + is vout, U32, aka Vout + is blinding, U64 + union state, FungibleState, aka RevealedValue + is bits64, U64, wrapped, tag 0 + rec confidentialSeal, tag 1 + bytes seal, len 32, aka SecretSeal + union state, FungibleState, aka RevealedValue + is bits64, U64, wrapped, tag 0 + list structured, len 1..MAX16, wrapped, aka AssignVecAssignRevealedDataBlindSealTxPtr, tag 2 + union AssignRevealedDataBlindSealTxPtr + rec revealed, tag 0 + rec seal, BlindSealTxPtr + union txid, TxPtr + is witnessTx, Unit, tag 0 + bytes txid, len 32, wrapped, aka Txid, tag 1 + is vout, U32, aka Vout + is blinding, U64 + bytes state, len 0..MAX16, aka RevealedData + rec confidentialSeal, tag 1 + bytes seal, len 32, aka SecretSeal + bytes state, len 0..MAX16, aka RevealedData + bytes some, len 64, option, wrapped, aka Signature, tag 1 + rec schema, Schema + is ffv, U16, aka Ffv + ascii name, aka TypeName, first AlphaCapsLodash, rest AlphaNumLodash, len 1..100 + map metaTypes, len 0..MAX8 + is key, U16, aka MetaType + rec value, MetaDetails + bytes semId, len 32, aka SemId + ascii name, aka FieldName, first AlphaSmallLodash, rest AlphaNumLodash, len 1..100 + map globalTypes, len 0..MAX8 + is key, U16, aka GlobalStateType + rec value, GlobalDetails + rec globalStateSchema, GlobalStateSchema + bytes semId, len 32, aka SemId + is maxItems, U24 + ascii name, aka FieldName, first AlphaSmallLodash, rest AlphaNumLodash, len 1..100 + map ownedTypes, len 0..MAX8 + is key, U16, aka AssignmentType + rec value, AssignmentDetails + union ownedStateSchema, OwnedStateSchema + is declarative, Unit, tag 0 + enum fungible, FungibleType, wrapped, unsigned64Bit 8, tag 1 + bytes structured, len 32, wrapped, aka SemId, tag 2 + ascii name, aka FieldName, first AlphaSmallLodash, rest AlphaNumLodash, len 1..100 + is defaultTransition, U16, aka TransitionType + rec genesis, GenesisSchema + set metadata, len 0..MAX8 + is element, U16, aka MetaType + map globals, len 0..MAX8 + is key, U16, aka GlobalStateType + rec value, Occurrences + is min, U16 + is max, U16 + map assignments, len 0..MAX8 + is key, U16, aka AssignmentType + rec value, Occurrences + is min, U16 + is max, U16 + rec some, LibSite, option, wrapped, tag 1 + bytes lib, len 32, aka LibId + is pos, U16 + map transitions, len 0..MAX8 + is key, U16, aka TransitionType + rec value, TransitionDetails + rec transitionSchema, TransitionSchema + set metadata, len 0..MAX8 + is element, U16, aka MetaType + map globals, len 0..MAX8 + is key, U16, aka GlobalStateType + rec value, Occurrences + is min, U16 + is max, U16 + map inputs, len 0..MAX8 + is key, U16, aka AssignmentType + rec value, Occurrences + is min, U16 + is max, U16 + map assignments, len 0..MAX8 + is key, U16, aka AssignmentType + rec value, Occurrences + is min, U16 + is max, U16 + rec some, LibSite, option, wrapped, tag 1 + bytes lib, len 32, aka LibId + is pos, U16 + ascii name, aka FieldName, first AlphaSmallLodash, rest AlphaNumLodash, len 1..100 + is some, U16, option, wrapped, aka AssignmentType, tag 1 + map types, len 0..MAX24, aka TypeSystem + bytes key, len 32, aka SemId + union value, TySemId + is primitive, U8, wrapped, aka Primitive, tag 0 + is unicode, Unit, tag 1 + set enum, len 1..MAX8, wrapped, aka EnumVariants, tag 2 + rec Variant + ascii name, aka VariantName, first AlphaSmallLodash, rest AlphaNumLodash, len 1..100 + is tag, U8 + map union, len 0..MAX8, wrapped, aka UnionVariantsSemId, tag 3 + is key, U8 + rec value, VariantInfoSemId + ascii name, aka VariantName, first AlphaSmallLodash, rest AlphaNumLodash, len 1..100 + bytes ty, len 32, aka SemId + list tuple, len 1..MAX8, wrapped, aka UnnamedFieldsSemId, tag 4 + bytes element, len 32, aka SemId + list struct, len 1..MAX8, wrapped, aka NamedFieldsSemId, tag 5 + rec FieldSemId + ascii name, aka FieldName, first AlphaSmallLodash, rest AlphaNumLodash, len 1..100 + bytes ty, len 32, aka SemId + tuple array, tag 6 + bytes _, len 32, aka SemId + is _, U16 + tuple list, tag 7 + bytes _, len 32, aka SemId + rec Sizing + is min, U64 + is max, U64 + tuple set, tag 8 + bytes _, len 32, aka SemId + rec Sizing + is min, U64 + is max, U64 + tuple map, tag 9 + bytes _, len 32, aka SemId + bytes _, len 32, aka SemId + rec Sizing + is min, U64 + is max, U64 + set scripts, len 0..1024 + rec Lib + set isae, len 0..64, aka IsaSeg + ascii element, aka IsaName, first AlphaCaps, rest AlphaCapsNum, len 2..8 + bytes code, len 0..MAX16 + bytes data, len 0..MAX16 + set libs, len 0..MAX8, aka LibSeg + bytes element, len 32, aka LibId diff --git a/stl/src/main.rs b/stl/src/main.rs index 3cf24119..c8576d2f 100644 --- a/stl/src/main.rs +++ b/stl/src/main.rs @@ -26,8 +26,8 @@ use std::io::Write; use commit_verify::CommitmentLayout; use rgbstd::containers::Transfer; use rgbstd::stl::{ - aluvm_stl, bp_core_stl, bp_tx_stl, commit_verify_stl, rgb_commit_stl, rgb_contract_stl, - rgb_logic_stl, rgb_std_stl, rgb_storage_stl, + aluvm_stl, bp_consensus_stl, bp_core_stl, bp_tx_stl, commit_verify_stl, rgb_commit_stl, + rgb_contract_stl, rgb_logic_stl, rgb_std_stl, rgb_storage_stl, }; use strict_types::stl::{std_stl, strict_types_stl}; use strict_types::{parse_args, StlFormat, SystemBuilder}; @@ -50,7 +50,7 @@ fn main() { "0.11.0", Some( " - Description: Types for writing RGB contracts and interfaces + Description: Types for writing RGB schemata Author: Dr Maxim Orlovsky Copyright (C) 2023-2024 LNP/BP Standards Association. All rights reserved. License: Apache-2.0", @@ -107,6 +107,7 @@ fn main() { let rgb_commit = rgb_commit_stl(); let rgb_logic = rgb_logic_stl(); let tx = bp_tx_stl(); + let consensus = bp_consensus_stl(); let bp = bp_core_stl(); let cv = commit_verify_stl(); let st = strict_types_stl(); @@ -121,6 +122,8 @@ fn main() { .unwrap() .import(vm) .unwrap() + .import(consensus) + .unwrap() .import(bp) .unwrap() .import(tx)