diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1e23eae6..d4f59b61 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,12 +5,12 @@ on: branches: - master tags: - - 'v[0-9]+\.*' + - 'v[0-9]+.*' pull_request: branches: - master - develop - - 'v[0-9]+.[0-9]+' + - 'v[0-9]+.?*' env: CARGO_TERM_COLOR: always @@ -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/codecov.yml b/.github/workflows/codecov.yml index 3711d7ad..9ba863f5 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -5,12 +5,12 @@ on: branches: - master tags: - - 'v[0-9]+\.*' + - 'v[0-9]+.*' pull_request: branches: - master - develop - - 'v[0-9]+.[0-9]+' + - 'v[0-9]+.?*' env: CARGO_TERM_COLOR: always diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index ab5249d1..91f36b69 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -5,7 +5,7 @@ on: branches: - master - develop - - 'v[0-9]+.[0-9]+' + - 'v[0-9]+.?*' env: CARGO_TERM_COLOR: always diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9821d00e..a0982385 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,12 +5,12 @@ on: branches: - master tags: - - 'v[0-9]+\.*' + - 'v[0-9]+.*' pull_request: branches: - master - develop - - 'v[0-9]+.[0-9]+' + - 'v[0-9]+.?*' env: CARGO_TERM_COLOR: always @@ -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 cb5520c3..1af4d602 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,15 +4,15 @@ version = 3 [[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.2", "half", "paste", "ripemd", @@ -25,16 +25,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", @@ -101,9 +101,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 +122,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 +135,15 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[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", @@ -167,22 +167,32 @@ dependencies = [ ] [[package]] -name = "bitcoin-private" -version = "0.1.0" +name = "bitcoin-io" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73290177011694f38ec25e165d0387ab7ea749a4b81cd4c80dae5988229f7a57" +checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" + +[[package]] +name = "bitcoin_hashes" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" +dependencies = [ + "bitcoin-io", + "hex-conservative", +] [[package]] name = "bitflags" -version = "2.6.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[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", @@ -202,9 +212,9 @@ dependencies = [ [[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", @@ -217,16 +227,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.2", "serde", "single_use_seals", "strict_encoding", @@ -236,9 +247,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", @@ -251,16 +262,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", @@ -268,21 +279,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - -[[package]] -name = "byteorder" -version = "1.5.0" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "cc" -version = "1.1.16" +version = "1.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9d013ecb737093c0e86b151a7b837993cf9ec6c502946cfb44bedc392421e0b" +checksum = "04da6a0d40b948dfc4fa8f5bbf402b0fc1a64a28dbf7d12ffd683550f2c1b63a" dependencies = [ "shlex", ] @@ -295,23 +300,23 @@ 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", "js-sys", "num-traits", "wasm-bindgen", - "windows-targets", + "windows-link", ] [[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", @@ -322,13 +327,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", @@ -337,16 +342,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" @@ -361,18 +356,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" @@ -396,9 +391,9 @@ dependencies = [ [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "generic-array" @@ -412,22 +407,36 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "js-sys", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", "wasm-bindgen", ] [[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", @@ -435,9 +444,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" [[package]] name = "heck" @@ -445,16 +454,26 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hex-conservative" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" +dependencies = [ + "arrayvec", +] + [[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", ] @@ -470,40 +489,42 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.5.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", "hashbrown", + "serde", ] [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[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 = "libc" -version = "0.2.158" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[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" @@ -519,9 +540,9 @@ 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", @@ -544,9 +565,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" @@ -556,31 +577,37 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[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 = "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" @@ -588,8 +615,18 @@ 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" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", ] [[package]] @@ -599,7 +636,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "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]] @@ -608,12 +655,21 @@ 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.2", ] [[package]] name = "rgb-core" -version = "0.11.0-beta.8" +version = "0.11.1-alpha.3+unreviewed" dependencies = [ "aluvm", "amplify", @@ -621,10 +677,10 @@ dependencies = [ "bp-core", "chrono", "commit_verify", - "getrandom", + "getrandom 0.3.2", "mime", - "rand", - "secp256k1-zkp", + "rand 0.9.1", + "secp256k1", "serde", "single_use_seals", "strict_encoding", @@ -642,11 +698,17 @@ dependencies = [ "digest", ] +[[package]] +name = "rustversion" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" + [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "same-file" @@ -657,80 +719,52 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - [[package]] name = "secp256k1" -version = "0.29.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e0cc0f1cf93f4969faf3ea1c7d8a9faed25918d96affa959720823dfe86d4f3" +checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" dependencies = [ - "rand", + "bitcoin_hashes", + "rand 0.8.5", "secp256k1-sys", "serde", ] [[package]] name = "secp256k1-sys" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1433bd67156263443f14d603720b082dd3121779323fce20cba2aa07b874bc1b" -dependencies = [ - "cc", -] - -[[package]] -name = "secp256k1-zkp" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52a44aed3002b5ae975f8624c5df3a949cfbf00479e18778b6058fcd213b76e3" -dependencies = [ - "bitcoin-private", - "rand", - "secp256k1", - "secp256k1-zkp-sys", - "serde", -] - -[[package]] -name = "secp256k1-zkp-sys" -version = "0.10.0" +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.127" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "itoa", "memchr", @@ -740,9 +774,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", ] @@ -772,9 +806,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", @@ -789,21 +823,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", @@ -811,9 +844,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", @@ -824,14 +857,13 @@ 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", "serde", "serde_json", @@ -866,9 +898,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", @@ -877,29 +909,29 @@ 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 = "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", @@ -909,37 +941,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", "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" @@ -955,9 +994,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", @@ -979,49 +1018,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", @@ -1029,33 +1078,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", @@ -1063,20 +1113,20 @@ 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", @@ -1093,11 +1143,61 @@ dependencies = [ [[package]] name = "windows-core" -version = "0.52.0" +version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" dependencies = [ - "windows-targets", + "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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +dependencies = [ + "windows-link", ] [[package]] @@ -1175,30 +1275,38 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.18" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +checksum = "9e27d6ad3dac991091e4d35de9ba2d2d00647c5d0fc26c5496dee55984ae111b" 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", ] diff --git a/Cargo.toml b/Cargo.toml index efaf1d9b..1128950c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rgb-core" -version = "0.11.0-beta.8" +version = "0.11.1-alpha.3+unreviewed" authors = ["Dr Maxim Orlovsky "] description = "RGB Core Library: confidential & scalable smart contracts on Bitcoin & Lightning (consensus layer)" repository = "https://github.com/RGB-WG/rgb-core" @@ -10,7 +10,7 @@ categories = ["cryptography::cryptocurrencies"] readme = "README.md" license = "Apache-2.0" edition = "2021" -rust-version = "1.76.0" +rust-version = "1.81.0" exclude = [".github"] [lib] @@ -22,15 +22,15 @@ name = "rgbcore-stl" required-features = ["stl"] [dependencies] -amplify = { version = "~4.7.0", features = ["rand"] } -baid64 = "~0.2.2" -strict_encoding = "~2.7.0" -strict_types = { version = "~2.7.0", features = ["armor"] } -aluvm = { version = "~0.11.0-beta.8", features = ["std", "ascii-armor"] } -commit_verify = { version = "~0.11.0-beta.8", features = ["rand", "derive"] } -single_use_seals = "~0.11.0-beta.8" -bp-core = { version = "~0.11.0-beta.8" } -secp256k1-zkp = { version = "0.11.0", features = ["rand", "rand-std", "global-context"] } # TODO: Update version before the release +amplify = { version = "~4.8.0", features = ["rand"] } +baid64 = "~0.4.1" +strict_encoding = "~2.8.2" +strict_types = { version = "~2.8.3", features = ["armor"] } +aluvm = { version = "~0.11.1-alpha.2", features = ["std", "ascii-armor"] } +commit_verify = { version = "0.11.1-alpha.2", features = ["rand", "derive"] } +single_use_seals = "~0.11.1-alpha.2" +bp-core = { version = "~0.11.1-alpha.2" } +secp256k1 = { version = "0.30.0", features = ["global-context"] } mime = "~0.3.17" serde_crate = { package = "serde", version = "1", features = ["derive"], optional = true } chrono = "0.4.38" @@ -47,13 +47,13 @@ serde = [ "commit_verify/serde", "bp-core/serde", "aluvm/serde", - "secp256k1-zkp/serde" + "secp256k1/serde" ] [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 05b4196b..62511887 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/doc/Commitments.md b/doc/Commitments.md index 9eb90d18..69baa643 100644 --- a/doc/Commitments.md +++ b/doc/Commitments.md @@ -141,7 +141,7 @@ Here are more details on each type of the commitments: | Commitment ID | Produced by | Procedure | Tag URN suffix(1) | |----------------------|--------------------------------------|------------------------------------------------------------------------------------------------|-------------------------------| | `SchemaID` | `RootSchema`, `SubSchema` | strict serialization | `rgb:schema#2024-02-03` | -| `OpId`, `ContractId` | `Genesis`, `Transition`, `Extension` | nested commitments with concealing, merklization etc via intermediate `OpCommitment` structure | `rgb:operation#2024-02-03` | +| `OpId`, `ContractId` | `Genesis`, `Transition` | nested commitments with concealing, merklization etc via intermediate `OpCommitment` structure | `rgb:operation#2024-02-03` | | `BundleId` | `TransitionBundle` | conceal and partial strict serialization | `rgb:bundle#2024-02-03` | | `SecretSeal` | `BlindSeal` | strict serialization | `seals:secret#2024-02-03` | | `ConcealedData` | `RevealedData` | strict serialization | `rgb:state-data#2024-02-12` | @@ -187,8 +187,8 @@ code. ### Operation ID and Contract ID -Operation id is represented by a `OpId` type and produced for `Genesis`, -`Transition` and `Extension` types via custom algorithm, which first creates a +Operation id is represented by a `OpId` type and produced for `Genesis` and +`Transition` types via custom algorithm, which first creates a dedicated `OpCommitment` structure, and strict-serializes it to hasher, initialized with `urn:lnp-bp:rgb:operation#2024-02-03` hash tag. @@ -201,10 +201,9 @@ assignments are concealed before the merklization, and range proofs are removed from the commitment, such that an aggregation of the historical proofs can be applied without changing the operation ids. -To ensure succinctness, other types of collections, such as redeemed and -defined valencies and list of alternate layer 1 in genesis are not merklized -and strict-serialized producing `StrictHash`, which participates in the final -`OpCommitment` structure. +To ensure succinctness, other types of collections, such as metadata, are not +merklized and strict-serialized producing `StrictHash`, which participates in +the final `OpCommitment` structure. ```mermaid flowchart LR @@ -215,14 +214,11 @@ flowchart LR Globals -- Merklize --> OpCommitment Inputs -- Merklize --> OpCommitment Assignments -- "Conceal\n + Merklize" --> OpCommitment - Redeemed -- StrictHash --> OpCommitment - Valencies -- StrictHash --> OpCommitment end subgraph "Genesis" schemaId --> BaseCommitment - testnet --> BaseCommitment - altLayers1 -- StrictHash --> BaseCommitment + chainNet --> BaseCommitment end subgraph "Transition" @@ -230,11 +226,6 @@ flowchart LR transitionType --> TypeCommitment end - subgraph "Extension" - ecid[contractId] --> TypeCommitment - extensionType --> TypeCommitment - end - BaseCommitment --> TypeCommitment OpCommitment -- hash --> OpId diff --git a/src/bin/rgbcore-stl.rs b/src/bin/rgbcore-stl.rs index 45908e92..9aac2db0 100644 --- a/src/bin/rgbcore-stl.rs +++ b/src/bin/rgbcore-stl.rs @@ -27,7 +27,7 @@ use aluvm::stl::aluvm_stl; use bp::stl::bp_core_stl; use commit_verify::stl::commit_verify_stl; use commit_verify::CommitmentLayout; -use rgbcore::stl::bp_tx_stl; +use rgbcore::stl::{bp_consensus_stl, bp_tx_stl}; use rgbcore::{Schema, Transition, TransitionBundle}; use strict_types::stl::{std_stl, strict_types_stl}; use strict_types::typelib::parse_args; @@ -70,6 +70,7 @@ fn main() { .expect("unable to write to the file"); let std = std_stl(); + let consensus = bp_consensus_stl(); let tx = bp_tx_stl(); let bp = bp_core_stl(); let cv = commit_verify_stl(); @@ -85,6 +86,8 @@ fn main() { .unwrap() .import(bp) .unwrap() + .import(consensus) + .unwrap() .import(tx) .unwrap() .import(cv) @@ -138,11 +141,11 @@ Transition vesper lexicon=types+commitments let tt = sys.type_tree("RGBCommit.Transition").unwrap(); writeln!(file, "{tt}").unwrap(); - let mut file = fs::File::create(format!("{dir}/AnchoredBundle.vesper")).unwrap(); + let mut file = fs::File::create(format!("{dir}/TransitionBundle.vesper")).unwrap(); writeln!( file, "{{- - Description: RGB Anchored Bundles + Description: RGB Transition Bundles Author: Dr Maxim Orlovsky Copyright (C) 2024 LNP/BP Standards Association. All rights reserved. License: Apache-2.0 diff --git a/src/lib.rs b/src/lib.rs index 22194544..1945cdf0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,10 +44,9 @@ pub mod vm; pub mod stl; pub mod prelude { - pub use commit_verify::ReservedBytes; + pub use bp::Txid; pub use operation::*; pub use schema::*; - pub use vm::XWitnessId; #[cfg(feature = "stl")] pub use super::stl; @@ -130,6 +129,3 @@ macro_rules! impl_serde_baid64 { } }; } - -// TODO: Validate strict type data -// TODO: Add parsed global and structured state to the ContractState diff --git a/src/operation/assignments.rs b/src/operation/assignments.rs index 7e719423..b7e55034 100644 --- a/src/operation/assignments.rs +++ b/src/operation/assignments.rs @@ -25,17 +25,45 @@ use core::fmt::Debug; use std::collections::{btree_map, BTreeSet}; use std::hash::Hash; -use amplify::confinement::{Confined, SmallVec, TinyOrdMap}; -use commit_verify::{Conceal, ReservedBytes}; -use strict_encoding::{StrictDumb, StrictEncode}; +use amplify::confinement::{Confined, NonEmptyVec, SmallOrdMap, U16}; +use commit_verify::Conceal; +use strict_encoding::{StrictDecode, StrictDumb, StrictEncode}; use super::ExposedState; use crate::operation::seal::GenesisSeal; use crate::{ - AssignmentType, ExposedSeal, GraphSeal, RevealedAttach, RevealedData, RevealedValue, - SecretSeal, StateType, VoidState, XChain, LIB_NAME_RGB_COMMIT, + AssignmentType, ExposedSeal, GraphSeal, RevealedData, RevealedValue, SecretSeal, StateType, + VoidState, LIB_NAME_RGB_COMMIT, }; +#[derive(Wrapper, WrapperMut, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, From)] +#[wrapper(Deref)] +#[wrapper_mut(DerefMut)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_RGB_COMMIT, dumb = Self(NonEmptyVec::with(A::strict_dumb())))] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde( + crate = "serde_crate", + transparent, + bound = "A: serde::Serialize + serde::de::DeserializeOwned" + ) +)] +pub struct AssignVec(NonEmptyVec) +where A: StrictDumb + StrictEncode + StrictDecode; + +impl AssignVec { + pub fn with(vec: NonEmptyVec) -> Self { Self(vec) } +} + +impl IntoIterator for AssignVec { + type Item = A; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } +} + #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Display, Error)] #[display(doc_comments)] /// the requested data are not present. @@ -44,55 +72,27 @@ pub struct UnknownDataError; pub type AssignRights = Assign; pub type AssignFungible = Assign; pub type AssignData = Assign; -pub type AssignAttach = Assign; /// State data are assigned to a seal definition, which means that they are /// owned by a person controlling spending of the seal UTXO, unless the seal /// is closed, indicating that a transfer of ownership had taken place -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] #[strict_type( lib = LIB_NAME_RGB_COMMIT, tags = custom, - dumb = { Self::Confidential { seal: strict_dumb!(), state: strict_dumb!(), lock: default!() } } + dumb = { Self::Revealed { seal: strict_dumb!(), state: strict_dumb!() } } )] #[cfg_attr( feature = "serde", derive(Serialize, Deserialize), - serde( - crate = "serde_crate", - rename_all = "camelCase", - untagged, - bound = "State::Confidential: serde::Serialize + serde::de::DeserializeOwned, State: \ - serde::Serialize + serde::de::DeserializeOwned, Seal: serde::Serialize + \ - serde::de::DeserializeOwned" - ) + serde(crate = "serde_crate", rename_all = "camelCase", untagged) )] pub enum Assign { #[strict_type(tag = 0x00)] - Confidential { - seal: XChain, - state: State::Confidential, - lock: ReservedBytes<2, 0>, - }, - #[strict_type(tag = 0x03)] - Revealed { - seal: XChain, - state: State, - lock: ReservedBytes<2, 0>, - }, - #[strict_type(tag = 0x02)] - ConfidentialSeal { - seal: XChain, - state: State, - lock: ReservedBytes<2, 0>, - }, + Revealed { seal: Seal, state: State }, #[strict_type(tag = 0x01)] - ConfidentialState { - seal: XChain, - state: State::Confidential, - lock: ReservedBytes<2, 0>, - }, + ConfidentialSeal { seal: SecretSeal, state: State }, } // Consensus-critical! @@ -110,118 +110,67 @@ impl Ord for Assign { } } -impl PartialEq for Assign { - fn eq(&self, other: &Self) -> bool { - self.to_confidential_seal() == other.to_confidential_seal() - && self.to_confidential_state() == other.to_confidential_state() - } -} - -impl Eq for Assign {} - impl Assign { - pub fn revealed(seal: XChain, state: State) -> Self { - Assign::Revealed { - seal, - state, - lock: default!(), - } - } + pub fn revealed(seal: Seal, state: State) -> Self { Assign::Revealed { seal, state } } - pub fn with_seal_replaced(assignment: &Self, seal: XChain) -> Self { + pub fn with_seal_replaced(assignment: &Self, seal: Seal) -> Self { match assignment { - Assign::Confidential { - seal: _, - state, - lock, - } - | Assign::ConfidentialState { - seal: _, - state, - lock, - } => Assign::ConfidentialState { - seal, - state: *state, - lock: *lock, - }, - Assign::ConfidentialSeal { - seal: _, - state, - lock, - } - | Assign::Revealed { - seal: _, - state, - lock, - } => Assign::Revealed { - seal, - state: state.clone(), - lock: *lock, - }, - } - } - - pub fn to_confidential_seal(&self) -> XChain { - match self { - Assign::Revealed { seal, .. } | Assign::ConfidentialState { seal, .. } => { - seal.conceal() + Assign::ConfidentialSeal { seal: _, state } | Assign::Revealed { seal: _, state } => { + Assign::Revealed { + seal, + state: state.clone(), + } } - Assign::Confidential { seal, .. } | Assign::ConfidentialSeal { seal, .. } => *seal, } } - pub fn revealed_seal(&self) -> Option> { + pub fn to_confidential_seal(&self) -> SecretSeal { match self { - Assign::Revealed { seal, .. } | Assign::ConfidentialState { seal, .. } => Some(*seal), - Assign::Confidential { .. } | Assign::ConfidentialSeal { .. } => None, + Assign::Revealed { seal, .. } => seal.conceal(), + Assign::ConfidentialSeal { seal, .. } => *seal, } } - pub fn to_confidential_state(&self) -> State::Confidential { + pub fn revealed_seal(&self) -> Option { match self { - Assign::Revealed { state, .. } | Assign::ConfidentialSeal { state, .. } => { - state.conceal() - } - Assign::Confidential { state, .. } | Assign::ConfidentialState { state, .. } => *state, + Assign::Revealed { seal, .. } => Some(*seal), + Assign::ConfidentialSeal { .. } => None, } } - pub fn as_revealed_state(&self) -> Option<&State> { + pub fn as_revealed_state(&self) -> &State { match self { - Assign::Revealed { state, .. } | Assign::ConfidentialSeal { state, .. } => Some(state), - Assign::Confidential { .. } | Assign::ConfidentialState { .. } => None, + Assign::Revealed { state, .. } | Assign::ConfidentialSeal { state, .. } => state, } } - pub fn as_revealed_state_mut(&mut self) -> Option<&mut State> { + pub fn as_revealed_state_mut(&mut self) -> &mut State { match self { - Assign::Revealed { state, .. } | Assign::ConfidentialSeal { state, .. } => Some(state), - Assign::Confidential { .. } | Assign::ConfidentialState { .. } => None, + Assign::Revealed { state, .. } | Assign::ConfidentialSeal { state, .. } => state, } } - pub fn into_revealed_state(self) -> Option { + pub fn into_revealed_state(self) -> State { match self { - Assign::Revealed { state, .. } | Assign::ConfidentialSeal { state, .. } => Some(state), - Assign::Confidential { .. } | Assign::ConfidentialState { .. } => None, + Assign::Revealed { state, .. } | Assign::ConfidentialSeal { state, .. } => state, } } - pub fn as_revealed(&self) -> Option<(&XChain, &State)> { + pub fn as_revealed(&self) -> Option<(&Seal, &State)> { match self { Assign::Revealed { seal, state, .. } => Some((seal, state)), _ => None, } } - pub fn to_revealed(&self) -> Option<(XChain, State)> { + pub fn to_revealed(&self) -> Option<(Seal, State)> { match self { Assign::Revealed { seal, state, .. } => Some((*seal, state.clone())), _ => None, } } - pub fn into_revealed(self) -> Option<(XChain, State)> { + pub fn into_revealed(self) -> Option<(Seal, State)> { match self { Assign::Revealed { seal, state, .. } => Some((seal, state)), _ => None, @@ -236,22 +185,11 @@ where Self: Clone fn conceal(&self) -> Self::Concealed { match self { - Assign::Confidential { .. } => self.clone(), - Assign::ConfidentialState { seal, state, lock } => Self::Confidential { + Assign::Revealed { seal, state } => Self::ConfidentialSeal { seal: seal.conceal(), - state: *state, - lock: *lock, - }, - Assign::Revealed { seal, state, lock } => Self::Confidential { - seal: seal.conceal(), - state: state.conceal(), - lock: *lock, - }, - Assign::ConfidentialSeal { seal, state, lock } => Self::Confidential { - seal: *seal, - state: state.conceal(), - lock: *lock, + state: state.clone(), }, + Assign::ConfidentialSeal { .. } => self.clone(), } } } @@ -259,25 +197,13 @@ where Self: Clone impl Assign { pub fn transmutate_seals(&self) -> Assign { match self { - Assign::Confidential { seal, state, lock } => Assign::Confidential { - seal: *seal, - state: *state, - lock: *lock, - }, - Assign::ConfidentialSeal { seal, state, lock } => Assign::ConfidentialSeal { + Assign::ConfidentialSeal { seal, state } => Assign::ConfidentialSeal { seal: *seal, state: state.clone(), - lock: *lock, }, - Assign::Revealed { seal, state, lock } => Assign::Revealed { + Assign::Revealed { seal, state } => Assign::Revealed { seal: seal.transmutate(), state: state.clone(), - lock: *lock, - }, - Assign::ConfidentialState { seal, state, lock } => Assign::ConfidentialState { - seal: seal.transmutate(), - state: *state, - lock: *lock, }, } } @@ -298,15 +224,12 @@ impl Assign { ) )] pub enum TypedAssigns { - // TODO: Consider using non-empty variants #[strict_type(tag = 0x00)] - Declarative(SmallVec>), + Declarative(AssignVec>), #[strict_type(tag = 0x01)] - Fungible(SmallVec>), + Fungible(AssignVec>), #[strict_type(tag = 0x02)] - Structured(SmallVec>), - #[strict_type(tag = 0xFF)] - Attachment(SmallVec>), + Structured(AssignVec>), } impl Conceal for TypedAssigns { @@ -315,23 +238,18 @@ impl Conceal for TypedAssigns { match self { TypedAssigns::Declarative(s) => { let concealed_iter = s.iter().map(AssignRights::::conceal); - let inner = SmallVec::try_from_iter(concealed_iter).expect("same size"); - TypedAssigns::Declarative(inner) + let inner = NonEmptyVec::try_from_iter(concealed_iter).expect("same size"); + TypedAssigns::Declarative(AssignVec::with(inner)) } TypedAssigns::Fungible(s) => { let concealed_iter = s.iter().map(AssignFungible::::conceal); - let inner = SmallVec::try_from_iter(concealed_iter).expect("same size"); - TypedAssigns::Fungible(inner) + let inner = NonEmptyVec::try_from_iter(concealed_iter).expect("same size"); + TypedAssigns::Fungible(AssignVec::with(inner)) } TypedAssigns::Structured(s) => { let concealed_iter = s.iter().map(AssignData::::conceal); - let inner = SmallVec::try_from_iter(concealed_iter).expect("same size"); - TypedAssigns::Structured(inner) - } - TypedAssigns::Attachment(s) => { - let concealed_iter = s.iter().map(AssignAttach::::conceal); - let inner = SmallVec::try_from_iter(concealed_iter).expect("same size"); - TypedAssigns::Attachment(inner) + let inner = NonEmptyVec::try_from_iter(concealed_iter).expect("same size"); + TypedAssigns::Structured(AssignVec::with(inner)) } } } @@ -343,7 +261,6 @@ impl TypedAssigns { TypedAssigns::Declarative(set) => set.is_empty(), TypedAssigns::Fungible(set) => set.is_empty(), TypedAssigns::Structured(set) => set.is_empty(), - TypedAssigns::Attachment(set) => set.is_empty(), } } @@ -352,7 +269,6 @@ impl TypedAssigns { TypedAssigns::Declarative(set) => set.len_u16(), TypedAssigns::Fungible(set) => set.len_u16(), TypedAssigns::Structured(set) => set.len_u16(), - TypedAssigns::Attachment(set) => set.len_u16(), } } @@ -362,7 +278,6 @@ impl TypedAssigns { TypedAssigns::Declarative(_) => StateType::Void, TypedAssigns::Fungible(_) => StateType::Fungible, TypedAssigns::Structured(_) => StateType::Structured, - TypedAssigns::Attachment(_) => StateType::Attachment, } } @@ -375,9 +290,6 @@ impl TypedAssigns { #[inline] pub fn is_structured(&self) -> bool { matches!(self, TypedAssigns::Structured(_)) } - #[inline] - pub fn is_attachment(&self) -> bool { matches!(self, TypedAssigns::Attachment(_)) } - #[inline] pub fn as_declarative(&self) -> &[AssignRights] { match self { @@ -403,15 +315,7 @@ impl TypedAssigns { } #[inline] - pub fn as_attachment(&self) -> &[AssignAttach] { - match self { - TypedAssigns::Attachment(set) => set, - _ => Default::default(), - } - } - - #[inline] - pub fn as_declarative_mut(&mut self) -> Option<&mut SmallVec>> { + pub fn as_declarative_mut(&mut self) -> Option<&mut NonEmptyVec, U16>> { match self { TypedAssigns::Declarative(set) => Some(set), _ => None, @@ -419,7 +323,7 @@ impl TypedAssigns { } #[inline] - pub fn as_fungible_mut(&mut self) -> Option<&mut SmallVec>> { + pub fn as_fungible_mut(&mut self) -> Option<&mut NonEmptyVec, U16>> { match self { TypedAssigns::Fungible(set) => Some(set), _ => None, @@ -427,25 +331,17 @@ impl TypedAssigns { } #[inline] - pub fn as_structured_mut(&mut self) -> Option<&mut SmallVec>> { + pub fn as_structured_mut(&mut self) -> Option<&mut NonEmptyVec, U16>> { match self { TypedAssigns::Structured(set) => Some(set), _ => None, } } - #[inline] - pub fn as_attachment_mut(&mut self) -> Option<&mut SmallVec>> { - match self { - TypedAssigns::Attachment(set) => Some(set), - _ => None, - } - } - /// If seal definition does not exist, returns [`UnknownDataError`]. If the /// seal is confidential, returns `Ok(None)`; otherwise returns revealed /// seal data packed as `Ok(Some(`[`Seal`]`))` - pub fn revealed_seal_at(&self, index: u16) -> Result>, UnknownDataError> { + pub fn revealed_seal_at(&self, index: u16) -> Result, UnknownDataError> { Ok(match self { TypedAssigns::Declarative(vec) => vec .get(index as usize) @@ -459,14 +355,35 @@ impl TypedAssigns { .get(index as usize) .ok_or(UnknownDataError)? .revealed_seal(), - TypedAssigns::Attachment(vec) => vec - .get(index as usize) - .ok_or(UnknownDataError)? - .revealed_seal(), }) } - pub fn to_confidential_seals(&self) -> Vec> { + pub fn reveal_seal(&mut self, seal: Seal) { + fn reveal( + vec: &mut NonEmptyVec, U16>, + revealed: Seal, + ) { + for assign in vec.iter_mut() { + match assign { + Assign::ConfidentialSeal { seal, state } if *seal == revealed.conceal() => { + *assign = Assign::Revealed { + seal: revealed, + state: state.clone(), + } + } + _ => {} + } + } + } + + match self { + TypedAssigns::Declarative(v) => reveal(v, seal), + TypedAssigns::Fungible(v) => reveal(v, seal), + TypedAssigns::Structured(v) => reveal(v, seal), + } + } + + pub fn to_confidential_seals(&self) -> Vec { match self { TypedAssigns::Declarative(s) => s .iter() @@ -480,17 +397,10 @@ impl TypedAssigns { .iter() .map(AssignData::::to_confidential_seal) .collect(), - TypedAssigns::Attachment(s) => s - .iter() - .map(AssignAttach::::to_confidential_seal) - .collect(), } } - pub fn as_structured_state_at( - &self, - index: u16, - ) -> Result, UnknownDataError> { + pub fn as_structured_state_at(&self, index: u16) -> Result<&RevealedData, UnknownDataError> { match self { TypedAssigns::Structured(vec) => Ok(vec .get(index as usize) @@ -500,10 +410,7 @@ impl TypedAssigns { } } - pub fn as_fungible_state_at( - &self, - index: u16, - ) -> Result, UnknownDataError> { + pub fn as_fungible_state_at(&self, index: u16) -> Result<&RevealedValue, UnknownDataError> { match self { TypedAssigns::Fungible(vec) => Ok(vec .get(index as usize) @@ -513,31 +420,25 @@ impl TypedAssigns { } } - pub fn into_structured_state_at( - self, - index: u16, - ) -> Result, UnknownDataError> { + pub fn into_structured_state_at(self, index: u16) -> Result { match self { TypedAssigns::Structured(vec) => { if index as usize >= vec.len() { return Err(UnknownDataError); } - Ok(vec.release().remove(index as usize).into_revealed_state()) + Ok(vec.0.release().remove(index as usize).into_revealed_state()) } _ => Err(UnknownDataError), } } - pub fn into_fungible_state_at( - self, - index: u16, - ) -> Result, UnknownDataError> { + pub fn into_fungible_state_at(self, index: u16) -> Result { match self { TypedAssigns::Fungible(vec) => { if index as usize >= vec.len() { return Err(UnknownDataError); } - Ok(vec.release().remove(index as usize).into_revealed_state()) + Ok(vec.0.release().remove(index as usize).into_revealed_state()) } _ => Err(UnknownDataError), } @@ -547,22 +448,18 @@ impl TypedAssigns { impl TypedAssigns { pub fn transmutate_seals(&self) -> TypedAssigns { match self { - TypedAssigns::Declarative(a) => TypedAssigns::Declarative( - Confined::try_from_iter(a.iter().map(|a| a.transmutate_seals())) - .expect("same size"), - ), - TypedAssigns::Fungible(a) => TypedAssigns::Fungible( + TypedAssigns::Declarative(a) => TypedAssigns::Declarative(AssignVec::with( Confined::try_from_iter(a.iter().map(|a| a.transmutate_seals())) .expect("same size"), - ), - TypedAssigns::Structured(a) => TypedAssigns::Structured( + )), + TypedAssigns::Fungible(a) => TypedAssigns::Fungible(AssignVec::with( Confined::try_from_iter(a.iter().map(|a| a.transmutate_seals())) .expect("same size"), - ), - TypedAssigns::Attachment(a) => TypedAssigns::Attachment( + )), + TypedAssigns::Structured(a) => TypedAssigns::Structured(AssignVec::with( Confined::try_from_iter(a.iter().map(|a| a.transmutate_seals())) .expect("same size"), - ), + )), } } } @@ -581,7 +478,7 @@ impl TypedAssigns { bound = "Seal: serde::Serialize + serde::de::DeserializeOwned" ) )] -pub struct Assignments(TinyOrdMap>) +pub struct Assignments(SmallOrdMap>) where Seal: ExposedSeal; impl Default for Assignments { diff --git a/src/operation/attachment.rs b/src/operation/attachment.rs deleted file mode 100644 index 43084a06..00000000 --- a/src/operation/attachment.rs +++ /dev/null @@ -1,210 +0,0 @@ -// RGB Core Library: consensus layer for RGB smart contracts. -// -// 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. -// Copyright (C) 2019-2024 Dr Maxim Orlovsky. 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::{ByteArray, Bytes32}; -use baid64::{Baid64ParseError, DisplayBaid64, FromBaid64Str}; -use bp::secp256k1::rand::{random, Rng, RngCore}; -use commit_verify::{CommitId, CommitmentId, Conceal, DigestExt, Sha256}; -use strict_encoding::{StrictEncode, StrictSerialize}; - -use super::{ConfidentialState, ExposedState}; -use crate::{ - impl_serde_baid64, ConcealedState, MediaType, RevealedState, StateType, LIB_NAME_RGB_COMMIT, -}; - -/// Unique data attachment identifier -#[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_COMMIT)] -pub struct AttachId( - #[from] - #[from([u8; 32])] - Bytes32, -); - -impl DisplayBaid64 for AttachId { - const HRI: &'static str = "rgb:fs"; - 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 AttachId {} -impl FromStr for AttachId { - type Err = Baid64ParseError; - fn from_str(s: &str) -> Result { Self::from_baid64_str(s) } -} -impl Display for AttachId { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { self.fmt_baid64(f) } -} - -impl_serde_baid64!(AttachId); - -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Display)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_COMMIT)] -#[display("{id}:{media_type}")] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub struct AttachState { - pub id: AttachId, - /// We do not enforce a MIME standard since non-standard types can be also - /// used - pub media_type: MediaType, -} -impl StrictSerialize for AttachState {} - -impl From for AttachState { - fn from(attach: RevealedAttach) -> Self { - AttachState { - id: attach.file.id, - media_type: attach.file.media_type, - } - } -} - -#[derive(Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Debug)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_COMMIT)] -#[derive(CommitEncode)] -#[commit_encode(strategy = strict, id = ConcealedAttach)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub struct RevealedAttach { - pub file: AttachState, - pub salt: u64, -} - -impl RevealedAttach { - /// Constructs new state using the provided value using random blinding - /// factor. - pub fn new_random_salt(id: AttachId, media_type: impl Into) -> Self { - Self::with_salt(id, media_type, random()) - } - - /// Constructs new state using the provided value and random generator for - /// creating blinding factor. - pub fn with_rng( - id: AttachId, - media_type: impl Into, - rng: &mut R, - ) -> Self { - Self::with_salt(id, media_type, rng.next_u64()) - } - - /// Convenience constructor. - pub fn with_salt(id: AttachId, media_type: impl Into, salt: u64) -> Self { - Self { - file: AttachState { - id, - media_type: media_type.into(), - }, - salt, - } - } -} - -impl ExposedState for RevealedAttach { - type Confidential = ConcealedAttach; - fn state_type(&self) -> StateType { StateType::Attachment } - fn state_data(&self) -> RevealedState { RevealedState::Attachment(self.clone()) } -} - -impl Conceal for RevealedAttach { - type Concealed = ConcealedAttach; - - fn conceal(&self) -> Self::Concealed { self.commit_id() } -} - -/// Confidential version of an attachment information. -/// -/// See also revealed version [`RevealedAttach`]. -#[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_COMMIT)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", transparent) -)] -pub struct ConcealedAttach( - #[from] - #[from([u8; 32])] - Bytes32, -); - -impl ConfidentialState for ConcealedAttach { - fn state_type(&self) -> StateType { StateType::Attachment } - fn state_commitment(&self) -> ConcealedState { ConcealedState::Attachment(*self) } -} - -impl From for ConcealedAttach { - fn from(hasher: Sha256) -> Self { hasher.finish().into() } -} - -impl CommitmentId for ConcealedAttach { - const TAG: &'static str = "urn:lnp-bp:rgb:state-attach#2024-02-12"; -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn attach_id_display() { - const ID: &str = - "rgb:fs:bGxsbGxs-bGxsbGx-sbGxsbG-xsbGxsb-GxsbGxs-bGxsbGw#invite-potato-oval"; - let id = AttachId::from_byte_array([0x6c; 32]); - assert_eq!(ID, id.to_string()); - assert_eq!(ID, id.to_baid64_string()); - assert_eq!("invite-potato-oval", id.to_baid64_mnemonic()); - } - - #[test] - fn attach_id_from_str() { - let id = AttachId::from_byte_array([0x6c; 32]); - assert_eq!( - id, - AttachId::from_str( - "rgb:fs:bGxsbGxs-bGxsbGx-sbGxsbG-xsbGxsb-GxsbGxs-bGxsbGw#invite-potato-oval" - ) - .unwrap() - ); - assert_eq!( - id, - AttachId::from_str("rgb:fs:bGxsbGxs-bGxsbGx-sbGxsbG-xsbGxsb-GxsbGxs-bGxsbGw").unwrap() - ); - } -} diff --git a/src/operation/bundle.rs b/src/operation/bundle.rs index 6a69d4f1..10412bc7 100644 --- a/src/operation/bundle.rs +++ b/src/operation/bundle.rs @@ -20,15 +20,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::collections::{btree_map, BTreeMap}; +use std::collections::btree_set; +use std::iter; -use amplify::confinement::{Confined, U16 as U16MAX}; +use amplify::confinement::{NonEmptyOrdMap, NonEmptyOrdSet, U16 as U16MAX}; use amplify::{Bytes32, Wrapper}; -use bp::seals::txout::CloseMethod; use bp::Vout; use commit_verify::{mpc, CommitEncode, CommitEngine, CommitId, CommitmentId, DigestExt, Sha256}; use strict_encoding::{StrictDumb, StrictEncode}; +use super::GraphSeal; +use crate::operation::operations::Operation; use crate::{OpId, Transition, LIB_NAME_RGB_COMMIT}; pub type Vin = Vout; @@ -69,36 +71,25 @@ impl From for BundleId { #[derive(Wrapper, WrapperMut, Clone, PartialEq, Eq, Hash, Debug, From)] #[wrapper(Deref)] #[wrapper_mut(DerefMut)] -#[derive(StrictType, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_COMMIT)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_RGB_COMMIT, dumb = Self(NonEmptyOrdSet::with(OpId::strict_dumb())))] #[cfg_attr( feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate", transparent) )] -pub struct InputMap(Confined, 1, U16MAX>); +pub struct InputOpids(NonEmptyOrdSet); -impl StrictDumb for InputMap { - fn strict_dumb() -> Self { Self(Confined::with_key_value(strict_dumb!(), strict_dumb!())) } -} +impl<'a> IntoIterator for &'a InputOpids { + type Item = OpId; + type IntoIter = iter::Copied>; -impl InputMap { - pub fn with(input: Vin, id: OpId) -> Self { InputMap(Confined::with((input, id))) } + fn into_iter(self) -> Self::IntoIter { self.0.iter().copied() } } -impl IntoIterator for InputMap { - type Item = (Vin, OpId); - type IntoIter = btree_map::IntoIter; - - fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } -} - -impl<'a> IntoIterator for &'a InputMap { - type Item = (&'a Vin, &'a OpId); - type IntoIter = btree_map::Iter<'a, Vin, OpId>; - - fn into_iter(self) -> Self::IntoIter { self.0.iter() } -} +#[derive(Clone, Eq, PartialEq, Debug, Display, Error)] +#[display("state transition {0} is not a part of the bundle.")] +pub struct UnrelatedTransition(OpId); #[derive(Clone, PartialEq, Eq, Debug, From)] #[derive(StrictType, StrictEncode, StrictDecode)] @@ -109,30 +100,54 @@ impl<'a> IntoIterator for &'a InputMap { serde(crate = "serde_crate", rename_all = "camelCase") )] pub struct TransitionBundle { - pub close_method: CloseMethod, - pub input_map: InputMap, - pub known_transitions: Confined, 1, U16MAX>, + pub input_map: NonEmptyOrdMap, + pub known_transitions: NonEmptyOrdMap, } impl CommitEncode for TransitionBundle { type CommitmentId = BundleId; - fn commit_encode(&self, e: &mut CommitEngine) { - e.commit_to_serialized(&self.close_method); - e.commit_to_serialized(&self.input_map); - } + fn commit_encode(&self, e: &mut CommitEngine) { e.commit_to_map(&self.input_map); } } impl StrictDumb for TransitionBundle { fn strict_dumb() -> Self { Self { - close_method: strict_dumb!(), - input_map: strict_dumb!(), - known_transitions: Confined::with_key_value(strict_dumb!(), strict_dumb!()), + input_map: NonEmptyOrdMap::with_key_value(strict_dumb!(), strict_dumb!()), + known_transitions: NonEmptyOrdMap::with_key_value(strict_dumb!(), strict_dumb!()), } } } impl TransitionBundle { pub fn bundle_id(&self) -> BundleId { self.commit_id() } + + pub fn reveal_seal(&mut self, bundle_id: BundleId, seal: GraphSeal) -> bool { + if self.bundle_id() != bundle_id { + return false; + } + self.known_transitions + .values_mut() + .flat_map(|t| t.assignments.values_mut()) + .for_each(|a| a.reveal_seal(seal)); + + true + } + + pub fn reveal_transition( + &mut self, + transition: Transition, + ) -> Result { + let opid = transition.id(); + if self.input_map.values().all(|ids| !ids.contains(&opid)) { + return Err(UnrelatedTransition(opid)); + } + 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/operation/commit.rs b/src/operation/commit.rs index 2a84b383..6ee4f484 100644 --- a/src/operation/commit.rs +++ b/src/operation/commit.rs @@ -32,16 +32,15 @@ use amplify::{hex, ByteArray, Bytes32, FromSliceError, Wrapper}; use baid64::{Baid64ParseError, DisplayBaid64, FromBaid64Str}; use commit_verify::{ mpc, CommitEncode, CommitEngine, CommitId, CommitmentId, Conceal, DigestExt, MerkleHash, - MerkleLeaves, ReservedBytes, Sha256, StrictHash, + MerkleLeaves, Sha256, StrictHash, }; use strict_encoding::StrictDumb; use crate::{ - impl_serde_baid64, Assign, AssignmentType, Assignments, BundleId, ConcealedAttach, - ConcealedData, ConcealedState, ConfidentialState, DataState, ExposedSeal, ExposedState, - Extension, ExtensionType, Ffv, Genesis, GlobalState, GlobalStateType, Operation, - PedersenCommitment, Redeemed, SchemaId, SecretSeal, Transition, TransitionBundle, - TransitionType, TypedAssigns, XChain, LIB_NAME_RGB_COMMIT, + impl_serde_baid64, Assign, AssignmentType, Assignments, BundleId, ChainNet, ExposedSeal, + ExposedState, Ffv, Genesis, GlobalState, GlobalStateType, Operation, RevealedData, + RevealedState, RevealedValue, SchemaId, SealClosingStrategy, SecretSeal, Transition, + TransitionBundle, TransitionType, TypedAssigns, LIB_NAME_RGB_COMMIT, }; /// Unique contract identifier equivalent to the contract genesis commitment @@ -95,7 +94,7 @@ impl From for mpc::ProtocolId { impl_serde_baid64!(ContractId); -/// Unique operation (genesis, extensions & state transition) identifier +/// Unique operation (genesis & state transition) identifier /// equivalent to the commitment hash #[derive(Wrapper, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, From)] #[wrapper(Deref, BorrowSlice, Hex, Index, RangeOps)] @@ -188,10 +187,9 @@ impl AssignmentIndex { #[commit_encode(strategy = strict, id = DiscloseHash)] pub struct OpDisclose { pub id: OpId, - pub seals: MediumOrdMap>, - pub fungible: MediumOrdMap, - pub data: MediumOrdMap, - pub attach: MediumOrdMap, + pub seals: MediumOrdMap, + pub fungible: MediumOrdMap, + pub data: MediumOrdMap, } #[derive(Clone, Eq, PartialEq, Hash, Debug)] @@ -232,13 +230,11 @@ impl TransitionBundle { #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_RGB_COMMIT)] pub struct BaseCommitment { - pub flags: ReservedBytes<1, 0>, pub schema_id: SchemaId, pub timestamp: i64, pub issuer: StrictHash, - pub testnet: bool, - pub alt_layers1: StrictHash, - pub asset_tags: StrictHash, + pub chain_net: ChainNet, + pub seal_closing_strategy: SealClosingStrategy, } #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] @@ -250,9 +246,6 @@ pub enum TypeCommitment { #[strict_type(tag = 1)] Transition(ContractId, TransitionType), - - #[strict_type(tag = 2)] - Extension(ContractId, ExtensionType), } #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] @@ -268,22 +261,16 @@ pub struct OpCommitment { pub globals: MerkleHash, pub inputs: MerkleHash, pub assignments: MerkleHash, - pub redeemed: StrictHash, - pub valencies: StrictHash, - pub witness: MerkleHash, - pub validator: StrictHash, } impl Genesis { pub fn commit(&self) -> OpCommitment { let base = BaseCommitment { - flags: self.flags, schema_id: self.schema_id, timestamp: self.timestamp, - testnet: self.testnet, - alt_layers1: self.alt_layers1.commit_id(), + chain_net: self.chain_net, + seal_closing_strategy: self.seal_closing_strategy, issuer: self.issuer.commit_id(), - asset_tags: self.asset_tags.commit_id(), }; OpCommitment { ffv: self.ffv, @@ -293,10 +280,6 @@ impl Genesis { globals: MerkleHash::merklize(&self.globals), inputs: MerkleHash::void(0, u256::ZERO), assignments: MerkleHash::merklize(&self.assignments), - redeemed: Redeemed::default().commit_id(), - valencies: self.valencies.commit_id(), - witness: MerkleHash::void(0, u256::ZERO), - validator: self.validator.commit_id(), } } @@ -313,49 +296,25 @@ impl Transition { globals: MerkleHash::merklize(&self.globals), inputs: MerkleHash::merklize(&self.inputs), assignments: MerkleHash::merklize(&self.assignments), - redeemed: Redeemed::default().commit_id(), - valencies: self.valencies.commit_id(), - witness: MerkleHash::void(0, u256::ZERO), - validator: self.validator.commit_id(), } } } -impl Extension { - pub fn commit(&self) -> OpCommitment { - OpCommitment { - ffv: self.ffv, - nonce: self.nonce, - op_type: TypeCommitment::Extension(self.contract_id, self.extension_type), - metadata: self.metadata.commit_id(), - globals: MerkleHash::merklize(&self.globals), - inputs: MerkleHash::void(0, u256::ZERO), - assignments: MerkleHash::merklize(&self.assignments), - redeemed: self.redeemed.commit_id(), - valencies: self.valencies.commit_id(), - witness: MerkleHash::void(0, u256::ZERO), - validator: self.validator.commit_id(), - } - } -} - -impl ConcealedState { +impl RevealedState { fn commit_encode(&self, e: &mut CommitEngine) { match self { - ConcealedState::Void => {} - ConcealedState::Fungible(val) => e.commit_to_serialized(&val.commitment), - ConcealedState::Structured(dat) => e.commit_to_serialized(dat), - ConcealedState::Attachment(att) => e.commit_to_serialized(att), + Self::Void => {} + Self::Fungible(val) => e.commit_to_serialized(&val), + Self::Structured(dat) => e.commit_to_serialized(dat), } } } -#[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[derive(Clone, Eq, PartialEq, Debug)] pub struct AssignmentCommitment { pub ty: AssignmentType, - pub state: ConcealedState, - pub seal: XChain, - pub lock: ReservedBytes<2, 0>, + pub state: RevealedState, + pub seal: SecretSeal, } impl CommitEncode for AssignmentCommitment { @@ -365,28 +324,28 @@ impl CommitEncode for AssignmentCommitment { e.commit_to_serialized(&self.ty); self.state.commit_encode(e); e.commit_to_serialized(&self.seal); - e.commit_to_serialized(&self.lock); e.set_finished(); } } impl Assign { pub fn commitment(&self, ty: AssignmentType) -> AssignmentCommitment { - let Self::Confidential { seal, state, lock } = self.conceal() else { + let Self::ConfidentialSeal { seal, state } = self.conceal() else { unreachable!(); }; AssignmentCommitment { ty, - state: state.state_commitment(), + state: state.state_data(), seal, - lock, } } } impl MerkleLeaves for Assignments { type Leaf = AssignmentCommitment; - type LeafIter<'tmp> = vec::IntoIter where Seal: 'tmp; + type LeafIter<'tmp> + = vec::IntoIter + where Seal: 'tmp; fn merkle_leaves(&self) -> Self::LeafIter<'_> { self.iter() @@ -401,9 +360,6 @@ impl MerkleLeaves for Assignments { TypedAssigns::Structured(list) => { list.iter().map(|a| a.commitment(*ty)).collect() } - TypedAssigns::Attachment(list) => { - list.iter().map(|a| a.commitment(*ty)).collect() - } } .into_iter() }) @@ -415,7 +371,7 @@ impl MerkleLeaves for Assignments { #[derive(Clone, Eq, PartialEq, Hash, Debug)] pub struct GlobalCommitment { pub ty: GlobalStateType, - pub state: DataState, + pub state: RevealedData, } impl CommitEncode for GlobalCommitment { diff --git a/src/operation/data.rs b/src/operation/data.rs index 1ad420b0..d4129a45 100644 --- a/src/operation/data.rs +++ b/src/operation/data.rs @@ -20,18 +20,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -use core::fmt::{self, Debug, Formatter}; -use std::cmp::Ordering; - use amplify::confinement::SmallBlob; -use amplify::hex::ToHex; -use amplify::{Bytes32, Wrapper}; -use bp::secp256k1::rand::{random, Rng, RngCore}; -use commit_verify::{CommitId, CommitmentId, Conceal, DigestExt, Sha256}; -use strict_encoding::{StrictSerialize, StrictType}; +use amplify::Wrapper; +use strict_encoding::StrictType; -use super::{ConfidentialState, ExposedState}; -use crate::{ConcealedState, RevealedState, StateType, LIB_NAME_RGB_COMMIT}; +use super::ExposedState; +use crate::{RevealedState, StateType, LIB_NAME_RGB_COMMIT}; /// Struct using for storing Void (i.e. absent) state #[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Hash, Display, Default)] @@ -41,32 +35,26 @@ use crate::{ConcealedState, RevealedState, StateType, LIB_NAME_RGB_COMMIT}; #[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] pub struct VoidState(()); -impl ConfidentialState for VoidState { - fn state_type(&self) -> StateType { StateType::Void } - fn state_commitment(&self) -> ConcealedState { ConcealedState::Void } -} - impl ExposedState for VoidState { - type Confidential = VoidState; fn state_type(&self) -> StateType { StateType::Void } fn state_data(&self) -> RevealedState { RevealedState::Void } } -impl Conceal for VoidState { - type Concealed = VoidState; - fn conceal(&self) -> Self::Concealed { *self } -} - #[derive(Wrapper, Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Hash, From, Display, Default)] #[display(LowerHex)] #[wrapper(Deref, AsSlice, BorrowSlice, Hex)] #[derive(StrictType, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_RGB_COMMIT)] -pub struct DataState(SmallBlob); -impl StrictSerialize for DataState {} +pub struct RevealedData(SmallBlob); -impl From for DataState { - fn from(data: RevealedData) -> Self { data.value } +impl RevealedData { + /// Convenience constructor. + pub fn new(value: impl Into) -> Self { Self(value.into()) } +} + +impl ExposedState for RevealedData { + fn state_type(&self) -> StateType { StateType::Structured } + fn state_data(&self) -> RevealedState { RevealedState::Structured(self.clone()) } } #[cfg(feature = "serde")] @@ -77,14 +65,14 @@ mod _serde { use super::*; - impl Serialize for DataState { + impl Serialize for RevealedData { fn serialize(&self, serializer: S) -> Result where S: Serializer { serializer.serialize_str(&self.to_string()) } } - impl<'de> Deserialize<'de> for DataState { + impl<'de> Deserialize<'de> for RevealedData { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de> { let s = String::deserialize(deserializer)?; @@ -92,101 +80,3 @@ mod _serde { } } } - -#[derive(Clone, Eq, PartialEq, Hash)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_COMMIT)] -#[derive(CommitEncode)] -#[commit_encode(strategy = strict, id = ConcealedData)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] -pub struct RevealedData { - pub value: DataState, - pub salt: u128, -} - -impl RevealedData { - /// Constructs new state using the provided value using random blinding - /// factor. - pub fn new_random_salt(value: impl Into) -> Self { Self::with_salt(value, random()) } - - /// Constructs new state using the provided value and random generator for - /// creating blinding factor. - pub fn with_rng(value: impl Into, rng: &mut R) -> Self { - Self::with_salt(value, rng.gen()) - } - - /// Convenience constructor. - pub fn with_salt(value: impl Into, salt: u128) -> Self { - Self { - value: value.into(), - salt, - } - } -} - -impl ExposedState for RevealedData { - type Confidential = ConcealedData; - fn state_type(&self) -> StateType { StateType::Structured } - fn state_data(&self) -> RevealedState { RevealedState::Structured(self.clone()) } -} - -impl Conceal for RevealedData { - type Concealed = ConcealedData; - - fn conceal(&self) -> Self::Concealed { self.commit_id() } -} - -impl PartialOrd for RevealedData { - fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } -} - -impl Ord for RevealedData { - fn cmp(&self, other: &Self) -> Ordering { - match self.value.cmp(&other.value) { - Ordering::Equal => self.salt.cmp(&other.salt), - other => other, - } - } -} - -impl Debug for RevealedData { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let val = String::from_utf8(self.value.to_vec()).unwrap_or_else(|_| self.value.to_hex()); - - f.debug_struct("RevealedData") - .field("value", &val) - .field("salt", &self.salt) - .finish() - } -} - -/// Confidential version of an structured state data. -/// -/// See also revealed version [`RevealedData`]. -#[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_COMMIT, rename = "ConcealedData")] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", transparent) -)] -pub struct ConcealedData( - #[from] - #[from([u8; 32])] - Bytes32, -); - -impl ConfidentialState for ConcealedData { - fn state_type(&self) -> StateType { StateType::Structured } - fn state_commitment(&self) -> ConcealedState { ConcealedState::Structured(*self) } -} - -impl From for ConcealedData { - fn from(hasher: Sha256) -> Self { hasher.finish().into() } -} - -impl CommitmentId for ConcealedData { - const TAG: &'static str = "urn:lnp-bp:rgb:state-data#2024-02-12"; -} diff --git a/src/operation/fungible.rs b/src/operation/fungible.rs index 1af77801..c28b55b4 100644 --- a/src/operation/fungible.rs +++ b/src/operation/fungible.rs @@ -30,76 +30,15 @@ //! properties regarding their total sum and, thus, can be made confidential //! using elliptic curve homomorphic cryptography such as Pedesen commitments. -use core::cmp::Ordering; use core::fmt::Debug; use core::num::ParseIntError; -use core::ops::Deref; use core::str::FromStr; use std::hash::Hash; -use std::io; -use amplify::confinement::U8; -use amplify::hex::ToHex; -// We do not import particular modules to keep aware with namespace prefixes -// that we do not use the standard secp256k1zkp library -use amplify::{hex, Array, Bytes32, Wrapper}; -use bp::secp256k1::rand::thread_rng; -use chrono::{DateTime, Utc}; -use commit_verify::{ - CommitVerify, CommitmentProtocol, Conceal, DigestExt, Sha256, UntaggedProtocol, -}; -use secp256k1_zkp::rand::{Rng, RngCore}; -use secp256k1_zkp::SECP256K1; -use strict_encoding::{ - DecodeError, ReadTuple, StrictDecode, StrictDumb, StrictEncode, TypedRead, TypedWrite, - WriteTuple, -}; +use strict_encoding::{StrictDecode, StrictDumb, StrictEncode}; -use super::{ConfidentialState, ExposedState}; -use crate::{ - schema, AssignmentType, ConcealedState, RevealedState, StateType, LIB_NAME_RGB_COMMIT, -}; - -#[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_COMMIT)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", transparent) -)] -pub struct AssetTag( - #[from] - #[from([u8; 32])] - Bytes32, -); - -impl AssetTag { - pub fn new_random(contract_domain: impl AsRef, assignment_type: AssignmentType) -> Self { - AssetTag::new_deterministic( - contract_domain, - assignment_type, - Utc::now(), - thread_rng().next_u64(), - ) - } - - pub fn new_deterministic( - contract_domain: impl AsRef, - assignment_type: AssignmentType, - timestamp: DateTime, - salt: u64, - ) -> Self { - let timestamp = timestamp.timestamp(); - let mut hasher = Sha256::default(); - hasher.input_with_len::(contract_domain.as_ref().as_bytes()); - hasher.input_raw(&assignment_type.to_le_bytes()); - hasher.input_raw(×tamp.to_le_bytes()); - hasher.input_raw(&salt.to_le_bytes()); - AssetTag::from(hasher.finish()) - } -} +use super::ExposedState; +use crate::{schema, RevealedState, StateType, LIB_NAME_RGB_COMMIT}; /// An atom of an additive state, which thus can be monomorphically encrypted. #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, From)] @@ -123,10 +62,6 @@ impl Default for FungibleState { fn default() -> Self { FungibleState::Bits64(0) } } -impl From for FungibleState { - fn from(revealed: RevealedValue) -> Self { revealed.value } -} - impl FromStr for FungibleState { type Err = ParseIntError; fn from_str(s: &str) -> Result { s.parse().map(FungibleState::Bits64) } @@ -150,487 +85,27 @@ impl FungibleState { pub fn as_u64(&self) -> u64 { (*self).into() } } -/// value provided for a blinding factor overflows prime field order for -/// Secp256k1 curve. -#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Display, Error, From)] -#[display(doc_comments)] -#[from(secp256k1_zkp::UpstreamError)] -pub struct InvalidFieldElement; - -/// Errors parsing string representation of a blinding factor. -#[derive(Copy, Clone, Eq, PartialEq, Debug, Display, Error, From)] -#[display(doc_comments)] -pub enum BlindingParseError { - /// invalid blinding factor hex representation - {0} - #[from] - Hex(hex::Error), - - /// blinding factor value is invalid and does not belong to the Secp256k1 - /// curve field. - #[from(InvalidFieldElement)] - InvalidFieldElement, -} - -/// Blinding factor used in creating Pedersen commitment to an [`AtomicValue`]. -/// -/// Knowledge of the blinding factor is important to reproduce the commitment -/// process if the original value is kept. -#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)] -#[display(Self::to_hex)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_COMMIT)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", try_from = "secp256k1_zkp::SecretKey") -)] -pub struct BlindingFactor(Bytes32); - -impl BlindingFactor { - pub const EMPTY: Self = BlindingFactor(Bytes32::from_array([0x7E; 32])); -} - -impl Deref for BlindingFactor { - type Target = [u8; 32]; - fn deref(&self) -> &Self::Target { self.0.as_inner() } -} - -impl ToHex for BlindingFactor { - fn to_hex(&self) -> String { self.0.to_hex() } -} - -impl FromStr for BlindingFactor { - type Err = BlindingParseError; - fn from_str(s: &str) -> Result { - let bytes = Bytes32::from_str(s)?; - Self::try_from(bytes).map_err(BlindingParseError::from) - } -} - -impl From for BlindingFactor { - fn from(key: secp256k1_zkp::SecretKey) -> Self { Self(Bytes32::from_inner(*key.as_ref())) } -} - -impl From for secp256k1_zkp::SecretKey { - fn from(bf: BlindingFactor) -> Self { bf.to_secret_key() } -} - -impl BlindingFactor { - /// Creates a random blinding factor. - #[inline] - pub fn random() -> Self { Self::random_custom(&mut thread_rng()) } - - /// Generates a random blinding factor using custom random number generator. - #[inline] - pub fn random_custom(rng: &mut R) -> Self { - secp256k1_zkp::SecretKey::new(rng).into() - } - - /// Generates new blinding factor which balances a given set of negatives - /// and positives into zero. - /// - /// # Errors - /// - /// * if negatives are empty set; - /// * if any subset of the negatives or positives are inverses of other negatives or positives, - /// * if the balancing factor is zero (sum of negatives already equal to the sum of positives). - pub fn zero_balanced( - negative: impl IntoIterator, - positive: impl IntoIterator, - ) -> Result { - let mut blinding_neg_sum = secp256k1_zkp::Scalar::ZERO; - let mut blinding_pos_sum = secp256k1_zkp::Scalar::ZERO; - for neg in negative { - blinding_neg_sum = neg.to_secret_key().add_tweak(&blinding_neg_sum)?.into(); - } - let blinding_neg_sum = - secp256k1_zkp::SecretKey::from_slice(&blinding_neg_sum.to_be_bytes())?.negate(); - for pos in positive { - blinding_pos_sum = pos.to_secret_key().add_tweak(&blinding_pos_sum)?.into(); - } - let blinding_correction = blinding_neg_sum.add_tweak(&blinding_pos_sum)?.negate(); - Ok(blinding_correction.into()) - } - - fn to_secret_key(self) -> secp256k1_zkp::SecretKey { - secp256k1_zkp::SecretKey::from_slice(self.0.as_slice()) - .expect("blinding factor is an invalid secret key") - } -} - -impl TryFrom<[u8; 32]> for BlindingFactor { - type Error = InvalidFieldElement; - - fn try_from(array: [u8; 32]) -> Result { - secp256k1_zkp::SecretKey::from_slice(&array) - .map_err(|_| InvalidFieldElement) - .map(Self::from) - } -} - -impl TryFrom for BlindingFactor { - type Error = InvalidFieldElement; - - fn try_from(bytes: Bytes32) -> Result { - Self::try_from(bytes.to_byte_array()) - } -} - /// State item for a homomorphically-encryptable state. /// /// Consists of the 64-bit value and -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +#[derive(Wrapper, Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord, From, Display)] +#[display(inner)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_COMMIT, rename = "RevealedFungible")] +#[wrapper(Deref)] +#[strict_type(lib = LIB_NAME_RGB_COMMIT)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] -pub struct RevealedValue { - /// Original value in smallest indivisible units - pub value: FungibleState, - - /// Blinding factor used in Pedersen commitment - pub blinding: BlindingFactor, - - /// Asset-specific tag preventing mixing assets of different type. - pub tag: AssetTag, -} +pub struct RevealedValue(FungibleState); impl RevealedValue { - /// Constructs new state using the provided value using random blinding - /// factor. - pub fn new_random_blinding(value: impl Into, tag: AssetTag) -> Self { - Self::with_blinding(value, BlindingFactor::random(), tag) - } - - /// Constructs new state using the provided value and random generator for - /// creating blinding factor. - pub fn with_rng( - value: impl Into, - rng: &mut R, - tag: AssetTag, - ) -> Self { - Self::with_blinding(value, BlindingFactor::random_custom(rng), tag) - } - /// Convenience constructor. - pub fn with_blinding( - value: impl Into, - blinding: BlindingFactor, - tag: AssetTag, - ) -> Self { - Self { - value: value.into(), - blinding, - tag, - } - } + pub fn new(value: impl Into) -> Self { Self(value.into()) } } impl ExposedState for RevealedValue { - type Confidential = ConcealedValue; fn state_type(&self) -> StateType { StateType::Fungible } fn state_data(&self) -> RevealedState { RevealedState::Fungible(*self) } } -impl Conceal for RevealedValue { - type Concealed = ConcealedValue; - - fn conceal(&self) -> Self::Concealed { ConcealedValue::commit(self) } -} - -impl PartialOrd for RevealedValue { - fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } -} - -impl Ord for RevealedValue { - fn cmp(&self, other: &Self) -> Ordering { - match self.value.cmp(&other.value) { - Ordering::Equal => self.blinding.0.cmp(&other.blinding.0), - other => other, - } - } -} - -/// Opaque type holding pedersen commitment for an [`FungibleState`]. -#[derive(Wrapper, Copy, Clone, Eq, PartialEq, Hash, Debug, From)] -#[wrapper(Deref, FromStr, Display, LowerHex)] -#[derive(StrictType)] -#[strict_type(lib = LIB_NAME_RGB_COMMIT)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", transparent) -)] -pub struct PedersenCommitment(secp256k1_zkp::PedersenCommitment); - -impl StrictDumb for PedersenCommitment { - fn strict_dumb() -> Self { - secp256k1_zkp::PedersenCommitment::from_slice(&[0x08; 33]) - .expect("hardcoded pedersen commitment value") - .into() - } -} - -impl StrictEncode for PedersenCommitment { - fn strict_encode(&self, writer: W) -> io::Result { - writer.write_tuple::(|w| Ok(w.write_field(&self.0.serialize())?.complete())) - } -} - -impl StrictDecode for PedersenCommitment { - fn strict_decode(reader: &mut impl TypedRead) -> Result { - reader.read_tuple(|r| { - let commitment = r.read_field::<[u8; 33]>()?; - secp256k1_zkp::PedersenCommitment::from_slice(&commitment) - .map_err(|_| { - DecodeError::DataIntegrityError(s!("invalid pedersen commitment data")) - }) - .map(PedersenCommitment::from_inner) - }) - } -} - -impl CommitVerify for PedersenCommitment { - fn commit(revealed: &RevealedValue) -> Self { - use secp256k1_zkp::{Generator, Tag, Tweak}; - - let blinding = Tweak::from_inner(revealed.blinding.0.into_inner()) - .expect("type guarantees of BlindingFactor are broken"); - let FungibleState::Bits64(value) = revealed.value; - - let tag = Tag::from(revealed.tag.to_byte_array()); - let generator = Generator::new_unblinded(SECP256K1, tag); - - secp256k1_zkp::PedersenCommitment::new(SECP256K1, value, blinding, generator).into() - } -} - -/// A dumb placeholder for a future bulletproofs. -#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] -#[derive(StrictType, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_COMMIT)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", transparent) -)] -pub struct NoiseDumb(Array); - -impl Default for NoiseDumb { - fn default() -> Self { - let mut dumb = [0u8; 512]; - thread_rng().fill(&mut dumb); - NoiseDumb(dumb.into()) - } -} - -/// Range proof value. -/// -/// Range proofs must be used alongside [`PedersenCommitment`]s to ensure that -/// the value do not overflow on arithmetic operations with the commitments. -#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] -#[derive(StrictType)] -#[strict_type(lib = LIB_NAME_RGB_COMMIT, tags = custom)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase", untagged) -)] -pub enum RangeProof { - /// Value used when bulletproofs library is not available. - /// - /// Always fails validation if no source value is given. - #[strict_type(tag = 0xFF)] - Placeholder(NoiseDumb), -} - -impl Default for RangeProof { - fn default() -> Self { RangeProof::Placeholder(default!()) } -} - -impl StrictEncode for RangeProof { - fn strict_encode(&self, writer: W) -> io::Result { - eprintln!("bulletproof dummies must never be stored"); - Ok(writer) - } -} - -impl StrictDecode for RangeProof { - fn strict_decode(_: &mut impl TypedRead) -> Result { - panic!("bulletproofs dummies must never be read") - } -} - -pub struct PedersenProtocol; - -impl CommitmentProtocol for PedersenProtocol {} - -/// Confidential version of the additive state. -/// -/// See also revealed version [`RevealedValue`]. -#[derive(Clone, Copy, Eq, Debug)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_COMMIT, rename = "ConcealedFungible")] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub struct ConcealedValue { - /// Pedersen commitment to the original [`FungibleState`]. - pub commitment: PedersenCommitment, - /// Range proof for the [`FungibleState`] not exceeding type boundaries. - pub range_proof: RangeProof, -} - -impl PartialEq for ConcealedValue { - fn eq(&self, other: &Self) -> bool { self.commitment == other.commitment } -} - -impl ConfidentialState for ConcealedValue { - fn state_type(&self) -> StateType { StateType::Fungible } - fn state_commitment(&self) -> ConcealedState { ConcealedState::Fungible(*self) } -} - -impl CommitVerify for ConcealedValue { - fn commit(revealed: &RevealedValue) -> Self { - let commitment = PedersenCommitment::commit(revealed); - // TODO: Do actual conceal upon integration of bulletproofs library - let range_proof = RangeProof::default(); - ConcealedValue { - commitment, - range_proof, - } - } -} - -/// Errors verifying range proofs. -#[derive(Copy, Clone, PartialEq, Eq, Debug, Display, Error)] -#[display(doc_comments)] -pub enum RangeProofError { - /// invalid blinding factor {0}. - InvalidBlinding(BlindingFactor), - - /// bulletproofs verification is not implemented in RGB Core v0.10. Please - /// update your software and try again, or ask your software producer to use - /// latest RGB release. - BulletproofsAbsent, -} - -impl ConcealedValue { - /// Verifies validity of the range proof. - pub fn verify_range_proof(&self) -> Result { - // We always fail here - Err(RangeProofError::BulletproofsAbsent) - } -} - -#[cfg(test)] -mod test { - use amplify::ByteArray; - - use super::*; - - #[test] - fn pedersen_blinding_mismatch() { - let mut r = thread_rng(); - let tag = AssetTag::from_byte_array([1u8; 32]); - - let a = PedersenCommitment::commit(&RevealedValue::with_rng(15, &mut r, tag)).into_inner(); - let b = PedersenCommitment::commit(&RevealedValue::with_rng(7, &mut r, tag)).into_inner(); - - let c = PedersenCommitment::commit(&RevealedValue::with_rng(13, &mut r, tag)).into_inner(); - let d = PedersenCommitment::commit(&RevealedValue::with_rng(9, &mut r, tag)).into_inner(); - - assert!(!secp256k1_zkp::verify_commitments_sum_to_equal(SECP256K1, &[a, b], &[c, d])) - } - - #[test] - fn pedersen_blinding_same() { - let blinding = - BlindingFactor::from(secp256k1_zkp::SecretKey::from_slice(&[1u8; 32]).unwrap()); - let tag = AssetTag::from_byte_array([1u8; 32]); - - let a = PedersenCommitment::commit(&RevealedValue::with_blinding(15, blinding, tag)) - .into_inner(); - let b = PedersenCommitment::commit(&RevealedValue::with_blinding(7, blinding, tag)) - .into_inner(); - - let c = PedersenCommitment::commit(&RevealedValue::with_blinding(13, blinding, tag)) - .into_inner(); - let d = PedersenCommitment::commit(&RevealedValue::with_blinding(9, blinding, tag)) - .into_inner(); - - assert!(secp256k1_zkp::verify_commitments_sum_to_equal(SECP256K1, &[a, b], &[c, d])) - } - - #[test] - fn pedersen_blinding_same_tag_differ() { - let blinding = - BlindingFactor::from(secp256k1_zkp::SecretKey::from_slice(&[1u8; 32]).unwrap()); - let tag = AssetTag::from_byte_array([1u8; 32]); - let tag2 = AssetTag::from_byte_array([2u8; 32]); - - let a = PedersenCommitment::commit(&RevealedValue::with_blinding(15, blinding, tag2)) - .into_inner(); - let b = PedersenCommitment::commit(&RevealedValue::with_blinding(7, blinding, tag)) - .into_inner(); - - let c = PedersenCommitment::commit(&RevealedValue::with_blinding(13, blinding, tag2)) - .into_inner(); - let d = PedersenCommitment::commit(&RevealedValue::with_blinding(9, blinding, tag)) - .into_inner(); - - assert!(!secp256k1_zkp::verify_commitments_sum_to_equal(SECP256K1, &[a, b], &[c, d])) - } - - #[test] - fn pedersen_two_tags() { - let blinding = - BlindingFactor::from(secp256k1_zkp::SecretKey::from_slice(&[1u8; 32]).unwrap()); - let tag = AssetTag::from_byte_array([1u8; 32]); - let tag2 = AssetTag::from_byte_array([2u8; 32]); - - let a = PedersenCommitment::commit(&RevealedValue::with_blinding(15, blinding, tag2)) - .into_inner(); - let b = PedersenCommitment::commit(&RevealedValue::with_blinding(7, blinding, tag2)) - .into_inner(); - let c = PedersenCommitment::commit(&RevealedValue::with_blinding(2, blinding, tag)) - .into_inner(); - let d = PedersenCommitment::commit(&RevealedValue::with_blinding(4, blinding, tag)) - .into_inner(); - - let e = PedersenCommitment::commit(&RevealedValue::with_blinding(13, blinding, tag2)) - .into_inner(); - let f = PedersenCommitment::commit(&RevealedValue::with_blinding(9, blinding, tag2)) - .into_inner(); - let g = PedersenCommitment::commit(&RevealedValue::with_blinding(1, blinding, tag)) - .into_inner(); - let h = PedersenCommitment::commit(&RevealedValue::with_blinding(5, blinding, tag)) - .into_inner(); - - assert!(secp256k1_zkp::verify_commitments_sum_to_equal(SECP256K1, &[a, b, c, d], &[ - e, f, g, h - ])) - } - - #[test] - fn pedersen_blinding_balance() { - let blinding1 = BlindingFactor::random(); - let blinding2 = BlindingFactor::random(); - let blinding3 = BlindingFactor::random(); - let blinding4 = BlindingFactor::zero_balanced([blinding1, blinding2], [blinding3]).unwrap(); - let tag = AssetTag::from_byte_array([1u8; 32]); - - let a = PedersenCommitment::commit(&RevealedValue::with_blinding(15, blinding1, tag)) - .into_inner(); - let b = PedersenCommitment::commit(&RevealedValue::with_blinding(7, blinding2, tag)) - .into_inner(); - - let c = PedersenCommitment::commit(&RevealedValue::with_blinding(13, blinding3, tag)) - .into_inner(); - let d = PedersenCommitment::commit(&RevealedValue::with_blinding(9, blinding4, tag)) - .into_inner(); - - assert!(secp256k1_zkp::verify_commitments_sum_to_equal(SECP256K1, &[a, b], &[c, d])) - } +impl From for RevealedValue { + fn from(value: u64) -> Self { Self(FungibleState::Bits64(value)) } } diff --git a/src/operation/global.rs b/src/operation/global.rs index 25dadc12..a59c8f8c 100644 --- a/src/operation/global.rs +++ b/src/operation/global.rs @@ -27,7 +27,7 @@ use amplify::confinement::{Confined, TinyOrdMap, U16}; use amplify::{confinement, Wrapper}; use strict_encoding::StrictDumb; -use crate::{schema, DataState, LIB_NAME_RGB_COMMIT}; +use crate::{schema, RevealedData, LIB_NAME_RGB_COMMIT}; #[derive(Wrapper, WrapperMut, Clone, PartialEq, Eq, Hash, Debug, From)] #[wrapper(Deref)] @@ -39,19 +39,19 @@ use crate::{schema, DataState, LIB_NAME_RGB_COMMIT}; derive(Serialize, Deserialize), serde(crate = "serde_crate", transparent) )] -pub struct GlobalValues(Confined, 1, U16>); +pub struct GlobalValues(Confined, 1, U16>); impl StrictDumb for GlobalValues { - fn strict_dumb() -> Self { Self(Confined::with(DataState::strict_dumb())) } + fn strict_dumb() -> Self { Self(Confined::with(RevealedData::strict_dumb())) } } impl GlobalValues { - pub fn with(state: DataState) -> Self { GlobalValues(Confined::with(state)) } + pub fn with(state: RevealedData) -> Self { GlobalValues(Confined::with(state)) } } impl IntoIterator for GlobalValues { - type Item = DataState; - type IntoIter = vec::IntoIter; + type Item = RevealedData; + type IntoIter = vec::IntoIter; fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } } @@ -72,7 +72,7 @@ impl GlobalState { pub fn add_state( &mut self, ty: schema::GlobalStateType, - state: DataState, + state: RevealedData, ) -> Result<(), confinement::Error> { match self.0.get_mut(&ty) { Some(vec) => vec.push(state), @@ -83,7 +83,7 @@ impl GlobalState { pub fn extend_state( &mut self, ty: schema::GlobalStateType, - iter: impl IntoIterator, + iter: impl IntoIterator, ) -> Result<(), confinement::Error> { match self.0.get_mut(&ty) { Some(vec) => vec.extend(iter), diff --git a/src/operation/layer1.rs b/src/operation/layer1.rs new file mode 100644 index 00000000..209207bc --- /dev/null +++ b/src/operation/layer1.rs @@ -0,0 +1,143 @@ +// RGB Core Library: consensus layer for RGB smart contracts. +// +// 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. +// Copyright (C) 2019-2024 Dr Maxim Orlovsky. 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::str::FromStr; + +use bp::BlockHash; +use strict_encoding::{StrictDecode, StrictEncode, StrictType}; + +use crate::{LIB_NAME_RGB_COMMIT, LIB_NAME_RGB_LOGIC}; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] +#[display(lowercase)] +#[derive(StrictType, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_RGB_LOGIC, tags = repr, into_u8, try_from_u8)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", rename_all = "camelCase") +)] +#[repr(u8)] +#[derive(Default)] +pub enum Layer1 { + #[default] + Bitcoin = 0, + Liquid = 1, +} + +#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)] +#[display(inner)] +#[derive(StrictType, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_RGB_COMMIT, tags = repr, into_u8, try_from_u8)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", rename_all = "camelCase") +)] +#[repr(u8)] +#[derive(Default)] +pub enum ChainNet { + BitcoinMainnet = 0, + BitcoinTestnet3 = 1, + #[default] + BitcoinTestnet4 = 2, + BitcoinSignet = 3, + BitcoinRegtest = 4, + LiquidMainnet = 5, + LiquidTestnet = 6, +} + +impl ChainNet { + pub fn prefix(&self) -> &str { + match self { + ChainNet::BitcoinMainnet => "bc", + ChainNet::BitcoinTestnet3 => "tb3", + ChainNet::BitcoinTestnet4 => "tb4", + ChainNet::BitcoinRegtest => "bcrt", + ChainNet::BitcoinSignet => "sb", + ChainNet::LiquidMainnet => "lq", + ChainNet::LiquidTestnet => "tl", + } + } + + pub fn layer1(&self) -> Layer1 { + match self { + ChainNet::BitcoinMainnet + | ChainNet::BitcoinTestnet3 + | ChainNet::BitcoinTestnet4 + | ChainNet::BitcoinSignet + | ChainNet::BitcoinRegtest => Layer1::Bitcoin, + ChainNet::LiquidMainnet | ChainNet::LiquidTestnet => Layer1::Liquid, + } + } + + pub fn genesis_block_hash(&self) -> BlockHash { + BlockHash::from_str(match self { + ChainNet::BitcoinMainnet => { + "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f" + } + ChainNet::BitcoinTestnet3 => { + "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943" + } + ChainNet::BitcoinTestnet4 => { + "00000000da84f2bafbbc53dee25a72ae507ff4914b867c565be350b0da8bf043" + } + ChainNet::BitcoinSignet => { + "00000008819873e925422c1ff0f99f7cc9bbb232af63a077a480a3633bee1ef6" + } + ChainNet::BitcoinRegtest => { + "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206" + } + ChainNet::LiquidMainnet => { + "4f4eac81e5f9f04f5d2a17b03e6726e6a1af69d9c3f00d820f1c82fcb6000000" + } + ChainNet::LiquidTestnet => { + "f9f21a7636b35c12f080ff73fc8bb16bb7c3ceafdc2eb1b673f0ea7a40c00000" + } + }) + .unwrap() + } +} + +#[derive(Debug, Display, Error, From)] +#[display(doc_comments)] +pub enum ChainNetParseError { + /// invalid chain-network pair {0}. + Invalid(String), +} + +impl FromStr for ChainNet { + type Err = ChainNetParseError; + + fn from_str(s: &str) -> Result { + match s.to_lowercase() { + x if ChainNet::BitcoinMainnet.prefix() == x => Ok(ChainNet::BitcoinMainnet), + x if ChainNet::BitcoinRegtest.prefix() == x => Ok(ChainNet::BitcoinRegtest), + x if ChainNet::BitcoinSignet.prefix() == x => Ok(ChainNet::BitcoinSignet), + x if ChainNet::BitcoinTestnet3.prefix() == x => Ok(ChainNet::BitcoinTestnet3), + x if ChainNet::BitcoinTestnet4.prefix() == x => Ok(ChainNet::BitcoinTestnet4), + x if ChainNet::LiquidMainnet.prefix() == x => Ok(ChainNet::LiquidMainnet), + x if ChainNet::LiquidTestnet.prefix() == x => Ok(ChainNet::LiquidTestnet), + _ => Err(ChainNetParseError::Invalid(s.to_owned())), + } + } +} diff --git a/src/operation/mod.rs b/src/operation/mod.rs index c13450be..795b7dc7 100644 --- a/src/operation/mod.rs +++ b/src/operation/mod.rs @@ -24,42 +24,30 @@ mod meta; mod global; mod data; mod fungible; -mod attachment; mod state; pub mod seal; pub mod assignments; mod operations; mod bundle; -mod xchain; +mod layer1; mod commit; pub use assignments::{ - Assign, AssignAttach, AssignData, AssignFungible, AssignRights, Assignments, AssignmentsRef, - TypedAssigns, + Assign, AssignData, AssignFungible, AssignRights, Assignments, AssignmentsRef, TypedAssigns, }; -pub use attachment::{AttachId, AttachState, ConcealedAttach, RevealedAttach}; -pub use bundle::{BundleId, InputMap, TransitionBundle, Vin}; +pub use bundle::{BundleId, InputOpids, TransitionBundle, UnrelatedTransition, Vin}; pub use commit::{ AssignmentCommitment, AssignmentIndex, BaseCommitment, BundleDisclosure, ContractId, DiscloseHash, GlobalCommitment, OpCommitment, OpDisclose, OpId, TypeCommitment, }; -pub use data::{ConcealedData, DataState, RevealedData, VoidState}; -pub use fungible::{ - AssetTag, BlindingFactor, BlindingParseError, ConcealedValue, FungibleState, - InvalidFieldElement, NoiseDumb, PedersenCommitment, RangeProof, RangeProofError, RevealedValue, -}; +pub use data::{RevealedData, VoidState}; +pub use fungible::{FungibleState, RevealedValue}; pub use global::{GlobalState, GlobalValues}; +pub use layer1::{ChainNet, Layer1}; pub use meta::{MetaValue, Metadata, MetadataError}; pub use operations::{ - AssetTags, Extension, Genesis, Identity, Input, Inputs, Operation, Opout, OpoutParseError, - Redeemed, Transition, Valencies, -}; -pub use seal::{ - ExposedSeal, GenesisSeal, GraphSeal, OutputSeal, SecretSeal, TxoSeal, XGenesisSeal, XGraphSeal, - XOutputSeal, -}; -pub use state::{ConcealedState, ConfidentialState, ExposedState, RevealedState, StateType}; -pub use xchain::{ - AltLayer1, AltLayer1Set, Impossible, Layer1, XChain, XChainParseError, XOutpoint, - XCHAIN_BITCOIN_PREFIX, XCHAIN_LIQUID_PREFIX, + Genesis, Identity, Inputs, Operation, Opout, OpoutParseError, SealClosingStrategy, Signature, + Transition, }; +pub use seal::{ExposedSeal, GenesisSeal, GraphSeal, OutputSeal, SecretSeal, TxoSeal}; +pub use state::{ExposedState, RevealedState, StateType}; diff --git a/src/operation/operations.rs b/src/operation/operations.rs index e4ed6cf2..eca418f9 100644 --- a/src/operation/operations.rs +++ b/src/operation/operations.rs @@ -20,32 +20,28 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::cmp::Ordering; -use std::collections::{btree_map, btree_set, BTreeMap}; +use std::collections::{btree_set, BTreeMap}; use std::iter; use std::num::ParseIntError; -use std::str::FromStr; -use amplify::confinement::{Confined, SmallOrdSet, TinyOrdMap, TinyOrdSet}; -use amplify::{hex, Wrapper}; -use commit_verify::{ - CommitEncode, CommitEngine, CommitId, Conceal, MerkleHash, MerkleLeaves, ReservedBytes, - StrictHash, -}; +use amplify::confinement::{Confined, NonEmptyOrdSet, TinyOrdSet, U16}; +use amplify::{hex, Bytes64, Wrapper}; +use commit_verify::{CommitEncode, CommitEngine, CommitId, MerkleHash, MerkleLeaves, StrictHash}; use strict_encoding::stl::AsciiPrintable; use strict_encoding::{RString, StrictDeserialize, StrictEncode, StrictSerialize}; -use crate::schema::{self, ExtensionType, OpFullType, OpType, SchemaId, TransitionType}; +use crate::schema::{OpFullType, SchemaId, TransitionType}; use crate::{ - AltLayer1Set, AssetTag, Assign, AssignmentIndex, AssignmentType, Assignments, AssignmentsRef, - ConcealedAttach, ConcealedData, ConcealedValue, ContractId, DiscloseHash, ExposedState, Ffv, - GenesisSeal, GlobalState, GraphSeal, Metadata, OpDisclose, OpId, SecretSeal, TypedAssigns, - VoidState, XChain, LIB_NAME_RGB_COMMIT, + Assign, AssignmentIndex, AssignmentType, Assignments, AssignmentsRef, ChainNet, ContractId, + DiscloseHash, ExposedState, Ffv, GenesisSeal, GlobalState, GraphSeal, Metadata, OpDisclose, + OpId, RevealedData, RevealedValue, SecretSeal, TypedAssigns, VoidState, LIB_NAME_RGB_COMMIT, }; #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_RGB_COMMIT)] +#[derive(CommitEncode)] +#[commit_encode(strategy = strict, id = MerkleHash)] #[cfg_attr( feature = "serde", derive(Serialize, Deserialize), @@ -79,144 +75,38 @@ pub enum OpoutParseError { WrongFormat(String), } -impl FromStr for Opout { - type Err = OpoutParseError; - - fn from_str(s: &str) -> Result { - let mut split = s.split('/'); - match (split.next(), split.next(), split.next(), split.next()) { - (Some(op), Some(ty), Some(no), None) => Ok(Opout { - op: op.parse()?, - ty: ty.parse().map_err(OpoutParseError::InvalidType)?, - no: no.parse().map_err(OpoutParseError::InvalidOutputNo)?, - }), - _ => Err(OpoutParseError::WrongFormat(s.to_owned())), - } - } -} - -#[derive(Wrapper, WrapperMut, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Default, From)] -#[wrapper(Deref)] -#[wrapper_mut(DerefMut)] -#[derive(StrictType, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_COMMIT)] -#[derive(CommitEncode)] -#[commit_encode(strategy = strict, id = StrictHash)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", transparent) -)] -pub struct AssetTags(TinyOrdMap); - -#[derive(Wrapper, WrapperMut, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Default, From)] -#[wrapper(Deref)] -#[wrapper_mut(DerefMut)] -#[derive(StrictType, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_COMMIT)] -#[derive(CommitEncode)] -#[commit_encode(strategy = strict, id = StrictHash)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", transparent) -)] -pub struct Valencies(TinyOrdSet); - -impl<'a> IntoIterator for &'a Valencies { - type Item = schema::ValencyType; - type IntoIter = iter::Copied>; - - fn into_iter(self) -> Self::IntoIter { self.0.iter().copied() } -} - -#[derive(Wrapper, WrapperMut, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Default, From)] -#[wrapper(Deref)] -#[wrapper_mut(DerefMut)] -#[derive(StrictType, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_COMMIT)] -#[derive(CommitEncode)] -#[commit_encode(strategy = strict, id = StrictHash)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", transparent) -)] -pub struct Redeemed(TinyOrdMap); - -impl<'a> IntoIterator for &'a Redeemed { - type Item = (&'a schema::ValencyType, &'a OpId); - type IntoIter = btree_map::Iter<'a, schema::ValencyType, OpId>; - - fn into_iter(self) -> Self::IntoIter { self.0.iter() } -} - -#[derive(Wrapper, WrapperMut, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Default, From)] +#[derive(Wrapper, WrapperMut, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)] #[wrapper(Deref)] #[wrapper_mut(DerefMut)] -#[derive(StrictType, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_COMMIT)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_RGB_COMMIT, dumb = Self(NonEmptyOrdSet::with(Opout::strict_dumb())))] #[cfg_attr( feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate", transparent) )] -// TODO: Consider requiring minimum number of inputs to be 1 -pub struct Inputs(SmallOrdSet); +pub struct Inputs(NonEmptyOrdSet); impl<'a> IntoIterator for &'a Inputs { - type Item = Input; - type IntoIter = iter::Copied>; + type Item = Opout; + type IntoIter = iter::Copied>; fn into_iter(self) -> Self::IntoIter { self.0.iter().copied() } } impl MerkleLeaves for Inputs { - type Leaf = Input; - type LeafIter<'tmp> = as MerkleLeaves>::LeafIter<'tmp>; + type Leaf = Opout; + type LeafIter<'tmp> = as MerkleLeaves>::LeafIter<'tmp>; fn merkle_leaves(&self) -> Self::LeafIter<'_> { self.0.merkle_leaves() } } -#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_COMMIT)] -#[derive(CommitEncode)] -#[commit_encode(strategy = strict, id = MerkleHash)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -#[display("{prev_out}")] -pub struct Input { - pub prev_out: Opout, - #[cfg_attr(feature = "serde", serde(skip))] - reserved: ReservedBytes<2>, -} - -impl Input { - pub fn with(prev_out: Opout) -> Input { - Input { - prev_out, - reserved: default!(), - } - } -} - /// RGB contract operation API, defined as trait /// /// Implemented by all contract operation types (see [`OpType`]): /// - Genesis ([`Genesis`]) /// - State transitions ([`Transitions`]) -/// - Public state extensions ([`Extensions`]) pub trait Operation { - /// Returns type of the operation (see [`OpType`]). Unfortunately, this - /// can't be just a const, since it will break our ability to convert - /// concrete `Node` types into `&dyn Node` (entities implementing traits - /// with const definitions can't be made into objects) - fn op_type(&self) -> OpType; - /// Returns full contract operation type information fn full_type(&self) -> OpFullType; @@ -227,58 +117,43 @@ pub trait Operation { /// Returns [`ContractId`] this operation belongs to. fn contract_id(&self) -> ContractId; - /// Returns nonce used in consensus ordering of state transitions and - /// extensions. + /// Returns nonce used in consensus ordering of state transitions fn nonce(&self) -> u64; - /// Returns [`Option::Some`]`(`[`TransitionType`]`)` for transitions or - /// [`Option::None`] for genesis and extension operation types - fn transition_type(&self) -> Option; - - /// Returns [`Option::Some`]`(`[`ExtensionType`]`)` for extension nodes or - /// [`Option::None`] for genesis and state transitions - fn extension_type(&self) -> Option; - /// Returns metadata associated with the operation, if any. fn metadata(&self) -> &Metadata; /// Returns reference to a full set of metadata (in form of [`GlobalState`] /// wrapper structure) for the contract operation. fn globals(&self) -> &GlobalState; - fn valencies(&self) -> &Valencies; fn assignments(&self) -> AssignmentsRef; fn assignments_by_type(&self, t: AssignmentType) -> Option>; - /// For genesis and public state extensions always returns an empty list. - /// While public state extension do have parent nodes, they do not contain - /// indexed rights. - fn inputs(&self) -> Inputs; - /// Provides summary about parts of the operation which are revealed. fn disclose(&self) -> OpDisclose { fn proc_seals( ty: AssignmentType, a: &[Assign], - seals: &mut BTreeMap>, - state: &mut BTreeMap, + seals: &mut BTreeMap, + state: &mut BTreeMap, ) { for (index, assignment) in a.iter().enumerate() { if let Some(seal) = assignment.revealed_seal() { seals.insert(AssignmentIndex::new(ty, index as u16), seal.to_secret_seal()); } - if let Some(revealed) = assignment.as_revealed_state() { - state.insert(AssignmentIndex::new(ty, index as u16), revealed.conceal()); - } + state.insert( + AssignmentIndex::new(ty, index as u16), + assignment.as_revealed_state().clone(), + ); } } - let mut seals: BTreeMap> = bmap!(); + let mut seals: BTreeMap = bmap!(); let mut void: BTreeMap = bmap!(); - let mut fungible: BTreeMap = bmap!(); - let mut data: BTreeMap = bmap!(); - let mut attach: BTreeMap = bmap!(); + let mut fungible: BTreeMap = bmap!(); + let mut data: BTreeMap = bmap!(); for (ty, assigns) in self.assignments().flat() { match assigns { TypedAssigns::Declarative(a) => { @@ -290,20 +165,14 @@ pub trait Operation { TypedAssigns::Structured(a) => { proc_seals(ty, &a, &mut seals, &mut data); } - TypedAssigns::Attachment(a) => { - proc_seals(ty, &a, &mut seals, &mut attach); - } } } OpDisclose { id: self.id(), seals: Confined::from_checked(seals), - fungible: Confined::from_iter_checked( - fungible.into_iter().map(|(k, s)| (k, s.commitment)), - ), + fungible: Confined::from_iter_checked(fungible), data: Confined::from_checked(data), - attach: Confined::from_checked(attach), } } @@ -340,9 +209,21 @@ impl From<&'static str> for Identity { fn from(s: &'static str) -> Self { Self(RString::from(s)) } } -impl Identity { - pub fn is_empty(&self) -> bool { self.is_anonymous() } - pub fn is_anonymous(&self) -> bool { self == &default!() } +#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)] +#[display(inner)] +#[derive(StrictType, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_RGB_COMMIT, tags = repr, into_u8, try_from_u8)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", rename_all = "camelCase") +)] +#[repr(u8)] +#[derive(Default)] +#[non_exhaustive] +pub enum SealClosingStrategy { + #[default] + FirstOpretOrTapret = 0, } #[derive(Clone, PartialEq, Eq, Debug)] @@ -356,23 +237,21 @@ impl Identity { pub struct Genesis { pub ffv: Ffv, pub schema_id: SchemaId, - pub flags: ReservedBytes<1, 0>, pub timestamp: i64, pub issuer: Identity, - pub testnet: bool, - pub alt_layers1: AltLayer1Set, - pub asset_tags: AssetTags, + pub chain_net: ChainNet, + pub seal_closing_strategy: SealClosingStrategy, pub metadata: Metadata, pub globals: GlobalState, pub assignments: Assignments, - pub valencies: Valencies, - pub validator: ReservedBytes<1, 0>, } impl StrictSerialize for Genesis {} impl StrictDeserialize for Genesis {} -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(Wrapper, WrapperMut, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)] +#[wrapper(Deref)] +#[wrapper_mut(DerefMut)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_RGB_COMMIT)] #[cfg_attr( @@ -380,30 +259,7 @@ impl StrictDeserialize for Genesis {} derive(Serialize, Deserialize), serde(crate = "serde_crate", rename_all = "camelCase") )] -pub struct Extension { - pub ffv: Ffv, - pub contract_id: ContractId, - pub nonce: u64, - pub extension_type: ExtensionType, - pub metadata: Metadata, - pub globals: GlobalState, - pub assignments: Assignments, - pub redeemed: Redeemed, - pub valencies: Valencies, - pub validator: ReservedBytes<1, 0>, - pub witness: ReservedBytes<2, 0>, -} - -impl StrictSerialize for Extension {} -impl StrictDeserialize for Extension {} - -impl Ord for Extension { - fn cmp(&self, other: &Self) -> Ordering { self.id().cmp(&other.id()) } -} - -impl PartialOrd for Extension { - fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } -} +pub struct Signature(Bytes64); #[derive(Clone, PartialEq, Eq, Debug)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] @@ -422,58 +278,12 @@ pub struct Transition { pub globals: GlobalState, pub inputs: Inputs, pub assignments: Assignments, - pub valencies: Valencies, - pub validator: ReservedBytes<1, 0>, - pub witness: ReservedBytes<2, 0>, + pub signature: Option, } impl StrictSerialize for Transition {} impl StrictDeserialize for Transition {} -impl Ord for Transition { - fn cmp(&self, other: &Self) -> Ordering { self.id().cmp(&other.id()) } -} - -impl PartialOrd for Transition { - fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } -} - -impl Conceal for Genesis { - type Concealed = Self; - fn conceal(&self) -> Self::Concealed { - let mut concealed = self.clone(); - concealed - .assignments - .keyed_values_mut() - .for_each(|(_, a)| *a = a.conceal()); - concealed - } -} - -impl Conceal for Transition { - type Concealed = Self; - fn conceal(&self) -> Self::Concealed { - let mut concealed = self.clone(); - concealed - .assignments - .keyed_values_mut() - .for_each(|(_, a)| *a = a.conceal()); - concealed - } -} - -impl Conceal for Extension { - type Concealed = Self; - fn conceal(&self) -> Self::Concealed { - let mut concealed = self.clone(); - concealed - .assignments - .keyed_values_mut() - .for_each(|(_, a)| *a = a.conceal()); - concealed - } -} - impl CommitEncode for Genesis { type CommitmentId = OpId; fn commit_encode(&self, e: &mut CommitEngine) { e.commit_to_serialized(&self.commit()) } @@ -484,31 +294,14 @@ impl CommitEncode for Transition { fn commit_encode(&self, e: &mut CommitEngine) { e.commit_to_serialized(&self.commit()) } } -impl CommitEncode for Extension { - type CommitmentId = OpId; - fn commit_encode(&self, e: &mut CommitEngine) { e.commit_to_serialized(&self.commit()) } -} - impl Transition { /// Returns reference to information about the owned rights in form of /// [`Inputs`] wrapper structure which this operation updates with /// state transition ("parent owned rights"). - pub fn prev_state(&self) -> &Inputs { &self.inputs } -} - -impl Extension { - /// Returns reference to information about the public rights (in form of - /// [`Redeemed`] wrapper structure), defined with "parent" state - /// extensions (i.e. those finalized with the current state transition) or - /// referenced by another state extension, which this operation updates - /// ("parent public rights"). - pub fn redeemed(&self) -> &Redeemed { &self.redeemed } + pub fn inputs(&self) -> &Inputs { &self.inputs } } impl Operation for Genesis { - #[inline] - fn op_type(&self) -> OpType { OpType::Genesis } - #[inline] fn full_type(&self) -> OpFullType { OpFullType::Genesis } @@ -521,21 +314,12 @@ impl Operation for Genesis { #[inline] fn nonce(&self) -> u64 { u64::MAX } - #[inline] - fn transition_type(&self) -> Option { None } - - #[inline] - fn extension_type(&self) -> Option { None } - #[inline] fn metadata(&self) -> &Metadata { &self.metadata } #[inline] fn globals(&self) -> &GlobalState { &self.globals } - #[inline] - fn valencies(&self) -> &Valencies { &self.valencies } - #[inline] fn assignments(&self) -> AssignmentsRef { (&self.assignments).into() } @@ -545,60 +329,9 @@ impl Operation for Genesis { .get(&t) .map(TypedAssigns::transmutate_seals) } - - #[inline] - fn inputs(&self) -> Inputs { empty!() } -} - -impl Operation for Extension { - #[inline] - fn op_type(&self) -> OpType { OpType::StateExtension } - - #[inline] - fn full_type(&self) -> OpFullType { OpFullType::StateExtension(self.extension_type) } - - #[inline] - fn id(&self) -> OpId { self.commit_id() } - - #[inline] - fn contract_id(&self) -> ContractId { self.contract_id } - - #[inline] - fn nonce(&self) -> u64 { self.nonce } - - #[inline] - fn transition_type(&self) -> Option { None } - - #[inline] - fn extension_type(&self) -> Option { Some(self.extension_type) } - - #[inline] - fn metadata(&self) -> &Metadata { &self.metadata } - - #[inline] - fn globals(&self) -> &GlobalState { &self.globals } - - #[inline] - fn valencies(&self) -> &Valencies { &self.valencies } - - #[inline] - fn assignments(&self) -> AssignmentsRef { (&self.assignments).into() } - - #[inline] - fn assignments_by_type(&self, t: AssignmentType) -> Option> { - self.assignments - .get(&t) - .map(TypedAssigns::transmutate_seals) - } - - #[inline] - fn inputs(&self) -> Inputs { empty!() } } impl Operation for Transition { - #[inline] - fn op_type(&self) -> OpType { OpType::StateTransition } - #[inline] fn full_type(&self) -> OpFullType { OpFullType::StateTransition(self.transition_type) } @@ -611,21 +344,12 @@ impl Operation for Transition { #[inline] fn nonce(&self) -> u64 { self.nonce } - #[inline] - fn transition_type(&self) -> Option { Some(self.transition_type) } - - #[inline] - fn extension_type(&self) -> Option { None } - #[inline] fn metadata(&self) -> &Metadata { &self.metadata } #[inline] fn globals(&self) -> &GlobalState { &self.globals } - #[inline] - fn valencies(&self) -> &Valencies { &self.valencies } - #[inline] fn assignments(&self) -> AssignmentsRef { (&self.assignments).into() } @@ -633,12 +357,12 @@ impl Operation for Transition { fn assignments_by_type(&self, t: AssignmentType) -> Option> { self.assignments.get(&t).cloned() } - - fn inputs(&self) -> Inputs { self.inputs.clone() } } #[cfg(test)] mod test { + use std::str::FromStr; + use amplify::ByteArray; use baid64::DisplayBaid64; diff --git a/src/operation/seal.rs b/src/operation/seal.rs index 186af74c..94dab24e 100644 --- a/src/operation/seal.rs +++ b/src/operation/seal.rs @@ -23,25 +23,18 @@ use core::fmt::Debug; use std::hash::Hash; -use bp::dbc::Method; pub use bp::seals::txout::blind::{ChainBlindSeal, ParseError, SingleBlindSeal}; +use bp::seals::txout::ExplicitSeal; pub use bp::seals::txout::TxoSeal; -use bp::seals::txout::{BlindSeal, CloseMethod, ExplicitSeal, SealTxid}; pub use bp::seals::SecretSeal; -use bp::{Outpoint, Txid, Vout}; +use bp::Txid; use commit_verify::Conceal; use strict_encoding::{StrictDecode, StrictDumb, StrictEncode}; -use crate::{XChain, XOutpoint}; +pub type GenesisSeal = SingleBlindSeal; +pub type GraphSeal = ChainBlindSeal; -pub type GenesisSeal = SingleBlindSeal; -pub type GraphSeal = ChainBlindSeal; - -pub type OutputSeal = ExplicitSeal; - -pub type XGenesisSeal = XChain; -pub type XGraphSeal = XChain; -pub type XOutputSeal = XChain; +pub type OutputSeal = ExplicitSeal; pub trait ExposedSeal: Debug @@ -55,109 +48,44 @@ pub trait ExposedSeal: + TxoSeal + Conceal { -} - -impl ExposedSeal for GraphSeal {} - -impl ExposedSeal for GenesisSeal {} - -impl TxoSeal for XChain { - fn method(&self) -> CloseMethod { - match self { - XChain::Bitcoin(seal) | XChain::Liquid(seal) => seal.method(), - XChain::Other(_) => unreachable!(), - } - } - - fn txid(&self) -> Option { - match self { - XChain::Bitcoin(seal) | XChain::Liquid(seal) => seal.txid(), - XChain::Other(_) => unreachable!(), - } - } - - fn vout(&self) -> Vout { - match self { - XChain::Bitcoin(seal) | XChain::Liquid(seal) => seal.vout(), - XChain::Other(_) => unreachable!(), - } - } - - fn outpoint(&self) -> Option { - match self { - XChain::Bitcoin(seal) | XChain::Liquid(seal) => seal.outpoint(), - XChain::Other(_) => unreachable!(), - } - } - - fn txid_or(&self, default_txid: Txid) -> Txid { - match self { - XChain::Bitcoin(seal) | XChain::Liquid(seal) => seal.txid_or(default_txid), - XChain::Other(_) => unreachable!(), - } + #[inline] + fn to_output_seal(self) -> Option { + let outpoint = self.outpoint()?; + Some(ExplicitSeal::new(outpoint)) } - fn outpoint_or(&self, default_txid: Txid) -> Outpoint { - match self { - XChain::Bitcoin(seal) | XChain::Liquid(seal) => seal.outpoint_or(default_txid), - XChain::Other(_) => unreachable!(), - } + fn to_output_seal_or_default(self, witness_id: Txid) -> OutputSeal { + self.to_output_seal() + .unwrap_or(ExplicitSeal::new(self.outpoint_or(witness_id))) } } -/* -#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_COMMIT)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", transparent) -)] -pub struct SealPreimage(Bytes32); - */ - -impl From> for XOutpoint { - #[inline] - fn from(seal: XChain) -> Self { seal.to_outpoint() } -} - -impl XChain { - pub fn transmutate(self) -> XChain { self.map_ref(|seal| seal.transmutate()) } - - /// Converts seal into a transaction outpoint. - #[inline] - pub fn to_outpoint(&self) -> XOutpoint { self.map_ref(GenesisSeal::to_outpoint).into() } -} +impl ExposedSeal for GraphSeal {} -impl XChain> { - /// Converts revealed seal into concealed. - #[inline] - pub fn to_secret_seal(&self) -> XChain { self.conceal() } -} +impl ExposedSeal for GenesisSeal {} #[cfg(test)] mod test { use amplify::hex::FromHex; - use bp::seals::txout::TxPtr; + use bp::seals::txout::{BlindSeal, TxPtr}; + use bp::Vout; use super::*; #[test] fn secret_seal_is_sha256d() { - let reveal = XChain::Bitcoin(BlindSeal { - method: CloseMethod::TapretFirst, + let reveal = BlindSeal { blinding: 54683213134637, txid: TxPtr::Txid( Txid::from_hex("646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839") .unwrap(), ), vout: Vout::from(2), - }); + }; let secret = reveal.to_secret_seal(); assert_eq!( secret.to_string(), - "bc:utxob:lD72u61i-sxCEKth-vqjH0mI-kcEwa1Q-fbnPLon-tDtXveO-keHh0" + "utxob:nBRVm39A-ioJydHE-ug2d90m-aZyfPI0-MCc0ZNM-oMXMs2O-opKQ7" ); assert_eq!(reveal.to_secret_seal(), reveal.conceal()) } diff --git a/src/operation/state.rs b/src/operation/state.rs index db230257..a0e92e80 100644 --- a/src/operation/state.rs +++ b/src/operation/state.rs @@ -23,32 +23,14 @@ use core::fmt::Debug; use core::hash::Hash; -use commit_verify::Conceal; use strict_encoding::{StrictDecode, StrictDumb, StrictEncode}; -use crate::{ - ConcealedAttach, ConcealedData, ConcealedValue, RevealedAttach, RevealedData, RevealedValue, -}; - -/// Marker trait for types of state which are just a commitment to the actual -/// state data. -pub trait ConfidentialState: Debug + Eq + Copy { - fn state_type(&self) -> StateType; - fn state_commitment(&self) -> ConcealedState; -} +use crate::{RevealedData, RevealedValue}; /// Marker trait for types of state holding explicit state data. pub trait ExposedState: - Debug - + StrictDumb - + StrictEncode - + StrictDecode - + Conceal - + Eq - + Ord - + Clone + Debug + StrictDumb + StrictEncode + StrictDecode + Eq + Ord + Clone { - type Confidential: ConfidentialState + StrictEncode + StrictDecode + StrictDumb; fn state_type(&self) -> StateType; fn state_data(&self) -> RevealedState; } @@ -65,15 +47,11 @@ pub enum StateType { /// No state data Void, - /// Value-based state, i.e. which can be committed to with a Pedersen - /// commitment + /// Value-based state Fungible, /// State defined with custom data Structured, - - /// Attached data container - Attachment, } /// Categories of the state @@ -87,7 +65,6 @@ pub enum RevealedState { Void, Fungible(RevealedValue), Structured(RevealedData), - Attachment(RevealedAttach), } impl RevealedState { @@ -96,33 +73,6 @@ impl RevealedState { RevealedState::Void => StateType::Void, RevealedState::Fungible(_) => StateType::Fungible, RevealedState::Structured(_) => StateType::Structured, - RevealedState::Attachment(_) => StateType::Attachment, - } - } -} - -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase", tag = "type") -)] -#[allow(clippy::large_enum_variant)] -pub enum ConcealedState { - Void, - Fungible(ConcealedValue), - Structured(ConcealedData), - Attachment(ConcealedAttach), -} - -impl ConfidentialState for ConcealedState { - fn state_type(&self) -> StateType { - match self { - ConcealedState::Void => StateType::Void, - ConcealedState::Fungible(_) => StateType::Fungible, - ConcealedState::Structured(_) => StateType::Structured, - ConcealedState::Attachment(_) => StateType::Attachment, } } - fn state_commitment(&self) -> ConcealedState { *self } } diff --git a/src/operation/xchain.rs b/src/operation/xchain.rs deleted file mode 100644 index 81fc737d..00000000 --- a/src/operation/xchain.rs +++ /dev/null @@ -1,580 +0,0 @@ -// RGB Core Library: consensus layer for RGB smart contracts. -// -// 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. -// Copyright (C) 2019-2024 Dr Maxim Orlovsky. 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::convert::Infallible; -use std::fmt::{Debug, Display, Formatter}; -use std::str::FromStr; -use std::{fmt, io}; - -use amplify::confinement::TinyOrdSet; -use bp::{Bp, Outpoint}; -use commit_verify::{Conceal, StrictHash}; -use strict_encoding::{ - DecodeError, DefineUnion, ReadTuple, ReadUnion, StrictDecode, StrictDumb, StrictEncode, - StrictEnum, StrictSum, StrictType, StrictUnion, TypedRead, TypedWrite, VariantError, - WriteUnion, -}; - -use crate::{OutputSeal, XOutputSeal, LIB_NAME_RGB_COMMIT}; - -pub const XCHAIN_BITCOIN_PREFIX: &str = "bc"; -pub const XCHAIN_LIQUID_PREFIX: &str = "lq"; - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -#[display(lowercase)] -#[derive( - strict_encoding::StrictType, - StrictDumb, - strict_encoding::StrictEncode, - strict_encoding::StrictDecode -)] -#[strict_type(lib = LIB_NAME_RGB_COMMIT, 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 Layer1 { - #[strict_type(dumb)] - Bitcoin = 0, - Liquid = 1, -} - -#[derive(Wrapper, WrapperMut, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, From)] -#[wrapper(Deref, FromStr, Display)] -#[wrapper_mut(DerefMut)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_COMMIT)] -pub struct XOutpoint(XChain); - -impl From for XOutpoint { - #[inline] - fn from(seal: XOutputSeal) -> Self { seal.to_outpoint() } -} - -impl XOutputSeal { - /// Converts seal into a transaction outpoint. - #[inline] - pub fn to_outpoint(&self) -> XOutpoint { self.map_ref(OutputSeal::to_outpoint).into() } -} - -#[cfg(feature = "serde")] -mod _serde { - use serde_crate::de::Error; - use serde_crate::{Deserialize, Deserializer, Serialize, Serializer}; - - use super::*; - - impl Serialize for XOutpoint { - fn serialize(&self, serializer: S) -> Result - where S: Serializer { - if serializer.is_human_readable() { - serializer.serialize_str(&self.to_string()) - } else { - self.0.serialize(serializer) - } - } - } - - impl<'de> Deserialize<'de> for XOutpoint { - fn deserialize(deserializer: D) -> Result - where D: Deserializer<'de> { - if deserializer.is_human_readable() { - let s = String::deserialize(deserializer)?; - Self::from_str(&s).map_err(D::Error::custom) - } else { - XChain::::deserialize(deserializer).map(Self) - } - } - } -} - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -#[display(lowercase)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_COMMIT, 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 AltLayer1 { - #[strict_type(dumb)] - Liquid = 1, - // Abraxas = 0x10, - // Prime = 0x11, -} - -impl AltLayer1 { - pub fn layer1(&self) -> Layer1 { - match self { - AltLayer1::Liquid => Layer1::Liquid, - } - } -} - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase", tag = "chain", content = "data") -)] -pub enum Impossible {} - -impl TryFrom for Impossible { - type Error = VariantError; - - fn try_from(_: u8) -> Result { panic!("must not be instantiated") } -} -impl From for u8 { - fn from(_: Impossible) -> Self { unreachable!() } -} - -impl StrictDumb for Impossible { - fn strict_dumb() -> Self { panic!("must not be instantiated") } -} -impl StrictType for Impossible { - const STRICT_LIB_NAME: &'static str = LIB_NAME_RGB_COMMIT; -} -impl StrictSum for Impossible { - const ALL_VARIANTS: &'static [(u8, &'static str)] = &[]; - fn variant_name(&self) -> &'static str { unreachable!() } -} -impl StrictEnum for Impossible {} -impl StrictEncode for Impossible { - fn strict_encode(&self, _writer: W) -> io::Result { unreachable!() } -} -impl StrictDecode for Impossible { - fn strict_decode(_reader: &mut impl TypedRead) -> Result { - panic!("must not be deserialized") - } -} - -impl Conceal for Impossible { - type Concealed = Self; - fn conceal(&self) -> Self::Concealed { unreachable!() } -} - -impl Display for Impossible { - fn fmt(&self, _: &mut Formatter<'_>) -> fmt::Result { unreachable!() } -} -impl FromStr for Impossible { - type Err = Infallible; - fn from_str(_: &str) -> Result { panic!("must not be parsed") } -} - -#[derive(Wrapper, WrapperMut, Clone, PartialEq, Eq, Hash, Debug, Default, From)] -#[wrapper(Deref)] -#[wrapper_mut(DerefMut)] -#[derive(StrictType, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_COMMIT)] -#[derive(CommitEncode)] -#[commit_encode(strategy = strict, id = StrictHash)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", transparent) -)] -pub struct AltLayer1Set(TinyOrdSet); - -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase", tag = "chain", content = "data") -)] -pub enum XChain { - Bitcoin(T), - - Liquid(T), - - Other(X), -} - -impl PartialOrd for XChain { - fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } -} - -impl Ord for XChain { - fn cmp(&self, other: &Self) -> Ordering { - match (self, other) { - (Self::Bitcoin(t1), Self::Bitcoin(t2)) => t1.cmp(t2), - (Self::Liquid(t1), Self::Liquid(t2)) => t1.cmp(t2), - (Self::Bitcoin(_), _) => Ordering::Greater, - (_, Self::Bitcoin(_)) => Ordering::Less, - (Self::Liquid(_), _) => Ordering::Greater, - (_, Self::Liquid(_)) => Ordering::Less, - (Self::Other(x1), Self::Other(x2)) => x1.cmp(x2), - } - } -} - -impl Conceal for XChain { - type Concealed = XChain; - - #[inline] - fn conceal(&self) -> Self::Concealed { self.map2_ref(|t| t.conceal(), |x| x.conceal()) } -} - -impl StrictType for XChain -where T: StrictDumb + StrictType -{ - const STRICT_LIB_NAME: &'static str = LIB_NAME_RGB_COMMIT; -} -impl StrictSum for XChain -where T: StrictDumb + StrictType -{ - const ALL_VARIANTS: &'static [(u8, &'static str)] = &[(0x00, "bitcoin"), (0x01, "liquid")]; - - fn variant_name(&self) -> &'static str { - match self { - XChain::Bitcoin(_) => Self::ALL_VARIANTS[0].1, - XChain::Liquid(_) => Self::ALL_VARIANTS[1].1, - XChain::Other(_) => unreachable!(), - } - } -} -impl StrictUnion for XChain where T: StrictDumb + StrictType {} -impl StrictDumb for XChain -where T: StrictDumb -{ - fn strict_dumb() -> Self { XChain::Bitcoin(strict_dumb!()) } -} -impl StrictEncode for XChain -where T: StrictDumb + StrictEncode -{ - fn strict_encode(&self, writer: W) -> io::Result { - writer.write_union::(|w| { - let w = w - .define_newtype::(vname!(Self::ALL_VARIANTS[0].1)) - .define_newtype::(vname!(Self::ALL_VARIANTS[1].1)) - .complete(); - Ok(match self { - XChain::Bitcoin(t) => w.write_newtype(vname!(Self::ALL_VARIANTS[0].1), t)?, - XChain::Liquid(t) => w.write_newtype(vname!(Self::ALL_VARIANTS[1].1), t)?, - XChain::Other(_) => unreachable!(), - } - .complete()) - }) - } -} -impl StrictDecode for XChain -where T: StrictDumb + StrictDecode -{ - fn strict_decode(reader: &mut impl TypedRead) -> Result { - reader.read_union(|field, r| match field.as_str() { - x if x == Self::ALL_VARIANTS[0].1 => { - r.read_tuple(|r| r.read_field().map(Self::Bitcoin)) - } - x if x == Self::ALL_VARIANTS[1].1 => r.read_tuple(|r| r.read_field().map(Self::Liquid)), - _ => unreachable!(), - }) - } -} - -impl XChain { - pub fn layer1(&self) -> Layer1 { - match self { - XChain::Bitcoin(_) => Layer1::Bitcoin, - XChain::Liquid(_) => Layer1::Liquid, - XChain::Other(_) => unreachable!(), - } - } - - pub fn as_bp(&self) -> Bp<&T> - where for<'a> &'a T: StrictDumb + StrictEncode + StrictDecode { - match self { - XChain::Bitcoin(t) => Bp::Bitcoin(t), - XChain::Liquid(t) => Bp::Liquid(t), - XChain::Other(_) => unreachable!(), - } - } - - pub fn into_bp(self) -> Bp - where T: StrictDumb + StrictEncode + StrictDecode { - match self { - XChain::Bitcoin(t) => Bp::Bitcoin(t), - XChain::Liquid(t) => Bp::Liquid(t), - XChain::Other(_) => unreachable!(), - } - } - - pub fn as_reduced_unsafe(&self) -> &T { - match self { - XChain::Bitcoin(t) | XChain::Liquid(t) => t, - XChain::Other(_) => unreachable!(), - } - } - - /// Maps the value from one internal type into another. - pub fn map(self, f: impl FnOnce(T) -> U) -> XChain { - match self { - Self::Bitcoin(t) => XChain::Bitcoin(f(t)), - Self::Liquid(t) => XChain::Liquid(f(t)), - Self::Other(_) => unreachable!(), - } - } - - /// Maps the value from a reference on internal type into another. - pub fn map_ref(&self, f: impl FnOnce(&T) -> U) -> XChain { - match self { - Self::Bitcoin(t) => XChain::Bitcoin(f(t)), - Self::Liquid(t) => XChain::Liquid(f(t)), - Self::Other(_) => unreachable!(), - } - } - - /// Maps the value from one internal type into another, covering cases which - /// may error. - pub fn try_map(self, f: impl FnOnce(T) -> Result) -> Result, E> { - match self { - Self::Bitcoin(t) => f(t).map(XChain::Bitcoin), - Self::Liquid(t) => f(t).map(XChain::Liquid), - Self::Other(_) => unreachable!(), - } - } - - /// Maps the value from one internal type into another, covering cases which - /// may error. - pub fn try_map_ref(&self, f: impl FnOnce(&T) -> Result) -> Result, E> { - match self { - Self::Bitcoin(t) => f(t).map(XChain::Bitcoin), - Self::Liquid(t) => f(t).map(XChain::Liquid), - Self::Other(_) => unreachable!(), - } - } - - /// Maps the value from one internal type into another, covering cases which - /// may result in an optional value. - pub fn maybe_map(self, f: impl FnOnce(T) -> Option) -> Option> { - match self { - Self::Bitcoin(t) => f(t).map(XChain::Bitcoin), - Self::Liquid(t) => f(t).map(XChain::Liquid), - Self::Other(_) => unreachable!(), - } - } - - /// Maps the value from one internal type into another, covering cases which - /// may result in an optional value. - pub fn maybe_map_ref(&self, f: impl FnOnce(&T) -> Option) -> Option> { - match self { - Self::Bitcoin(t) => f(t).map(XChain::Bitcoin), - Self::Liquid(t) => f(t).map(XChain::Liquid), - Self::Other(_) => unreachable!(), - } - } - - /// Returns iterator over elements - pub fn iter<'i>( - &'i self, - ) -> Box::Item>> + 'i> - where &'i T: IntoIterator { - match self { - XChain::Bitcoin(t) => Box::new(t.into_iter().map(XChain::Bitcoin)), - XChain::Liquid(t) => Box::new(t.into_iter().map(XChain::Liquid)), - Self::Other(_) => unreachable!(), - } - } -} - -impl XChain { - pub fn with(layer1: Layer1, data: impl Into) -> Self { - match layer1 { - Layer1::Bitcoin => XChain::Bitcoin(data.into()), - Layer1::Liquid => XChain::Liquid(data.into()), - } - } - - pub fn is_bitcoin(&self) -> bool { matches!(self, XChain::Bitcoin(_)) } - pub fn is_liquid(&self) -> bool { matches!(self, XChain::Liquid(_)) } - pub fn is_bp(&self) -> bool { - match self { - XChain::Bitcoin(_) | XChain::Liquid(_) => true, - XChain::Other(_) => false, - } - } - - /// Maps the value from one internal type into another. - pub fn map2(self, f1: impl FnOnce(T) -> U, f2: impl FnOnce(X) -> Y) -> XChain { - match self { - Self::Bitcoin(t) => XChain::Bitcoin(f1(t)), - Self::Liquid(t) => XChain::Liquid(f1(t)), - Self::Other(x) => XChain::Other(f2(x)), - } - } - - /// Maps the value from a reference on internal type into another. - pub fn map2_ref( - &self, - f1: impl FnOnce(&T) -> U, - f2: impl FnOnce(&X) -> Y, - ) -> XChain { - match self { - Self::Bitcoin(t) => XChain::Bitcoin(f1(t)), - Self::Liquid(t) => XChain::Liquid(f1(t)), - Self::Other(x) => XChain::Other(f2(x)), - } - } - - /// Maps the value from one internal type into another, covering cases which - /// may error. - pub fn try_map2( - self, - f1: impl FnOnce(T) -> Result, - f2: impl FnOnce(X) -> Result, - ) -> Result, E> { - match self { - Self::Bitcoin(t) => f1(t).map(XChain::Bitcoin), - Self::Liquid(t) => f1(t).map(XChain::Liquid), - Self::Other(x) => f2(x).map(XChain::Other), - } - } - - /// Maps the value from one internal type into another, covering cases which - /// may error. - pub fn try_map2_ref( - &self, - f1: impl FnOnce(&T) -> Result, - f2: impl FnOnce(&X) -> Result, - ) -> Result, E> { - match self { - Self::Bitcoin(t) => f1(t).map(XChain::Bitcoin), - Self::Liquid(t) => f1(t).map(XChain::Liquid), - Self::Other(x) => f2(x).map(XChain::Other), - } - } - - /// Maps the value from one internal type into another, covering cases which - /// may result in an optional value. - pub fn maybe_map2( - self, - f1: impl FnOnce(T) -> Option, - f2: impl FnOnce(X) -> Option, - ) -> Option> { - match self { - Self::Bitcoin(t) => f1(t).map(XChain::Bitcoin), - Self::Liquid(t) => f1(t).map(XChain::Liquid), - Self::Other(x) => f2(x).map(XChain::Other), - } - } - - /// Maps the value from one internal type into another, covering cases which - /// may result in an optional value. - pub fn maybe_map2_ref( - &self, - f1: impl FnOnce(&T) -> Option, - f2: impl FnOnce(&X) -> Option, - ) -> Option> { - match self { - Self::Bitcoin(t) => f1(t).map(XChain::Bitcoin), - Self::Liquid(t) => f1(t).map(XChain::Liquid), - Self::Other(x) => f2(x).map(XChain::Other), - } - } -} - -impl<'a, T: Copy, X: Copy> XChain<&'a T, &'a X> { - pub fn copied(self) -> XChain { self.map2(|t| *t, |x| *x) } -} - -impl<'a, T: Clone, X: Clone> XChain<&'a T, &'a X> { - pub fn cloned(self) -> XChain { self.map2(T::clone, X::clone) } -} - -impl XChain, Impossible> { - pub fn transpose(self) -> Option> { - match self { - XChain::Bitcoin(inner) => inner.map(XChain::Bitcoin), - XChain::Liquid(inner) => inner.map(XChain::Liquid), - XChain::Other(_) => unreachable!(), - } - } -} - -impl Iterator for XChain { - type Item = XChain<::Item>; - - fn next(&mut self) -> Option { - match self { - XChain::Bitcoin(t) => t.next().map(XChain::Bitcoin), - XChain::Liquid(t) => t.next().map(XChain::Liquid), - XChain::Other(_) => unreachable!(), - } - } -} - -#[derive(Clone, Debug, Display, Error, From)] -pub enum XChainParseError { - #[display("unknown chain prefix '{0}'; only 'bc:' and 'lq:' are currently supported")] - UnknownPrefix(String), - - #[from] - #[display(inner)] - Inner(E), -} - -impl FromStr for XChain -where - T: StrictDumb + StrictEncode + StrictDecode, - T::Err: Debug + Display, - X: StrictDumb + StrictEncode + StrictDecode, - X::Err: Debug + Display, -{ - type Err = XChainParseError; - - fn from_str(s: &str) -> Result { - if let Some((prefix, s)) = s.split_once(':') { - match prefix { - XCHAIN_BITCOIN_PREFIX => s - .parse() - .map(XChain::Bitcoin) - .map_err(XChainParseError::from), - XCHAIN_LIQUID_PREFIX => s - .parse() - .map(XChain::Liquid) - .map_err(XChainParseError::from), - unknown => Err(XChainParseError::UnknownPrefix(unknown.to_owned())), - } - } else { - s.parse() - .map(XChain::Bitcoin) - .map_err(XChainParseError::from) - } - } -} - -impl Display for XChain -where - T: StrictDumb + StrictEncode + StrictDecode, - X: StrictDumb + StrictEncode + StrictDecode, -{ - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - XChain::Bitcoin(t) => write!(f, "{XCHAIN_BITCOIN_PREFIX}:{t}"), - XChain::Liquid(t) => write!(f, "{XCHAIN_LIQUID_PREFIX}:{t}"), - XChain::Other(x) => Display::fmt(x, f), - } - } -} diff --git a/src/schema/mod.rs b/src/schema/mod.rs index 54073447..f6b436d0 100644 --- a/src/schema/mod.rs +++ b/src/schema/mod.rs @@ -28,8 +28,11 @@ mod occurrences; pub use occurrences::{Occurrences, OccurrencesMismatch}; pub use operations::{ - AssignmentType, AssignmentsSchema, ExtensionSchema, GenesisSchema, GlobalSchema, MetaSchema, - OpFullType, OpSchema, OpType, TransitionSchema, ValencySchema, ValencyType, + AssignmentType, AssignmentsSchema, GenesisSchema, GlobalSchema, MetaSchema, OpFullType, + OpSchema, TransitionSchema, }; -pub use schema::{ExtensionType, GlobalStateType, MetaType, Schema, SchemaId, TransitionType}; -pub use state::{FungibleType, GlobalStateSchema, MediaType, OwnedStateSchema}; +pub use schema::{ + AssignmentDetails, GlobalDetails, GlobalStateType, MetaDetails, MetaType, Schema, SchemaId, + TransitionDetails, TransitionType, +}; +pub use state::{FungibleType, GlobalStateSchema, OwnedStateSchema}; diff --git a/src/schema/operations.rs b/src/schema/operations.rs index fa1dc820..d9c7e312 100644 --- a/src/schema/operations.rs +++ b/src/schema/operations.rs @@ -24,7 +24,7 @@ use aluvm::library::LibSite; use amplify::confinement::{TinyOrdMap, TinyOrdSet}; use amplify::Wrapper; -use super::{ExtensionType, GlobalStateType, Occurrences, TransitionType}; +use super::{GlobalStateType, Occurrences, TransitionType}; use crate::schema::schema::MetaType; use crate::LIB_NAME_RGB_COMMIT; @@ -43,59 +43,17 @@ impl AssignmentType { pub const fn with(ty: u16) -> Self { Self(ty) } #[inline] pub fn to_le_bytes(&self) -> [u8; 2] { self.0.to_le_bytes() } -} -#[derive(Wrapper, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From, Display)] -#[wrapper(FromStr, LowerHex, UpperHex)] -#[display("0x{0:04X}")] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_COMMIT)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub struct ValencyType(u16); -impl ValencyType { - pub const fn with(ty: u16) -> Self { Self(ty) } + pub const ASSET: Self = AssignmentType(4000); + pub fn is_asset(self) -> bool { self == Self::ASSET } } pub type MetaSchema = TinyOrdSet; pub type GlobalSchema = TinyOrdMap; -pub type ValencySchema = TinyOrdSet; pub type InputsSchema = TinyOrdMap; pub type AssignmentsSchema = TinyOrdMap; -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -#[repr(u8)] -/// Node type: genesis, extensions and state transitions -pub enum OpType { - /// Genesis: single operation per contract, defining contract and - /// committing to a specific schema and underlying chain hash - #[display("genesis")] - Genesis = 0, - - /// Multiple points for decentralized & unowned contract extension, - /// committing either to a genesis or some state transition via their - /// valencies - #[display("extension")] - StateExtension = 1, - - /// State transition performing owned change to the state data and - /// committing to (potentially multiple) ancestors (i.e. genesis, - /// extensions and/or other state transitions) via spending - /// corresponding transaction outputs assigned some state by ancestors - #[display("transition")] - StateTransition = 2, -} - -/// Aggregated type used to supply full contract operation type and -/// transition/state extension type information +/// Aggregated type used to supply full contract operation type and transition type information #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)] #[cfg_attr( feature = "serde", @@ -104,16 +62,19 @@ pub enum OpType { )] pub enum OpFullType { /// Genesis operation (no subtypes) + /// + /// Genesis: single operation per contract, defining contract and + /// committing to a specific schema and underlying chain hash #[display("genesis")] Genesis, /// State transition contract operation, subtyped by transition type - #[display("state transition #{0}")] + /// + /// State transition performing owned change to the state data and committing to (potentially + /// multiple) ancestors (i.e. genesis and/or other state transitions) via spending + /// corresponding transaction outputs assigned some state by ancestors + #[display("transition #{0}")] StateTransition(TransitionType), - - /// State extension contract operation, subtyped by extension type - #[display("state extension #{0}")] - StateExtension(ExtensionType), } impl OpFullType { @@ -121,24 +82,18 @@ impl OpFullType { match self { OpFullType::Genesis => 0, OpFullType::StateTransition(ty) => ty.to_inner(), - OpFullType::StateExtension(ty) => ty.to_inner(), } } pub fn is_transition(self) -> bool { matches!(self, Self::StateTransition(_)) } - - pub fn is_extension(self) -> bool { matches!(self, Self::StateExtension(_)) } } /// Trait defining common API for all operation type schemata pub trait OpSchema { - fn op_type(&self) -> OpType; fn metadata(&self) -> &MetaSchema; fn globals(&self) -> &GlobalSchema; fn inputs(&self) -> Option<&InputsSchema>; - fn redeems(&self) -> Option<&ValencySchema>; fn assignments(&self) -> &AssignmentsSchema; - fn valencies(&self) -> &ValencySchema; } #[derive(Clone, PartialEq, Eq, Debug, Default)] @@ -153,28 +108,10 @@ pub struct GenesisSchema { pub metadata: MetaSchema, pub globals: GlobalSchema, pub assignments: AssignmentsSchema, - pub valencies: ValencySchema, // NB: it is possible to transform option into enum covering other virtual machines pub validator: Option, } -#[derive(Clone, PartialEq, Eq, Debug, Default)] -#[derive(StrictType, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_COMMIT)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") -)] -pub struct ExtensionSchema { - pub metadata: MetaSchema, - pub globals: GlobalSchema, - pub redeems: ValencySchema, - pub assignments: AssignmentsSchema, - pub valencies: ValencySchema, - pub validator: Option, -} - #[derive(Clone, PartialEq, Eq, Debug, Default)] #[derive(StrictType, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_RGB_COMMIT)] @@ -188,13 +125,10 @@ pub struct TransitionSchema { pub globals: GlobalSchema, pub inputs: InputsSchema, pub assignments: AssignmentsSchema, - pub valencies: ValencySchema, pub validator: Option, } impl OpSchema for GenesisSchema { - #[inline] - fn op_type(&self) -> OpType { OpType::Genesis } #[inline] fn metadata(&self) -> &MetaSchema { &self.metadata } #[inline] @@ -202,33 +136,10 @@ impl OpSchema for GenesisSchema { #[inline] fn inputs(&self) -> Option<&InputsSchema> { None } #[inline] - fn redeems(&self) -> Option<&ValencySchema> { None } - #[inline] fn assignments(&self) -> &AssignmentsSchema { &self.assignments } - #[inline] - fn valencies(&self) -> &ValencySchema { &self.valencies } -} - -impl OpSchema for ExtensionSchema { - #[inline] - fn op_type(&self) -> OpType { OpType::StateExtension } - #[inline] - fn metadata(&self) -> &MetaSchema { &self.metadata } - #[inline] - fn globals(&self) -> &GlobalSchema { &self.globals } - #[inline] - fn inputs(&self) -> Option<&InputsSchema> { None } - #[inline] - fn redeems(&self) -> Option<&ValencySchema> { Some(&self.redeems) } - #[inline] - fn assignments(&self) -> &AssignmentsSchema { &self.assignments } - #[inline] - fn valencies(&self) -> &ValencySchema { &self.valencies } } impl OpSchema for TransitionSchema { - #[inline] - fn op_type(&self) -> OpType { OpType::StateTransition } #[inline] fn metadata(&self) -> &MetaSchema { &self.metadata } #[inline] @@ -236,9 +147,5 @@ impl OpSchema for TransitionSchema { #[inline] fn inputs(&self) -> Option<&AssignmentsSchema> { Some(&self.inputs) } #[inline] - fn redeems(&self) -> Option<&ValencySchema> { None } - #[inline] fn assignments(&self) -> &AssignmentsSchema { &self.assignments } - #[inline] - fn valencies(&self) -> &ValencySchema { &self.valencies } } diff --git a/src/schema/schema.rs b/src/schema/schema.rs index 5f70800f..69cf6796 100644 --- a/src/schema/schema.rs +++ b/src/schema/schema.rs @@ -25,23 +25,17 @@ use std::fmt::{self, Display, Formatter}; use std::str::FromStr; use aluvm::library::LibId; -use amplify::confinement::{TinyOrdMap, TinyOrdSet}; +use amplify::confinement::TinyOrdMap; use amplify::{ByteArray, Bytes32}; use baid64::{Baid64ParseError, DisplayBaid64, FromBaid64Str}; -use commit_verify::{ - CommitEncode, CommitEngine, CommitId, CommitmentId, DigestExt, ReservedBytes, Sha256, -}; +use commit_verify::{CommitEncode, CommitEngine, CommitId, CommitmentId, DigestExt, Sha256}; use strict_encoding::{ StrictDecode, StrictDeserialize, StrictEncode, StrictSerialize, StrictType, TypeName, }; -use strict_types::SemId; +use strict_types::{FieldName, SemId}; -use super::{ - AssignmentType, ExtensionSchema, GenesisSchema, OwnedStateSchema, TransitionSchema, ValencyType, -}; -use crate::{ - impl_serde_baid64, Ffv, GlobalStateSchema, Identity, Occurrences, LIB_NAME_RGB_COMMIT, -}; +use super::{AssignmentType, GenesisSchema, OwnedStateSchema, TransitionSchema}; +use crate::{impl_serde_baid64, Ffv, GlobalStateSchema, StateType, LIB_NAME_RGB_COMMIT}; #[derive(Wrapper, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From, Display)] #[wrapper(FromStr, LowerHex, UpperHex)] @@ -83,30 +77,67 @@ impl GlobalStateType { derive(Serialize, Deserialize), serde(crate = "serde_crate", rename_all = "camelCase") )] -pub struct ExtensionType(u16); -impl ExtensionType { +pub struct TransitionType(u16); +impl TransitionType { pub const fn with(ty: u16) -> Self { Self(ty) } } -#[derive(Wrapper, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From, Display)] -#[wrapper(FromStr, LowerHex, UpperHex)] -#[display("0x{0:04X}")] +#[derive(Clone, PartialEq, Eq, Debug)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_COMMIT)] +#[strict_type(lib = LIB_NAME_RGB_COMMIT, tags = order)] #[cfg_attr( feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate", rename_all = "camelCase") )] -pub struct TransitionType(u16); -impl TransitionType { - pub const fn with(ty: u16) -> Self { Self(ty) } +pub struct AssignmentDetails { + pub owned_state_schema: OwnedStateSchema, + pub name: FieldName, + pub default_transition: TransitionType, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_RGB_COMMIT, tags = order)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", rename_all = "camelCase") +)] +pub struct GlobalDetails { + pub global_state_schema: GlobalStateSchema, + pub name: FieldName, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_RGB_COMMIT, tags = order)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", rename_all = "camelCase") +)] +pub struct MetaDetails { + pub sem_id: SemId, + pub name: FieldName, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_RGB_COMMIT, tags = order)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", rename_all = "camelCase") +)] +pub struct TransitionDetails { + pub transition_schema: TransitionSchema, + pub name: FieldName, } impl TransitionType { - pub const BLANK: Self = TransitionType(u16::MAX); - /// Easily check if the TransitionType is blank with convention method - pub fn is_blank(self) -> bool { self == Self::BLANK } + pub const REPLACE: Self = TransitionType(8011); + pub fn is_replace(self) -> bool { self == Self::REPLACE } } /// Schema identifier. @@ -122,6 +153,10 @@ pub struct SchemaId( Bytes32, ); +impl SchemaId { + pub const fn from_array(id: [u8; 32]) -> Self { SchemaId(Bytes32::from_array(id)) } +} + impl From for SchemaId { fn from(hasher: Sha256) -> Self { hasher.finish().into() } } @@ -159,21 +194,16 @@ impl_serde_baid64!(SchemaId); )] pub struct Schema { pub ffv: Ffv, - pub flags: ReservedBytes<1, 0>, pub name: TypeName, - pub timestamp: i64, - pub developer: Identity, - pub meta_types: TinyOrdMap, - pub global_types: TinyOrdMap, - pub owned_types: TinyOrdMap, - pub valency_types: TinyOrdSet, + pub meta_types: TinyOrdMap, + pub global_types: TinyOrdMap, + pub owned_types: TinyOrdMap, pub genesis: GenesisSchema, - pub extensions: TinyOrdMap, - pub transitions: TinyOrdMap, + pub transitions: TinyOrdMap, - pub reserved: ReservedBytes<8, 0>, + pub default_assignment: Option, } impl CommitEncode for Schema { @@ -181,21 +211,16 @@ impl CommitEncode for Schema { fn commit_encode(&self, e: &mut CommitEngine) { e.commit_to_serialized(&self.ffv); - e.commit_to_serialized(&self.flags); e.commit_to_serialized(&self.name); - e.commit_to_serialized(&self.timestamp); - e.commit_to_serialized(&self.developer); e.commit_to_map(&self.meta_types); e.commit_to_map(&self.global_types); e.commit_to_map(&self.owned_types); - e.commit_to_set(&self.valency_types); e.commit_to_serialized(&self.genesis); - e.commit_to_map(&self.extensions); e.commit_to_map(&self.transitions); - e.commit_to_serialized(&self.reserved); + e.commit_to_option(&self.default_assignment); } } @@ -218,24 +243,20 @@ impl Schema { #[inline] pub fn schema_id(&self) -> SchemaId { self.commit_id() } - pub fn blank_transition(&self) -> TransitionSchema { - let mut schema = TransitionSchema::default(); - for id in self.owned_types.keys() { - schema.inputs.insert(*id, Occurrences::NoneOrMore).ok(); - schema.assignments.insert(*id, Occurrences::NoneOrMore).ok(); - } - schema - } - pub fn types(&self) -> impl Iterator + '_ { self.meta_types .values() - .copied() - .chain(self.global_types.values().map(|i| i.sem_id)) + .map(|i| &i.sem_id) + .cloned() + .chain( + self.global_types + .values() + .map(|i| i.global_state_schema.sem_id), + ) .chain( self.owned_types .values() - .filter_map(OwnedStateSchema::sem_id), + .filter_map(|ai| OwnedStateSchema::sem_id(&ai.owned_state_schema)), ) } @@ -244,10 +265,102 @@ impl Schema { .validator .iter() .copied() - .chain(self.transitions.values().filter_map(|i| i.validator)) - .chain(self.extensions.values().filter_map(|i| i.validator)) + .chain( + self.transitions + .values() + .filter_map(|i| i.transition_schema.validator), + ) .map(|site| site.lib) } + + pub fn default_transition_for_assignment( + &self, + assignment_type: &AssignmentType, + ) -> TransitionType { + self.owned_types + .get(assignment_type) + .expect("invalid schema") + .default_transition + } + + pub fn assignment(&self, name: impl Into) -> (&AssignmentType, &AssignmentDetails) { + let name = name.into(); + self.owned_types + .iter() + .find(|(_, i)| i.name == name) + .expect("cannot find assignment with the given name") + } + + pub fn assignment_type(&self, name: impl Into) -> AssignmentType { + *self.assignment(name).0 + } + + pub fn assignment_name(&self, type_id: AssignmentType) -> &FieldName { + &self + .owned_types + .iter() + .find(|(id, _)| *id == &type_id) + .expect("cannot find assignment with the given type ID") + .1 + .name + } + + pub fn assignment_types_for_state(&self, state_type: StateType) -> Vec<&AssignmentType> { + self.owned_types + .iter() + .filter_map(|(at, ai)| { + if ai.owned_state_schema.state_type() == state_type { + Some(at) + } else { + None + } + }) + .collect() + } + + pub fn global(&self, name: impl Into) -> (&GlobalStateType, &GlobalDetails) { + let name = name.into(); + self.global_types + .iter() + .find(|(_, i)| i.name == name) + .expect("cannot find global with the given name") + } + + pub fn global_type(&self, name: impl Into) -> GlobalStateType { + *self.global(name).0 + } + + pub fn meta(&self, name: impl Into) -> (&MetaType, &MetaDetails) { + let name = name.into(); + self.meta_types + .iter() + .find(|(_, i)| i.name == name) + .expect("cannot find meta with the given name") + } + + pub fn meta_type(&self, name: impl Into) -> MetaType { *self.meta(name).0 } + + pub fn meta_name(&self, type_id: MetaType) -> &FieldName { + &self + .meta_types + .iter() + .find(|(id, _)| *id == &type_id) + .expect("cannot find meta with the given type ID") + .1 + .name + } + + pub fn transition(&self, name: impl Into) -> (&TransitionType, &TransitionDetails) { + let name = name.into(); + self.transitions + .iter() + .find(|(_, i)| i.name == name) + .expect("cannot find transition with the given name") + } + + pub fn transition_type(&self, name: impl Into) -> TransitionType { + *self.transition(name).0 + } } #[cfg(test)] diff --git a/src/schema/state.rs b/src/schema/state.rs index f74a79de..7b9072d8 100644 --- a/src/schema/state.rs +++ b/src/schema/state.rs @@ -21,37 +21,11 @@ // limitations under the License. use amplify::num::u24; -use commit_verify::ReservedBytes; use strict_encoding::Primitive; use strict_types::SemId; use crate::{StateType, LIB_NAME_RGB_COMMIT}; -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] -#[strict_type(lib = LIB_NAME_RGB_COMMIT, tags = repr, into_u8, try_from_u8)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase", tag = "type") -)] -#[non_exhaustive] -#[repr(u8)] -pub enum MediaType { - #[display("*/*")] - #[strict_type(dumb)] - Any = 0xFF, - // TODO: Complete MIME type implementation -} - -impl MediaType { - pub fn conforms(&self, other: &MediaType) -> bool { - match (self, other) { - (MediaType::Any, MediaType::Any) => true, - } - } -} - #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_RGB_COMMIT, tags = order)] @@ -65,8 +39,6 @@ pub enum OwnedStateSchema { Declarative, Fungible(FungibleType), Structured(SemId), - Attachment(MediaType), - // TODO: Computed state (RCP240327A) will be added here } impl OwnedStateSchema { @@ -75,22 +47,18 @@ impl OwnedStateSchema { OwnedStateSchema::Declarative => StateType::Void, OwnedStateSchema::Fungible(_) => StateType::Fungible, OwnedStateSchema::Structured(_) => StateType::Structured, - OwnedStateSchema::Attachment(_) => StateType::Attachment, } } pub fn sem_id(&self) -> Option { - if let Self::Structured(id) = self { Some(*id) } else { None } + if let Self::Structured(id) = self { + Some(*id) + } else { + None + } } } -/// Today we support only a single format of confidential data, because of the -/// limitations of the underlying secp256k1-zkp library: it works only with -/// u64 numbers. Nevertheless, homomorphic commitments can be created to -/// everything that has up to 256 bits and commutative arithmetics, so in the -/// future we plan to support more types. We reserve this possibility by -/// internally encoding [`ConfidentialFormat`] with the same type specification -/// details as used for [`DateFormat`] #[derive(Copy, Clone, PartialEq, Eq, Debug, Default, Display)] #[derive(StrictType, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_RGB_COMMIT, tags = repr, into_u8, try_from_u8)] @@ -115,9 +83,6 @@ pub enum FungibleType { serde(crate = "serde_crate", rename_all = "camelCase") )] pub struct GlobalStateSchema { - // TODO: Reserved for computed state (RCP240327A): will be used as an enum tag with computed - // state having value 1. - pub reserved: ReservedBytes<1>, pub sem_id: SemId, pub max_items: u24, } @@ -125,7 +90,6 @@ pub struct GlobalStateSchema { impl GlobalStateSchema { pub fn once(sem_id: SemId) -> Self { GlobalStateSchema { - reserved: default!(), sem_id, max_items: u24::ONE, } @@ -133,7 +97,6 @@ impl GlobalStateSchema { pub fn many(sem_id: SemId) -> Self { GlobalStateSchema { - reserved: default!(), sem_id, max_items: u24::MAX, } diff --git a/src/stl.rs b/src/stl.rs index 6063f0ba..c8ba2a06 100644 --- a/src/stl.rs +++ b/src/stl.rs @@ -21,60 +21,60 @@ // limitations under the License. pub use aluvm::stl::aluvm_stl; -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; +use bp::Txid; use commit_verify::stl::commit_verify_stl; use strict_types::stl::{std_stl, strict_types_stl}; use strict_types::typelib::LibBuilder; use strict_types::{CompileError, TypeLib}; use crate::validation::DbcProof; -use crate::vm::{GlobalOrd, XWitnessId}; +use crate::vm::GlobalOrd; use crate::{ - Extension, Genesis, OpCommitment, Schema, TransitionBundle, LIB_NAME_RGB_COMMIT, + BundleId, Genesis, OpCommitment, Schema, TransitionBundle, LIB_NAME_RGB_COMMIT, LIB_NAME_RGB_LOGIC, }; /// Strict types id for the library providing data types for RGB consensus. pub const LIB_ID_RGB_COMMIT: &str = - "stl:ZMTVCU25-QDo98xR-wI91wcu-ydb7kui-QfZbF$n-0KDS2ow#tuna-safari-design"; + "stl:XbiECcs9-xlyofco-wkXoupT-gJ61JJf-XWL0DWf-INKzIp0#support-iris-depend"; /// Strict types id for the library providing data types for RGB consensus. pub const LIB_ID_RGB_LOGIC: &str = - "stl:bioTBozT-NqelHGE-SPbnpMA-XBNSbXZ-6X0dANE-WHVirL8#explain-marvin-bless"; + "stl:qolQpjNB-4ZkpJIo-U1tktjI-mwAYyEg-kOGQttY-ZoK3Loo#colombo-famous-erosion"; -fn _rgb_commit_stl() -> Result { - LibBuilder::new(libname!(LIB_NAME_RGB_COMMIT), tiny_bset! { - std_stl().to_dependency(), - strict_types_stl().to_dependency(), - commit_verify_stl().to_dependency(), - bp_tx_stl().to_dependency(), - bp_core_stl().to_dependency(), - aluvm_stl().to_dependency() - }) +fn _rgb_commit_stl() -> Result> { + Ok(LibBuilder::with(libname!(LIB_NAME_RGB_COMMIT), [ + std_stl().to_dependency_types(), + strict_types_stl().to_dependency_types(), + commit_verify_stl().to_dependency_types(), + bp_tx_stl().to_dependency_types(), + bp_core_stl().to_dependency_types(), + aluvm_stl().to_dependency_types(), + ]) + .transpile::() .transpile::() .transpile::() - .transpile::() + .transpile::() .transpile::() - .transpile::() + .transpile::() .transpile::() - .compile() + .compile()?) } -fn _rgb_logic_stl() -> Result { - LibBuilder::new(libname!(LIB_NAME_RGB_LOGIC), tiny_bset! { - std_stl().to_dependency(), - strict_types_stl().to_dependency(), - commit_verify_stl().to_dependency(), - bp_tx_stl().to_dependency(), - bp_core_stl().to_dependency(), - aluvm_stl().to_dependency(), - rgb_commit_stl().to_dependency() - }) - .transpile::() - .transpile::() - // TODO: Commit to the RGB ISA once AluVM will support strict types - // .transpile::() - .compile() +fn _rgb_logic_stl() -> Result> { + Ok(LibBuilder::with(libname!(LIB_NAME_RGB_LOGIC), [ + std_stl().to_dependency_types(), + strict_types_stl().to_dependency_types(), + commit_verify_stl().to_dependency_types(), + bp_consensus_stl().to_dependency_types(), + bp_core_stl().to_dependency_types(), + aluvm_stl().to_dependency_types(), + rgb_commit_stl().to_dependency_types(), + ]) + .transpile::() + .transpile::() + .compile()?) } /// Generates strict type library providing data types for RGB consensus. diff --git a/src/validation/commitments.rs b/src/validation/commitments.rs index de19afb2..c59b412e 100644 --- a/src/validation/commitments.rs +++ b/src/validation/commitments.rs @@ -25,7 +25,7 @@ use bp::dbc::tapret::TapretProof; use bp::dbc::Method; use bp::{dbc, Tx}; use commit_verify::mpc::Commitment; -use commit_verify::{mpc, ConvolveVerifyError, EmbedVerifyError}; +use commit_verify::{ConvolveVerifyError, EmbedVerifyError}; use strict_encoding::{StrictDeserialize, StrictDumb, StrictSerialize}; use crate::LIB_NAME_RGB_LOGIC; @@ -86,7 +86,13 @@ impl StrictDeserialize for DbcProof {} impl dbc::Proof for DbcProof { type Error = DbcError; - const METHOD: Method = Method::OpretFirst; + + fn method(&self) -> Method { + match self { + DbcProof::Tapret(_) => Method::TapretFirst, + DbcProof::Opret(_) => Method::OpretFirst, + } + } fn verify(&self, msg: &Commitment, tx: &Tx) -> Result<(), Self::Error> { match self { @@ -111,4 +117,4 @@ impl dbc::Proof for DbcProof { } /// Anchor which DBC proof is either Tapret or Opret. -pub type EAnchor

= dbc::Anchor; +pub type EAnchor = dbc::Anchor; diff --git a/src/validation/consignment.rs b/src/validation/consignment.rs index 9499d2e1..1f5cc8ce 100644 --- a/src/validation/consignment.rs +++ b/src/validation/consignment.rs @@ -21,26 +21,22 @@ // limitations under the License. //! Common API for accessing RGB contract operation graph, including individual -//! state transitions, extensions, genesis, outputs, assignments & -//! single-use-seal data. - -use std::collections::BTreeMap; +//! state transitions, genesis, outputs, assignments & single-use-seal data. use aluvm::library::{Lib, LibId}; -use amplify::confinement::Confined; +use amplify::confinement::ConfinedOrdMap; +use bp::Txid; use strict_types::TypeSystem; use super::EAnchor; -use crate::vm::XWitnessId; use crate::{ - AssignmentType, AssignmentsRef, BundleId, ContractId, Extension, ExtensionType, Genesis, - GlobalState, GraphSeal, Inputs, Metadata, OpFullType, OpId, OpType, Operation, Schema, - Transition, TransitionBundle, TransitionType, TypedAssigns, Valencies, + AssignmentType, AssignmentsRef, BundleId, ContractId, Genesis, GlobalState, GraphSeal, + Metadata, OpFullType, OpId, Operation, Schema, Transition, TransitionBundle, TypedAssigns, }; pub const CONSIGNMENT_MAX_LIBS: usize = 1024; -pub type Scripts = Confined, 0, CONSIGNMENT_MAX_LIBS>; +pub type Scripts = ConfinedOrdMap; #[derive(Copy, Clone, PartialEq, Eq, Debug, From)] pub enum OpRef<'op> { @@ -48,24 +44,13 @@ pub enum OpRef<'op> { Genesis(&'op Genesis), #[from] Transition(&'op Transition), - #[from] - Extension(&'op Extension), } impl<'op> Operation for OpRef<'op> { - fn op_type(&self) -> OpType { - match self { - Self::Genesis(op) => op.op_type(), - Self::Transition(op) => op.op_type(), - Self::Extension(op) => op.op_type(), - } - } - fn full_type(&self) -> OpFullType { match self { Self::Genesis(op) => op.full_type(), Self::Transition(op) => op.full_type(), - Self::Extension(op) => op.full_type(), } } @@ -73,7 +58,6 @@ impl<'op> Operation for OpRef<'op> { match self { Self::Genesis(op) => op.id(), Self::Transition(op) => op.id(), - Self::Extension(op) => op.id(), } } @@ -81,7 +65,6 @@ impl<'op> Operation for OpRef<'op> { match self { Self::Genesis(op) => op.contract_id(), Self::Transition(op) => op.contract_id(), - Self::Extension(op) => op.contract_id(), } } @@ -89,23 +72,6 @@ impl<'op> Operation for OpRef<'op> { match self { Self::Genesis(op) => op.nonce(), Self::Transition(op) => op.nonce(), - Self::Extension(op) => op.nonce(), - } - } - - fn transition_type(&self) -> Option { - match self { - Self::Genesis(op) => op.transition_type(), - Self::Transition(op) => op.transition_type(), - Self::Extension(op) => op.transition_type(), - } - } - - fn extension_type(&self) -> Option { - match self { - Self::Genesis(op) => op.extension_type(), - Self::Transition(op) => op.extension_type(), - Self::Extension(op) => op.extension_type(), } } @@ -113,7 +79,6 @@ impl<'op> Operation for OpRef<'op> { match self { Self::Genesis(op) => op.metadata(), Self::Transition(op) => op.metadata(), - Self::Extension(op) => op.metadata(), } } @@ -121,15 +86,6 @@ impl<'op> Operation for OpRef<'op> { match self { Self::Genesis(op) => op.globals(), Self::Transition(op) => op.globals(), - Self::Extension(op) => op.globals(), - } - } - - fn valencies(&self) -> &Valencies { - match self { - Self::Genesis(op) => op.valencies(), - Self::Transition(op) => op.valencies(), - Self::Extension(op) => op.valencies(), } } @@ -137,7 +93,6 @@ impl<'op> Operation for OpRef<'op> { match self { Self::Genesis(op) => (&op.assignments).into(), Self::Transition(op) => (&op.assignments).into(), - Self::Extension(op) => (&op.assignments).into(), } } @@ -145,15 +100,6 @@ impl<'op> Operation for OpRef<'op> { match self { Self::Genesis(op) => op.assignments_by_type(t), Self::Transition(op) => op.assignments_by_type(t), - Self::Extension(op) => op.assignments_by_type(t), - } - } - - fn inputs(&self) -> Inputs { - match self { - Self::Genesis(op) => op.inputs(), - Self::Transition(op) => op.inputs(), - Self::Extension(op) => op.inputs(), } } } @@ -164,7 +110,7 @@ impl<'consignment, C: ConsignmentApi> CheckedConsignment<'consignment, C> { pub fn new(consignment: &'consignment C) -> Self { Self(consignment) } } -impl<'consignment, C: ConsignmentApi> ConsignmentApi for CheckedConsignment<'consignment, C> { +impl ConsignmentApi for CheckedConsignment<'_, C> { fn schema(&self) -> &Schema { self.0.schema() } fn types(&self) -> &TypeSystem { self.0.types() } @@ -185,11 +131,9 @@ impl<'consignment, C: ConsignmentApi> ConsignmentApi for CheckedConsignment<'con .filter(|b| b.bundle_id() == bundle_id) } - fn anchor(&self, bundle_id: BundleId) -> Option<(XWitnessId, &EAnchor)> { - self.0.anchor(bundle_id) - } + fn anchor(&self, bundle_id: BundleId) -> Option<(Txid, &EAnchor)> { self.0.anchor(bundle_id) } - fn op_witness_id(&self, opid: OpId) -> Option { self.0.op_witness_id(opid) } + fn op_witness_id(&self, opid: OpId) -> Option { self.0.op_witness_id(opid) } } /// Trait defining common data access API for all storage-related RGB structures @@ -210,8 +154,8 @@ pub trait ConsignmentApi { /// validation. fn scripts(&self) -> &Scripts; - /// Retrieves reference to an operation (genesis, state transition or state - /// extension) matching the provided id, or `None` otherwise + /// Retrieves reference to an operation (genesis or state transition) matching the provided id, + /// or `None` otherwise fn operation(&self, opid: OpId) -> Option; /// Contract genesis. @@ -224,8 +168,8 @@ pub trait ConsignmentApi { fn bundle(&self, bundle_id: BundleId) -> Option<&TransitionBundle>; /// Returns a grip given a bundle id. - fn anchor(&self, bundle_id: BundleId) -> Option<(XWitnessId, &EAnchor)>; + fn anchor(&self, bundle_id: BundleId) -> Option<(Txid, &EAnchor)>; /// Returns witness id for a given operation. - fn op_witness_id(&self, opid: OpId) -> Option; + fn op_witness_id(&self, opid: OpId) -> Option; } diff --git a/src/validation/logic.rs b/src/validation/logic.rs index 0afd7f93..0e69d806 100644 --- a/src/validation/logic.rs +++ b/src/validation/logic.rs @@ -21,25 +21,27 @@ // limitations under the License. use std::cell::RefCell; -use std::collections::BTreeSet; +use std::collections::btree_map::Entry; +use std::collections::{BTreeMap, BTreeSet}; use std::rc::Rc; use aluvm::data::Number; use aluvm::isa::Instr; use aluvm::reg::{Reg32, RegA}; use aluvm::Vm; -use amplify::confinement::Confined; +use amplify::confinement::{Confined, NonEmptyVec}; use amplify::Wrapper; use strict_types::TypeSystem; -use crate::schema::{AssignmentsSchema, GlobalSchema, ValencySchema}; +use crate::assignments::AssignVec; +use crate::schema::{AssignmentsSchema, GlobalSchema}; use crate::validation::{CheckedConsignment, ConsignmentApi}; use crate::vm::{ContractStateAccess, ContractStateEvolve, OpInfo, OrdOpRef, RgbIsa, VmContext}; use crate::{ - validation, Assign, AssignmentType, Assignments, AssignmentsRef, ConcealedState, - ConfidentialState, ExposedSeal, ExposedState, Extension, GlobalState, GlobalStateSchema, - GlobalValues, GraphSeal, Inputs, MetaSchema, Metadata, OpId, Operation, Opout, - OwnedStateSchema, RevealedState, Schema, StateType, Transition, TypedAssigns, Valencies, + validation, Assign, AssignmentType, Assignments, AssignmentsRef, ExposedSeal, ExposedState, + GlobalState, GlobalStateSchema, GlobalValues, GraphSeal, Inputs, MetaSchema, Metadata, OpId, + Operation, Opout, OwnedStateSchema, RevealedState, Schema, SealClosingStrategy, Transition, + TypedAssigns, }; impl Schema { @@ -57,39 +59,22 @@ impl Schema { let mut status = validation::Status::new(); let empty_assign_schema = AssignmentsSchema::default(); - let empty_valency_schema = ValencySchema::default(); - let blank_transition = self.blank_transition(); - let ( - metadata_schema, - global_schema, - owned_schema, - redeem_schema, - assign_schema, - valency_schema, - validator, - ty, - ) = match op { + let (metadata_schema, global_schema, owned_schema, assign_schema, validator, ty) = match op + { OrdOpRef::Genesis(genesis) => { - for id in genesis.asset_tags.keys() { - if !matches!(self.owned_types.get(id), Some(OwnedStateSchema::Fungible(_))) { - status.add_failure(validation::Failure::AssetTagNoState(*id)); - } - } - for (id, ss) in &self.owned_types { - if ss.state_type() == StateType::Fungible - && !genesis.asset_tags.contains_key(id) - { - status.add_failure(validation::Failure::FungibleStateNoTag(*id)); - } + if genesis.seal_closing_strategy != SealClosingStrategy::FirstOpretOrTapret { + return validation::Status::with_failure( + validation::Failure::SchemaUnknownSealClosingStrategy( + opid, + genesis.seal_closing_strategy, + ), + ); } - ( &self.genesis.metadata, &self.genesis.globals, &empty_assign_schema, - &empty_valency_schema, &self.genesis.assignments, - &self.genesis.valencies, self.genesis.validator, None::, ) @@ -110,7 +95,6 @@ impl Schema { */ let transition_schema = match self.transitions.get(transition_type) { - None if transition_type.is_blank() => &blank_transition, None => { return validation::Status::with_failure( validation::Failure::SchemaUnknownTransitionType( @@ -119,54 +103,20 @@ impl Schema { ), ); } - Some(transition_schema) => transition_schema, + Some(transition_details) => &transition_details.transition_schema, }; ( &transition_schema.metadata, &transition_schema.globals, &transition_schema.inputs, - &empty_valency_schema, &transition_schema.assignments, - &transition_schema.valencies, transition_schema.validator, Some(transition_type.into_inner()), ) } - OrdOpRef::Extension(Extension { extension_type, .. }, ..) => { - // Right now we do not have actions to implement; but later - // we may have embedded procedures which must be verified - // here - /* - if let Some(procedure) = extension_type.abi.get(&ExtensionAction::NoOp) { - - } - */ - - let extension_schema = match self.extensions.get(extension_type) { - None => { - return validation::Status::with_failure( - validation::Failure::SchemaUnknownExtensionType(opid, *extension_type), - ); - } - Some(extension_schema) => extension_schema, - }; - - ( - &extension_schema.metadata, - &extension_schema.globals, - &empty_assign_schema, - &extension_schema.redeems, - &extension_schema.assignments, - &extension_schema.redeems, - extension_schema.validator, - Some(extension_type.into_inner()), - ) - } }; - // Validate type system - status += self.validate_type_system(); status += self.validate_metadata(opid, op.metadata(), metadata_schema, consignment.types()); status += self.validate_global_state(opid, op.globals(), global_schema, consignment.types()); @@ -177,13 +127,6 @@ impl Schema { } else { Assignments::default() }; - let mut redeemed = Valencies::default(); - if let OrdOpRef::Extension(extension, ..) = op { - for valency in extension.redeemed.keys() { - redeemed.push(*valency).expect("same size"); - } - status += self.validate_redeemed(opid, &redeemed, redeem_schema); - } status += match op.assignments() { AssignmentsRef::Genesis(assignments) => { self.validate_owned_state(opid, assignments, assign_schema, consignment.types()) @@ -193,13 +136,10 @@ impl Schema { } }; - status += self.validate_valencies(opid, op.valencies(), valency_schema); - let genesis = consignment.genesis(); - let op_info = OpInfo::with(opid, &op, &prev_state, &redeemed); + let op_info = OpInfo::with(opid, &op, &prev_state); let context = VmContext { contract_id: genesis.contract_id(), - asset_tags: &genesis.asset_tags, op_info, contract_state, }; @@ -233,18 +173,6 @@ impl Schema { status } - fn validate_type_system(&self) -> validation::Status { - validation::Status::new() - // TODO: Validate type system - // Currently, validation is performed at the level of state values, i.e. - // if the system is inconsistent (references semantic ids not - // present in it) this will be detected during state validation. - // We should not prohibit schema with inconsistent type system, instead - // we need just to issue warnings here if some of semantic ids are - // missed - and add information messages if some excessive semantic ids - // are present. - } - fn validate_metadata( &self, opid: OpId, @@ -269,16 +197,20 @@ impl Schema { continue; }; - let sem_id = self.meta_types.get(type_id).expect( - "if this metadata type were absent, the schema would not be able to pass the \ - internal validation and we would not reach this point", - ); + let sem_id = self + .meta_types + .get(type_id) + .expect( + "if this metadata type were absent, the schema would not be able to pass the \ + internal validation and we would not reach this point", + ) + .sem_id; if types - .strict_deserialize_type(*sem_id, value.as_ref()) + .strict_deserialize_type(sem_id, value.as_ref()) .is_err() { - status.add_failure(validation::Failure::SchemaInvalidMetadata(opid, *sem_id)); + status.add_failure(validation::Failure::SchemaInvalidMetadata(opid, sem_id)); }; } @@ -312,14 +244,14 @@ impl Schema { .map(Confined::release) .unwrap_or_default(); - let GlobalStateSchema { - sem_id, - max_items, - reserved: _, - } = self.global_types.get(type_id).expect( - "if the field were absent, the schema would not be able to pass the internal \ - validation and we would not reach this point", - ); + let GlobalStateSchema { sem_id, max_items } = self + .global_types + .get(type_id) + .expect( + "if the field were absent, the schema would not be able to pass the internal \ + validation and we would not reach this point", + ) + .global_state_schema; // Checking number of field occurrences let count = set.len() as u16; @@ -330,18 +262,18 @@ impl Schema { } if count as u32 > max_items.to_u32() { status.add_failure(validation::Failure::SchemaGlobalStateLimit( - opid, *type_id, count, *max_items, + opid, *type_id, count, max_items, )); } // Validating data types for data in set { if types - .strict_deserialize_type(*sem_id, data.as_ref()) + .strict_deserialize_type(sem_id, data.as_ref()) .is_err() { status.add_failure(validation::Failure::SchemaInvalidGlobalValue( - opid, *type_id, *sem_id, + opid, *type_id, sem_id, )); }; } @@ -388,26 +320,6 @@ impl Schema { status } - fn validate_redeemed( - &self, - id: OpId, - valencies: &Valencies, - valency_schema: &ValencySchema, - ) -> validation::Status { - let mut status = validation::Status::new(); - - valencies - .difference(valency_schema) - .for_each(|public_type_id| { - status.add_failure(validation::Failure::SchemaUnknownValencyType( - id, - *public_type_id, - )); - }); - - status - } - fn validate_owned_state( &self, id: OpId, @@ -441,10 +353,14 @@ impl Schema { )); } - let assignment = &self.owned_types.get(state_id).expect( - "If the assignment were absent, the schema would not be able to pass the internal \ - validation and we would not reach this point", - ); + let assignment = &self + .owned_types + .get(state_id) + .expect( + "If the assignment were absent, the schema would not be able to pass the \ + internal validation and we would not reach this point", + ) + .owned_state_schema; match owned_state.get(state_id) { None => {} @@ -457,34 +373,11 @@ impl Schema { Some(TypedAssigns::Structured(set)) => set .iter() .for_each(|data| status += assignment.validate(id, *state_id, data, types)), - Some(TypedAssigns::Attachment(set)) => set - .iter() - .for_each(|data| status += assignment.validate(id, *state_id, data, types)), }; } status } - - fn validate_valencies( - &self, - id: OpId, - valencies: &Valencies, - valency_schema: &ValencySchema, - ) -> validation::Status { - let mut status = validation::Status::new(); - - valencies - .difference(valency_schema) - .for_each(|public_type_id| { - status.add_failure(validation::Failure::SchemaUnknownValencyType( - id, - *public_type_id, - )); - }); - - status - } } fn extract_prev_state( @@ -493,9 +386,9 @@ fn extract_prev_state( inputs: &Inputs, status: &mut validation::Status, ) -> Assignments { - let mut assignments = bmap! {}; + let mut assignments: BTreeMap> = bmap! {}; for input in inputs { - let Opout { op, ty, no } = input.prev_out; + let Opout { op, ty, no } = input; let prev_op = match consignment.operation(op) { None => { @@ -509,54 +402,56 @@ fn extract_prev_state( match prev_op.assignments_by_type(ty) { Some(TypedAssigns::Declarative(prev_assignments)) => { if let Some(prev_assign) = prev_assignments.get(no) { - if let Some(typed_assigns) = assignments - .entry(ty) - .or_insert_with(|| TypedAssigns::Declarative(Default::default())) - .as_declarative_mut() - { - typed_assigns.push(prev_assign.clone()).expect("same size"); + match assignments.entry(ty) { + Entry::Occupied(mut entry) => { + if let Some(typed_assigns) = entry.get_mut().as_declarative_mut() { + typed_assigns.push(prev_assign.clone()).expect("same size"); + } + } + Entry::Vacant(entry) => { + entry.insert(TypedAssigns::Declarative(AssignVec::with( + NonEmptyVec::with(prev_assign.clone()), + ))); + } } } else { - status.add_failure(validation::Failure::NoPrevOut(opid, input.prev_out)); + status.add_failure(validation::Failure::NoPrevOut(opid, input)); } } Some(TypedAssigns::Fungible(prev_assignments)) => { if let Some(prev_assign) = prev_assignments.get(no) { - if let Some(typed_assigns) = assignments - .entry(ty) - .or_insert_with(|| TypedAssigns::Fungible(Default::default())) - .as_fungible_mut() - { - typed_assigns.push(prev_assign.clone()).expect("same size"); + match assignments.entry(ty) { + Entry::Occupied(mut entry) => { + if let Some(typed_assigns) = entry.get_mut().as_fungible_mut() { + typed_assigns.push(prev_assign.clone()).expect("same size"); + } + } + Entry::Vacant(entry) => { + entry.insert(TypedAssigns::Fungible(AssignVec::with( + NonEmptyVec::with(prev_assign.clone()), + ))); + } } } else { - status.add_failure(validation::Failure::NoPrevOut(opid, input.prev_out)); + status.add_failure(validation::Failure::NoPrevOut(opid, input)); } } Some(TypedAssigns::Structured(prev_assignments)) => { if let Some(prev_assign) = prev_assignments.get(no) { - if let Some(typed_assigns) = assignments - .entry(ty) - .or_insert_with(|| TypedAssigns::Structured(Default::default())) - .as_structured_mut() - { - typed_assigns.push(prev_assign.clone()).expect("same size"); - } - } else { - status.add_failure(validation::Failure::NoPrevOut(opid, input.prev_out)); - } - } - Some(TypedAssigns::Attachment(prev_assignments)) => { - if let Some(prev_assign) = prev_assignments.get(no) { - if let Some(typed_assigns) = assignments - .entry(ty) - .or_insert_with(|| TypedAssigns::Attachment(Default::default())) - .as_attachment_mut() - { - typed_assigns.push(prev_assign.clone()).expect("same size"); + match assignments.entry(ty) { + Entry::Occupied(mut entry) => { + if let Some(typed_assigns) = entry.get_mut().as_structured_mut() { + typed_assigns.push(prev_assign.clone()).expect("same size"); + } + } + Entry::Vacant(entry) => { + entry.insert(TypedAssigns::Structured(AssignVec::with( + NonEmptyVec::with(prev_assign.clone()), + ))); + } } } else { - status.add_failure(validation::Failure::NoPrevOut(opid, input.prev_out)); + status.add_failure(validation::Failure::NoPrevOut(opid, input)); } } None => { @@ -581,68 +476,23 @@ impl OwnedStateSchema { ) -> validation::Status { let mut status = validation::Status::new(); match data { - Assign::Confidential { state, .. } | Assign::ConfidentialState { state, .. } => { - match (self, state.state_commitment()) { - (OwnedStateSchema::Declarative, ConcealedState::Void) => {} - (OwnedStateSchema::Fungible(_), ConcealedState::Fungible(value)) => { - // [SECURITY-CRITICAL]: Bulletproofs validation - if let Err(err) = value.verify_range_proof() { - status.add_failure(validation::Failure::BulletproofsInvalid( - opid, - state_type, - err.to_string(), - )); - } - } - (OwnedStateSchema::Structured(_), ConcealedState::Structured(_)) => { - status.add_warning(validation::Warning::UncheckableConfidentialState( - opid, state_type, - )); - } - (OwnedStateSchema::Attachment(_), ConcealedState::Attachment(_)) => { - status.add_warning(validation::Warning::UncheckableConfidentialState( - opid, state_type, - )); - } - // all other options are mismatches - (state_schema, found) => { - status.add_failure(validation::Failure::StateTypeMismatch { - opid, - state_type, - expected: state_schema.state_type(), - found: found.state_type(), - }); - } - } - } Assign::Revealed { state, .. } | Assign::ConfidentialSeal { state, .. } => { match (self, state.state_data()) { (OwnedStateSchema::Declarative, RevealedState::Void) => {} - ( - OwnedStateSchema::Attachment(media_type), - RevealedState::Attachment(attach), - ) if !attach.file.media_type.conforms(media_type) => { - status.add_failure(validation::Failure::MediaTypeMismatch { - opid, - state_type, - expected: *media_type, - found: attach.file.media_type, - }); - } (OwnedStateSchema::Fungible(schema), RevealedState::Fungible(v)) - if v.value.fungible_type() != *schema => + if v.as_inner().fungible_type() != *schema => { status.add_failure(validation::Failure::FungibleTypeMismatch { opid, state_type, expected: *schema, - found: v.value.fungible_type(), + found: v.as_inner().fungible_type(), }); } (OwnedStateSchema::Fungible(_), RevealedState::Fungible(_)) => {} (OwnedStateSchema::Structured(sem_id), RevealedState::Structured(data)) => { if type_system - .strict_deserialize_type(*sem_id, data.value.as_ref()) + .strict_deserialize_type(*sem_id, data.as_ref()) .is_err() { status.add_failure(validation::Failure::SchemaInvalidOwnedValue( diff --git a/src/validation/mod.rs b/src/validation/mod.rs index c7c3eb09..18d96cd8 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -29,5 +29,5 @@ mod commitments; pub use commitments::{DbcError, DbcProof, EAnchor}; pub use consignment::{CheckedConsignment, ConsignmentApi, OpRef, Scripts, CONSIGNMENT_MAX_LIBS}; -pub use status::{Failure, Info, Status, Validity, Warning}; +pub use status::{Failure, Info, Status, UnsafeHistoryMap, Validity, Warning}; pub use validator::{ResolveWitness, Validator, WitnessResolverError}; diff --git a/src/validation/schema.rs b/src/validation/schema.rs index 4e8dafc2..890607c8 100644 --- a/src/validation/schema.rs +++ b/src/validation/schema.rs @@ -22,41 +22,40 @@ use strict_types::TypeSystem; -use crate::{validation, OpFullType, OpSchema, OwnedStateSchema, Schema, TransitionType}; +use crate::{validation, OpFullType, OpSchema, OwnedStateSchema, Schema}; impl Schema { pub fn verify(&self, types: &TypeSystem) -> validation::Status { let mut status = validation::Status::new(); status += self.verify_operation(OpFullType::Genesis, &self.genesis); - for (type_id, schema) in &self.transitions { - status += self.verify_operation(OpFullType::StateTransition(*type_id), schema); - } - for (type_id, schema) in &self.extensions { - status += self.verify_operation(OpFullType::StateExtension(*type_id), schema); - } - // Check that the schema doesn't contain reserved type ids - if self.transitions.contains_key(&TransitionType::BLANK) { - status.add_failure(validation::Failure::SchemaBlankTransitionRedefined); + for (type_id, transition_details) in &self.transitions { + status += self.verify_operation( + OpFullType::StateTransition(*type_id), + &transition_details.transition_schema, + ); } - for (type_id, sem_id) in &self.meta_types { - if !types.contains_key(sem_id) { - status.add_failure(validation::Failure::SchemaMetaSemIdUnknown(*type_id, *sem_id)); + for (type_id, meta_details) in &self.meta_types { + if !types.contains_key(&meta_details.sem_id) { + status.add_failure(validation::Failure::SchemaMetaSemIdUnknown( + *type_id, + meta_details.sem_id, + )); } } - for (type_id, schema) in &self.global_types { - if !types.contains_key(&schema.sem_id) { + for (type_id, global_details) in &self.global_types { + if !types.contains_key(&global_details.global_state_schema.sem_id) { status.add_failure(validation::Failure::SchemaGlobalSemIdUnknown( *type_id, - schema.sem_id, + global_details.global_state_schema.sem_id, )); } } - for (type_id, schema) in &self.owned_types { - if let OwnedStateSchema::Structured(sem_id) = schema { + for (type_id, assignment_details) in &self.owned_types { + if let OwnedStateSchema::Structured(sem_id) = &assignment_details.owned_state_schema { if !types.contains_key(sem_id) { status.add_failure(validation::Failure::SchemaOwnedSemIdUnknown( *type_id, *sem_id, @@ -79,9 +78,6 @@ impl Schema { if matches!(schema.inputs(), Some(inputs) if inputs.is_empty()) { status.add_failure(validation::Failure::SchemaOpEmptyInputs(op_type)); } - if matches!(schema.redeems(), Some(inputs) if inputs.is_empty()) { - status.add_failure(validation::Failure::SchemaOpEmptyInputs(op_type)); - } for type_id in schema.globals().keys() { if !self.global_types.contains_key(type_id) { status @@ -95,13 +91,6 @@ impl Schema { )); } } - for type_id in schema.valencies() { - if !self.valency_types.contains(type_id) { - status.add_failure(validation::Failure::SchemaOpValencyTypeUnknown( - op_type, *type_id, - )); - } - } status } diff --git a/src/validation/status.rs b/src/validation/status.rs index f77c6a0a..ec4a7e7d 100644 --- a/src/validation/status.rs +++ b/src/validation/status.rs @@ -21,20 +21,24 @@ // limitations under the License. use core::ops::AddAssign; +use std::collections::{BTreeSet, HashMap, HashSet}; use std::fmt::{self, Display, Formatter}; use amplify::num::u24; +use bp::seals::txout::CloseMethod; +use bp::Txid; use commit_verify::mpc::InvalidProof; use strict_types::SemId; use crate::schema::{self, SchemaId}; use crate::validation::WitnessResolverError; -use crate::vm::XWitnessId; use crate::{ - BundleId, ContractId, Layer1, OccurrencesMismatch, OpFullType, OpId, Opout, StateType, Vin, - XGraphSeal, XOutputSeal, + BundleId, ChainNet, ContractId, OccurrencesMismatch, OpFullType, OpId, Opout, + SealClosingStrategy, StateType, Vin, }; +pub type UnsafeHistoryMap = HashMap>; + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Display)] #[repr(u8)] pub enum Validity { @@ -58,6 +62,7 @@ pub struct Status { pub failures: Vec, pub warnings: Vec, pub info: Vec, + pub validated_opids: BTreeSet, } impl Display for Status { @@ -105,6 +110,7 @@ impl Status { failures: vec![v], warnings: vec![], info: vec![], + validated_opids: bset![], } } } @@ -162,9 +168,13 @@ impl Status { )] #[display(doc_comments)] pub enum Failure { - /// the contract network doesn't match (validator runs in testnet={0} + /// the contract chain-network pair doesn't match (validator runs in chain_net={0} + /// configuration). + ContractChainNetMismatch(ChainNet), + + /// the resolver chain-network pair doesn't match (validator runs in chain_net={0} /// configuration). - NetworkMismatch(bool), + ResolverChainNetMismatch(ChainNet), /// schema {actual} provided for the consignment validation doesn't match /// schema {expected} used by the contract. This means that the consignment @@ -175,8 +185,6 @@ pub enum Failure { /// Actual schema id provided by the consignment. actual: SchemaId, }, - /// schema uses reserved type for the blank state transition. - SchemaBlankTransitionRedefined, /// schema global state #{0} uses semantic data type absent in type library /// ({1}). @@ -196,11 +204,7 @@ pub enum Failure { SchemaOpGlobalTypeUnknown(OpFullType, schema::GlobalStateType), /// schema for {0} references undeclared owned state type {1}. SchemaOpAssignmentTypeUnknown(OpFullType, schema::AssignmentType), - /// schema for {0} references undeclared valency type {1}. - SchemaOpValencyTypeUnknown(OpFullType, schema::ValencyType), - /// operation {0} uses invalid state extension type {1}. - SchemaUnknownExtensionType(OpId, schema::ExtensionType), /// operation {0} uses invalid state transition type {1}. SchemaUnknownTransitionType(OpId, schema::TransitionType), /// operation {0} uses invalid metadata type {1}. @@ -209,8 +213,8 @@ pub enum Failure { SchemaUnknownGlobalStateType(OpId, schema::GlobalStateType), /// operation {0} uses invalid assignment type {1}. SchemaUnknownAssignmentType(OpId, schema::AssignmentType), - /// operation {0} uses invalid valency type {1}. - SchemaUnknownValencyType(OpId, schema::ValencyType), + /// operation {0} uses invalid seal closing strategy {1}. + SchemaUnknownSealClosingStrategy(OpId, SealClosingStrategy), /// invalid number of global state entries of type {1} in operation {0} - /// {2} @@ -248,9 +252,11 @@ pub enum Failure { /// bundle {0} public witness {1} is not known to the resolver; validation /// stopped since operations can't be consensus-ordered. The resolver /// responded with error {2} - WitnessUnresolved(BundleId, XWitnessId, WitnessResolverError), + WitnessUnresolved(BundleId, Txid, WitnessResolverError), /// operation {0} is under a different contract {1}. ContractMismatch(OpId, ContractId), + /// opout {0} appears more than once as input + DoubleSpend(Opout), // Errors checking bundle commitments /// transition bundle {0} references state transition {1} which is not @@ -258,17 +264,10 @@ pub enum Failure { BundleExtraTransition(BundleId, OpId), /// transition bundle {0} references non-existing input in witness {2} for /// the state transition {1}. - BundleInvalidInput(BundleId, OpId, XWitnessId), + BundleInvalidInput(BundleId, OpId, Txid), /// transition bundle {0} doesn't commit to the input {1} in the witness {2} /// which is an input of the state transition {3}. - BundleInvalidCommitment(BundleId, Vin, XWitnessId, OpId), - - // Errors checking asset tags - /// asset type provided in genesis references unknown fungible state of type - /// {0}. - AssetTagNoState(schema::AssignmentType), - /// fungible state {0} has no asset tag defined. - FungibleStateNoTag(schema::AssignmentType), + BundleInvalidCommitment(BundleId, Vin, Txid, OpId), // Errors checking seal closing /// transition {opid} references state type {state_type} absent in the @@ -285,42 +284,21 @@ pub enum Failure { ConfidentialSeal(Opout), /// bundle {0} public witness {1} is not known to the resolver. Resolver /// reported error {2} - SealNoPubWitness(BundleId, XWitnessId, WitnessResolverError), - /// witness layer 1 {anchor} doesn't match seal definition {seal}. - SealWitnessLayer1Mismatch { seal: Layer1, anchor: Layer1 }, - /// seal {1} is defined on {0} which is not in the set of layers allowed - /// by the contract genesis. - SealLayerMismatch(Layer1, XGraphSeal), - /// seal {1} has a different closing method from the bundle {0} requirement. - SealInvalidMethod(BundleId, XOutputSeal), + SealNoPubWitness(BundleId, Txid, WitnessResolverError), /// transition bundle {0} doesn't close seal with the witness {1}. Details: /// {2} - SealsInvalid(BundleId, XWitnessId, String), + SealsInvalid(BundleId, Txid, String), /// single-use seals for the operation {0} were not validated, which /// probably indicates unanchored state transition. SealsUnvalidated(OpId), - /// anchor provides different type of DBC proof than required by the bundle - /// {0}. - AnchorMethodMismatch(BundleId), /// transition bundle {0} is not properly anchored to the witness {1}. /// Details: {2} - MpcInvalid(BundleId, XWitnessId, InvalidProof), - - // State extensions errors - /// valency {valency} redeemed by state extension {opid} references - /// non-existing operation {prev_id} - ValencyNoParent { - opid: OpId, - prev_id: OpId, - valency: schema::ValencyType, - }, - /// state extension {opid} references valency {valency} absent in the parent - /// {prev_id}. - NoPrevValency { - opid: OpId, - prev_id: OpId, - valency: schema::ValencyType, - }, + MpcInvalid(BundleId, Txid, InvalidProof), + /// witness transaction {0} has no taproot or OP_RETURN output. + NoDbcOutput(Txid), + /// first DBC-compatible output of witness transaction {0} doesn't match the provided proof + /// type ({1}) + InvalidProofType(Txid, CloseMethod), // State check errors /// state in {opid}/{state_type} is of {found} type, while schema requires @@ -333,22 +311,12 @@ pub enum Failure { }, /// state in {opid}/{state_type} is of {found} type, while schema requires /// it to be {expected}. - MediaTypeMismatch { - opid: OpId, - state_type: schema::AssignmentType, - expected: schema::MediaType, - found: schema::MediaType, - }, - /// state in {opid}/{state_type} is of {found} type, while schema requires - /// it to be {expected}. FungibleTypeMismatch { opid: OpId, state_type: schema::AssignmentType, expected: schema::FungibleType, found: schema::FungibleType, }, - /// invalid bulletproofs in {0}:{1}: {2} - BulletproofsInvalid(OpId, schema::AssignmentType, String), /// evaluation of AluVM script for operation {0} has failed with the code /// {1:?} and message {2:?}. ScriptFailure(OpId, Option, Option), @@ -368,9 +336,8 @@ pub enum Failure { )] #[display(doc_comments)] pub enum Warning { - /// operation {0} contains state in assignment {1} which is confidential and - /// thus was not validated. - UncheckableConfidentialState(OpId, schema::AssignmentType), + /// Map of transfer history TXs with potentially unsafe height. + UnsafeHistory(UnsafeHistoryMap), /// Custom warning by external services on top of RGB Core. #[display(inner)] diff --git a/src/validation/validator.rs b/src/validation/validator.rs index 38908243..4f4a5dfb 100644 --- a/src/validation/validator.rs +++ b/src/validation/validator.rs @@ -21,23 +21,23 @@ // limitations under the License. use std::cell::RefCell; -use std::collections::{BTreeMap, BTreeSet}; +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; +use std::num::NonZeroU32; use std::rc::Rc; use bp::dbc::Anchor; -use bp::seals::txout::{CloseMethod, TxoSeal, Witness}; -use bp::{dbc, Outpoint}; +use bp::seals::txout::{CloseMethod, Witness}; +use bp::{dbc, Outpoint, Tx, Txid}; use commit_verify::mpc; use single_use_seals::SealWitness; -use super::status::Failure; -use super::{CheckedConsignment, ConsignmentApi, DbcProof, EAnchor, OpRef, Status, Validity}; -use crate::vm::{ - ContractStateAccess, ContractStateEvolve, OrdOpRef, WitnessOrd, XWitnessId, XWitnessTx, -}; +use super::status::{Failure, Warning}; +use super::{CheckedConsignment, ConsignmentApi, EAnchor, Status, Validity}; +use crate::operation::seal::ExposedSeal; +use crate::vm::{ContractStateAccess, ContractStateEvolve, OrdOpRef, WitnessOrd}; use crate::{ - validation, AltLayer1, BundleId, ContractId, Layer1, OpId, OpType, Operation, Opout, Schema, - SchemaId, TransitionBundle, XChain, XOutpoint, XOutputSeal, + validation, BundleId, ChainNet, ContractId, OpFullType, OpId, Operation, Opout, OutputSeal, + Schema, SchemaId, TransitionBundle, }; #[derive(Clone, PartialEq, Eq, Debug, Display, Error, From)] @@ -49,43 +49,39 @@ use crate::{ )] pub enum WitnessResolverError { /// actual witness id {actual} doesn't match expected id {expected}. - IdMismatch { - actual: XWitnessId, - expected: XWitnessId, - }, + IdMismatch { actual: Txid, expected: Txid }, /// witness {0} does not exist. - Unknown(XWitnessId), + Unknown(Txid), /// unable to retrieve witness {0}, {1} - Other(XWitnessId, String), + Other(Txid, String), + /// resolver is for another chain-network pair + WrongChainNet, } pub trait ResolveWitness { - // TODO: Return with SPV proof data - fn resolve_pub_witness( - &self, - witness_id: XWitnessId, - ) -> Result; + fn resolve_pub_witness(&self, witness_id: Txid) -> Result; - fn resolve_pub_witness_ord( - &self, - witness_id: XWitnessId, - ) -> Result; + fn resolve_pub_witness_ord(&self, witness_id: Txid) + -> Result; + + fn check_chain_net(&self, chain_net: ChainNet) -> Result<(), WitnessResolverError>; } impl ResolveWitness for &T { - fn resolve_pub_witness( - &self, - witness_id: XWitnessId, - ) -> Result { + fn resolve_pub_witness(&self, witness_id: Txid) -> Result { ResolveWitness::resolve_pub_witness(*self, witness_id) } fn resolve_pub_witness_ord( &self, - witness_id: XWitnessId, + witness_id: Txid, ) -> Result { ResolveWitness::resolve_pub_witness_ord(*self, witness_id) } + + fn check_chain_net(&self, chain_net: ChainNet) -> Result<(), WitnessResolverError> { + ResolveWitness::check_chain_net(*self, chain_net) + } } struct CheckedWitnessResolver { @@ -97,12 +93,9 @@ impl From for CheckedWitnessResolver { } impl ResolveWitness for CheckedWitnessResolver { - fn resolve_pub_witness( - &self, - witness_id: XWitnessId, - ) -> Result { + fn resolve_pub_witness(&self, witness_id: Txid) -> Result { let witness = self.inner.resolve_pub_witness(witness_id)?; - let actual_id = witness.witness_id(); + let actual_id = witness.txid(); if actual_id != witness_id { return Err(WitnessResolverError::IdMismatch { actual: actual_id, @@ -115,10 +108,14 @@ impl ResolveWitness for CheckedWitnessResolver { #[inline] fn resolve_pub_witness_ord( &self, - witness_id: XWitnessId, + witness_id: Txid, ) -> Result { self.inner.resolve_pub_witness_ord(witness_id) } + + fn check_chain_net(&self, chain_net: ChainNet) -> Result<(), WitnessResolverError> { + self.inner.check_chain_net(chain_net) + } } pub struct Validator< @@ -134,23 +131,32 @@ pub struct Validator< schema_id: SchemaId, contract_id: ContractId, - layers1: BTreeSet, + chain_net: ChainNet, contract_state: Rc>, - validated_op_seals: RefCell>, + input_assignments: RefCell>, + // Operations in this set will not be validated + trusted_op_seals: BTreeSet, resolver: CheckedWitnessResolver<&'resolver R>, + safe_height: Option, } impl< - 'consignment, - 'resolver, - S: ContractStateAccess + ContractStateEvolve, - C: ConsignmentApi, - R: ResolveWitness, -> Validator<'consignment, 'resolver, S, C, R> + 'consignment, + 'resolver, + S: ContractStateAccess + ContractStateEvolve, + C: ConsignmentApi, + R: ResolveWitness, + > Validator<'consignment, 'resolver, S, C, R> { - fn init(consignment: &'consignment C, resolver: &'resolver R, context: S::Context<'_>) -> Self { + fn init( + consignment: &'consignment C, + resolver: &'resolver R, + context: S::Context<'_>, + safe_height: Option, + trusted_op_seals: BTreeSet, + ) -> Self { // We use validation status object to store all detected failures and // warnings let status = Status::default(); @@ -160,22 +166,21 @@ impl< let genesis = consignment.genesis(); let contract_id = genesis.contract_id(); let schema_id = genesis.schema_id; + let chain_net = genesis.chain_net; - // Prevent repeated validation of single-use seals - let validated_op_seals = RefCell::new(BTreeSet::::new()); - - let mut layers1 = bset! { Layer1::Bitcoin }; - layers1.extend(genesis.alt_layers1.iter().map(AltLayer1::layer1)); + let input_transitions = RefCell::new(BTreeSet::::new()); Self { consignment, status: RefCell::new(status), schema_id, contract_id, - layers1, - validated_op_seals, + chain_net, + trusted_op_seals, + input_assignments: input_transitions, resolver: CheckedWitnessResolver::from(resolver), contract_state: Rc::new(RefCell::new(S::init(context))), + safe_height, } } @@ -191,17 +196,27 @@ impl< pub fn validate( consignment: &'consignment C, resolver: &'resolver R, - testnet: bool, + chain_net: ChainNet, context: S::Context<'_>, + safe_height: Option, + trusted_op_seals: BTreeSet, ) -> Status { - let mut validator = Self::init(consignment, resolver, context); - // If the network mismatches there is no point in validating the contract since - // all witness transactions will be missed. - if testnet != validator.consignment.genesis().testnet { + let mut validator = + Self::init(consignment, resolver, context, safe_height, trusted_op_seals); + // If the chain-network pair doesn't match there is no point in validating the contract + // since all witness transactions will be missed. + if validator.chain_net != chain_net { validator .status .borrow_mut() - .add_failure(Failure::NetworkMismatch(testnet)); + .add_failure(Failure::ContractChainNetMismatch(chain_net)); + return validator.status.into_inner(); + } + if resolver.check_chain_net(chain_net).is_err() { + validator + .status + .borrow_mut() + .add_failure(Failure::ResolverChainNetMismatch(chain_net)); return validator.status.into_inner(); } @@ -259,6 +274,7 @@ impl< // [VALIDATION]: Iterating over all consignment operations, ordering them according to the // consensus ordering rules. let mut ops = BTreeSet::::new(); + let mut unsafe_history_map: HashMap> = HashMap::new(); for bundle_id in self.consignment.bundle_ids() { let bundle = self .consignment @@ -279,32 +295,30 @@ impl< return; } }; - for op in bundle.known_transitions.values() { - ops.insert(OrdOpRef::Transition(op, witness_id, witness_ord)); - for input in &op.inputs { - // We will error in `validate_operations` below on the absent extension from the - // consignment. - if let Some(OpRef::Extension(extension)) = - self.consignment.operation(input.prev_out.op) - { - let ext = OrdOpRef::Extension(extension, witness_id, witness_ord); - // Account only for the first time when extension seal was closed - let prev = ops.iter().find(|r| matches!(r, OrdOpRef::Extension(ext, ..) if ext.id() == extension.id())).copied(); - match prev { - Some(old) if old > ext => { - ops.remove(&old); - ops.insert(ext) - } - None => ops.insert(ext), - _ => { - /* the extension is already present in the queue and properly - * ordered, so we have nothing to add or change */ - true - } - }; + if let Some(safe_height) = self.safe_height { + match witness_ord { + WitnessOrd::Mined(witness_pos) => { + let witness_height = witness_pos.height(); + if witness_height > safe_height { + unsafe_history_map + .entry(witness_height.into()) + .or_default() + .insert(witness_id); + } + } + WitnessOrd::Tentative | WitnessOrd::Ignored | WitnessOrd::Archived => { + unsafe_history_map.entry(0).or_default().insert(witness_id); } } } + for op in bundle.known_transitions.values() { + ops.insert(OrdOpRef::Transition(op, witness_id, witness_ord, bundle_id)); + } + } + if self.safe_height.is_some() && !unsafe_history_map.is_empty() { + self.status + .borrow_mut() + .add_warning(Warning::UnsafeHistory(unsafe_history_map)); } for op in ops { // We do not skip validating archive operations since after a re-org they may @@ -317,6 +331,9 @@ impl< fn validate_operation(&self, operation: OrdOpRef<'consignment>) { let schema = self.consignment.schema(); let opid = operation.id(); + if self.trusted_op_seals.contains(&opid) { + return; + } if operation.contract_id() != self.contract_id { self.status @@ -324,8 +341,8 @@ impl< .add_failure(Failure::ContractMismatch(opid, operation.contract_id())); } - if !self.validated_op_seals.borrow().contains(&opid) - && operation.op_type() == OpType::StateTransition + if !self.status.borrow_mut().validated_opids.contains(&opid) + && matches!(operation.full_type(), OpFullType::StateTransition(_)) { self.status .borrow_mut() @@ -341,35 +358,10 @@ impl< } OrdOpRef::Transition(transition, ..) => { for input in &transition.inputs { - if self.consignment.operation(input.prev_out.op).is_none() { - self.status - .borrow_mut() - .add_failure(Failure::OperationAbsent(input.prev_out.op)); - } - } - } - OrdOpRef::Extension(extension, ..) => { - for (valency, prev_id) in &extension.redeemed { - let Some(prev_op) = self.consignment.operation(*prev_id) else { + if self.consignment.operation(input.op).is_none() { self.status .borrow_mut() - .add_failure(Failure::ValencyNoParent { - opid, - prev_id: *prev_id, - valency: *valency, - }); - continue; - }; - - if !prev_op.valencies().contains(valency) { - self.status - .borrow_mut() - .add_failure(Failure::NoPrevValency { - opid, - prev_id: *prev_id, - valency: *valency, - }); - continue; + .add_failure(Failure::OperationAbsent(input.op)); } } } @@ -392,17 +384,13 @@ impl< continue; }; - // [VALIDATION]: We validate that the seals were properly defined on BP-type layers - let (seals, input_map) = self.validate_seal_definitions(witness_id.layer1(), bundle); - - // [VALIDATION]: We validate that the seals were properly closed on BP-type layers - let Some(witness_tx) = self.validate_seal_commitments( - &seals, - bundle_id, - witness_id, - bundle.close_method, - anchor, - ) else { + // [VALIDATION]: We validate that the seals were properly defined on BP-type layer + let (seals, input_map) = self.validate_seal_definitions(bundle); + + // [VALIDATION]: We validate that the seals were properly closed on BP-type layer + let Some(witness_tx) = + self.validate_seal_commitments(&seals, bundle_id, witness_id, anchor) + else { continue; }; @@ -419,31 +407,34 @@ impl< &self, bundle_id: BundleId, bundle: &TransitionBundle, - pub_witness: XWitnessTx, - input_map: BTreeMap>, + pub_witness: Tx, + input_map: BTreeMap>, ) { - let witness_id = pub_witness.witness_id(); - for (vin, opid) in &bundle.input_map { - let Some(outpoints) = input_map.get(opid) else { - self.status - .borrow_mut() - .add_failure(Failure::BundleExtraTransition(bundle_id, *opid)); - continue; - }; - let layer1 = pub_witness.layer1(); - let pub_witness = pub_witness.as_reduced_unsafe(); - let Some(input) = pub_witness.inputs.get(vin.to_usize()) else { - self.status - .borrow_mut() - .add_failure(Failure::BundleInvalidInput(bundle_id, *opid, witness_id)); - continue; - }; - if !outpoints.contains(&XChain::with(layer1, input.prev_output)) { - self.status - .borrow_mut() - .add_failure(Failure::BundleInvalidCommitment( - bundle_id, *vin, witness_id, *opid, - )); + let witness_id = pub_witness.txid(); + for (vin, opids) in &bundle.input_map { + for opid in opids { + if self.trusted_op_seals.contains(&opid) { + continue; + } + let Some(outpoints) = input_map.get(&opid) else { + self.status + .borrow_mut() + .add_failure(Failure::BundleExtraTransition(bundle_id, opid)); + continue; + }; + let Some(input) = pub_witness.inputs.get(vin.to_usize()) else { + self.status + .borrow_mut() + .add_failure(Failure::BundleInvalidInput(bundle_id, opid, witness_id)); + continue; + }; + if !outpoints.contains(&input.prev_output) { + self.status + .borrow_mut() + .add_failure(Failure::BundleInvalidCommitment( + bundle_id, *vin, witness_id, opid, + )); + } } } } @@ -452,12 +443,11 @@ impl< /// bitcoin commitments with opret and tapret schema. fn validate_seal_commitments( &self, - seals: impl AsRef<[XOutputSeal]>, + seals: impl AsRef<[OutputSeal]>, bundle_id: BundleId, - witness_id: XWitnessId, - close_method: CloseMethod, + witness_id: Txid, anchor: &EAnchor, - ) -> Option { + ) -> Option { // Check that the anchor is committed into a transaction spending all the // transition inputs. // Here the method can do SPV proof instead of querying the indexer. The SPV @@ -480,41 +470,8 @@ impl< } Ok(pub_witness) => { let seals = seals.as_ref(); - for seal in seals.iter().filter(|seal| seal.method() != close_method) { - self.status - .borrow_mut() - .add_failure(Failure::SealInvalidMethod(bundle_id, *seal)); - } - match (close_method, anchor.clone()) { - ( - CloseMethod::TapretFirst, - EAnchor { - mpc_proof, - dbc_proof: DbcProof::Tapret(tapret), - .. - }, - ) => { - let witness = pub_witness.clone().map(|tx| Witness::with(tx, tapret)); - self.validate_seal_closing(seals, bundle_id, witness, mpc_proof) - } - ( - CloseMethod::OpretFirst, - EAnchor { - mpc_proof, - dbc_proof: DbcProof::Opret(opret), - .. - }, - ) => { - let witness = pub_witness.clone().map(|tx| Witness::with(tx, opret)); - self.validate_seal_closing(seals, bundle_id, witness, mpc_proof) - } - (_, _) => { - self.status - .borrow_mut() - .add_failure(Failure::AnchorMethodMismatch(bundle_id)); - } - } - + let witness = Witness::with(pub_witness.clone(), anchor.dbc_proof.clone()); + self.validate_seal_closing(seals, bundle_id, witness, anchor.mpc_proof.clone()); Some(pub_witness) } } @@ -522,19 +479,20 @@ impl< /// Single-use-seal definition validation. /// - /// Takes state transition, extracts all seals from its inputs and makes - /// sure they are defined or a correct layer1. + /// Takes state transition, extracts all seals from its inputs and validates them. fn validate_seal_definitions( &self, - layer1: Layer1, bundle: &TransitionBundle, - ) -> (Vec, BTreeMap>) { - let mut input_map: BTreeMap> = bmap!(); + ) -> (Vec, BTreeMap>) { + let mut input_map: BTreeMap> = bmap!(); let mut seals = vec![]; for (opid, transition) in &bundle.known_transitions { + if self.trusted_op_seals.contains(opid) { + continue; + } let opid = *opid; - if !self.validated_op_seals.borrow_mut().insert(opid) { + if !self.status.borrow_mut().validated_opids.insert(opid) { self.status .borrow_mut() .add_failure(Failure::CyclicGraph(opid)); @@ -543,7 +501,12 @@ impl< // Checking that witness transaction closes seals defined by transition previous // outputs. for input in &transition.inputs { - let Opout { op, ty, no } = input.prev_out; + let Opout { op, ty, no } = input; + if !self.input_assignments.borrow_mut().insert(input) { + self.status + .borrow_mut() + .add_failure(Failure::DoubleSpend(input)); + } let Some(prev_op) = self.consignment.operation(op) else { // Node, referenced as the ancestor, was not found in the consignment. @@ -566,7 +529,7 @@ impl< let Ok(seal) = variant.revealed_seal_at(no) else { self.status .borrow_mut() - .add_failure(Failure::NoPrevOut(opid, input.prev_out)); + .add_failure(Failure::NoPrevOut(opid, input)); continue; }; let Some(seal) = seal else { @@ -574,56 +537,28 @@ impl< // full verification and have to report the failure self.status .borrow_mut() - .add_failure(Failure::ConfidentialSeal(input.prev_out)); + .add_failure(Failure::ConfidentialSeal(input)); continue; }; - if seal.layer1() != layer1 { - self.status - .borrow_mut() - .add_failure(Failure::SealWitnessLayer1Mismatch { - seal: seal.layer1(), - anchor: layer1, - }); - continue; - } - if !self.layers1.contains(&seal.layer1()) { - self.status - .borrow_mut() - .add_failure(Failure::SealLayerMismatch(seal.layer1(), seal)); - continue; - } - - let seal = if prev_op.op_type() == OpType::StateTransition { + let seal = if matches!(prev_op.full_type(), OpFullType::StateTransition(_)) { let Some(witness_id) = self.consignment.op_witness_id(op) else { self.status .borrow_mut() .add_failure(Failure::OperationAbsent(op)); continue; }; - - match seal.try_to_output_seal(witness_id) { - Ok(seal) => seal, - Err(_) => { - self.status.borrow_mut().add_failure( - Failure::SealWitnessLayer1Mismatch { - seal: seal.layer1(), - anchor: witness_id.layer1(), - }, - ); - continue; - } - } + seal.to_output_seal_or_default(witness_id) } else { seal.to_output_seal() - .expect("genesis and state extensions must have explicit seals") + .expect("genesis must have explicit seals") }; seals.push(seal); input_map .entry(opid) .or_default() - .insert(seal.map(|seal| Outpoint::new(seal.txid, seal.vout)).into()); + .insert(Outpoint::new(seal.txid, seal.vout)); } } (seals, input_map) @@ -643,14 +578,14 @@ impl< &self, seals: impl IntoIterator, bundle_id: BundleId, - witness: XChain>, + witness: Witness, mpc_proof: mpc::MerkleProof, ) where - XChain>: SealWitness, + Witness: SealWitness, { let message = mpc::Message::from(bundle_id); - let witness_id = witness.witness_id(); - let anchor = Anchor::new(mpc_proof, witness.as_reduced_unsafe().proof.clone()); + let witness_id = witness.txid; + let anchor = Anchor::new(mpc_proof, witness.proof.clone()); // [VALIDATION]: Checking anchor MPC commitment match anchor.convolve(self.contract_id, message) { Err(err) => { @@ -661,6 +596,28 @@ impl< .add_failure(Failure::MpcInvalid(bundle_id, witness_id, err)); } Ok(commitment) => { + // [VALIDATION]: Verify commitment + let Some(output) = witness + .tx + .outputs() + .find(|out| out.script_pubkey.is_op_return() || out.script_pubkey.is_p2tr()) + else { + self.status + .borrow_mut() + .add_failure(Failure::NoDbcOutput(witness_id)); + return; + }; + let output_method = if output.script_pubkey.is_op_return() { + CloseMethod::OpretFirst + } else { + CloseMethod::TapretFirst + }; + let proof_method = witness.proof.method(); + if proof_method != output_method { + self.status + .borrow_mut() + .add_failure(Failure::InvalidProofType(witness_id, proof_method)); + } // [VALIDATION]: CHECKING SINGLE-USE-SEALS witness .verify_many_seals(seals, &commitment) diff --git a/src/vm/contract.rs b/src/vm/contract.rs index 9bf0e2f5..ce51c6cc 100644 --- a/src/vm/contract.rs +++ b/src/vm/contract.rs @@ -23,118 +23,21 @@ use std::borrow::Borrow; use std::cell::RefCell; use std::cmp::Ordering; -use std::fmt::Debug; +use std::fmt::{self, Debug, Display, Formatter}; use std::num::NonZeroU32; use std::rc::Rc; -use amplify::confinement; use amplify::num::u24; -use bp::seals::txout::{CloseMethod, ExplicitSeal, VerifyError, Witness}; -use bp::{dbc, Tx, Txid}; -use commit_verify::mpc; -use single_use_seals::SealWitness; +use bp::{BlockHeight, Outpoint, Txid}; +use chrono::{MappedLocalTime, TimeZone, Utc}; use strict_encoding::{StrictDecode, StrictDumb, StrictEncode}; use crate::{ - AssetTags, AssignmentType, Assignments, AssignmentsRef, AttachState, ContractId, DataState, - ExposedSeal, Extension, ExtensionType, FungibleState, Genesis, GlobalState, GlobalStateType, - GraphSeal, Impossible, Inputs, Metadata, OpFullType, OpId, OpType, Operation, Transition, - TransitionType, TxoSeal, TypedAssigns, Valencies, XChain, XOutpoint, XOutputSeal, - LIB_NAME_RGB_LOGIC, + AssignmentType, Assignments, AssignmentsRef, BundleId, ContractId, FungibleState, Genesis, + GlobalState, GlobalStateType, GraphSeal, Layer1, Metadata, OpFullType, OpId, Operation, + RevealedData, Transition, TransitionType, TypedAssigns, LIB_NAME_RGB_LOGIC, }; -pub type XWitnessId = XChain; - -pub type XWitnessTx = XChain; - -impl XWitnessTx { - pub fn witness_id(&self) -> XWitnessId { - match self { - Self::Bitcoin(tx) => XWitnessId::Bitcoin(tx.txid()), - Self::Liquid(tx) => XWitnessId::Liquid(tx.txid()), - Self::Other(_) => unreachable!(), - } - } -} - -impl XChain> { - pub fn witness_id(&self) -> XWitnessId { - match self { - Self::Bitcoin(w) => XWitnessId::Bitcoin(w.txid), - Self::Liquid(w) => XWitnessId::Liquid(w.txid), - Self::Other(_) => unreachable!(), - } - } -} - -impl SealWitness for XChain> { - type Message = mpc::Commitment; - type Error = VerifyError; - - fn verify_seal(&self, seal: &Seal, msg: &Self::Message) -> Result<(), Self::Error> { - match self { - Self::Bitcoin(witness) | Self::Liquid(witness) => witness.verify_seal(seal, msg), - Self::Other(_) => unreachable!(), - } - } - - fn verify_many_seals<'seal>( - &self, - seals: impl IntoIterator, - msg: &Self::Message, - ) -> Result<(), Self::Error> - where - Seal: 'seal, - { - match self { - Self::Bitcoin(witness) | Self::Liquid(witness) => witness.verify_many_seals(seals, msg), - Self::Other(_) => unreachable!(), - } - } -} - -impl XChain { - pub fn method(self) -> CloseMethod - where U: TxoSeal { - match self { - XChain::Bitcoin(seal) => seal.method(), - XChain::Liquid(seal) => seal.method(), - XChain::Other(_) => unreachable!(), - } - } - - #[inline] - pub fn to_output_seal(self) -> Option - where U: TxoSeal { - Some(match self { - XChain::Bitcoin(seal) => { - let outpoint = seal.outpoint()?; - XChain::Bitcoin(ExplicitSeal::new(seal.method(), outpoint)) - } - XChain::Liquid(seal) => { - let outpoint = seal.outpoint()?; - XChain::Liquid(ExplicitSeal::new(seal.method(), outpoint)) - } - XChain::Other(_) => unreachable!(), - }) - } - - pub fn try_to_output_seal(self, witness_id: XWitnessId) -> Result - where U: TxoSeal { - self.to_output_seal() - .or(match (self, witness_id) { - (XChain::Bitcoin(seal), XWitnessId::Bitcoin(txid)) => { - Some(XChain::Bitcoin(ExplicitSeal::new(seal.method(), seal.outpoint_or(txid)))) - } - (XChain::Liquid(seal), XWitnessId::Liquid(txid)) => { - Some(XChain::Liquid(ExplicitSeal::new(seal.method(), seal.outpoint_or(txid)))) - } - _ => None, - }) - .ok_or(self) - } -} - /// The type is used during validation and computing a contract state. It /// combines both the operation with the information required for its ordering /// in the contract history (via construction of [`OpOrd`]) according to the @@ -143,61 +46,50 @@ impl XChain { pub enum OrdOpRef<'op> { #[from] Genesis(&'op Genesis), - Transition(&'op Transition, XWitnessId, WitnessOrd), - Extension(&'op Extension, XWitnessId, WitnessOrd), + Transition(&'op Transition, Txid, WitnessOrd, BundleId), } -impl<'op> PartialOrd for OrdOpRef<'op> { +impl PartialOrd for OrdOpRef<'_> { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } -impl<'op> Ord for OrdOpRef<'op> { +impl Ord for OrdOpRef<'_> { fn cmp(&self, other: &Self) -> Ordering { self.op_ord().cmp(&other.op_ord()) } } -impl<'op> OrdOpRef<'op> { - pub fn witness_id(&self) -> Option { +impl OrdOpRef<'_> { + pub fn witness_id(&self) -> Option { match self { OrdOpRef::Genesis(_) => None, - OrdOpRef::Transition(_, witness_id, ..) | OrdOpRef::Extension(_, witness_id, ..) => { - Some(*witness_id) - } + OrdOpRef::Transition(_, witness_id, ..) => Some(*witness_id), + } + } + + pub fn bundle_id(&self) -> Option { + match self { + OrdOpRef::Genesis(_) => None, + OrdOpRef::Transition(_, _, _, bundle_id) => Some(*bundle_id), } } pub fn op_ord(&self) -> OpOrd { match self { OrdOpRef::Genesis(_) => OpOrd::Genesis, - OrdOpRef::Transition(op, _, witness_ord) => OpOrd::Transition { + OrdOpRef::Transition(op, _, witness_ord, _) => OpOrd::Transition { witness: *witness_ord, ty: op.transition_type, nonce: op.nonce, opid: op.id(), }, - OrdOpRef::Extension(op, _, witness_ord) => OpOrd::Extension { - witness: *witness_ord, - ty: op.extension_type, - nonce: op.nonce, - opid: op.id(), - }, } } } impl<'op> Operation for OrdOpRef<'op> { - fn op_type(&self) -> OpType { - match self { - OrdOpRef::Genesis(op) => op.op_type(), - OrdOpRef::Transition(op, ..) => op.op_type(), - OrdOpRef::Extension(op, ..) => op.op_type(), - } - } - fn full_type(&self) -> OpFullType { match self { OrdOpRef::Genesis(op) => op.full_type(), OrdOpRef::Transition(op, ..) => op.full_type(), - OrdOpRef::Extension(op, ..) => op.full_type(), } } @@ -205,7 +97,6 @@ impl<'op> Operation for OrdOpRef<'op> { match self { OrdOpRef::Genesis(op) => op.id(), OrdOpRef::Transition(op, ..) => op.id(), - OrdOpRef::Extension(op, ..) => op.id(), } } @@ -213,7 +104,6 @@ impl<'op> Operation for OrdOpRef<'op> { match self { OrdOpRef::Genesis(op) => op.contract_id(), OrdOpRef::Transition(op, ..) => op.contract_id(), - OrdOpRef::Extension(op, ..) => op.contract_id(), } } @@ -221,23 +111,6 @@ impl<'op> Operation for OrdOpRef<'op> { match self { OrdOpRef::Genesis(op) => op.nonce(), OrdOpRef::Transition(op, ..) => op.nonce(), - OrdOpRef::Extension(op, ..) => op.nonce(), - } - } - - fn transition_type(&self) -> Option { - match self { - OrdOpRef::Genesis(op) => op.transition_type(), - OrdOpRef::Transition(op, ..) => op.transition_type(), - OrdOpRef::Extension(op, ..) => op.transition_type(), - } - } - - fn extension_type(&self) -> Option { - match self { - OrdOpRef::Genesis(op) => op.extension_type(), - OrdOpRef::Transition(op, ..) => op.extension_type(), - OrdOpRef::Extension(op, ..) => op.extension_type(), } } @@ -245,7 +118,6 @@ impl<'op> Operation for OrdOpRef<'op> { match self { OrdOpRef::Genesis(op) => op.metadata(), OrdOpRef::Transition(op, ..) => op.metadata(), - OrdOpRef::Extension(op, ..) => op.metadata(), } } @@ -253,23 +125,13 @@ impl<'op> Operation for OrdOpRef<'op> { match self { OrdOpRef::Genesis(op) => op.globals(), OrdOpRef::Transition(op, ..) => op.globals(), - OrdOpRef::Extension(op, ..) => op.globals(), - } - } - - fn valencies(&self) -> &Valencies { - match self { - OrdOpRef::Genesis(op) => op.valencies(), - OrdOpRef::Transition(op, ..) => op.valencies(), - OrdOpRef::Extension(op, ..) => op.valencies(), } } fn assignments(&self) -> AssignmentsRef<'op> { match self { - OrdOpRef::Genesis(op) => (&op.assignments).into(), - OrdOpRef::Transition(op, ..) => (&op.assignments).into(), - OrdOpRef::Extension(op, ..) => (&op.assignments).into(), + OrdOpRef::Genesis(op) => op.assignments(), + OrdOpRef::Transition(op, ..) => op.assignments(), } } @@ -277,42 +139,75 @@ impl<'op> Operation for OrdOpRef<'op> { match self { OrdOpRef::Genesis(op) => op.assignments_by_type(t), OrdOpRef::Transition(op, ..) => op.assignments_by_type(t), - OrdOpRef::Extension(op, ..) => op.assignments_by_type(t), - } - } - - fn inputs(&self) -> Inputs { - match self { - OrdOpRef::Genesis(op) => op.inputs(), - OrdOpRef::Transition(op, ..) => op.inputs(), - OrdOpRef::Extension(op, ..) => op.inputs(), } } } -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Display)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[derive(Getters, Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(StrictType, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_RGB_LOGIC)] #[cfg_attr( feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate", rename_all = "camelCase") )] -#[display("{height}@{timestamp}")] pub struct WitnessPos { - height: u32, + #[getter(as_copy)] + layer1: Layer1, + + #[getter(as_copy)] + height: BlockHeight, + + #[getter(as_copy)] timestamp: i64, } +impl StrictDumb for WitnessPos { + fn strict_dumb() -> Self { + Self { + layer1: Layer1::Bitcoin, + height: NonZeroU32::MIN, + timestamp: 1231006505, + } + } +} + +// Sat Jan 03 18:15:05 2009 UTC +const BITCOIN_GENESIS_TIMESTAMP: i64 = 1231006505; + +// Sat Jan 03 18:15:05 2009 UTC +const LIQUID_GENESIS_TIMESTAMP: i64 = 1296692202; + impl WitnessPos { - pub fn new(height: u32, timestamp: i64) -> Option { - if height == 0 || timestamp < 1231006505 { + #[deprecated( + since = "0.11.0-beta.9", + note = "please use `WitnessPos::bitcoin` or `WitnessPos::liquid` instead" + )] + pub fn new(height: NonZeroU32, timestamp: i64) -> Option { + Self::bitcoin(height, timestamp) + } + + pub fn bitcoin(height: NonZeroU32, timestamp: i64) -> Option { + if timestamp < BITCOIN_GENESIS_TIMESTAMP { return None; } - Some(WitnessPos { height, timestamp }) + Some(WitnessPos { + layer1: Layer1::Bitcoin, + height, + timestamp, + }) } - pub fn height(&self) -> NonZeroU32 { NonZeroU32::new(self.height).expect("invariant") } + pub fn liquid(height: NonZeroU32, timestamp: i64) -> Option { + if timestamp < LIQUID_GENESIS_TIMESTAMP { + return None; + } + Some(WitnessPos { + layer1: Layer1::Liquid, + height, + timestamp, + }) + } } impl PartialOrd for WitnessPos { @@ -327,15 +222,37 @@ impl Ord for WitnessPos { fn cmp(&self, other: &Self) -> Ordering { assert!(self.timestamp > 0); assert!(other.timestamp > 0); - self.timestamp.cmp(&other.timestamp) + const BLOCK_TIME: i64 = 10 /*min*/ * 60 /*secs*/; + match (self.layer1, other.layer1) { + (a, b) if a == b => self.height.cmp(&other.height), + (Layer1::Bitcoin, Layer1::Liquid) + if (self.timestamp - other.timestamp).abs() < BLOCK_TIME => + { + Ordering::Greater + } + (Layer1::Liquid, Layer1::Bitcoin) + if (other.timestamp - self.timestamp).abs() < BLOCK_TIME => + { + Ordering::Less + } + _ => self.timestamp.cmp(&other.timestamp), + } } } -/// RGB consensus information about the status of a witness transaction. This -/// information is used in ordering state transition and state extension -/// processing in the AluVM during the validation, as well as consensus ordering -/// of the contract global state data, as they are presented to all contract -/// users. +impl Display for WitnessPos { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{}:{}, ", self.layer1, self.height)?; + match Utc.timestamp_opt(self.timestamp, 0) { + MappedLocalTime::Single(time) => write!(f, "{}", time.format("%Y-%m-%d %H:%M:%S")), + _ => f.write_str("invalid timestamp"), + } + } +} + +/// RGB consensus information about the status of a witness transaction. This information is used +/// in ordering state transitions during the validation, as well as consensus ordering of the +/// contract global state data, as they are presented to all contract users. #[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Debug, Display, From)] #[display(lowercase)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] @@ -346,18 +263,6 @@ impl Ord for WitnessPos { serde(crate = "serde_crate", rename_all = "camelCase") )] pub enum WitnessOrd { - /// Witness transaction must be excluded from the state processing. - /// - /// Cases for the exclusion: - /// - transaction was removed from blockchain after a re-org and its inputs were spent by other - /// transaction; - /// - previous transaction(s) after RBF replacement, once it is excluded from the mempool and - /// replaced by RBFed successors; - /// - past state channel transactions once a new channel state is signed (and until they may - /// become valid once again due to an uncooperative channel closing). - #[strict_type(dumb)] - Archived, - /// Transaction is included into layer 1 blockchain at a specific height and /// timestamp. /// @@ -383,6 +288,21 @@ pub enum WitnessOrd { /// - transaction is an RBF replacement prepared to be broadcast (with the previous transaction /// set to [`Self::Archived`] at the same moment). Tentative, + + /// Witness transaction must be ignored by the update witnesses process. + Ignored, + + /// Witness transaction must be excluded from the state processing. + /// + /// Cases for the exclusion: + /// - transaction was removed from blockchain after a re-org and its inputs were spent by other + /// transaction; + /// - previous transaction(s) after RBF replacement, once it is excluded from the mempool and + /// replaced by RBFed successors; + /// - past state channel transactions once a new channel state is signed (and until they may + /// become valid once again due to an uncooperative channel closing). + #[strict_type(dumb)] + Archived, } impl WitnessOrd { @@ -397,8 +317,6 @@ impl WitnessOrd { /// - Genesis is processed first. /// - Other operations are ordered according to their witness transactions (see [`WitnessOrd`] for /// the details). -/// - Extensions share witness transaction with the state transition which first to close one of the -/// seals defined in the extension, but are processed before that state transition. /// - If two or more operations share the same witness transaction ordering, they are first ordered /// basing on their `nonce` value, and if it is also the same, basing on their operation id value. /// @@ -414,13 +332,6 @@ impl WitnessOrd { pub enum OpOrd { #[strict_type(tag = 0x00, dumb)] Genesis, - #[strict_type(tag = 0x01)] - Extension { - witness: WitnessOrd, - ty: ExtensionType, - nonce: u64, - opid: OpId, - }, #[strict_type(tag = 0xFF)] Transition { witness: WitnessOrd, @@ -433,16 +344,10 @@ pub enum OpOrd { impl OpOrd { #[inline] pub fn is_archived(&self) -> bool { - matches!( - self, - Self::Extension { - witness: WitnessOrd::Archived, - .. - } | Self::Transition { - witness: WitnessOrd::Archived, - .. - } - ) + matches!(self, Self::Transition { + witness: WitnessOrd::Archived, + .. + }) } } @@ -484,27 +389,10 @@ impl GlobalOrd { idx, } } - pub fn extension( - opid: OpId, - idx: u16, - ty: ExtensionType, - nonce: u64, - witness: WitnessOrd, - ) -> Self { - Self { - op_ord: OpOrd::Extension { - witness, - ty, - nonce, - opid, - }, - idx, - } - } } pub trait GlobalStateIter { - type Data: Borrow; + type Data: Borrow; fn size(&mut self) -> u24; fn prev(&mut self) -> Option<(GlobalOrd, Self::Data)>; fn last(&mut self) -> Option<(GlobalOrd, Self::Data)>; @@ -548,15 +436,6 @@ impl GlobalContractState { fn prev_checked(&mut self) -> Option<(GlobalOrd, I::Data)> { let (ord, item) = self.iter.prev()?; - if self.last_ord.map(|last| ord <= last).unwrap_or_default() { - panic!( - "global contract state iterator has invalid implementation: it fails to order \ - global state according to the consensus ordering" - ); - } - if ord.op_ord.is_archived() { - panic!("invalid GlobalStateIter implementation returning WitnessOrd::Archived") - } self.checked_depth += u24::ONE; self.last_ord = Some(ord); Some((ord, item)) @@ -565,8 +444,8 @@ impl GlobalContractState { /// Retrieves global state data located `depth` items back from the most /// recent global state value. Ensures that the global state ordering is /// consensus-based. - pub fn nth(&mut self, depth: u24) -> Option + '_> { - if depth >= self.iter.size() { + pub fn nth(&mut self, depth: u24) -> Option + '_> { + if depth > self.iter.size() { return None; } if depth >= self.checked_depth { @@ -574,7 +453,7 @@ impl GlobalContractState { } else { self.iter.reset(self.checked_depth); let size = self.iter.size(); - let to = (depth - self.checked_depth).to_u32(); + let to = (self.checked_depth - depth).to_u32(); for inc in 0..to { if self.prev_checked().is_none() { panic!( @@ -606,69 +485,76 @@ pub trait ContractStateAccess: Debug { ty: GlobalStateType, ) -> Result, UnknownGlobalStateType>; - fn rights(&self, outpoint: XOutpoint, ty: AssignmentType) -> u32; + fn rights(&self, outpoint: Outpoint, ty: AssignmentType) -> u32; fn fungible( &self, - outpoint: XOutpoint, + outpoint: Outpoint, ty: AssignmentType, ) -> impl DoubleEndedIterator; fn data( &self, - outpoint: XOutpoint, - ty: AssignmentType, - ) -> impl DoubleEndedIterator>; - - fn attach( - &self, - outpoint: XOutpoint, + outpoint: Outpoint, ty: AssignmentType, - ) -> impl DoubleEndedIterator>; + ) -> impl DoubleEndedIterator>; } pub trait ContractStateEvolve { type Context<'ctx>; + type Error: std::error::Error; fn init(context: Self::Context<'_>) -> Self; - // TODO: distinguish contract validation failure errors from connectivity - // errors. Allow custom error types here. - fn evolve_state(&mut self, op: OrdOpRef) -> Result<(), confinement::Error>; + fn evolve_state(&mut self, op: OrdOpRef) -> Result<(), Self::Error>; } pub struct VmContext<'op, S: ContractStateAccess> { pub contract_id: ContractId, - pub asset_tags: &'op AssetTags, pub op_info: OpInfo<'op>, pub contract_state: Rc>, } pub struct OpInfo<'op> { pub id: OpId, - pub ty: OpFullType, - pub metadata: &'op Metadata, pub prev_state: &'op Assignments, - pub owned_state: AssignmentsRef<'op>, - pub redeemed: &'op Valencies, - pub valencies: &'op Valencies, - pub global: &'op GlobalState, + pub op: &'op OrdOpRef<'op>, } impl<'op> OpInfo<'op> { - pub fn with( - id: OpId, - op: &'op OrdOpRef<'op>, - prev_state: &'op Assignments, - redeemed: &'op Valencies, - ) -> Self { - OpInfo { - id, - ty: op.full_type(), - metadata: op.metadata(), - prev_state, - owned_state: op.assignments(), - redeemed, - valencies: op.valencies(), - global: op.globals(), - } + pub fn with(id: OpId, op: &'op OrdOpRef<'op>, prev_state: &'op Assignments) -> Self { + OpInfo { id, prev_state, op } + } + + pub fn global(&self) -> &'op GlobalState { self.op.globals() } + + pub fn metadata(&self) -> &'op Metadata { self.op.metadata() } + + pub fn owned_state(&self) -> AssignmentsRef<'op> { self.op.assignments() } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn witness_post_timestamp() { + assert_eq!(WitnessPos::bitcoin(NonZeroU32::MIN, BITCOIN_GENESIS_TIMESTAMP - 1), None); + assert_eq!(WitnessPos::liquid(NonZeroU32::MIN, LIQUID_GENESIS_TIMESTAMP - 1), None); + assert_eq!(WitnessPos::liquid(NonZeroU32::MIN, BITCOIN_GENESIS_TIMESTAMP), None); + assert!(WitnessPos::bitcoin(NonZeroU32::MIN, BITCOIN_GENESIS_TIMESTAMP).is_some()); + assert!(WitnessPos::liquid(NonZeroU32::MIN, LIQUID_GENESIS_TIMESTAMP).is_some()); + assert!(WitnessPos::bitcoin(NonZeroU32::MIN, LIQUID_GENESIS_TIMESTAMP).is_some()); + } + + #[test] + fn witness_pos_getters() { + let pos = WitnessPos::bitcoin(NonZeroU32::MIN, BITCOIN_GENESIS_TIMESTAMP).unwrap(); + assert_eq!(pos.height(), NonZeroU32::MIN); + assert_eq!(pos.timestamp(), BITCOIN_GENESIS_TIMESTAMP); + assert_eq!(pos.layer1(), Layer1::Bitcoin); + + let pos = WitnessPos::liquid(NonZeroU32::MIN, LIQUID_GENESIS_TIMESTAMP).unwrap(); + assert_eq!(pos.height(), NonZeroU32::MIN); + assert_eq!(pos.timestamp(), LIQUID_GENESIS_TIMESTAMP); + assert_eq!(pos.layer1(), Layer1::Liquid); } } diff --git a/src/vm/isa.rs b/src/vm/isa.rs index 8148b5ef..b78b4869 100644 --- a/src/vm/isa.rs +++ b/src/vm/isa.rs @@ -29,7 +29,7 @@ use aluvm::library::{CodeEofError, IsaSeg, LibSite, Read, Write}; use aluvm::reg::{CoreRegs, Reg}; use super::opcodes::{INSTR_RGBISA_FROM, INSTR_RGBISA_TO}; -use super::{ContractOp, ContractStateAccess, TimechainOp, VmContext}; +use super::{ContractOp, ContractStateAccess, VmContext}; #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)] #[display(inner)] @@ -37,8 +37,6 @@ use super::{ContractOp, ContractStateAccess, TimechainOp, VmContext}; pub enum RgbIsa { Contract(ContractOp), - Timechain(TimechainOp), - /// All other future unsupported operations, which must set `st0` to /// `false`. Fail(u8), @@ -52,7 +50,6 @@ impl InstructionSet for RgbIsa { fn src_regs(&self) -> BTreeSet { match self { RgbIsa::Contract(op) => op.src_regs(), - RgbIsa::Timechain(op) => op.src_regs(), RgbIsa::Fail(_) => bset![], } } @@ -60,7 +57,6 @@ impl InstructionSet for RgbIsa { fn dst_regs(&self) -> BTreeSet { match self { RgbIsa::Contract(op) => op.dst_regs(), - RgbIsa::Timechain(op) => op.dst_regs(), RgbIsa::Fail(_) => bset![], } } @@ -68,7 +64,6 @@ impl InstructionSet for RgbIsa { fn complexity(&self) -> u64 { match self { RgbIsa::Contract(op) => op.complexity(), - RgbIsa::Timechain(op) => op.complexity(), RgbIsa::Fail(_) => u64::MAX, } } @@ -76,7 +71,6 @@ impl InstructionSet for RgbIsa { fn exec(&self, regs: &mut CoreRegs, site: LibSite, context: &Self::Context<'_>) -> ExecStep { match self { RgbIsa::Contract(op) => op.exec(regs, site, context), - RgbIsa::Timechain(op) => op.exec(regs, site, &()), RgbIsa::Fail(_) => { isa::ControlFlowOp::Fail.exec(regs, site, &()); ExecStep::Stop @@ -91,7 +85,6 @@ impl Bytecode for RgbIsa { fn instr_byte(&self) -> u8 { match self { RgbIsa::Contract(op) => op.instr_byte(), - RgbIsa::Timechain(op) => op.instr_byte(), RgbIsa::Fail(code) => *code, } } @@ -100,7 +93,6 @@ impl Bytecode for RgbIsa { where W: Write { match self { RgbIsa::Contract(op) => op.encode_args(writer), - RgbIsa::Timechain(op) => op.encode_args(writer), RgbIsa::Fail(_) => Ok(()), } } @@ -115,9 +107,6 @@ impl Bytecode for RgbIsa { instr if ContractOp::::instr_range().contains(&instr) => { RgbIsa::Contract(ContractOp::decode(reader)?) } - instr if TimechainOp::instr_range().contains(&instr) => { - RgbIsa::Timechain(TimechainOp::decode(reader)?) - } x => RgbIsa::Fail(x), }) } diff --git a/src/vm/macroasm.rs b/src/vm/macroasm.rs index f05a3d71..d702e1ed 100644 --- a/src/vm/macroasm.rs +++ b/src/vm/macroasm.rs @@ -24,7 +24,7 @@ macro_rules! rgbasm { ($( $tt:tt )+) => {{ #[allow(unused_imports)] { use amplify::num::{u4, u5}; - use $crate::vm::{RgbIsa, ContractOp, TimechainOp}; + use $crate::vm::{RgbIsa, ContractOp}; use $crate::vm::aluasm_isa; use $crate::isa_instr; aluasm_isa! { RgbIsa<_> => $( $tt )+ } @@ -33,14 +33,40 @@ macro_rules! rgbasm { #[macro_export] macro_rules! isa_instr { - (pcvs $no:ident) => {{ RgbIsa::Contract(ContractOp::Pcvs($no)) }}; - (pcas $no:ident) => {{ RgbIsa::Contract(ContractOp::Pcas($no)) }}; - (pcps $no:ident) => {{ RgbIsa::Contract(ContractOp::Pcps($no)) }}; - (cng $t:ident,a8[$a_idx:literal]) => {{ RgbIsa::Contract(ContractOp::CnG($t, Reg32::from(u5::with($a_idx)))) }}; - (cnc $t:ident,a16[$a_idx:literal]) => {{ RgbIsa::Contract(ContractOp::CnC($t, Reg32::from(u5::with($a_idx)))) }}; - (ldm $t:ident,s16[$s_idx:literal]) => {{ RgbIsa::Contract(ContractOp::LdM($t, RegS::from($s_idx))) }}; - (ldg $t:ident,a8[$a_idx:literal],s16[$s_idx:literal]) => {{ RgbIsa::Contract(ContractOp::LdG($t, Reg16::from(u4::with($a_idx)), RegS::from($s_idx))) }}; - (ldp $t:ident,a16[$a_idx:literal],s16[$s_idx:literal]) => {{ RgbIsa::Contract(ContractOp::LdP($t, Reg16::from(u4::with($a_idx)), RegS::from($s_idx))) }}; - (lds $t:ident,a16[$a_idx:literal],s16[$s_idx:literal]) => {{ RgbIsa::Contract(ContractOp::LdS($t, Reg16::from(u4::with($a_idx)), RegS::from($s_idx))) }}; - ($op:ident $($tt:tt)+) => {{ compile_error!(concat!("unknown RGB assembly opcode `", stringify!($op), "`")) }}; + (svs $no:ident) => {{ + RgbIsa::Contract(ContractOp::Svs($no)) + }}; + (sas $no:ident) => {{ + RgbIsa::Contract(ContractOp::Sas($no)) + }}; + (sps $no:ident) => {{ + RgbIsa::Contract(ContractOp::Sps($no)) + }}; + (cng $t:ident,a8[$a_idx:literal]) => {{ + RgbIsa::Contract(ContractOp::CnG($t, Reg32::from(u5::with($a_idx)))) + }}; + (cnc $t:ident,a16[$a_idx:literal]) => {{ + RgbIsa::Contract(ContractOp::CnC($t, Reg32::from(u5::with($a_idx)))) + }}; + (ldc $t:ident,a32[$a_idx:literal],s16[$s_idx:literal]) => {{ + RgbIsa::Contract(ContractOp::LdC($t, Reg16::from(u4::with($a_idx)), RegS::from($s_idx))) + }}; + (ldm $t:ident,s16[$s_idx:literal]) => {{ + RgbIsa::Contract(ContractOp::LdM($t, RegS::from($s_idx))) + }}; + (ldg $t:ident,a8[$a_idx:literal],s16[$s_idx:literal]) => {{ + RgbIsa::Contract(ContractOp::LdG($t, Reg16::from(u4::with($a_idx)), RegS::from($s_idx))) + }}; + (ldp $t:ident,a16[$a_idx:literal],s16[$s_idx:literal]) => {{ + RgbIsa::Contract(ContractOp::LdP($t, Reg16::from(u4::with($a_idx)), RegS::from($s_idx))) + }}; + (lds $t:ident,a16[$a_idx:literal],s16[$s_idx:literal]) => {{ + RgbIsa::Contract(ContractOp::LdS($t, Reg16::from(u4::with($a_idx)), RegS::from($s_idx))) + }}; + (vts s16[$s_idx:literal]) => {{ + RgbIsa::Contract(ContractOp::Vts(RegS::from($s_idx))) + }}; + ($op:ident $($tt:tt)+) => {{ + compile_error!(concat!("unknown RGB assembly opcode `", stringify!($op), "`")) + }}; } diff --git a/src/vm/mod.rs b/src/vm/mod.rs index 89ccf6ad..5a3b4bed 100644 --- a/src/vm/mod.rs +++ b/src/vm/mod.rs @@ -27,7 +27,6 @@ pub mod opcodes; mod isa; mod op_contract; -mod op_timechain; #[macro_use] mod macroasm; mod contract; @@ -35,9 +34,8 @@ mod contract; pub use aluvm::aluasm_isa; pub use contract::{ ContractStateAccess, ContractStateEvolve, GlobalContractState, GlobalOrd, GlobalStateIter, - OpOrd, OrdOpRef, UnknownGlobalStateType, WitnessOrd, WitnessPos, XWitnessId, XWitnessTx, + OpOrd, OrdOpRef, UnknownGlobalStateType, WitnessOrd, WitnessPos, }; pub(crate) use contract::{OpInfo, VmContext}; pub use isa::RgbIsa; pub use op_contract::ContractOp; -pub use op_timechain::TimechainOp; diff --git a/src/vm/op_contract.rs b/src/vm/op_contract.rs index 5d971ff9..a1c58b0d 100644 --- a/src/vm/op_contract.rs +++ b/src/vm/op_contract.rs @@ -33,14 +33,12 @@ use aluvm::library::{CodeEofError, IsaSeg, LibSite, Read, Write}; use aluvm::reg::{CoreRegs, Reg, Reg16, Reg32, RegA, RegS}; use amplify::num::{u24, u3, u4}; use amplify::Wrapper; -use commit_verify::CommitVerify; +use secp256k1::{ecdsa, Message, PublicKey}; use super::opcodes::*; use super::{ContractStateAccess, VmContext}; -use crate::{ - Assign, AssignmentType, BlindingFactor, GlobalStateType, MetaType, PedersenCommitment, - RevealedValue, TypedAssigns, -}; +use crate::vm::OrdOpRef; +use crate::{Assign, AssignmentType, GlobalStateType, MetaType, TypedAssigns}; #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)] pub enum ContractOp { @@ -137,7 +135,7 @@ pub enum ContractOp { #[display("ldm {0},{1}")] LdM(MetaType, RegS), - /// Verify sum of pedersen commitments from inputs and outputs. + /// Verify sum of inputs and outputs are equal. /// /// The only argument specifies owned state type for the sum operation. If /// this state does not exist, or either inputs or outputs does not have @@ -145,11 +143,10 @@ pub enum ContractOp { /// /// If verification succeeds, doesn't change `st0` value; otherwise sets it /// to `false` and stops execution. - #[display("pcvs {0}")] - Pcvs(AssignmentType), + #[display("svs {0}")] + Svs(AssignmentType), - /// Verifies equivalence of a sum of pedersen commitments for the list of - /// assignment outputs to a value from `a64[0]` register. + /// Verify sum of outputs and value in `a64[0]` register are equal. /// /// The first argument specifies owned state type for the sum operation. If /// this state does not exist, or either inputs or outputs does not have @@ -159,11 +156,10 @@ pub enum ContractOp { /// /// If verification succeeds, doesn't change `st0` value; otherwise sets it /// to `false` and stops execution. - #[display("pcas {0}")] - Pcas(/** owned state type */ AssignmentType), + #[display("sas {0}")] + Sas(/** owned state type */ AssignmentType), - /// Verifies equivalence of a sum of pedersen commitments for the list of - /// inputs to a value from `a64[0]` register. + /// Verify sum of inputs and value in `a64[0]` register are equal. /// /// The first argument specifies owned state type for the sum operation. If /// this state does not exist, or either inputs or outputs does not have @@ -173,8 +169,16 @@ pub enum ContractOp { /// /// If verification succeeds, doesn't change `st0` value; otherwise sets it /// to `false` and stops execution. - #[display("pcps {0}")] - Pcps(/** owned state type */ AssignmentType), + #[display("sps {0}")] + Sps(/** owned state type */ AssignmentType), + + /// Verifies the signature of a transition against the pubkey in the first argument. + /// + /// If the register doesn't contain a valid public key or the operation + /// signature is missing or invalid, sets `st0` to fail state and terminates + /// the program. + #[display("vts {0}")] + Vts(RegS), /// All other future unsupported operations, which must set `st0` to /// `false` and stop the execution. @@ -200,8 +204,11 @@ impl InstructionSet for ContractOp { | ContractOp::CnG(_, _) | ContractOp::CnC(_, _) | ContractOp::LdM(_, _) => bset![], - ContractOp::Pcvs(_) => bset![], - ContractOp::Pcas(_) | ContractOp::Pcps(_) => bset![Reg::A(RegA::A64, Reg32::Reg0)], + ContractOp::Svs(_) => bset![], + ContractOp::Sas(_) | ContractOp::Sps(_) => bset![Reg::A(RegA::A64, Reg32::Reg0)], + + ContractOp::Vts(_) => bset![], + ContractOp::Fail(_, _) => bset![], } } @@ -224,9 +231,10 @@ impl InstructionSet for ContractOp { | ContractOp::LdM(_, reg) => { bset![Reg::S(*reg)] } - ContractOp::Pcvs(_) | ContractOp::Pcas(_) | ContractOp::Pcps(_) => { + ContractOp::Svs(_) | ContractOp::Sas(_) | ContractOp::Sps(_) => { bset![] } + ContractOp::Vts(reg) => bset![Reg::S(*reg)], ContractOp::Fail(_, _) => bset![], } } @@ -243,8 +251,8 @@ impl InstructionSet for ContractOp { | ContractOp::LdG(_, _, _) | ContractOp::LdC(_, _, _) => 8, ContractOp::LdM(_, _) => 6, - ContractOp::Pcvs(_) => 1024, - ContractOp::Pcas(_) | ContractOp::Pcps(_) => 512, + ContractOp::Svs(_) | ContractOp::Sas(_) | ContractOp::Sps(_) => 20, + ContractOp::Vts(_) => 512, ContractOp::Fail(_, _) => u64::MAX, } } @@ -256,32 +264,32 @@ impl InstructionSet for ContractOp { return ExecStep::Stop; }}; } - macro_rules! load_inputs { + macro_rules! load_revealed_inputs { ($state_type:ident) => {{ - let Some(prev_state) = context.op_info.prev_state.get($state_type) else { - fail!() - }; - match prev_state { - TypedAssigns::Fungible(state) => state - .iter() - .map(Assign::to_confidential_state) - .map(|s| s.commitment.into_inner()) - .collect::>(), + match context.op_info.prev_state.get($state_type) { + Some(TypedAssigns::Fungible(state)) => { + let mut values = vec![]; + for assign in state.iter().map(Assign::as_revealed_state) { + values.push(assign.as_inner().as_u64()) + } + values + } + None => vec![], _ => fail!(), } }}; } - macro_rules! load_outputs { + macro_rules! load_revealed_outputs { ($state_type:ident) => {{ - let Some(new_state) = context.op_info.owned_state.get(*$state_type) else { - fail!() - }; - match new_state { - TypedAssigns::Fungible(state) => state - .iter() - .map(Assign::to_confidential_state) - .map(|s| s.commitment.into_inner()) - .collect::>(), + match context.op_info.owned_state().get(*$state_type) { + Some(TypedAssigns::Fungible(state)) => { + let mut values = vec![]; + for assign in state.iter().map(Assign::as_revealed_state) { + values.push(assign.as_inner().as_u64()) + } + values + } + None => vec![], _ => fail!(), } }}; @@ -305,7 +313,7 @@ impl InstructionSet for ContractOp { *reg, context .op_info - .owned_state + .owned_state() .get(*state_type) .map(|a| a.len_u16()), ); @@ -314,7 +322,11 @@ impl InstructionSet for ContractOp { regs.set_n( RegA::A8, *reg, - context.op_info.global.get(state_type).map(|a| a.len_u16()), + context + .op_info + .global() + .get(state_type) + .map(|a| a.len_u16()), ); } ContractOp::CnC(state_type, reg) => { @@ -339,8 +351,8 @@ impl InstructionSet for ContractOp { else { fail!() }; - let state = state.map(|s| s.value.as_inner()); - regs.set_s(*reg, state); + let state = state.as_inner(); + regs.set_s16(*reg, state); } ContractOp::LdS(state_type, reg_32, reg) => { let Some(reg_32) = *regs.get_n(RegA::A16, *reg_32) else { @@ -350,14 +362,14 @@ impl InstructionSet for ContractOp { let Some(Ok(state)) = context .op_info - .owned_state + .owned_state() .get(*state_type) .map(|a| a.into_structured_state_at(index)) else { fail!() }; - let state = state.map(|s| s.value.into_inner()); - regs.set_s(*reg, state); + let state = state.into_inner(); + regs.set_s16(*reg, state); } ContractOp::LdF(state_type, reg_32, reg) => { let Some(reg_32) = *regs.get_n(RegA::A16, *reg_32) else { @@ -367,13 +379,13 @@ impl InstructionSet for ContractOp { let Some(Ok(state)) = context .op_info - .owned_state + .owned_state() .get(*state_type) .map(|a| a.into_fungible_state_at(index)) else { fail!() }; - regs.set_n(RegA::A64, *reg, state.map(|s| s.value.as_u64())); + regs.set_n(RegA::A64, *reg, state.as_inner().as_u64()); } ContractOp::LdG(state_type, reg_8, reg_s) => { let Some(reg_32) = *regs.get_n(RegA::A8, *reg_8) else { @@ -383,13 +395,13 @@ impl InstructionSet for ContractOp { let Some(state) = context .op_info - .global + .global() .get(state_type) .and_then(|a| a.get(index as usize)) else { fail!() }; - regs.set_s(*reg_s, Some(state.as_inner())); + regs.set_s16(*reg_s, state.as_inner()); } ContractOp::LdC(state_type, reg_32, reg_s) => { @@ -407,72 +419,92 @@ impl InstructionSet for ContractOp { let Some(state) = global.nth(index) else { fail!() }; - regs.set_s(*reg_s, Some(state.borrow().as_inner())); + regs.set_s16(*reg_s, state.borrow().as_inner()); } ContractOp::LdM(type_id, reg) => { - let Some(meta) = context.op_info.metadata.get(type_id) else { + let Some(meta) = context.op_info.metadata().get(type_id) else { fail!() }; - regs.set_s(*reg, Some(meta.to_inner())); + regs.set_s16(*reg, meta.to_inner()); } - - ContractOp::Pcvs(state_type) => { - let inputs = load_inputs!(state_type); - let outputs = load_outputs!(state_type); - if !secp256k1_zkp::verify_commitments_sum_to_equal( - secp256k1_zkp::SECP256K1, - &inputs, - &outputs, - ) { + ContractOp::Svs(state_type) => { + let Some(input_amt) = load_revealed_inputs!(state_type) + .iter() + .try_fold(0u64, |acc, &x| acc.checked_add(x)) + else { + fail!() + }; + let Some(output_amt) = load_revealed_outputs!(state_type) + .iter() + .try_fold(0u64, |acc, &x| acc.checked_add(x)) + else { + fail!() + }; + if input_amt != output_amt { fail!() } } - - ContractOp::Pcas(owned_state) => { + ContractOp::Sas(owned_state) => { let Some(sum) = *regs.get_n(RegA::A64, Reg32::Reg0) else { fail!() }; let sum = u64::from(sum); - let Some(tag) = context.asset_tags.get(owned_state) else { + let outputs = load_revealed_outputs!(owned_state); + if outputs.contains(&0) { + fail!() + } + let Some(output_amt) = outputs.iter().try_fold(0u64, |acc, &x| acc.checked_add(x)) + else { fail!() }; - let sum = RevealedValue::with_blinding(sum, BlindingFactor::EMPTY, *tag); - - let inputs = [PedersenCommitment::commit(&sum).into_inner()]; - let outputs = load_outputs!(owned_state); - if !secp256k1_zkp::verify_commitments_sum_to_equal( - secp256k1_zkp::SECP256K1, - &inputs, - &outputs, - ) { + if sum != output_amt { fail!() } } - - ContractOp::Pcps(owned_state) => { + ContractOp::Sps(owned_state) => { let Some(sum) = *regs.get_n(RegA::A64, Reg32::Reg0) else { fail!() }; let sum = u64::from(sum); - let Some(tag) = context.asset_tags.get(owned_state) else { + let Some(input_amt) = load_revealed_inputs!(owned_state) + .iter() + .try_fold(0u64, |acc, &x| acc.checked_add(x)) + else { fail!() }; - let sum = RevealedValue::with_blinding(sum, BlindingFactor::EMPTY, *tag); - let inputs = [PedersenCommitment::commit(&sum).into_inner()]; - let outputs = load_inputs!(owned_state); - - if !secp256k1_zkp::verify_commitments_sum_to_equal( - secp256k1_zkp::SECP256K1, - &inputs, - &outputs, - ) { + if sum != input_amt { fail!() } } + ContractOp::Vts(reg_s) => match context.op_info.op { + OrdOpRef::Genesis(_) => fail!(), + OrdOpRef::Transition(transition, _, _, _) => { + let Some(pubkey) = regs.s16(*reg_s) else { + fail!() + }; + let Ok(pubkey) = PublicKey::from_slice(&pubkey.to_vec()) else { + fail!() + }; + let Some(ref witness) = transition.signature else { + fail!() + }; + let sig_bytes = witness.clone().into_inner().into_inner(); + let Ok(sig) = ecdsa::Signature::from_compact(&sig_bytes) else { + fail!() + }; + + let transition_id = context.op_info.id.into_inner().into_inner(); + let msg = Message::from_digest(transition_id); + + if sig.verify(&msg, &pubkey).is_err() { + fail!() + } + } + }, // All other future unsupported operations, which must set `st0` to `false`. _ => fail!(), } @@ -497,9 +529,11 @@ impl Bytecode for ContractOp { ContractOp::LdC(_, _, _) => INSTR_LDC, ContractOp::LdM(_, _) => INSTR_LDM, - ContractOp::Pcvs(_) => INSTR_PCVS, - ContractOp::Pcas(_) => INSTR_PCAS, - ContractOp::Pcps(_) => INSTR_PCPS, + ContractOp::Svs(_) => INSTR_SVS, + ContractOp::Sas(_) => INSTR_SAS, + ContractOp::Sps(_) => INSTR_SPS, + + ContractOp::Vts(_) => INSTR_VTS, ContractOp::Fail(other, _) => *other, } @@ -559,9 +593,11 @@ impl Bytecode for ContractOp { writer.write_u4(u4::ZERO)?; } - ContractOp::Pcvs(state_type) => writer.write_u16(*state_type)?, - ContractOp::Pcas(owned_type) => writer.write_u16(*owned_type)?, - ContractOp::Pcps(owned_type) => writer.write_u16(*owned_type)?, + ContractOp::Svs(state_type) => writer.write_u16(*state_type)?, + ContractOp::Sas(owned_type) => writer.write_u16(*owned_type)?, + ContractOp::Sps(owned_type) => writer.write_u16(*owned_type)?, + + ContractOp::Vts(reg_s) => writer.write_u4(*reg_s)?, ContractOp::Fail(_, _) => {} } @@ -626,48 +662,13 @@ impl Bytecode for ContractOp { i } - INSTR_PCVS => Self::Pcvs(reader.read_u16()?.into()), - INSTR_PCAS => Self::Pcas(reader.read_u16()?.into()), - INSTR_PCPS => Self::Pcps(reader.read_u16()?.into()), + INSTR_SVS => Self::Svs(reader.read_u16()?.into()), + INSTR_SAS => Self::Sas(reader.read_u16()?.into()), + INSTR_SPS => Self::Sps(reader.read_u16()?.into()), + + INSTR_VTS => Self::Vts(reader.read_u4()?.into()), x => Self::Fail(x, PhantomData), }) } } - -// TODO: Re-enable once we will have a test ContractState object -/* -#[cfg(test)] -mod test { - use aluvm::isa::Instr; - use aluvm::library::Lib; - use amplify::hex::ToHex; - use strict_encoding::StrictSerialize; - - use super::*; - use crate::vm::RgbIsa; - - #[test] - fn encoding() { - let code = - [Instr::ExtensionCodes(RgbIsa::Contract(ContractOp::Pcvs(AssignmentType::from(4000))))]; - let alu_lib = Lib::assemble(&code).unwrap(); - eprintln!("{alu_lib}"); - let alu_id = alu_lib.id(); - - assert_eq!( - alu_id.to_string(), - "alu:zI4PtPCR-Eut023!-Hqblf3X-N2J4GZb-TR2ZEsI-vQfhKOU#ruby-sherman-tonight" - ); - assert_eq!(alu_lib.code.as_ref().to_hex(), "d0a00f"); - assert_eq!( - alu_lib - .to_strict_serialized::<{ usize::MAX }>() - .unwrap() - .to_hex(), - "0303414c55084250444947455354035247420300d0a00f000000" - ); - assert_eq!(alu_lib.disassemble::>>().unwrap(), code); - } -} -*/ diff --git a/src/vm/op_timechain.rs b/src/vm/op_timechain.rs deleted file mode 100644 index d258f29d..00000000 --- a/src/vm/op_timechain.rs +++ /dev/null @@ -1,88 +0,0 @@ -// RGB Core Library: consensus layer for RGB smart contracts. -// -// 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. -// Copyright (C) 2019-2024 Dr Maxim Orlovsky. 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::BTreeSet; -use std::ops::RangeInclusive; - -use aluvm::isa::{Bytecode, BytecodeError, ExecStep, InstructionSet}; -use aluvm::library::{CodeEofError, IsaSeg, LibSite, Read, Write}; -use aluvm::reg::{CoreRegs, Reg}; - -use super::opcodes::{INSTR_TIMECHAIN_FROM, INSTR_TIMECHAIN_TO}; - -// TODO: Implement bitcoin blockchain introspection - -#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)] -#[display(inner)] -#[non_exhaustive] -pub enum TimechainOp { - Fail, -} - -impl InstructionSet for TimechainOp { - type Context<'ctx> = (); - - fn isa_ids() -> IsaSeg { IsaSeg::with("RGB") } - - fn src_regs(&self) -> BTreeSet { bset![] } - - fn dst_regs(&self) -> BTreeSet { bset![] } - - fn complexity(&self) -> u64 { u64::MAX } - - fn exec(&self, regs: &mut CoreRegs, _site: LibSite, _context: &Self::Context<'_>) -> ExecStep { - match self { - TimechainOp::Fail => { - regs.set_failure(); - ExecStep::Stop - } - } - } -} - -impl Bytecode for TimechainOp { - fn instr_range() -> RangeInclusive { INSTR_TIMECHAIN_FROM..=INSTR_TIMECHAIN_TO } - - fn instr_byte(&self) -> u8 { - match self { - TimechainOp::Fail => INSTR_TIMECHAIN_FROM, - } - } - - fn encode_args(&self, _writer: &mut W) -> Result<(), BytecodeError> - where W: Write { - match self { - TimechainOp::Fail => Ok(()), - } - } - - fn decode(reader: &mut R) -> Result - where - Self: Sized, - R: Read, - { - match reader.read_u8()? { - INSTR_TIMECHAIN_FROM..=INSTR_TIMECHAIN_TO => Ok(Self::Fail), - _ => unreachable!(), - } - } -} diff --git a/src/vm/opcodes.rs b/src/vm/opcodes.rs index b1db3ea4..8b189a23 100644 --- a/src/vm/opcodes.rs +++ b/src/vm/opcodes.rs @@ -43,18 +43,11 @@ pub const INSTR_LDC: u8 = 0b11_001_001; pub const INSTR_LDM: u8 = 0b11_001_010; // Reserved 0b11_001_111 -pub const INSTR_PCVS: u8 = 0b11_010_000; -pub const INSTR_PCAS: u8 = 0b11_010_001; -pub const INSTR_PCPS: u8 = 0b11_010_010; -// Reserved 0b11_010_011 -pub const INSTR_CONTRACT_FROM: u8 = 0b11_000_000; -pub const INSTR_CONTRACT_TO: u8 = 0b11_010_011; - -// TIMECHAIN: -pub const INSTR_TIMECHAIN_FROM: u8 = 0b11_011_100; -pub const INSTR_TIMECHAIN_TO: u8 = 0b11_011_111; +pub const INSTR_SVS: u8 = 0b11_010_000; +pub const INSTR_SAS: u8 = 0b11_010_001; +pub const INSTR_SPS: u8 = 0b11_010_010; +pub const INSTR_VTS: u8 = 0b11_010_011; +// Reserved 0b11_010_100 -// Reserved 0b11_011_100 -// Reserved 0b11_011_101 -// Reserved 0b11_011_110 -// Reserved 0b11_011_111 +pub const INSTR_CONTRACT_FROM: u8 = 0b11_000_000; +pub const INSTR_CONTRACT_TO: u8 = 0b11_010_100; diff --git a/stl/AnchoredBundle.vesper b/stl/AnchoredBundle.vesper deleted file mode 100644 index 70188880..00000000 --- a/stl/AnchoredBundle.vesper +++ /dev/null @@ -1,279 +0,0 @@ -{- - Description: RGB Anchored Bundles - Author: Dr Maxim Orlovsky - Copyright (C) 2024 LNP/BP Standards Association. All rights reserved. - License: Apache-2.0 --} - -Bundles vesper lexicon=types+commitments - -BundleId commitment hasher=SHA256 tagged=urn:lnp-bp:rgb:bundle#2024-02-03 - Method serialized - InputMap serialized - -DbcProof union - tapret rec TapretProof wrapped tag=0 - pathProof rec TapretPathProof - some union TapretNodePartner option wrapped tag=1 - rightBranch rec TapretRightBranch wrapped tag=2 - nonce is U8 - opret is Unit wrapped aka=OpretProof tag=1 - -TransitionBundle rec - 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 - diff --git a/stl/RGBCommit@0.1.0.sta b/stl/RGBCommit@0.1.0.sta index bddb58fd..44614e51 100644 --- a/stl/RGBCommit@0.1.0.sta +++ b/stl/RGBCommit@0.1.0.sta @@ -1,270 +1,159 @@ -----BEGIN STRICT TYPE LIB----- -Id: stl:ZMTVCU25-QDo98xR-wI91wcu-ydb7kui-QfZbF$n-0KDS2ow#tuna-safari-design +Id: stl:XbiECcs9-xlyofco-wkXoupT-gJ61JJf-XWL0DWf-INKzIp0#support-iris-depend Name: RGBCommit Dependencies: - StrictTypes#century-comrade-chess, - AluVM#congo-archive-folio, - CommitVerify#miller-pancake-elastic, - BPCore#totem-holiday-helena, - Std#ralph-blue-lucky, + BPCore#juliet-super-dominic, + CommitVerify#violet-panther-herbert, + Std#delete-roman-hair, + AluVM#jargon-gorilla-poetic, + StrictTypes#henry-heart-survive, Bitcoin#signal-color-cipher -Check-SHA256: bca1c6f36dad26f2d0a6f0c23ed8bd7a3c2584bf1f60a4f1ded87de9f8fafa87 +Check-SHA256: d929bd2f8100135aeea673c5949e0a762b52943dd8ebe2c0156e02c453d4294f -2~tNwLvL+uX>Z4!V_T!KNI`QJ|h6;Zj^jB$MPK+?7Lu3>C`4HI)Q*?4^V{}w`aAk91a5aA+<>R2X -hQO_4{AcS-HH^7AVzASV8M4NYxyCjHL2PwaO?m?z-)Viz@~C%8KNS}Z0aQ3s^SOqbBwN-D{wl@OCJaMw -ZEb0ER%LQ&W_hMrvQRIBF~gy)hQg^4yxce6i-HaxmCGKABZpBR?$8E8P(yEWWy&lbZ-bfLFbqC#o>4E? -M+l67UG^w8*<_XZ#%uyqCj(P-Wc6$lVk7oBr%DNv+($;q`HHK!gIHa)*%m(-e#9sm3I{@IbYpL6ZUzNG +2~tNwLvL+uX>pKz(VCaX%3mIP&XB&sB3y@ML2MxM-cip;h4&;#rLQq3*a%D_q7H}ii_tAk8EiIL^ +F;WduOYhK!;vHZzZO$@xxWWuWZ*6U9bXH|@X=ZtXiR(=d3vg7gbV~*3!PlK51EySK%g?1}nECovJTYnm +Q*>mTDm~KXV7O_2K9yF<>+h;;t~i_0*|LWuHI04?{jxCyL2PwaP3fMJ=Q}``f02HLt~iCiD@{1Jw0_*8 +A_pi$)ov?1PzzIZa%p39RC#b^bMaU=OZ$bvG|>z)+>9PT;Au-7)~D;-++hbyX<}1pbY-V7RRS&fT*&Z=qeY@Wmfle* -z!SF)@h8|JkU^FEQwjx4X<|ua20~CnZ*pY?04}!>CAn^87TS9h9ibhaZ&^Bcn*B*;w|~I;-PD|t>jq6_ -bZBp6MklB)P_)|`Y=H7dO_e!^G2i>0SdC0NppV!6v|_i_4nk~cZe&wsVQf@*P;_!=8SA{&vly$FvzVnz -Hf7z~rv`86=_Ka^V5yX|y#`JSQ)OdvWpq?<6X>I}lA>%$n -#j0HLDJN5-IKgM_J7b(p+0MPGk2Gl)y2(Rz1Xgc#bfbbo^UK%K(4i9Ajp1M~R@C@!4#dQE#lUD;OiKi1 -Rs>XdX=DsTZ*6U9bXH|@X=Zr^063mQh9?yTI7S;;e;>sZfv!yd427@;7veO2zMB=|GYU;*a%*g5NMUnm -ZLh7x^`{^P$fKg#%8c8X#<$(NgM!uni2C|Kr}onZ3R84)X=8LqVRLAc_h5K%L=laq&yA1JoJ^{7>oKLk -F4~iax8KK|47hp@Qe|^xa&~28LV0v$b1}=fEj#9D^K)f#Cf|Xn@L3mU0Z2&n-dr?jcD1Ll0RawDWpib6 -c4cHjd30rSGX8=VN#A(BKKz&v`r;e6DUv<<*U}cmb;*F>vukd; -=m`ygb@x#_>`RmOO$0)3Z)}yry~#}iVEJ)s5j^%uEnQ9{n2s|9Fa^ps+HG#`XS5DMY;b5{PIYZeZ)9O} -Xt{%a=RmHK6WZ%EWRm@*ULd%lgGoFTxUTU -76^nC$%1sKzB<;EQA|)S-x88IWKN#S$#@T&w`gPtRC#b^PGN0jYXqYdo~D%m7H6OD0<^0n_2##VWXRdj -y=DB@qgYOj1yf~hNn}O<2|;XhOksItaxnt|25f0@b!lV)3_)ykOksItaxqh7bOiwb2?5A!f_n>Eea4Xl -By!~v3=AX`3Ri0000000000{{R3000000B0+O=X=iRyWp-s@Y-MCYbaY{3XhLjhZe&wsVQf@*P;_zx1ONKtpQ8M_qJyiO1VIUJ=H=)@ii_1#@&^bY%hjG)3KC -&kYOztQDksx&yAAHY;R+00(t`--)Viz@~C%8KNS}Z0aQ3s^SOqbBwN-D{wl@O -Cd;@jJLYKfb7gWS-+`&{Sr=ykNJ#YFTr_BQwWqKF0Ra(XZ*FF3WMyu2X<=+rbYXO500sjDb7f&{0gt=F -=tr7PJW4?*h+3X!X@fjr@d0IimUQ-Q1Z;0(YXW)$9p7nv%krpqNJW4?*h+3X! -X@fjr@d0IimUQ-Q1Z;0(YXW)$9p7nv%krpqNc;WdH^P1aoC!YypA{4XEgn0epQk*PX+nL>xOm%pK>soMo}bYXO50sJ&Y-CxfQ3;(PYq10COKearHwcS=7M7YzYaI8*bvhMOc?)(rkC#nUFsxGg*8X!CPrawgw_sqk4BX8}k^^xj-FXm+)y -umJ%A5Mys{W@%()Zggp3Y*S@nYybuW1aoC!YysPfS{i~B5OpZ>_>4e9YQ#rfba;u!+d5tm#=h2RwFCuo -bYXO50p}%){+CCYqok=CI6O*0D2Q5~XK8~xVetWFewK9hZ3Jv@V`~C>10COKearHwcS=7M7YzYaI8*bv -hMOc?)(rkC#nUFsxGg*8X!CPrawgw_sqk4BX8}k^^xj-FXm+)yumJ%B2y$h1WnpY(WB>*O1aoC!YypA{ -4XEgn0epQk*PX+nL>xOm%pK>soMo}bYXO50p}%){+CCYqok=CI6O*0D2Q5~XK8~xVetWF -ewK9hZ3Jv@V`~C>10COKearHwcS=7M7YzYaI8*bvhMOc?)(rkC#nUFsxGg*8X!CPrawgw_sqk4BX8}k^ -^xj-FXm+)yumJ%eL349yXKqquc4c8~Wn@HQbYVhlX>MdwWnpYocu;h51OxyKV{dL|X=G(?bZKF100sjD -b7f&{0o#gN8iEuMbtv-qj6g$b#7A9pc!|f`I$jaRzSe2A1O;<+VRU5ya1CV;vVwtcAGbZ_5@VACR|ut2 -VXXq-)V^B9&!_4M1Z;0(YXW)$9p7nv%krpqNc;WdH^P1aoC!YypqE!sthuPUKDEU2%WC`V+X+ -(UG)mk--2W1{>juaWw^VbYXO50dNgv5VC@SZy&ck10COK -earHwcS=7M7YzYaI8*bvhMOc?)(rkC#nUFsxGg*8X!CPrawgw_sqk4BX8}k^^xj-FXm+)yumJ%A5Mys{ -W@%()Zggp3Y*S@nYybuW1aoC!YysPfS{i~B5OpZ>_>4e9YQ#rfba;u!+d5tm#=h2RwFCuobYXO50sm-Y -z<5%CY59k^g5#W{6D&GDo53%OaP0&iRq10COKearHwcS=7M7YzYaI8*bvhMOc?)(rkC -#nUFsxGg*8X!CPrawgw_sqk4BX8}k^^xj-FXm+)yumJ%B2y$h1WnpY(WB>*O1aoC!YypqE!sthuPUKDE -U2%WC`V+X+(UG)mk--2W1{>juaWw^VbYXO50sm-Yz<5%CY59k^g5#W{6D&GDo53%OaP0&iRq10COKearHwcS=7M7YzYaI8*bvhMOc?)(rkC#nUFsxGg*8X!CPrawgw_sqk4BX8}k^^xj-FXm+)y -umJ%dL349yXKqquc4c8~Wn@HQbYVhlX>MdwWnpYocxhw=1ONKtpQ8M_qJyiO1VIUJ=H=)@ii_1#@&^bY%f>4P_9rf`M-zw>{+&W0M0{2&GbCtpecGzFNi4 -r|Jm=Y;R+00(t`--)Viz@~C%8KNS}Z0aQ3s^SOqbBwN-D{wl@OCd;@jJLYKfb7gWS-+`&{Sr=ykNJ#YF -Tr_BQwWqKF0Ra(XZ*FF3WMyu2X<=+rbYXO500sjDb7f&{0fGz-uWS7@0e2{fI -fIMdwWnpYocu;h51OxyKV{dL|X=G(?bZKF100sjDb7f&{0o#gN8iEuMbtv-q -j6g$b#7A9pc!|f`I$jaRzSe2A1O;<+VRU5yN@#iqkT|?l*=bx{^0c**fmF&H)l(b`S3$sb4!MK-1Z;0( -YXW)$9p7nv%krpqNc;WdH^P1aoC!YypqE!sthuPUKDEU2%WC`V+X+(UG)mk--2W1{>juaWw^V -bYXO50ZM3k2aq_tRM}}10COKearHwcS=7M7YzYaI8*bv -hMOc?)(rkC#nUFsxGg*8X!CPrawgw_sqk4BX8}k^^xj-FXm+)yumJ%A5Mys{W@%()Zggp3Y*S@nYybuW -1aoC!YysPfS{i~B5OpZ>_>4e9YQ#rfba;u!+d5tm#=h2RwFCuobYXO50c}La^e<`!Iztr?rsl#d#OQkE -ER^^L)C{HEhxT=ipag7hV`~C>10COKearHwcS=7M7YzYaI8*bvhMOc?)(rkC#nUFsxGg*8X!CPrawgw_ -sqk4BX8}k^^xj-FXm+)yumJ%B2y$h1WnpY(WB>*O1aoC!YypqE!sthuPUKDEU2%WC`V+X+(UG)mk--2W -1{>juaWw^VbYXO50c}La^e<`!Iztr?rsl#d#OQkEER^^L)C{HEhxT=ipag7hV`~C>10COKearHwcS=7M -7YzYaI8*bvhMOc?)(rkC#nUFsxGg*8X!CPrawgw_sqk4BX8}k^^xj-FXm+)yumJ%eL349yXKqquc4c8~ -Wn@-iY;|QqY-w&}Q)OXnRCsA*1OxyKV{dL|X=G(?bZKF100sjDb7f&{0o#gN8iEuMbtv-qj6g$b#7A9p -c!|f`I$jaRzSe2A1O;<+VRU5yN@#iqkT|?l*=bx{^0c**fmF&H)l(b`S3$sb4!MK-1Z;0(YXW)$9p7nv -%krpqNc;WdH^P1aoC!YypA{4XEgn0epQk*PX+nL>xOm%pK>soMo}bYXO50ZM3k -2aq_tRM}}10COKearHwcS=7M7YzYaI8*bvhMOc?)(rkC -#nUFsxGg*8X!CPrawgw_sqk4BX8}k^^xj-FXm+)yumJ%A5Mys{W@%()Zggp3Y*S@nYybuW1aoC!YysPf -S{i~B5OpZ>_>4e9YQ#rfba;u!+d5tm#=h2RwFCuobYXO50c}La^e<`!Iztr?rsl#d#OQkEER^^L)C{HE -hxT=ipag7hV`~C>10COKearHwcS=7M7YzYaI8*bvhMOc?)(rkC#nUFsxGg*8X!CPrawgw_sqk4BX8}k^ -^xj-FXm+)yumJ%B2y$h1WnpY(WB>*O1aoC!YypA{4XEgn0epQk*PX+nL>xOm%pK>soMo} -bYXO50c}La^e<`!Iztr?rsl#d#OQkEER^^L)C{HEhxT=ipag7hV`~C>10COKearHwcS=7M7YzYaI8*bv -hMOc?)(rkC#nUFsxGg*8X!CPrawgw_sqk4BX8}k^^xj-FXm+)yumJ%bL349yXKq$+X=GD$VRU6eY-w&} -Q)OXnRCrKyas&hb3}bI@W@%()Zggp3YybuW1aoC!YysPfS{i~B5OpZ>_>4e9YQ#rfba;u!+d5tm#=h2R -wFCuobYXO50WPwo{ujV7L@=1(T$>wOY}Ov_b`4?P%YY`+Wb+o`y98`+V`~C>10COKearHwcS=7M7YzYa -I8*bvhMOc?)(rkC#nUFsxGg*8X!CPrawgw_sqk4BX8}k^^xj-FXm+)yumJ%95o2#|W@%()Zggp3Y*Tb$ -bY%br0|awrVQc}9yTa&4noi_R;$3lnz4{Zl)X|Z&ZIQtMA_g1big7gsb97;JWdSa-rT!PdFhnqz;9Q#< -T5Q%H?RE`e-pha{(`54&;kyKEZ)0l$dIKHbX?@G`sCP;~6&DQwR5(-fxrUo0Th_>4e9YQ#rfba;u!+d5tm#=h2RwFCuobYXO50WPwo -{ujV7L@=1(T$>wOY}Ov_b`4?P%YY`+Wb+o`y98`+V`~C>10COKearHwcS=7M7YzYaI8*bvhMOc?)(rkC -#nUFsxGg*8X!CPrawgw_sqk4BX8}k^^xj-FXm+)yumJ%95o2#|W@%()Zggp3Y*Tb$bY%br0|awrVQc|{ -3=OYq{WJl0D5$WZob97;JWdSa-rT!PdFhnqz;9Q#WZob97;JWdSa-rT!PdFhnqz;9Q#<~nFGwl#> -J&iiT&+eT*0000000000{{R30000007(sJ$X=iS2Wo~qHLTqVnWK(5fY*ct@WCZ~L3IT`y;$>KfZ0H=m -hJ>?uVc;Wd;HQX=DL}aSf9!PV~dK2uo>;u!nFdemP_$ -e?^hl+JkM;eY!XaZDnL>VN`i=WdTAkVTFju)T~Wpi|4 -ZEyepNCs(hb9H5M0(t`--)Viz@~C%8KNS}Z0aQ3s^SOqbBwN-D{wl@OCT*{+#r3Bk2FRnL+RBXEn8vr= -x`Tq%|A_kfK&ST81_yLyb98QHbOOpO9&dx0-7pM3Z=O*v*GCA9fL-<|HrZsA`NnJlR3~AEBGG%U@MZ$v -=XJ?|;InIPy66cFfOYp#JM2r7_Du?5Y;;Uvd1Z2QF#>u69p7nv%krpqNMd`Zf8beV{~tF1pxpD -002NB01rcNZewL(Y-MCYbaY{3XaxZP2LJ#-AOHD -0Z6?XZWsH8I~II?C0;dW+k!*yDqgzlqQwf$39g<|8VW;iZgg^CV{}Pm1pxpD002NB00~54bYW9;VRU5$ -0RRX906+i$000000096000000000R^cywiMb7^mG1`7jbW_AJEn^6;37FKqUhx?i3R+Mr!fY&(;2BFL( -m@EZk_srD_V{dMBa$#e1Nn`<^2rNlD$O59e#ogQsB77jPl+h5j^*S;IZf|a5WdHyO4P|(A -Wo~n6Z*Ek1aAg5xbsj>g6`?#s5rWnKhSeO?L~x^!;Y#eFP|P}0Z%EzBpbEf7FA*KKfDWJ -b8~5DZf#|5baMe=>KFFSbgda38zdDXQ0-0pHK5k@bh=O+>c= -6oKLk -F4~iax8KK|47hp+cWHEPWpi@^dIKHbX?@G`sCP;~6&DQwR5(-fxrUo0Thxis%Ku=2wF+7z(Wqt=tdZk`V^s(Ana000000093000000000Ma -Wn^V#ZF2w#0Y>fS!w4MxxaL=+DqP^k2!wz9AHH68xp8!<%Jqp^&Hw-a000000RI300000001IJrb7^O8 -ZDnqBa{vkfhyLPaScq)s9KMExvw34D6J>+NwrBxfixd_%u|$Wt0XD%jq57bK6Q|uUfIMEX^1}Vv6tLB! -)|10-o)0prc>n+a000000RI3000000010+sY-Mg^X=QT&2?0j!=EDda{kY~=q$*tC#t4Le{2#tvcDZqM -smk?aU=OZ$bvG|>z)+>9PT;Au-7)~D;-++htxcywiMb7^mG -RC#b^1pxp60s}^7b_D?d00Iq0b#7;AVr*qobYXO51OW&JVrg`9HZ%YQ0RR993`TWsXK7+=WmI`^Wdi{X -b#8NMXKrO=HZ($MbO;AWWo~72X>$e*17>D+0ot2U6Id2jc94hrndMfLayEe1ISdA&%p{mB1!VWk)d+KA -Xk~3-Nn`<(Qq$W5tE;F{pQrXd&=l*`O?@#x{Qdy?T_k!`1dtE~W^7?+a{_t;9p7nv%krpqNDb5bYX39002k^X>)UR -WpV+w=zxYCD0L!x4tB5Hm3vFbl?lapNXe%XU~*fKJ0+Y4bY*jNZe?@=$}AplgPGkh3_fq3Q7_j=2#kPT -_9!;lWR>~GYywm#VTK~nd#>BpbEf7FA*KKfDWJb8~5DZf#|5baMe=>KFFSbgda38zdDXQa{=9jW&m$tWDykZj`7#3_zANbB(SO{shhGe=&H{tM@n+a000000RI300000001IJrb7^O8ZDnqBa{vkfhyLPaScq)s9KMExvw34D -6J>+NwrBxfixd_%u|$Wt0XD%jq57bK6Q|uUfIMEX^1}Vv6tLB!)|10-o)0prc>n+a000000RI3000000 -010+sY-Mg^X=QT&2?0j!=EDda{kY~=q$*tC#t4Le{2#tvcDZqMsmk?aU=OZ$bvG|>z)+>9PT;Au-7)~D;-++hnxY;R&=Y*Tb$bY%qr015%s?vf5kh_h+&YE#h%O8d1V -_{UOl9{V;uR#^q%$8ES^HwOl>wiJc;WmI`^ -Wd#8M00In0Y;R&=Y*t}xb!Bq}0RRXAGM-jZ2Kh}DE2o;HYydTtf}Q!WH{}bI!u)W*#(e~Z0RR9100000 -|Nj60000002uWmRZggpMc?AIg1p)%fEFN!zncXl9K5w2;FV{y1jDTJCC^p$-mHEbO0#qkRz9SbZ=!8X@ -=Yuq$20sb<4l#S`iz7Vef}@Ca=a#qt2m;D19&dx0-7pM3Z=O*v*GCA9fL-<|HrZsA`NnJlR3}KjBNr;@ -ghiU?gEXK9KMDE{F?;HZBRuDVqlk6qmbd@_000000093F00000000F^Zg6#U1_B3ga%FZ;b#wuf5WIk~ -G+K)5%bR49t5yk`^qQ9la%FR6a&~280(t`--)Viz@~C%8KNS}Z0aQ3s^SOqbBwN-D -{wl@OCd;@jJLYKfb7gWS-+`&{Sr=ykNJ#YFTr_BQwWqKF0SHNMaCLM|VQ>Wj015*2Y!hN5_Bp3Y36tDM -M#=e#tGI($UA5U3KNx<*C>jbO<32;hs$B9ZCsU(1!DsC|W1LOd&b_IRG-(&Q$wPGkmB{9L9(7`0)Rt93 -YLV-HLXe?vTA1;^Q1`ZqBog<<0RR9100000|Nj600000021#ykb#!wD0RRaB)s0^W44ZlVPs)+VFdvI3ITQGP59r=ivkR6Lh9EroO>_;0000000030000000000B -Ph(?sa&l#EV`Xy&0t0PnZU6uR18re=0006EPjEwTZEb0EZDnqB1`7jbW_AJEn^6;37FKqUhx?i3R+Mr! -fY&(;2BFL(m@EZk_srD=Zf|a5WdHyO25)dwd2nR`=kby$tK%HuPpRtMKe5+wDRP}k(QuARKUbDjTz^bE -2yJC_VPs)+VFG#s9p7nv%krpqNM?JbaMiF10COKearHwcS=7M7YzYaI8*bvhMOc?)(rkC#nUD@oClv)aMjKgwAH@`bu1x<7g|G$};xvA~n-$_S2y$g)Wo2z;WCD5v9p7nv%krpqNv2!%ioJ -pYH;+t0eX2w~A!Q+0eaZ{MVycPK^T!VRUq1V`yzR000000RI3000000 -01i@Rc4c8~Wn@8gbYWv?1_A_TX>4Ty3ATR{-|K6Y3I$*BbiAvUS*tg{!Gb}P!O*^_P#qhP1ao0*bN~Pd -3{quwWnpY(WJFD+0ot2U6Id2jc94hrndMfLayEe1ISdA&%p{mB1!VWk)dgm3VP|s!dIKHb -X?@G`sCP;~6&DQwR5(-fxrUo0ThOi(W05|TJ%PM*ricn_Pm -Xk-a=X>Db5bYX39002k{WMy_`Y;SO7asjsJfQB3>bs~EXcCXx(drQcb3B`Fx$)^%va$Ar)C7cOuWprUw -d2nTO015$hC`}q*rYXqYdo~D%m7H6OD0<^0n_2##VWXRdjy=DB@qgYOj0000000000{{R30000003ukO^Vqt7l -d2nTO015%s?vf5kh_h+&YE#h%O8d1V_{UOl9{V;uR#^q%KfZ0H=mhJ>?uV9R0ZFSEMRj;Km4qfBYZ5UUs>0bg9bqiCNA700000000300000000007XJu|>b7^w{ -7)aIA#9XnshcC})U)TI#r3b0kyqD7}ejM+$yUGm(3T1e7Wo~n6Z*Fq{3ISww9zv-Vp*%wog4O?q)g04A -aHEjnO6;Ie%sNwVNZtVhXadZr-SPY!h`6Z9%SYt5l1;p3@00000000300000000008a%FR6a&~280(t`--)Viz@~C%8KNS}Z0aQ3s^SOqb -BwN-D{wl@OCjNpJN#A(BKKz&v`r;e6DUv<<*U}cD+0ot2U6Id2jc94hrndMfLayEe1ISdA&%p{mB1!VWk)e2*8Zgg^CV{}Pm0iOsgNjk^^ -qPoT1+zTRnAg`3vXv9d*8d@RXy~6c6G6imLZewKt00<6ra$#@6CZd7@2WdSr&53UoI8eY9A{1GER -g--GiI0S#x1is&)M%fmnGH3{GWprU=VRT^u^?FS>S$_F2)vN@Mb6UJ-F(lri_dqerx4lR4>iBsz2WM<= -Vqt7^0p25#Yo@G%*b#-tU^&3KX?w7l?~*Sh8@1jRRblZzyas7*aCLNZ0jZ*TSChz_$|Xx}eRkFNAr%^e -Ll(1e@}~9=0-ijXfD2)Bb7^O8ZDnqBa{)s3lIz?v1U>x&T2C;PAKlCCveQ{N4udSh#@3Dqj&%ukVQgh? -V`*h`0o{dW0B>Pr5ftu@@z<*O39}j`u&O7io3b$Is?RA$O$l~kY-wa+bZ>G3dIKHbX?@G`sCP;~6&DQw -R5(-fxrUo0Th*%N~j&hfyEy&@Q(SCAn^8 -7TS9h9ibhaZ&^Bcn*B*;w|~I;-PD|t>j-IXaCLM|VQ>KznP+6nwW~k}RP!NmuV?G015$>$mV(;bz)!CmQ_M(k?Vd!kfCo{nDM?)_qK{868FUc))-~W22H5+ -SDrhMohTRsfLT>C1dS{r3#^{o?N>fE0RR9100000|Nj60000005L9wuZgXjLX>V>*V`yb04xKINJ~fc1UzTDb_LBRjrSXj?;k=^zm6d~Uh7!wPz^$C +X>MdwWnpYocxhy48SA{&vly$FvzVnzHf7z~rv`86=_Ka^V5yX|y#`JSQ)OdvWpqi7rMzqbp%##b#$YGLi5Yl +(a@n1+Ku60FILp}Zw|!7cE!MGSxid=WmW`Kcxhw|LvL+uX>?X)a%pCH0suIkLxv|61vo|1ioJpYH;+t0eX2w~A!Q ++0eaZ{MVycPK^aqWo=1hIRGF*b8~5DZc=4-WnpY(WJFcz4~pa1P{=r>OF|0}@Y#k~NrxRcQq+F$P2q2)nlU +E64L7%3W7R1#@&^bY%e`@=#VHk7myNk^1UqykExfX%7&o7^|1Fn59cLW!>7R25;!; +BSw%P#_(xeJx6(SSwF1NH_CEIWgkIvb7^O8Qe}2!VQgh& +L}7GcLTqVnWK(5fY*ct@WCQ{L2y$h1WnpY(WB>*N1aoC!Yyv&&I}!?D=!eA%8DA7<8;7e4kWwcH4Z3)D +-MVlN`(=BbYXO50U+{FRw9pP&is-3>Sw%P#_(xe +Jx6(SSwF1NH_CEIWdRUlZ*FF3WMyu2X<=+rWnpXp1_A_gWnpXrJ?lFX3Sj7m#S0l<6lWWUs|%1)CkG9> +cz4~pa1P{=Xc_Cg)w39@m$R6qOEzWQ+NTC@=;*N +1aoC!Yyv&&I}!?D=!eA%8DA7<8;7e4kWwcH4Z3)D-MVlNcz4~pa1P{=Xc_Cg)w39@m$R6qOEzWQ+NTC@=;hpP*aQYQxux_Ecp +x^NEUkt_p^NJ~fc1UzTDb_LBRjrSXj?;k=^zm6d~Uh7!wPz7^zVRU5y;>Mp{wIfQLx1JC|lRtv}2|vJ# +Rj;5&YI0c6F%?4p0T5$vZf0p@Wo~q7VQf=nVQc^f0t9nqVQd0D>pKz(VCaX%3mIP&XB&sB3y@ML2MxM- +cip;h4&;$&8SA{&vly$FvzVnzHf7z~rv`86=_Ka^V5yX|y#`JNb97;JWdY*GpIx;hN}RWz5J8hag8d0U +z>8I{phs$QSkN&QLjNg2b8~5DZdPSuL349yXKqquc4c8~Wn@HQbYVhlX>MdwWnpYocu;h51pxpE0SrRj +skq)xpV`Gw<@$3$0NV?f{JPT8u3(v#XUGyU%mDxZ000000RR600000004PCob7^O8R%K&Bb8~5DZc=4- +WnpY(WJFIGsVJuTN&(i?_00000 +0096000000001UIb8~5DZdPSuL349yXKq$+X=GD$VRU6eY-w&}Q)OXnRCrKyas>eZ2muyOO0=mQ@c;WkPIeZe&wsVQf@*P;_zx0sshdWp-s@Y-MBs1_A_gWnpXrJ?lFX3Sj7m#S0l< +6lWWUs|%1)CkG9>cz4~pa1P{=r>OF|0}@Y#k~NrxRcQq+F$P2q2)nlUE64L7%3W7R1#@&^bY%f9vZekP +z%WEGnBZKS8(M7E9_@AwVcyGtCevi|7U8=A5Mys{W@%()Zggp3Y*S@nYybuV1aoC!Yyv&&I}!?D=!eA% +8DA7<8;7e4kWwcH4Z3)D-MVlN%7&o7^|1Fn59cLW!>7R25;!;BwOY}Ov_b`4?P%YY`+Wb+o`yBtAtb7^O8R&Qx!Q*>c;WkPIeZe&wsVQf@*X=DTf00?qr +c4c8~Wn=&b0t9nqVQd0D>pKz(VCaX%3mIP&XB&sB3y@ML2MxM-cip;h4&;$61C2;aNA(0eXS#L;%_oib +8;tKCLQ=nuAv#{`SnN;*b97;JWdSa-rT!PdFhnqz;9Q#hpP*aQYQxux_Ecpx^NEUk!Ts~yw$T9tCzEwrAszt +-P)%HZ|LbH=L2A=l(W4CP6cyxVRU5yF0!Tm7r-z?Fqq(6n;Tke)*kJ44PoBPfF{#q^A_Q|5kYfvX=iS2 +Wo~ptWprU_Y;y(!5N~&GWn@!yVRU6vV`yb7J73J3yCzk$#1)IEB9}O*pr-e%zuW2Pj065I6*X)C9iYp+?yjr7~y^L349yXKrm} +Zgf<6aAgGn0006RL349yXKrm}Zgg`(Y-w&}Q)OXnRCrKyas>eZ3IT`y;$>KfZ0H=mhJ>?uV_ +ZDC1d0hChH+XJhss8OG%_CC-Q>(otsF+cqN0Qy}ddQ=3E5D9c?ZDn(GVQp{#07wRDb8~fNaso_c7H}ii +_tAk8EiIL^F;WduOYhK!;vHZzZO$@xxWa9(t;O}HAO^^zqT0%g+nC0;-MWK<)&Ge4`aq}l(*_7*Xklq? +PGxiftsXy=AYX8dDQEQRO5D(yMKm~r|3!X*hx(?lDDx}U6LV!@Y(s2sb7^j8Q*?4+bY*9G0h%q;z`x)s +Sf3Z(2eI_JHZ((SFcY?<6X>Ltn +X>M+1bN~-xX>?<6X>L?yb98QHbTa`DVrg_^Z)t8+Wpi|HWpp$G4Pt3@V{d70Q)y>zWpo1$Vrg_^Z)t8) +WoL9{b94j^Y-w?IX=F`dX>M+1bOjA;X>oOFWK?BybZ%vI1`0!OZgg^CV{}Pm1pxpD002NB00Ty5b_D?d +00Iq0b#7;AVr*qobYXO51OW&JVrg`9HZ%YQ0RR993`TWsXK7+=WmI`^Wdi{Xb#8NMXKrO=HZ($MbO;AW +Wo~72X>$e%17>D+0ot2U6Id2jc94hrndMfLayEe1ISdA&%p{mB1!VWk)d+KAXk~3-Nn`<(Qq$W5tE;F{ +pQrXd&=l*`O?@#x{Qdy?T_k!`1dtF3bZKp6b97;CZ~y>E25ED1b!Bn^w&;L{94K`ndk%K5+?9Jv$dw7j +c}U5p5@2#$kUJ%u2xDkrX>LwsbOEg%Ka?O}aEvKu^y*67(3wRvID`L1eu0Pjrm!gUE7lWpWnpYXY;SXE +Zf8?;a$$63XL$jdE!4oj;3`<37v2Z4^tv`QLvAn=wSY4a%d)dfQpu?ZZDn*}WMOn+0rh%KI9Y!AFx9LC +k8@hQXE7w+qW3^C%eTEp@#^?_H3w&GZ(?C=a{;0=$>=Cf%ypRj207<6(8~ghODx=kkv^H(szyE})=~>$ +b8~5DZf#|5baMgBJ0NihtH%g9L?J?oYV5-8A$VUWu}%GxXkH{w0y&TkM`dnhb7^x^V`ybM?JbaMa-0f+wLWmt%8=p4R=gtK{LClh6Z#kObxUW*hKHnBv9xdAr8 -G@<&SffJ|QFn~N>u=2wF+7z(Wqt=tdZk`V^s(Ana000000093000000000YNb8~5DZf#|5baMa-0f+wL -Wmt%8=p4R=gtK{LClh6Z#kObxUW*hKHnBv9xdAr8G@<&SffJ|QFn~N>u=2wF+7z(Wqt=tdZk`V^s(Ana -000000093000000000SgVQgh?V`*h`00{v`?dHP>9R0ZFSEMRj;Km4qfBYZ5UUs>0bg9bqiCNA700000 -000300000000009c42I3WMOn~asUJZ00eGtZe;)f009JZZ*64&1pxwLa5aA+<>R2XhQO_4{AcS-HH^7A -VzASV8M4NYxyCka@1Z8)ymjIKNK5;L!8FkfGTe+FK;UUh9M-4n+}vRfRB~Z%b7^#GZ*Ek1aAgGn0006G -RC#b^LvL+uX>@I6Zgd0#00(DfZe??6a{vVa0W)M-Q2pM493%15wcJ8Z{z5k9VD)f0JnAj^7J5MZ9{~z< -a$#@6CZU6-W0iOsgNjk^^qPoT1+zTRnAg`3vXv9d*8d@RXy~6c6G66JF53UoI8eY9A{1GERg--Gi -I0S#x1is&)M%fmnGH3z`Wq5RDZgXjGZU6-W0iOsgNjk^^qPoT1+zTRnAg`3vXv9d*8d@RXy~6c6G67_D -9zv-Vp*%wog4O?q)g04AaHEjnO6;Ie%sNwVNZuM$d2nT9L349yXKr&sY-w&}Q)OXnRCrKyas&hb3uI+u -Y+-U?bZK^F00jX62mv`K^WREqS2tt~EBII@xVqZNcP`ond^UU-JbUWd$~FK100000009600000000039 -W_507X<}?;00jX62m#u~=^e=I{=p`1zMng|0+NmwUpUW`Z@54^_oW>WVpRYD0000000960000000006C -b98cbV{~&L00;qEk8=qnO(R<<%JIK< -1B78x*e6}1oxDzJ3ElvocGBqp0000000030{{R30000303So3~VPj}*Wo~o;1pxpE0aMUzRzj^*Tk1C) -pGbjYG7000000RR600000000~xMY-Mg^X=QT-0RRaBM(yUq2ps*m=2xUD -T;RqCgn#@WzFu~@adfH5^@&-|0000000000{{R30000003szxlWo~16RC#b^1pxp60tr@cX=GD$VRU5$ -0RR916j(!OVQFqcY-w&}Q)OXnRCrKyas&bZ2V!Y-V{d7000jX8rdhI3FM~0|p{<6(sS&)~H{Xkb4j+}v -9*QG}Q6KKmMklB)P_)|`Y=H7dO_e!^G2i>0SdC0NppV!6v|_i_0S0Voadl~A00jX8rdhI3FM~0|p{<6( -sS&)~H{Xkb4j+}v9*QG}Q6KKmMklB)P_)|`Y=H7dO_e!^G2i>0SdC0NppV!6v|_i_6IerNVQFqcY-w&} -Q)OXnRCsA*1OfmDVrg_^Z)t7-1pxx4S+Y5xm?t-;06{AC=10daZB@{PThHqO25f0@b!lV(1pxx4S+Y5xm?t-;06{AC=10daZB@{PThHqdSVL%GX>L5xm?t-;06{AC=15xm?t-;06{AC=1L?_X=DTf00&}ebYpL6ZU6-V0`+VYVk7oBr%DNv+($;q`HHK!gIHa) -*%m(-e#9sm3ZsHT^UK%K(4i9Ajp1M~R@C@!4#dQE#lUD;OiKi1RsjZVX>oOFWB>&L0`+VYVk7oBr%DNv -+($;q`HHK!gIHa)*%m(-e#9sm3ZsHT^UK%K(4i9Ajp1M~R@C@!4#dQE#lUD;OiKi1Rs +GOBq10000000030000000000BVRLh7XKrm}Zgg`13IT`y;$>KfZ0H=mhJ>?uV+h;;t~i_0*|LWuHI04?{jxD_@1Z8)ymjIKNK5;L +!8FkfGTe+FK;UUh9M-4n+}vReM{I9mVQfTYbYW?1a|QwtXKZg`VQf=$VRU6vV`yb7J73J3yCzk$#1)IEB9}O*pr-e%zuW2Pj0xis%K=6-eWf1lXbfKaZ!f0AZ|T{?Qtye5DYBGCrj5p+UZwy5000000093000000000q3 +Y;R&=Y*Tb$bY)XxXk~3-1_A|hWo=1h0_mQT=Q}``f02HLt~iCiD@{1Jw0_*8A_pi$)ov?1P-_IE6rQG) +02XJT?*g=|B=zREie$*y(7k2+*P~cYjRc;WmI`^Wd#8M00In0Y;R&= +Y*t}xb!Bq}0RRXAAo5UFB9CUy{E_rD>}a8$2!O9kk`*PSB+rd(so&!uOW`TABoF=|M@BNr;@ghiU?gEXK9KMDE{ +F?;HZBRuDVqlk6qmbd@_000000093F00000000U}Zg6#UPjG2ua|Hna2?3SJ=6W7=VqesjRYGc!>wZFz +p>JB4@xD;^wu&SY_r(DK000000096000000000I_Zg6#Ua|Hna2?3K3ynwMZT8l5kSW@l}O=!>^xB4~9 +n`Dx!RtcK)nwJ3p000000096000000000Y3WprUgWprU_Y;y(z1#@L>Nn`@)o|5M~K$m}!eub_$g}*CJ +IJdNZ+@c}}C`8q6D?CtZ1fvw5rj-B|XP@r^w5ufb=C_Ju$l1`nW&GEpSWb-uZeeX@0_mQT=Q}``f02HL +t~iCiD@{1Jw0_*8A_pi$)ov?1P#d&Z0rFt3ZOHs70;T-agdg$OP=xIp;K4#IcLF!~atKXjbYWC^aAgGn +0006BO=WapR$**)Wd#8M2mk;;0000000000|Nj60000002u)>lVPs)+VFdvI3ITQGP59r=ivkR6Lh9EroO>_;000000003000000 +0000BPh(?sa&l#EV`Xy&0t0PnZU6uR18re=0006EPjEwTZEb0EZDnqB1_uLXW_AJEn^6;37FKqUhx?i3 +R+Mr!fY&(;2BFL(m@EZk_srD=Zf|a5WdHyO25)dwd2nR`Z%+rR=)-r^_Y&HclF!H13$90G`4!8IQf!w` +1|=wh2yJC_VPs)+VFFBL7H}ii_tAk8EiIL^F;WduOYhK!;vHZzZO$@xxWa9(t;O}HAO^^zqT0%g+nC0; +-MWK<)&Ge4`aq}l(*_4;Y;R&=Y;yukWfpKF*Z0wZ5-ly2voTT)QcLg9hvFSzGHuQ>ceuhhoM?JbaMhsWfpKF*Z0wZ5-ly2voTT)QcLg9hvFSzGHuQ>ceuhh +oClv)aMjKgwAH@`bu1x<7g|G$};xvA~n-$_S1W#~DWCZ~L2LJ#-AOHnVaBp>V +1_J_bZ~>Lb=6W7=VqesjRYGc!>wZFzp>JB4@xD;^wu&SY_r(Hqc>#z1;$>KfZ0H=mhJ>?uVM +0r~W-2xhK9cV^W63=w?&L0_mQT=Q}``f02HLt~iCi +D@{1Jw0_*8A_pi$)ov?1P-_IE6rQG)02XJT?*g=|B=zREie$*y(7k2+*P~cYjSNy{c4c8~Wn@HQbYTSn +00;m8KmY&$000000RR600000001Z-Qc4c8~Wn@-iY;|P?0Rg6rFp)<~$~wYgjK`HkjV#@&#T1_fGnK3M +JXK)_7bXT%V`yb +mw%Cdg|0Y-zbj2Rx3qrTq9O+2!s^Lf^?|9I@Xg>Oi(W05|TJ%PM*ricn_PmXk-a(WprUw +d2nTO015$ht0000000000{{R30000003ukO^Vqt7ld2nTO015%s?vf5kh_h+&YE#h%O8d1V_{UOl9{V;u +R#^q%lDklY(DVQRZ2_xNXm|`iS|~;BRWrW0000000000{{R30000003U7CAWn@%& +aAk7<3IT`y;$>KfZ0H=mhJ>?uVb7^w{PHf7Wpb~G=Pm~XxYI*`6rJj{160lxOxY(3#7BW?i +3v_Z}ZgXjLX>V?G015##QV*^ZmKt8YDf|&5KZQ>65I6*X)C9iYp+?yjr7~y%r?zUHR4te2+NwrBxfixd_%u|$Wt2vcKdWo=D+0ot2U6Id2jc94hrndMfLayEe1ISdA&%p{mB1!VWk)e2*8Zgg^CV{}Pm0iOsgNjk^^qPoT1 ++zTRnAg`3vXv9d*8d@RXy~6c6G6imLZewKt00<6ra$#@6CZd7@2WdSr&53UoI8eY9A{1GERg--Gi +I0S#x1is&)M%fmnGH3{GWprU=VRT^u^?FS>S$_F2)vN@Mb6UJ-F(lri_dqerx4lR4>iBsz2WM<=Vqt7^ +0irX>=qOIib(s7HIp;Lc%L0r`EZl^VKAG97Mm{9gQU+;maCLNZ0X7CplUE@k+(@AMA;**Avw@PI9YE8Q +DR*w0ca`xfVhdq&b7^O8ZDnqBa{-I4R4RNCFj+|XM1C+DXk-poZ)C_D*YlJ+82MQED-Q{CX=iR>bairN +00aU61a5C`WdHyG0R(ezZDjxj0Rcq9?S_9%zBIrmKs6QQX?psHjFs5t#D;?!SMk=|KFbhPa$#@6C +ZbEf#WNc*y0tjhtaCLM|VQ>Hn0`+VYVk7oBr%DNv+($;q`HHK!gIHa)*%m(-e#9sm3L)b@L&d6G@+l`% +qd385?K@+fP1(-9sgE>i7rMzqbpe9wZFzp>JB4@xD;^wu&SY_r(E19cP`Q +rta`4SL+aF4>h_8B*ZM$_{DUqzm~P&L)y{-0000000030{{R300000HRB~Z%b7^#GZ*D|obYW?1a|Qws +baG*Cb7^#GZ*Eg#Xk~3-0jz(i9yuZQ_f<_~3)9CPP+$;5D=2dnwEKV#qFOR4as+N+ZDj)Ko|5M~K$m}! +eub_$g}*CJIJdNZ+@c}}C`8q6D?Csev{(W1V6JV*{3!yZ{M3XW@z+pxis%K +u=2wF+7z(Wqt=tdZk`V^s(Ana000000093000000000JQZg6#Ua{vkfhyLPaScq)s9KMExvw34D6J>+N +wrBxfixd_%u|$Wt0XD%jq57bK6Q|uUfIMEX^1}Vv6tLB!)|10-o)0prc>n+a000000RI300000001IJr +b7^O8ZDnqBa{vkfhyLPaScq)s9KMExvw34D6J>+NwrBxfixd_%u|$Wt0XD%jq57bK6Q|uUfIMEX^1}Vv +6tLB!)|10-o)0prc>n+a000000RI3000000010+sY-wa+bZ>G11OfmAZf|a7000011aog~WdH>M0-Gv5 +(&k{eX@5SIR>|w{s%)+}o6^~`ha@$PehdAwF>UXmCgHqw;r~cW`-Qb7^w`1pxuGATjcBM0KUs3yuTaBu#bt +AlTMkBr_I*#EX^1JzD<(3UqQ|ZgXjLX>V=-1p)z|2rNlD$O59e#ogQsB77jPl+h5j^*S;E +G*S<)6P6lYy(#<=BR_>s@(?%#f7ArN-=Rj?7Ns(18dQ03Wn@8fb7^O8b3$xsZe&wsVQf@*P;_zx0{{zT +Wn*k%a$$67c4Yts0Rfg5{EB0fg;c|RERyKwyc?S`-(k#Jc79Ua6d|cNYh3{dW_507X<}?;00jX7t)le6 +tF1w|qkKKoQ8@+ft}Og`cxWD32N9OlJRp>80t$0Lb#i5700jX7w}_%X+LPM83Yle?T&{7IutOmr +M0ry;ESwv+3 +lBrcu^)moMuzDTL*_JDxK=Xf+&45B--vJ0_b#7;AVr*pq1pxt*I?dmD_aL)Lb)L?ZrQ2W!68hCK)<9B4 +o`z7Y8iKY03UhRFbz^jOa%E%y1pxs#vU_4@XKMa?TsAlK-}9~&K4&!}B_Q_~{-JMMpvoBuR&Qx!Q*>c; +Wd#8M000 -----END STRICT TYPE LIB----- diff --git a/stl/RGBCommit@0.1.0.stl b/stl/RGBCommit@0.1.0.stl index 1ffee9d2..95b376c0 100644 Binary files a/stl/RGBCommit@0.1.0.stl and b/stl/RGBCommit@0.1.0.stl differ diff --git a/stl/RGBCommit@0.1.0.sty b/stl/RGBCommit@0.1.0.sty index cf78d5c5..cf7d043d 100644 --- a/stl/RGBCommit@0.1.0.sty +++ b/stl/RGBCommit@0.1.0.sty @@ -1,5 +1,5 @@ {- - Id: stl:ZMTVCU25-QDo98xR-wI91wcu-ydb7kui-QfZbF$n-0KDS2ow#tuna-safari-design + Id: stl:XbiECcs9-xlyofco-wkXoupT-gJ61JJf-XWL0DWf-INKzIp0#support-iris-depend Name: RGBCommit Version: 0.1.0 Description: Consensus commitment layer for RGB smart contracts @@ -11,291 +11,163 @@ @context typelib RGBCommit -import StrictTypes#century-comrade-chess - use TypeName#edgar-carol-mystery - use SemId#logic-absorb-hilton - -import AluVM#congo-archive-folio - use LibSite#ultra-grace-message - use LibId#germany-culture-olivia +import BPCore#juliet-super-dominic + use BlindSealTxid#halt-crash-valid + use SecretSeal#dollar-iris-wizard + use BlindSealTxPtr#content-paradox-dominic + use TxPtr#italian-july-eddie -import CommitVerify#miller-pancake-elastic +import CommitVerify#violet-panther-herbert use MerkleHash#horse-popcorn-bundle use StrictHash#pizza-sherman-sound - use ReservedBytes1#origin-roger-relax - use ReservedBytes2#florida-libra-circus - use ReservedBytes8#rudolf-tape-adrian -import BPCore#totem-holiday-helena - use Method#bali-boris-plasma - use BlindSealTxPtr#fortune-iron-salmon - use SecretSeal#dollar-iris-wizard - use BlindSealTxid#media-judge-anita - use TxPtr#italian-july-eddie - -import Std#ralph-blue-lucky +import Std#delete-roman-hair use AsciiPrintable#ultra-sunset-format - use Bool#oxygen-complex-duet use AlphaNumLodash#percent-bingo-caesar use AlphaCapsLodash#duet-hammer-labor + use AlphaSmallLodash#pioneer-eagle-spell + +import AluVM#jargon-gorilla-poetic + use LibSite#ultra-grace-message + use LibId#germany-culture-olivia + +import StrictTypes#henry-heart-survive + use FieldName#present-flute-herman + use TypeName#edgar-carol-mystery + use SemId#logic-absorb-hilton import Bitcoin#signal-color-cipher use Vout#brush-gloria-heroic use Txid#shallow-light-reverse -@mnemonic(edison-survive-nitro) -data AltLayer1 : liquid#1 - - -@mnemonic(almond-office-pulse) -data AltLayer1Set : {AltLayer1 ^ ..0xff} - -@mnemonic(slang-amber-club) -data AssetTag : [Byte ^ 32] - -@mnemonic(crash-singer-corner) -data AssetTags : {AssignmentType -> ^ ..0xff AssetTag} - -@mnemonic(airport-ladder-joseph) -data AssignRevealedAttachBlindSealTxPtr : confidential (seal XChainSecretSeal - , state ConcealedAttach - , lock CommitVerify.ReservedBytes2) - | confidentialState (seal XChainBlindSealTxPtr - , state ConcealedAttach - , lock CommitVerify.ReservedBytes2) - | confidentialSeal (seal XChainSecretSeal - , state RevealedAttach - , lock CommitVerify.ReservedBytes2) - | revealed (seal XChainBlindSealTxPtr - , state RevealedAttach - , lock CommitVerify.ReservedBytes2) - -@mnemonic(member-camera-parking) -data AssignRevealedAttachBlindSealTxid : confidential (seal XChainSecretSeal - , state ConcealedAttach - , lock CommitVerify.ReservedBytes2) - | confidentialState (seal XChainBlindSealTxid - , state ConcealedAttach - , lock CommitVerify.ReservedBytes2) - | confidentialSeal (seal XChainSecretSeal - , state RevealedAttach - , lock CommitVerify.ReservedBytes2) - | revealed (seal XChainBlindSealTxid - , state RevealedAttach - , lock CommitVerify.ReservedBytes2) - -@mnemonic(genius-editor-formula) -data AssignRevealedDataBlindSealTxPtr : confidential (seal XChainSecretSeal - , state ConcealedData - , lock CommitVerify.ReservedBytes2) - | confidentialState (seal XChainBlindSealTxPtr - , state ConcealedData - , lock CommitVerify.ReservedBytes2) - | confidentialSeal (seal XChainSecretSeal - , state RevealedData - , lock CommitVerify.ReservedBytes2) - | revealed (seal XChainBlindSealTxPtr - , state RevealedData - , lock CommitVerify.ReservedBytes2) - -@mnemonic(soda-edison-music) -data AssignRevealedDataBlindSealTxid : confidential (seal XChainSecretSeal - , state ConcealedData - , lock CommitVerify.ReservedBytes2) - | confidentialState (seal XChainBlindSealTxid - , state ConcealedData - , lock CommitVerify.ReservedBytes2) - | confidentialSeal (seal XChainSecretSeal - , state RevealedData - , lock CommitVerify.ReservedBytes2) - | revealed (seal XChainBlindSealTxid - , state RevealedData - , lock CommitVerify.ReservedBytes2) - -@mnemonic(rachel-unique-logic) -data AssignRevealedValueBlindSealTxPtr : confidential (seal XChainSecretSeal - , state ConcealedFungible - , lock CommitVerify.ReservedBytes2) - | confidentialState (seal XChainBlindSealTxPtr - , state ConcealedFungible - , lock CommitVerify.ReservedBytes2) - | confidentialSeal (seal XChainSecretSeal - , state RevealedFungible - , lock CommitVerify.ReservedBytes2) - | revealed (seal XChainBlindSealTxPtr - , state RevealedFungible - , lock CommitVerify.ReservedBytes2) - -@mnemonic(cadet-book-pablo) -data AssignRevealedValueBlindSealTxid : confidential (seal XChainSecretSeal - , state ConcealedFungible - , lock CommitVerify.ReservedBytes2) - | confidentialState (seal XChainBlindSealTxid - , state ConcealedFungible - , lock CommitVerify.ReservedBytes2) - | confidentialSeal (seal XChainSecretSeal - , state RevealedFungible - , lock CommitVerify.ReservedBytes2) - | revealed (seal XChainBlindSealTxid - , state RevealedFungible - , lock CommitVerify.ReservedBytes2) - -@mnemonic(cycle-panther-cave) -data AssignVoidStateBlindSealTxPtr : confidential (seal XChainSecretSeal - , state VoidState - , lock CommitVerify.ReservedBytes2) - | confidentialState (seal XChainBlindSealTxPtr - , state VoidState - , lock CommitVerify.ReservedBytes2) - | confidentialSeal (seal XChainSecretSeal - , state VoidState - , lock CommitVerify.ReservedBytes2) - | revealed (seal XChainBlindSealTxPtr - , state VoidState - , lock CommitVerify.ReservedBytes2) - -@mnemonic(dynasty-iron-athena) -data AssignVoidStateBlindSealTxid : confidential (seal XChainSecretSeal - , state VoidState - , lock CommitVerify.ReservedBytes2) - | confidentialState (seal XChainBlindSealTxid - , state VoidState - , lock CommitVerify.ReservedBytes2) - | confidentialSeal (seal XChainSecretSeal - , state VoidState - , lock CommitVerify.ReservedBytes2) - | revealed (seal XChainBlindSealTxid - , state VoidState - , lock CommitVerify.ReservedBytes2) +@mnemonic(control-basic-labor) +data AssignRevealedDataBlindSealTxPtr : revealed (seal BPCore.BlindSealTxPtr, state RevealedData) + | confidentialSeal (seal BPCore.SecretSeal, state RevealedData) -@mnemonic(secret-penguin-limit) -data AssignmentType : U16 +@mnemonic(passage-circus-compact) +data AssignRevealedDataBlindSealTxid : revealed (seal BPCore.BlindSealTxid, state RevealedData) + | confidentialSeal (seal BPCore.SecretSeal, state RevealedData) -@mnemonic(target-valid-tiger) -data AssignmentsBlindSealTxPtr : {AssignmentType -> ^ ..0xff TypedAssignsBlindSealTxPtr} +@mnemonic(beatles-service-husband) +data AssignRevealedValueBlindSealTxPtr : revealed (seal BPCore.BlindSealTxPtr, state RevealedValue) + | confidentialSeal (seal BPCore.SecretSeal, state RevealedValue) -@mnemonic(baker-size-sonar) -data AssignmentsBlindSealTxid : {AssignmentType -> ^ ..0xff TypedAssignsBlindSealTxid} +@mnemonic(address-natural-neptune) +data AssignRevealedValueBlindSealTxid : revealed (seal BPCore.BlindSealTxid, state RevealedValue) + | confidentialSeal (seal BPCore.SecretSeal, state RevealedValue) -@mnemonic(factor-hair-everest) -data AttachId : [Byte ^ 32] +@mnemonic(critic-alcohol-actor) +data AssignVecAssignRevealedDataBlindSealTxPtr : [AssignRevealedDataBlindSealTxPtr ^ 1..] -@mnemonic(harvard-burma-bicycle) -data AttachState : id AttachId, mediaType MediaType +@mnemonic(sonata-food-tina) +data AssignVecAssignRevealedDataBlindSealTxid : [AssignRevealedDataBlindSealTxid ^ 1..] -@mnemonic(amadeus-sunday-casino) -data BaseCommitment : flags CommitVerify.ReservedBytes1 - , schemaId SchemaId - , timestamp I64 - , issuer CommitVerify.StrictHash - , testnet Std.Bool - , altLayers1 CommitVerify.StrictHash - , assetTags CommitVerify.StrictHash +@mnemonic(magic-pupil-model) +data AssignVecAssignRevealedValueBlindSealTxPtr : [AssignRevealedValueBlindSealTxPtr ^ 1..] -@mnemonic(animal-plume-minus) -data BlindingFactor : [Byte ^ 32] +@mnemonic(equator-sector-regard) +data AssignVecAssignRevealedValueBlindSealTxid : [AssignRevealedValueBlindSealTxid ^ 1..] -@mnemonic(meter-arizona-albino) -data ConcealedAttach : [Byte ^ 32] +@mnemonic(begin-process-outside) +data AssignVecAssignVoidStateBlindSealTxPtr : [AssignVoidStateBlindSealTxPtr ^ 1..] -@mnemonic(ivan-tripod-young) -data ConcealedData : [Byte ^ 32] +@mnemonic(arena-neuron-cantina) +data AssignVecAssignVoidStateBlindSealTxid : [AssignVoidStateBlindSealTxid ^ 1..] -@mnemonic(arizona-basic-moment) -data ConcealedFungible : commitment PedersenCommitment, rangeProof PedersenCommitment +@mnemonic(magnum-tango-bronze) +data AssignVoidStateBlindSealTxPtr : revealed (seal BPCore.BlindSealTxPtr, state VoidState) + | confidentialSeal (seal BPCore.SecretSeal, state VoidState) -@mnemonic(uniform-welcome-papa) -data ContractId : [Byte ^ 32] +@mnemonic(shelf-korea-begin) +data AssignVoidStateBlindSealTxid : revealed (seal BPCore.BlindSealTxid, state VoidState) + | confidentialSeal (seal BPCore.SecretSeal, state VoidState) -@mnemonic(short-noise-postal) -data DataState : [Byte] +@mnemonic(sabine-magic-deal) +data AssignmentDetails : ownedStateSchema OwnedStateSchema + , name StrictTypes.FieldName + , defaultTransition TransitionType -@mnemonic(reform-garden-ballet) -data Extension : ffv Ffv - , contractId ContractId - , nonce U64 - , extensionType ExtensionType - , metadata Metadata - , globals GlobalState - , assignments AssignmentsBlindSealTxid - , redeemed Redeemed - , valencies Valencies - , validator CommitVerify.ReservedBytes1 - , witness CommitVerify.ReservedBytes2 +@mnemonic(secret-penguin-limit) +data AssignmentType : U16 -@mnemonic(delta-elastic-germany) -data ExtensionSchema : metadata {MetaType ^ ..0xff} - , globals {GlobalStateType -> ^ ..0xff Occurrences} - , redeems {ValencyType ^ ..0xff} - , assignments {AssignmentType -> ^ ..0xff Occurrences} - , valencies {ValencyType ^ ..0xff} - , validator AluVM.LibSite? +@mnemonic(mercy-first-clone) +data AssignmentsBlindSealTxPtr : {AssignmentType -> TypedAssignsBlindSealTxPtr} + +@mnemonic(extra-signal-alaska) +data AssignmentsBlindSealTxid : {AssignmentType -> TypedAssignsBlindSealTxid} + +@mnemonic(fiesta-gilbert-fiber) +data BaseCommitment : schemaId SchemaId + , timestamp I64 + , issuer CommitVerify.StrictHash + , chainNet ChainNet + , sealClosingStrategy SealClosingStrategy + +@mnemonic(carmen-farmer-diesel) +data BundleId : [Byte ^ 32] -@mnemonic(apropos-scoop-viva) -data ExtensionType : U16 +@mnemonic(ringo-fashion-enrico) +data ChainNet : bitcoinMainnet | bitcoinTestnet3 | bitcoinTestnet4 | bitcoinSignet + | bitcoinRegtest | liquidMainnet | liquidTestnet + + +@mnemonic(uniform-welcome-papa) +data ContractId : [Byte ^ 32] @mnemonic(pigment-career-hippie) data Ffv : U16 @mnemonic(guide-poker-coconut) -data FungibleState : bits64#8 U64 +data FungibleState : bits64#8 U64 | (|) @mnemonic(matrix-optimal-sinatra) -data FungibleType : unsigned64Bit#8 +data FungibleType : unsigned64Bit#8 | (|) -@mnemonic(fashion-delta-polka) +@mnemonic(salami-water-climax) data Genesis : ffv Ffv , schemaId SchemaId - , flags CommitVerify.ReservedBytes1 , timestamp I64 , issuer Identity - , testnet Std.Bool - , altLayers1 AltLayer1Set - , assetTags AssetTags + , chainNet ChainNet + , sealClosingStrategy SealClosingStrategy , metadata Metadata , globals GlobalState , assignments AssignmentsBlindSealTxid - , valencies Valencies - , validator CommitVerify.ReservedBytes1 -@mnemonic(concept-gloria-shock) +@mnemonic(strong-mister-version) data GenesisSchema : metadata {MetaType ^ ..0xff} , globals {GlobalStateType -> ^ ..0xff Occurrences} , assignments {AssignmentType -> ^ ..0xff Occurrences} - , valencies {ValencyType ^ ..0xff} , validator AluVM.LibSite? +@mnemonic(eddie-super-cartoon) +data GlobalDetails : globalStateSchema GlobalStateSchema, name StrictTypes.FieldName + @mnemonic(initial-malta-sierra) data GlobalState : {GlobalStateType -> ^ ..0xff GlobalValues} -@mnemonic(silk-college-august) -data GlobalStateSchema : reserved CommitVerify.ReservedBytes1 - , semId StrictTypes.SemId - , maxItems U24 +@mnemonic(zipper-pioneer-visa) +data GlobalStateSchema : semId StrictTypes.SemId, maxItems U24 @mnemonic(yoga-quick-jasmine) data GlobalStateType : U16 -@mnemonic(charter-fractal-maze) -data GlobalValues : [DataState ^ 1..] +@mnemonic(henry-kevin-carrot) +data GlobalValues : [RevealedData ^ 1..] @mnemonic(smart-pioneer-nominal) data Identity : Std.AsciiPrintable, [Std.AsciiPrintable ^ ..0xfff] -@mnemonic(kevin-morgan-shrink) -data Input : prevOut Opout, reserved CommitVerify.ReservedBytes2 - -@mnemonic(tobacco-open-join) -data InputMap : {Bitcoin.Vout -> ^ 1.. OpId} - -@mnemonic(sector-charlie-diagram) -data Inputs : {Input} +@mnemonic(legal-milk-gold) +data InputOpids : {OpId ^ 1..} -@mnemonic(isabel-heaven-north) -data MediaType : any#255 +@mnemonic(judo-invest-thermos) +data Inputs : {Opout ^ 1..} +@mnemonic(arena-janet-prepare) +data MetaDetails : semId StrictTypes.SemId, name StrictTypes.FieldName @mnemonic(quebec-mission-quota) data MetaType : U16 @@ -309,7 +181,7 @@ data Metadata : {MetaType -> ^ ..0xff MetaValue} @mnemonic(source-olga-mirage) data Occurrences : min U16, max U16 -@mnemonic(survive-citizen-harris) +@mnemonic(oregano-dexter-exact) data OpCommitment : ffv Ffv , nonce U64 , opType TypeCommitment @@ -317,10 +189,6 @@ data OpCommitment : ffv Ffv , globals CommitVerify.MerkleHash , inputs CommitVerify.MerkleHash , assignments CommitVerify.MerkleHash - , redeemed CommitVerify.StrictHash - , valencies CommitVerify.StrictHash - , witness CommitVerify.MerkleHash - , validator CommitVerify.StrictHash @mnemonic(picnic-single-gloria) data OpId : [Byte ^ 32] @@ -330,48 +198,38 @@ data Opout : op OpId , ty AssignmentType , no U16 -@mnemonic(neutral-mixer-visual) +@mnemonic(sincere-teacher-stella) data OwnedStateSchema : declarative () | fungible FungibleType | structured StrictTypes.SemId - | attachment MediaType -@mnemonic(pupil-scale-jerome) -data PedersenCommitment : [Byte ^ 33] +@mnemonic(salon-clarion-factor) +data RevealedData : [Byte] -@mnemonic(anita-vega-pirate) -data Redeemed : {ValencyType -> ^ ..0xff OpId} +@mnemonic(chance-stage-kermit) +data RevealedValue : FungibleState -@mnemonic(simple-bombay-salute) -data RevealedAttach : file AttachState, salt U64 - -@mnemonic(sleep-source-figure) -data RevealedData : value DataState, salt U128 - -@mnemonic(source-contact-member) -data RevealedFungible : value FungibleState - , blinding BlindingFactor - , tag AssetTag - -@mnemonic(corona-igloo-sierra) +@mnemonic(burma-bambino-nobody) data Schema : ffv Ffv - , flags CommitVerify.ReservedBytes1 , name StrictTypes.TypeName - , timestamp I64 - , developer Identity - , metaTypes {MetaType -> ^ ..0xff StrictTypes.SemId} - , globalTypes {GlobalStateType -> ^ ..0xff GlobalStateSchema} - , ownedTypes {AssignmentType -> ^ ..0xff OwnedStateSchema} - , valencyTypes {ValencyType ^ ..0xff} + , metaTypes {MetaType -> ^ ..0xff MetaDetails} + , globalTypes {GlobalStateType -> ^ ..0xff GlobalDetails} + , ownedTypes {AssignmentType -> ^ ..0xff AssignmentDetails} , genesis GenesisSchema - , extensions {ExtensionType -> ^ ..0xff ExtensionSchema} - , transitions {TransitionType -> ^ ..0xff TransitionSchema} - , reserved CommitVerify.ReservedBytes8 + , transitions {TransitionType -> ^ ..0xff TransitionDetails} + , defaultAssignment AssignmentType? @mnemonic(ramirez-patron-simon) data SchemaId : [Byte ^ 32] -@mnemonic(michael-exact-eric) +@mnemonic(exile-target-catalog) +data SealClosingStrategy : firstOpretOrTapret | (|) + + +@mnemonic(ferrari-zebra-tempo) +data Signature : [Byte ^ 64] + +@mnemonic(remark-gravity-vitamin) data Transition : ffv Ffv , contractId ContractId , nonce U64 @@ -380,66 +238,39 @@ data Transition : ffv Ffv , globals GlobalState , inputs Inputs , assignments AssignmentsBlindSealTxPtr - , valencies Valencies - , validator CommitVerify.ReservedBytes1 - , witness CommitVerify.ReservedBytes2 + , signature Signature? -@mnemonic(antonio-adios-analyze) -data TransitionBundle : closeMethod BPCore.Method - , inputMap InputMap - , knownTransitions {OpId -> ^ 1.. Transition} +@mnemonic(isabel-duet-cover) +data TransitionBundle : inputMap {Bitcoin.Vout -> ^ 1.. InputOpids}, knownTransitions {OpId -> ^ 1.. Transition} -@mnemonic(pirate-lithium-side) +@mnemonic(orbit-pattern-goblin) +data TransitionDetails : transitionSchema TransitionSchema, name StrictTypes.FieldName + +@mnemonic(scratch-magnum-polaris) data TransitionSchema : metadata {MetaType ^ ..0xff} , globals {GlobalStateType -> ^ ..0xff Occurrences} , inputs {AssignmentType -> ^ ..0xff Occurrences} , assignments {AssignmentType -> ^ ..0xff Occurrences} - , valencies {ValencyType ^ ..0xff} , validator AluVM.LibSite? @mnemonic(picture-reflex-brigade) data TransitionType : U16 -@mnemonic(biscuit-pandora-bagel) +@mnemonic(yellow-young-stone) data TypeCommitment : genesis BaseCommitment | transition (ContractId, TransitionType) - | extension (ContractId, ExtensionType) - -@mnemonic(giant-trinity-lagoon) -data TypedAssignsBlindSealTxPtr : declarative [AssignVoidStateBlindSealTxPtr] - | fungible [AssignRevealedValueBlindSealTxPtr] - | structured [AssignRevealedDataBlindSealTxPtr] - | attachment#255 [AssignRevealedAttachBlindSealTxPtr] -@mnemonic(penguin-raja-machine) -data TypedAssignsBlindSealTxid : declarative [AssignVoidStateBlindSealTxid] - | fungible [AssignRevealedValueBlindSealTxid] - | structured [AssignRevealedDataBlindSealTxid] - | attachment#255 [AssignRevealedAttachBlindSealTxid] +@mnemonic(ship-trilogy-clara) +data TypedAssignsBlindSealTxPtr : declarative AssignVecAssignVoidStateBlindSealTxPtr + | fungible AssignVecAssignRevealedValueBlindSealTxPtr + | structured AssignVecAssignRevealedDataBlindSealTxPtr -@mnemonic(shock-jester-orion) -data Valencies : {ValencyType ^ ..0xff} - -@mnemonic(aloha-dublin-brush) -data ValencyType : U16 +@mnemonic(felix-compare-history) +data TypedAssignsBlindSealTxid : declarative AssignVecAssignVoidStateBlindSealTxid + | fungible AssignVecAssignRevealedValueBlindSealTxid + | structured AssignVecAssignRevealedDataBlindSealTxid @mnemonic(email-snow-safari) data VoidState : () -@mnemonic(senator-limbo-raymond) -data XChainBlindSealTxPtr : bitcoin BPCore.BlindSealTxPtr - | liquid BPCore.BlindSealTxPtr - -@mnemonic(dynamic-life-brown) -data XChainBlindSealTxid : bitcoin BPCore.BlindSealTxid - | liquid BPCore.BlindSealTxid - -@mnemonic(alex-griffin-left) -data XChainSecretSeal : bitcoin BPCore.SecretSeal - | liquid BPCore.SecretSeal - -@mnemonic(liquid-river-absorb) -data XChainTxid : bitcoin Bitcoin.Txid - | liquid Bitcoin.Txid - diff --git a/stl/RGBLogic@0.1.0.sta b/stl/RGBLogic@0.1.0.sta index 4fb6ec11..3b3e1751 100644 --- a/stl/RGBLogic@0.1.0.sta +++ b/stl/RGBLogic@0.1.0.sta @@ -1,14 +1,14 @@ -----BEGIN STRICT TYPE LIB----- -Id: stl:bioTBozT-NqelHGE-SPbnpMA-XBNSbXZ-6X0dANE-WHVirL8#explain-marvin-bless +Id: stl:qolQpjNB-4ZkpJIo-U1tktjI-mwAYyEg-kOGQttY-ZoK3Loo#colombo-famous-erosion Name: RGBLogic Dependencies: - RGBCommit#tuna-safari-design, - BPCore#totem-holiday-helena, - Bitcoin#signal-color-cipher -Check-SHA256: b4f6b967a70e12d12c5831aa223bd85052f40dbc2bfd5ab3f739e1aa37242f31 + BPCore#juliet-super-dominic, + RGBCommit#support-iris-depend, + Bitcoin#quiz-patent-exit +Check-SHA256: b5ace1d4b22b65bde7b4ed6e0d76575244f48e7bbe5873cce25832abcf618ac1 -2vSEvOmAmtV*_Nw)d@|xKsr716mTQmaB}ROZ@Dgs2ia_2=g^?i+KdTOM?ynyZEb0ErdhI3FM~0|p{<6( -sS&)~H{Xkb4j+}v9*QG}Q6KKm20~CnZ*pbzY!hN5_Bp3Y36tDMM#=e#tGI($UA5U3KNx<*C>ja}LTPkk +2vSEvOmAmtV*@?wI}!?D=!eA%8DA7<8;7e4kWwcH4Z3)D-MVlNwB!7L}MA7sFvK#<=RP4S#T1Vv-hhTICs&5fM~jaB^jIPH$voP+@X(Ze?;0 wjY>38tto&d&=eG4cRAF#( @@ -17,21 +17,18 @@ Wpq+$XJ~Xna$#;`Xh%-ZT+rxDK6vW;JU&?LxLM72H?wDC1Zo}=N}D)4mkLjCa%FT-a&K>D2SRCdV{d70 DJ{*3f84s>#k$1lf7uIEVQ@}wWMxQUb7)_z*=^-NPQ?`2v5jYd+6t@dEhY>7H!Y*UdZb-BpG^u(WnpGh V{&P5bg6}ecT=8d`>?<6$C@F;S3|*6`1-v+nBdcqJ?FPKcnV2wbY*gGVQf%qwlfK-7{9iX4Q|L-q$GzU Mp|h#f#{Gz8SzLEaTf~c{WkYggkPIjuQHS#3Ua|L6d7%qre2Ut&TYs@(?%#f7ArN-=Rj?7Ns(14peesZgXjLX>V>+d2nT9bsj>g -6`?#s5rWnKhSeO?L~x^!;Y#eFP|P}0Z%Ez^MR;^&ZgXjGZd7@2WtGV0dLDIRU(}XWLTZugenOC;Z(5k~ -zEJnJiX;;E#RN}qNn`~82t;CIP;zf?W&{EO26SO?a%FS?1pxx4S+Y5xm?t-;06{AC=1< -iX(?nAMVf(wjY>38tto&d&=eV=-1_TFpX>@L7b8`Xq3nQ8ri$WPp5w@a4b3LR7M69fMnD+{F6rHCsWOZ=@ba?_~#MKE+ -xj;HS^AvC+-Eea3oo~4=i3iziU+2)E(%Otg6`?#s5rWnKhSeO?L~x^!;Y#eFP|P}0Z%Ez+Zf|a5 -WdHyO1aEL@WCCQw)d@|xKsr716mTQmaB}ROZ@Dgs2ia_2=g^?i+KiRR=6W7=VqesjRYGc!>wZFzp>JB4 -@xD;^wu&SY_r?DTbaG*Cb7^#GZ*Bkv1P6C%bZ%vHa{=}XBbpbBLK#dEwxUFHJ){RjtgLvL_X>0rou@Kn -b#Vf8c>-j_)d@|xKsr716mTQmaB}ROZ@Dgs2ia_2=g^?i+Ke<(53UoI8eY9A{1GERg--GiI0S#x1is&) -M%fmnGH3;EZ*F5{000OCZ*Xa30%XM12~D{`Iz96ga3kGta_pUNxh{zZ*=%3u(4f-VjFrgddLDIRU(}XW -LTZugenOC;Z(5k~zEJnJiX;;E#R^wxbZ%vHb5C+)1OosFVRB<=X?A5~000011#M|=Wn=&a0Raxbxa~M% -Zlr^{H1ibsRCVcA*+(@K+$R_oJY%-uiLn9+bY*UIVRUJBWdHyG3Rh`#Ze??GP;YYv0tRShX=iA3000CD -bZKp6b97;CZ~y>E +2~tNwLvL+uX>s@(?%#f7ArN-=Rj?7Ns(14peesZgXjLX>V>+d2nTw$mV(; +bz)!CmQ_M(k?Vd!kfCo{nDM?)_qK{868FUfPjE?O1^@^|Vq;KpZ*OJ<0s#heVQ_L~bN~eb0zKhpP*aQYQxux_Ecpx^NEUkq@>Xm_HirtB!lh<{Yi-S-!KI0_27BH<@sVme~^s3<3pjaB^jI +00jX8J?lFX3Sj7m#S0l<6lWWUs|%1)CkG9>cz4~pa1P{=M^4XN(CAD)cag{2}f*iVqt7ga%2Vq1#fUqa%2Inp_$2edG9dLzy*OG4Oq4bRCJgzb0?22v{-Vn;AC_IX=Hc+00IU~ +VR>b8F#`ezVrg_^Z)t7-25f0@b!lV)1y68Ka%2Po00(DfZe??6a{vGU{|a<+VQzD2bZKvH00smHcWHEP +Wpi@@kLzIp{8F`MCJ5#v8Msl{)Y>tRJ&yoJw@!EgNsxtt0(5x-UATk^%RRs@(?%#f7ArN-=Rj?7Ns(11#WL{V`TsU2n26%X=DOjxP%GIJ;q$9 +eabMAdM>3n3F}fxeRYe_HJ>2Tvm%|9$mV(;bz)!CmQ_M(k?Vd!kfCo{nDM?)_qK{868FUlS7~%^Wpi^+ +a%2Pq00nJnZe?Tu1pxtE+zulZFL@%{hr{`tLyD)K8_+CBIb!yB_q58$0Duw!33O#{bYXO9c4Ytn00IYT +XKrtDWn=&V00RhNa${&|c4cG$000VCX>@L7b8}E{a|QzjY+-q2axnpU#Mns)Y&M6LqDeqU-jv95KHlTh +h+c{38p;h*Y8ns*Xk}? + Copyright (C) 2024 LNP/BP Standards Association. All rights reserved. + License: Apache-2.0 +-} + +Bundles vesper lexicon=types+commitments + +commitment BundleId, hasher SHA256, tagged urn:lnp-bp:rgb:bundle#2024-02-03 + map InputOpids, len 1..MAX16 + mapKey Vout + mapValue InputOpids + +union DbcProof + rec tapret, TapretProof, wrapped, tag 0 + rec pathProof, TapretPathProof + union some, TapretNodePartner, option, wrapped, tag 1 + bytes leftNode, len 32, wrapped, aka TapNodeHash, tag 0 + rec rightLeaf, LeafScript, wrapped, tag 1 + is version, U8, aka LeafVer + bytes script, len 0..MAX32, aka ScriptBytes + rec rightBranch, TapretRightBranch, wrapped, tag 2 + bytes leftNodeHash, len 32, aka TapNodeHash + bytes rightNodeHash, len 32, aka TapNodeHash + is nonce, U8 + bytes internalPk, len 32, aka InternalPk, aka XOnlyPk + is opret, Unit, wrapped, aka OpretProof, tag 1 + +rec 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 +