diff --git a/.gitignore b/.gitignore index 4b6d5f0..d07900d 100644 --- a/.gitignore +++ b/.gitignore @@ -260,3 +260,15 @@ proving_keys/ provers/test-util/el/el_proofs/ provers/test-util/cl/cl_proofs/ provers/sp1/proofs/ + +# secrets +.secrets/ + +# log files +*.log* + +# verifier scripts +*.scripts* + +# backup files +*.bak diff --git a/.vscode/settings.json b/.vscode/settings.json index 4453797..a888793 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -21,9 +21,9 @@ // "provers/sp1/guest-cl-agg/Cargo.toml", ], "rust-analyzer.diagnostics.enable": true, - // "rust-analyzer.cargo.features": [ - // "prover" - // ], + "rust-analyzer.cargo.features": [ + "prover" + ], "todohighlight.isEnable": true, "todohighlight.isCaseSensitive": true, "todohighlight.keywords": [ @@ -67,4 +67,4 @@ "**/.vscode/**" ], "todohighlight.maxFilesForSearch": 5120 -} \ No newline at end of file +} diff --git a/Cargo.lock b/Cargo.lock index 9ef6e42..4f5a75c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -103,9 +103,9 @@ dependencies = [ [[package]] name = "alloy-chains" -version = "0.1.44" +version = "0.1.46" source = "registry+/~https://github.com/rust-lang/crates.io-index" -checksum = "c660915971620592abe2c292c859957eb60e73a60c0eba34a6793eea60512cff" +checksum = "836cf02383d9ebb35502d379bcd1ae803155094077eaab9c29131d888cd5fa3e" dependencies = [ "alloy-primitives 0.8.10", "num_enum 0.7.3", @@ -387,7 +387,7 @@ checksum = "2b09cae092c27b6f1bde952653a22708691802e57bfef4a2973b80bea21efd3f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -516,7 +516,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -533,7 +533,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", "syn-solidity", "tiny-keccak", ] @@ -551,7 +551,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.85", + "syn 2.0.87", "syn-solidity", ] @@ -706,9 +706,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.9" +version = "1.0.10" source = "registry+/~https://github.com/rust-lang/crates.io-index" -checksum = "8365de52b16c035ff4fcafe0092ba9390540e3e352870ac09933bebcaa2c8c56" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" @@ -740,9 +740,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.91" +version = "1.0.92" source = "registry+/~https://github.com/rust-lang/crates.io-index" -checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8" +checksum = "74f37166d7d48a0284b99dd824694c26119c700b53bf0d1540cdb147dbdaaf13" [[package]] name = "arbitrary" @@ -762,7 +762,7 @@ checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" [[package]] name = "ark-bn254" version = "0.4.0" -source = "git+/~https://github.com/Antalpha-Labs/algebra/#55d2a52390156ba1f5d07d551d0a24adef9a5866" +source = "git+/~https://github.com/chainwayxyz/algebra/?branch=new-ate-loop#ac23fde284ca4d7ede298018f7866ce8ce64467f" dependencies = [ "ark-ec", "ark-ff 0.4.2", @@ -772,13 +772,12 @@ dependencies = [ [[package]] name = "ark-crypto-primitives" version = "0.4.0" -source = "git+/~https://github.com/Antalpha-Labs/crypto-primitives/#331644f862e3011da86bc176275ca0a20d7cd99a" +source = "git+/~https://github.com/arkworks-rs/crypto-primitives/#b13983815e5b3a0fbeed0e7da0edec751beac270" dependencies = [ "ahash", "ark-crypto-primitives-macros", "ark-ec", "ark-ff 0.4.2", - "ark-poly", "ark-relations", "ark-serialize 0.4.2", "ark-snark", @@ -787,6 +786,7 @@ dependencies = [ "derivative", "digest 0.10.7", "fnv", + "merlin", "rayon", "sha2", ] @@ -794,17 +794,17 @@ dependencies = [ [[package]] name = "ark-crypto-primitives-macros" version = "0.4.0" -source = "git+/~https://github.com/Antalpha-Labs/crypto-primitives/#331644f862e3011da86bc176275ca0a20d7cd99a" +source = "git+/~https://github.com/arkworks-rs/crypto-primitives/#b13983815e5b3a0fbeed0e7da0edec751beac270" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] name = "ark-ec" version = "0.4.2" -source = "git+/~https://github.com/Antalpha-Labs/algebra/#55d2a52390156ba1f5d07d551d0a24adef9a5866" +source = "git+/~https://github.com/chainwayxyz/algebra/?branch=new-ate-loop#ac23fde284ca4d7ede298018f7866ce8ce64467f" dependencies = [ "ahash", "ark-ff 0.4.2", @@ -813,7 +813,7 @@ dependencies = [ "ark-std 0.4.0", "educe", "fnv", - "hashbrown 0.14.5", + "hashbrown 0.15.0", "itertools 0.13.0", "num-bigint 0.4.6", "num-integer", @@ -843,7 +843,7 @@ dependencies = [ [[package]] name = "ark-ff" version = "0.4.2" -source = "git+/~https://github.com/Antalpha-Labs/algebra/#55d2a52390156ba1f5d07d551d0a24adef9a5866" +source = "git+/~https://github.com/chainwayxyz/algebra/?branch=new-ate-loop#ac23fde284ca4d7ede298018f7866ce8ce64467f" dependencies = [ "ark-ff-asm 0.4.2", "ark-ff-macros 0.4.2", @@ -873,10 +873,10 @@ dependencies = [ [[package]] name = "ark-ff-asm" version = "0.4.2" -source = "git+/~https://github.com/Antalpha-Labs/algebra/#55d2a52390156ba1f5d07d551d0a24adef9a5866" +source = "git+/~https://github.com/chainwayxyz/algebra/?branch=new-ate-loop#ac23fde284ca4d7ede298018f7866ce8ce64467f" dependencies = [ "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -894,19 +894,19 @@ dependencies = [ [[package]] name = "ark-ff-macros" version = "0.4.2" -source = "git+/~https://github.com/Antalpha-Labs/algebra/#55d2a52390156ba1f5d07d551d0a24adef9a5866" +source = "git+/~https://github.com/chainwayxyz/algebra/?branch=new-ate-loop#ac23fde284ca4d7ede298018f7866ce8ce64467f" dependencies = [ "num-bigint 0.4.6", "num-traits", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] name = "ark-groth16" version = "0.4.0" -source = "git+/~https://github.com/Antalpha-Labs/groth16#2007f430c1f42931d7a420e8058e488965c31eb3" +source = "git+/~https://github.com/arkworks-rs/groth16#16ac1dd387fe5dc89b6fc1a1cf87c9fdcfd8493f" dependencies = [ "ark-crypto-primitives", "ark-ec", @@ -921,7 +921,7 @@ dependencies = [ [[package]] name = "ark-poly" version = "0.4.2" -source = "git+/~https://github.com/Antalpha-Labs/algebra/#55d2a52390156ba1f5d07d551d0a24adef9a5866" +source = "git+/~https://github.com/chainwayxyz/algebra/?branch=new-ate-loop#ac23fde284ca4d7ede298018f7866ce8ce64467f" dependencies = [ "ahash", "ark-ff 0.4.2", @@ -929,14 +929,30 @@ dependencies = [ "ark-std 0.4.0", "educe", "fnv", - "hashbrown 0.14.5", + "hashbrown 0.15.0", "rayon", ] +[[package]] +name = "ark-r1cs-std" +version = "0.4.0" +source = "git+/~https://github.com/arkworks-rs/r1cs-std/#dfdad2b729ebc7e8104931b683f3799246252adc" +dependencies = [ + "ark-ec", + "ark-ff 0.4.2", + "ark-relations", + "ark-std 0.4.0", + "educe", + "num-bigint 0.4.6", + "num-integer", + "num-traits", + "tracing", +] + [[package]] name = "ark-relations" version = "0.4.0" -source = "git+/~https://github.com/Antalpha-Labs/snark/#17000edf677448ca4525ccb49940aa75aca39fc0" +source = "git+/~https://github.com/arkworks-rs/snark/#202b9e02b808d1d15823298b8ac16f5c54ae4ef6" dependencies = [ "ark-ff 0.4.2", "ark-std 0.4.0", @@ -957,7 +973,7 @@ dependencies = [ [[package]] name = "ark-serialize" version = "0.4.2" -source = "git+/~https://github.com/Antalpha-Labs/algebra/#55d2a52390156ba1f5d07d551d0a24adef9a5866" +source = "git+/~https://github.com/chainwayxyz/algebra/?branch=new-ate-loop#ac23fde284ca4d7ede298018f7866ce8ce64467f" dependencies = [ "ark-serialize-derive", "ark-std 0.4.0", @@ -970,17 +986,17 @@ dependencies = [ [[package]] name = "ark-serialize-derive" version = "0.4.2" -source = "git+/~https://github.com/Antalpha-Labs/algebra/#55d2a52390156ba1f5d07d551d0a24adef9a5866" +source = "git+/~https://github.com/chainwayxyz/algebra/?branch=new-ate-loop#ac23fde284ca4d7ede298018f7866ce8ce64467f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] name = "ark-snark" version = "0.4.0" -source = "git+/~https://github.com/Antalpha-Labs/snark/#17000edf677448ca4525ccb49940aa75aca39fc0" +source = "git+/~https://github.com/arkworks-rs/snark/#202b9e02b808d1d15823298b8ac16f5c54ae4ef6" dependencies = [ "ark-ff 0.4.2", "ark-relations", @@ -1172,7 +1188,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -1189,7 +1205,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -1217,7 +1233,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -1282,9 +1298,9 @@ dependencies = [ [[package]] name = "aws-sdk-s3" -version = "1.58.0" +version = "1.59.0" source = "registry+/~https://github.com/rust-lang/crates.io-index" -checksum = "0656a79cf5e6ab0d4bb2465cd750a7a2fd7ea26c062183ed94225f5782e22365" +checksum = "9f883bb1e349fa8343dc46336c252c0f32ceb6e81acb146aeef2e0f8afc9183e" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1663,6 +1679,15 @@ version = "0.11.0" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" +[[package]] +name = "beef" +version = "0.5.2" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" +dependencies = [ + "serde", +] + [[package]] name = "bimap" version = "0.6.3" @@ -1697,7 +1722,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.85", + "syn 2.0.87", "which", ] @@ -1885,7 +1910,7 @@ dependencies = [ [[package]] name = "bitvm" version = "0.1.0" -source = "git+/~https://github.com/alpenlabs/BitVM.git?branch=bridge-poc#67c20f84a2f33596df3dbf6df9eb1de65b809396" +source = "git+/~https://github.com/alpenlabs/BitVM.git?branch=bridge-poc#8438988493b7bb024e310257f1630ceedc923c31" dependencies = [ "alloy", "ark-bn254", @@ -2037,7 +2062,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", "syn_derive", ] @@ -2068,6 +2093,20 @@ name = "bytemuck" version = "1.19.0" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.8.0" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] [[package]] name = "byteorder" @@ -2143,15 +2182,21 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.31" +version = "1.1.34" source = "registry+/~https://github.com/rust-lang/crates.io-index" -checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" +checksum = "67b9470d453346108f93a59222a9a1a5724db32d0a4727b7ab7ace4b4d822dc9" dependencies = [ "jobserver", "libc", "shlex", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cexpr" version = "0.6.0" @@ -2181,8 +2226,10 @@ checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", + "js-sys", "num-traits", "serde", + "wasm-bindgen", "windows-targets 0.52.6", ] @@ -2238,7 +2285,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -2311,6 +2358,16 @@ version = "1.0.3" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -2549,7 +2606,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -2573,7 +2630,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -2584,7 +2641,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -2733,7 +2790,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -2744,7 +2801,7 @@ checksum = "64b697ac90ff296f0fc031ee5a61c7ac31fb9fff50e3fb32873b09223613fc0c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -2757,7 +2814,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -2777,7 +2834,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", "unicode-xid", ] @@ -2888,7 +2945,7 @@ dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -3005,7 +3062,7 @@ checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -3025,7 +3082,7 @@ checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -3036,7 +3093,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -3200,7 +3257,7 @@ dependencies = [ "regex", "serde", "serde_json", - "syn 2.0.85", + "syn 2.0.87", "toml", "walkdir", ] @@ -3218,7 +3275,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -3244,7 +3301,7 @@ dependencies = [ "serde", "serde_json", "strum", - "syn 2.0.85", + "syn 2.0.87", "tempfile", "thiserror", "tiny-keccak", @@ -3571,7 +3628,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -3694,6 +3751,27 @@ version = "0.3.1" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "gloo-net" +version = "0.5.0" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "43aaa242d1239a8822c15c645f02166398da4f8b5c4bae795c1f5b44e9eee173" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils", + "http 0.2.12", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "gloo-timers" version = "0.2.6" @@ -3718,6 +3796,19 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "gloo-utils" +version = "0.2.0" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "group" version = "0.12.1" @@ -4029,7 +4120,7 @@ dependencies = [ "hyper 0.14.31", "log", "rustls 0.21.12", - "rustls-native-certs", + "rustls-native-certs 0.6.3", "tokio", "tokio-rustls 0.24.1", ] @@ -4044,6 +4135,7 @@ dependencies = [ "http 1.1.0", "hyper 1.5.0", "hyper-util", + "log", "rustls 0.23.16", "rustls-pki-types", "tokio", @@ -4287,6 +4379,26 @@ version = "1.0.11" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "jni" +version = "0.19.0" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" +dependencies = [ + "cesu8", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + [[package]] name = "jobserver" version = "0.1.32" @@ -4305,6 +4417,181 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonrpsee" +version = "0.23.2" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "62b089779ad7f80768693755a031cc14a7766aba707cbe886674e3f79e9b7e47" +dependencies = [ + "jsonrpsee-client-transport", + "jsonrpsee-core", + "jsonrpsee-http-client", + "jsonrpsee-proc-macros", + "jsonrpsee-server", + "jsonrpsee-types", + "jsonrpsee-wasm-client", + "jsonrpsee-ws-client", + "tokio", + "tracing", +] + +[[package]] +name = "jsonrpsee-client-transport" +version = "0.23.2" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "08163edd8bcc466c33d79e10f695cdc98c00d1e6ddfb95cec41b6b0279dd5432" +dependencies = [ + "base64 0.22.1", + "futures-channel", + "futures-util", + "gloo-net", + "http 1.1.0", + "jsonrpsee-core", + "pin-project", + "rustls 0.23.16", + "rustls-pki-types", + "rustls-platform-verifier", + "soketto", + "thiserror", + "tokio", + "tokio-rustls 0.26.0", + "tokio-util", + "tracing", + "url", +] + +[[package]] +name = "jsonrpsee-core" +version = "0.23.2" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "79712302e737d23ca0daa178e752c9334846b08321d439fd89af9a384f8c830b" +dependencies = [ + "anyhow", + "async-trait", + "beef", + "bytes", + "futures-timer", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "jsonrpsee-types", + "parking_lot", + "pin-project", + "rand", + "rustc-hash 1.1.0", + "serde", + "serde_json", + "thiserror", + "tokio", + "tokio-stream", + "tracing", + "wasm-bindgen-futures", +] + +[[package]] +name = "jsonrpsee-http-client" +version = "0.23.2" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "2d90064e04fb9d7282b1c71044ea94d0bbc6eff5621c66f1a0bce9e9de7cf3ac" +dependencies = [ + "async-trait", + "base64 0.22.1", + "http-body 1.0.1", + "hyper 1.5.0", + "hyper-rustls 0.27.3", + "hyper-util", + "jsonrpsee-core", + "jsonrpsee-types", + "rustls 0.23.16", + "rustls-platform-verifier", + "serde", + "serde_json", + "thiserror", + "tokio", + "tower 0.4.13", + "tracing", + "url", +] + +[[package]] +name = "jsonrpsee-proc-macros" +version = "0.23.2" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "7895f186d5921065d96e16bd795e5ca89ac8356ec423fafc6e3d7cf8ec11aee4" +dependencies = [ + "heck", + "proc-macro-crate 3.2.0", + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "jsonrpsee-server" +version = "0.23.2" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "654afab2e92e5d88ebd8a39d6074483f3f2bfdf91c5ac57fe285e7127cdd4f51" +dependencies = [ + "anyhow", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.5.0", + "hyper-util", + "jsonrpsee-core", + "jsonrpsee-types", + "pin-project", + "route-recognizer", + "serde", + "serde_json", + "soketto", + "thiserror", + "tokio", + "tokio-stream", + "tokio-util", + "tower 0.4.13", + "tracing", +] + +[[package]] +name = "jsonrpsee-types" +version = "0.23.2" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "d9c465fbe385238e861fdc4d1c85e04ada6c1fd246161d26385c1b311724d2af" +dependencies = [ + "beef", + "http 1.1.0", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "jsonrpsee-wasm-client" +version = "0.23.2" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "4727ac037f834c6f04c0912cada7532dbddb54e92fbc64e33d6cb8c24af313c9" +dependencies = [ + "jsonrpsee-client-transport", + "jsonrpsee-core", + "jsonrpsee-types", +] + +[[package]] +name = "jsonrpsee-ws-client" +version = "0.23.2" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "1c28759775f5cb2f1ea9667672d3fe2b0e701d1f4b7b67954e60afe7fd058b5e" +dependencies = [ + "http 1.1.0", + "jsonrpsee-client-transport", + "jsonrpsee-core", + "jsonrpsee-types", + "url", +] + [[package]] name = "jsonwebtoken" version = "8.3.0" @@ -4425,7 +4712,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -4570,6 +4857,18 @@ version = "0.2.1" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "2145869435ace5ea6ea3d35f59be559317ec9a0d04e1812d5f185a87b6d36f1a" +[[package]] +name = "merlin" +version = "3.0.0" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" +dependencies = [ + "byteorder", + "keccak", + "rand_core", + "zeroize", +] + [[package]] name = "mime" version = "0.3.17" @@ -4902,7 +5201,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -5123,7 +5422,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -5635,7 +5934,7 @@ checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -5742,7 +6041,7 @@ source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" dependencies = [ "proc-macro2", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -5851,7 +6150,27 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", +] + +[[package]] +name = "prover-test-utils" +version = "0.1.0" +dependencies = [ + "bitcoin 0.32.3", + "borsh", + "serde", + "snark-bn254-verifier 1.0.2 (git+/~https://github.com/succinctlabs/snark-bn254-verifier.git?rev=96bce2079584b68597f3241551e4b97300fd92c6)", + "sp1-core-machine", + "strata-btcio", + "strata-primitives", + "strata-proofimpl-btc-blockspace", + "strata-state", + "strata-tx-parser", + "strata-zkvm", + "substrate-bn", + "tokio", + "tower 0.4.13", ] [[package]] @@ -6212,7 +6531,7 @@ dependencies = [ "convert_case 0.6.0", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -6420,6 +6739,12 @@ dependencies = [ "byteorder", ] +[[package]] +name = "route-recognizer" +version = "0.3.1" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" + [[package]] name = "rrs-succinct" version = "0.1.0" @@ -6534,6 +6859,7 @@ version = "0.23.16" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "eee87ff5d9b36712a58574e12e9f0ea80f915a5b0ac518d322b24a465617925e" dependencies = [ + "log", "once_cell", "ring 0.17.8", "rustls-pki-types", @@ -6554,6 +6880,19 @@ dependencies = [ "security-framework", ] +[[package]] +name = "rustls-native-certs" +version = "0.7.3" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" +dependencies = [ + "openssl-probe", + "rustls-pemfile 2.2.0", + "rustls-pki-types", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -6579,8 +6918,35 @@ source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" [[package]] -name = "rustls-webpki" -version = "0.101.7" +name = "rustls-platform-verifier" +version = "0.3.4" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "afbb878bdfdf63a336a5e63561b1835e7a8c91524f51621db870169eac84b490" +dependencies = [ + "core-foundation", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls 0.23.16", + "rustls-native-certs 0.7.3", + "rustls-platform-verifier-android", + "rustls-webpki 0.102.8", + "security-framework", + "security-framework-sys", + "webpki-roots 0.26.6", + "winapi", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + +[[package]] +name = "rustls-webpki" +version = "0.101.7" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ @@ -6662,7 +7028,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -6839,6 +7205,7 @@ dependencies = [ "core-foundation", "core-foundation-sys", "libc", + "num-bigint 0.4.6", "security-framework-sys", ] @@ -6929,7 +7296,7 @@ checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -7002,7 +7369,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -7050,7 +7417,7 @@ checksum = "82fe9db325bcef1fbcde82e078a5cc4efdf787e96b3b9cf45b50b529f2083d67" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -7181,6 +7548,34 @@ dependencies = [ "serde", ] +[[package]] +name = "snark-bn254-verifier" +version = "1.0.2" +source = "git+/~https://github.com/succinctlabs/snark-bn254-verifier.git?branch=bhargav/groth16-verf#6e8c1b423b5572c48131ca10b426fb59b6845c02" +dependencies = [ + "lazy_static", + "num-bigint 0.4.6", + "num-traits", + "rand", + "sha2", + "substrate-bn", + "thiserror", +] + +[[package]] +name = "snark-bn254-verifier" +version = "1.0.2" +source = "git+/~https://github.com/succinctlabs/snark-bn254-verifier.git?rev=96bce2079584b68597f3241551e4b97300fd92c6#96bce2079584b68597f3241551e4b97300fd92c6" +dependencies = [ + "lazy_static", + "num-bigint 0.4.6", + "num-traits", + "rand", + "sha2", + "substrate-bn", + "thiserror", +] + [[package]] name = "snowbridge-amcl" version = "1.0.2" @@ -7201,6 +7596,22 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "soketto" +version = "0.8.0" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "37468c595637c10857701c990f93a40ce0e357cedb0953d1c26c8d8027f9bb53" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures", + "http 1.1.0", + "httparse", + "log", + "rand", + "sha1", +] + [[package]] name = "sp1-build" version = "2.0.0" @@ -7359,6 +7770,20 @@ dependencies = [ "sp1-build", ] +[[package]] +name = "sp1-lib" +version = "1.2.0" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "bea7811abd2d3a991007fcb284f41152840b8388c171288d0c52c6793956609c" +dependencies = [ + "anyhow", + "bincode", + "cfg-if", + "hex", + "serde", + "snowbridge-amcl", +] + [[package]] name = "sp1-primitives" version = "2.0.0" @@ -7779,6 +8204,63 @@ version = "0.3.3" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "4af28eeb7c18ac2dbdb255d40bee63f203120e1db6b0024b177746ebec7049c1" +[[package]] +name = "strata-bridge" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "bitcoin 0.31.0", + "chrono", + "clap", + "esplora-client", + "hex", + "jsonrpsee", + "rand", + "secp256k1 0.28.2", + "strata-bridge-agent", + "strata-bridge-btcio", + "strata-bridge-db", + "strata-bridge-primitives", + "strata-bridge-rpc", + "strata-bridge-tx-graph", + "strata-common", + "strata-rpc", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "strata-bridge-agent" +version = "0.1.0" +dependencies = [ + "anyhow", + "bincode", + "bitcoin 0.31.0", + "bitcoin-script 0.3.0", + "bitcoin-scriptexec", + "bitvm", + "esplora-client", + "hex", + "lazy_static", + "musig2 0.0.11", + "rand", + "secp256k1 0.28.2", + "serde", + "serde_json", + "sha2", + "strata-bridge-btcio", + "strata-bridge-db", + "strata-bridge-primitives", + "strata-bridge-tx-builder", + "strata-bridge-tx-graph", + "strata-rpc", + "strata-state", + "tokio", + "tracing", +] + [[package]] name = "strata-bridge-btcio" version = "0.1.0" @@ -7805,6 +8287,32 @@ dependencies = [ "tracing", ] +[[package]] +name = "strata-bridge-db" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "bincode", + "bitcoin 0.31.0", + "bitcoin-script 0.3.0", + "bitcoin-scriptexec", + "bitvm", + "esplora-client", + "hex", + "lazy_static", + "musig2 0.0.11", + "rand", + "secp256k1 0.28.2", + "serde", + "serde_json", + "sha2", + "strata-bridge-btcio", + "strata-bridge-primitives", + "tokio", + "tracing", +] + [[package]] name = "strata-bridge-guest-builder" version = "0.1.0" @@ -7813,10 +8321,65 @@ dependencies = [ "sp1-sdk", ] +[[package]] +name = "strata-bridge-primitives" +version = "0.1.0" +dependencies = [ + "anyhow", + "ark-bn254", + "ark-crypto-primitives", + "ark-ec", + "ark-ff 0.4.2", + "ark-groth16", + "ark-r1cs-std", + "ark-relations", + "ark-std 0.4.0", + "bincode", + "bitcoin 0.31.0", + "bitcoin-script 0.3.0", + "bitcoin-scriptexec", + "bitvm", + "esplora-client", + "hex", + "lazy_static", + "musig2 0.0.11", + "rand", + "secp256k1 0.28.2", + "serde", + "serde_json", + "sha2", + "strata-bridge-tx-graph", +] + +[[package]] +name = "strata-bridge-rpc" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "bitcoin 0.31.0", + "bitcoin-script 0.3.0", + "bitcoin-scriptexec", + "bitvm", + "esplora-client", + "hex", + "jsonrpsee", + "lazy_static", + "musig2 0.0.11", + "rand", + "secp256k1 0.28.2", + "serde", + "serde_json", + "sha2", + "strata-bridge-db", + "strata-bridge-primitives", + "tokio", +] + [[package]] name = "strata-bridge-tx-builder" version = "0.1.0" -source = "git+/~https://github.com/alpenlabs/strata.git?branch=releases/0.1.0#b7078bd625a178b4ab93491e5c8cbfbaaa8a3ba8" +source = "git+/~https://github.com/alpenlabs/strata.git?branch=bitvm2#fcc7d616d924d0c8cb21f707b47717f0f12b2f3f" dependencies = [ "bitcoin 0.32.3", "lazy_static", @@ -7844,12 +8407,48 @@ dependencies = [ "serde", "serde_json", "sha2", + "strata-bridge-db", + "strata-bridge-primitives", + "tracing", +] + +[[package]] +name = "strata-btcio" +version = "0.1.0" +source = "git+/~https://github.com/alpenlabs/strata.git?branch=bitvm2#c93aeece9c92d3565d6fa52f1d99ff7f012b9e03" +dependencies = [ + "anyhow", + "async-trait", + "base64 0.22.1", + "bitcoin 0.32.3", + "borsh", + "bytes", + "hex", + "musig2 0.1.0", + "rand", + "reqwest 0.12.9", + "serde", + "serde_json", + "sha2", + "strata-bridge-tx-builder", + "strata-db", + "strata-primitives", + "strata-rpc-types", + "strata-state", + "strata-status", + "strata-storage", + "strata-tasks", + "strata-tx-parser", + "thiserror", + "threadpool", + "tokio", + "tracing", ] [[package]] name = "strata-common" version = "0.1.0" -source = "git+/~https://github.com/alpenlabs/strata.git?branch=releases/0.1.0#b7078bd625a178b4ab93491e5c8cbfbaaa8a3ba8" +source = "git+/~https://github.com/alpenlabs/strata.git?branch=bitvm2#fcc7d616d924d0c8cb21f707b47717f0f12b2f3f" dependencies = [ "tracing", "tracing-subscriber 0.3.18", @@ -7858,7 +8457,7 @@ dependencies = [ [[package]] name = "strata-crypto" version = "0.1.0" -source = "git+/~https://github.com/alpenlabs/strata.git?branch=releases/0.1.0#b7078bd625a178b4ab93491e5c8cbfbaaa8a3ba8" +source = "git+/~https://github.com/alpenlabs/strata.git?branch=bitvm2#fcc7d616d924d0c8cb21f707b47717f0f12b2f3f" dependencies = [ "borsh", "secp256k1 0.29.1", @@ -7866,10 +8465,44 @@ dependencies = [ "strata-primitives", ] +[[package]] +name = "strata-db" +version = "0.1.0" +source = "git+/~https://github.com/alpenlabs/strata.git?branch=bitvm2#fcc7d616d924d0c8cb21f707b47717f0f12b2f3f" +dependencies = [ + "anyhow", + "arbitrary", + "bitcoin 0.32.3", + "borsh", + "hex", + "musig2 0.1.0", + "parking_lot", + "serde", + "strata-mmr", + "strata-primitives", + "strata-state", + "strata-zkvm", + "thiserror", + "tracing", +] + +[[package]] +name = "strata-mmr" +version = "0.1.0" +source = "git+/~https://github.com/alpenlabs/strata.git?branch=bitvm2#fcc7d616d924d0c8cb21f707b47717f0f12b2f3f" +dependencies = [ + "arbitrary", + "borsh", + "digest 0.10.7", + "hex", + "sha2", + "thiserror", +] + [[package]] name = "strata-primitives" version = "0.1.0" -source = "git+/~https://github.com/alpenlabs/strata.git?branch=releases/0.1.0#b7078bd625a178b4ab93491e5c8cbfbaaa8a3ba8" +source = "git+/~https://github.com/alpenlabs/strata.git?branch=bitvm2#fcc7d616d924d0c8cb21f707b47717f0f12b2f3f" dependencies = [ "anyhow", "arbitrary", @@ -7893,18 +8526,127 @@ dependencies = [ name = "strata-proofimpl-bitvm-bridge" version = "0.1.0" dependencies = [ - "bitcoin 0.31.0", + "bincode", + "bitcoin 0.32.3", + "borsh", + "prover-test-utils", + "serde", + "snark-bn254-verifier 1.0.2 (git+/~https://github.com/succinctlabs/snark-bn254-verifier.git?rev=96bce2079584b68597f3241551e4b97300fd92c6)", + "sp1-core-machine", + "strata-btcio", + "strata-primitives", + "strata-proofimpl-btc-blockspace", + "strata-state", + "strata-tx-parser", + "strata-zkvm", + "substrate-bn", + "tokio", + "tower 0.4.13", +] + +[[package]] +name = "strata-proofimpl-btc-blockspace" +version = "0.1.0" +source = "git+/~https://github.com/alpenlabs/strata.git?branch=bitvm2#c93aeece9c92d3565d6fa52f1d99ff7f012b9e03" +dependencies = [ + "bitcoin 0.32.3", "borsh", "serde", + "sha2", + "strata-bridge-tx-builder", "strata-primitives", "strata-state", + "strata-tx-parser", +] + +[[package]] +name = "strata-prover-manager" +version = "0.1.0" +dependencies = [ + "anyhow", + "bincode", + "bitcoin 0.32.3", + "borsh", + "hex", + "num-bigint 0.4.6", + "num-traits", + "serde", + "sp1-sdk", + "strata-bridge-guest-builder", + "strata-primitives", + "strata-proofimpl-bitvm-bridge", + "strata-sp1-adapter", + "strata-state", + "strata-zkvm", +] + +[[package]] +name = "strata-rpc" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "bitcoin 0.31.0", + "bitcoin-script 0.3.0", + "bitcoin-scriptexec", + "bitvm", + "esplora-client", + "hex", + "jsonrpsee", + "lazy_static", + "musig2 0.0.11", + "rand", + "secp256k1 0.28.2", + "serde", + "serde_json", + "sha2", + "strata-bridge-primitives", + "strata-rpc-types", + "tokio", +] + +[[package]] +name = "strata-rpc-types" +version = "0.1.0" +source = "git+/~https://github.com/alpenlabs/strata.git?branch=bitvm2#fcc7d616d924d0c8cb21f707b47717f0f12b2f3f" +dependencies = [ + "bitcoin 0.32.3", + "hex", + "jsonrpsee", + "serde", + "serde_json", + "serde_with", + "strata-db", + "strata-state", + "thiserror", +] + +[[package]] +name = "strata-sp1-adapter" +version = "0.1.0" +source = "git+/~https://github.com/alpenlabs/strata.git?branch=bitvm2#c93aeece9c92d3565d6fa52f1d99ff7f012b9e03" +dependencies = [ + "anyhow", + "bincode", + "borsh", + "hex", + "num-bigint 0.4.6", + "num-traits", + "serde", + "serde_json", + "sha2", + "snark-bn254-verifier 1.0.2 (git+/~https://github.com/succinctlabs/snark-bn254-verifier.git?branch=bhargav/groth16-verf)", + "sp1-sdk", + "strata-primitives", "strata-zkvm", + "substrate-bn", + "tracing", ] [[package]] name = "strata-state" version = "0.1.0" -source = "git+/~https://github.com/alpenlabs/strata.git?branch=releases/0.1.0#b7078bd625a178b4ab93491e5c8cbfbaaa8a3ba8" +source = "git+/~https://github.com/alpenlabs/strata.git?branch=bitvm2#fcc7d616d924d0c8cb21f707b47717f0f12b2f3f" dependencies = [ "arbitrary", "bitcoin 0.32.3", @@ -7922,6 +8664,49 @@ dependencies = [ "tracing", ] +[[package]] +name = "strata-status" +version = "0.1.0" +source = "git+/~https://github.com/alpenlabs/strata.git?branch=bitvm2#c93aeece9c92d3565d6fa52f1d99ff7f012b9e03" +dependencies = [ + "strata-primitives", + "strata-rpc-types", + "strata-state", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "strata-storage" +version = "0.1.0" +source = "git+/~https://github.com/alpenlabs/strata.git?branch=bitvm2#c93aeece9c92d3565d6fa52f1d99ff7f012b9e03" +dependencies = [ + "anyhow", + "async-trait", + "bitcoin 0.32.3", + "lru", + "paste", + "strata-db", + "strata-primitives", + "strata-state", + "threadpool", + "tokio", + "tracing", +] + +[[package]] +name = "strata-tasks" +version = "0.1.0" +source = "git+/~https://github.com/alpenlabs/strata.git?branch=bitvm2#c93aeece9c92d3565d6fa52f1d99ff7f012b9e03" +dependencies = [ + "anyhow", + "futures-util", + "thiserror", + "tokio", + "tracing", +] + [[package]] name = "strata-test-utils" version = "0.1.0" @@ -7932,10 +8717,28 @@ dependencies = [ "tracing", ] +[[package]] +name = "strata-tx-parser" +version = "0.1.0" +source = "git+/~https://github.com/alpenlabs/strata.git?branch=bitvm2#c93aeece9c92d3565d6fa52f1d99ff7f012b9e03" +dependencies = [ + "anyhow", + "arbitrary", + "bitcoin 0.32.3", + "borsh", + "hex", + "musig2 0.1.0", + "strata-bridge-tx-builder", + "strata-primitives", + "strata-state", + "thiserror", + "tracing", +] + [[package]] name = "strata-zkvm" version = "0.1.0" -source = "git+/~https://github.com/alpenlabs/strata.git?branch=releases/0.1.0#b7078bd625a178b4ab93491e5c8cbfbaaa8a3ba8" +source = "git+/~https://github.com/alpenlabs/strata.git?branch=bitvm2#fcc7d616d924d0c8cb21f707b47717f0f12b2f3f" dependencies = [ "anyhow", "arbitrary", @@ -7968,7 +8771,23 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.85", + "syn 2.0.87", +] + +[[package]] +name = "substrate-bn" +version = "0.7.0" +source = "git+/~https://github.com/sp1-patches/bn?branch=patch-v0.7.0#3c53d2561492f26b9428c1d37d134031d0156152" +dependencies = [ + "bytemuck", + "byteorder", + "cfg-if", + "crunchy", + "lazy_static", + "num-bigint 0.4.6", + "rand", + "rustc-hex", + "sp1-lib", ] [[package]] @@ -8016,9 +8835,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.85" +version = "2.0.87" source = "registry+/~https://github.com/rust-lang/crates.io-index" -checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -8034,7 +8853,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -8046,7 +8865,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -8148,22 +8967,22 @@ checksum = "a38c90d48152c236a3ab59271da4f4ae63d678c5d7ad6b7714d7cb9760be5e4b" [[package]] name = "thiserror" -version = "1.0.65" +version = "1.0.66" source = "registry+/~https://github.com/rust-lang/crates.io-index" -checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" +checksum = "5d171f59dbaa811dbbb1aee1e73db92ec2b122911a48e1390dfe327a821ddede" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.65" +version = "1.0.66" source = "registry+/~https://github.com/rust-lang/crates.io-index" -checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" +checksum = "b08be0f17bd307950653ce45db00cd31200d82b624b36e181337d9c7d92765b5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -8298,7 +9117,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -8390,6 +9209,7 @@ checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" dependencies = [ "bytes", "futures-core", + "futures-io", "futures-sink", "pin-project-lite", "tokio", @@ -8503,7 +9323,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -8654,7 +9474,7 @@ checksum = "560b82d656506509d43abe30e0ba64c56b1953ab3d4fe7ba5902747a7a3cedd5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -8901,7 +9721,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", "wasm-bindgen-shared", ] @@ -8935,7 +9755,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -9320,7 +10140,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -9340,7 +10160,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -9397,8 +10217,3 @@ dependencies = [ "cc", "pkg-config", ] - -[[patch.unused]] -name = "ark-r1cs-std" -version = "0.4.0" -source = "git+/~https://github.com/Antalpha-Labs/r1cs-std/#255ff9408e22316156009c8606ed60b34ffbfd18" diff --git a/Cargo.toml b/Cargo.toml index deac818..11844fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,33 +1,68 @@ [workspace] members = [ + "crates/agent", "crates/btcio", + "crates/db", + "crates/primitives", "crates/proof-impl/bitvm-bridge", + "crates/proof-impl/prover-manager", + "crates/rpc/bridge", + "crates/rpc/strata", + "crates/proof-impl/test-utils", "crates/tx-graph", "bridge-proofs", # binaries listed separately + "bin/strata-bridge", # test utilities "crates/test-utils", ] -default-members = [] +default-members = ["bin/strata-bridge"] resolver = "2" [workspace.dependencies] -# imported from strata +# deps in this workspace +prover-test-utils = { path = "crates/proof-impl/test-utils" } +strata-bridge-agent = { path = "crates/agent" } strata-bridge-btcio = { path = "crates/btcio" } - -# new crates +strata-bridge-db = { path = "crates/db" } +strata-bridge-guest-builder = { path = "bridge-proofs" } +strata-bridge-primitives = { path = "crates/primitives" } +strata-bridge-rpc = { path = "crates/rpc/bridge" } strata-bridge-tx-graph = { path = "crates/tx-graph" } strata-proofimpl-bitvm-bridge = { path = "crates/proof-impl/bitvm-bridge" } +strata-rpc = { path = "crates/rpc/strata" } strata-test-utils = { path = "crates/test-utils" } +# deps from original strata repo +shrex = { git = "/~https://github.com/alpenlabs/strata.git", branch = "bitvm2", features = [ + "serde", +] } +strata-bridge-tx-builder = { git = "/~https://github.com/alpenlabs/strata.git", branch = "bitvm2" } +strata-common = { git = "/~https://github.com/alpenlabs/strata.git", branch = "bitvm2" } +strata-primitives = { git = "/~https://github.com/alpenlabs/strata.git", branch = "bitvm2" } +strata-rpc-api = { git = "/~https://github.com/alpenlabs/strata.git", branch = "bitvm2", features = [ + "client", +] } +strata-rpc-types = { git = "/~https://github.com/alpenlabs/strata.git", branch = "bitvm2" } +strata-sp1-adapter = { git = "/~https://github.com/alpenlabs/strata.git", branch = "bitvm2" } +strata-state = { git = "/~https://github.com/alpenlabs/strata.git", branch = "bitvm2" } +strata-zkvm = { git = "/~https://github.com/alpenlabs/strata.git", branch = "bitvm2" } + # external deps anyhow = "1.0.86" arbitrary = { version = "1.3.2", features = ["derive"] } -argh = "0.1" +ark-bn254 = { git = "/~https://github.com/chainwayxyz/algebra/", branch = "new-ate-loop" } +ark-crypto-primitives = { git = "/~https://github.com/arkworks-rs/crypto-primitives/" } +ark-ec = { git = "/~https://github.com/chainwayxyz/algebra/", branch = "new-ate-loop" } +ark-ff = { git = "/~https://github.com/chainwayxyz/algebra/", branch = "new-ate-loop" } +ark-groth16 = { git = "/~https://github.com/arkworks-rs/groth16" } +ark-r1cs-std = { git = "/~https://github.com/arkworks-rs/r1cs-std/" } +ark-relations = { git = "/~https://github.com/arkworks-rs/snark/" } +ark-std = { git = "/~https://github.com/arkworks-rs/std/" } async-trait = "0.1.81" aws-sdk-s3 = "1.40.0" base64 = "0.22.1" @@ -42,6 +77,7 @@ bitvm = { git = "/~https://github.com/alpenlabs/BitVM.git", branch = "bridge-poc" borsh = { version = "1.5.0", features = ["derive"] } bytes = "1.6.0" chrono = "0.4.38" +clap = { version = "4.5.20", features = ["cargo", "derive"] } digest = "0.10" dotenvy = "0.15.7" esplora-client = { git = "/~https://github.com/BitVM/rust-esplora-client", default-features = false, features = [ @@ -91,15 +127,6 @@ serde_json = { version = "1.0", default-features = false, features = [ ] } serde_with = "3.9.0" sha2 = "0.10" -shrex = { git = "/~https://github.com/alpenlabs/strata.git", branch = "releases/0.1.0", features = [ - "serde", -] } -strata-bridge-tx-builder = { git = "/~https://github.com/alpenlabs/strata.git", branch = "releases/0.1.0" } -strata-common = { git = "/~https://github.com/alpenlabs/strata.git", branch = "releases/0.1.0" } -strata-primitives = { git = "/~https://github.com/alpenlabs/strata.git", branch = "releases/0.1.0" } -strata-sp1-adapter = { git = "/~https://github.com/alpenlabs/strata.git", branch = "releases/0.1.0" } -strata-state = { git = "/~https://github.com/alpenlabs/strata.git", branch = "releases/0.1.0" } -strata-zkvm = { git = "/~https://github.com/alpenlabs/strata.git", branch = "releases/0.1.0" } suppaftp = { version = "6.0.1", features = ["async", "async-native-tls"] } tempfile = "3.10.1" terrors = "0.3.0" @@ -116,22 +143,21 @@ tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } opt-level = 3 [patch.crates-io] +ark-r1cs-std = { git = "/~https://github.com/arkworks-rs/r1cs-std/" } +ark-ff = { git = "/~https://github.com/chainwayxyz/algebra/", branch = "new-ate-loop" } base58check = { git = "/~https://github.com/rust-bitcoin/rust-bitcoin", branch = "bitvm" } bitcoin = { git = "/~https://github.com/rust-bitcoin/rust-bitcoin", branch = "bitvm" } bitcoin_hashes = { git = "/~https://github.com/rust-bitcoin/rust-bitcoin", branch = "bitvm" } bitcoin-internals = { git = "/~https://github.com/rust-bitcoin/rust-bitcoin", branch = "bitvm" } bitcoin-io = { git = "/~https://github.com/rust-bitcoin/rust-bitcoin", branch = "bitvm" } # bitcoin-units = { git = "/~https://github.com/rust-bitcoin/rust-bitcoin", branch = "bitvm" } # causes compilation issues with const fns +ark-ec = { git = "/~https://github.com/chainwayxyz/algebra/", branch = "new-ate-loop" } +ark-poly = { git = "/~https://github.com/chainwayxyz/algebra/", branch = "new-ate-loop" } +ark-serialize = { git = "/~https://github.com/chainwayxyz/algebra/", branch = "new-ate-loop" } +ark-bn254 = { git = "/~https://github.com/chainwayxyz/algebra/", branch = "new-ate-loop" } +ark-crypto-primitives = { git = "/~https://github.com/arkworks-rs/crypto-primitives/" } -ark-ff = { git = "/~https://github.com/Antalpha-Labs/algebra/" } -ark-ec = { git = "/~https://github.com/Antalpha-Labs/algebra/" } -ark-poly = { git = "/~https://github.com/Antalpha-Labs/algebra/" } -ark-serialize = { git = "/~https://github.com/Antalpha-Labs/algebra" } +ark-relations = { git = "/~https://github.com/arkworks-rs/snark/" } +ark-snark = { git = "/~https://github.com/arkworks-rs/snark/" } +ark-groth16 = { git = "/~https://github.com/arkworks-rs/groth16" } ark-std = { git = "/~https://github.com/arkworks-rs/std/" } - -ark-r1cs-std = { git = "/~https://github.com/Antalpha-Labs/r1cs-std/" } -ark-crypto-primitives = { git = "/~https://github.com/Antalpha-Labs/crypto-primitives/" } - -ark-relations = { git = "/~https://github.com/Antalpha-Labs/snark/" } -ark-snark = { git = "/~https://github.com/Antalpha-Labs/snark/" } -ark-groth16 = { git = "/~https://github.com/Antalpha-Labs/groth16" } diff --git a/Makefile b/Makefile index 610b734..3332d9f 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,7 @@ .DEFAULT_GOAL := help GIT_TAG ?= $(shell git describe --tags --abbrev=0) +TIMESTAMP ?= $(shell date +%s) BUILD_PATH = "target" @@ -9,7 +10,7 @@ DOCKER_DIR = docker DOCKER_DATADIR = data # Cargo profile for builds. Default is for local builds, CI uses an override. -PROFILE ?= release +PROFILE ?= dev # Extra flags for Cargo CARGO_INSTALL_EXTRA_FLAGS ?= @@ -183,3 +184,24 @@ pr: lint sec rustdocs test-doc test-unit test-functional ## Runs lints (without @test -z "$$(git status --porcelain)" || echo "WARNNG: You have uncommitted changes" @echo "All good to create a PR!" + +.PHONY: run +run: + RUST_LOG=trace,soketto=error,strata_bridge_db=warn,strata_bridge_tx_graph=warn,strata_bridge_btcio=warn,strata_bridge_agent=debug,hyper_util=error,jsonrpsee=error \ + cargo r \ + --bin strata-bridge \ + --profile "$(PROFILE)" \ + -- \ + --rpc-port 4782 \ + --strata-url ws://localhost:8432 \ + --btc-url http://localhost:18443 \ + --btc-user rpcuser \ + --btc-pass rpcpassword \ + --btc-genesis-height 210 \ + --btc-scan-interval 390 \ + --fault-tolerance 0 \ + --duty-interval 30000 \ + --num-threads 4 \ + --stack-size 512 \ + --xpriv-file .secrets/xprivs.bin 2>&1 | tee run.log.$(TIMESTAMP) + diff --git a/bin/strata-bridge/Cargo.toml b/bin/strata-bridge/Cargo.toml new file mode 100644 index 0000000..9aad821 --- /dev/null +++ b/bin/strata-bridge/Cargo.toml @@ -0,0 +1,36 @@ +[package] +edition = "2021" +name = "strata-bridge" +version = "0.1.0" + +[[bin]] +name = "strata-bridge" +path = "src/main.rs" + +[dependencies] +strata-bridge-agent.workspace = true +strata-bridge-btcio.workspace = true +strata-bridge-db.workspace = true +strata-bridge-primitives.workspace = true +strata-bridge-rpc.workspace = true +strata-bridge-tx-graph.workspace = true +strata-rpc.workspace = true + +strata-common.workspace = true + +anyhow.workspace = true +async-trait.workspace = true +bitcoin.workspace = true +chrono.workspace = true +clap.workspace = true +esplora-client.workspace = true +hex.workspace = true +jsonrpsee = { workspace = true, features = ["client", "server"] } +rand.workspace = true +secp256k1 = { workspace = true, features = ["global-context", "rand-std"] } +thiserror.workspace = true +tokio.workspace = true +tracing.workspace = true + +[features] +mock = ["strata-bridge-agent/mock"] diff --git a/bin/strata-bridge/src/bootstrap.rs b/bin/strata-bridge/src/bootstrap.rs new file mode 100644 index 0000000..2221776 --- /dev/null +++ b/bin/strata-bridge/src/bootstrap.rs @@ -0,0 +1,205 @@ +//! Module to bootstrap the operator node by hooking up all the required services. + +use std::sync::Arc; + +use bitcoin::{Address, Network}; +use jsonrpsee::ws_client::WsClientBuilder; +use rand::{rngs::OsRng, Rng}; +use secp256k1::{Keypair, SECP256K1}; +use strata_bridge_agent::{ + base::Agent, + bitcoin_watcher::BitcoinWatcher, + duty_watcher::{DutyWatcher, DutyWatcherConfig}, + operator::Operator, + signal::{CovenantNonceSignal, CovenantSignatureSignal, DepositSignal}, + verifier::{Verifier, VerifierDuty}, +}; +use strata_bridge_btcio::{traits::Reader, BitcoinClient}; +use strata_bridge_db::{operator::OperatorDb, public::PublicDb}; +use strata_bridge_primitives::{ + build_context::{BuildContext, TxBuildContext}, + duties::BridgeDuty, + types::PublickeyTable, +}; +use strata_rpc::StrataApiClient; +use tokio::{sync::broadcast, task::JoinSet}; +use tracing::{debug, error, info}; + +use crate::{ + cli::Cli, + constants::{ + COVENANT_QUEUE_MULTIPLIER, DEPOSIT_QUEUE_MULTIPLIER, DUTY_QUEUE_SIZE, + VERIFIER_DUTY_QUEUE_SIZE, + }, + rpc_server::{self, BridgeRpc}, + xpriv::get_keypairs_and_load_xpriv, +}; + +pub(crate) async fn bootstrap(args: Cli) { + // instantiate RPC client for Strata and Bitcoin + let strata_rpc_client = WsClientBuilder::default() + .request_timeout(args.strata_ws_timeout) + .build(args.strata_url.as_str()) + .await + .expect("failed to connect to the strata RPC server"); + + let bitcoin_rpc_client = Arc::new( + BitcoinClient::new(&args.btc_url, &args.btc_user, &args.btc_pass) + .expect("should be able to create bitcoin client"), + ); + + let network = bitcoin_rpc_client + .network() + .await + .expect("should be able to get network information"); + + debug!(action = "querying for operator pubkey set"); + let pubkey_table = strata_rpc_client + .get_active_operator_chain_pubkey_set() + .await + .expect("should be able to fetch pubkey table"); + + let duty_watcher_config = DutyWatcherConfig { + poll_interval: args.duty_interval, + }; + + let mut duty_watcher = DutyWatcher::new(duty_watcher_config, Arc::new(strata_rpc_client)); + + let (duty_sender, _duty_receiver) = broadcast::channel::(DUTY_QUEUE_SIZE); + + // initialize public database + let public_db = PublicDb::default(); + debug!(event = "initialized public db"); + + public_db.set_musig_pubkey_table(&pubkey_table.0).await; + + let operators = + generate_operator_set(&args, pubkey_table.clone(), public_db.clone(), network).await; + + let mut tasks = JoinSet::new(); + + let duty_sender_copy = duty_sender.clone(); + info!(action = "starting duty watcher", poll_interval=?args.duty_interval); + tasks.spawn(async move { + duty_watcher.start(duty_sender_copy.clone()).await; + }); + + info!(action = "starting operators"); + for operator in operators { + let mut duty_receiver = duty_sender.subscribe(); + + tasks.spawn(async move { + let mut operator = operator; + operator.start(&mut duty_receiver).await; + }); + } + + // spawn verifier and bitcoin watcher + let (notification_sender, mut notification_receiver) = + broadcast::channel::(VERIFIER_DUTY_QUEUE_SIZE); + + let bitcoin_watcher = BitcoinWatcher::new( + public_db.clone(), + bitcoin_rpc_client.clone(), + args.btc_scan_interval, + args.btc_genesis_height, + ); + + let keypair = Keypair::new(SECP256K1, &mut rand::thread_rng()); + let agent = Agent::new(keypair, &args.btc_url, &args.btc_user, &args.btc_pass); + + let verifier_build_context = TxBuildContext::new(network, pubkey_table, u32::MAX); // operator_id + // does not matter for verifier + let mut verifier = Verifier::new(public_db.clone(), verifier_build_context, agent); + + tasks.spawn(async move { + verifier.start(&mut notification_receiver).await; + }); + + tasks.spawn(async move { bitcoin_watcher.start(notification_sender).await }); + + // spawn rpc server + let bridge_rpc = BridgeRpc::new(); + + let rpc_host = args.rpc_host.clone(); + let rpc_port = args.rpc_port; + let rpc_addr = format!("{rpc_host}:{rpc_port}"); + + info!(action = "starting rpc server"); + tasks.spawn(async move { + if let Err(e) = rpc_server::start(&bridge_rpc, rpc_addr.as_str()).await { + error!(error = %e, "could not start RPC server"); + } + }); + + tasks.join_all().await; +} + +pub async fn generate_operator_set( + args: &Cli, + pubkey_table: PublickeyTable, + public_db: PublicDb, + network: Network, +) -> Vec { + let operator_indexes_and_keypairs = + get_keypairs_and_load_xpriv(&args.xpriv_file, &pubkey_table); + + let num_operators = operator_indexes_and_keypairs.len(); + + assert!( + num_operators == pubkey_table.0.len(), + "operator count in strata and number of xprivs do not match" + ); + + let deposit_queue_size = num_operators * DEPOSIT_QUEUE_MULTIPLIER; // buffer for nonces and signatures (overkill) + let (deposit_signal_sender, _deposit_signal_receiver) = + broadcast::channel::(deposit_queue_size); + + let covenant_queue_size = num_operators * COVENANT_QUEUE_MULTIPLIER; // higher 'cause nonces and signatures are sent in bulk + let (covenant_nonce_signal_sender, _covenant_nonce_signal_receiver) = + broadcast::channel::(covenant_queue_size); + + let (covenant_sig_signal_sender, _covenant_sig_signal_receiver) = + broadcast::channel::(covenant_queue_size); + + let mut faulty_idxs = Vec::new(); + let mut operator_set: Vec = Vec::with_capacity(num_operators); + + for (operator_idx, keypair) in operator_indexes_and_keypairs { + let agent = Agent::new(keypair, &args.btc_url, &args.btc_user, &args.btc_pass); + + let build_context = TxBuildContext::new(network, pubkey_table.clone(), operator_idx); + + let aggregated_pubkey = build_context.aggregated_pubkey(); + let bridge_address = Address::p2tr(SECP256K1, aggregated_pubkey, None, network); + info!(event = "build context initialized", %bridge_address, %network, %aggregated_pubkey); + + let is_faulty = OsRng.gen_ratio(args.fault_tolerance as u32, 100); + + if is_faulty { + faulty_idxs.push(operator_idx); + } + + let operator_db = OperatorDb::default(); + let operator = Operator::new( + agent, + build_context, + is_faulty, + operator_db, + public_db.clone(), + deposit_signal_sender.clone(), + deposit_signal_sender.subscribe(), + covenant_nonce_signal_sender.clone(), + covenant_nonce_signal_sender.subscribe(), + covenant_sig_signal_sender.clone(), + covenant_sig_signal_sender.subscribe(), + ) + .await; + + operator_set.push(operator); + } + + info!(event = "operator set initialization complete", %num_operators, num_faulty_operators=%faulty_idxs.len(), ?faulty_idxs); + + operator_set +} diff --git a/bin/strata-bridge/src/cli.rs b/bin/strata-bridge/src/cli.rs new file mode 100644 index 0000000..4877570 --- /dev/null +++ b/bin/strata-bridge/src/cli.rs @@ -0,0 +1,92 @@ +//! Parses command-line arguments for the bridge-client CLI. + +use std::{path::PathBuf, time::Duration}; + +use clap::{crate_version, Parser}; + +use crate::constants::{ + DEFAULT_NUM_THREADS, DEFAULT_RPC_HOST, DEFAULT_RPC_PORT, DEFAULT_STACK_SIZE_MB, +}; + +#[derive(Debug, Parser)] +#[clap( + name = "strata-bridge", + about = "The bridge node for Strata", + version = crate_version!() +)] +pub(crate) struct Cli { + #[clap(long, help = "ws URL of the rollup RPC server")] + pub strata_url: String, + + #[clap(long, help = "Request timeout for websocket connection to strata (in secs)", default_value = "300", value_parser = parse_duration)] + pub strata_ws_timeout: Duration, + + #[clap(long, help = "URL of the bitcoind node")] + pub btc_url: String, + + #[clap(long, help = "Bitcoind username")] + pub btc_user: String, + + #[clap(long, help = "Bitcoind password")] + pub btc_pass: String, + + #[clap( + long, + default_value = "1", + help = "Bridge duty polling interval (in milliseconds)", + value_parser = parse_duration, + )] + pub btc_scan_interval: Duration, + + #[clap(long, default_value_t = 0, help = "Block height to start scans from")] + pub btc_genesis_height: u32, + + #[clap(long, help = "RPC server host for the bridge node", default_value_t = DEFAULT_RPC_HOST.to_string())] + pub rpc_host: String, + + #[clap(long, help = "RPC server host for the bridge node", default_value_t = DEFAULT_RPC_PORT)] + pub rpc_port: u32, + + #[clap( + long, + help = "percentage of operators that are faulty (max: 100)", + default_value_t = 33, + value_parser = parse_fault_tolerance, + )] + pub fault_tolerance: u8, + + #[clap( + long, + default_value = "30", + help = "Bridge duty polling interval (in milliseconds)", + value_parser = parse_duration, + )] + pub duty_interval: Duration, + + #[clap( + long, + help = "The file containing the list of client privkeys to use (one per line)", + default_value = "xpriv.bin" + )] + pub xpriv_file: PathBuf, + + #[clap(long, help = "The number of tokio threads to use", default_value_t = DEFAULT_NUM_THREADS)] + pub num_threads: usize, + + #[clap(long, help = "The stack size per thread (in MB)", default_value_t = DEFAULT_STACK_SIZE_MB)] + pub stack_size: usize, +} + +fn parse_duration(arg: &str) -> Result { + let seconds = arg.parse()?; + + Ok(std::time::Duration::from_millis(seconds)) +} + +fn parse_fault_tolerance(arg: &str) -> anyhow::Result { + let value: u8 = arg.parse()?; + + let value = value.min(100); + + Ok(value) +} diff --git a/bin/strata-bridge/src/constants.rs b/bin/strata-bridge/src/constants.rs new file mode 100644 index 0000000..6205ab1 --- /dev/null +++ b/bin/strata-bridge/src/constants.rs @@ -0,0 +1,11 @@ +pub const DEFAULT_RPC_HOST: &str = "127.0.0.1"; +pub const DEFAULT_RPC_PORT: u32 = 4781; + +pub const DUTY_QUEUE_SIZE: usize = 5; // probably overkill +pub const DEPOSIT_QUEUE_MULTIPLIER: usize = 2; +pub const COVENANT_QUEUE_MULTIPLIER: usize = 10; + +pub const VERIFIER_DUTY_QUEUE_SIZE: usize = 20; // only one verifier so needs a lot of buffer + +pub const DEFAULT_NUM_THREADS: usize = 3; +pub const DEFAULT_STACK_SIZE_MB: usize = 512; diff --git a/bin/strata-bridge/src/main.rs b/bin/strata-bridge/src/main.rs new file mode 100644 index 0000000..cbe0799 --- /dev/null +++ b/bin/strata-bridge/src/main.rs @@ -0,0 +1,37 @@ +//! Bridge Operator client. +//! +//! Responsible for facilitating bridge-in and bridge-out operations by creating, storing and +//! publishing appropriate transactions. Can also perform challenger duties. + +mod bootstrap; +mod cli; +mod constants; +mod rpc_server; +pub mod xpriv; + +use bootstrap::bootstrap; +use clap::Parser; +use cli::Cli; +use strata_common::logging; +use tracing::{info, trace}; + +fn main() { + logging::init(); + + let cli_args: Cli = Cli::parse(); + + info!("starting node"); + trace!(action = "creating runtime", num_threads = %cli_args.num_threads, stack_size_per_thread_mb = %cli_args.stack_size); + + const NUM_BYTES_PER_MB: usize = 1024 * 1024; + let runtime = tokio::runtime::Builder::new_multi_thread() + .worker_threads(cli_args.num_threads) + .thread_stack_size(cli_args.stack_size * NUM_BYTES_PER_MB) + .enable_all() + .build() + .expect("must be able to create runtime"); + + runtime.block_on(async { + bootstrap(cli_args).await; + }); +} diff --git a/bin/strata-bridge/src/rpc_server.rs b/bin/strata-bridge/src/rpc_server.rs new file mode 100644 index 0000000..f6bd079 --- /dev/null +++ b/bin/strata-bridge/src/rpc_server.rs @@ -0,0 +1,77 @@ +//! Bootstraps an RPC server for the operator. + +use anyhow::Context; +use async_trait::async_trait; +use chrono::{DateTime, Utc}; +use jsonrpsee::{core::RpcResult, RpcModule}; +use strata_bridge_rpc::StrataBridgeControlApiServer; +use tokio::sync::oneshot; +use tracing::{info, warn}; + +pub(crate) async fn start(rpc_impl: &T, rpc_addr: &str) -> anyhow::Result<()> +where + T: StrataBridgeControlApiServer + Clone + Sync + Send, +{ + let mut rpc_module = RpcModule::new(rpc_impl.clone()); + + let control_api = StrataBridgeControlApiServer::into_rpc(rpc_impl.clone()); + + rpc_module.merge(control_api).context("merge control api")?; + + info!("Starting bridge RPC server at: {rpc_addr}"); + let rpc_server = jsonrpsee::server::ServerBuilder::new() + .build(&rpc_addr) + .await + .expect("build bridge rpc server"); + + let rpc_handle = rpc_server.start(rpc_module); + // Using `_` for `_stop_tx` as the variable causes it to be dropped immediately! + // NOTE: The `_stop_tx` should be used by the shutdown manager (see the `strata-tasks` crate). + // At the moment, the impl below just stops the client from stopping. + let (_stop_tx, stop_rx): (oneshot::Sender, oneshot::Receiver) = oneshot::channel(); + + info!("bridge RPC server started at: {rpc_addr}"); + + let _ = stop_rx.await; + info!("stopping RPC server"); + + if rpc_handle.stop().is_err() { + warn!("rpc server already stopped"); + } + + Ok(()) +} + +/// Struct to implement the [`strata_bridge_rpc_api::StrataBridgeNetworkApiServer`] on. Contains +/// fields corresponding the global context for the RPC. +#[derive(Clone)] +pub(crate) struct BridgeRpc { + start_time: DateTime, +} + +impl BridgeRpc { + pub fn new() -> Self { + Self { + start_time: Utc::now(), + } + } +} + +#[async_trait] +impl StrataBridgeControlApiServer for BridgeRpc { + async fn get_uptime(&self) -> RpcResult { + let current_time = Utc::now().timestamp(); + let start_time = self.start_time.timestamp(); + + // The user might care about their system time being incorrect. + if current_time <= start_time { + return Err(jsonrpsee::types::ErrorObjectOwned::owned::<_>( + -32000, + "system time may be inaccurate", // `start_time` may have been incorrect too + Some(current_time.saturating_sub(start_time)), + )); + } + + Ok(current_time.abs_diff(start_time)) + } +} diff --git a/bin/strata-bridge/src/xpriv.rs b/bin/strata-bridge/src/xpriv.rs new file mode 100644 index 0000000..3e0ecf1 --- /dev/null +++ b/bin/strata-bridge/src/xpriv.rs @@ -0,0 +1,76 @@ +//! Descriptor parsing utilities. + +use std::{fs, path::Path}; + +use bitcoin::bip32::{ChildNumber, DerivationPath, Xpriv}; +use secp256k1::{Keypair, Parity, PublicKey, SecretKey, XOnlyPublicKey, SECP256K1}; +use strata_bridge_agent::operator::OperatorIdx; +use strata_bridge_primitives::types::PublickeyTable; +use tracing::info; + +const DERIV_BASE_IDX: u32 = 56; +const DERIV_OP_IDX: u32 = 20; +const DERIV_OP_SIGNING_IDX: u32 = 100; +const DERIV_OP_WALLET_IDX: u32 = 101; + +/// Derives the signing and wallet xprivs for a Strata operator. +pub fn derive_op_purpose_xprivs(root: &Xpriv) -> anyhow::Result<(Xpriv, Xpriv)> { + let signing_path = DerivationPath::master().extend([ + ChildNumber::from_hardened_idx(DERIV_BASE_IDX).unwrap(), + ChildNumber::from_hardened_idx(DERIV_OP_IDX).unwrap(), + ChildNumber::from_normal_idx(DERIV_OP_SIGNING_IDX).unwrap(), + ]); + + let wallet_path = DerivationPath::master().extend([ + ChildNumber::from_hardened_idx(DERIV_BASE_IDX).unwrap(), + ChildNumber::from_hardened_idx(DERIV_OP_IDX).unwrap(), + ChildNumber::from_normal_idx(DERIV_OP_WALLET_IDX).unwrap(), + ]); + + let signing_xpriv = root.derive_priv(bitcoin::secp256k1::SECP256K1, &signing_path)?; + let wallet_xpriv = root.derive_priv(bitcoin::secp256k1::SECP256K1, &wallet_path)?; + + Ok((signing_xpriv, wallet_xpriv)) +} + +pub fn get_keypairs_and_load_xpriv( + xpriv_file: impl AsRef, + pubkey_table: &PublickeyTable, +) -> Vec<(OperatorIdx, Keypair)> { + let xprivs = fs::read_to_string(xpriv_file).expect("must be able to read xpriv file"); + + let mut indexes_and_keypairs: Vec<(OperatorIdx, Keypair)> = + Vec::with_capacity(pubkey_table.0.len()); + + for xpriv_str in xprivs.lines() { + // Get the keypair after deriving the wallet xpriv. + let master_xpriv = xpriv_str.parse::().expect("could not parse xpriv"); + let (_, wallet_xpriv) = derive_op_purpose_xprivs(&master_xpriv) + .expect("should be able to derive xprivs from master xpriv"); + + let mut keypair = wallet_xpriv.to_keypair(SECP256K1); + let mut sk = SecretKey::from_keypair(&keypair); + + // adjust for parity, which should always be even + let (_, parity) = XOnlyPublicKey::from_keypair(&keypair); + if matches!(parity, Parity::Odd) { + sk = sk.negate(); + keypair = Keypair::from_secret_key(SECP256K1, &sk); + }; + + let pubkey = PublicKey::from_secret_key(SECP256K1, &sk); + + // Get this client's pubkey from the bitcoin wallet. + let own_index: OperatorIdx = pubkey_table + .0 + .iter() + .find_map(|(id, pk)| if pk == &pubkey { Some(*id) } else { None }) + .expect("could not find this operator's pubkey in the rollup pubkey table"); + + indexes_and_keypairs.push((own_index, keypair)); + } + + info!(event = "parsed keypairs and operator indexes", operator_count=%indexes_and_keypairs.len()); + + indexes_and_keypairs +} diff --git a/bridge-proofs/guest-bridge/Cargo.lock b/bridge-proofs/guest-bridge/Cargo.lock index 5c57a27..f8d34c2 100644 --- a/bridge-proofs/guest-bridge/Cargo.lock +++ b/bridge-proofs/guest-bridge/Cargo.lock @@ -14,6 +14,15 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "allocator-api2" version = "0.2.18" @@ -22,12 +31,12 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "alloy-chains" -version = "0.1.42" +version = "0.1.46" source = "registry+/~https://github.com/rust-lang/crates.io-index" -checksum = "dca4a1469a3e572e9ba362920ff145f5d0a00a3e71a64ddcb4a3659cf64c76a7" +checksum = "836cf02383d9ebb35502d379bcd1ae803155094077eaab9c29131d888cd5fa3e" dependencies = [ - "alloy-primitives 0.8.9", - "num_enum", + "alloy-primitives 0.8.10", + "num_enum 0.7.3", "serde", "strum", ] @@ -97,9 +106,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.8.9" +version = "0.8.10" source = "registry+/~https://github.com/rust-lang/crates.io-index" -checksum = "c71738eb20c42c5fb149571e76536a0f309d142f3957c28791662b96baf77a3d" +checksum = "8edae627382349b56cd6a7a2106f4fd69b243a9233e560c55c2e03cabb7e1d3c" dependencies = [ "alloy-rlp", "bytes", @@ -108,7 +117,7 @@ dependencies = [ "derive_more 1.0.0", "foldhash", "hex-literal", - "indexmap", + "indexmap 2.6.0", "itoa", "k256", "keccak-asm", @@ -141,7 +150,7 @@ checksum = "2b09cae092c27b6f1bde952653a22708691802e57bfef4a2973b80bea21efd3f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -171,11 +180,35 @@ dependencies = [ "tracing", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + [[package]] name = "anyhow" -version = "1.0.91" +version = "1.0.92" source = "registry+/~https://github.com/rust-lang/crates.io-index" -checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8" +checksum = "74f37166d7d48a0284b99dd824694c26119c700b53bf0d1540cdb147dbdaaf13" [[package]] name = "arbitrary" @@ -310,6 +343,12 @@ dependencies = [ "rand", ] +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + [[package]] name = "arrayvec" version = "0.7.6" @@ -324,7 +363,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -339,25 +378,22 @@ version = "0.2.0" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" -[[package]] -name = "base58check" -version = "0.1.0" -source = "git+/~https://github.com/rust-bitcoin/rust-bitcoin?branch=bitvm#48efda928ae569a8fdee4ff5cc951646ab280205" -dependencies = [ - "bitcoin-internals 0.2.0", - "bitcoin_hashes 0.13.0", -] - [[package]] name = "base58ck" version = "0.1.0" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "2c8d66485a3a2ea485c1913c4572ce0256067a5377ac8c75c4960e1cda98605f" dependencies = [ - "bitcoin-internals 0.3.0", - "bitcoin_hashes 0.14.0", + "bitcoin-internals", + "bitcoin_hashes", ] +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "base64ct" version = "1.6.0" @@ -396,44 +432,19 @@ checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" [[package]] name = "bitcoin" -version = "0.31.0" -source = "git+/~https://github.com/rust-bitcoin/rust-bitcoin?branch=bitvm#48efda928ae569a8fdee4ff5cc951646ab280205" -dependencies = [ - "base58check", - "bech32", - "bitcoin-internals 0.2.0", - "bitcoin-io", - "bitcoin-units", - "bitcoin_hashes 0.13.0", - "hex-conservative", - "hex_lit", - "secp256k1 0.28.2", - "serde", -] - -[[package]] -name = "bitcoin" -version = "0.32.1" +version = "0.32.3" source = "registry+/~https://github.com/rust-lang/crates.io-index" -checksum = "4bf33434c870e98ecc8608588ccc990c5daba9ba9ad39733dc85fba22c211504" +checksum = "0032b0e8ead7074cda7fc4f034409607e3f03a6f71d66ade8a307f79b4d99e73" dependencies = [ "base58ck", "bech32", - "bitcoin-internals 0.3.0", + "bitcoin-internals", "bitcoin-io", "bitcoin-units", - "bitcoin_hashes 0.14.0", + "bitcoin_hashes", "hex-conservative", "hex_lit", - "secp256k1 0.29.1", - "serde", -] - -[[package]] -name = "bitcoin-internals" -version = "0.2.0" -source = "git+/~https://github.com/rust-bitcoin/rust-bitcoin?branch=bitvm#48efda928ae569a8fdee4ff5cc951646ab280205" -dependencies = [ + "secp256k1", "serde", ] @@ -448,9 +459,9 @@ dependencies = [ [[package]] name = "bitcoin-io" -version = "0.1.2" +version = "0.1.3" source = "registry+/~https://github.com/rust-lang/crates.io-index" -checksum = "340e09e8399c7bd8912f495af6aa58bea0c9214773417ffaa8f6460f93aaee56" +checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" [[package]] name = "bitcoin-units" @@ -458,17 +469,7 @@ version = "0.1.2" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "5285c8bcaa25876d07f37e3d30c303f2609179716e11d688f51e8f1fe70063e2" dependencies = [ - "bitcoin-internals 0.3.0", - "serde", -] - -[[package]] -name = "bitcoin_hashes" -version = "0.13.0" -source = "git+/~https://github.com/rust-bitcoin/rust-bitcoin?branch=bitvm#48efda928ae569a8fdee4ff5cc951646ab280205" -dependencies = [ - "bitcoin-io", - "hex-conservative", + "bitcoin-internals", "serde", ] @@ -505,13 +506,27 @@ dependencies = [ "wyz", ] +[[package]] +name = "blake3" +version = "1.5.4" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "d82033247fd8e890df8f740e407ad4d038debb9eb1f40533fffb32e7d17dc6f7" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "rayon-core", +] + [[package]] name = "block-buffer" version = "0.10.4" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -556,13 +571,19 @@ source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "c3ef8005764f53cd4dca619f5bf64cafd4664dada50ece25e4d81de54c80cc0b" dependencies = [ "once_cell", - "proc-macro-crate", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", "syn_derive", ] +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + [[package]] name = "byte-slice-cast" version = "1.2.2" @@ -574,6 +595,20 @@ name = "bytemuck" version = "1.19.0" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.8.0" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] [[package]] name = "byteorder" @@ -607,9 +642,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.31" +version = "1.1.34" source = "registry+/~https://github.com/rust-lang/crates.io-index" -checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" +checksum = "67b9470d453346108f93a59222a9a1a5724db32d0a4727b7ab7ace4b4d822dc9" dependencies = [ "shlex", ] @@ -626,6 +661,25 @@ version = "0.2.1" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "windows-targets", +] + +[[package]] +name = "cobs" +version = "0.2.3" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" + [[package]] name = "const-hex" version = "1.13.1" @@ -645,6 +699,12 @@ version = "0.9.6" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "constant_time_eq" +version = "0.3.1" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" + [[package]] name = "convert_case" version = "0.4.0" @@ -660,6 +720,12 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "cpufeatures" version = "0.2.14" @@ -721,7 +787,7 @@ version = "0.5.5" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ - "generic-array", + "generic-array 0.14.7", "rand_core", "subtle", "zeroize", @@ -733,10 +799,149 @@ version = "0.1.6" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array", + "generic-array 0.14.7", "typenum", ] +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "fiat-crypto", + "rustc_version 0.4.1", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.87", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "dashu" +version = "0.4.2" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "85b3e5ac1e23ff1995ef05b912e2b012a8784506987a2651552db2c73fb3d7e0" +dependencies = [ + "dashu-base", + "dashu-float", + "dashu-int", + "dashu-macros", + "dashu-ratio", + "rustversion", +] + +[[package]] +name = "dashu-base" +version = "0.4.1" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "c0b80bf6b85aa68c58ffea2ddb040109943049ce3fbdf4385d0380aef08ef289" + +[[package]] +name = "dashu-float" +version = "0.4.3" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "85078445a8dbd2e1bd21f04a816f352db8d333643f0c9b78ca7c3d1df71063e7" +dependencies = [ + "dashu-base", + "dashu-int", + "num-modular", + "num-order", + "rustversion", + "static_assertions", +] + +[[package]] +name = "dashu-int" +version = "0.4.1" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "ee99d08031ca34a4d044efbbb21dff9b8c54bb9d8c82a189187c0651ffdb9fbf" +dependencies = [ + "cfg-if", + "dashu-base", + "num-modular", + "num-order", + "rustversion", + "static_assertions", +] + +[[package]] +name = "dashu-macros" +version = "0.4.1" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "93381c3ef6366766f6e9ed9cf09e4ef9dec69499baf04f0c60e70d653cf0ab10" +dependencies = [ + "dashu-base", + "dashu-float", + "dashu-int", + "dashu-ratio", + "paste", + "proc-macro2", + "quote", + "rustversion", +] + +[[package]] +name = "dashu-ratio" +version = "0.4.1" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "47e33b04dd7ce1ccf8a02a69d3419e354f2bbfdf4eb911a0b7465487248764c9" +dependencies = [ + "dashu-base", + "dashu-float", + "dashu-int", + "num-modular", + "num-order", + "rustversion", +] + [[package]] name = "der" version = "0.7.9" @@ -747,6 +952,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + [[package]] name = "derivative" version = "2.2.0" @@ -766,7 +981,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -779,7 +994,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -799,7 +1014,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", "unicode-xid", ] @@ -809,7 +1024,7 @@ version = "0.9.0" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -824,6 +1039,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + [[package]] name = "dyn-clone" version = "1.0.17" @@ -844,27 +1065,18 @@ dependencies = [ "spki", ] -[[package]] -name = "ecdsa" -version = "0.16.9" -source = "git+/~https://github.com/sp1-patches/signatures?branch=patch-ecdsa-v0.16.9#475daa8834035cc170a567e7656329ab8de8cc44" -dependencies = [ - "anyhow", - "cfg-if", - "digest 0.10.7", - "elliptic-curve", - "hex-literal", - "signature", - "sp1-lib 3.0.0", - "spki", -] - [[package]] name = "either" version = "1.13.0" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +[[package]] +name = "elf" +version = "0.7.4" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "4445909572dbd556c457c849c4ca58623d84b27c8fff1e74b0b4227d8b90d17b" + [[package]] name = "elliptic-curve" version = "0.13.8" @@ -875,7 +1087,7 @@ dependencies = [ "crypto-bigint", "digest 0.10.7", "ff", - "generic-array", + "generic-array 0.14.7", "group", "pkcs8", "rand_core", @@ -884,6 +1096,39 @@ dependencies = [ "zeroize", ] +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + +[[package]] +name = "enum-map" +version = "2.7.3" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9" +dependencies = [ + "enum-map-derive", + "serde", +] + +[[package]] +name = "enum-map-derive" +version = "0.17.0" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "enumn" version = "0.1.14" @@ -892,7 +1137,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -917,6 +1162,16 @@ version = "1.5.0" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "b90ca2580b73ab6a1f724b76ca11ab632df820fd6040c336200d2c1df7b3c82c" +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + [[package]] name = "fastrand" version = "2.1.1" @@ -945,6 +1200,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + [[package]] name = "fixed-hash" version = "0.8.0" @@ -992,6 +1253,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "generic-array" +version = "1.1.0" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "96512db27971c2c3eece70a1e106fbe6c87760234e31e8f7e5634912fe52794a" +dependencies = [ + "serde", + "typenum", +] + [[package]] name = "getrandom" version = "0.2.15" @@ -1028,6 +1299,12 @@ dependencies = [ "strata-proofimpl-bitvm-bridge", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hashbrown" version = "0.14.5" @@ -1096,6 +1373,35 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "impl-codec" version = "0.6.0" @@ -1116,6 +1422,23 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + [[package]] name = "indexmap" version = "2.6.0" @@ -1124,6 +1447,7 @@ checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", "hashbrown 0.15.0", + "serde", ] [[package]] @@ -1159,6 +1483,15 @@ version = "1.0.11" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "js-sys" +version = "0.3.72" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "k256" version = "0.13.4" @@ -1166,7 +1499,7 @@ source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" dependencies = [ "cfg-if", - "ecdsa 0.16.9 (registry+/~https://github.com/rust-lang/crates.io-index)", + "ecdsa", "elliptic-curve", "once_cell", "sha2", @@ -1212,6 +1545,9 @@ name = "lazy_static" version = "1.5.0" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] [[package]] name = "libc" @@ -1221,9 +1557,9 @@ checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" [[package]] name = "libm" -version = "0.2.9" +version = "0.2.11" source = "registry+/~https://github.com/rust-lang/crates.io-index" -checksum = "3bda4c6077b0b08da2c48b172195795498381a7c8988c9e6212a6c55c5b9bd70" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" [[package]] name = "linux-raw-sys" @@ -1231,6 +1567,21 @@ version = "0.4.14" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "log" +version = "0.4.22" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "memchr" version = "2.7.4" @@ -1268,13 +1619,52 @@ dependencies = [ "hmac", "once_cell", "secp", - "secp256k1 0.29.1", + "secp256k1", "serde", "serdect", "sha2", "subtle", ] +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -1285,6 +1675,21 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-integer" version = "0.1.46" @@ -1294,6 +1699,43 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-modular" +version = "0.6.1" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "17bb261bf36fa7d83f4c294f834e91256769097b3cb505d44831e0a179ac647f" + +[[package]] +name = "num-order" +version = "1.2.0" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "537b596b97c40fcf8056d153049eb22f481c17ebce72a513ec9286e4986d1bb6" +dependencies = [ + "num-modular", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1314,13 +1756,34 @@ dependencies = [ "libc", ] +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive 0.5.11", +] + [[package]] name = "num_enum" version = "0.7.3" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" dependencies = [ - "num_enum_derive", + "num_enum_derive 0.7.3", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -1329,10 +1792,10 @@ version = "0.7.3" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -1354,6 +1817,22 @@ version = "1.20.2" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "p3-air" +version = "0.1.3-succinct" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "45e909ef66fa5d77ff0fd3cb5af4b33b27fa6fb68d02b9b1e70edbc29383e565" +dependencies = [ + "p3-field", + "p3-matrix", +] + [[package]] name = "p3-baby-bear" version = "0.1.3-succinct" @@ -1369,6 +1848,43 @@ dependencies = [ "serde", ] +[[package]] +name = "p3-blake3" +version = "0.1.3-succinct" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "36ef32d6ea21dd5cf9fec8a31bf0c64e6ceee8901dbf50966b83a443093c2aba" +dependencies = [ + "blake3", + "p3-symmetric", +] + +[[package]] +name = "p3-challenger" +version = "0.1.3-succinct" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "a6662ea899a5d848b60c699944491d72757873b5e1fd46798e4712f90a03a4e9" +dependencies = [ + "p3-field", + "p3-maybe-rayon", + "p3-symmetric", + "p3-util", + "tracing", +] + +[[package]] +name = "p3-commit" +version = "0.1.3-succinct" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "fc3563918b5cc44ef5280bf9b51753e70dc78802de25e3fb81ed6c94617ccb6e" +dependencies = [ + "itertools 0.12.1", + "p3-challenger", + "p3-field", + "p3-matrix", + "p3-util", + "serde", +] + [[package]] name = "p3-dft" version = "0.1.3-succinct" @@ -1396,6 +1912,62 @@ dependencies = [ "serde", ] +[[package]] +name = "p3-fri" +version = "0.1.3-succinct" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "c22ddb958f200d9289cc73ff68847b0167ca0c14557b791dd9e318f98c2d1b28" +dependencies = [ + "itertools 0.12.1", + "p3-challenger", + "p3-commit", + "p3-dft", + "p3-field", + "p3-interpolation", + "p3-matrix", + "p3-maybe-rayon", + "p3-util", + "serde", + "tracing", +] + +[[package]] +name = "p3-interpolation" +version = "0.1.3-succinct" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "d032cda212f6b408d7d5b0b9a8270a9455acb93742fe55a0880d82be8e90e500" +dependencies = [ + "p3-field", + "p3-matrix", + "p3-util", +] + +[[package]] +name = "p3-keccak" +version = "0.1.3-succinct" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "7c56abdd5a8a780049d2f8e92cea1df57b55a2ef50a40d1103f2732f7a00e4b1" +dependencies = [ + "p3-symmetric", + "tiny-keccak", +] + +[[package]] +name = "p3-keccak-air" +version = "0.1.3-succinct" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "e8398f1694ccc38513df0b8cab5f9ef7325423f27cd9e4fa20bdc77d5079cf1b" +dependencies = [ + "p3-air", + "p3-field", + "p3-matrix", + "p3-maybe-rayon", + "p3-util", + "tracing", + "tracing-forest", + "tracing-subscriber", +] + [[package]] name = "p3-matrix" version = "0.1.3-succinct" @@ -1416,6 +1988,9 @@ name = "p3-maybe-rayon" version = "0.1.3-succinct" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "55f5575d3d61bedb3e05681abb0f36b8bb339d65aa395d50756bfa64e9cd3f46" +dependencies = [ + "rayon", +] [[package]] name = "p3-mds" @@ -1432,6 +2007,23 @@ dependencies = [ "rand", ] +[[package]] +name = "p3-merkle-tree" +version = "0.1.3-succinct" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "af46b41cba75d483ec8a553cbab1d2d794935ae3403d75394acfa4fb2c977cce" +dependencies = [ + "itertools 0.12.1", + "p3-commit", + "p3-field", + "p3-matrix", + "p3-maybe-rayon", + "p3-symmetric", + "p3-util", + "serde", + "tracing", +] + [[package]] name = "p3-poseidon2" version = "0.1.3-succinct" @@ -1456,6 +2048,28 @@ dependencies = [ "serde", ] +[[package]] +name = "p3-uni-stark" +version = "0.1.3-succinct" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "1af5c038b22b058bf1d49fb1ea3dd6c240a3e46c3278fde5c444e0034f7ffe37" +dependencies = [ + "itertools 0.12.1", + "p3-air", + "p3-challenger", + "p3-commit", + "p3-dft", + "p3-field", + "p3-matrix", + "p3-maybe-rayon", + "p3-util", + "postcard", + "serde", + "tracing", + "tracing-forest", + "tracing-subscriber", +] + [[package]] name = "p3-util" version = "0.1.3-succinct" @@ -1494,7 +2108,7 @@ version = "3.6.12" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", "syn 1.0.109", @@ -1533,6 +2147,24 @@ dependencies = [ "spki", ] +[[package]] +name = "postcard" +version = "1.0.10" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "5f7f0a8d620d71c457dd1d47df76bb18960378da56af4527aaa10f515eee732e" +dependencies = [ + "cobs", + "embedded-io 0.4.0", + "embedded-io 0.6.1", + "serde", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.20" @@ -1553,13 +2185,23 @@ dependencies = [ "uint", ] +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + [[package]] name = "proc-macro-crate" version = "3.2.0" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ - "toml_edit", + "toml_edit 0.22.22", ] [[package]] @@ -1608,7 +2250,7 @@ dependencies = [ "rand", "rand_chacha", "rand_xorshift", - "regex-syntax", + "regex-syntax 0.8.5", "rusty-fork", "tempfile", "unarray", @@ -1695,8 +2337,55 @@ dependencies = [ ] [[package]] -name = "regex-syntax" -version = "0.8.5" +name = "rayon-scan" +version = "0.1.1" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "3f87cc11a0140b4b0da0ffc889885760c61b13672d80a908920b2c0df078fa14" +dependencies = [ + "rayon", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.8", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.8" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.5" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" @@ -1724,7 +2413,7 @@ dependencies = [ "convert_case 0.6.0", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -1763,7 +2452,7 @@ dependencies = [ "reth-static-file-types", "reth-trie-common", "revm-primitives", - "secp256k1 0.29.1", + "secp256k1", "serde", "thiserror-no-std", ] @@ -1870,6 +2559,17 @@ dependencies = [ "byteorder", ] +[[package]] +name = "rrs-succinct" +version = "0.1.0" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "3372685893a9f67d18e98e792d690017287fd17379a83d798d958e517d380fa9" +dependencies = [ + "downcast-rs", + "num_enum 0.5.11", + "paste", +] + [[package]] name = "ruint" version = "1.12.3" @@ -1932,9 +2632,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.37" +version = "0.38.38" source = "registry+/~https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +checksum = "aa260229e6538e52293eeb577aabd09945a09d6d9cc0fc550ed7529056c2e32a" dependencies = [ "bitflags", "errno", @@ -1985,10 +2685,10 @@ version = "2.11.5" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "46385cc24172cf615450267463f937c10072516359b3ff1cb24228a4a08bf951" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -1999,7 +2699,7 @@ checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct", "der", - "generic-array", + "generic-array 0.14.7", "pkcs8", "subtle", "zeroize", @@ -2013,52 +2713,29 @@ checksum = "dd426921f62c7e334ca27173743cd90a7263566a3be76ef0b47773d631633cf9" dependencies = [ "base16ct", "once_cell", - "secp256k1 0.29.1", + "secp256k1", "serde", "serdect", "subtle", ] -[[package]] -name = "secp256k1" -version = "0.28.2" -source = "registry+/~https://github.com/rust-lang/crates.io-index" -checksum = "d24b59d129cdadea20aea4fb2352fa053712e5d713eee47d700cd4b2bc002f10" -dependencies = [ - "bitcoin_hashes 0.13.0", - "rand", - "secp256k1-sys 0.9.2", - "serde", -] - [[package]] name = "secp256k1" version = "0.29.1" -source = "git+/~https://github.com/sp1-patches/rust-secp256k1?branch=patch-secp256k1-v0.29.1#ee67ba569fb05c254823921954cdf56ca1dfb659" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" dependencies = [ - "bitcoin_hashes 0.13.0", - "cfg-if", - "ecdsa 0.16.9 (git+/~https://github.com/sp1-patches/signatures?branch=patch-ecdsa-v0.16.9)", - "elliptic-curve", - "k256", + "bitcoin_hashes", "rand", - "secp256k1-sys 0.10.0", + "secp256k1-sys", "serde", ] [[package]] name = "secp256k1-sys" -version = "0.9.2" +version = "0.10.1" source = "registry+/~https://github.com/rust-lang/crates.io-index" -checksum = "e5d1746aae42c19d583c3c1a8c646bfad910498e2051c551a7f2e3c0c9fbb7eb" -dependencies = [ - "cc", -] - -[[package]] -name = "secp256k1-sys" -version = "0.10.0" -source = "git+/~https://github.com/sp1-patches/rust-secp256k1?branch=patch-secp256k1-v0.29.1#ee67ba569fb05c254823921954cdf56ca1dfb659" +checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" dependencies = [ "cc", ] @@ -2089,22 +2766,22 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.213" +version = "1.0.214" source = "registry+/~https://github.com/rust-lang/crates.io-index" -checksum = "3ea7893ff5e2466df8d720bb615088341b295f849602c6956047f8f80f0e9bc1" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.213" +version = "1.0.214" source = "registry+/~https://github.com/rust-lang/crates.io-index" -checksum = "7e85ad2009c50b58e87caa8cd6dac16bdf511bbfb7af6c33df902396aa480fa5" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -2119,13 +2796,43 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "3.11.0" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817" +dependencies = [ + "base64", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.6.0", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.11.0" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "serde_yaml" version = "0.9.34+deprecated" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap", + "indexmap 2.6.0", "itoa", "ryu", "serde", @@ -2172,6 +2879,15 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shlex" version = "1.3.0" @@ -2188,6 +2904,12 @@ dependencies = [ "rand_core", ] +[[package]] +name = "size" +version = "0.4.1" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "9fed904c7fb2856d868b92464fc8fa597fce366edea1a9cbfaa8cb5fe080bd6d" + [[package]] name = "smallvec" version = "1.13.2" @@ -2197,6 +2919,20 @@ dependencies = [ "serde", ] +[[package]] +name = "snark-bn254-verifier" +version = "1.0.2" +source = "git+/~https://github.com/succinctlabs/snark-bn254-verifier.git?rev=96bce2079584b68597f3241551e4b97300fd92c6#96bce2079584b68597f3241551e4b97300fd92c6" +dependencies = [ + "lazy_static", + "num-bigint", + "num-traits", + "rand", + "sha2", + "substrate-bn", + "thiserror", +] + [[package]] name = "snowbridge-amcl" version = "1.0.2" @@ -2208,27 +2944,167 @@ dependencies = [ ] [[package]] -name = "sp1-lib" +name = "sp1-core-executor" version = "2.0.0" source = "registry+/~https://github.com/rust-lang/crates.io-index" -checksum = "413956de14568d7fb462213b9505ad4607d75c875301b9eca567cfb2e58eaac1" +checksum = "9855631556145c827f959527a2f60cb2c6ededfb42afcee49e41dab5f507ce7b" +dependencies = [ + "bincode", + "bytemuck", + "elf", + "enum-map", + "eyre", + "generic-array 1.1.0", + "hashbrown 0.14.5", + "hex", + "itertools 0.13.0", + "log", + "nohash-hasher", + "num", + "p3-field", + "p3-keccak-air", + "p3-maybe-rayon", + "rand", + "rrs-succinct", + "serde", + "serde_with", + "sp1-curves", + "sp1-derive", + "sp1-primitives", + "sp1-stark", + "strum", + "strum_macros", + "thiserror", + "tiny-keccak", + "tracing", + "typenum", + "vec_map", +] + +[[package]] +name = "sp1-core-machine" +version = "2.0.0" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "c7d9c51416ef52b258fa637538c02a2ac06ecaeec79d075c620013a77f035dee" dependencies = [ "anyhow", + "arrayref", "bincode", + "blake3", + "bytemuck", "cfg-if", + "curve25519-dalek", + "elf", + "elliptic-curve", + "generic-array 1.1.0", + "hashbrown 0.14.5", "hex", + "itertools 0.13.0", + "k256", + "log", + "nohash-hasher", + "num", + "num-bigint", + "num_cpus", + "p3-air", + "p3-baby-bear", + "p3-blake3", + "p3-challenger", + "p3-commit", + "p3-dft", + "p3-field", + "p3-fri", + "p3-keccak", + "p3-keccak-air", + "p3-matrix", + "p3-maybe-rayon", + "p3-merkle-tree", + "p3-poseidon2", + "p3-symmetric", + "p3-uni-stark", + "p3-util", + "rand", + "rayon-scan", + "rrs-succinct", "serde", + "serde_with", + "size", "snowbridge-amcl", + "sp1-core-executor", + "sp1-curves", + "sp1-derive", + "sp1-primitives", + "sp1-stark", + "static_assertions", + "strum", + "strum_macros", + "tempfile", + "thiserror", + "tracing", + "tracing-forest", + "tracing-subscriber", + "typenum", + "web-time", +] + +[[package]] +name = "sp1-curves" +version = "2.0.0" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "d7c33d4d3f5e3590cc8c8d8a3d074097020bc0be0b9dd35fe43e04c586440c85" +dependencies = [ + "curve25519-dalek", + "dashu", + "elliptic-curve", + "generic-array 1.1.0", + "itertools 0.13.0", + "k256", + "num", + "p3-field", + "serde", + "snowbridge-amcl", + "sp1-primitives", + "sp1-stark", + "typenum", +] + +[[package]] +name = "sp1-derive" +version = "2.0.0" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "3e33825340e1b21b37a29f5304fbd18a1c7c97ba1376fa35b7c017fd95dd627e" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] name = "sp1-lib" -version = "3.0.0" +version = "1.2.0" source = "registry+/~https://github.com/rust-lang/crates.io-index" -checksum = "9c372b16988e765af85ccf958b18b26d89c05886f2592d313a285415dcc769cb" +checksum = "bea7811abd2d3a991007fcb284f41152840b8388c171288d0c52c6793956609c" dependencies = [ + "anyhow", "bincode", + "cfg-if", + "hex", "serde", + "snowbridge-amcl", +] + +[[package]] +name = "sp1-lib" +version = "2.0.0" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "413956de14568d7fb462213b9505ad4607d75c875301b9eca567cfb2e58eaac1" +dependencies = [ + "anyhow", + "bincode", + "cfg-if", + "hex", + "serde", + "snowbridge-amcl", ] [[package]] @@ -2245,6 +3121,38 @@ dependencies = [ "p3-symmetric", ] +[[package]] +name = "sp1-stark" +version = "2.0.0" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "4048fc99a6c1123f5040b5ade1ae2839ca1be421e4c427fc7d1fd9bbf6e174f5" +dependencies = [ + "arrayref", + "getrandom", + "hashbrown 0.14.5", + "itertools 0.13.0", + "p3-air", + "p3-baby-bear", + "p3-challenger", + "p3-commit", + "p3-dft", + "p3-field", + "p3-fri", + "p3-matrix", + "p3-maybe-rayon", + "p3-merkle-tree", + "p3-poseidon2", + "p3-symmetric", + "p3-uni-stark", + "p3-util", + "rayon-scan", + "serde", + "sp1-derive", + "sp1-primitives", + "sysinfo", + "tracing", +] + [[package]] name = "sp1-zkvm" version = "2.0.0" @@ -2266,6 +3174,12 @@ dependencies = [ "sp1-primitives", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "spki" version = "0.7.3" @@ -2285,9 +3199,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "strata-bridge-tx-builder" version = "0.1.0" -source = "git+/~https://github.com/alpenlabs/strata.git?branch=releases/0.1.0#1038dd849f9d1d3f2ca17940dc4631072852a25a" +source = "git+/~https://github.com/alpenlabs/strata.git?branch=bitvm2#c93aeece9c92d3565d6fa52f1d99ff7f012b9e03" dependencies = [ - "bitcoin 0.32.1", + "bitcoin", "lazy_static", "musig2", "serde", @@ -2298,10 +3212,10 @@ dependencies = [ [[package]] name = "strata-crypto" version = "0.1.0" -source = "git+/~https://github.com/alpenlabs/strata.git?branch=releases/0.1.0#1038dd849f9d1d3f2ca17940dc4631072852a25a" +source = "git+/~https://github.com/alpenlabs/strata.git?branch=bitvm2#c93aeece9c92d3565d6fa52f1d99ff7f012b9e03" dependencies = [ "borsh", - "secp256k1 0.29.1", + "secp256k1", "sha2", "strata-primitives", ] @@ -2309,19 +3223,19 @@ dependencies = [ [[package]] name = "strata-primitives" version = "0.1.0" -source = "git+/~https://github.com/alpenlabs/strata.git?branch=releases/0.1.0#1038dd849f9d1d3f2ca17940dc4631072852a25a" +source = "git+/~https://github.com/alpenlabs/strata.git?branch=bitvm2#c93aeece9c92d3565d6fa52f1d99ff7f012b9e03" dependencies = [ "anyhow", "arbitrary", "bincode", - "bitcoin 0.32.1", + "bitcoin", "borsh", "digest 0.10.7", "hex", "musig2", "rand", "reth-primitives", - "secp256k1 0.29.1", + "secp256k1", "serde", "serde_json", "sha2", @@ -2333,26 +3247,46 @@ dependencies = [ name = "strata-proofimpl-bitvm-bridge" version = "0.1.0" dependencies = [ - "bitcoin 0.31.0", + "bitcoin", "borsh", "serde", + "snark-bn254-verifier", + "sp1-core-machine", "strata-primitives", + "strata-proofimpl-btc-blockspace", "strata-state", + "strata-tx-parser", "strata-zkvm", + "substrate-bn", +] + +[[package]] +name = "strata-proofimpl-btc-blockspace" +version = "0.1.0" +source = "git+/~https://github.com/alpenlabs/strata.git?branch=bitvm2#c93aeece9c92d3565d6fa52f1d99ff7f012b9e03" +dependencies = [ + "bitcoin", + "borsh", + "serde", + "sha2", + "strata-bridge-tx-builder", + "strata-primitives", + "strata-state", + "strata-tx-parser", ] [[package]] name = "strata-state" version = "0.1.0" -source = "git+/~https://github.com/alpenlabs/strata.git?branch=releases/0.1.0#1038dd849f9d1d3f2ca17940dc4631072852a25a" +source = "git+/~https://github.com/alpenlabs/strata.git?branch=bitvm2#c93aeece9c92d3565d6fa52f1d99ff7f012b9e03" dependencies = [ "arbitrary", - "bitcoin 0.32.1", + "bitcoin", "borsh", "digest 0.10.7", "ethnum", "hex", - "num_enum", + "num_enum 0.7.3", "serde", "sha2", "strata-bridge-tx-builder", @@ -2362,10 +3296,28 @@ dependencies = [ "tracing", ] +[[package]] +name = "strata-tx-parser" +version = "0.1.0" +source = "git+/~https://github.com/alpenlabs/strata.git?branch=bitvm2#c93aeece9c92d3565d6fa52f1d99ff7f012b9e03" +dependencies = [ + "anyhow", + "arbitrary", + "bitcoin", + "borsh", + "hex", + "musig2", + "strata-bridge-tx-builder", + "strata-primitives", + "strata-state", + "thiserror", + "tracing", +] + [[package]] name = "strata-zkvm" version = "0.1.0" -source = "git+/~https://github.com/alpenlabs/strata.git?branch=releases/0.1.0#1038dd849f9d1d3f2ca17940dc4631072852a25a" +source = "git+/~https://github.com/alpenlabs/strata.git?branch=bitvm2#c93aeece9c92d3565d6fa52f1d99ff7f012b9e03" dependencies = [ "anyhow", "arbitrary", @@ -2373,6 +3325,12 @@ dependencies = [ "serde", ] +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "strum" version = "0.26.3" @@ -2392,7 +3350,23 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.85", + "syn 2.0.87", +] + +[[package]] +name = "substrate-bn" +version = "0.7.0" +source = "git+/~https://github.com/sp1-patches/bn?branch=patch-v0.7.0#3c53d2561492f26b9428c1d37d134031d0156152" +dependencies = [ + "bytemuck", + "byteorder", + "cfg-if", + "crunchy", + "lazy_static", + "num-bigint", + "rand", + "rustc-hex", + "sp1-lib 1.2.0", ] [[package]] @@ -2414,9 +3388,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.85" +version = "2.0.87" source = "registry+/~https://github.com/rust-lang/crates.io-index" -checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -2432,7 +3406,22 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", +] + +[[package]] +name = "sysinfo" +version = "0.30.13" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "0a5b4ddaee55fb2bea2bf0e5000747e5f5c0de765e5a5ff87f4cd106439f4bb3" +dependencies = [ + "cfg-if", + "core-foundation-sys", + "libc", + "ntapi", + "once_cell", + "rayon", + "windows", ] [[package]] @@ -2456,22 +3445,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.65" +version = "1.0.67" source = "registry+/~https://github.com/rust-lang/crates.io-index" -checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" +checksum = "3b3c6efbfc763e64eb85c11c25320f0737cb7364c4b6336db90aa9ebe27a0bbd" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.65" +version = "1.0.67" source = "registry+/~https://github.com/rust-lang/crates.io-index" -checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" +checksum = "b607164372e89797d78b8e23a6d67d5d1038c1c65efd52e1389ef8b77caba2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -2494,6 +3483,16 @@ dependencies = [ "thiserror-impl-no-std", ] +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "threadpool" version = "1.8.1" @@ -2503,6 +3502,37 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "time" +version = "0.3.36" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "tiny-keccak" version = "2.0.2" @@ -2518,15 +3548,26 @@ version = "0.6.8" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.6.0", + "toml_datetime", + "winnow 0.5.40", +] + [[package]] name = "toml_edit" version = "0.22.22" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap", + "indexmap 2.6.0", "toml_datetime", - "winnow", + "winnow 0.6.20", ] [[package]] @@ -2548,7 +3589,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -2558,6 +3599,49 @@ source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-forest" +version = "0.1.6" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "ee40835db14ddd1e3ba414292272eddde9dad04d3d4b65509656414d1c42592f" +dependencies = [ + "ansi_term", + "smallvec", + "thiserror", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", ] [[package]] @@ -2620,6 +3704,15 @@ version = "0.1.0" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +dependencies = [ + "serde", +] + [[package]] name = "version_check" version = "0.9.5" @@ -2641,6 +3734,112 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.95" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.95" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.87", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.95" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.95" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.95" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core", + "windows-targets", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.52.0" @@ -2723,6 +3922,15 @@ version = "0.52.6" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+/~https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + [[package]] name = "winnow" version = "0.6.20" @@ -2759,7 +3967,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] [[package]] @@ -2779,5 +3987,10 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.87", ] + +[[patch.unused]] +name = "secp256k1" +version = "0.29.0" +source = "git+/~https://github.com/sp1-patches/rust-secp256k1?branch=patch-secp256k1-v0.29.0#13910d476dbdaf436312a9f096ee312593028557" diff --git a/bridge-proofs/guest-bridge/Cargo.toml b/bridge-proofs/guest-bridge/Cargo.toml index 871a61f..4475e28 100644 --- a/bridge-proofs/guest-bridge/Cargo.toml +++ b/bridge-proofs/guest-bridge/Cargo.toml @@ -10,8 +10,5 @@ sp1-zkvm = { version = "2.0.0", features = ["verify"] } strata-proofimpl-bitvm-bridge = { path = "../../crates/proof-impl/bitvm-bridge" } [patch.crates-io] -base58check = { git = "/~https://github.com/rust-bitcoin/rust-bitcoin", branch = "bitvm" } -bitcoin_hashes = { git = "/~https://github.com/rust-bitcoin/rust-bitcoin", branch = "bitvm" } -bitcoin-internals = { git = "/~https://github.com/rust-bitcoin/rust-bitcoin", branch = "bitvm" } -secp256k1 = { git = "/~https://github.com/sp1-patches/rust-secp256k1", branch = "patch-secp256k1-v0.29.1" } +secp256k1 = { git = "/~https://github.com/sp1-patches/rust-secp256k1", branch = "patch-secp256k1-v0.29.0" } sha2-v0-10-8 = { git = "/~https://github.com/sp1-patches/RustCrypto-hashes", package = "sha2", branch = "patch-sha2-v0.10.8" } diff --git a/crates/agent/Cargo.toml b/crates/agent/Cargo.toml new file mode 100644 index 0000000..d0c56e0 --- /dev/null +++ b/crates/agent/Cargo.toml @@ -0,0 +1,43 @@ +[package] +edition = "2021" +name = "strata-bridge-agent" +version = "0.1.0" + +[lints] +rust.missing_debug_implementations = "warn" +rust.rust_2018_idioms = { level = "deny", priority = -1 } +# rust.unreachable_pub = "warn" +# rust.unused_crate_dependencies = "deny" +rust.unused_must_use = "deny" + +[dependencies] +strata-bridge-btcio.workspace = true +strata-bridge-db.workspace = true +strata-bridge-primitives.workspace = true +strata-bridge-tx-graph.workspace = true +strata-rpc = { workspace = true, features = ["client"] } + +strata-bridge-tx-builder.workspace = true +strata-state.workspace = true + +anyhow.workspace = true +bincode.workspace = true +bitcoin = { workspace = true, features = ["rand-std"] } +bitcoin-script = { workspace = true } +bitcoin-scriptexec = { workspace = true } +bitvm = { workspace = true } +esplora-client.workspace = true +hex.workspace = true +lazy_static.workspace = true +musig2 = { workspace = true, features = ["serde"] } +rand.workspace = true +secp256k1 = { workspace = true, features = ["global-context", "rand-std"] } +serde.workspace = true +serde_json.workspace = true +sha2.workspace = true +tokio.workspace = true +tracing.workspace = true + +[features] +default = ["mock"] +mock = [] diff --git a/crates/agent/src/base.rs b/crates/agent/src/base.rs new file mode 100644 index 0000000..a376eea --- /dev/null +++ b/crates/agent/src/base.rs @@ -0,0 +1,156 @@ +use std::{collections::HashSet, sync::Arc, time::Duration}; + +use bitcoin::{ + hashes::Hash, + key::TapTweak, + sighash::{Prevouts, SighashCache}, + Address, Amount, Network, OutPoint, TapSighashType, Transaction, TxOut, Txid, +}; +use musig2::{KeyAggContext, SecNonce}; +use rand::{rngs::OsRng, RngCore}; +use secp256k1::{schnorr::Signature, Keypair, PublicKey, SecretKey, SECP256K1}; +use strata_bridge_btcio::{ + error::ClientResult, + traits::{Broadcaster, Reader, Wallet}, + BitcoinClient, +}; +use strata_bridge_primitives::{params::prelude::MIN_RELAY_FEE, scripts::prelude::*}; +use tracing::trace; + +#[derive(Debug, Clone)] +pub struct Agent { + keypair: Keypair, + + pub client: Arc, +} + +impl Agent { + pub fn new(keypair: Keypair, btc_url: &str, btc_user: &str, btc_pass: &str) -> Self { + let client = BitcoinClient::new(btc_url, btc_user, btc_pass) + .expect("should be able to create bitcoin client"); + let client = Arc::new(client); + + Self { keypair, client } + } + + pub fn sign(&self, tx: &Transaction, prevouts: &[TxOut], input_index: usize) -> Signature { + let mut sighash_cache = SighashCache::new(tx); + let msg = create_message_hash( + &mut sighash_cache, + Prevouts::All(prevouts), + &TaprootWitness::Key, + TapSighashType::All, + input_index, + ) + .expect("should be able to create message hash"); + + SECP256K1.sign_schnorr(&msg, &self.keypair) + } + + pub async fn wait_and_broadcast( + &self, + tx: &Transaction, + wait_time: Duration, + ) -> ClientResult { + // sleep to confirm parent + tokio::time::sleep(wait_time).await; + + self.client.send_raw_transaction(tx).await + } + + pub fn public_key(&self) -> PublicKey { + self.keypair.public_key() + } + + pub fn secret_key(&self) -> SecretKey { + SecretKey::from_keypair(&self.keypair) + } + + pub fn taproot_address(&self, network: Network) -> Address { + let public_key = self.public_key().x_only_public_key().0; + + Address::p2tr_tweaked(public_key.dangerous_assume_tweaked(), network) + } + + pub async fn select_utxo( + &self, + target_amount: Amount, + reserved_utxos: HashSet, + ) -> Option<(Address, OutPoint, Amount, TxOut)> { + let unspent_utxos = self + .client + .get_utxos() + .await + .expect("should be able to get unspent utxos"); + + let change_address = self + .client + .get_new_address() + .await + .expect("should get change address"); + + let network = self + .client + .network() + .await + .expect("should get network from node"); + + // FIXME: allow selecting multiple UTXOs that sum up to the required amount + for entry in unspent_utxos { + let outpoint = OutPoint { + txid: entry.txid, + vout: entry.vout, + }; + if reserved_utxos.contains(&outpoint) { + // this utxo has already been selected for some other tx + continue; + } + + trace!(%entry.amount, %entry.txid, %entry.vout, %entry.confirmations, "checking unspent utxos"); + if entry.amount > target_amount + MIN_RELAY_FEE { + return Some(( + change_address, + OutPoint { + txid: entry.txid, + vout: entry.vout, + }, + entry.amount, + TxOut { + value: entry.amount, + script_pubkey: entry + .address + .require_network(network) + .expect("address should be valid") + .script_pubkey(), + }, + )); + } + } + + None + } + + /// Generate a random secret nonce. + /// + /// Please refer to MuSig2 nonce generation section in + /// [BIP 327](/~https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki). + /// + /// # Notes + /// + /// The entropy is pooled using the underlying operating system's + /// cryptographic-safe pseudo-random number generator with [`OsRng`]. + pub fn generate_sec_nonce(&self, txid: &Txid, key_agg_ctx: &KeyAggContext) -> SecNonce { + let aggregated_pubkey: PublicKey = key_agg_ctx.aggregated_pubkey(); + + let mut nonce_seed = [0u8; 32]; + OsRng.fill_bytes(&mut nonce_seed); + + let seckey = SecretKey::from_keypair(&self.keypair); + + SecNonce::build(nonce_seed) + .with_seckey(seckey) + .with_message(txid.as_byte_array()) + .with_aggregated_pubkey(aggregated_pubkey) + .build() + } +} diff --git a/crates/agent/src/bitcoin_watcher.rs b/crates/agent/src/bitcoin_watcher.rs new file mode 100644 index 0000000..8a51970 --- /dev/null +++ b/crates/agent/src/bitcoin_watcher.rs @@ -0,0 +1,166 @@ +use std::{collections::HashMap, sync::Arc, time::Duration}; + +use bitcoin::{Transaction, Txid}; +use strata_bridge_btcio::{traits::Reader, BitcoinClient}; +use strata_bridge_db::{connector_db::ConnectorDb, public::PublicDb}; +use strata_bridge_tx_graph::transactions::constants::NUM_ASSERT_DATA_TX; +use tokio::sync::{broadcast, RwLock}; +use tracing::{debug, info, warn}; + +use crate::{operator::OperatorIdx, verifier::VerifierDuty}; + +#[derive(Debug, Clone)] +pub struct BitcoinWatcher { + db: PublicDb, + + poll_interval: Duration, + + client: Arc, + + genesis_height: u32, + + relevant_txs: Arc>>, +} + +impl BitcoinWatcher { + pub fn new( + db: PublicDb, + client: Arc, + poll_interval: Duration, + genesis_height: u32, + ) -> Self { + Self { + db, + client, + poll_interval, + genesis_height, + relevant_txs: Default::default(), + } + } + + pub async fn start(&self, notifier: broadcast::Sender) { + info!(action = "starting bitcoin watcher", %self.genesis_height); + + let mut height = self.genesis_height; + loop { + let block = self.client.get_block_at(height).await; + + if let Err(e) = block { + if height % 1000 == 0 { + warn!(%e, %height, msg = "could not get block"); + } + tokio::time::sleep(self.poll_interval).await; + + continue; + } + + let block = block.unwrap(); + + for tx in block.txdata { + let txid = tx.compute_txid(); + + if let Some((operator_idx, deposit_txid)) = + self.db.get_operator_and_deposit_for_claim(&txid).await + { + info!(event = "noticed claim transaction", by_operator=%operator_idx, for_deposit_txid=%deposit_txid); + + warn!(action = "not dispatching challenge duty for now as it is unimplemented"); + + self.relevant_txs.write().await.insert(txid, tx); + + // FIXME: uncomment when `handle_claim()` is updated + // let duty = self.handle_claim().await; + // debug!(action = "dispatching challenge duty for verifier", claim_txid=%txid); + // notifier + // .send(duty) + // .expect("should be able to send challenge duty to the verifier"); + } else if let Some((operator_idx, deposit_txid)) = self + .db + .get_operator_and_deposit_for_post_assert(&txid) + .await + { + info!(event = "noticed post-assert transaction", by_operator=%operator_idx, for_deposit_txid=%deposit_txid); + let duty = self.handle_assertion(tx, operator_idx, deposit_txid).await; + + debug!(action = "dispatching disprove duty for verifier", post_assert_txid=%txid); + notifier + .send(duty) + .expect("should be able to send disprove duty to the verifier"); + } else if let Some((_operator_idx, _deposit_txid)) = self + .db + .get_operator_and_deposit_for_assert_data(&txid) + .await + { + // cache it to use later + self.relevant_txs.write().await.insert(txid, tx); + } else if let Some((_operator_idx, _deposit_txid)) = + self.db.get_operator_and_deposit_for_pre_assert(&txid).await + { + // cache it to use later + self.relevant_txs.write().await.insert(txid, tx); + } + } + + tokio::time::sleep(self.poll_interval).await; + height += 1; + + if height % 10 == 0 { + info!(event = "block scanned", cur_height=%height); + } + } + } + + pub async fn handle_claim(&self) -> VerifierDuty { + unimplemented!("challenge not supported yet"); + } + + pub async fn handle_assertion( + &self, + post_assert_tx: Transaction, + operator_id: OperatorIdx, + deposit_txid: Txid, + ) -> VerifierDuty { + let mut assert_data_txs = Vec::new(); + // skip the first input i.e., the stake + let relevant_txs = self.relevant_txs.read().await; + + for txin in post_assert_tx.input.iter().skip(1) { + let txid = &txin.previous_output.txid; + + let tx = relevant_txs + .get(txid) + .expect("should be able to fetch assert-data tx"); + + assert_data_txs.push(tx.clone()); + } + + assert_eq!( + assert_data_txs.len(), + NUM_ASSERT_DATA_TX, + "number of assert data txs must be as expected" + ); + + let assert_data_txs: [Transaction; NUM_ASSERT_DATA_TX] = assert_data_txs + .try_into() + .expect("the number of assert-data txs must match"); + + let pre_assert_tx = relevant_txs + .get(&assert_data_txs[0].input[0].previous_output.txid) + .expect("pre-assert tx must exist") + .clone(); + + let claim_tx = relevant_txs + .get(&pre_assert_tx.input[0].previous_output.txid) + .expect("claim tx must exist") + .clone(); + + VerifierDuty::VerifyAssertions { + operator_id, + deposit_txid, + + post_assert_tx, + claim_tx, + assert_data_txs, + } + } +} diff --git a/crates/agent/src/duty_watcher.rs b/crates/agent/src/duty_watcher.rs new file mode 100644 index 0000000..efa5d0a --- /dev/null +++ b/crates/agent/src/duty_watcher.rs @@ -0,0 +1,83 @@ +use std::{collections::HashSet, sync::Arc, time::Duration}; + +use bitcoin::Txid; +use strata_bridge_primitives::duties::{BridgeDuties, BridgeDuty}; +use strata_rpc::StrataApiClient; +use tokio::sync::broadcast; +use tracing::{debug, error, info}; + +#[derive(Debug, Clone)] +pub struct DutyWatcherConfig { + pub poll_interval: Duration, +} + +#[derive(Debug, Clone)] +pub struct DutyWatcher { + config: DutyWatcherConfig, + + strata_rpc_client: Arc, + + last_fetched_duty_index: u64, + + dispatched_duties: HashSet, +} + +impl DutyWatcher { + pub fn new(config: DutyWatcherConfig, strata_rpc_client: Arc) -> Self { + Self { + config, + strata_rpc_client, + last_fetched_duty_index: 0, + dispatched_duties: HashSet::new(), + } + } + + pub async fn start(&mut self, duty_sender: broadcast::Sender) { + loop { + let operator_idx = u32::MAX; // doesn't really matter in the current impl + + match self + .strata_rpc_client + .get_bridge_duties(operator_idx, self.last_fetched_duty_index) + .await + { + Ok(BridgeDuties { + duties, + start_index, + stop_index, + }) => { + let num_duties = duties.len(); + info!(event = "fetched duties", %start_index, %stop_index, %num_duties); + + for duty in duties { + let txid = match &duty { + BridgeDuty::SignDeposit(deposit_info) => { + deposit_info.deposit_request_outpoint().txid + } + BridgeDuty::FulfillWithdrawal(withdrawal_info) => { + withdrawal_info.deposit_outpoint().txid + } + }; + + if self.dispatched_duties.contains(&txid) { + debug!(action = "ignoring duplicate duty", %txid); + continue; + } + + self.dispatched_duties.insert(txid); + + debug!(action = "dispatching duty", ?duty); + duty_sender.send(duty).expect("should be able to send duty"); + } + + self.last_fetched_duty_index = stop_index; + } + Err(e) => { + error!(?e, "could not get duties from strata"); + } + } + + tokio::time::sleep(self.config.poll_interval).await; + } + } +} diff --git a/crates/agent/src/lib.rs b/crates/agent/src/lib.rs new file mode 100644 index 0000000..387abbc --- /dev/null +++ b/crates/agent/src/lib.rs @@ -0,0 +1,6 @@ +pub mod base; +pub mod bitcoin_watcher; +pub mod duty_watcher; +pub mod operator; +pub mod signal; +pub mod verifier; diff --git a/crates/agent/src/operator.rs b/crates/agent/src/operator.rs new file mode 100644 index 0000000..81c801a --- /dev/null +++ b/crates/agent/src/operator.rs @@ -0,0 +1,4165 @@ +use core::fmt; +use std::{collections::HashSet, time::Duration}; + +use anyhow::bail; +#[cfg(not(feature = "mock"))] +use bitcoin::hex::DisplayHex; +use bitcoin::{ + consensus, + hashes::Hash, + sighash::{Prevouts, SighashCache}, + TapSighashType, Transaction, TxOut, Txid, +}; +use musig2::{ + aggregate_partial_signatures, sign_partial, AggNonce, KeyAggContext, PartialSignature, PubNonce, +}; +#[cfg(not(feature = "mock"))] +use rand::{rngs::OsRng, Rng, RngCore}; +use secp256k1::schnorr::Signature; +#[cfg(not(feature = "mock"))] +use secp256k1::XOnlyPublicKey; +#[cfg(not(feature = "mock"))] +use strata_bridge_btcio::traits::Reader; +use strata_bridge_btcio::traits::{Broadcaster, Signer}; +use strata_bridge_db::{ + connector_db::ConnectorDb, + operator::{KickoffInfo, OperatorDb}, + public::PublicDb, +}; +use strata_bridge_primitives::{ + build_context::{BuildContext, TxBuildContext, TxKind}, + deposit::DepositInfo, + duties::BridgeDuty, + params::prelude::*, + scripts::{ + taproot::{create_message_hash, finalize_input, TaprootWitness}, + wots::{generate_wots_public_keys, generate_wots_signatures, mock, Assertions}, + }, + types::TxSigningData, + withdrawal::WithdrawalInfo, +}; +use strata_bridge_tx_graph::{ + connectors::params::PAYOUT_TIMELOCK, + peg_out_graph::{PegOutGraph, PegOutGraphConnectors, PegOutGraphInput}, + transactions::prelude::*, +}; +use tokio::sync::broadcast::{self, error::RecvError}; +use tracing::{debug, error, info, trace, warn}; + +use crate::{ + base::Agent, + signal::{ + AggNonces, CovenantNonceRequest, CovenantNonceRequestFulfilled, CovenantNonceSignal, + CovenantSigRequest, CovenantSigRequestFulfilled, CovenantSignatureSignal, DepositSignal, + }, +}; + +pub type OperatorIdx = u32; + +#[derive(Debug)] +pub struct Operator { + pub agent: Agent, + + msk: String, + + build_context: TxBuildContext, + + db: OperatorDb, + + public_db: PublicDb, + + is_faulty: bool, + + deposit_signal_sender: broadcast::Sender, + + deposit_signal_receiver: broadcast::Receiver, + + covenant_nonce_sender: broadcast::Sender, + + covenant_nonce_receiver: broadcast::Receiver, + + covenant_sig_sender: broadcast::Sender, + + covenant_sig_receiver: broadcast::Receiver, +} + +impl Operator { + #[allow(clippy::too_many_arguments)] + pub async fn new( + agent: Agent, + build_context: TxBuildContext, + is_faulty: bool, + db: OperatorDb, + public_db: PublicDb, + deposit_signal_sender: broadcast::Sender, + deposit_signal_receiver: broadcast::Receiver, + covenant_nonce_sender: broadcast::Sender, + covenant_nonce_receiver: broadcast::Receiver, + covenant_sig_sender: broadcast::Sender, + covenant_sig_receiver: broadcast::Receiver, + ) -> Self { + #[cfg(feature = "mock")] + let msk = "secret".to_string(); + + #[cfg(not(feature = "mock"))] + let msk = { + let mut msk_bytes: [u8; 32] = [0u8; 32]; + rand::thread_rng().fill_bytes(&mut msk_bytes); + msk_bytes.to_lower_hex_string() + }; + + Self { + agent, + msk, + build_context, + db, + public_db, + is_faulty, + deposit_signal_sender, + deposit_signal_receiver, + covenant_nonce_sender, + covenant_nonce_receiver, + covenant_sig_sender, + covenant_sig_receiver, + } + } + + pub fn am_i_faulty(&self) -> bool { + self.is_faulty + } + + pub async fn start(&mut self, duty_receiver: &mut broadcast::Receiver) { + let own_index = self.build_context.own_index(); + info!(action = "starting operator", %own_index); + + loop { + match duty_receiver.recv().await { + Ok(bridge_duty) => { + debug!(event = "received duty", ?bridge_duty, %own_index); + self.process_duty(bridge_duty).await; + } + Err(RecvError::Lagged(skipped_messages)) => { + warn!(action = "processing last available duty", event = "duty executor lagging behind, please adjust '--duty-interval' arg", %skipped_messages); + } + Err(err) => { + error!(msg = "error receiving duties", ?err); + + panic!("duty sender closed unexpectedly"); + } + } + } + } + + pub async fn process_duty(&mut self, duty: BridgeDuty) { + let own_index = self.build_context.own_index(); + + match duty { + BridgeDuty::SignDeposit(deposit_info) => { + let txid = deposit_info.deposit_request_outpoint().txid; + info!(event = "received deposit", %own_index, drt_txid = %txid); + + self.handle_deposit(deposit_info).await; + } + BridgeDuty::FulfillWithdrawal(cooperative_withdrawal_info) => { + let txid = cooperative_withdrawal_info.deposit_outpoint().txid; + let assignee_id = cooperative_withdrawal_info.assigned_operator_idx(); + + info!(event = "received withdrawal", dt_txid = %txid, assignee = %assignee_id, %own_index); + + if assignee_id != own_index { + warn!(action = "ignoring withdrawal duty unassigned to this operator", %assignee_id, %own_index); + return; + } + + self.handle_withdrawal(cooperative_withdrawal_info).await; + } + } + } + + pub async fn handle_deposit(&mut self, deposit_info: DepositInfo) { + let own_index = self.build_context.own_index(); + + // 1. aggregate_tx_graph + let mut deposit_tx = deposit_info + .construct_signing_data(&self.build_context) + .expect("should be able to create build context"); + + #[cfg(feature = "mock")] + let deposit_txid = Txid::from_byte_array(mock::PUBLIC_INPUTS.0); + + #[cfg(not(feature = "mock"))] + let deposit_txid = deposit_tx.psbt.unsigned_tx.compute_txid(); + + info!(action = "generating wots public keys", %deposit_txid, %own_index); + let public_keys = generate_wots_public_keys(&self.msk, deposit_txid); + self.public_db + .set_wots_public_keys(self.build_context.own_index(), deposit_txid, &public_keys) + .await; + + info!(action = "generating kickoff", %deposit_txid, %own_index); + let reserved_outpoints = self.db.selected_outpoints().await; + let (change_address, funding_input, total_amount, funding_utxo) = self + .agent + .select_utxo(OPERATOR_STAKE, reserved_outpoints) + .await + .expect("should be able to get outpoints"); + + self.db.add_outpoint(funding_input).await; + + let funding_inputs = vec![funding_input]; + let funding_utxos = vec![funding_utxo]; + let change_amt = total_amount - OPERATOR_STAKE - MIN_RELAY_FEE; + + info!(action = "composing pegout graph input", %deposit_txid, %own_index); + let peg_out_graph_input = PegOutGraphInput { + network: self.build_context.network(), + deposit_amount: BRIDGE_DENOMINATION, + operator_pubkey: self.agent.public_key().x_only_public_key().0, + kickoff_data: KickoffTxData { + funding_inputs: funding_inputs.clone(), + funding_utxos: funding_utxos.clone(), + change_address: change_address.as_unchecked().clone(), + change_amt, + deposit_txid, + }, + }; + + info!(action = "adding kickoff info to db", %deposit_txid, %own_index); + self.db + .add_kickoff_info( + deposit_txid, + KickoffInfo { + funding_inputs, + funding_utxos, + change_address, + change_amt, + }, + ) + .await; + + info!(action = "composing pegout graph connectors", %deposit_txid, %own_index); + let peg_out_graph_connectors = PegOutGraphConnectors::new( + self.public_db.clone(), + &self.build_context, + deposit_txid, + self.build_context.own_index(), + ) + .await; + + info!(action = "generating pegout graph", %deposit_txid, %own_index); + let peg_out_graph = PegOutGraph::generate( + peg_out_graph_input.clone(), + deposit_txid, + peg_out_graph_connectors, + own_index, + &self.public_db, + ) + .await; + + // 2. Aggregate nonces for peg out graph txs that require covenant. + info!(action = "aggregating nonces for emulated covenant", %deposit_txid, %own_index); + self.aggregate_covenant_nonces( + deposit_txid, + peg_out_graph_input.clone(), + peg_out_graph.clone(), + ) + .await; + + // 3. Aggregate signatures for peg out graph txs that require covenant. + info!(action = "aggregating signatures for emulated covenant", %deposit_txid, %own_index); + self.aggregate_covenant_signatures(deposit_txid, peg_out_graph_input, peg_out_graph) + .await; + + // 4. Collect nonces and signatures for deposit tx. + info!(action = "aggregating nonces for deposit sweeping", %deposit_txid, %own_index); + let agg_nonce = self + .aggregate_nonces(deposit_tx.clone()) + .await + .expect("nonce aggregation must complete"); + + info!(action = "aggregating signatures for deposit sweeping", %deposit_txid, %own_index); + let signed_deposit_tx = self + .aggregate_signatures(agg_nonce, &mut deposit_tx) + .await + .expect("should be able to construct fully signed deposit tx"); + + // 5. Broadcast deposit tx. + info!(action = "broadcasting deposit tx", operator_id=%own_index, %deposit_txid); + match self + .agent + .client + .send_raw_transaction(&signed_deposit_tx) + .await + { + Ok(txid) => { + info!(event = "deposit tx successfully broadcasted", %txid); + } + Err(e) => { + error!(?e, "could not broadcast deposit tx"); + } + } + } + + pub async fn aggregate_covenant_nonces( + &mut self, + deposit_txid: Txid, + self_peg_out_graph_input: PegOutGraphInput, + self_peg_out_graph: PegOutGraph, + ) { + let own_index = self.build_context.own_index(); + + // 1. Prepare txs + let PegOutGraph { + kickoff_tx: _, + claim_tx: _, + assert_chain, + payout_tx, + disprove_tx, + } = self_peg_out_graph; + let AssertChain { + pre_assert, + assert_data: _, + post_assert, + } = assert_chain; + + // 2. Generate own nonces + info!(action = "generating nonce for this operator", %deposit_txid, %own_index); + self.generate_covenant_nonces( + pre_assert.clone(), + post_assert.clone(), + payout_tx.clone(), + disprove_tx.clone(), + self.build_context.own_index(), + ) + .await; + + // 3. Broadcast nonce request + info!(action = "broadcasting this operator's nonce", %deposit_txid, %own_index); + let details = CovenantNonceRequest { + peg_out_graph_input: self_peg_out_graph_input, + }; + + self.covenant_nonce_sender + .send(CovenantNonceSignal::Request { + details, + sender_id: self.build_context.own_index(), + }) + .expect("should be able to send covenant signal"); + + // 4. Listen for requests and fulfillment data from others. + self.gather_and_fulfill_nonces( + deposit_txid, + pre_assert.compute_txid(), + post_assert.compute_txid(), + payout_tx.compute_txid(), + disprove_tx.compute_txid(), + ) + .await; + } + + async fn generate_covenant_nonces( + &self, + pre_assert: PreAssertTx, + post_assert: PostAssertTx, + payout_tx: PayoutTx, + disprove_tx: DisproveTx, + operator_index: OperatorIdx, + ) -> CovenantNonceRequestFulfilled { + let key_agg_ctx = KeyAggContext::new(self.build_context.pubkey_table().0.values().copied()) + .expect("should be able to create key agg ctx"); + let key_agg_ctx_keypath = key_agg_ctx + .clone() + .with_unspendable_taproot_tweak() + .expect("should be able to create key agg ctx with unspendable key"); + + // As all these calls lock on the same `HashMap`, there is no point in making these + // concurrent. + trace!(action = "creating secnonce and pubnonce for pre-assert tx", %operator_index); + let pre_assert_pubnonce = self + .generate_nonces(operator_index, &key_agg_ctx, 0, &pre_assert) + .await; + + trace!(action = "creating secnonce and pubnonce for post-assert tx", %operator_index); + let post_assert_pubnonce = self + .generate_nonces(operator_index, &key_agg_ctx_keypath, 0, &post_assert) + .await; + + trace!(action = "creating secnonce and pubnonce for payout tx output 0", %operator_index); + let payout_pubnonce_0 = self + .generate_nonces(operator_index, &key_agg_ctx_keypath, 0, &payout_tx) + .await; + + trace!(action = "creating secnonce and pubnonce for payout tx output 1", %operator_index); + let payout_pubnonce_1 = self + .generate_nonces(operator_index, &key_agg_ctx, 1, &payout_tx) + .await; + + trace!(action = "creating secnonce and pubnonce for disprove tx", %operator_index); + let disprove_pubnonce = self + .generate_nonces(operator_index, &key_agg_ctx, 0, &disprove_tx) + .await; + + CovenantNonceRequestFulfilled { + pre_assert: pre_assert_pubnonce, + post_assert: post_assert_pubnonce, + disprove: disprove_pubnonce, + payout_0: payout_pubnonce_0, + payout_1: payout_pubnonce_1, + } + } + + async fn gather_and_fulfill_nonces( + &mut self, + deposit_txid: Txid, + pre_assert_txid: Txid, + post_assert_txid: Txid, + payout_txid: Txid, + disprove_txid: Txid, + ) { + let own_index = self.build_context.own_index(); + + let mut requests_served = HashSet::new(); + requests_served.insert(own_index); + + let mut self_requests_fulfilled = false; + + let num_signers = self.build_context.pubkey_table().0.len(); + + // FIXME: beware of `continue`-ing in this while loop. Since we don't close the sender + // ever (as it is shared), continue-ing may cause the loop to wait for a message that will + // never be received. + while let Ok(msg) = self.covenant_nonce_receiver.recv().await { + match msg { + CovenantNonceSignal::Request { details, sender_id } => { + if sender_id == self.build_context.own_index() { + if self_requests_fulfilled && requests_served.len() == num_signers { + info!(event = "all nonce requests fulfilled and served", %deposit_txid, %own_index); + + return; + } + + info!(event = "self request ignored", %deposit_txid, %sender_id, %own_index); + + // ignore own request + continue; + } + + // fulfill request + let CovenantNonceRequest { + peg_out_graph_input, + } = details; + info!(event = "received covenant request for nonce", %deposit_txid, %sender_id, %own_index); + let connectors = PegOutGraphConnectors::new( + self.public_db.clone(), + &self.build_context, + deposit_txid, + sender_id, + ) + .await; + let PegOutGraph { + kickoff_tx: _, + claim_tx: _, + assert_chain, + disprove_tx, + payout_tx, + } = PegOutGraph::generate( + peg_out_graph_input, + deposit_txid, + connectors, + sender_id, + &self.public_db, + ) + .await; + let AssertChain { + pre_assert, + assert_data: _, + post_assert, + } = assert_chain; + + info!(action = "fulfilling covenant request for nonce", %deposit_txid, %sender_id, %own_index); + let request_fulfilled = self + .generate_covenant_nonces( + pre_assert, + post_assert, + payout_tx, + disprove_tx, + sender_id, + ) + .await; + + info!(action = "sending covenant request fulfillment signal for nonce", %deposit_txid, %sender_id, %own_index); + self.covenant_nonce_sender + .send(CovenantNonceSignal::RequestFulfilled { + details: request_fulfilled, + sender_id: self.build_context.own_index(), + destination_id: sender_id, + }) + .expect("should be able to send through the covenant signal sender"); + + requests_served.insert(sender_id); + let count = requests_served.len(); + trace!(event = "requests served", %deposit_txid, %count, %own_index); + + if count == num_signers && self_requests_fulfilled { + info!(event = "all nonce requests served and fulfilled", %deposit_txid, %count, %own_index); + + return; + } + } + CovenantNonceSignal::RequestFulfilled { + details, + sender_id, + destination_id, + } => { + info!(event = "received covenant fulfillment data for nonce", %deposit_txid, %sender_id, %destination_id, %own_index); + + if destination_id != own_index { + if self_requests_fulfilled && requests_served.len() == num_signers { + info!(event = "all nonce requests fulfilled and served", %deposit_txid, %own_index); + + return; + } + + // ignore messages meant for others + continue; + } + + let CovenantNonceRequestFulfilled { + pre_assert, + post_assert, + disprove, + payout_0, + payout_1, + } = details; + info!(event = "received covenant fulfillment data for nonce", %deposit_txid, %sender_id, %own_index); + + let txid_input_index_and_nonce = [ + (pre_assert_txid, 0, pre_assert), + (post_assert_txid, 0, post_assert), + (disprove_txid, 0, disprove), + (payout_txid, 0, payout_0), + (payout_txid, 1, payout_1), + ]; + + let mut all_done = true; + for (txid, input_index, nonce) in txid_input_index_and_nonce { + self.db + .add_pubnonce(txid, input_index, sender_id, nonce) + .await; + + all_done = self + .db + .collected_pubnonces(txid, input_index) + .await + .is_some_and(|v| v.len() == num_signers); + } + + self_requests_fulfilled = all_done; + if self_requests_fulfilled && requests_served.len() == num_signers { + info!(event = "nonce requests fulfilled and served", %own_index); + + return; + } + } + } + } + } + + pub async fn aggregate_covenant_signatures( + &mut self, + deposit_txid: Txid, + self_peg_out_graph_input: PegOutGraphInput, + self_peg_out_graph: PegOutGraph, + ) { + let own_index = self.build_context.own_index(); + + // 1. Prepare txs + let PegOutGraph { + kickoff_tx: _, + claim_tx: _, + assert_chain, + payout_tx, + disprove_tx, + } = self_peg_out_graph; + let AssertChain { + pre_assert, + assert_data: _, + post_assert, + } = assert_chain; + + // 2. Generate agg nonces + info!(action = "getting aggregated nonces", %deposit_txid, %own_index); + let pre_assert_agg_nonce = self + .get_aggregated_nonce(pre_assert.compute_txid(), 0) + .await + .expect("pre-assert nonce must exist"); + let post_assert_agg_nonce = self + .get_aggregated_nonce(post_assert.compute_txid(), 0) + .await + .expect("post-assert nonce must exist"); + let disprove_agg_nonce = self + .get_aggregated_nonce(disprove_tx.compute_txid(), 0) + .await + .expect("disprove nonce must exist"); + let payout_agg_nonce_0 = self + .get_aggregated_nonce(payout_tx.compute_txid(), 0) + .await + .expect("payout 0 nonce must exist"); + let payout_agg_nonce_1 = self + .get_aggregated_nonce(payout_tx.compute_txid(), 1) + .await + .expect("payout nonce 1 must exist"); + + let agg_nonces = AggNonces { + pre_assert: pre_assert_agg_nonce, + post_assert: post_assert_agg_nonce, + disprove: disprove_agg_nonce, + payout_0: payout_agg_nonce_0, + payout_1: payout_agg_nonce_1, + }; + + // 3. Generate own signatures + info!(action = "generating signature for this operator", deposit_txid = %deposit_txid, %own_index); + self.generate_covenant_signatures( + agg_nonces.clone(), + own_index, + pre_assert.clone(), + post_assert.clone(), + payout_tx.clone(), + disprove_tx.clone(), + ) + .await; + + // 3. Broadcast signature request + info!(action = "broadcasting this operator's signature", %deposit_txid, %own_index); + let details = CovenantSigRequest { + peg_out_graph_input: self_peg_out_graph_input, + agg_nonces: agg_nonces.clone(), + }; + self.covenant_sig_sender + .send(CovenantSignatureSignal::Request { + details, + sender_id: own_index, + }) + .expect("should be able to send covenant signal"); + + // 4. Listen for requests and fulfillment data from others. + info!(action = "listening for signature requests and fulfillments", deposit_txid = %deposit_txid, %own_index ); + self.gather_and_fulfill_signatures( + deposit_txid, + pre_assert.compute_txid(), + post_assert.compute_txid(), + payout_tx.compute_txid(), + disprove_tx.compute_txid(), + ) + .await; + + // 5. Update public db with aggregated signatures + info!(action = "computing aggregate signatures", deposit_txid = %deposit_txid, %own_index ); + let key_agg_ctx = KeyAggContext::new(self.build_context.pubkey_table().0.values().copied()) + .expect("should be able to create key agg ctx"); + + let all_inputs = pre_assert.witnesses().len(); + self.compute_agg_sig( + &key_agg_ctx, + all_inputs, + pre_assert, + vec![agg_nonces.pre_assert; all_inputs].as_ref(), + ) + .await; + debug!(event = "computed aggregate signature for pre-assert", deposit_txid = %deposit_txid, %own_index); + + let all_inputs = post_assert.witnesses().len(); + self.compute_agg_sig( + &key_agg_ctx, + all_inputs, + post_assert, + vec![agg_nonces.post_assert; all_inputs].as_ref(), + ) + .await; + debug!(event = "computed aggregate signature for post-assert", deposit_txid = %deposit_txid, %own_index); + + self.compute_agg_sig( + &key_agg_ctx, + all_inputs, + payout_tx, + &[agg_nonces.payout_0, agg_nonces.payout_1], + ) + .await; + debug!(event = "computed aggregate signature for payout", deposit_txid = %deposit_txid, %own_index); + + let all_inputs = disprove_tx.witnesses().len(); + self.compute_agg_sig( + &key_agg_ctx, + 1, + disprove_tx, + vec![agg_nonces.disprove; all_inputs].as_ref(), + ) + .await; + debug!(event = "computed aggregate signature for disprove", deposit_txid = %deposit_txid, %own_index); + } + + #[allow(clippy::too_many_arguments)] + pub async fn generate_covenant_signatures( + &self, + agg_nonces: AggNonces, + operator_index: OperatorIdx, + pre_assert: PreAssertTx, + post_assert: PostAssertTx, + payout_tx: PayoutTx, + disprove_tx: DisproveTx, + ) -> CovenantSigRequestFulfilled { + let own_index = self.build_context.own_index(); + + let key_agg_ctx = KeyAggContext::new(self.build_context.pubkey_table().0.values().copied()) + .expect("should be able to create key agg ctx"); + + let all_inputs = pre_assert.witnesses().len(); + trace!(action = "signing pre-assert tx partially", %operator_index); + let pre_assert_partial_sigs = self + .sign_partial( + &key_agg_ctx, + TapSighashType::Default, + all_inputs, + own_index, + operator_index, + pre_assert, + vec![agg_nonces.pre_assert; all_inputs].as_ref(), + ) + .await; + + trace!(action = "signing post-assert tx partially", %operator_index); + let all_inputs = post_assert.witnesses().len(); + let post_assert_partial_sigs = self + .sign_partial( + &key_agg_ctx, + TapSighashType::Default, + all_inputs, + own_index, + operator_index, + post_assert, + vec![agg_nonces.post_assert; all_inputs].as_ref(), + ) + .await; + + trace!(action = "signing payout tx partially", %operator_index); + let payout_partial_sigs = self + .sign_partial( + &key_agg_ctx, + TapSighashType::Default, + all_inputs, + own_index, + operator_index, + payout_tx, + &[agg_nonces.payout_0, agg_nonces.payout_1], + ) + .await; + + trace!(action = "signing disprove tx partially", %operator_index); + let inputs_to_sign = disprove_tx.witnesses().len(); + let disprove_partial_sigs = self + .sign_partial( + &key_agg_ctx, + TapSighashType::Single, + inputs_to_sign, + own_index, + operator_index, + disprove_tx, + vec![agg_nonces.disprove; inputs_to_sign].as_ref(), + ) + .await; + + CovenantSigRequestFulfilled { + pre_assert: pre_assert_partial_sigs, + post_assert: post_assert_partial_sigs, + disprove: disprove_partial_sigs, + payout: payout_partial_sigs, + } + } + + pub async fn gather_and_fulfill_signatures( + &mut self, + deposit_txid: Txid, + pre_assert_txid: Txid, + post_assert_txid: Txid, + payout_txid: Txid, + disprove_txid: Txid, + ) { + let own_index = self.build_context.own_index(); + + let mut requests_served = HashSet::new(); + requests_served.insert(own_index); + + let mut self_requests_fulfilled = false; + + let num_signers = self.build_context.pubkey_table().0.len(); + + // FIXME: beware of `continue`-ing in this while loop. Since we don't close the sender + // ever (as it is shared), continue-ing may cause the loop to wait for a message that will + // never be received. + while let Ok(msg) = self.covenant_sig_receiver.recv().await { + match msg { + CovenantSignatureSignal::Request { details, sender_id } => { + if sender_id == own_index { + if self_requests_fulfilled && requests_served.len() == num_signers { + info!(event = "all nonce requests fulfilled and served", %deposit_txid, %own_index); + + return; + } + + info!(event = "ignored self request for signatures", %deposit_txid, %own_index); + continue; + } + + // fulfill request + let CovenantSigRequest { + agg_nonces, + peg_out_graph_input, + } = details; + info!(event = "received covenant request for signatures", %deposit_txid, %sender_id, %own_index); + let connectors = PegOutGraphConnectors::new( + self.public_db.clone(), + &self.build_context, + deposit_txid, + sender_id, + ) + .await; + let PegOutGraph { + kickoff_tx: _, + claim_tx: _, + assert_chain, + disprove_tx, + payout_tx, + } = PegOutGraph::generate( + peg_out_graph_input, + deposit_txid, + connectors, + sender_id, + &self.public_db, + ) + .await; + let AssertChain { + pre_assert, + assert_data: _, + post_assert, + } = assert_chain; + + info!(action = "fulfilling covenant request for signatures", %deposit_txid, %sender_id, %own_index); + let request_fulfilled = self + .generate_covenant_signatures( + agg_nonces, + sender_id, + pre_assert, + post_assert, + payout_tx, + disprove_tx, + ) + .await; + + info!(action = "sending covenant request fulfillment signal for signatures", %deposit_txid, destination_id = %sender_id, %own_index); + self.covenant_sig_sender + .send(CovenantSignatureSignal::RequestFulfilled { + details: request_fulfilled, + sender_id: own_index, + destination_id: sender_id, + }) + .expect("should be able to send through the covenant signal sender"); + + requests_served.insert(sender_id); + let count = requests_served.len(); + trace!(event = "requests served", %deposit_txid, %count, %own_index); + + if count == num_signers && self_requests_fulfilled { + info!(event = "all signature requests served and fulfilled", %deposit_txid, %own_index); + + return; + } + } + CovenantSignatureSignal::RequestFulfilled { + details, + sender_id, + destination_id, + } => { + if destination_id != own_index { + if self_requests_fulfilled && requests_served.len() == num_signers { + info!(event = "all nonce requests fulfilled and served", %deposit_txid, %own_index); + + return; + } + + // ignore messages meant for others + continue; + } + + let CovenantSigRequestFulfilled { + pre_assert, + post_assert, + disprove, + payout, + } = details; + info!(event = "received covenant fulfillment data for signature", %deposit_txid, %sender_id, %own_index); + + let txid_and_signatures = [ + (pre_assert_txid, pre_assert), + (post_assert_txid, post_assert), + (disprove_txid, disprove), + (payout_txid, payout), + ]; + + let mut all_done = true; + for (txid, signatures) in txid_and_signatures { + for (input_index, partial_sig) in signatures.into_iter().enumerate() { + self.db + .add_partial_signature( + txid, + input_index as u32, + sender_id, + partial_sig, + ) + .await; + + all_done = all_done + && self + .db + .collected_signatures_per_msg(txid, input_index as u32) + .await + .is_some_and(|v| { + let sig_count = v.1.len(); + trace!(event = "got sig count", %sig_count, %txid, %input_index, %own_index); + + sig_count == num_signers + }); + } + } + + self_requests_fulfilled = all_done; + if self_requests_fulfilled && requests_served.len() == num_signers { + info!(event = "all signature requests fulfilled and served", %deposit_txid, %own_index); + + return; + } + } + } + } + } + + pub async fn aggregate_nonces(&mut self, tx_signing_data: TxSigningData) -> Option { + let tx = tx_signing_data.psbt.unsigned_tx.clone(); + let txid = tx.compute_txid(); + + let own_index = self.build_context.own_index(); + + info!(action = "generating one's own nonce for deposit sweeping", deposit_txid=%txid, operator_idx=%own_index); + let key_agg_ctx = KeyAggContext::new(self.build_context.pubkey_table().0.values().copied()) + .expect("should be able to create key agg context"); + + let secnonce = self.agent.generate_sec_nonce(&txid, &key_agg_ctx); + self.db.add_secnonce(txid, 0, secnonce.clone()).await; + + let pubnonce = secnonce.public_nonce(); + + self.db + .add_pubnonce(txid, 0, own_index, pubnonce.clone()) + .await; + + info!(action = "broadcasting one's own nonce for deposit sweeping", deposit_txid=%txid, %own_index); + self.deposit_signal_sender + .send(DepositSignal::Nonce { + txid, + pubnonce, + sender_id: own_index, + }) + .expect("should be able to send deposit pubnonce"); + + info!(action = "listening for nonces for deposit sweeping", deposit_txid=%txid, %own_index); + + let expected_nonce_count = self.build_context.pubkey_table().0.len(); + while let Ok(deposit_signal) = self.deposit_signal_receiver.recv().await { + if let DepositSignal::Nonce { + txid, + pubnonce, + sender_id, + } = deposit_signal + { + info!(event = "received nonce for deposit sweeping", deposit_txid=%txid, %own_index, %sender_id); + self.db.add_pubnonce(txid, 0, sender_id, pubnonce).await; + + if let Some(collected_nonces) = self.db.collected_pubnonces(txid, 0).await { + let nonce_count = collected_nonces.len(); + if nonce_count != expected_nonce_count { + // NOTE: there is still some nonce to be received, so continuing to listen + // on the channel is fine. + debug!(event = "collected nonces but not sufficient yet", %nonce_count, %expected_nonce_count); + + continue; + } + + info!(event = "received all required nonces for deposit sweeping", deposit_txid=%txid, %own_index, %sender_id); + return Some(collected_nonces.values().sum()); + } + } else { + // ignore signatures in this function + warn!( + ?deposit_signal, + %own_index, + "should not receive signatures in this function" + ); + } + } + + error!(event = "deposit signal sender closed before completion", deposit_txid=%txid, %own_index); + None + } + + pub async fn aggregate_signatures( + &mut self, + agg_nonce: AggNonce, + tx_signing_data: &mut TxSigningData, + ) -> Option { + let own_index = self.build_context.own_index(); + + let tx = &tx_signing_data.psbt.unsigned_tx; + let txid = tx.compute_txid(); + + let prevouts = tx_signing_data + .psbt + .inputs + .iter() + .map(|i| { + i.witness_utxo + .clone() + .expect("witness UTXO must be present") + }) + .collect::>(); + let prevouts = Prevouts::All(&prevouts); + + let key_agg_ctx = KeyAggContext::new(self.build_context.pubkey_table().0.values().copied()) + .expect("should be able to generate agg key context"); + let seckey = self.agent.secret_key(); + let secnonce = self + .db + .secnonce(txid, 0) + .await + .expect("secnonce should exist before adding signatures"); + + info!(action = "generating one's own signature for deposit sweeping", deposit_txid=%txid, operator_idx=%own_index); + + let mut sighash_cache = SighashCache::new(tx); + let message = create_message_hash( + &mut sighash_cache, + prevouts, + &tx_signing_data.spend_path, + TapSighashType::Default, + 0, + ) + .expect("should be able to create message hash"); + let message = message.as_ref(); + + let partial_signature = sign_partial(&key_agg_ctx, seckey, secnonce, &agg_nonce, message) + .expect("should be able to sign deposit"); + self.db + .add_message_hash_and_signature(txid, 0, message.to_vec(), own_index, partial_signature) + .await; + + info!(action = "broadcasting one's own signature for deposit sweeping", deposit_txid=%txid, operator_idx=%own_index); + self.deposit_signal_sender + .send(DepositSignal::Signature { + txid, + signature: partial_signature, + sender_id: own_index, + }) + .expect("should be able to send signature"); + + info!(action = "listening for signatures for deposit sweeping", deposit_txid=%txid, operator_idx=%own_index); + + let expected_signature_count = self.build_context.pubkey_table().0.len(); + while let Ok(deposit_signal) = self.deposit_signal_receiver.recv().await { + if let DepositSignal::Signature { + txid, + signature, + sender_id, + } = deposit_signal + { + // TODO: add signature verification logic in prod + // for now, this is fine because musig2 validates every signature during generation. + self.db + .add_partial_signature(txid, 0, sender_id, signature) + .await; + + if let Some((_, collected_signatures)) = + self.db.collected_signatures_per_msg(txid, 0).await + { + let sig_count = collected_signatures.len(); + if collected_signatures.len() != expected_signature_count { + // NOTE: there is still some signature to be received, so continuing to + // listen on the channel is fine. + debug!(event = "collected signatures but not sufficient yet", %sig_count, %expected_signature_count); + + continue; + } + + info!(event = "received all required signatures for deposit sweeping"); + + let agg_signature: Signature = aggregate_partial_signatures( + &key_agg_ctx, + &agg_nonce, + collected_signatures.values().copied(), + message, + ) + .expect("should be able to aggregate signatures"); + + info!(event = "signature aggregation complete for deposit sweeping", deposit_txid=%txid, operator_idx=%own_index); + + if let TaprootWitness::Script { + script_buf, + control_block, + } = tx_signing_data.spend_path.clone() + { + let witnesses = [ + agg_signature.as_ref().to_vec(), + script_buf.to_bytes(), + control_block.serialize(), + ]; + finalize_input( + tx_signing_data + .psbt + .inputs + .first_mut() + .expect("the first input must exist"), + witnesses, + ); + + let signed_tx = tx_signing_data + .psbt + .clone() + .extract_tx() + .expect("should be able to extract fully signed tx"); + debug!(event = "created signed tx", ?signed_tx); + info!(event = "deposit transaction fully signed and ready for broadcasting", deposit_txid=%txid, operator_idx=%own_index); + + return Some(signed_tx); + } else { + unreachable!("deposit request should have a script spend path"); + }; + } + } else { + // ignore nonces in this function + warn!(?deposit_signal, %own_index, "should not receive nonces in this function"); + } + } + + error!(event = "deposit signal sender closed before completion", deposit_txid=%txid, %own_index); + None + } + + pub async fn generate_nonces( + &self, + operator_idx: OperatorIdx, + key_agg_ctx: &KeyAggContext, + input_index: u32, + tx: &impl CovenantTx, + ) -> PubNonce { + let txid = tx.compute_txid(); + + let secnonce = self.agent.generate_sec_nonce(&txid, key_agg_ctx); + let pubnonce = secnonce.public_nonce(); + + // add the secnonce and pubnonce to db even for txid from others as it is required for + // partial signing later. + self.db.add_secnonce(txid, input_index, secnonce).await; + self.db + .add_pubnonce(txid, input_index, operator_idx, pubnonce.clone()) + .await; + + pubnonce + } + + /// Get the aggregated nonce from the list of collected nonces for the transaction + /// corresponding to the given [`Txid`]. + /// + /// Please refer to MuSig2 nonce aggregation section in + /// [BIP 327](/~https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki). + /// # Errors + /// + /// If not all nonces have been colllected yet. + pub async fn get_aggregated_nonce( + &self, + txid: Txid, + input_index: u32, + ) -> anyhow::Result { + if let Some(collected_nonces) = self.db.collected_pubnonces(txid, input_index).await { + let expected_nonce_count = self.build_context.pubkey_table().0.len(); + if collected_nonces.len() != expected_nonce_count { + let collected: Vec = collected_nonces.keys().copied().collect(); + error!(?collected, %expected_nonce_count, "nonce collection incomplete"); + + bail!("nonce collection incomplete"); + } + + Ok(collected_nonces.values().sum()) + } else { + error!(%txid, %input_index, "nonces not found"); + + bail!("nonce not found"); + } + } + + /// Create partial signature for the tx. + /// + /// Make sure that `prevouts`, `agg_nonces` and `witnesses` have the same length. + #[expect(clippy::too_many_arguments)] + async fn sign_partial( + &self, + key_agg_ctx: &KeyAggContext, + sighash_type: TapSighashType, + inputs_to_sign: usize, + own_index: OperatorIdx, + operator_index: OperatorIdx, + covenant_tx: Tx, + agg_nonces: &[AggNonce], + ) -> Vec { + let tx = &covenant_tx.psbt().unsigned_tx; + let txid = tx.compute_txid(); + + let prevouts = covenant_tx.prevouts(); + let witnesses = covenant_tx.witnesses(); + + let mut sighash_cache = SighashCache::new(tx); + + let mut partial_sigs: Vec = Vec::with_capacity(witnesses.len()); + for (input_index, (agg_nonce, witness)) in agg_nonces + .iter() + .zip(witnesses) + .enumerate() + .take(inputs_to_sign) + { + trace!(action = "creating message hash", ?covenant_tx, %input_index); + + let message = create_message_hash( + &mut sighash_cache, + prevouts.clone(), + witness, + sighash_type, + input_index, + ) + .expect("should be able to create a message hash"); + let message = message.as_ref(); + + let secnonce = if let Some(secnonce) = self.db.secnonce(txid, input_index as u32).await + { + secnonce + } else { + // use the first secnonce if the given input_index does not exist + // this is the case for post_assert inputs (but not for payout) + self.db + .secnonce(txid, 0) + .await + .expect("first secnonce should exist") + }; + + let seckey = self.agent.secret_key(); + + let agg_ctx = if matches!(witness, TaprootWitness::Key) { + &key_agg_ctx + .clone() + .with_unspendable_taproot_tweak() + .expect("should be able to add unspendable key tweak") + } else { + key_agg_ctx + }; + + let partial_sig: PartialSignature = + sign_partial(agg_ctx, seckey, secnonce, agg_nonce, message) + .expect("should be able to sign pre-assert"); + + partial_sigs.push(partial_sig); + + if own_index == operator_index { + self.db + .add_message_hash_and_signature( + txid, + input_index as u32, + message.to_vec(), + own_index, + partial_sig, + ) + .await; + } + } + + partial_sigs + } + + async fn compute_agg_sig( + &self, + key_agg_ctx: &KeyAggContext, + inputs_to_sign: usize, + covenant_tx: impl CovenantTx, + agg_nonces: &[AggNonce], + ) { + let txid = covenant_tx.compute_txid(); + + let witnesses = covenant_tx.witnesses(); + + for (input_index, (agg_nonce, witness)) in agg_nonces + .iter() + .zip(witnesses) + .enumerate() + .take(inputs_to_sign) + { + let agg_ctx = if matches!(witness, TaprootWitness::Key) { + &key_agg_ctx + .clone() + .with_unspendable_taproot_tweak() + .expect("should be able to add unspendable key tweak") + } else { + key_agg_ctx + }; + + let collected_msgs_and_sigs = self + .db + .collected_signatures_per_msg(txid, input_index as u32) + .await + .expect("partial signatures must be present"); + let message = collected_msgs_and_sigs.0; + let partial_sigs: Vec = + collected_msgs_and_sigs.1.values().copied().collect(); + + let agg_sig: Signature = + aggregate_partial_signatures(agg_ctx, agg_nonce, partial_sigs, message) + .expect("signature aggregation must succeed"); + + self.public_db + .put_signature( + self.build_context.own_index(), + txid, + input_index as u32, + agg_sig, + ) + .await; + } + } + + pub async fn handle_withdrawal(&self, withdrawal_info: WithdrawalInfo) { + // 0. get context + let network = self.build_context.network(); + let own_index = self.build_context.own_index(); + + #[cfg(feature = "mock")] + let deposit_txid = Txid::from_byte_array(mock::PUBLIC_INPUTS.0); + + #[cfg(not(feature = "mock"))] + let deposit_txid = withdrawal_info.deposit_outpoint().txid; + + let own_pubkey = self.agent.public_key().x_only_public_key().0; + + // 1. pay the user with PoW transaction + let user_pk = withdrawal_info.user_pk(); + + info!(action = "paying out the user", %user_pk, %own_index); + #[cfg(feature = "mock")] + let bridge_out_txid = Txid::from_byte_array(mock::PUBLIC_INPUTS.2); + + #[cfg(not(feature = "mock"))] + let bridge_out_txid = self + .pay_user(user_pk, network, own_index) + .await + .expect("must be able to pay user"); + + // 2. create tx graph from public data + info!(action = "reconstructing pegout graph", %deposit_txid, %own_index); + let KickoffInfo { + funding_inputs, + funding_utxos, + change_address, + change_amt, + } = self + .db + .get_kickoff_info(deposit_txid) + .await + .expect("kickoff data for the deposit must be present"); + + let peg_out_graph_input = PegOutGraphInput { + network, + deposit_amount: BRIDGE_DENOMINATION, + operator_pubkey: own_pubkey, + kickoff_data: KickoffTxData { + funding_inputs, + funding_utxos, + change_address: change_address.as_unchecked().clone(), + change_amt, + deposit_txid, + }, + }; + + let connectors = PegOutGraphConnectors::new( + self.public_db.clone(), + &self.build_context, + deposit_txid, + own_index, + ) + .await; + + let PegOutGraph { + kickoff_tx, + claim_tx, + assert_chain, + payout_tx, + disprove_tx: _, + } = PegOutGraph::generate( + peg_out_graph_input, + deposit_txid, + connectors.clone(), + own_index, + &self.public_db, + ) + .await; + + // 3. publish kickoff -> claim + self.broadcast_kickoff_and_claim( + &connectors, + own_index, + deposit_txid, + kickoff_tx, + claim_tx, + bridge_out_txid, + ) + .await; + + // 4. compute superblock and proof (skip) + info!(action = "creating assertion signatures", %own_index); + + #[cfg(feature = "mock")] + let assert_data_signatures = { + let mut assertions = mock_assertions(); + if self.am_i_faulty() { + warn!(action = "making a faulty assertion"); + assertions.groth16.0[0] = [0u8; 32]; + } + generate_wots_signatures(&self.msk, deposit_txid, assertions) + }; + + // 5. publish assert chain + let AssertChain { + pre_assert, + assert_data, + post_assert, + } = assert_chain; + + let pre_assert_txid = pre_assert.compute_txid(); + let n_of_n_sig = self + .public_db + .get_signature(own_index, pre_assert_txid, 0) + .await; + let signed_pre_assert = pre_assert.finalize(n_of_n_sig, connectors.claim_out_0); + let vsize = signed_pre_assert.vsize(); + let total_size = signed_pre_assert.total_size(); + let weight = signed_pre_assert.weight(); + info!(event = "finalized pre-assert tx", %pre_assert_txid, %vsize, %total_size, %weight, %own_index); + + let txid = self + .agent + .wait_and_broadcast(&signed_pre_assert, BTC_CONFIRM_PERIOD) + .await + .expect("should settle pre-assert"); + info!(event = "broadcasted pre-assert", %txid, %own_index); + + let signed_assert_data_txs = assert_data.finalize( + connectors.assert_data160_factory, + connectors.assert_data256_factory, + &self.msk, + assert_data_signatures, + ); + + let num_assert_data_txs = signed_assert_data_txs.len(); + info!( + event = "finalized signed assert data txs", + num_assert_data_txs + ); + + info!(action = "estimating finalized assert data tx sizes", %own_index); + for (index, signed_assert_data_tx) in signed_assert_data_txs.iter().enumerate() { + let txid = signed_assert_data_tx.compute_txid(); + let vsize = signed_assert_data_tx.vsize(); + let total_size = signed_assert_data_tx.total_size(); + let weight = signed_assert_data_tx.weight(); + info!(event = "assert-data tx", %index, %txid, %vsize, %total_size, %weight, %own_index); + } + + info!(action = "broadcasting finalized assert data txs", %own_index); + for (index, signed_assert_data_tx) in signed_assert_data_txs.iter().enumerate() { + info!(event = "broadcasting signed assert data tx", %index, %num_assert_data_txs); + + self.agent + .wait_and_broadcast(signed_assert_data_tx, Duration::from_secs(1)) + .await + .expect("should settle assert-data"); + + info!(event = "broadcasted signed assert data tx", %index, %num_assert_data_txs); + } + + let post_assert_txid = post_assert.compute_txid(); + let mut signatures = Vec::new(); + // num_assert_data_tx + 1 for stake + for input_index in 0..=num_assert_data_txs { + let n_of_n_sig = self + .public_db + .get_signature(own_index, post_assert_txid, input_index as u32) + .await; + + signatures.push(n_of_n_sig); + } + + let signed_post_assert = post_assert.finalize(&signatures); + let vsize = signed_pre_assert.vsize(); + let total_size = signed_pre_assert.total_size(); + let weight = signed_pre_assert.weight(); + info!(event = "finalized post-assert tx", %post_assert_txid, %vsize, %total_size, %weight, %own_index); + + self.agent + .client + .send_raw_transaction(&signed_post_assert) + .await + .expect("should be able to finalize post-assert tx"); + + info!(event = "broadcasted post-assert tx", %post_assert_txid, %own_index); + + // 6. settle reimbursement tx after wait time + let wait_time = Duration::from_secs(PAYOUT_TIMELOCK as u64 + 20); + info!(action = "waiting for timeout period before seeking reimbursement", wait_time_secs=%wait_time.as_secs()); + tokio::time::sleep(wait_time).await; + + let n_of_n_signature = self + .public_db + .get_signature(own_index, payout_tx.compute_txid(), 0) + .await; + let signed_payout_tx = payout_tx.finalize(n_of_n_signature); + + info!(action = "trying to get reimbursement", payout_txid=%signed_payout_tx.compute_txid(), %own_index); + + let txid = self + .agent + .wait_and_broadcast(&signed_payout_tx, BTC_CONFIRM_PERIOD) + .await + .expect("should be able to broadcast payout tx"); + + info!(event = "successfully received reimbursement", %txid, %own_index); + } + + async fn broadcast_kickoff_and_claim( + &self, + connectors: &PegOutGraphConnectors, + own_index: u32, + deposit_txid: Txid, + kickoff_tx: KickOffTx, + claim_tx: ClaimTx, + bridge_out_txid: Txid, + ) { + #[cfg(feature = "mock")] + let superblock_period_start_ts = mock::PUBLIC_INPUTS.3; + + #[cfg(not(feature = "mock"))] + let superblock_period_start_ts = self + .agent + .client + .get_current_timestamp() + .await + .expect("should be able to get the latest timestamp from the best block"); + debug!(event = "got current timestamp (T_s)", %superblock_period_start_ts, %own_index); + + let unsigned_kickoff = &kickoff_tx.psbt().unsigned_tx; + let funded_kickoff = self + .agent + .client + .sign_raw_transaction_with_wallet(unsigned_kickoff) + .await + .expect("should be able to sign kickoff tx with wallet"); + let funded_kickoff_tx: Transaction = + consensus::encode::deserialize_hex(&funded_kickoff.hex) + .expect("must be able to decode kickoff tx"); + + let kickoff_txid = self + .agent + .client + .send_raw_transaction(&funded_kickoff_tx) + .await + .expect("should be able to broadcast signed kickoff tx"); + + info!(event = "broadcasted kickoff tx", %deposit_txid, %kickoff_txid, %own_index); + + let claim_tx_with_commitment = claim_tx + .finalize( + deposit_txid, + &connectors.kickoff, + &self.msk, + bridge_out_txid, + superblock_period_start_ts, + ) + .await; + + let raw_claim_tx: String = consensus::encode::serialize_hex(&claim_tx_with_commitment); + trace!(event = "finalized claim tx", %deposit_txid, ?claim_tx_with_commitment, %raw_claim_tx, %own_index); + + let claim_txid = self + .agent + .client + .send_raw_transaction(&claim_tx_with_commitment) + .await + .expect("should be able to publish claim tx with commitment to bridge_out_txid and superblock period start_ts"); + + info!(event = "broadcasted claim tx", %deposit_txid, %claim_txid, %own_index); + } + + #[cfg(not(feature = "mock"))] + async fn pay_user( + &self, + user_pk: XOnlyPublicKey, + network: bitcoin::Network, + own_index: OperatorIdx, + ) -> anyhow::Result { + if self.am_i_faulty() { + let buffer: [u8; 32] = OsRng.gen(); + let fake_txid = Txid::from_byte_array(buffer); + + warn!(action = "faking bridge out", %fake_txid, %own_index); + return Ok(fake_txid); + } + + let net_payment = BRIDGE_DENOMINATION - OPERATOR_FEE; + + // don't use kickoff utxo for payment + let reserved_utxos = self.db.selected_outpoints().await; + + let (change_address, outpoint, total_amount, prevout) = self + .agent + .select_utxo(net_payment, reserved_utxos) + .await + .expect("at least one funding utxo must be present in wallet"); + + let change_amount = total_amount - net_payment - MIN_RELAY_FEE; + debug!(%change_address, %change_amount, %total_amount, %net_payment, ?prevout, "found funding utxo for bridge out"); + + let bridge_out = BridgeOut::new( + network, + own_index, + vec![outpoint], + net_payment, + change_address, + change_amount, + user_pk, + ); + + let signed_tx_result = self + .agent + .client + .sign_raw_transaction_with_wallet(&bridge_out.tx()) + .await + .expect("must be able to sign bridge out transaction"); + + assert!( + signed_tx_result.complete, + "bridge out tx must be completely signed" + ); + + let signed_tx: Transaction = consensus::encode::deserialize_hex(&signed_tx_result.hex) + .expect("should be able to deserialize signed tx"); + + match self.agent.client.send_raw_transaction(&signed_tx).await { + Ok(txid) => { + info!(event = "paid the user successfully", %txid, %own_index); + Ok(txid) + } + Err(e) => { + error!(?e, "could not broadcast bridge out tx"); + + bail!(e.to_string()); + } + } + } + + pub async fn handle_withdrawal_faulty(&self) { + // for withdrawal duty (assigned and self.am_i_faulty()), + // 1. create tx graph from public data + // 2. publish kickoff -> claim + // 3. compute superblock and faulty proof + // 4. publish assert chain + // 5. try to settle reimbursement tx after wait time + } +} + +pub fn mock_assertions() -> Assertions { + Assertions { + bridge_out_txid: [ + 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240, 32, 17, 34, 51, + 68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255, 32, + ], + superblock_hash: [ + 170, 187, 204, 221, 238, 255, 32, 17, 34, 51, 68, 85, 102, 119, 136, 153, 170, 187, + 204, 221, 238, 255, 32, 17, 34, 51, 68, 85, 102, 119, 136, 153, + ], + superblock_period_start_ts: [239, 190, 173, 222], + groth16: ( + [[ + 80, 109, 248, 180, 9, 193, 114, 154, 115, 169, 168, 240, 183, 156, 241, 46, 0, 195, + 134, 12, 230, 246, 101, 202, 24, 228, 13, 116, 183, 37, 201, 128, + ]], + [ + [ + 112, 32, 253, 238, 88, 207, 72, 195, 44, 15, 28, 45, 111, 218, 102, 212, 59, + 62, 92, 205, 53, 209, 79, 23, 87, 165, 0, 22, 142, 131, 210, 116, + ], + [ + 177, 135, 40, 180, 190, 166, 111, 221, 234, 212, 8, 155, 1, 162, 125, 235, 228, + 15, 224, 181, 139, 66, 227, 106, 118, 195, 110, 65, 62, 146, 11, 174, + ], + [ + 96, 67, 40, 63, 210, 100, 68, 67, 214, 86, 1, 22, 44, 130, 135, 244, 41, 213, + 154, 166, 25, 183, 13, 58, 206, 22, 59, 119, 155, 206, 164, 7, + ], + [ + 161, 121, 10, 93, 68, 113, 217, 16, 116, 190, 44, 205, 49, 203, 204, 151, 29, + 64, 97, 119, 220, 119, 161, 205, 165, 142, 60, 253, 251, 164, 35, 102, + ], + [ + 49, 164, 104, 126, 26, 19, 151, 135, 154, 195, 146, 177, 199, 88, 105, 212, 55, + 99, 238, 5, 201, 100, 61, 237, 129, 46, 207, 160, 5, 222, 135, 113, + ], + [ + 177, 101, 136, 196, 109, 10, 161, 23, 222, 239, 198, 72, 186, 53, 129, 183, 11, + 169, 19, 90, 210, 42, 21, 233, 37, 62, 94, 92, 233, 138, 38, 221, + ], + [ + 65, 111, 124, 126, 121, 141, 146, 7, 244, 45, 7, 234, 109, 117, 143, 250, 187, + 31, 40, 105, 45, 110, 231, 13, 208, 112, 33, 98, 222, 18, 248, 182, + ], + [ + 225, 84, 108, 194, 119, 110, 110, 100, 36, 85, 157, 165, 159, 96, 252, 210, + 185, 57, 95, 149, 195, 81, 156, 52, 88, 214, 244, 12, 247, 11, 150, 71, + ], + [ + 97, 64, 80, 35, 13, 233, 167, 154, 124, 78, 178, 162, 106, 54, 59, 64, 229, + 179, 205, 196, 179, 223, 128, 226, 154, 243, 232, 177, 81, 68, 225, 216, + ], + [ + 161, 235, 105, 177, 186, 96, 116, 226, 41, 91, 243, 16, 241, 48, 178, 229, 142, + 128, 16, 246, 216, 208, 89, 32, 239, 124, 63, 233, 126, 76, 180, 23, + ], + [ + 209, 164, 126, 151, 173, 208, 97, 238, 235, 5, 224, 7, 170, 235, 4, 111, 116, + 29, 236, 170, 243, 230, 80, 104, 195, 75, 164, 95, 185, 53, 160, 200, + ], + [ + 18, 237, 242, 40, 102, 5, 209, 218, 203, 74, 198, 220, 106, 192, 241, 142, 48, + 170, 113, 196, 156, 149, 152, 116, 126, 63, 208, 214, 111, 195, 2, 134, + ], + [ + 2, 118, 176, 214, 113, 165, 255, 95, 224, 25, 139, 17, 73, 243, 154, 101, 2, + 132, 23, 136, 64, 114, 71, 63, 147, 223, 126, 135, 172, 229, 202, 46, + ], + [ + 225, 174, 132, 233, 32, 201, 204, 25, 231, 53, 13, 57, 118, 65, 43, 6, 44, 10, + 246, 127, 187, 252, 10, 118, 144, 48, 173, 111, 177, 232, 52, 242, + ], + [ + 161, 238, 13, 77, 218, 255, 35, 80, 161, 139, 171, 94, 167, 97, 85, 133, 185, + 254, 205, 2, 202, 32, 222, 196, 152, 2, 158, 85, 82, 183, 194, 43, + ], + [ + 66, 40, 154, 53, 235, 37, 20, 104, 168, 251, 111, 239, 167, 14, 29, 246, 251, + 129, 195, 129, 43, 143, 225, 43, 37, 10, 249, 219, 153, 244, 81, 69, + ], + [ + 66, 148, 190, 211, 17, 10, 225, 206, 34, 114, 27, 22, 10, 245, 246, 229, 241, + 243, 190, 26, 8, 159, 9, 69, 69, 22, 216, 100, 82, 178, 209, 192, + ], + [ + 226, 128, 110, 219, 218, 31, 84, 117, 203, 190, 151, 2, 182, 60, 152, 50, 228, + 226, 113, 85, 201, 228, 147, 231, 30, 85, 93, 7, 188, 199, 21, 130, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + ], + [ + 240, 2, 111, 140, 141, 170, 146, 156, 123, 129, 139, 28, 145, 231, 173, 147, + 67, 224, 16, 227, 126, 213, 11, 245, 2, 63, 96, 228, 99, 183, 243, 153, + ], + [ + 48, 250, 81, 177, 119, 8, 60, 97, 236, 87, 237, 15, 206, 171, 148, 158, 63, + 212, 185, 82, 223, 90, 219, 222, 81, 101, 40, 161, 223, 41, 21, 34, + ], + [ + 50, 122, 72, 143, 82, 176, 116, 146, 90, 178, 11, 62, 220, 97, 27, 42, 71, 131, + 187, 165, 151, 31, 43, 113, 197, 175, 166, 237, 95, 160, 193, 49, + ], + [ + 130, 60, 21, 130, 34, 229, 41, 195, 72, 175, 30, 0, 239, 16, 211, 164, 38, 224, + 6, 180, 249, 252, 116, 123, 29, 54, 207, 71, 31, 252, 177, 136, + ], + [ + 146, 119, 142, 36, 1, 73, 54, 239, 41, 236, 201, 145, 97, 184, 197, 82, 161, + 148, 11, 6, 91, 21, 204, 98, 144, 7, 81, 53, 1, 227, 4, 156, + ], + [ + 96, 72, 119, 220, 79, 200, 158, 38, 193, 169, 124, 194, 123, 92, 241, 152, 212, + 180, 246, 186, 105, 179, 171, 181, 143, 38, 42, 59, 119, 70, 248, 71, + ], + [ + 194, 6, 108, 27, 82, 109, 131, 35, 198, 133, 189, 198, 159, 231, 156, 33, 5, + 101, 87, 175, 165, 57, 35, 144, 61, 178, 57, 128, 85, 52, 217, 63, + ], + [ + 225, 19, 214, 240, 111, 230, 42, 175, 121, 238, 205, 215, 242, 188, 146, 232, + 70, 11, 116, 173, 66, 2, 104, 11, 207, 99, 41, 159, 146, 157, 2, 23, + ], + [ + 146, 116, 206, 214, 98, 58, 7, 94, 70, 102, 163, 46, 31, 171, 104, 32, 149, 57, + 53, 222, 215, 125, 170, 131, 173, 134, 101, 87, 221, 143, 151, 174, + ], + [ + 33, 160, 77, 9, 52, 137, 255, 131, 189, 194, 178, 99, 236, 56, 226, 119, 188, + 219, 238, 255, 26, 235, 48, 168, 52, 20, 146, 108, 12, 163, 35, 26, + ], + [ + 161, 164, 82, 52, 34, 242, 246, 38, 232, 70, 77, 148, 15, 219, 212, 148, 39, + 29, 85, 15, 216, 125, 37, 219, 54, 79, 175, 203, 197, 31, 84, 233, + ], + ], + [ + [ + 53, 97, 8, 126, 205, 170, 108, 97, 149, 233, 13, 68, 51, 217, 183, 26, 169, 55, + 27, 144, + ], + [ + 68, 156, 85, 148, 242, 169, 175, 91, 208, 190, 113, 253, 70, 150, 124, 80, 67, + 82, 221, 144, + ], + [ + 20, 102, 248, 19, 244, 7, 157, 84, 22, 135, 65, 2, 175, 59, 115, 28, 63, 57, + 219, 225, + ], + [ + 7, 99, 180, 128, 201, 48, 60, 2, 254, 124, 34, 223, 52, 122, 230, 113, 253, + 198, 193, 171, + ], + [ + 81, 173, 157, 193, 190, 169, 69, 64, 134, 135, 98, 116, 82, 74, 213, 240, 128, + 135, 30, 162, + ], + [ + 41, 96, 163, 78, 226, 27, 34, 213, 50, 126, 78, 164, 231, 164, 224, 230, 218, + 130, 110, 248, + ], + [ + 194, 66, 145, 64, 22, 197, 140, 64, 81, 178, 183, 100, 126, 235, 73, 208, 48, + 250, 90, 12, + ], + [ + 1, 250, 184, 76, 55, 239, 238, 224, 108, 74, 140, 130, 176, 205, 18, 213, 168, + 167, 134, 140, + ], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [ + 186, 190, 195, 165, 99, 209, 112, 248, 62, 5, 197, 146, 50, 65, 57, 164, 221, + 201, 14, 205, + ], + [ + 186, 178, 209, 41, 9, 247, 140, 37, 182, 5, 78, 93, 55, 145, 192, 184, 40, 140, + 31, 45, + ], + [ + 165, 73, 71, 32, 214, 193, 67, 161, 30, 215, 123, 132, 244, 173, 230, 34, 41, + 1, 153, 129, + ], + [ + 43, 193, 157, 22, 1, 57, 173, 66, 169, 219, 218, 197, 254, 46, 190, 212, 188, + 227, 223, 156, + ], + [ + 99, 183, 228, 207, 41, 53, 29, 170, 195, 119, 1, 1, 230, 67, 84, 88, 166, 83, + 110, 232, + ], + [ + 94, 253, 71, 217, 180, 8, 207, 67, 109, 248, 149, 196, 120, 13, 169, 27, 169, + 88, 53, 202, + ], + [ + 1, 109, 28, 218, 244, 69, 57, 231, 54, 76, 164, 43, 215, 103, 1, 178, 181, 181, + 228, 153, + ], + [ + 130, 95, 162, 67, 152, 201, 119, 14, 23, 102, 132, 6, 20, 100, 63, 82, 70, 234, + 140, 171, + ], + [ + 251, 7, 160, 74, 10, 35, 248, 53, 12, 50, 18, 5, 95, 89, 118, 185, 253, 97, 74, + 86, + ], + [ + 19, 41, 2, 182, 219, 167, 72, 54, 69, 128, 28, 108, 253, 71, 144, 18, 92, 247, + 52, 124, + ], + [ + 140, 139, 61, 168, 12, 37, 69, 182, 164, 101, 131, 161, 92, 103, 82, 230, 18, + 64, 172, 15, + ], + [ + 220, 90, 113, 13, 182, 186, 25, 233, 229, 31, 106, 53, 8, 248, 154, 207, 7, + 241, 151, 45, + ], + [ + 9, 33, 170, 238, 51, 70, 36, 34, 213, 31, 130, 204, 178, 80, 18, 21, 74, 236, + 103, 230, + ], + [ + 194, 108, 99, 78, 10, 19, 61, 207, 251, 118, 61, 122, 220, 127, 252, 80, 172, + 8, 105, 143, + ], + [ + 79, 35, 248, 41, 56, 124, 41, 227, 32, 254, 255, 13, 208, 196, 114, 202, 120, + 164, 4, 2, + ], + [ + 101, 253, 79, 200, 193, 227, 195, 172, 155, 187, 155, 56, 127, 207, 146, 148, + 192, 21, 161, 104, + ], + [ + 36, 229, 111, 131, 207, 67, 39, 200, 178, 163, 223, 100, 45, 35, 48, 67, 44, + 97, 59, 142, + ], + [ + 32, 206, 225, 15, 239, 200, 45, 254, 183, 204, 79, 215, 125, 183, 36, 95, 6, + 92, 38, 255, + ], + [ + 89, 57, 45, 54, 150, 13, 29, 94, 137, 226, 85, 126, 82, 112, 40, 244, 149, 29, + 118, 136, + ], + [ + 120, 33, 170, 53, 8, 136, 11, 29, 217, 212, 24, 46, 214, 233, 227, 198, 85, 77, + 13, 42, + ], + [ + 128, 108, 16, 98, 229, 27, 241, 248, 127, 48, 112, 244, 147, 118, 169, 231, 20, + 120, 149, 46, + ], + [ + 162, 36, 43, 207, 223, 231, 241, 66, 102, 40, 72, 71, 11, 247, 178, 141, 238, + 109, 113, 253, + ], + [ + 86, 169, 195, 159, 75, 98, 69, 66, 40, 219, 140, 138, 45, 214, 117, 37, 93, 43, + 201, 138, + ], + [ + 212, 207, 77, 229, 120, 63, 80, 103, 110, 95, 72, 220, 158, 229, 249, 0, 228, + 151, 98, 87, + ], + [ + 136, 231, 135, 102, 160, 122, 93, 225, 255, 167, 253, 87, 163, 77, 188, 253, + 223, 227, 102, 58, + ], + [ + 174, 222, 136, 142, 155, 38, 151, 115, 156, 221, 36, 30, 64, 69, 28, 98, 33, + 46, 193, 84, + ], + [ + 142, 240, 102, 46, 48, 62, 20, 182, 222, 173, 138, 111, 198, 129, 72, 187, 66, + 142, 154, 7, + ], + [ + 23, 145, 74, 201, 44, 121, 239, 204, 176, 57, 192, 194, 145, 172, 31, 89, 50, + 13, 239, 43, + ], + [ + 199, 217, 94, 129, 216, 196, 92, 41, 40, 202, 212, 178, 186, 200, 174, 165, + 248, 182, 60, 124, + ], + [ + 100, 87, 201, 223, 79, 86, 247, 248, 39, 245, 173, 93, 49, 107, 217, 181, 155, + 121, 204, 66, + ], + [ + 252, 159, 26, 49, 229, 254, 114, 95, 31, 3, 135, 134, 254, 78, 122, 16, 151, + 235, 73, 252, + ], + [ + 181, 67, 179, 64, 85, 200, 33, 95, 196, 122, 173, 186, 228, 156, 65, 115, 31, + 98, 114, 12, + ], + [ + 218, 207, 169, 142, 43, 204, 7, 235, 60, 238, 202, 28, 172, 137, 249, 195, 120, + 60, 50, 165, + ], + [ + 155, 195, 3, 80, 70, 133, 53, 206, 243, 173, 80, 208, 21, 10, 168, 59, 29, 34, + 15, 24, + ], + [ + 89, 251, 162, 79, 164, 82, 111, 130, 148, 133, 218, 7, 36, 172, 93, 151, 68, + 58, 185, 169, + ], + [ + 150, 104, 230, 250, 31, 37, 38, 235, 143, 244, 81, 56, 25, 250, 129, 121, 171, + 224, 204, 179, + ], + [ + 111, 19, 21, 208, 180, 213, 65, 41, 215, 50, 91, 28, 127, 115, 83, 68, 207, + 113, 213, 11, + ], + [ + 181, 11, 247, 66, 223, 178, 19, 65, 45, 141, 235, 10, 245, 198, 18, 231, 7, + 136, 225, 219, + ], + [ + 32, 123, 43, 203, 136, 150, 158, 43, 123, 102, 224, 15, 130, 129, 120, 241, 84, + 170, 143, 234, + ], + [ + 229, 255, 214, 58, 215, 84, 129, 64, 218, 78, 115, 6, 150, 99, 231, 120, 102, + 132, 90, 183, + ], + [ + 21, 21, 64, 161, 176, 180, 135, 204, 246, 228, 107, 88, 5, 77, 197, 46, 232, + 211, 250, 60, + ], + [ + 101, 242, 191, 132, 56, 38, 132, 101, 223, 201, 185, 88, 34, 247, 78, 132, 253, + 54, 252, 191, + ], + [ + 234, 16, 221, 38, 223, 178, 231, 109, 106, 26, 169, 53, 16, 59, 77, 116, 129, + 8, 34, 182, + ], + [ + 214, 247, 216, 161, 249, 211, 27, 149, 182, 112, 221, 96, 155, 140, 172, 47, + 133, 192, 81, 109, + ], + [ + 179, 108, 39, 196, 126, 2, 189, 60, 101, 230, 140, 58, 162, 149, 57, 187, 59, + 137, 152, 222, + ], + [ + 10, 40, 154, 133, 18, 33, 154, 130, 42, 79, 111, 1, 13, 141, 198, 250, 185, + 210, 247, 144, + ], + [ + 147, 112, 58, 90, 145, 103, 205, 106, 192, 96, 250, 31, 52, 157, 72, 213, 5, + 156, 222, 20, + ], + [ + 77, 179, 121, 196, 169, 217, 40, 172, 7, 192, 2, 168, 234, 217, 4, 230, 102, + 118, 33, 160, + ], + [ + 197, 76, 225, 121, 20, 116, 89, 195, 250, 232, 46, 183, 80, 207, 35, 153, 173, + 206, 50, 208, + ], + [ + 236, 224, 63, 120, 177, 191, 96, 252, 93, 13, 23, 231, 72, 189, 112, 150, 7, + 152, 135, 19, + ], + [ + 167, 44, 224, 96, 35, 250, 26, 189, 84, 198, 161, 188, 126, 35, 35, 171, 196, + 250, 24, 51, + ], + [ + 157, 24, 71, 138, 144, 106, 145, 215, 98, 97, 70, 11, 102, 248, 152, 130, 230, + 11, 236, 55, + ], + [ + 49, 162, 205, 68, 127, 190, 55, 235, 124, 71, 40, 233, 121, 19, 71, 153, 27, + 152, 138, 64, + ], + [ + 50, 166, 226, 38, 183, 245, 82, 29, 27, 202, 177, 193, 91, 248, 52, 100, 133, + 215, 179, 36, + ], + [ + 166, 196, 254, 115, 63, 83, 44, 56, 27, 84, 228, 115, 176, 48, 47, 121, 206, + 159, 219, 169, + ], + [ + 134, 168, 201, 87, 249, 120, 103, 217, 64, 171, 149, 183, 180, 44, 9, 75, 242, + 10, 127, 119, + ], + [ + 25, 73, 39, 239, 120, 189, 152, 141, 174, 90, 117, 117, 192, 35, 107, 112, 100, + 119, 195, 129, + ], + [ + 252, 236, 121, 241, 91, 198, 165, 239, 91, 160, 166, 203, 192, 238, 11, 205, + 61, 194, 199, 230, + ], + [ + 70, 44, 123, 81, 215, 116, 221, 237, 52, 201, 148, 249, 211, 13, 77, 148, 17, + 184, 86, 215, + ], + [ + 109, 148, 235, 158, 56, 81, 91, 84, 156, 173, 73, 11, 65, 249, 249, 97, 121, + 220, 89, 40, + ], + [ + 88, 20, 4, 32, 234, 69, 66, 190, 16, 111, 92, 234, 238, 43, 144, 214, 215, 37, + 202, 233, + ], + [ + 37, 201, 175, 12, 193, 38, 141, 15, 77, 131, 53, 238, 42, 195, 223, 8, 236, 87, + 30, 2, + ], + [ + 140, 107, 226, 226, 73, 97, 156, 189, 147, 106, 103, 223, 38, 89, 183, 29, 49, + 135, 135, 90, + ], + [ + 174, 230, 167, 136, 156, 238, 78, 184, 101, 7, 101, 64, 105, 192, 8, 23, 55, + 223, 250, 246, + ], + [ + 74, 227, 28, 145, 141, 158, 57, 208, 125, 127, 198, 46, 217, 3, 135, 198, 215, + 44, 134, 152, + ], + [ + 169, 241, 178, 138, 192, 162, 39, 38, 88, 235, 6, 251, 234, 210, 92, 55, 171, + 197, 158, 139, + ], + [ + 242, 61, 232, 35, 243, 73, 203, 245, 229, 224, 46, 171, 138, 254, 29, 129, 129, + 174, 141, 58, + ], + [ + 21, 178, 78, 19, 120, 116, 65, 188, 94, 23, 1, 212, 84, 19, 183, 99, 251, 107, + 159, 148, + ], + [ + 208, 39, 225, 131, 7, 231, 155, 239, 186, 91, 50, 108, 173, 222, 211, 196, 205, + 81, 80, 56, + ], + [ + 29, 226, 49, 0, 201, 210, 34, 214, 156, 86, 18, 174, 231, 215, 172, 8, 237, 23, + 227, 29, + ], + [ + 4, 113, 58, 245, 88, 66, 218, 149, 29, 42, 197, 122, 219, 134, 113, 226, 226, + 170, 228, 4, + ], + [ + 185, 22, 92, 127, 54, 137, 96, 0, 42, 49, 6, 137, 33, 242, 18, 87, 76, 32, 132, + 85, + ], + [ + 79, 27, 74, 33, 250, 87, 37, 46, 226, 45, 173, 188, 191, 50, 235, 105, 208, + 145, 103, 242, + ], + [ + 85, 90, 49, 57, 9, 197, 207, 103, 117, 13, 108, 197, 88, 174, 185, 14, 182, 68, + 95, 158, + ], + [ + 173, 176, 72, 104, 224, 199, 247, 196, 112, 53, 47, 203, 225, 44, 24, 141, 228, + 131, 63, 197, + ], + [ + 24, 37, 219, 63, 220, 219, 20, 96, 204, 132, 136, 35, 184, 228, 214, 188, 200, + 237, 98, 203, + ], + [ + 106, 140, 171, 171, 218, 31, 219, 61, 37, 13, 185, 87, 162, 27, 16, 70, 119, + 231, 134, 196, + ], + [ + 144, 34, 177, 204, 187, 110, 148, 215, 153, 86, 216, 181, 84, 130, 162, 145, + 140, 114, 221, 83, + ], + [ + 34, 174, 122, 41, 93, 65, 154, 224, 196, 30, 85, 203, 254, 192, 244, 21, 206, + 208, 70, 174, + ], + [ + 103, 204, 201, 132, 83, 146, 137, 73, 16, 55, 153, 112, 166, 248, 230, 134, + 154, 214, 151, 185, + ], + [ + 160, 38, 37, 22, 73, 105, 127, 210, 224, 174, 6, 151, 151, 186, 61, 76, 113, + 62, 126, 168, + ], + [ + 45, 164, 116, 84, 30, 108, 120, 56, 145, 158, 37, 119, 135, 214, 94, 231, 162, + 109, 29, 174, + ], + [ + 5, 130, 61, 57, 158, 99, 22, 88, 62, 208, 253, 0, 101, 227, 88, 194, 11, 221, + 251, 75, + ], + [ + 64, 133, 79, 202, 144, 157, 236, 15, 54, 191, 101, 110, 251, 70, 47, 242, 234, + 129, 144, 26, + ], + [ + 98, 68, 199, 193, 116, 32, 158, 110, 139, 248, 201, 106, 33, 218, 234, 44, 37, + 251, 91, 109, + ], + [ + 37, 237, 209, 7, 129, 194, 233, 239, 160, 26, 247, 37, 86, 95, 247, 3, 41, 56, + 85, 237, + ], + [ + 16, 19, 3, 95, 37, 21, 79, 5, 118, 91, 116, 205, 51, 28, 230, 214, 172, 133, + 73, 40, + ], + [ + 144, 105, 10, 22, 146, 44, 97, 137, 220, 189, 191, 161, 216, 82, 126, 82, 12, + 81, 251, 171, + ], + [ + 112, 75, 141, 242, 31, 73, 233, 232, 164, 142, 15, 96, 119, 31, 32, 135, 65, + 76, 105, 225, + ], + [ + 100, 61, 65, 215, 28, 253, 107, 116, 68, 113, 226, 135, 9, 149, 250, 92, 180, + 61, 143, 176, + ], + [ + 65, 240, 192, 94, 225, 114, 159, 197, 217, 187, 234, 15, 167, 124, 202, 199, + 42, 68, 217, 119, + ], + [ + 64, 91, 95, 106, 200, 255, 162, 63, 135, 206, 239, 224, 16, 35, 43, 197, 239, + 104, 115, 175, + ], + [ + 72, 47, 7, 200, 183, 197, 236, 20, 164, 151, 244, 131, 167, 187, 86, 184, 62, + 122, 54, 18, + ], + [ + 208, 62, 23, 247, 134, 116, 148, 75, 51, 171, 80, 132, 75, 41, 42, 55, 119, + 156, 169, 221, + ], + [ + 189, 61, 156, 6, 246, 16, 255, 228, 221, 39, 211, 187, 251, 175, 6, 177, 50, + 173, 55, 29, + ], + [ + 121, 112, 237, 115, 138, 191, 148, 111, 92, 242, 103, 187, 36, 24, 135, 211, + 248, 98, 183, 225, + ], + [ + 156, 226, 144, 114, 53, 43, 193, 98, 249, 1, 234, 184, 252, 116, 88, 143, 89, + 123, 220, 226, + ], + [ + 192, 245, 91, 16, 5, 16, 231, 131, 101, 75, 224, 156, 14, 233, 0, 42, 76, 210, + 72, 53, + ], + [ + 238, 148, 119, 163, 80, 72, 38, 62, 239, 49, 242, 191, 178, 233, 17, 230, 129, + 34, 146, 32, + ], + [ + 148, 90, 214, 0, 149, 149, 228, 63, 146, 255, 21, 138, 103, 93, 134, 150, 247, + 236, 24, 170, + ], + [ + 34, 200, 46, 152, 169, 118, 33, 139, 253, 4, 35, 81, 94, 128, 204, 81, 206, + 225, 6, 124, + ], + [ + 250, 108, 179, 20, 87, 229, 31, 175, 191, 95, 45, 122, 138, 142, 42, 172, 80, + 133, 102, 4, + ], + [ + 177, 95, 245, 193, 39, 222, 232, 210, 207, 103, 122, 137, 66, 125, 84, 62, 23, + 197, 100, 42, + ], + [ + 199, 199, 185, 252, 149, 232, 160, 16, 125, 246, 4, 237, 40, 181, 45, 206, 184, + 97, 95, 55, + ], + [ + 171, 213, 235, 216, 162, 253, 228, 83, 22, 40, 106, 52, 72, 14, 115, 98, 170, + 48, 187, 191, + ], + [ + 73, 175, 177, 58, 117, 190, 58, 216, 135, 211, 43, 53, 204, 109, 213, 217, 131, + 193, 130, 103, + ], + [ + 157, 233, 219, 70, 104, 34, 35, 182, 42, 2, 171, 255, 234, 192, 155, 5, 50, 78, + 214, 65, + ], + [ + 73, 137, 189, 41, 217, 91, 212, 73, 104, 173, 240, 12, 132, 9, 171, 18, 144, + 111, 76, 90, + ], + [ + 105, 218, 90, 13, 119, 118, 97, 174, 138, 183, 103, 231, 43, 212, 15, 72, 22, + 158, 44, 3, + ], + [ + 38, 108, 243, 6, 193, 198, 229, 219, 155, 18, 221, 195, 213, 146, 241, 216, + 124, 217, 235, 175, + ], + [ + 176, 209, 42, 182, 170, 49, 89, 194, 37, 151, 112, 240, 135, 130, 158, 133, 89, + 100, 63, 186, + ], + [ + 206, 172, 2, 17, 104, 126, 171, 170, 5, 219, 45, 150, 70, 125, 244, 17, 90, + 182, 252, 92, + ], + [ + 230, 178, 84, 14, 111, 23, 40, 64, 116, 155, 186, 89, 124, 227, 47, 120, 134, + 241, 2, 162, + ], + [ + 204, 72, 170, 7, 17, 182, 131, 189, 54, 229, 45, 51, 136, 142, 105, 95, 226, + 227, 171, 50, + ], + [ + 252, 36, 153, 6, 229, 55, 133, 23, 114, 147, 51, 97, 182, 237, 158, 233, 127, + 30, 111, 26, + ], + [ + 240, 219, 185, 48, 239, 134, 164, 216, 67, 6, 87, 10, 63, 226, 150, 93, 90, + 187, 29, 21, + ], + [ + 135, 41, 241, 245, 192, 102, 193, 215, 64, 72, 58, 39, 221, 190, 147, 18, 251, + 14, 36, 242, + ], + [ + 185, 135, 227, 198, 115, 69, 122, 249, 141, 193, 202, 247, 14, 5, 164, 66, 191, + 149, 41, 231, + ], + [ + 46, 209, 107, 115, 212, 92, 218, 242, 212, 101, 224, 26, 200, 178, 85, 248, + 205, 79, 180, 185, + ], + [ + 51, 169, 142, 166, 199, 174, 5, 61, 204, 90, 42, 17, 48, 250, 37, 230, 118, + 170, 138, 251, + ], + [ + 21, 4, 138, 214, 218, 192, 161, 211, 125, 66, 116, 50, 171, 156, 42, 138, 25, + 81, 210, 27, + ], + [ + 80, 239, 128, 9, 124, 13, 200, 221, 48, 37, 94, 111, 139, 19, 24, 93, 51, 30, + 150, 151, + ], + [ + 151, 7, 100, 230, 123, 171, 250, 190, 33, 132, 144, 116, 81, 104, 185, 69, 33, + 129, 132, 71, + ], + [ + 148, 16, 24, 119, 120, 31, 220, 101, 247, 83, 66, 0, 128, 55, 251, 255, 34, + 112, 225, 141, + ], + [ + 192, 233, 227, 143, 151, 250, 141, 254, 228, 212, 20, 2, 2, 70, 117, 148, 140, + 39, 134, 34, + ], + [ + 58, 229, 237, 174, 133, 105, 61, 93, 146, 175, 216, 63, 251, 95, 70, 88, 177, + 226, 72, 235, + ], + [ + 22, 229, 248, 98, 1, 99, 178, 62, 198, 100, 17, 44, 146, 46, 96, 117, 212, 34, + 170, 236, + ], + [ + 1, 145, 113, 254, 232, 28, 151, 122, 43, 135, 219, 10, 80, 251, 222, 120, 249, + 180, 63, 9, + ], + [ + 0, 185, 7, 117, 77, 69, 243, 64, 60, 223, 57, 47, 213, 231, 155, 1, 29, 184, 0, + 168, + ], + [ + 181, 113, 17, 65, 90, 102, 182, 154, 26, 1, 95, 159, 114, 55, 216, 7, 101, 37, + 36, 180, + ], + [ + 67, 227, 74, 226, 196, 231, 48, 81, 126, 60, 170, 23, 235, 147, 5, 34, 67, 58, + 2, 251, + ], + [ + 93, 70, 237, 19, 215, 246, 173, 117, 135, 145, 91, 181, 156, 133, 216, 155, 15, + 79, 134, 242, + ], + [ + 143, 186, 108, 111, 212, 53, 183, 160, 76, 141, 133, 224, 91, 175, 243, 98, + 187, 88, 173, 128, + ], + [ + 148, 21, 204, 111, 61, 87, 155, 186, 65, 105, 217, 1, 188, 76, 134, 201, 148, + 242, 143, 7, + ], + [ + 150, 93, 56, 52, 178, 168, 28, 228, 169, 111, 3, 28, 220, 141, 160, 176, 39, + 154, 116, 215, + ], + [ + 223, 106, 14, 181, 238, 209, 238, 244, 118, 130, 43, 77, 103, 121, 2, 1, 178, + 0, 219, 88, + ], + [ + 6, 226, 244, 108, 50, 134, 249, 50, 101, 249, 168, 240, 190, 153, 229, 152, + 154, 189, 234, 164, + ], + [ + 184, 169, 115, 177, 172, 28, 193, 113, 147, 100, 235, 36, 164, 112, 235, 97, + 102, 16, 80, 51, + ], + [ + 200, 83, 245, 31, 178, 177, 152, 158, 196, 239, 47, 157, 154, 6, 130, 45, 192, + 209, 169, 174, + ], + [ + 52, 137, 153, 213, 42, 78, 175, 153, 79, 175, 142, 137, 208, 14, 125, 181, 189, + 212, 26, 233, + ], + [ + 76, 35, 226, 136, 26, 85, 34, 66, 76, 118, 161, 13, 23, 136, 238, 137, 201, + 246, 122, 156, + ], + [ + 84, 157, 189, 139, 187, 126, 46, 64, 146, 56, 227, 157, 77, 158, 121, 242, 165, + 45, 251, 190, + ], + [ + 96, 143, 181, 238, 74, 175, 218, 181, 19, 92, 170, 120, 27, 172, 89, 64, 117, + 209, 155, 117, + ], + [ + 13, 152, 39, 89, 134, 6, 182, 1, 139, 14, 13, 5, 254, 110, 52, 249, 254, 164, + 191, 45, + ], + [ + 230, 98, 108, 134, 196, 228, 178, 136, 93, 119, 181, 214, 165, 50, 232, 25, 63, + 190, 247, 53, + ], + [ + 76, 97, 2, 93, 83, 26, 205, 115, 215, 223, 118, 71, 136, 29, 189, 147, 36, 21, + 37, 77, + ], + [ + 224, 223, 107, 41, 142, 92, 254, 25, 154, 212, 224, 43, 164, 59, 192, 19, 4, + 70, 176, 64, + ], + [ + 232, 181, 86, 215, 197, 45, 221, 211, 126, 184, 166, 178, 154, 185, 90, 156, + 30, 250, 138, 17, + ], + [ + 233, 26, 4, 224, 44, 106, 89, 126, 198, 232, 20, 245, 43, 163, 12, 126, 21, + 168, 6, 130, + ], + [ + 38, 123, 216, 228, 115, 124, 135, 125, 101, 68, 119, 225, 237, 162, 195, 102, + 207, 77, 17, 10, + ], + [ + 239, 80, 245, 221, 144, 61, 9, 104, 34, 72, 125, 142, 204, 178, 65, 190, 156, + 156, 189, 45, + ], + [ + 138, 222, 173, 46, 149, 38, 20, 205, 85, 28, 171, 206, 19, 134, 165, 31, 184, + 84, 187, 36, + ], + [ + 90, 42, 99, 219, 115, 11, 46, 251, 208, 111, 31, 175, 108, 69, 126, 220, 162, + 233, 244, 51, + ], + [ + 129, 192, 205, 33, 198, 125, 199, 187, 94, 151, 228, 235, 50, 144, 20, 140, + 156, 133, 211, 49, + ], + [ + 37, 213, 19, 174, 117, 129, 9, 146, 87, 223, 133, 126, 52, 163, 39, 77, 223, 3, + 85, 11, + ], + [ + 225, 66, 97, 246, 130, 20, 161, 119, 225, 229, 99, 235, 51, 23, 80, 253, 38, + 249, 177, 90, + ], + [ + 224, 49, 90, 82, 38, 49, 92, 192, 91, 215, 77, 108, 57, 166, 181, 194, 12, 223, + 212, 213, + ], + [ + 235, 205, 220, 105, 126, 101, 96, 111, 40, 59, 171, 84, 73, 182, 143, 25, 176, + 187, 19, 83, + ], + [ + 18, 178, 209, 128, 58, 134, 167, 140, 106, 3, 43, 40, 152, 88, 112, 81, 166, + 252, 151, 159, + ], + [ + 114, 201, 84, 103, 153, 172, 239, 158, 103, 145, 63, 201, 189, 139, 190, 139, + 44, 115, 132, 13, + ], + [ + 195, 25, 200, 251, 196, 26, 163, 192, 250, 37, 146, 10, 111, 243, 137, 17, 209, + 102, 15, 233, + ], + [ + 109, 169, 253, 134, 34, 210, 164, 253, 181, 38, 229, 66, 157, 38, 29, 21, 77, + 11, 54, 9, + ], + [ + 247, 187, 220, 13, 198, 164, 38, 252, 150, 57, 163, 19, 97, 82, 62, 193, 73, + 246, 253, 154, + ], + [ + 191, 16, 121, 44, 167, 59, 57, 244, 119, 204, 186, 13, 72, 99, 56, 174, 207, 1, + 126, 201, + ], + [ + 252, 194, 99, 172, 202, 8, 170, 94, 122, 224, 83, 103, 128, 93, 196, 253, 106, + 34, 222, 51, + ], + [ + 21, 53, 124, 177, 27, 157, 176, 255, 11, 97, 195, 83, 50, 133, 220, 153, 38, + 204, 63, 1, + ], + [ + 121, 181, 165, 241, 130, 84, 159, 56, 250, 95, 170, 75, 76, 168, 119, 214, 19, + 33, 107, 35, + ], + [ + 92, 181, 59, 161, 250, 183, 211, 248, 237, 253, 210, 29, 115, 235, 214, 43, 28, + 24, 223, 96, + ], + [ + 170, 141, 119, 179, 113, 26, 115, 176, 152, 218, 54, 5, 209, 228, 212, 79, 229, + 93, 61, 200, + ], + [ + 15, 72, 108, 22, 127, 22, 101, 112, 122, 61, 212, 179, 64, 2, 72, 185, 0, 130, + 198, 214, + ], + [ + 153, 19, 114, 82, 176, 115, 214, 160, 250, 236, 166, 57, 224, 132, 61, 128, 51, + 100, 46, 68, + ], + [ + 171, 66, 109, 167, 99, 238, 97, 132, 178, 70, 109, 197, 134, 83, 25, 157, 158, + 183, 140, 151, + ], + [ + 25, 28, 95, 36, 83, 167, 81, 4, 199, 80, 121, 201, 113, 181, 138, 161, 108, + 221, 40, 17, + ], + [ + 54, 200, 102, 66, 243, 110, 216, 27, 202, 215, 125, 89, 218, 136, 226, 119, 69, + 207, 119, 160, + ], + [ + 60, 211, 17, 117, 115, 16, 210, 20, 153, 23, 104, 198, 234, 168, 4, 146, 75, + 22, 136, 180, + ], + [ + 32, 30, 201, 251, 171, 231, 228, 71, 97, 193, 249, 91, 233, 236, 201, 129, 201, + 78, 59, 118, + ], + [ + 203, 216, 23, 19, 10, 121, 252, 209, 184, 8, 38, 19, 28, 17, 166, 18, 215, 30, + 35, 192, + ], + [ + 248, 28, 194, 227, 140, 130, 204, 124, 89, 26, 118, 196, 207, 232, 200, 27, 64, + 50, 248, 209, + ], + [ + 127, 39, 85, 244, 124, 11, 194, 211, 208, 160, 76, 169, 118, 0, 28, 123, 34, + 83, 249, 116, + ], + [ + 224, 23, 85, 57, 130, 67, 112, 81, 166, 239, 151, 170, 173, 65, 37, 98, 59, 44, + 35, 164, + ], + [ + 255, 125, 185, 240, 70, 216, 108, 64, 101, 222, 220, 158, 150, 196, 215, 196, + 60, 16, 148, 196, + ], + [ + 120, 100, 219, 181, 237, 39, 0, 84, 206, 123, 235, 30, 249, 98, 52, 91, 167, + 171, 185, 63, + ], + [ + 35, 35, 55, 79, 26, 162, 36, 235, 109, 136, 43, 163, 5, 223, 18, 19, 245, 49, + 51, 157, + ], + [ + 188, 120, 64, 134, 30, 243, 90, 168, 84, 152, 88, 109, 168, 38, 2, 213, 93, + 159, 55, 202, + ], + [ + 233, 43, 176, 248, 43, 81, 67, 163, 147, 78, 8, 51, 216, 32, 108, 117, 29, 146, + 62, 31, + ], + [ + 135, 171, 193, 55, 243, 235, 134, 43, 247, 59, 234, 207, 162, 194, 139, 180, + 19, 49, 123, 216, + ], + [ + 167, 66, 104, 221, 190, 190, 140, 239, 129, 235, 136, 130, 21, 64, 16, 233, + 210, 109, 153, 124, + ], + [ + 163, 152, 153, 239, 143, 52, 113, 36, 141, 120, 167, 176, 102, 0, 216, 196, + 116, 120, 151, 225, + ], + [ + 141, 103, 197, 20, 188, 171, 189, 16, 206, 128, 153, 230, 144, 92, 187, 55, + 192, 255, 192, 214, + ], + [ + 137, 209, 218, 139, 94, 212, 176, 201, 128, 221, 135, 216, 32, 180, 58, 187, + 176, 73, 180, 20, + ], + [ + 203, 217, 73, 192, 139, 118, 160, 40, 107, 84, 91, 193, 104, 92, 253, 8, 94, + 96, 6, 240, + ], + [ + 59, 223, 136, 137, 52, 234, 193, 50, 195, 136, 206, 23, 63, 150, 19, 193, 230, + 139, 26, 184, + ], + [ + 139, 121, 24, 228, 203, 210, 35, 157, 218, 110, 221, 121, 240, 252, 149, 210, + 107, 188, 98, 118, + ], + [ + 254, 95, 232, 186, 154, 78, 32, 137, 52, 45, 163, 159, 52, 56, 233, 9, 28, 230, + 106, 137, + ], + [ + 226, 0, 18, 129, 100, 49, 72, 78, 116, 54, 9, 150, 230, 212, 118, 1, 149, 225, + 235, 146, + ], + [ + 9, 176, 75, 162, 117, 107, 182, 224, 72, 249, 123, 173, 29, 3, 66, 120, 24, 24, + 144, 103, + ], + [ + 150, 37, 188, 247, 209, 34, 91, 40, 166, 8, 27, 68, 194, 25, 153, 193, 253, 41, + 4, 236, + ], + [ + 149, 87, 28, 219, 49, 45, 144, 254, 2, 180, 233, 247, 52, 234, 90, 54, 115, 89, + 194, 127, + ], + [ + 201, 216, 230, 218, 7, 199, 9, 192, 191, 219, 29, 238, 123, 53, 35, 188, 175, + 179, 84, 119, + ], + [ + 98, 33, 17, 39, 29, 198, 223, 41, 241, 99, 37, 81, 14, 19, 176, 0, 17, 37, 0, + 230, + ], + [ + 30, 255, 147, 249, 111, 220, 114, 206, 161, 181, 221, 100, 70, 200, 67, 57, + 140, 35, 187, 137, + ], + [ + 85, 174, 177, 196, 110, 200, 160, 73, 232, 112, 133, 232, 133, 18, 57, 85, 11, + 139, 88, 57, + ], + [ + 39, 19, 220, 37, 27, 234, 81, 76, 22, 65, 253, 143, 60, 107, 43, 140, 7, 197, + 56, 199, + ], + [ + 176, 216, 77, 184, 97, 79, 169, 101, 23, 138, 204, 117, 85, 103, 5, 168, 224, + 239, 136, 116, + ], + [ + 68, 76, 87, 152, 149, 65, 237, 211, 122, 1, 52, 104, 134, 38, 213, 221, 9, 208, + 160, 116, + ], + [ + 234, 80, 54, 21, 240, 102, 255, 12, 207, 187, 126, 4, 165, 193, 242, 255, 193, + 177, 40, 35, + ], + [ + 132, 239, 46, 192, 69, 13, 202, 113, 252, 43, 172, 170, 179, 58, 197, 184, 173, + 221, 243, 107, + ], + [ + 247, 179, 107, 217, 49, 157, 17, 161, 179, 40, 155, 118, 250, 253, 235, 80, + 228, 180, 183, 92, + ], + [ + 150, 201, 217, 18, 164, 238, 139, 87, 15, 106, 211, 238, 188, 85, 52, 73, 252, + 145, 200, 255, + ], + [ + 110, 64, 94, 200, 193, 4, 7, 183, 235, 213, 233, 138, 254, 193, 143, 110, 250, + 6, 140, 132, + ], + [ + 59, 116, 55, 93, 249, 247, 236, 235, 51, 228, 144, 107, 9, 128, 151, 119, 233, + 195, 225, 177, + ], + [ + 130, 218, 85, 115, 212, 14, 67, 101, 122, 57, 134, 206, 206, 99, 166, 165, 8, + 102, 13, 80, + ], + [ + 145, 43, 137, 172, 128, 156, 44, 175, 201, 169, 145, 172, 242, 204, 194, 237, + 101, 134, 135, 48, + ], + [ + 78, 198, 154, 53, 62, 144, 8, 91, 207, 161, 157, 63, 141, 172, 175, 85, 187, + 112, 238, 153, + ], + [ + 192, 2, 253, 152, 11, 31, 166, 170, 189, 18, 136, 184, 187, 22, 163, 48, 62, + 78, 212, 224, + ], + [ + 222, 85, 175, 169, 28, 201, 209, 22, 235, 22, 186, 7, 165, 245, 93, 53, 165, + 79, 195, 166, + ], + [ + 237, 109, 88, 215, 2, 106, 20, 208, 165, 102, 201, 25, 84, 186, 184, 73, 37, + 48, 110, 130, + ], + [ + 180, 112, 123, 26, 11, 47, 241, 182, 97, 212, 150, 52, 18, 238, 202, 245, 22, + 93, 68, 234, + ], + [ + 222, 149, 93, 27, 125, 57, 51, 121, 246, 165, 67, 179, 98, 174, 85, 112, 34, + 218, 58, 144, + ], + [ + 168, 54, 160, 241, 22, 67, 151, 106, 57, 239, 72, 32, 76, 184, 140, 90, 51, 40, + 42, 118, + ], + [ + 178, 223, 254, 204, 49, 218, 80, 248, 102, 218, 11, 63, 51, 16, 239, 21, 154, + 221, 221, 68, + ], + [ + 0, 80, 197, 189, 115, 33, 191, 191, 138, 139, 81, 201, 6, 206, 48, 201, 7, 237, + 183, 33, + ], + [ + 232, 141, 7, 170, 230, 227, 166, 214, 71, 45, 106, 17, 12, 34, 18, 162, 139, + 39, 176, 108, + ], + [ + 173, 143, 60, 164, 90, 137, 75, 17, 124, 242, 125, 44, 92, 198, 230, 25, 205, + 178, 246, 114, + ], + [ + 211, 57, 172, 200, 170, 36, 96, 117, 103, 217, 65, 61, 51, 85, 223, 185, 188, + 49, 212, 227, + ], + [ + 221, 231, 177, 83, 124, 70, 246, 217, 47, 253, 121, 109, 184, 148, 152, 43, + 169, 116, 136, 94, + ], + [ + 53, 155, 147, 193, 178, 253, 60, 179, 113, 102, 254, 169, 84, 130, 247, 12, 21, + 142, 124, 82, + ], + [ + 123, 21, 51, 218, 233, 181, 209, 181, 3, 72, 104, 160, 126, 216, 19, 250, 150, + 69, 201, 17, + ], + [ + 77, 221, 69, 167, 185, 107, 245, 15, 219, 73, 104, 88, 205, 246, 224, 100, 39, + 220, 252, 243, + ], + [ + 162, 230, 60, 140, 104, 54, 113, 211, 199, 131, 128, 133, 120, 222, 42, 78, + 193, 97, 144, 80, + ], + [ + 13, 57, 26, 43, 28, 226, 157, 149, 177, 69, 214, 62, 12, 247, 20, 49, 48, 25, + 233, 210, + ], + [ + 204, 98, 176, 168, 170, 193, 139, 39, 231, 167, 154, 231, 202, 193, 193, 158, + 57, 51, 248, 198, + ], + [ + 155, 190, 198, 164, 218, 125, 184, 62, 163, 81, 219, 89, 101, 122, 82, 59, 121, + 89, 57, 194, + ], + [ + 98, 230, 62, 238, 8, 6, 247, 111, 30, 138, 116, 68, 22, 223, 245, 124, 141, + 174, 254, 199, + ], + [ + 4, 73, 208, 175, 233, 170, 239, 226, 152, 33, 18, 13, 145, 144, 238, 221, 134, + 50, 22, 99, + ], + [ + 62, 51, 44, 95, 235, 227, 254, 34, 30, 216, 152, 135, 184, 25, 84, 70, 138, + 104, 214, 174, + ], + [ + 43, 172, 71, 142, 150, 99, 188, 139, 139, 102, 45, 68, 195, 163, 205, 234, 230, + 112, 124, 157, + ], + [ + 41, 54, 46, 149, 159, 192, 250, 165, 96, 188, 0, 12, 133, 101, 227, 169, 54, + 173, 46, 239, + ], + [ + 26, 128, 246, 182, 7, 245, 120, 96, 212, 214, 194, 117, 164, 110, 146, 128, 52, + 94, 143, 92, + ], + [ + 190, 227, 253, 116, 244, 51, 129, 238, 228, 165, 148, 251, 143, 195, 34, 136, + 240, 52, 240, 133, + ], + [ + 180, 30, 58, 98, 99, 185, 52, 187, 150, 165, 246, 117, 14, 98, 184, 163, 83, + 95, 245, 67, + ], + [ + 197, 192, 17, 204, 119, 132, 197, 243, 117, 173, 164, 10, 228, 26, 29, 190, + 166, 21, 181, 252, + ], + [ + 92, 175, 169, 48, 223, 195, 134, 93, 132, 239, 38, 169, 44, 251, 42, 107, 174, + 17, 24, 85, + ], + [ + 18, 198, 208, 225, 149, 61, 27, 88, 134, 81, 196, 202, 21, 138, 24, 27, 212, + 39, 197, 191, + ], + [ + 93, 181, 248, 2, 188, 93, 224, 219, 206, 48, 82, 219, 137, 187, 158, 128, 41, + 35, 125, 21, + ], + [ + 178, 83, 36, 122, 56, 245, 175, 232, 70, 167, 1, 24, 43, 195, 196, 56, 3, 49, + 80, 52, + ], + [ + 8, 124, 177, 229, 187, 213, 17, 158, 169, 73, 159, 31, 77, 4, 108, 199, 34, + 124, 81, 213, + ], + [ + 167, 194, 123, 247, 76, 52, 198, 110, 245, 145, 78, 227, 172, 194, 132, 140, + 162, 242, 112, 57, + ], + [ + 179, 9, 29, 152, 162, 222, 56, 125, 217, 87, 175, 122, 138, 225, 124, 218, 215, + 6, 239, 132, + ], + [ + 230, 118, 27, 213, 42, 13, 152, 42, 85, 50, 192, 21, 210, 10, 14, 210, 204, 62, + 239, 112, + ], + [ + 173, 25, 69, 81, 150, 27, 80, 164, 153, 23, 5, 176, 240, 200, 168, 134, 240, + 85, 47, 216, + ], + [ + 167, 67, 115, 248, 34, 111, 181, 151, 106, 194, 67, 148, 91, 174, 86, 6, 161, + 21, 78, 173, + ], + [ + 196, 215, 176, 176, 90, 245, 233, 36, 185, 173, 246, 18, 214, 239, 101, 187, + 228, 203, 65, 177, + ], + [ + 172, 116, 27, 44, 246, 240, 193, 181, 254, 87, 229, 18, 226, 25, 46, 35, 46, + 87, 147, 168, + ], + [ + 139, 29, 222, 54, 169, 205, 108, 220, 224, 110, 230, 97, 34, 20, 222, 245, 77, + 98, 44, 187, + ], + [ + 255, 117, 61, 242, 225, 183, 248, 224, 193, 244, 75, 186, 162, 147, 198, 70, + 172, 198, 76, 254, + ], + [ + 217, 129, 128, 249, 129, 38, 80, 106, 204, 173, 195, 190, 150, 228, 114, 193, + 93, 70, 181, 139, + ], + [ + 38, 238, 197, 123, 121, 47, 251, 13, 188, 230, 170, 43, 141, 119, 81, 225, 175, + 219, 49, 48, + ], + [ + 42, 231, 249, 184, 117, 17, 77, 168, 8, 127, 130, 123, 19, 193, 199, 55, 70, + 196, 245, 232, + ], + [ + 228, 226, 71, 187, 141, 114, 32, 12, 111, 20, 216, 13, 187, 119, 72, 170, 149, + 123, 167, 136, + ], + [ + 251, 216, 135, 58, 249, 85, 96, 242, 131, 89, 241, 170, 170, 213, 137, 65, 211, + 229, 138, 215, + ], + [ + 162, 64, 93, 178, 156, 113, 62, 121, 149, 40, 125, 192, 187, 200, 225, 241, + 179, 191, 253, 167, + ], + [ + 103, 168, 35, 167, 147, 92, 149, 154, 99, 219, 223, 237, 86, 174, 21, 31, 170, + 192, 218, 3, + ], + [ + 79, 250, 153, 180, 215, 134, 51, 45, 41, 234, 169, 67, 83, 229, 81, 41, 74, + 113, 229, 97, + ], + [ + 199, 238, 181, 1, 78, 141, 210, 2, 66, 6, 255, 200, 182, 8, 98, 159, 171, 227, + 65, 104, + ], + [ + 184, 36, 77, 7, 67, 61, 14, 203, 21, 229, 6, 7, 213, 216, 225, 229, 211, 17, + 108, 240, + ], + [ + 245, 158, 118, 39, 210, 5, 166, 242, 28, 112, 170, 65, 159, 137, 7, 74, 197, + 55, 134, 253, + ], + [ + 167, 50, 169, 68, 46, 93, 88, 93, 198, 20, 9, 43, 204, 127, 8, 192, 179, 223, + 94, 72, + ], + [ + 197, 206, 193, 5, 77, 128, 7, 43, 90, 60, 197, 64, 173, 40, 21, 131, 65, 71, + 70, 93, + ], + [ + 11, 159, 58, 44, 220, 219, 40, 229, 149, 159, 251, 156, 110, 28, 223, 81, 87, + 90, 195, 4, + ], + [ + 106, 239, 182, 47, 220, 202, 54, 135, 249, 143, 216, 194, 113, 232, 98, 37, 29, + 28, 255, 92, + ], + [ + 249, 125, 26, 198, 178, 128, 113, 229, 241, 11, 86, 111, 207, 73, 208, 134, 42, + 167, 192, 16, + ], + [ + 125, 9, 10, 153, 194, 224, 232, 53, 113, 14, 188, 65, 159, 39, 63, 127, 118, + 84, 140, 183, + ], + [ + 136, 174, 194, 91, 82, 125, 65, 203, 27, 165, 141, 182, 101, 147, 135, 103, + 175, 40, 158, 88, + ], + [ + 2, 178, 38, 208, 137, 106, 117, 132, 60, 138, 7, 126, 35, 149, 82, 100, 228, + 218, 51, 122, + ], + [ + 193, 162, 81, 16, 190, 218, 220, 18, 15, 169, 87, 247, 94, 118, 92, 250, 187, + 70, 117, 192, + ], + [ + 39, 55, 97, 127, 84, 50, 127, 94, 140, 141, 233, 46, 130, 29, 200, 250, 122, + 49, 128, 170, + ], + [ + 41, 132, 219, 235, 107, 36, 134, 184, 87, 206, 117, 247, 227, 193, 226, 33, + 202, 171, 174, 137, + ], + [ + 204, 118, 199, 230, 129, 146, 80, 30, 30, 247, 118, 144, 166, 205, 19, 159, + 196, 4, 138, 102, + ], + [ + 198, 192, 184, 129, 112, 174, 38, 40, 116, 3, 102, 2, 168, 206, 108, 71, 103, + 167, 242, 99, + ], + [ + 238, 165, 249, 43, 97, 69, 37, 245, 102, 170, 58, 42, 127, 231, 255, 51, 148, + 135, 213, 209, + ], + [ + 20, 21, 43, 12, 35, 32, 99, 231, 153, 168, 49, 47, 249, 166, 106, 98, 204, 172, + 234, 255, + ], + [ + 237, 34, 231, 144, 28, 150, 138, 200, 122, 58, 68, 4, 74, 169, 252, 254, 109, + 106, 57, 187, + ], + [ + 185, 19, 249, 126, 232, 164, 64, 113, 95, 120, 11, 162, 186, 60, 89, 141, 154, + 80, 79, 169, + ], + [ + 153, 179, 12, 222, 189, 2, 246, 37, 225, 86, 252, 12, 207, 185, 237, 244, 209, + 46, 30, 153, + ], + [ + 134, 42, 144, 3, 113, 157, 106, 94, 92, 81, 47, 93, 14, 129, 135, 87, 64, 238, + 209, 180, + ], + [ + 189, 115, 243, 110, 146, 138, 114, 102, 49, 235, 191, 234, 71, 60, 32, 180, + 237, 173, 159, 236, + ], + [ + 145, 24, 72, 129, 51, 133, 110, 198, 221, 32, 115, 57, 169, 189, 122, 116, 215, + 244, 90, 81, + ], + [ + 81, 44, 197, 42, 172, 193, 164, 82, 211, 231, 7, 132, 166, 214, 233, 221, 212, + 143, 51, 18, + ], + [ + 247, 216, 162, 207, 158, 89, 148, 32, 184, 51, 229, 235, 143, 186, 26, 248, + 140, 89, 241, 55, + ], + [ + 45, 29, 82, 28, 151, 57, 241, 16, 132, 26, 1, 13, 20, 214, 214, 30, 83, 60, + 243, 116, + ], + [ + 128, 138, 73, 68, 1, 1, 46, 13, 96, 66, 139, 46, 86, 230, 2, 64, 124, 255, 169, + 185, + ], + [ + 91, 69, 207, 210, 110, 254, 80, 85, 245, 238, 45, 234, 139, 104, 135, 87, 116, + 174, 27, 58, + ], + [ + 25, 107, 101, 58, 183, 237, 199, 42, 4, 83, 120, 44, 108, 123, 181, 222, 231, + 226, 95, 20, + ], + [ + 229, 45, 182, 124, 93, 7, 103, 25, 59, 148, 120, 38, 92, 78, 82, 39, 36, 134, + 91, 68, + ], + [ + 73, 79, 247, 174, 39, 31, 223, 150, 159, 202, 174, 116, 156, 242, 65, 185, 255, + 142, 74, 70, + ], + [ + 195, 179, 131, 72, 121, 13, 226, 14, 68, 101, 218, 252, 97, 134, 91, 125, 237, + 179, 87, 63, + ], + [ + 93, 38, 195, 168, 18, 111, 238, 83, 240, 48, 234, 232, 238, 136, 131, 31, 91, + 47, 109, 204, + ], + [ + 98, 8, 103, 22, 137, 228, 97, 119, 84, 73, 191, 31, 130, 112, 55, 168, 91, 115, + 159, 196, + ], + [ + 188, 155, 207, 121, 84, 224, 44, 200, 141, 35, 8, 213, 9, 56, 99, 235, 117, 31, + 108, 40, + ], + [ + 139, 249, 25, 58, 215, 173, 118, 191, 221, 118, 240, 184, 172, 201, 152, 247, + 243, 253, 80, 21, + ], + [ + 94, 147, 243, 113, 233, 80, 204, 102, 83, 93, 8, 218, 117, 106, 236, 148, 82, + 160, 75, 54, + ], + [ + 186, 157, 182, 62, 16, 32, 152, 251, 7, 155, 168, 42, 210, 74, 89, 166, 95, 5, + 210, 22, + ], + [ + 62, 146, 198, 9, 164, 92, 160, 130, 73, 68, 118, 6, 175, 120, 225, 78, 241, 95, + 208, 251, + ], + [ + 135, 180, 193, 177, 134, 231, 21, 200, 150, 11, 83, 25, 220, 199, 132, 42, 46, + 140, 90, 174, + ], + [ + 172, 92, 200, 0, 8, 66, 55, 145, 252, 48, 111, 12, 49, 70, 225, 132, 68, 67, + 200, 139, + ], + [ + 60, 145, 15, 157, 129, 205, 87, 157, 179, 209, 128, 75, 86, 73, 125, 107, 39, + 28, 117, 135, + ], + [ + 94, 250, 209, 215, 153, 147, 39, 27, 125, 235, 38, 123, 132, 126, 254, 241, + 174, 97, 207, 94, + ], + [ + 135, 239, 192, 203, 218, 110, 132, 137, 3, 128, 152, 31, 209, 47, 150, 242, + 246, 124, 198, 129, + ], + [ + 183, 131, 243, 104, 251, 23, 112, 196, 132, 215, 116, 227, 50, 253, 53, 137, + 55, 13, 97, 5, + ], + [ + 234, 167, 82, 131, 202, 80, 202, 72, 102, 225, 171, 214, 246, 182, 87, 56, 169, + 215, 115, 121, + ], + [ + 220, 145, 253, 161, 67, 249, 224, 43, 21, 81, 219, 97, 0, 214, 6, 126, 3, 119, + 96, 2, + ], + [ + 247, 51, 52, 255, 130, 160, 143, 135, 117, 63, 198, 167, 49, 249, 214, 98, 146, + 56, 117, 214, + ], + [ + 18, 184, 112, 155, 0, 13, 128, 205, 222, 187, 47, 192, 254, 251, 84, 123, 90, + 201, 219, 138, + ], + [ + 41, 206, 114, 114, 49, 63, 178, 153, 210, 4, 90, 193, 244, 251, 169, 214, 246, + 34, 251, 111, + ], + [ + 192, 220, 164, 203, 29, 3, 34, 34, 117, 150, 62, 211, 16, 117, 152, 79, 192, + 237, 0, 245, + ], + [ + 153, 84, 125, 30, 254, 93, 214, 229, 80, 23, 115, 77, 67, 236, 149, 153, 174, + 215, 177, 106, + ], + [ + 244, 20, 232, 68, 145, 132, 221, 17, 214, 206, 126, 62, 57, 143, 208, 15, 193, + 15, 131, 98, + ], + [ + 223, 72, 231, 185, 177, 16, 185, 199, 248, 156, 205, 37, 9, 122, 76, 166, 115, + 214, 172, 108, + ], + [ + 158, 24, 197, 97, 47, 251, 96, 114, 98, 129, 223, 69, 230, 155, 230, 171, 47, + 223, 214, 64, + ], + [ + 114, 60, 60, 213, 70, 203, 227, 232, 246, 157, 73, 156, 83, 12, 159, 9, 216, + 109, 83, 222, + ], + [ + 119, 149, 117, 22, 204, 205, 48, 183, 89, 146, 56, 22, 182, 108, 49, 71, 26, + 114, 91, 164, + ], + [ + 7, 20, 110, 127, 156, 194, 97, 111, 165, 59, 136, 110, 69, 42, 21, 116, 217, + 71, 116, 18, + ], + [ + 48, 6, 92, 46, 23, 41, 33, 125, 205, 215, 220, 255, 105, 166, 90, 82, 207, 248, + 254, 94, + ], + [ + 40, 175, 174, 99, 225, 186, 166, 78, 43, 16, 18, 32, 148, 34, 220, 1, 227, 91, + 102, 248, + ], + [ + 132, 138, 7, 224, 160, 144, 194, 30, 231, 70, 227, 83, 141, 133, 180, 12, 118, + 95, 95, 188, + ], + [ + 152, 173, 240, 14, 174, 82, 117, 39, 132, 70, 59, 155, 178, 158, 124, 40, 61, + 180, 207, 35, + ], + [ + 96, 177, 129, 158, 85, 40, 217, 6, 101, 201, 29, 204, 105, 253, 58, 0, 194, 98, + 9, 239, + ], + [ + 146, 75, 191, 202, 193, 135, 97, 238, 123, 65, 149, 88, 153, 103, 148, 231, + 218, 145, 101, 178, + ], + [ + 163, 251, 94, 233, 187, 0, 197, 235, 221, 93, 187, 159, 91, 138, 197, 130, 90, + 235, 168, 170, + ], + [ + 119, 0, 142, 100, 195, 20, 198, 92, 46, 183, 105, 10, 70, 197, 64, 86, 127, 93, + 220, 255, + ], + [ + 39, 42, 121, 173, 133, 213, 37, 23, 15, 74, 51, 161, 239, 186, 178, 172, 166, + 83, 137, 139, + ], + [ + 250, 90, 46, 84, 183, 58, 50, 7, 216, 169, 240, 211, 223, 238, 229, 164, 152, + 139, 19, 17, + ], + [ + 253, 79, 83, 36, 191, 233, 2, 159, 136, 155, 4, 222, 188, 127, 35, 205, 220, + 107, 143, 54, + ], + [ + 2, 85, 47, 115, 42, 210, 35, 185, 54, 171, 129, 166, 38, 211, 107, 249, 174, + 175, 45, 237, + ], + [ + 4, 187, 41, 74, 131, 239, 10, 178, 114, 143, 214, 169, 205, 7, 68, 204, 187, + 206, 122, 118, + ], + [ + 160, 199, 212, 189, 252, 143, 5, 210, 177, 64, 2, 195, 91, 42, 120, 70, 167, + 69, 71, 17, + ], + [ + 244, 168, 202, 246, 213, 83, 116, 222, 126, 233, 183, 174, 44, 208, 79, 108, + 120, 255, 142, 198, + ], + [ + 75, 129, 244, 190, 43, 9, 115, 13, 206, 125, 198, 71, 70, 201, 137, 44, 103, + 147, 29, 20, + ], + [ + 168, 142, 179, 102, 150, 159, 156, 161, 68, 214, 135, 239, 30, 117, 187, 47, + 65, 238, 64, 21, + ], + [ + 194, 70, 132, 143, 108, 188, 100, 41, 235, 14, 37, 86, 148, 96, 149, 206, 248, + 241, 184, 72, + ], + [ + 100, 87, 133, 208, 21, 44, 15, 93, 16, 174, 113, 113, 128, 88, 45, 51, 42, 172, + 31, 129, + ], + [ + 21, 253, 66, 153, 210, 74, 74, 125, 10, 179, 38, 240, 147, 52, 236, 221, 236, + 93, 223, 170, + ], + [ + 222, 79, 209, 201, 26, 114, 96, 69, 221, 86, 181, 55, 5, 195, 75, 210, 221, + 191, 69, 213, + ], + [ + 48, 15, 170, 219, 26, 221, 69, 206, 93, 96, 105, 34, 210, 79, 41, 49, 62, 199, + 18, 227, + ], + [ + 19, 160, 108, 194, 235, 105, 216, 217, 150, 2, 56, 231, 16, 134, 21, 137, 39, + 155, 86, 22, + ], + [ + 176, 218, 216, 40, 79, 237, 228, 34, 140, 29, 56, 113, 96, 36, 76, 207, 172, + 111, 168, 173, + ], + [ + 53, 206, 154, 45, 1, 92, 116, 54, 44, 199, 162, 231, 224, 205, 132, 159, 68, + 225, 236, 41, + ], + [ + 214, 144, 111, 80, 154, 26, 167, 53, 20, 212, 28, 67, 99, 49, 85, 139, 173, 9, + 110, 209, + ], + [ + 203, 194, 123, 191, 127, 159, 148, 108, 252, 126, 23, 214, 54, 69, 74, 126, + 172, 187, 115, 43, + ], + [ + 33, 228, 83, 159, 37, 83, 148, 125, 70, 154, 136, 57, 240, 233, 198, 146, 75, + 25, 204, 183, + ], + [ + 254, 201, 191, 0, 25, 85, 62, 217, 235, 190, 84, 253, 244, 142, 57, 180, 72, + 86, 55, 138, + ], + [ + 134, 215, 156, 91, 137, 239, 87, 97, 169, 86, 242, 92, 205, 64, 118, 147, 85, + 102, 167, 137, + ], + [ + 95, 188, 244, 180, 13, 177, 169, 176, 217, 237, 21, 8, 140, 107, 67, 85, 36, + 210, 59, 225, + ], + [ + 51, 207, 12, 115, 243, 67, 82, 235, 124, 132, 83, 243, 17, 149, 223, 4, 198, + 12, 240, 155, + ], + [ + 73, 108, 55, 32, 221, 84, 235, 163, 26, 193, 205, 19, 83, 192, 54, 220, 44, + 250, 183, 166, + ], + [ + 159, 175, 70, 249, 104, 251, 98, 55, 162, 58, 64, 141, 186, 152, 126, 164, 144, + 67, 179, 250, + ], + [ + 101, 51, 22, 99, 55, 164, 156, 100, 229, 81, 64, 251, 88, 55, 17, 79, 26, 221, + 201, 114, + ], + [ + 51, 169, 104, 46, 79, 236, 48, 100, 220, 118, 98, 14, 221, 213, 229, 229, 62, + 3, 14, 140, + ], + [ + 75, 35, 100, 235, 127, 141, 109, 91, 138, 60, 2, 100, 77, 145, 218, 2, 216, 81, + 3, 139, + ], + [ + 127, 73, 144, 10, 17, 247, 118, 32, 34, 40, 150, 69, 146, 21, 93, 153, 190, + 101, 217, 145, + ], + [ + 167, 183, 111, 150, 157, 173, 75, 31, 208, 125, 93, 179, 230, 54, 103, 227, + 205, 76, 209, 240, + ], + [ + 255, 27, 23, 85, 215, 235, 94, 243, 206, 211, 161, 6, 18, 136, 74, 153, 34, 93, + 133, 75, + ], + [ + 103, 88, 164, 155, 53, 38, 58, 126, 41, 142, 21, 138, 147, 5, 37, 115, 151, + 109, 169, 174, + ], + [ + 77, 171, 245, 191, 213, 206, 89, 99, 236, 181, 77, 253, 229, 5, 194, 241, 224, + 68, 102, 240, + ], + [ + 4, 67, 151, 105, 242, 250, 235, 215, 168, 88, 166, 65, 0, 8, 74, 135, 65, 131, + 211, 48, + ], + [ + 150, 67, 200, 13, 95, 189, 196, 212, 73, 210, 168, 61, 186, 220, 149, 250, 28, + 105, 170, 221, + ], + [ + 23, 165, 226, 184, 162, 93, 3, 248, 3, 222, 141, 17, 230, 118, 176, 161, 77, + 245, 241, 138, + ], + [ + 117, 157, 69, 21, 213, 188, 213, 128, 142, 145, 35, 227, 88, 102, 4, 200, 115, + 253, 170, 254, + ], + [ + 243, 75, 229, 85, 73, 173, 196, 13, 247, 149, 154, 225, 132, 222, 94, 176, 233, + 155, 205, 128, + ], + [ + 7, 21, 111, 121, 76, 51, 235, 192, 68, 93, 46, 244, 30, 112, 164, 194, 60, 82, + 27, 213, + ], + [ + 148, 78, 79, 54, 42, 83, 22, 125, 196, 186, 197, 178, 92, 232, 224, 25, 250, + 45, 40, 66, + ], + [ + 99, 90, 215, 115, 230, 60, 68, 158, 170, 103, 88, 12, 223, 30, 63, 148, 166, + 106, 201, 68, + ], + [ + 154, 44, 20, 20, 171, 4, 185, 218, 40, 22, 67, 76, 144, 16, 13, 93, 254, 64, + 89, 253, + ], + [ + 220, 42, 129, 75, 87, 174, 179, 204, 165, 235, 34, 28, 8, 196, 200, 160, 95, + 19, 142, 96, + ], + [ + 24, 95, 247, 205, 252, 142, 137, 141, 208, 2, 4, 112, 173, 247, 4, 250, 241, + 240, 54, 32, + ], + [ + 147, 197, 168, 151, 9, 206, 139, 231, 80, 5, 249, 241, 199, 118, 111, 202, 205, + 141, 84, 110, + ], + [ + 91, 111, 85, 114, 216, 7, 254, 242, 8, 200, 54, 74, 202, 39, 18, 23, 19, 179, + 42, 20, + ], + [ + 168, 188, 82, 98, 90, 150, 74, 229, 246, 222, 38, 48, 39, 4, 24, 199, 115, 25, + 205, 255, + ], + [ + 9, 165, 84, 208, 64, 95, 204, 250, 62, 178, 250, 110, 249, 167, 46, 85, 91, + 221, 141, 13, + ], + [ + 75, 253, 175, 162, 228, 101, 25, 228, 238, 18, 86, 173, 247, 1, 206, 141, 83, + 253, 121, 129, + ], + [ + 43, 64, 149, 33, 65, 112, 161, 95, 200, 134, 57, 8, 238, 243, 51, 71, 30, 240, + 135, 55, + ], + [ + 37, 75, 217, 129, 130, 50, 201, 135, 223, 42, 45, 64, 100, 223, 211, 24, 45, + 152, 69, 249, + ], + [ + 155, 152, 46, 62, 79, 10, 99, 164, 136, 150, 149, 168, 237, 219, 127, 116, 158, + 189, 121, 140, + ], + [ + 67, 71, 49, 99, 156, 183, 185, 66, 217, 150, 252, 254, 189, 93, 215, 52, 22, + 236, 198, 2, + ], + [ + 113, 24, 16, 215, 49, 169, 237, 78, 8, 160, 176, 196, 79, 35, 17, 165, 174, + 130, 11, 252, + ], + [ + 248, 137, 74, 144, 211, 231, 132, 111, 117, 58, 73, 210, 196, 107, 183, 220, + 146, 104, 95, 130, + ], + [ + 115, 161, 159, 115, 23, 185, 225, 138, 208, 212, 86, 6, 77, 100, 60, 132, 209, + 236, 243, 238, + ], + [ + 103, 54, 198, 132, 20, 50, 89, 71, 249, 152, 17, 120, 192, 219, 25, 108, 226, + 39, 3, 197, + ], + [ + 66, 202, 130, 252, 56, 43, 13, 103, 197, 71, 183, 113, 177, 17, 20, 123, 207, + 243, 127, 162, + ], + [ + 22, 89, 138, 91, 232, 14, 52, 190, 200, 219, 88, 164, 76, 233, 167, 191, 213, + 219, 10, 124, + ], + [ + 222, 152, 137, 134, 76, 79, 246, 208, 117, 253, 140, 42, 98, 150, 188, 45, 92, + 135, 55, 241, + ], + [ + 26, 16, 125, 53, 92, 244, 29, 252, 235, 83, 145, 151, 174, 234, 57, 65, 184, + 209, 0, 31, + ], + [ + 30, 100, 167, 122, 34, 153, 110, 67, 52, 83, 202, 73, 41, 183, 161, 130, 59, + 49, 137, 92, + ], + [ + 252, 72, 68, 134, 35, 238, 39, 153, 175, 3, 120, 157, 117, 100, 118, 172, 76, + 184, 146, 236, + ], + [ + 114, 249, 126, 6, 79, 135, 60, 144, 238, 58, 239, 169, 146, 201, 54, 40, 94, + 135, 84, 109, + ], + [ + 177, 235, 229, 109, 75, 151, 38, 117, 218, 67, 50, 253, 242, 61, 83, 92, 208, + 100, 219, 188, + ], + [ + 164, 90, 236, 50, 217, 172, 209, 179, 93, 133, 137, 196, 126, 158, 104, 127, + 139, 245, 176, 47, + ], + [ + 116, 98, 154, 222, 179, 113, 245, 46, 92, 102, 122, 60, 245, 174, 244, 181, 99, + 73, 67, 44, + ], + [ + 73, 232, 231, 100, 238, 81, 75, 57, 65, 134, 172, 161, 115, 7, 233, 239, 143, + 249, 212, 125, + ], + [ + 45, 61, 244, 153, 204, 12, 93, 142, 112, 192, 42, 174, 10, 155, 246, 67, 248, + 106, 187, 44, + ], + [ + 238, 22, 206, 158, 168, 4, 39, 32, 49, 131, 28, 241, 125, 184, 146, 38, 114, + 166, 167, 83, + ], + [ + 2, 118, 114, 117, 69, 33, 247, 32, 32, 0, 212, 42, 167, 222, 27, 245, 100, 31, + 101, 126, + ], + [ + 138, 21, 133, 91, 123, 161, 204, 66, 216, 23, 122, 242, 73, 136, 37, 66, 70, + 61, 222, 18, + ], + [ + 220, 16, 218, 184, 137, 249, 143, 106, 105, 139, 81, 155, 202, 181, 209, 241, + 128, 218, 87, 217, + ], + [ + 122, 88, 114, 241, 188, 231, 248, 179, 67, 232, 144, 24, 62, 2, 22, 97, 96, + 152, 54, 147, + ], + [ + 126, 231, 202, 241, 98, 228, 75, 107, 40, 201, 110, 99, 16, 6, 45, 161, 129, + 99, 96, 222, + ], + [ + 162, 25, 7, 68, 22, 169, 132, 180, 2, 87, 254, 167, 36, 207, 196, 96, 37, 177, + 241, 91, + ], + [ + 65, 56, 177, 30, 102, 246, 37, 4, 157, 225, 125, 40, 13, 247, 94, 240, 183, + 250, 117, 217, + ], + [ + 185, 77, 212, 149, 184, 78, 151, 56, 167, 188, 71, 2, 20, 20, 90, 177, 200, + 147, 163, 75, + ], + [ + 91, 137, 131, 189, 117, 75, 134, 241, 230, 1, 120, 222, 144, 104, 183, 115, + 115, 201, 129, 197, + ], + [ + 181, 151, 214, 64, 67, 115, 78, 225, 167, 16, 34, 28, 155, 18, 114, 22, 24, + 204, 171, 135, + ], + [ + 148, 70, 139, 42, 167, 38, 52, 94, 40, 83, 46, 139, 226, 126, 143, 135, 211, + 241, 247, 193, + ], + [ + 89, 82, 162, 229, 108, 191, 136, 232, 45, 0, 185, 85, 243, 154, 220, 75, 42, + 29, 239, 172, + ], + [ + 191, 213, 221, 81, 67, 177, 255, 22, 246, 10, 18, 216, 198, 7, 57, 5, 119, 190, + 12, 8, + ], + [ + 89, 125, 176, 23, 218, 240, 48, 216, 77, 65, 8, 222, 14, 47, 176, 115, 128, + 212, 153, 167, + ], + [ + 164, 225, 147, 195, 148, 150, 35, 131, 70, 235, 253, 178, 238, 84, 119, 153, + 20, 137, 28, 46, + ], + [ + 106, 130, 240, 186, 17, 148, 129, 193, 43, 155, 97, 31, 119, 26, 177, 100, 201, + 94, 38, 197, + ], + [ + 66, 7, 206, 68, 186, 115, 118, 3, 40, 154, 250, 172, 241, 141, 50, 240, 24, 23, + 89, 209, + ], + [ + 122, 185, 80, 76, 45, 193, 236, 141, 49, 199, 190, 116, 22, 132, 9, 209, 49, + 227, 123, 87, + ], + [ + 155, 226, 36, 243, 124, 27, 130, 24, 198, 185, 154, 142, 225, 135, 91, 62, 101, + 38, 240, 221, + ], + [ + 83, 57, 141, 139, 49, 108, 226, 186, 114, 173, 250, 107, 37, 181, 23, 126, 70, + 106, 171, 103, + ], + [ + 44, 120, 122, 118, 158, 220, 138, 240, 105, 255, 128, 219, 23, 69, 98, 163, + 180, 18, 68, 76, + ], + [ + 100, 198, 45, 154, 113, 167, 39, 176, 144, 252, 163, 72, 77, 62, 240, 204, 197, + 53, 24, 49, + ], + [ + 136, 201, 75, 48, 115, 215, 20, 60, 227, 62, 46, 73, 133, 105, 63, 217, 4, 11, + 18, 147, + ], + [ + 226, 113, 63, 18, 151, 177, 27, 16, 32, 58, 163, 122, 164, 134, 56, 11, 117, + 96, 46, 126, + ], + [ + 222, 226, 5, 193, 5, 63, 89, 225, 143, 19, 156, 152, 183, 106, 138, 185, 78, + 198, 209, 102, + ], + [ + 240, 134, 231, 255, 151, 81, 79, 151, 251, 195, 100, 73, 207, 75, 27, 178, 41, + 36, 25, 17, + ], + [ + 11, 218, 243, 233, 65, 235, 154, 213, 113, 5, 194, 126, 157, 156, 252, 211, + 213, 17, 104, 63, + ], + [ + 109, 216, 157, 197, 135, 1, 230, 143, 142, 30, 181, 214, 22, 177, 102, 82, 174, + 18, 143, 190, + ], + [ + 155, 196, 231, 209, 19, 194, 45, 184, 158, 164, 106, 152, 248, 166, 189, 173, + 5, 226, 231, 203, + ], + [ + 200, 39, 206, 127, 207, 11, 32, 152, 20, 226, 191, 98, 252, 14, 162, 162, 251, + 148, 123, 189, + ], + [ + 82, 142, 1, 187, 22, 22, 193, 115, 245, 234, 125, 192, 145, 60, 95, 32, 243, + 149, 57, 86, + ], + [ + 180, 214, 50, 147, 215, 165, 142, 171, 221, 88, 136, 242, 46, 214, 218, 51, + 169, 144, 103, 243, + ], + [ + 231, 39, 3, 255, 24, 43, 211, 248, 94, 140, 34, 166, 95, 237, 146, 81, 111, 95, + 114, 118, + ], + [ + 92, 41, 49, 67, 117, 128, 48, 253, 160, 186, 55, 39, 195, 130, 102, 131, 150, + 235, 71, 39, + ], + [ + 91, 149, 0, 142, 211, 177, 58, 166, 69, 206, 117, 236, 16, 191, 167, 119, 89, + 86, 0, 159, + ], + [ + 46, 97, 38, 73, 228, 148, 32, 96, 209, 2, 61, 14, 6, 215, 198, 140, 91, 64, + 150, 129, + ], + [ + 91, 52, 214, 30, 132, 167, 105, 77, 212, 213, 238, 72, 55, 17, 18, 141, 218, + 109, 9, 115, + ], + [ + 62, 146, 34, 84, 150, 76, 163, 202, 159, 186, 238, 28, 195, 197, 136, 11, 165, + 174, 4, 12, + ], + [ + 83, 110, 191, 63, 238, 68, 250, 251, 154, 200, 238, 168, 102, 211, 207, 47, 63, + 27, 227, 230, + ], + [ + 190, 11, 116, 235, 41, 157, 46, 227, 5, 77, 43, 71, 5, 51, 202, 231, 29, 91, + 177, 68, + ], + [ + 191, 59, 90, 53, 48, 159, 135, 183, 190, 10, 189, 106, 57, 132, 25, 14, 36, + 166, 199, 160, + ], + [ + 115, 53, 3, 127, 116, 231, 166, 52, 128, 196, 91, 171, 44, 197, 210, 85, 194, + 74, 168, 74, + ], + [ + 74, 41, 71, 58, 74, 218, 121, 174, 125, 47, 173, 56, 19, 238, 119, 63, 165, 29, + 19, 22, + ], + [ + 88, 23, 1, 20, 187, 10, 114, 182, 59, 134, 112, 173, 44, 187, 78, 88, 155, 78, + 224, 123, + ], + [ + 180, 51, 233, 252, 2, 40, 230, 32, 72, 251, 189, 63, 90, 245, 90, 183, 84, 49, + 141, 176, + ], + [ + 19, 139, 34, 238, 53, 92, 241, 243, 174, 19, 30, 94, 217, 107, 80, 236, 87, + 135, 225, 81, + ], + [ + 59, 77, 118, 92, 24, 102, 107, 58, 172, 140, 252, 211, 131, 76, 107, 138, 2, 8, + 204, 135, + ], + [ + 65, 82, 97, 183, 41, 49, 33, 160, 137, 61, 214, 125, 78, 158, 111, 47, 63, 8, + 138, 254, + ], + [ + 29, 156, 76, 182, 151, 125, 53, 224, 66, 14, 153, 244, 172, 179, 56, 61, 5, + 216, 253, 14, + ], + [ + 91, 132, 229, 216, 5, 109, 58, 187, 83, 27, 0, 1, 70, 113, 45, 86, 46, 3, 96, + 135, + ], + [ + 116, 23, 74, 189, 213, 163, 133, 164, 123, 210, 171, 244, 109, 226, 160, 80, + 72, 64, 50, 224, + ], + [ + 73, 205, 175, 28, 89, 139, 222, 251, 233, 55, 114, 109, 86, 26, 4, 131, 187, + 47, 170, 158, + ], + [ + 94, 61, 255, 34, 64, 109, 175, 89, 19, 54, 94, 49, 206, 111, 237, 3, 149, 202, + 78, 213, + ], + [ + 200, 231, 95, 254, 246, 37, 223, 255, 84, 174, 6, 78, 47, 160, 47, 138, 31, + 232, 185, 120, + ], + [ + 242, 249, 203, 154, 124, 87, 184, 168, 186, 131, 57, 98, 73, 22, 101, 57, 131, + 88, 103, 44, + ], + [ + 1, 6, 109, 109, 81, 64, 158, 4, 36, 26, 211, 7, 81, 232, 63, 61, 228, 89, 51, + 126, + ], + [ + 139, 168, 24, 193, 189, 189, 145, 92, 88, 101, 52, 157, 76, 8, 71, 196, 97, 48, + 28, 76, + ], + [ + 239, 94, 131, 89, 92, 199, 82, 194, 162, 52, 183, 164, 233, 155, 91, 237, 248, + 77, 112, 97, + ], + [ + 211, 2, 197, 123, 155, 52, 192, 195, 134, 49, 255, 186, 249, 35, 62, 238, 95, + 40, 43, 222, + ], + [ + 160, 198, 198, 31, 150, 75, 157, 250, 13, 229, 36, 181, 5, 71, 119, 115, 48, + 79, 11, 178, + ], + [ + 216, 140, 77, 241, 3, 208, 111, 47, 252, 65, 176, 169, 17, 44, 26, 19, 182, + 254, 41, 40, + ], + [ + 250, 52, 145, 216, 126, 189, 213, 28, 37, 137, 5, 252, 252, 69, 177, 77, 37, + 27, 148, 83, + ], + [ + 235, 138, 86, 28, 224, 235, 125, 51, 197, 160, 160, 153, 71, 90, 150, 232, 164, + 234, 253, 207, + ], + [ + 27, 32, 169, 217, 33, 173, 211, 140, 122, 114, 99, 210, 177, 75, 78, 37, 201, + 120, 12, 44, + ], + [ + 98, 204, 13, 112, 221, 166, 57, 3, 46, 17, 216, 90, 166, 61, 14, 81, 186, 213, + 232, 69, + ], + [ + 48, 219, 99, 139, 60, 121, 181, 166, 141, 112, 228, 186, 38, 226, 207, 113, 79, + 124, 141, 173, + ], + [ + 39, 60, 126, 66, 214, 34, 159, 101, 218, 173, 109, 190, 203, 75, 47, 28, 118, + 12, 74, 243, + ], + [ + 87, 164, 83, 115, 188, 74, 51, 200, 95, 104, 75, 90, 110, 153, 186, 163, 162, + 52, 248, 86, + ], + [ + 223, 16, 79, 209, 203, 94, 3, 119, 106, 189, 53, 75, 65, 132, 110, 15, 114, + 154, 83, 45, + ], + [ + 125, 119, 188, 146, 24, 33, 44, 45, 85, 100, 165, 143, 183, 233, 173, 25, 241, + 23, 183, 23, + ], + [ + 242, 27, 246, 39, 27, 41, 226, 147, 193, 254, 76, 60, 95, 83, 63, 97, 13, 223, + 25, 156, + ], + [ + 121, 81, 238, 128, 63, 176, 213, 63, 203, 233, 57, 248, 24, 154, 220, 250, 148, + 182, 83, 94, + ], + [ + 250, 132, 230, 69, 78, 123, 143, 226, 181, 13, 168, 158, 36, 55, 161, 108, 95, + 141, 193, 2, + ], + [ + 192, 164, 41, 168, 187, 137, 231, 125, 78, 194, 7, 100, 103, 148, 231, 57, 216, + 123, 25, 137, + ], + [ + 153, 74, 232, 98, 229, 220, 175, 95, 99, 204, 79, 116, 81, 163, 138, 85, 229, + 116, 144, 84, + ], + [ + 173, 188, 181, 221, 28, 52, 21, 81, 157, 112, 136, 214, 124, 90, 225, 67, 79, + 35, 190, 34, + ], + [ + 237, 100, 12, 83, 201, 199, 170, 130, 213, 250, 219, 130, 95, 28, 111, 237, + 195, 168, 229, 165, + ], + [ + 6, 88, 176, 96, 205, 16, 218, 119, 14, 216, 86, 66, 122, 207, 226, 0, 54, 35, + 217, 151, + ], + [ + 111, 94, 100, 54, 108, 18, 74, 174, 215, 74, 132, 221, 18, 144, 87, 233, 86, + 247, 217, 119, + ], + [ + 20, 111, 23, 2, 3, 74, 29, 24, 91, 128, 129, 23, 79, 61, 34, 206, 126, 176, 21, + 55, + ], + [ + 67, 121, 209, 150, 140, 149, 121, 72, 236, 197, 73, 82, 3, 211, 76, 212, 203, + 107, 170, 152, + ], + [ + 209, 19, 152, 91, 164, 69, 146, 84, 112, 148, 192, 12, 140, 96, 103, 160, 95, + 54, 88, 171, + ], + [ + 125, 69, 169, 86, 18, 67, 235, 36, 216, 252, 24, 220, 243, 240, 110, 122, 185, + 175, 119, 170, + ], + [ + 8, 135, 21, 205, 105, 228, 247, 210, 227, 7, 52, 15, 68, 90, 80, 214, 197, 206, + 171, 60, + ], + [ + 155, 38, 93, 134, 51, 38, 124, 211, 149, 119, 215, 226, 208, 138, 216, 205, 45, + 255, 119, 196, + ], + [ + 86, 157, 108, 112, 180, 142, 40, 66, 34, 96, 58, 137, 115, 123, 61, 35, 116, + 186, 45, 245, + ], + [ + 186, 154, 182, 98, 174, 254, 182, 254, 49, 21, 103, 189, 190, 245, 170, 59, + 111, 231, 101, 68, + ], + [ + 110, 113, 100, 192, 134, 19, 236, 23, 221, 198, 168, 165, 61, 239, 167, 130, + 28, 14, 186, 13, + ], + [ + 204, 79, 196, 195, 82, 14, 38, 89, 239, 210, 11, 246, 161, 176, 193, 153, 103, + 13, 217, 193, + ], + [ + 228, 21, 162, 217, 82, 77, 210, 28, 24, 183, 101, 178, 108, 188, 2, 116, 94, + 163, 183, 229, + ], + [ + 15, 66, 23, 59, 25, 79, 120, 254, 204, 130, 180, 201, 19, 0, 161, 172, 247, 27, + 103, 63, + ], + [ + 206, 135, 234, 159, 46, 116, 62, 164, 170, 81, 184, 133, 115, 58, 54, 212, 226, + 50, 121, 67, + ], + [ + 241, 159, 133, 168, 87, 115, 96, 179, 226, 242, 225, 119, 10, 156, 114, 64, 61, + 15, 112, 214, + ], + [ + 198, 170, 78, 52, 16, 74, 209, 197, 148, 66, 147, 134, 125, 82, 181, 113, 80, + 204, 224, 54, + ], + [ + 2, 254, 44, 91, 175, 110, 143, 108, 197, 119, 173, 107, 130, 237, 16, 43, 41, + 171, 84, 90, + ], + [ + 101, 150, 4, 209, 191, 71, 78, 91, 28, 62, 239, 167, 41, 68, 140, 235, 92, 164, + 107, 170, + ], + [ + 12, 221, 6, 131, 123, 236, 230, 48, 5, 203, 105, 49, 65, 240, 135, 235, 171, + 170, 187, 232, + ], + [ + 92, 66, 254, 177, 120, 144, 192, 43, 173, 0, 11, 17, 199, 173, 70, 211, 173, + 46, 107, 26, + ], + [ + 174, 191, 81, 9, 70, 54, 81, 151, 190, 55, 226, 127, 156, 130, 240, 185, 15, + 60, 216, 233, + ], + [ + 76, 172, 50, 173, 53, 186, 126, 27, 172, 251, 76, 236, 119, 184, 146, 254, 147, + 223, 222, 170, + ], + [ + 18, 171, 81, 3, 220, 139, 3, 59, 174, 8, 151, 34, 161, 109, 251, 176, 104, 29, + 214, 243, + ], + [ + 107, 203, 116, 138, 178, 12, 201, 47, 126, 115, 181, 186, 141, 243, 194, 160, + 29, 144, 146, 111, + ], + [ + 157, 99, 167, 232, 15, 18, 49, 51, 35, 170, 215, 241, 178, 102, 228, 7, 211, 6, + 55, 138, + ], + [ + 188, 197, 238, 108, 230, 249, 92, 117, 71, 0, 22, 153, 200, 187, 61, 4, 214, 0, + 224, 155, + ], + [ + 169, 228, 9, 25, 115, 67, 221, 17, 221, 231, 166, 161, 29, 206, 145, 16, 216, + 143, 51, 154, + ], + [ + 44, 193, 109, 248, 96, 74, 145, 251, 225, 207, 156, 67, 251, 12, 30, 31, 70, + 108, 176, 212, + ], + [ + 126, 247, 11, 62, 167, 100, 6, 70, 193, 194, 136, 126, 105, 79, 124, 90, 238, + 0, 91, 3, + ], + [ + 76, 17, 218, 145, 44, 108, 184, 203, 0, 87, 92, 194, 141, 112, 62, 19, 14, 173, + 194, 162, + ], + [ + 8, 157, 103, 25, 136, 205, 213, 28, 120, 77, 1, 70, 178, 163, 25, 198, 77, 181, + 168, 121, + ], + [ + 16, 228, 6, 91, 37, 20, 233, 96, 137, 171, 207, 249, 82, 201, 237, 199, 191, + 233, 54, 128, + ], + [ + 199, 129, 24, 118, 188, 170, 203, 141, 74, 121, 220, 107, 79, 224, 119, 16, 18, + 86, 101, 137, + ], + [ + 148, 246, 243, 230, 229, 102, 29, 26, 135, 23, 125, 221, 47, 202, 127, 83, 69, + 140, 118, 61, + ], + [ + 47, 89, 239, 93, 238, 247, 251, 60, 113, 218, 41, 186, 217, 147, 118, 247, 147, + 211, 110, 71, + ], + [ + 248, 170, 177, 117, 150, 128, 130, 123, 205, 84, 86, 171, 204, 206, 197, 72, + 125, 213, 141, 33, + ], + [ + 164, 170, 4, 71, 125, 2, 65, 42, 34, 245, 139, 92, 182, 178, 201, 243, 95, 135, + 55, 172, + ], + [ + 85, 141, 48, 84, 77, 89, 218, 228, 5, 46, 110, 44, 29, 225, 123, 233, 86, 121, + 85, 164, + ], + [ + 43, 113, 63, 42, 169, 210, 138, 183, 16, 214, 38, 65, 17, 236, 100, 189, 40, + 164, 131, 97, + ], + [ + 142, 95, 29, 19, 143, 57, 108, 108, 163, 127, 117, 125, 252, 239, 50, 232, 150, + 16, 113, 83, + ], + [ + 186, 15, 234, 82, 63, 219, 212, 197, 196, 34, 118, 236, 98, 240, 196, 105, 168, + 40, 105, 29, + ], + [ + 18, 224, 72, 56, 142, 122, 233, 41, 142, 37, 4, 1, 220, 65, 184, 138, 197, 8, + 26, 47, + ], + [ + 129, 182, 106, 22, 119, 85, 17, 88, 80, 248, 17, 53, 147, 51, 154, 49, 141, + 245, 152, 123, + ], + [ + 185, 81, 255, 218, 171, 105, 200, 106, 62, 123, 245, 55, 112, 54, 55, 170, 157, + 177, 150, 227, + ], + [ + 147, 106, 143, 233, 122, 181, 142, 233, 164, 68, 169, 189, 54, 190, 122, 248, + 2, 125, 21, 134, + ], + [ + 239, 185, 83, 230, 18, 42, 86, 114, 34, 245, 255, 221, 78, 186, 112, 111, 67, + 22, 190, 171, + ], + [ + 209, 146, 18, 50, 187, 237, 175, 118, 73, 89, 224, 75, 92, 20, 146, 105, 25, + 234, 206, 29, + ], + [ + 30, 33, 57, 133, 105, 49, 203, 101, 40, 243, 45, 138, 11, 177, 101, 41, 161, + 195, 3, 124, + ], + [ + 122, 238, 28, 31, 60, 26, 102, 202, 11, 126, 46, 47, 199, 58, 106, 89, 99, 21, + 94, 176, + ], + [ + 184, 167, 138, 35, 137, 208, 139, 232, 214, 101, 244, 86, 159, 191, 223, 67, + 209, 240, 110, 131, + ], + [ + 82, 153, 26, 30, 64, 198, 66, 48, 126, 249, 81, 145, 95, 248, 125, 74, 21, 149, + 169, 158, + ], + [ + 81, 173, 157, 193, 190, 169, 69, 64, 134, 135, 98, 116, 82, 74, 213, 240, 128, + 135, 30, 162, + ], + [ + 41, 96, 163, 78, 226, 27, 34, 213, 50, 126, 78, 164, 231, 164, 224, 230, 218, + 130, 110, 248, + ], + [ + 66, 182, 121, 191, 45, 51, 122, 52, 173, 17, 169, 253, 223, 21, 54, 100, 109, + 216, 161, 167, + ], + [ + 133, 45, 4, 150, 216, 103, 61, 71, 144, 180, 94, 171, 187, 195, 244, 74, 251, + 43, 9, 31, + ], + [ + 7, 133, 72, 168, 108, 67, 98, 23, 149, 104, 13, 15, 170, 70, 244, 127, 198, + 181, 120, 166, + ], + [ + 210, 112, 157, 149, 196, 183, 9, 94, 40, 18, 75, 176, 188, 208, 52, 196, 8, + 109, 63, 114, + ], + [ + 155, 138, 204, 73, 192, 237, 241, 124, 140, 43, 155, 139, 101, 103, 186, 40, + 55, 191, 194, 115, + ], + [ + 35, 185, 12, 10, 44, 220, 81, 20, 54, 114, 94, 185, 147, 148, 138, 93, 138, + 170, 55, 8, + ], + [ + 67, 78, 142, 103, 15, 5, 145, 145, 115, 22, 137, 16, 24, 160, 23, 194, 235, + 110, 73, 248, + ], + [ + 224, 212, 156, 129, 16, 108, 254, 137, 47, 166, 196, 186, 223, 43, 59, 130, + 113, 108, 193, 161, + ], + [ + 174, 103, 195, 16, 149, 116, 35, 165, 92, 123, 100, 220, 83, 91, 131, 45, 55, + 106, 240, 233, + ], + [ + 4, 92, 105, 86, 142, 84, 213, 206, 5, 251, 4, 7, 4, 107, 240, 238, 202, 57, 71, + 140, + ], + [ + 238, 38, 255, 185, 70, 22, 45, 117, 168, 32, 117, 184, 34, 48, 251, 93, 224, + 240, 19, 144, + ], + [ + 156, 206, 14, 74, 209, 21, 218, 175, 253, 227, 250, 184, 26, 74, 236, 253, 19, + 112, 212, 213, + ], + [ + 168, 217, 141, 111, 154, 56, 73, 166, 57, 51, 96, 132, 20, 178, 162, 0, 229, + 211, 184, 221, + ], + [ + 10, 81, 129, 163, 154, 93, 189, 21, 72, 223, 88, 88, 140, 253, 31, 23, 179, 33, + 68, 41, + ], + [ + 157, 45, 89, 245, 45, 194, 234, 59, 132, 10, 1, 30, 243, 137, 95, 92, 244, 39, + 50, 221, + ], + [ + 52, 125, 15, 149, 177, 184, 204, 128, 45, 41, 204, 133, 92, 62, 107, 233, 85, + 74, 163, 186, + ], + [ + 131, 166, 93, 252, 227, 42, 65, 210, 48, 151, 4, 153, 110, 234, 191, 195, 236, + 157, 23, 100, + ], + [ + 177, 72, 95, 99, 40, 127, 101, 241, 120, 19, 138, 154, 200, 227, 142, 77, 10, + 5, 226, 96, + ], + [ + 95, 123, 244, 16, 121, 232, 63, 28, 253, 155, 47, 252, 135, 54, 170, 245, 233, + 144, 131, 27, + ], + [ + 225, 112, 207, 135, 136, 25, 241, 120, 23, 59, 174, 177, 110, 69, 170, 253, + 205, 30, 113, 7, + ], + [ + 208, 89, 164, 196, 139, 3, 56, 178, 196, 113, 97, 28, 96, 43, 43, 202, 3, 221, + 12, 114, + ], + [ + 144, 13, 62, 31, 131, 252, 165, 181, 223, 110, 84, 55, 142, 205, 146, 76, 108, + 52, 223, 2, + ], + [ + 38, 103, 38, 183, 102, 30, 246, 91, 51, 79, 200, 9, 79, 204, 88, 222, 78, 87, + 225, 254, + ], + [ + 43, 99, 6, 153, 6, 31, 193, 163, 10, 165, 141, 18, 64, 247, 103, 31, 25, 123, + 117, 160, + ], + [ + 15, 120, 243, 118, 239, 35, 246, 79, 146, 186, 106, 38, 230, 246, 169, 60, 73, + 106, 37, 30, + ], + [ + 6, 78, 87, 225, 197, 110, 224, 132, 24, 51, 19, 139, 125, 244, 229, 113, 127, + 172, 2, 137, + ], + [ + 148, 146, 96, 244, 144, 177, 206, 247, 143, 74, 29, 229, 59, 235, 232, 67, 81, + 252, 144, 58, + ], + [ + 29, 5, 222, 220, 41, 136, 219, 91, 134, 237, 73, 183, 97, 86, 128, 183, 208, + 72, 184, 152, + ], + [ + 65, 138, 40, 201, 224, 240, 20, 172, 4, 216, 201, 172, 224, 240, 132, 98, 47, + 146, 165, 24, + ], + [ + 4, 236, 211, 149, 15, 226, 187, 143, 39, 6, 173, 80, 128, 4, 182, 35, 16, 186, + 96, 5, + ], + [ + 19, 225, 171, 215, 110, 89, 70, 218, 144, 87, 196, 87, 47, 14, 124, 175, 172, + 170, 149, 148, + ], + [ + 185, 63, 43, 168, 249, 147, 181, 150, 103, 118, 142, 68, 76, 21, 117, 37, 41, + 51, 179, 227, + ], + ], + ), + } +} diff --git a/crates/agent/src/signal.rs b/crates/agent/src/signal.rs new file mode 100644 index 0000000..d8a01b8 --- /dev/null +++ b/crates/agent/src/signal.rs @@ -0,0 +1,112 @@ +use bitcoin::Txid; +use musig2::{AggNonce, PartialSignature, PubNonce}; +use strata_bridge_primitives::types::OperatorIdx; +use strata_bridge_tx_graph::peg_out_graph::PegOutGraphInput; + +#[derive(Debug, Clone)] +pub enum DepositSignal { + /// Sent by signers to each other. + Nonce { + txid: Txid, + pubnonce: PubNonce, + sender_id: OperatorIdx, + }, + + /// Sent by signers to each other. + Signature { + txid: Txid, + signature: PartialSignature, + sender_id: OperatorIdx, + }, +} + +#[derive(Debug, Clone)] +pub enum WatcherSignal { + /// Sent by bitcoin watcher to disprover + AssertChainAvailable { + claim_txid: Txid, + pre_assrt_txid: Txid, + assert_data_txid: Txid, + post_assert_txid: Txid, + }, + // Add other signals like: `ChallengeReceived` +} + +#[derive(Debug, Clone)] +#[allow(clippy::large_enum_variant)] +pub enum CovenantNonceSignal { + /// Sent by operators to signers. + Request { + details: CovenantNonceRequest, + + // metadata + sender_id: OperatorIdx, + }, + + /// Sent by signers to operators. + RequestFulfilled { + details: CovenantNonceRequestFulfilled, + + // metadata + sender_id: OperatorIdx, + destination_id: OperatorIdx, + }, +} + +#[derive(Debug, Clone)] +pub struct CovenantNonceRequest { + pub peg_out_graph_input: PegOutGraphInput, // single field struct created for consistency +} + +#[derive(Debug, Clone)] +pub struct CovenantNonceRequestFulfilled { + pub pre_assert: PubNonce, + pub post_assert: PubNonce, + pub disprove: PubNonce, + pub payout_0: PubNonce, // requires key-spend key aggregation + pub payout_1: PubNonce, // requires script-spend key aggregation +} + +#[derive(Debug, Clone)] +#[allow(clippy::large_enum_variant)] +pub enum CovenantSignatureSignal { + /// Sent by operators to signers. + Request { + details: CovenantSigRequest, + + // metadata + sender_id: OperatorIdx, + }, + + /// Sent by signers to operators. + RequestFulfilled { + details: CovenantSigRequestFulfilled, + + // metadata + sender_id: OperatorIdx, + destination_id: OperatorIdx, + }, +} + +#[derive(Debug, Clone)] +pub struct CovenantSigRequest { + pub peg_out_graph_input: PegOutGraphInput, + pub agg_nonces: AggNonces, +} + +#[derive(Debug, Clone)] +pub struct CovenantSigRequestFulfilled { + pub pre_assert: Vec, + pub post_assert: Vec, // for each of the inputs + pub disprove: Vec, + pub payout: Vec, +} + +#[derive(Debug, Clone)] +pub struct AggNonces { + pub pre_assert: AggNonce, + pub post_assert: AggNonce, + pub disprove: AggNonce, + pub payout_0: AggNonce, + pub payout_1: AggNonce, +} diff --git a/crates/agent/src/verifier.rs b/crates/agent/src/verifier.rs new file mode 100644 index 0000000..dfa3edd --- /dev/null +++ b/crates/agent/src/verifier.rs @@ -0,0 +1,3089 @@ +use bitcoin::{hashes::Hash, Transaction, TxOut, Txid}; +use bitvm::{ + groth16::g16, + signatures::wots::{wots256, wots32, SignatureImpl}, + treepp::*, +}; +use strata_bridge_db::{connector_db::ConnectorDb, public::PublicDb}; +use strata_bridge_primitives::{ + build_context::{BuildContext, TxBuildContext}, + helpers::hash_to_bn254_fq, + params::{ + prelude::{NUM_CONNECTOR_A160, NUM_CONNECTOR_A256}, + tx::{BTC_CONFIRM_PERIOD, DISPROVER_REWARD}, + }, + scripts::{ + parse_witness::parse_assertion_witnesses, + wots::{bridge_poc_verification_key, mock, Signatures}, + }, + types::OperatorIdx, +}; +use strata_bridge_tx_graph::{ + connectors::prelude::{ConnectorA30, ConnectorA31, ConnectorA31Leaf}, + transactions::{ + constants::{NUM_ASSERT_DATA_TX, NUM_ASSERT_DATA_TX1_A256_PK7}, + prelude::{DisproveData, DisproveTx}, + }, +}; +use tokio::sync::broadcast::{self, error::RecvError}; +use tracing::{error, info, trace, warn}; + +use crate::base::Agent; + +#[derive(Clone, Debug)] +#[expect(clippy::large_enum_variant)] +pub enum VerifierDuty { + VerifyClaim { + operator_id: OperatorIdx, + deposit_txid: Txid, + + claim_tx: Transaction, + }, + VerifyAssertions { + operator_id: OperatorIdx, + deposit_txid: Txid, + + post_assert_tx: Transaction, + claim_tx: Transaction, + assert_data_txs: [Transaction; NUM_ASSERT_DATA_TX], + }, +} + +pub type VerifierIdx = u32; + +#[derive(Debug)] +pub struct Verifier { + pub agent: Agent, // required for broadcasting tx + + build_context: TxBuildContext, + + public_db: PublicDb, +} + +impl Verifier { + #[allow(clippy::too_many_arguments)] + pub fn new(public_db: PublicDb, build_context: TxBuildContext, agent: Agent) -> Self { + Self { + public_db, + build_context, + agent, + } + } + + pub async fn start(&mut self, duty_receiver: &mut broadcast::Receiver) { + info!(action = "starting verifier"); + + loop { + match duty_receiver.recv().await { + Ok(verifier_duty) => { + trace!(event = "received duty", ?verifier_duty); // NOTE: this is a very big data structure beware before logging + self.process_duty(verifier_duty).await; + } + Err(RecvError::Lagged(skipped_messages)) => { + warn!(action = "processing last available duty", event = "duty executor lagging behind, please adjust '--duty-interval' arg", %skipped_messages); + } + Err(err) => { + error!(msg = "error receiving duties", ?err); + + panic!("verifier duty sender closed unexpectedly"); + } + } + } + } + + pub async fn process_duty(&mut self, duty: VerifierDuty) { + match duty { + VerifierDuty::VerifyClaim { + operator_id: _, + deposit_txid: _, + claim_tx, + } => { + warn!("No challenging yet!"); + let (_superblock_period_start_ts, _bridge_out_txid) = + self.parse_claim_tx(&claim_tx); + + // get bridge_out_tx from bitcoin canonical chain + // verify that the latest checkpoint state in the rollup has a withdrawal request + // that + // 1. matches the operator_id inscribed in the first OP_RETURN UTXO. + // 2. matches the recipient address in second P2TR UTXO. + // If these checks fail, the settle the challenge transaction (anyone can pay) + } + VerifierDuty::VerifyAssertions { + operator_id, + deposit_txid, + + post_assert_tx, + claim_tx, + assert_data_txs, + } => { + info!(event = "verifying assertion", by_operator=%operator_id, for_deposit=%deposit_txid); + + let (superblock_period_start_ts, bridge_out_txid) = self.parse_claim_tx(&claim_tx); + info!(event = "parsed claim transaction", superblock_start_ts_size = superblock_period_start_ts.len(), bridge_out_txid_size = %bridge_out_txid.len()); + + let (superblock_hash, groth16) = self.parse_assert_data_txs(&assert_data_txs); + info!(event = "parsed assert data", wots256_signature_size=%groth16.0.len(), groth16_signature_size=%groth16.1.len()); + + let signatures = Signatures { + bridge_out_txid, + superblock_hash, + superblock_period_start_ts, + groth16, + }; + info!(event = "constructed signatures"); + + let public_keys = self + .public_db + .get_wots_public_keys(operator_id, deposit_txid) + .await; + + let connector_leaf = { + // 1. public input hash validation + info!(action = "validating public input hash"); + + let public_inputs: mock::BridgeProofPublicParams = ( + deposit_txid.to_byte_array(), + superblock_hash.parse(), + bridge_out_txid.parse(), + u32::from_le_bytes(superblock_period_start_ts.parse()), + ); + let serialized_public_inputs = bincode::serialize(&public_inputs).unwrap(); + let public_inputs_hash = hash_to_bn254_fq(&serialized_public_inputs); + let committed_public_inputs_hash = groth16.0[0].parse(); + + // TODO: remove this: fix nibble flipping + let committed_public_inputs_hash = + committed_public_inputs_hash.map(|b| ((b & 0xf0) >> 4) | ((b & 0x0f) << 4)); + + if public_inputs_hash != committed_public_inputs_hash { + warn!(msg = "public inputs hash mismatch"); + Some(ConnectorA31Leaf::DisprovePublicInputsCommitment( + deposit_txid, + Some(( + superblock_hash, + bridge_out_txid, + superblock_period_start_ts, + groth16.0[0], + )), + )) + } else { + // 2. do superblock validation + let is_superblock_invalid = false; + if is_superblock_invalid { + unreachable!("always false for now"); + } else { + info!(action = "verifying groth16 assertions"); + // 3. groth16 proof validation + if let Some((tapleaf_index, witness_script)) = + g16::verify_signed_assertions( + bridge_poc_verification_key(), + public_keys.groth16, + signatures.groth16, + ) + { + let disprove_script = g16::generate_disprove_scripts( + public_keys.groth16, + &self.public_db.get_partial_disprove_scripts().await, + )[tapleaf_index] + .clone(); + Some(ConnectorA31Leaf::DisproveProof(( + disprove_script, + Some(witness_script), + ))) + } else { + None + } + } + } + }; + + const STAKE_OUTPUT_INDEX: usize = 0; + if let Some(disprove_leaf) = connector_leaf { + info!(action = "constructing disprove tx", for_operator_id=%operator_id, %deposit_txid); + let disprove_tx_data = DisproveData { + post_assert_txid: post_assert_tx.compute_txid(), + deposit_txid, + input_stake: post_assert_tx + .tx_out(STAKE_OUTPUT_INDEX) + .expect("stake output must exist in post-assert tx") + .value, + network: self.build_context.network(), + }; + + let connector_a30 = ConnectorA30::new( + self.build_context.aggregated_pubkey(), + self.build_context.network(), + self.public_db.clone(), + ); + let connector_a31 = + ConnectorA31::new(self.build_context.network(), self.public_db.clone()); + + let disprove_tx = DisproveTx::new( + disprove_tx_data, + operator_id, + connector_a30.clone(), + connector_a31.clone(), + ) + .await; + + let reward_out = TxOut { + value: DISPROVER_REWARD, + script_pubkey: self + .agent + .taproot_address(self.build_context.network()) + .script_pubkey(), + }; + let signed_disprove_tx = disprove_tx + .finalize( + connector_a30, + connector_a31, + reward_out, + deposit_txid, + operator_id, + disprove_leaf, + ) + .await; + + { + let vsize = signed_disprove_tx.vsize(); + let total_size = signed_disprove_tx.total_size(); + let weight = signed_disprove_tx.weight(); + info!(event = "finalized disprove tx", txid = %signed_disprove_tx.compute_txid(), %vsize, %total_size, %weight); + } + + let disprove_txid = self + .agent + .wait_and_broadcast(&signed_disprove_tx, BTC_CONFIRM_PERIOD) + .await + .expect("should settle disprove tx correctly"); + + info!(event = "broadcasted disprove tx successfully", %disprove_txid, %deposit_txid, %operator_id); + } else { + info!(event = "assertion is valid!", %operator_id, %deposit_txid); + } + } + } + } + + // parse claim tx + pub fn parse_claim_tx( + &self, + claim_tx: &Transaction, + ) -> (wots32::Signature, wots256::Signature) { + let witness = claim_tx.input.first().unwrap().witness.to_vec(); + let (witness_txid, witness_ts) = witness.split_at(2 * wots256::N_DIGITS as usize); + ( + std::array::from_fn(|i| { + let (i, j) = (2 * i, 2 * i + 1); + let preimage = witness_ts[i].clone().try_into().unwrap(); + let digit = if witness_ts[j].is_empty() { + 0 + } else { + witness_ts[j][0] + }; + (preimage, digit) + }), + std::array::from_fn(|i| { + let (i, j) = (2 * i, 2 * i + 1); + let preimage = witness_txid[i].clone().try_into().unwrap(); + let digit = if witness_txid[j].is_empty() { + 0 + } else { + witness_txid[j][0] + }; + (preimage, digit) + }), + ) + } + + // parse assert data txs + pub fn parse_assert_data_txs( + &self, + assert_data_txs: &[Transaction; NUM_ASSERT_DATA_TX], + ) -> (wots256::Signature, g16::Signatures) { + let witnesses = assert_data_txs + .iter() + .flat_map(|tx| { + tx.input + .iter() + .map(|txin| { + script! { + for w in &txin.witness.to_vec()[..txin.witness.len()-2] { + if w.len() == 1 { { w[0] } } else { { w.clone() } } + } + } + }) + .collect::>() + }) + .collect::>(); + + parse_assertion_witnesses( + witnesses[..NUM_CONNECTOR_A256].to_vec().try_into().unwrap(), + witnesses[NUM_ASSERT_DATA_TX1_A256_PK7..NUM_CONNECTOR_A256 + NUM_CONNECTOR_A160] + .to_vec() + .try_into() + .unwrap(), + witnesses.last().cloned(), + ) + } +} + +#[cfg(test)] +mod tests { + use std::collections::BTreeMap; + + use bitcoin::{ + absolute::LockTime, hashes::Hash, transaction::Version, Amount, Network, OutPoint, + ScriptBuf, Sequence, TxIn, TxOut, Txid, Witness, XOnlyPublicKey, + }; + use bitvm::{ + groth16::g16, + signatures::wots::{wots256, wots32, SignatureImpl}, + treepp::*, + }; + use secp256k1::{Keypair, Secp256k1, SECP256K1}; + use strata_bridge_db::{connector_db::ConnectorDb, public::PublicDb}; + use strata_bridge_primitives::{ + build_context::TxBuildContext, + scripts::wots::{ + generate_wots_public_keys, generate_wots_signatures, mock, Assertions, Signatures, + }, + types::PublickeyTable, + }; + use strata_bridge_tx_graph::{ + connectors::{ + connector_s::ConnectorS, + connectors_a::{ConnectorA160Factory, ConnectorA256Factory}, + }, + mock_txid, + transactions::{ + assert_data::{AssertDataTxBatch, AssertDataTxInput}, + constants::NUM_ASSERT_DATA_TX, + }, + }; + + use super::Verifier; + use crate::{base::Agent, verifier::VerifierDuty}; + + fn mock_x_only_public_key() -> XOnlyPublicKey { + let secp = Secp256k1::new(); + let (secret_key, _) = secp.generate_keypair(&mut rand::thread_rng()); + let keypair = Keypair::from_secret_key(&secp, &secret_key); + XOnlyPublicKey::from_keypair(&keypair).0 + } + + pub fn mock_assertions() -> Assertions { + Assertions { + bridge_out_txid: [ + 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240, 32, 17, 34, + 51, 68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255, 32, + ], + superblock_hash: [ + 170, 187, 204, 221, 238, 255, 32, 17, 34, 51, 68, 85, 102, 119, 136, 153, 170, 187, + 204, 221, 238, 255, 32, 17, 34, 51, 68, 85, 102, 119, 136, 153, + ], + superblock_period_start_ts: [239, 190, 173, 222], + groth16: ( + [[ + 80, 109, 248, 180, 9, 193, 114, 154, 115, 169, 168, 240, 183, 156, 241, 46, 0, + 195, 134, 12, 230, 246, 101, 202, 24, 228, 13, 116, 183, 37, 201, 128, + ]], + [ + [ + 112, 32, 253, 238, 88, 207, 72, 195, 44, 15, 28, 45, 111, 218, 102, 212, + 59, 62, 92, 205, 53, 209, 79, 23, 87, 165, 0, 22, 142, 131, 210, 116, + ], + [ + 177, 135, 40, 180, 190, 166, 111, 221, 234, 212, 8, 155, 1, 162, 125, 235, + 228, 15, 224, 181, 139, 66, 227, 106, 118, 195, 110, 65, 62, 146, 11, 174, + ], + [ + 96, 67, 40, 63, 210, 100, 68, 67, 214, 86, 1, 22, 44, 130, 135, 244, 41, + 213, 154, 166, 25, 183, 13, 58, 206, 22, 59, 119, 155, 206, 164, 7, + ], + [ + 161, 121, 10, 93, 68, 113, 217, 16, 116, 190, 44, 205, 49, 203, 204, 151, + 29, 64, 97, 119, 220, 119, 161, 205, 165, 142, 60, 253, 251, 164, 35, 102, + ], + [ + 49, 164, 104, 126, 26, 19, 151, 135, 154, 195, 146, 177, 199, 88, 105, 212, + 55, 99, 238, 5, 201, 100, 61, 237, 129, 46, 207, 160, 5, 222, 135, 113, + ], + [ + 177, 101, 136, 196, 109, 10, 161, 23, 222, 239, 198, 72, 186, 53, 129, 183, + 11, 169, 19, 90, 210, 42, 21, 233, 37, 62, 94, 92, 233, 138, 38, 221, + ], + [ + 65, 111, 124, 126, 121, 141, 146, 7, 244, 45, 7, 234, 109, 117, 143, 250, + 187, 31, 40, 105, 45, 110, 231, 13, 208, 112, 33, 98, 222, 18, 248, 182, + ], + [ + 225, 84, 108, 194, 119, 110, 110, 100, 36, 85, 157, 165, 159, 96, 252, 210, + 185, 57, 95, 149, 195, 81, 156, 52, 88, 214, 244, 12, 247, 11, 150, 71, + ], + [ + 97, 64, 80, 35, 13, 233, 167, 154, 124, 78, 178, 162, 106, 54, 59, 64, 229, + 179, 205, 196, 179, 223, 128, 226, 154, 243, 232, 177, 81, 68, 225, 216, + ], + [ + 161, 235, 105, 177, 186, 96, 116, 226, 41, 91, 243, 16, 241, 48, 178, 229, + 142, 128, 16, 246, 216, 208, 89, 32, 239, 124, 63, 233, 126, 76, 180, 23, + ], + [ + 209, 164, 126, 151, 173, 208, 97, 238, 235, 5, 224, 7, 170, 235, 4, 111, + 116, 29, 236, 170, 243, 230, 80, 104, 195, 75, 164, 95, 185, 53, 160, 200, + ], + [ + 18, 237, 242, 40, 102, 5, 209, 218, 203, 74, 198, 220, 106, 192, 241, 142, + 48, 170, 113, 196, 156, 149, 152, 116, 126, 63, 208, 214, 111, 195, 2, 134, + ], + [ + 2, 118, 176, 214, 113, 165, 255, 95, 224, 25, 139, 17, 73, 243, 154, 101, + 2, 132, 23, 136, 64, 114, 71, 63, 147, 223, 126, 135, 172, 229, 202, 46, + ], + [ + 225, 174, 132, 233, 32, 201, 204, 25, 231, 53, 13, 57, 118, 65, 43, 6, 44, + 10, 246, 127, 187, 252, 10, 118, 144, 48, 173, 111, 177, 232, 52, 242, + ], + [ + 161, 238, 13, 77, 218, 255, 35, 80, 161, 139, 171, 94, 167, 97, 85, 133, + 185, 254, 205, 2, 202, 32, 222, 196, 152, 2, 158, 85, 82, 183, 194, 43, + ], + [ + 66, 40, 154, 53, 235, 37, 20, 104, 168, 251, 111, 239, 167, 14, 29, 246, + 251, 129, 195, 129, 43, 143, 225, 43, 37, 10, 249, 219, 153, 244, 81, 69, + ], + [ + 66, 148, 190, 211, 17, 10, 225, 206, 34, 114, 27, 22, 10, 245, 246, 229, + 241, 243, 190, 26, 8, 159, 9, 69, 69, 22, 216, 100, 82, 178, 209, 192, + ], + [ + 226, 128, 110, 219, 218, 31, 84, 117, 203, 190, 151, 2, 182, 60, 152, 50, + 228, 226, 113, 85, 201, 228, 147, 231, 30, 85, 93, 7, 188, 199, 21, 130, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + ], + [ + 240, 2, 111, 140, 141, 170, 146, 156, 123, 129, 139, 28, 145, 231, 173, + 147, 67, 224, 16, 227, 126, 213, 11, 245, 2, 63, 96, 228, 99, 183, 243, + 153, + ], + [ + 48, 250, 81, 177, 119, 8, 60, 97, 236, 87, 237, 15, 206, 171, 148, 158, 63, + 212, 185, 82, 223, 90, 219, 222, 81, 101, 40, 161, 223, 41, 21, 34, + ], + [ + 50, 122, 72, 143, 82, 176, 116, 146, 90, 178, 11, 62, 220, 97, 27, 42, 71, + 131, 187, 165, 151, 31, 43, 113, 197, 175, 166, 237, 95, 160, 193, 49, + ], + [ + 130, 60, 21, 130, 34, 229, 41, 195, 72, 175, 30, 0, 239, 16, 211, 164, 38, + 224, 6, 180, 249, 252, 116, 123, 29, 54, 207, 71, 31, 252, 177, 136, + ], + [ + 146, 119, 142, 36, 1, 73, 54, 239, 41, 236, 201, 145, 97, 184, 197, 82, + 161, 148, 11, 6, 91, 21, 204, 98, 144, 7, 81, 53, 1, 227, 4, 156, + ], + [ + 96, 72, 119, 220, 79, 200, 158, 38, 193, 169, 124, 194, 123, 92, 241, 152, + 212, 180, 246, 186, 105, 179, 171, 181, 143, 38, 42, 59, 119, 70, 248, 71, + ], + [ + 194, 6, 108, 27, 82, 109, 131, 35, 198, 133, 189, 198, 159, 231, 156, 33, + 5, 101, 87, 175, 165, 57, 35, 144, 61, 178, 57, 128, 85, 52, 217, 63, + ], + [ + 225, 19, 214, 240, 111, 230, 42, 175, 121, 238, 205, 215, 242, 188, 146, + 232, 70, 11, 116, 173, 66, 2, 104, 11, 207, 99, 41, 159, 146, 157, 2, 23, + ], + [ + 146, 116, 206, 214, 98, 58, 7, 94, 70, 102, 163, 46, 31, 171, 104, 32, 149, + 57, 53, 222, 215, 125, 170, 131, 173, 134, 101, 87, 221, 143, 151, 174, + ], + [ + 33, 160, 77, 9, 52, 137, 255, 131, 189, 194, 178, 99, 236, 56, 226, 119, + 188, 219, 238, 255, 26, 235, 48, 168, 52, 20, 146, 108, 12, 163, 35, 26, + ], + [ + 161, 164, 82, 52, 34, 242, 246, 38, 232, 70, 77, 148, 15, 219, 212, 148, + 39, 29, 85, 15, 216, 125, 37, 219, 54, 79, 175, 203, 197, 31, 84, 233, + ], + ], + [ + [ + 53, 97, 8, 126, 205, 170, 108, 97, 149, 233, 13, 68, 51, 217, 183, 26, 169, + 55, 27, 144, + ], + [ + 68, 156, 85, 148, 242, 169, 175, 91, 208, 190, 113, 253, 70, 150, 124, 80, + 67, 82, 221, 144, + ], + [ + 20, 102, 248, 19, 244, 7, 157, 84, 22, 135, 65, 2, 175, 59, 115, 28, 63, + 57, 219, 225, + ], + [ + 7, 99, 180, 128, 201, 48, 60, 2, 254, 124, 34, 223, 52, 122, 230, 113, 253, + 198, 193, 171, + ], + [ + 81, 173, 157, 193, 190, 169, 69, 64, 134, 135, 98, 116, 82, 74, 213, 240, + 128, 135, 30, 162, + ], + [ + 41, 96, 163, 78, 226, 27, 34, 213, 50, 126, 78, 164, 231, 164, 224, 230, + 218, 130, 110, 248, + ], + [ + 194, 66, 145, 64, 22, 197, 140, 64, 81, 178, 183, 100, 126, 235, 73, 208, + 48, 250, 90, 12, + ], + [ + 1, 250, 184, 76, 55, 239, 238, 224, 108, 74, 140, 130, 176, 205, 18, 213, + 168, 167, 134, 140, + ], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [ + 186, 190, 195, 165, 99, 209, 112, 248, 62, 5, 197, 146, 50, 65, 57, 164, + 221, 201, 14, 205, + ], + [ + 186, 178, 209, 41, 9, 247, 140, 37, 182, 5, 78, 93, 55, 145, 192, 184, 40, + 140, 31, 45, + ], + [ + 165, 73, 71, 32, 214, 193, 67, 161, 30, 215, 123, 132, 244, 173, 230, 34, + 41, 1, 153, 129, + ], + [ + 43, 193, 157, 22, 1, 57, 173, 66, 169, 219, 218, 197, 254, 46, 190, 212, + 188, 227, 223, 156, + ], + [ + 99, 183, 228, 207, 41, 53, 29, 170, 195, 119, 1, 1, 230, 67, 84, 88, 166, + 83, 110, 232, + ], + [ + 94, 253, 71, 217, 180, 8, 207, 67, 109, 248, 149, 196, 120, 13, 169, 27, + 169, 88, 53, 202, + ], + [ + 1, 109, 28, 218, 244, 69, 57, 231, 54, 76, 164, 43, 215, 103, 1, 178, 181, + 181, 228, 153, + ], + [ + 130, 95, 162, 67, 152, 201, 119, 14, 23, 102, 132, 6, 20, 100, 63, 82, 70, + 234, 140, 171, + ], + [ + 251, 7, 160, 74, 10, 35, 248, 53, 12, 50, 18, 5, 95, 89, 118, 185, 253, 97, + 74, 86, + ], + [ + 19, 41, 2, 182, 219, 167, 72, 54, 69, 128, 28, 108, 253, 71, 144, 18, 92, + 247, 52, 124, + ], + [ + 140, 139, 61, 168, 12, 37, 69, 182, 164, 101, 131, 161, 92, 103, 82, 230, + 18, 64, 172, 15, + ], + [ + 220, 90, 113, 13, 182, 186, 25, 233, 229, 31, 106, 53, 8, 248, 154, 207, 7, + 241, 151, 45, + ], + [ + 9, 33, 170, 238, 51, 70, 36, 34, 213, 31, 130, 204, 178, 80, 18, 21, 74, + 236, 103, 230, + ], + [ + 194, 108, 99, 78, 10, 19, 61, 207, 251, 118, 61, 122, 220, 127, 252, 80, + 172, 8, 105, 143, + ], + [ + 79, 35, 248, 41, 56, 124, 41, 227, 32, 254, 255, 13, 208, 196, 114, 202, + 120, 164, 4, 2, + ], + [ + 101, 253, 79, 200, 193, 227, 195, 172, 155, 187, 155, 56, 127, 207, 146, + 148, 192, 21, 161, 104, + ], + [ + 36, 229, 111, 131, 207, 67, 39, 200, 178, 163, 223, 100, 45, 35, 48, 67, + 44, 97, 59, 142, + ], + [ + 32, 206, 225, 15, 239, 200, 45, 254, 183, 204, 79, 215, 125, 183, 36, 95, + 6, 92, 38, 255, + ], + [ + 89, 57, 45, 54, 150, 13, 29, 94, 137, 226, 85, 126, 82, 112, 40, 244, 149, + 29, 118, 136, + ], + [ + 120, 33, 170, 53, 8, 136, 11, 29, 217, 212, 24, 46, 214, 233, 227, 198, 85, + 77, 13, 42, + ], + [ + 128, 108, 16, 98, 229, 27, 241, 248, 127, 48, 112, 244, 147, 118, 169, 231, + 20, 120, 149, 46, + ], + [ + 162, 36, 43, 207, 223, 231, 241, 66, 102, 40, 72, 71, 11, 247, 178, 141, + 238, 109, 113, 253, + ], + [ + 86, 169, 195, 159, 75, 98, 69, 66, 40, 219, 140, 138, 45, 214, 117, 37, 93, + 43, 201, 138, + ], + [ + 212, 207, 77, 229, 120, 63, 80, 103, 110, 95, 72, 220, 158, 229, 249, 0, + 228, 151, 98, 87, + ], + [ + 136, 231, 135, 102, 160, 122, 93, 225, 255, 167, 253, 87, 163, 77, 188, + 253, 223, 227, 102, 58, + ], + [ + 174, 222, 136, 142, 155, 38, 151, 115, 156, 221, 36, 30, 64, 69, 28, 98, + 33, 46, 193, 84, + ], + [ + 142, 240, 102, 46, 48, 62, 20, 182, 222, 173, 138, 111, 198, 129, 72, 187, + 66, 142, 154, 7, + ], + [ + 23, 145, 74, 201, 44, 121, 239, 204, 176, 57, 192, 194, 145, 172, 31, 89, + 50, 13, 239, 43, + ], + [ + 199, 217, 94, 129, 216, 196, 92, 41, 40, 202, 212, 178, 186, 200, 174, 165, + 248, 182, 60, 124, + ], + [ + 100, 87, 201, 223, 79, 86, 247, 248, 39, 245, 173, 93, 49, 107, 217, 181, + 155, 121, 204, 66, + ], + [ + 252, 159, 26, 49, 229, 254, 114, 95, 31, 3, 135, 134, 254, 78, 122, 16, + 151, 235, 73, 252, + ], + [ + 181, 67, 179, 64, 85, 200, 33, 95, 196, 122, 173, 186, 228, 156, 65, 115, + 31, 98, 114, 12, + ], + [ + 218, 207, 169, 142, 43, 204, 7, 235, 60, 238, 202, 28, 172, 137, 249, 195, + 120, 60, 50, 165, + ], + [ + 155, 195, 3, 80, 70, 133, 53, 206, 243, 173, 80, 208, 21, 10, 168, 59, 29, + 34, 15, 24, + ], + [ + 89, 251, 162, 79, 164, 82, 111, 130, 148, 133, 218, 7, 36, 172, 93, 151, + 68, 58, 185, 169, + ], + [ + 150, 104, 230, 250, 31, 37, 38, 235, 143, 244, 81, 56, 25, 250, 129, 121, + 171, 224, 204, 179, + ], + [ + 111, 19, 21, 208, 180, 213, 65, 41, 215, 50, 91, 28, 127, 115, 83, 68, 207, + 113, 213, 11, + ], + [ + 181, 11, 247, 66, 223, 178, 19, 65, 45, 141, 235, 10, 245, 198, 18, 231, 7, + 136, 225, 219, + ], + [ + 32, 123, 43, 203, 136, 150, 158, 43, 123, 102, 224, 15, 130, 129, 120, 241, + 84, 170, 143, 234, + ], + [ + 229, 255, 214, 58, 215, 84, 129, 64, 218, 78, 115, 6, 150, 99, 231, 120, + 102, 132, 90, 183, + ], + [ + 21, 21, 64, 161, 176, 180, 135, 204, 246, 228, 107, 88, 5, 77, 197, 46, + 232, 211, 250, 60, + ], + [ + 101, 242, 191, 132, 56, 38, 132, 101, 223, 201, 185, 88, 34, 247, 78, 132, + 253, 54, 252, 191, + ], + [ + 234, 16, 221, 38, 223, 178, 231, 109, 106, 26, 169, 53, 16, 59, 77, 116, + 129, 8, 34, 182, + ], + [ + 214, 247, 216, 161, 249, 211, 27, 149, 182, 112, 221, 96, 155, 140, 172, + 47, 133, 192, 81, 109, + ], + [ + 179, 108, 39, 196, 126, 2, 189, 60, 101, 230, 140, 58, 162, 149, 57, 187, + 59, 137, 152, 222, + ], + [ + 10, 40, 154, 133, 18, 33, 154, 130, 42, 79, 111, 1, 13, 141, 198, 250, 185, + 210, 247, 144, + ], + [ + 147, 112, 58, 90, 145, 103, 205, 106, 192, 96, 250, 31, 52, 157, 72, 213, + 5, 156, 222, 20, + ], + [ + 77, 179, 121, 196, 169, 217, 40, 172, 7, 192, 2, 168, 234, 217, 4, 230, + 102, 118, 33, 160, + ], + [ + 197, 76, 225, 121, 20, 116, 89, 195, 250, 232, 46, 183, 80, 207, 35, 153, + 173, 206, 50, 208, + ], + [ + 236, 224, 63, 120, 177, 191, 96, 252, 93, 13, 23, 231, 72, 189, 112, 150, + 7, 152, 135, 19, + ], + [ + 167, 44, 224, 96, 35, 250, 26, 189, 84, 198, 161, 188, 126, 35, 35, 171, + 196, 250, 24, 51, + ], + [ + 157, 24, 71, 138, 144, 106, 145, 215, 98, 97, 70, 11, 102, 248, 152, 130, + 230, 11, 236, 55, + ], + [ + 49, 162, 205, 68, 127, 190, 55, 235, 124, 71, 40, 233, 121, 19, 71, 153, + 27, 152, 138, 64, + ], + [ + 50, 166, 226, 38, 183, 245, 82, 29, 27, 202, 177, 193, 91, 248, 52, 100, + 133, 215, 179, 36, + ], + [ + 166, 196, 254, 115, 63, 83, 44, 56, 27, 84, 228, 115, 176, 48, 47, 121, + 206, 159, 219, 169, + ], + [ + 134, 168, 201, 87, 249, 120, 103, 217, 64, 171, 149, 183, 180, 44, 9, 75, + 242, 10, 127, 119, + ], + [ + 25, 73, 39, 239, 120, 189, 152, 141, 174, 90, 117, 117, 192, 35, 107, 112, + 100, 119, 195, 129, + ], + [ + 252, 236, 121, 241, 91, 198, 165, 239, 91, 160, 166, 203, 192, 238, 11, + 205, 61, 194, 199, 230, + ], + [ + 70, 44, 123, 81, 215, 116, 221, 237, 52, 201, 148, 249, 211, 13, 77, 148, + 17, 184, 86, 215, + ], + [ + 109, 148, 235, 158, 56, 81, 91, 84, 156, 173, 73, 11, 65, 249, 249, 97, + 121, 220, 89, 40, + ], + [ + 88, 20, 4, 32, 234, 69, 66, 190, 16, 111, 92, 234, 238, 43, 144, 214, 215, + 37, 202, 233, + ], + [ + 37, 201, 175, 12, 193, 38, 141, 15, 77, 131, 53, 238, 42, 195, 223, 8, 236, + 87, 30, 2, + ], + [ + 140, 107, 226, 226, 73, 97, 156, 189, 147, 106, 103, 223, 38, 89, 183, 29, + 49, 135, 135, 90, + ], + [ + 174, 230, 167, 136, 156, 238, 78, 184, 101, 7, 101, 64, 105, 192, 8, 23, + 55, 223, 250, 246, + ], + [ + 74, 227, 28, 145, 141, 158, 57, 208, 125, 127, 198, 46, 217, 3, 135, 198, + 215, 44, 134, 152, + ], + [ + 169, 241, 178, 138, 192, 162, 39, 38, 88, 235, 6, 251, 234, 210, 92, 55, + 171, 197, 158, 139, + ], + [ + 242, 61, 232, 35, 243, 73, 203, 245, 229, 224, 46, 171, 138, 254, 29, 129, + 129, 174, 141, 58, + ], + [ + 21, 178, 78, 19, 120, 116, 65, 188, 94, 23, 1, 212, 84, 19, 183, 99, 251, + 107, 159, 148, + ], + [ + 208, 39, 225, 131, 7, 231, 155, 239, 186, 91, 50, 108, 173, 222, 211, 196, + 205, 81, 80, 56, + ], + [ + 29, 226, 49, 0, 201, 210, 34, 214, 156, 86, 18, 174, 231, 215, 172, 8, 237, + 23, 227, 29, + ], + [ + 4, 113, 58, 245, 88, 66, 218, 149, 29, 42, 197, 122, 219, 134, 113, 226, + 226, 170, 228, 4, + ], + [ + 185, 22, 92, 127, 54, 137, 96, 0, 42, 49, 6, 137, 33, 242, 18, 87, 76, 32, + 132, 85, + ], + [ + 79, 27, 74, 33, 250, 87, 37, 46, 226, 45, 173, 188, 191, 50, 235, 105, 208, + 145, 103, 242, + ], + [ + 85, 90, 49, 57, 9, 197, 207, 103, 117, 13, 108, 197, 88, 174, 185, 14, 182, + 68, 95, 158, + ], + [ + 173, 176, 72, 104, 224, 199, 247, 196, 112, 53, 47, 203, 225, 44, 24, 141, + 228, 131, 63, 197, + ], + [ + 24, 37, 219, 63, 220, 219, 20, 96, 204, 132, 136, 35, 184, 228, 214, 188, + 200, 237, 98, 203, + ], + [ + 106, 140, 171, 171, 218, 31, 219, 61, 37, 13, 185, 87, 162, 27, 16, 70, + 119, 231, 134, 196, + ], + [ + 144, 34, 177, 204, 187, 110, 148, 215, 153, 86, 216, 181, 84, 130, 162, + 145, 140, 114, 221, 83, + ], + [ + 34, 174, 122, 41, 93, 65, 154, 224, 196, 30, 85, 203, 254, 192, 244, 21, + 206, 208, 70, 174, + ], + [ + 103, 204, 201, 132, 83, 146, 137, 73, 16, 55, 153, 112, 166, 248, 230, 134, + 154, 214, 151, 185, + ], + [ + 160, 38, 37, 22, 73, 105, 127, 210, 224, 174, 6, 151, 151, 186, 61, 76, + 113, 62, 126, 168, + ], + [ + 45, 164, 116, 84, 30, 108, 120, 56, 145, 158, 37, 119, 135, 214, 94, 231, + 162, 109, 29, 174, + ], + [ + 5, 130, 61, 57, 158, 99, 22, 88, 62, 208, 253, 0, 101, 227, 88, 194, 11, + 221, 251, 75, + ], + [ + 64, 133, 79, 202, 144, 157, 236, 15, 54, 191, 101, 110, 251, 70, 47, 242, + 234, 129, 144, 26, + ], + [ + 98, 68, 199, 193, 116, 32, 158, 110, 139, 248, 201, 106, 33, 218, 234, 44, + 37, 251, 91, 109, + ], + [ + 37, 237, 209, 7, 129, 194, 233, 239, 160, 26, 247, 37, 86, 95, 247, 3, 41, + 56, 85, 237, + ], + [ + 16, 19, 3, 95, 37, 21, 79, 5, 118, 91, 116, 205, 51, 28, 230, 214, 172, + 133, 73, 40, + ], + [ + 144, 105, 10, 22, 146, 44, 97, 137, 220, 189, 191, 161, 216, 82, 126, 82, + 12, 81, 251, 171, + ], + [ + 112, 75, 141, 242, 31, 73, 233, 232, 164, 142, 15, 96, 119, 31, 32, 135, + 65, 76, 105, 225, + ], + [ + 100, 61, 65, 215, 28, 253, 107, 116, 68, 113, 226, 135, 9, 149, 250, 92, + 180, 61, 143, 176, + ], + [ + 65, 240, 192, 94, 225, 114, 159, 197, 217, 187, 234, 15, 167, 124, 202, + 199, 42, 68, 217, 119, + ], + [ + 64, 91, 95, 106, 200, 255, 162, 63, 135, 206, 239, 224, 16, 35, 43, 197, + 239, 104, 115, 175, + ], + [ + 72, 47, 7, 200, 183, 197, 236, 20, 164, 151, 244, 131, 167, 187, 86, 184, + 62, 122, 54, 18, + ], + [ + 208, 62, 23, 247, 134, 116, 148, 75, 51, 171, 80, 132, 75, 41, 42, 55, 119, + 156, 169, 221, + ], + [ + 189, 61, 156, 6, 246, 16, 255, 228, 221, 39, 211, 187, 251, 175, 6, 177, + 50, 173, 55, 29, + ], + [ + 121, 112, 237, 115, 138, 191, 148, 111, 92, 242, 103, 187, 36, 24, 135, + 211, 248, 98, 183, 225, + ], + [ + 156, 226, 144, 114, 53, 43, 193, 98, 249, 1, 234, 184, 252, 116, 88, 143, + 89, 123, 220, 226, + ], + [ + 192, 245, 91, 16, 5, 16, 231, 131, 101, 75, 224, 156, 14, 233, 0, 42, 76, + 210, 72, 53, + ], + [ + 238, 148, 119, 163, 80, 72, 38, 62, 239, 49, 242, 191, 178, 233, 17, 230, + 129, 34, 146, 32, + ], + [ + 148, 90, 214, 0, 149, 149, 228, 63, 146, 255, 21, 138, 103, 93, 134, 150, + 247, 236, 24, 170, + ], + [ + 34, 200, 46, 152, 169, 118, 33, 139, 253, 4, 35, 81, 94, 128, 204, 81, 206, + 225, 6, 124, + ], + [ + 250, 108, 179, 20, 87, 229, 31, 175, 191, 95, 45, 122, 138, 142, 42, 172, + 80, 133, 102, 4, + ], + [ + 177, 95, 245, 193, 39, 222, 232, 210, 207, 103, 122, 137, 66, 125, 84, 62, + 23, 197, 100, 42, + ], + [ + 199, 199, 185, 252, 149, 232, 160, 16, 125, 246, 4, 237, 40, 181, 45, 206, + 184, 97, 95, 55, + ], + [ + 171, 213, 235, 216, 162, 253, 228, 83, 22, 40, 106, 52, 72, 14, 115, 98, + 170, 48, 187, 191, + ], + [ + 73, 175, 177, 58, 117, 190, 58, 216, 135, 211, 43, 53, 204, 109, 213, 217, + 131, 193, 130, 103, + ], + [ + 157, 233, 219, 70, 104, 34, 35, 182, 42, 2, 171, 255, 234, 192, 155, 5, 50, + 78, 214, 65, + ], + [ + 73, 137, 189, 41, 217, 91, 212, 73, 104, 173, 240, 12, 132, 9, 171, 18, + 144, 111, 76, 90, + ], + [ + 105, 218, 90, 13, 119, 118, 97, 174, 138, 183, 103, 231, 43, 212, 15, 72, + 22, 158, 44, 3, + ], + [ + 38, 108, 243, 6, 193, 198, 229, 219, 155, 18, 221, 195, 213, 146, 241, 216, + 124, 217, 235, 175, + ], + [ + 176, 209, 42, 182, 170, 49, 89, 194, 37, 151, 112, 240, 135, 130, 158, 133, + 89, 100, 63, 186, + ], + [ + 206, 172, 2, 17, 104, 126, 171, 170, 5, 219, 45, 150, 70, 125, 244, 17, 90, + 182, 252, 92, + ], + [ + 230, 178, 84, 14, 111, 23, 40, 64, 116, 155, 186, 89, 124, 227, 47, 120, + 134, 241, 2, 162, + ], + [ + 204, 72, 170, 7, 17, 182, 131, 189, 54, 229, 45, 51, 136, 142, 105, 95, + 226, 227, 171, 50, + ], + [ + 252, 36, 153, 6, 229, 55, 133, 23, 114, 147, 51, 97, 182, 237, 158, 233, + 127, 30, 111, 26, + ], + [ + 240, 219, 185, 48, 239, 134, 164, 216, 67, 6, 87, 10, 63, 226, 150, 93, 90, + 187, 29, 21, + ], + [ + 135, 41, 241, 245, 192, 102, 193, 215, 64, 72, 58, 39, 221, 190, 147, 18, + 251, 14, 36, 242, + ], + [ + 185, 135, 227, 198, 115, 69, 122, 249, 141, 193, 202, 247, 14, 5, 164, 66, + 191, 149, 41, 231, + ], + [ + 46, 209, 107, 115, 212, 92, 218, 242, 212, 101, 224, 26, 200, 178, 85, 248, + 205, 79, 180, 185, + ], + [ + 51, 169, 142, 166, 199, 174, 5, 61, 204, 90, 42, 17, 48, 250, 37, 230, 118, + 170, 138, 251, + ], + [ + 21, 4, 138, 214, 218, 192, 161, 211, 125, 66, 116, 50, 171, 156, 42, 138, + 25, 81, 210, 27, + ], + [ + 80, 239, 128, 9, 124, 13, 200, 221, 48, 37, 94, 111, 139, 19, 24, 93, 51, + 30, 150, 151, + ], + [ + 151, 7, 100, 230, 123, 171, 250, 190, 33, 132, 144, 116, 81, 104, 185, 69, + 33, 129, 132, 71, + ], + [ + 148, 16, 24, 119, 120, 31, 220, 101, 247, 83, 66, 0, 128, 55, 251, 255, 34, + 112, 225, 141, + ], + [ + 192, 233, 227, 143, 151, 250, 141, 254, 228, 212, 20, 2, 2, 70, 117, 148, + 140, 39, 134, 34, + ], + [ + 58, 229, 237, 174, 133, 105, 61, 93, 146, 175, 216, 63, 251, 95, 70, 88, + 177, 226, 72, 235, + ], + [ + 22, 229, 248, 98, 1, 99, 178, 62, 198, 100, 17, 44, 146, 46, 96, 117, 212, + 34, 170, 236, + ], + [ + 1, 145, 113, 254, 232, 28, 151, 122, 43, 135, 219, 10, 80, 251, 222, 120, + 249, 180, 63, 9, + ], + [ + 0, 185, 7, 117, 77, 69, 243, 64, 60, 223, 57, 47, 213, 231, 155, 1, 29, + 184, 0, 168, + ], + [ + 181, 113, 17, 65, 90, 102, 182, 154, 26, 1, 95, 159, 114, 55, 216, 7, 101, + 37, 36, 180, + ], + [ + 67, 227, 74, 226, 196, 231, 48, 81, 126, 60, 170, 23, 235, 147, 5, 34, 67, + 58, 2, 251, + ], + [ + 93, 70, 237, 19, 215, 246, 173, 117, 135, 145, 91, 181, 156, 133, 216, 155, + 15, 79, 134, 242, + ], + [ + 143, 186, 108, 111, 212, 53, 183, 160, 76, 141, 133, 224, 91, 175, 243, 98, + 187, 88, 173, 128, + ], + [ + 148, 21, 204, 111, 61, 87, 155, 186, 65, 105, 217, 1, 188, 76, 134, 201, + 148, 242, 143, 7, + ], + [ + 150, 93, 56, 52, 178, 168, 28, 228, 169, 111, 3, 28, 220, 141, 160, 176, + 39, 154, 116, 215, + ], + [ + 223, 106, 14, 181, 238, 209, 238, 244, 118, 130, 43, 77, 103, 121, 2, 1, + 178, 0, 219, 88, + ], + [ + 6, 226, 244, 108, 50, 134, 249, 50, 101, 249, 168, 240, 190, 153, 229, 152, + 154, 189, 234, 164, + ], + [ + 184, 169, 115, 177, 172, 28, 193, 113, 147, 100, 235, 36, 164, 112, 235, + 97, 102, 16, 80, 51, + ], + [ + 200, 83, 245, 31, 178, 177, 152, 158, 196, 239, 47, 157, 154, 6, 130, 45, + 192, 209, 169, 174, + ], + [ + 52, 137, 153, 213, 42, 78, 175, 153, 79, 175, 142, 137, 208, 14, 125, 181, + 189, 212, 26, 233, + ], + [ + 76, 35, 226, 136, 26, 85, 34, 66, 76, 118, 161, 13, 23, 136, 238, 137, 201, + 246, 122, 156, + ], + [ + 84, 157, 189, 139, 187, 126, 46, 64, 146, 56, 227, 157, 77, 158, 121, 242, + 165, 45, 251, 190, + ], + [ + 96, 143, 181, 238, 74, 175, 218, 181, 19, 92, 170, 120, 27, 172, 89, 64, + 117, 209, 155, 117, + ], + [ + 13, 152, 39, 89, 134, 6, 182, 1, 139, 14, 13, 5, 254, 110, 52, 249, 254, + 164, 191, 45, + ], + [ + 230, 98, 108, 134, 196, 228, 178, 136, 93, 119, 181, 214, 165, 50, 232, 25, + 63, 190, 247, 53, + ], + [ + 76, 97, 2, 93, 83, 26, 205, 115, 215, 223, 118, 71, 136, 29, 189, 147, 36, + 21, 37, 77, + ], + [ + 224, 223, 107, 41, 142, 92, 254, 25, 154, 212, 224, 43, 164, 59, 192, 19, + 4, 70, 176, 64, + ], + [ + 232, 181, 86, 215, 197, 45, 221, 211, 126, 184, 166, 178, 154, 185, 90, + 156, 30, 250, 138, 17, + ], + [ + 233, 26, 4, 224, 44, 106, 89, 126, 198, 232, 20, 245, 43, 163, 12, 126, 21, + 168, 6, 130, + ], + [ + 38, 123, 216, 228, 115, 124, 135, 125, 101, 68, 119, 225, 237, 162, 195, + 102, 207, 77, 17, 10, + ], + [ + 239, 80, 245, 221, 144, 61, 9, 104, 34, 72, 125, 142, 204, 178, 65, 190, + 156, 156, 189, 45, + ], + [ + 138, 222, 173, 46, 149, 38, 20, 205, 85, 28, 171, 206, 19, 134, 165, 31, + 184, 84, 187, 36, + ], + [ + 90, 42, 99, 219, 115, 11, 46, 251, 208, 111, 31, 175, 108, 69, 126, 220, + 162, 233, 244, 51, + ], + [ + 129, 192, 205, 33, 198, 125, 199, 187, 94, 151, 228, 235, 50, 144, 20, 140, + 156, 133, 211, 49, + ], + [ + 37, 213, 19, 174, 117, 129, 9, 146, 87, 223, 133, 126, 52, 163, 39, 77, + 223, 3, 85, 11, + ], + [ + 225, 66, 97, 246, 130, 20, 161, 119, 225, 229, 99, 235, 51, 23, 80, 253, + 38, 249, 177, 90, + ], + [ + 224, 49, 90, 82, 38, 49, 92, 192, 91, 215, 77, 108, 57, 166, 181, 194, 12, + 223, 212, 213, + ], + [ + 235, 205, 220, 105, 126, 101, 96, 111, 40, 59, 171, 84, 73, 182, 143, 25, + 176, 187, 19, 83, + ], + [ + 18, 178, 209, 128, 58, 134, 167, 140, 106, 3, 43, 40, 152, 88, 112, 81, + 166, 252, 151, 159, + ], + [ + 114, 201, 84, 103, 153, 172, 239, 158, 103, 145, 63, 201, 189, 139, 190, + 139, 44, 115, 132, 13, + ], + [ + 195, 25, 200, 251, 196, 26, 163, 192, 250, 37, 146, 10, 111, 243, 137, 17, + 209, 102, 15, 233, + ], + [ + 109, 169, 253, 134, 34, 210, 164, 253, 181, 38, 229, 66, 157, 38, 29, 21, + 77, 11, 54, 9, + ], + [ + 247, 187, 220, 13, 198, 164, 38, 252, 150, 57, 163, 19, 97, 82, 62, 193, + 73, 246, 253, 154, + ], + [ + 191, 16, 121, 44, 167, 59, 57, 244, 119, 204, 186, 13, 72, 99, 56, 174, + 207, 1, 126, 201, + ], + [ + 252, 194, 99, 172, 202, 8, 170, 94, 122, 224, 83, 103, 128, 93, 196, 253, + 106, 34, 222, 51, + ], + [ + 21, 53, 124, 177, 27, 157, 176, 255, 11, 97, 195, 83, 50, 133, 220, 153, + 38, 204, 63, 1, + ], + [ + 121, 181, 165, 241, 130, 84, 159, 56, 250, 95, 170, 75, 76, 168, 119, 214, + 19, 33, 107, 35, + ], + [ + 92, 181, 59, 161, 250, 183, 211, 248, 237, 253, 210, 29, 115, 235, 214, 43, + 28, 24, 223, 96, + ], + [ + 170, 141, 119, 179, 113, 26, 115, 176, 152, 218, 54, 5, 209, 228, 212, 79, + 229, 93, 61, 200, + ], + [ + 15, 72, 108, 22, 127, 22, 101, 112, 122, 61, 212, 179, 64, 2, 72, 185, 0, + 130, 198, 214, + ], + [ + 153, 19, 114, 82, 176, 115, 214, 160, 250, 236, 166, 57, 224, 132, 61, 128, + 51, 100, 46, 68, + ], + [ + 171, 66, 109, 167, 99, 238, 97, 132, 178, 70, 109, 197, 134, 83, 25, 157, + 158, 183, 140, 151, + ], + [ + 25, 28, 95, 36, 83, 167, 81, 4, 199, 80, 121, 201, 113, 181, 138, 161, 108, + 221, 40, 17, + ], + [ + 54, 200, 102, 66, 243, 110, 216, 27, 202, 215, 125, 89, 218, 136, 226, 119, + 69, 207, 119, 160, + ], + [ + 60, 211, 17, 117, 115, 16, 210, 20, 153, 23, 104, 198, 234, 168, 4, 146, + 75, 22, 136, 180, + ], + [ + 32, 30, 201, 251, 171, 231, 228, 71, 97, 193, 249, 91, 233, 236, 201, 129, + 201, 78, 59, 118, + ], + [ + 203, 216, 23, 19, 10, 121, 252, 209, 184, 8, 38, 19, 28, 17, 166, 18, 215, + 30, 35, 192, + ], + [ + 248, 28, 194, 227, 140, 130, 204, 124, 89, 26, 118, 196, 207, 232, 200, 27, + 64, 50, 248, 209, + ], + [ + 127, 39, 85, 244, 124, 11, 194, 211, 208, 160, 76, 169, 118, 0, 28, 123, + 34, 83, 249, 116, + ], + [ + 224, 23, 85, 57, 130, 67, 112, 81, 166, 239, 151, 170, 173, 65, 37, 98, 59, + 44, 35, 164, + ], + [ + 255, 125, 185, 240, 70, 216, 108, 64, 101, 222, 220, 158, 150, 196, 215, + 196, 60, 16, 148, 196, + ], + [ + 120, 100, 219, 181, 237, 39, 0, 84, 206, 123, 235, 30, 249, 98, 52, 91, + 167, 171, 185, 63, + ], + [ + 35, 35, 55, 79, 26, 162, 36, 235, 109, 136, 43, 163, 5, 223, 18, 19, 245, + 49, 51, 157, + ], + [ + 188, 120, 64, 134, 30, 243, 90, 168, 84, 152, 88, 109, 168, 38, 2, 213, 93, + 159, 55, 202, + ], + [ + 233, 43, 176, 248, 43, 81, 67, 163, 147, 78, 8, 51, 216, 32, 108, 117, 29, + 146, 62, 31, + ], + [ + 135, 171, 193, 55, 243, 235, 134, 43, 247, 59, 234, 207, 162, 194, 139, + 180, 19, 49, 123, 216, + ], + [ + 167, 66, 104, 221, 190, 190, 140, 239, 129, 235, 136, 130, 21, 64, 16, 233, + 210, 109, 153, 124, + ], + [ + 163, 152, 153, 239, 143, 52, 113, 36, 141, 120, 167, 176, 102, 0, 216, 196, + 116, 120, 151, 225, + ], + [ + 141, 103, 197, 20, 188, 171, 189, 16, 206, 128, 153, 230, 144, 92, 187, 55, + 192, 255, 192, 214, + ], + [ + 137, 209, 218, 139, 94, 212, 176, 201, 128, 221, 135, 216, 32, 180, 58, + 187, 176, 73, 180, 20, + ], + [ + 203, 217, 73, 192, 139, 118, 160, 40, 107, 84, 91, 193, 104, 92, 253, 8, + 94, 96, 6, 240, + ], + [ + 59, 223, 136, 137, 52, 234, 193, 50, 195, 136, 206, 23, 63, 150, 19, 193, + 230, 139, 26, 184, + ], + [ + 139, 121, 24, 228, 203, 210, 35, 157, 218, 110, 221, 121, 240, 252, 149, + 210, 107, 188, 98, 118, + ], + [ + 254, 95, 232, 186, 154, 78, 32, 137, 52, 45, 163, 159, 52, 56, 233, 9, 28, + 230, 106, 137, + ], + [ + 226, 0, 18, 129, 100, 49, 72, 78, 116, 54, 9, 150, 230, 212, 118, 1, 149, + 225, 235, 146, + ], + [ + 9, 176, 75, 162, 117, 107, 182, 224, 72, 249, 123, 173, 29, 3, 66, 120, 24, + 24, 144, 103, + ], + [ + 150, 37, 188, 247, 209, 34, 91, 40, 166, 8, 27, 68, 194, 25, 153, 193, 253, + 41, 4, 236, + ], + [ + 149, 87, 28, 219, 49, 45, 144, 254, 2, 180, 233, 247, 52, 234, 90, 54, 115, + 89, 194, 127, + ], + [ + 201, 216, 230, 218, 7, 199, 9, 192, 191, 219, 29, 238, 123, 53, 35, 188, + 175, 179, 84, 119, + ], + [ + 98, 33, 17, 39, 29, 198, 223, 41, 241, 99, 37, 81, 14, 19, 176, 0, 17, 37, + 0, 230, + ], + [ + 30, 255, 147, 249, 111, 220, 114, 206, 161, 181, 221, 100, 70, 200, 67, 57, + 140, 35, 187, 137, + ], + [ + 85, 174, 177, 196, 110, 200, 160, 73, 232, 112, 133, 232, 133, 18, 57, 85, + 11, 139, 88, 57, + ], + [ + 39, 19, 220, 37, 27, 234, 81, 76, 22, 65, 253, 143, 60, 107, 43, 140, 7, + 197, 56, 199, + ], + [ + 176, 216, 77, 184, 97, 79, 169, 101, 23, 138, 204, 117, 85, 103, 5, 168, + 224, 239, 136, 116, + ], + [ + 68, 76, 87, 152, 149, 65, 237, 211, 122, 1, 52, 104, 134, 38, 213, 221, 9, + 208, 160, 116, + ], + [ + 234, 80, 54, 21, 240, 102, 255, 12, 207, 187, 126, 4, 165, 193, 242, 255, + 193, 177, 40, 35, + ], + [ + 132, 239, 46, 192, 69, 13, 202, 113, 252, 43, 172, 170, 179, 58, 197, 184, + 173, 221, 243, 107, + ], + [ + 247, 179, 107, 217, 49, 157, 17, 161, 179, 40, 155, 118, 250, 253, 235, 80, + 228, 180, 183, 92, + ], + [ + 150, 201, 217, 18, 164, 238, 139, 87, 15, 106, 211, 238, 188, 85, 52, 73, + 252, 145, 200, 255, + ], + [ + 110, 64, 94, 200, 193, 4, 7, 183, 235, 213, 233, 138, 254, 193, 143, 110, + 250, 6, 140, 132, + ], + [ + 59, 116, 55, 93, 249, 247, 236, 235, 51, 228, 144, 107, 9, 128, 151, 119, + 233, 195, 225, 177, + ], + [ + 130, 218, 85, 115, 212, 14, 67, 101, 122, 57, 134, 206, 206, 99, 166, 165, + 8, 102, 13, 80, + ], + [ + 145, 43, 137, 172, 128, 156, 44, 175, 201, 169, 145, 172, 242, 204, 194, + 237, 101, 134, 135, 48, + ], + [ + 78, 198, 154, 53, 62, 144, 8, 91, 207, 161, 157, 63, 141, 172, 175, 85, + 187, 112, 238, 153, + ], + [ + 192, 2, 253, 152, 11, 31, 166, 170, 189, 18, 136, 184, 187, 22, 163, 48, + 62, 78, 212, 224, + ], + [ + 222, 85, 175, 169, 28, 201, 209, 22, 235, 22, 186, 7, 165, 245, 93, 53, + 165, 79, 195, 166, + ], + [ + 237, 109, 88, 215, 2, 106, 20, 208, 165, 102, 201, 25, 84, 186, 184, 73, + 37, 48, 110, 130, + ], + [ + 180, 112, 123, 26, 11, 47, 241, 182, 97, 212, 150, 52, 18, 238, 202, 245, + 22, 93, 68, 234, + ], + [ + 222, 149, 93, 27, 125, 57, 51, 121, 246, 165, 67, 179, 98, 174, 85, 112, + 34, 218, 58, 144, + ], + [ + 168, 54, 160, 241, 22, 67, 151, 106, 57, 239, 72, 32, 76, 184, 140, 90, 51, + 40, 42, 118, + ], + [ + 178, 223, 254, 204, 49, 218, 80, 248, 102, 218, 11, 63, 51, 16, 239, 21, + 154, 221, 221, 68, + ], + [ + 0, 80, 197, 189, 115, 33, 191, 191, 138, 139, 81, 201, 6, 206, 48, 201, 7, + 237, 183, 33, + ], + [ + 232, 141, 7, 170, 230, 227, 166, 214, 71, 45, 106, 17, 12, 34, 18, 162, + 139, 39, 176, 108, + ], + [ + 173, 143, 60, 164, 90, 137, 75, 17, 124, 242, 125, 44, 92, 198, 230, 25, + 205, 178, 246, 114, + ], + [ + 211, 57, 172, 200, 170, 36, 96, 117, 103, 217, 65, 61, 51, 85, 223, 185, + 188, 49, 212, 227, + ], + [ + 221, 231, 177, 83, 124, 70, 246, 217, 47, 253, 121, 109, 184, 148, 152, 43, + 169, 116, 136, 94, + ], + [ + 53, 155, 147, 193, 178, 253, 60, 179, 113, 102, 254, 169, 84, 130, 247, 12, + 21, 142, 124, 82, + ], + [ + 123, 21, 51, 218, 233, 181, 209, 181, 3, 72, 104, 160, 126, 216, 19, 250, + 150, 69, 201, 17, + ], + [ + 77, 221, 69, 167, 185, 107, 245, 15, 219, 73, 104, 88, 205, 246, 224, 100, + 39, 220, 252, 243, + ], + [ + 162, 230, 60, 140, 104, 54, 113, 211, 199, 131, 128, 133, 120, 222, 42, 78, + 193, 97, 144, 80, + ], + [ + 13, 57, 26, 43, 28, 226, 157, 149, 177, 69, 214, 62, 12, 247, 20, 49, 48, + 25, 233, 210, + ], + [ + 204, 98, 176, 168, 170, 193, 139, 39, 231, 167, 154, 231, 202, 193, 193, + 158, 57, 51, 248, 198, + ], + [ + 155, 190, 198, 164, 218, 125, 184, 62, 163, 81, 219, 89, 101, 122, 82, 59, + 121, 89, 57, 194, + ], + [ + 98, 230, 62, 238, 8, 6, 247, 111, 30, 138, 116, 68, 22, 223, 245, 124, 141, + 174, 254, 199, + ], + [ + 4, 73, 208, 175, 233, 170, 239, 226, 152, 33, 18, 13, 145, 144, 238, 221, + 134, 50, 22, 99, + ], + [ + 62, 51, 44, 95, 235, 227, 254, 34, 30, 216, 152, 135, 184, 25, 84, 70, 138, + 104, 214, 174, + ], + [ + 43, 172, 71, 142, 150, 99, 188, 139, 139, 102, 45, 68, 195, 163, 205, 234, + 230, 112, 124, 157, + ], + [ + 41, 54, 46, 149, 159, 192, 250, 165, 96, 188, 0, 12, 133, 101, 227, 169, + 54, 173, 46, 239, + ], + [ + 26, 128, 246, 182, 7, 245, 120, 96, 212, 214, 194, 117, 164, 110, 146, 128, + 52, 94, 143, 92, + ], + [ + 190, 227, 253, 116, 244, 51, 129, 238, 228, 165, 148, 251, 143, 195, 34, + 136, 240, 52, 240, 133, + ], + [ + 180, 30, 58, 98, 99, 185, 52, 187, 150, 165, 246, 117, 14, 98, 184, 163, + 83, 95, 245, 67, + ], + [ + 197, 192, 17, 204, 119, 132, 197, 243, 117, 173, 164, 10, 228, 26, 29, 190, + 166, 21, 181, 252, + ], + [ + 92, 175, 169, 48, 223, 195, 134, 93, 132, 239, 38, 169, 44, 251, 42, 107, + 174, 17, 24, 85, + ], + [ + 18, 198, 208, 225, 149, 61, 27, 88, 134, 81, 196, 202, 21, 138, 24, 27, + 212, 39, 197, 191, + ], + [ + 93, 181, 248, 2, 188, 93, 224, 219, 206, 48, 82, 219, 137, 187, 158, 128, + 41, 35, 125, 21, + ], + [ + 178, 83, 36, 122, 56, 245, 175, 232, 70, 167, 1, 24, 43, 195, 196, 56, 3, + 49, 80, 52, + ], + [ + 8, 124, 177, 229, 187, 213, 17, 158, 169, 73, 159, 31, 77, 4, 108, 199, 34, + 124, 81, 213, + ], + [ + 167, 194, 123, 247, 76, 52, 198, 110, 245, 145, 78, 227, 172, 194, 132, + 140, 162, 242, 112, 57, + ], + [ + 179, 9, 29, 152, 162, 222, 56, 125, 217, 87, 175, 122, 138, 225, 124, 218, + 215, 6, 239, 132, + ], + [ + 230, 118, 27, 213, 42, 13, 152, 42, 85, 50, 192, 21, 210, 10, 14, 210, 204, + 62, 239, 112, + ], + [ + 173, 25, 69, 81, 150, 27, 80, 164, 153, 23, 5, 176, 240, 200, 168, 134, + 240, 85, 47, 216, + ], + [ + 167, 67, 115, 248, 34, 111, 181, 151, 106, 194, 67, 148, 91, 174, 86, 6, + 161, 21, 78, 173, + ], + [ + 196, 215, 176, 176, 90, 245, 233, 36, 185, 173, 246, 18, 214, 239, 101, + 187, 228, 203, 65, 177, + ], + [ + 172, 116, 27, 44, 246, 240, 193, 181, 254, 87, 229, 18, 226, 25, 46, 35, + 46, 87, 147, 168, + ], + [ + 139, 29, 222, 54, 169, 205, 108, 220, 224, 110, 230, 97, 34, 20, 222, 245, + 77, 98, 44, 187, + ], + [ + 255, 117, 61, 242, 225, 183, 248, 224, 193, 244, 75, 186, 162, 147, 198, + 70, 172, 198, 76, 254, + ], + [ + 217, 129, 128, 249, 129, 38, 80, 106, 204, 173, 195, 190, 150, 228, 114, + 193, 93, 70, 181, 139, + ], + [ + 38, 238, 197, 123, 121, 47, 251, 13, 188, 230, 170, 43, 141, 119, 81, 225, + 175, 219, 49, 48, + ], + [ + 42, 231, 249, 184, 117, 17, 77, 168, 8, 127, 130, 123, 19, 193, 199, 55, + 70, 196, 245, 232, + ], + [ + 228, 226, 71, 187, 141, 114, 32, 12, 111, 20, 216, 13, 187, 119, 72, 170, + 149, 123, 167, 136, + ], + [ + 251, 216, 135, 58, 249, 85, 96, 242, 131, 89, 241, 170, 170, 213, 137, 65, + 211, 229, 138, 215, + ], + [ + 162, 64, 93, 178, 156, 113, 62, 121, 149, 40, 125, 192, 187, 200, 225, 241, + 179, 191, 253, 167, + ], + [ + 103, 168, 35, 167, 147, 92, 149, 154, 99, 219, 223, 237, 86, 174, 21, 31, + 170, 192, 218, 3, + ], + [ + 79, 250, 153, 180, 215, 134, 51, 45, 41, 234, 169, 67, 83, 229, 81, 41, 74, + 113, 229, 97, + ], + [ + 199, 238, 181, 1, 78, 141, 210, 2, 66, 6, 255, 200, 182, 8, 98, 159, 171, + 227, 65, 104, + ], + [ + 184, 36, 77, 7, 67, 61, 14, 203, 21, 229, 6, 7, 213, 216, 225, 229, 211, + 17, 108, 240, + ], + [ + 245, 158, 118, 39, 210, 5, 166, 242, 28, 112, 170, 65, 159, 137, 7, 74, + 197, 55, 134, 253, + ], + [ + 167, 50, 169, 68, 46, 93, 88, 93, 198, 20, 9, 43, 204, 127, 8, 192, 179, + 223, 94, 72, + ], + [ + 197, 206, 193, 5, 77, 128, 7, 43, 90, 60, 197, 64, 173, 40, 21, 131, 65, + 71, 70, 93, + ], + [ + 11, 159, 58, 44, 220, 219, 40, 229, 149, 159, 251, 156, 110, 28, 223, 81, + 87, 90, 195, 4, + ], + [ + 106, 239, 182, 47, 220, 202, 54, 135, 249, 143, 216, 194, 113, 232, 98, 37, + 29, 28, 255, 92, + ], + [ + 249, 125, 26, 198, 178, 128, 113, 229, 241, 11, 86, 111, 207, 73, 208, 134, + 42, 167, 192, 16, + ], + [ + 125, 9, 10, 153, 194, 224, 232, 53, 113, 14, 188, 65, 159, 39, 63, 127, + 118, 84, 140, 183, + ], + [ + 136, 174, 194, 91, 82, 125, 65, 203, 27, 165, 141, 182, 101, 147, 135, 103, + 175, 40, 158, 88, + ], + [ + 2, 178, 38, 208, 137, 106, 117, 132, 60, 138, 7, 126, 35, 149, 82, 100, + 228, 218, 51, 122, + ], + [ + 193, 162, 81, 16, 190, 218, 220, 18, 15, 169, 87, 247, 94, 118, 92, 250, + 187, 70, 117, 192, + ], + [ + 39, 55, 97, 127, 84, 50, 127, 94, 140, 141, 233, 46, 130, 29, 200, 250, + 122, 49, 128, 170, + ], + [ + 41, 132, 219, 235, 107, 36, 134, 184, 87, 206, 117, 247, 227, 193, 226, 33, + 202, 171, 174, 137, + ], + [ + 204, 118, 199, 230, 129, 146, 80, 30, 30, 247, 118, 144, 166, 205, 19, 159, + 196, 4, 138, 102, + ], + [ + 198, 192, 184, 129, 112, 174, 38, 40, 116, 3, 102, 2, 168, 206, 108, 71, + 103, 167, 242, 99, + ], + [ + 238, 165, 249, 43, 97, 69, 37, 245, 102, 170, 58, 42, 127, 231, 255, 51, + 148, 135, 213, 209, + ], + [ + 20, 21, 43, 12, 35, 32, 99, 231, 153, 168, 49, 47, 249, 166, 106, 98, 204, + 172, 234, 255, + ], + [ + 237, 34, 231, 144, 28, 150, 138, 200, 122, 58, 68, 4, 74, 169, 252, 254, + 109, 106, 57, 187, + ], + [ + 185, 19, 249, 126, 232, 164, 64, 113, 95, 120, 11, 162, 186, 60, 89, 141, + 154, 80, 79, 169, + ], + [ + 153, 179, 12, 222, 189, 2, 246, 37, 225, 86, 252, 12, 207, 185, 237, 244, + 209, 46, 30, 153, + ], + [ + 134, 42, 144, 3, 113, 157, 106, 94, 92, 81, 47, 93, 14, 129, 135, 87, 64, + 238, 209, 180, + ], + [ + 189, 115, 243, 110, 146, 138, 114, 102, 49, 235, 191, 234, 71, 60, 32, 180, + 237, 173, 159, 236, + ], + [ + 145, 24, 72, 129, 51, 133, 110, 198, 221, 32, 115, 57, 169, 189, 122, 116, + 215, 244, 90, 81, + ], + [ + 81, 44, 197, 42, 172, 193, 164, 82, 211, 231, 7, 132, 166, 214, 233, 221, + 212, 143, 51, 18, + ], + [ + 247, 216, 162, 207, 158, 89, 148, 32, 184, 51, 229, 235, 143, 186, 26, 248, + 140, 89, 241, 55, + ], + [ + 45, 29, 82, 28, 151, 57, 241, 16, 132, 26, 1, 13, 20, 214, 214, 30, 83, 60, + 243, 116, + ], + [ + 128, 138, 73, 68, 1, 1, 46, 13, 96, 66, 139, 46, 86, 230, 2, 64, 124, 255, + 169, 185, + ], + [ + 91, 69, 207, 210, 110, 254, 80, 85, 245, 238, 45, 234, 139, 104, 135, 87, + 116, 174, 27, 58, + ], + [ + 25, 107, 101, 58, 183, 237, 199, 42, 4, 83, 120, 44, 108, 123, 181, 222, + 231, 226, 95, 20, + ], + [ + 229, 45, 182, 124, 93, 7, 103, 25, 59, 148, 120, 38, 92, 78, 82, 39, 36, + 134, 91, 68, + ], + [ + 73, 79, 247, 174, 39, 31, 223, 150, 159, 202, 174, 116, 156, 242, 65, 185, + 255, 142, 74, 70, + ], + [ + 195, 179, 131, 72, 121, 13, 226, 14, 68, 101, 218, 252, 97, 134, 91, 125, + 237, 179, 87, 63, + ], + [ + 93, 38, 195, 168, 18, 111, 238, 83, 240, 48, 234, 232, 238, 136, 131, 31, + 91, 47, 109, 204, + ], + [ + 98, 8, 103, 22, 137, 228, 97, 119, 84, 73, 191, 31, 130, 112, 55, 168, 91, + 115, 159, 196, + ], + [ + 188, 155, 207, 121, 84, 224, 44, 200, 141, 35, 8, 213, 9, 56, 99, 235, 117, + 31, 108, 40, + ], + [ + 139, 249, 25, 58, 215, 173, 118, 191, 221, 118, 240, 184, 172, 201, 152, + 247, 243, 253, 80, 21, + ], + [ + 94, 147, 243, 113, 233, 80, 204, 102, 83, 93, 8, 218, 117, 106, 236, 148, + 82, 160, 75, 54, + ], + [ + 186, 157, 182, 62, 16, 32, 152, 251, 7, 155, 168, 42, 210, 74, 89, 166, 95, + 5, 210, 22, + ], + [ + 62, 146, 198, 9, 164, 92, 160, 130, 73, 68, 118, 6, 175, 120, 225, 78, 241, + 95, 208, 251, + ], + [ + 135, 180, 193, 177, 134, 231, 21, 200, 150, 11, 83, 25, 220, 199, 132, 42, + 46, 140, 90, 174, + ], + [ + 172, 92, 200, 0, 8, 66, 55, 145, 252, 48, 111, 12, 49, 70, 225, 132, 68, + 67, 200, 139, + ], + [ + 60, 145, 15, 157, 129, 205, 87, 157, 179, 209, 128, 75, 86, 73, 125, 107, + 39, 28, 117, 135, + ], + [ + 94, 250, 209, 215, 153, 147, 39, 27, 125, 235, 38, 123, 132, 126, 254, 241, + 174, 97, 207, 94, + ], + [ + 135, 239, 192, 203, 218, 110, 132, 137, 3, 128, 152, 31, 209, 47, 150, 242, + 246, 124, 198, 129, + ], + [ + 183, 131, 243, 104, 251, 23, 112, 196, 132, 215, 116, 227, 50, 253, 53, + 137, 55, 13, 97, 5, + ], + [ + 234, 167, 82, 131, 202, 80, 202, 72, 102, 225, 171, 214, 246, 182, 87, 56, + 169, 215, 115, 121, + ], + [ + 220, 145, 253, 161, 67, 249, 224, 43, 21, 81, 219, 97, 0, 214, 6, 126, 3, + 119, 96, 2, + ], + [ + 247, 51, 52, 255, 130, 160, 143, 135, 117, 63, 198, 167, 49, 249, 214, 98, + 146, 56, 117, 214, + ], + [ + 18, 184, 112, 155, 0, 13, 128, 205, 222, 187, 47, 192, 254, 251, 84, 123, + 90, 201, 219, 138, + ], + [ + 41, 206, 114, 114, 49, 63, 178, 153, 210, 4, 90, 193, 244, 251, 169, 214, + 246, 34, 251, 111, + ], + [ + 192, 220, 164, 203, 29, 3, 34, 34, 117, 150, 62, 211, 16, 117, 152, 79, + 192, 237, 0, 245, + ], + [ + 153, 84, 125, 30, 254, 93, 214, 229, 80, 23, 115, 77, 67, 236, 149, 153, + 174, 215, 177, 106, + ], + [ + 244, 20, 232, 68, 145, 132, 221, 17, 214, 206, 126, 62, 57, 143, 208, 15, + 193, 15, 131, 98, + ], + [ + 223, 72, 231, 185, 177, 16, 185, 199, 248, 156, 205, 37, 9, 122, 76, 166, + 115, 214, 172, 108, + ], + [ + 158, 24, 197, 97, 47, 251, 96, 114, 98, 129, 223, 69, 230, 155, 230, 171, + 47, 223, 214, 64, + ], + [ + 114, 60, 60, 213, 70, 203, 227, 232, 246, 157, 73, 156, 83, 12, 159, 9, + 216, 109, 83, 222, + ], + [ + 119, 149, 117, 22, 204, 205, 48, 183, 89, 146, 56, 22, 182, 108, 49, 71, + 26, 114, 91, 164, + ], + [ + 7, 20, 110, 127, 156, 194, 97, 111, 165, 59, 136, 110, 69, 42, 21, 116, + 217, 71, 116, 18, + ], + [ + 48, 6, 92, 46, 23, 41, 33, 125, 205, 215, 220, 255, 105, 166, 90, 82, 207, + 248, 254, 94, + ], + [ + 40, 175, 174, 99, 225, 186, 166, 78, 43, 16, 18, 32, 148, 34, 220, 1, 227, + 91, 102, 248, + ], + [ + 132, 138, 7, 224, 160, 144, 194, 30, 231, 70, 227, 83, 141, 133, 180, 12, + 118, 95, 95, 188, + ], + [ + 152, 173, 240, 14, 174, 82, 117, 39, 132, 70, 59, 155, 178, 158, 124, 40, + 61, 180, 207, 35, + ], + [ + 96, 177, 129, 158, 85, 40, 217, 6, 101, 201, 29, 204, 105, 253, 58, 0, 194, + 98, 9, 239, + ], + [ + 146, 75, 191, 202, 193, 135, 97, 238, 123, 65, 149, 88, 153, 103, 148, 231, + 218, 145, 101, 178, + ], + [ + 163, 251, 94, 233, 187, 0, 197, 235, 221, 93, 187, 159, 91, 138, 197, 130, + 90, 235, 168, 170, + ], + [ + 119, 0, 142, 100, 195, 20, 198, 92, 46, 183, 105, 10, 70, 197, 64, 86, 127, + 93, 220, 255, + ], + [ + 39, 42, 121, 173, 133, 213, 37, 23, 15, 74, 51, 161, 239, 186, 178, 172, + 166, 83, 137, 139, + ], + [ + 250, 90, 46, 84, 183, 58, 50, 7, 216, 169, 240, 211, 223, 238, 229, 164, + 152, 139, 19, 17, + ], + [ + 253, 79, 83, 36, 191, 233, 2, 159, 136, 155, 4, 222, 188, 127, 35, 205, + 220, 107, 143, 54, + ], + [ + 2, 85, 47, 115, 42, 210, 35, 185, 54, 171, 129, 166, 38, 211, 107, 249, + 174, 175, 45, 237, + ], + [ + 4, 187, 41, 74, 131, 239, 10, 178, 114, 143, 214, 169, 205, 7, 68, 204, + 187, 206, 122, 118, + ], + [ + 160, 199, 212, 189, 252, 143, 5, 210, 177, 64, 2, 195, 91, 42, 120, 70, + 167, 69, 71, 17, + ], + [ + 244, 168, 202, 246, 213, 83, 116, 222, 126, 233, 183, 174, 44, 208, 79, + 108, 120, 255, 142, 198, + ], + [ + 75, 129, 244, 190, 43, 9, 115, 13, 206, 125, 198, 71, 70, 201, 137, 44, + 103, 147, 29, 20, + ], + [ + 168, 142, 179, 102, 150, 159, 156, 161, 68, 214, 135, 239, 30, 117, 187, + 47, 65, 238, 64, 21, + ], + [ + 194, 70, 132, 143, 108, 188, 100, 41, 235, 14, 37, 86, 148, 96, 149, 206, + 248, 241, 184, 72, + ], + [ + 100, 87, 133, 208, 21, 44, 15, 93, 16, 174, 113, 113, 128, 88, 45, 51, 42, + 172, 31, 129, + ], + [ + 21, 253, 66, 153, 210, 74, 74, 125, 10, 179, 38, 240, 147, 52, 236, 221, + 236, 93, 223, 170, + ], + [ + 222, 79, 209, 201, 26, 114, 96, 69, 221, 86, 181, 55, 5, 195, 75, 210, 221, + 191, 69, 213, + ], + [ + 48, 15, 170, 219, 26, 221, 69, 206, 93, 96, 105, 34, 210, 79, 41, 49, 62, + 199, 18, 227, + ], + [ + 19, 160, 108, 194, 235, 105, 216, 217, 150, 2, 56, 231, 16, 134, 21, 137, + 39, 155, 86, 22, + ], + [ + 176, 218, 216, 40, 79, 237, 228, 34, 140, 29, 56, 113, 96, 36, 76, 207, + 172, 111, 168, 173, + ], + [ + 53, 206, 154, 45, 1, 92, 116, 54, 44, 199, 162, 231, 224, 205, 132, 159, + 68, 225, 236, 41, + ], + [ + 214, 144, 111, 80, 154, 26, 167, 53, 20, 212, 28, 67, 99, 49, 85, 139, 173, + 9, 110, 209, + ], + [ + 203, 194, 123, 191, 127, 159, 148, 108, 252, 126, 23, 214, 54, 69, 74, 126, + 172, 187, 115, 43, + ], + [ + 33, 228, 83, 159, 37, 83, 148, 125, 70, 154, 136, 57, 240, 233, 198, 146, + 75, 25, 204, 183, + ], + [ + 254, 201, 191, 0, 25, 85, 62, 217, 235, 190, 84, 253, 244, 142, 57, 180, + 72, 86, 55, 138, + ], + [ + 134, 215, 156, 91, 137, 239, 87, 97, 169, 86, 242, 92, 205, 64, 118, 147, + 85, 102, 167, 137, + ], + [ + 95, 188, 244, 180, 13, 177, 169, 176, 217, 237, 21, 8, 140, 107, 67, 85, + 36, 210, 59, 225, + ], + [ + 51, 207, 12, 115, 243, 67, 82, 235, 124, 132, 83, 243, 17, 149, 223, 4, + 198, 12, 240, 155, + ], + [ + 73, 108, 55, 32, 221, 84, 235, 163, 26, 193, 205, 19, 83, 192, 54, 220, 44, + 250, 183, 166, + ], + [ + 159, 175, 70, 249, 104, 251, 98, 55, 162, 58, 64, 141, 186, 152, 126, 164, + 144, 67, 179, 250, + ], + [ + 101, 51, 22, 99, 55, 164, 156, 100, 229, 81, 64, 251, 88, 55, 17, 79, 26, + 221, 201, 114, + ], + [ + 51, 169, 104, 46, 79, 236, 48, 100, 220, 118, 98, 14, 221, 213, 229, 229, + 62, 3, 14, 140, + ], + [ + 75, 35, 100, 235, 127, 141, 109, 91, 138, 60, 2, 100, 77, 145, 218, 2, 216, + 81, 3, 139, + ], + [ + 127, 73, 144, 10, 17, 247, 118, 32, 34, 40, 150, 69, 146, 21, 93, 153, 190, + 101, 217, 145, + ], + [ + 167, 183, 111, 150, 157, 173, 75, 31, 208, 125, 93, 179, 230, 54, 103, 227, + 205, 76, 209, 240, + ], + [ + 255, 27, 23, 85, 215, 235, 94, 243, 206, 211, 161, 6, 18, 136, 74, 153, 34, + 93, 133, 75, + ], + [ + 103, 88, 164, 155, 53, 38, 58, 126, 41, 142, 21, 138, 147, 5, 37, 115, 151, + 109, 169, 174, + ], + [ + 77, 171, 245, 191, 213, 206, 89, 99, 236, 181, 77, 253, 229, 5, 194, 241, + 224, 68, 102, 240, + ], + [ + 4, 67, 151, 105, 242, 250, 235, 215, 168, 88, 166, 65, 0, 8, 74, 135, 65, + 131, 211, 48, + ], + [ + 150, 67, 200, 13, 95, 189, 196, 212, 73, 210, 168, 61, 186, 220, 149, 250, + 28, 105, 170, 221, + ], + [ + 23, 165, 226, 184, 162, 93, 3, 248, 3, 222, 141, 17, 230, 118, 176, 161, + 77, 245, 241, 138, + ], + [ + 117, 157, 69, 21, 213, 188, 213, 128, 142, 145, 35, 227, 88, 102, 4, 200, + 115, 253, 170, 254, + ], + [ + 243, 75, 229, 85, 73, 173, 196, 13, 247, 149, 154, 225, 132, 222, 94, 176, + 233, 155, 205, 128, + ], + [ + 7, 21, 111, 121, 76, 51, 235, 192, 68, 93, 46, 244, 30, 112, 164, 194, 60, + 82, 27, 213, + ], + [ + 148, 78, 79, 54, 42, 83, 22, 125, 196, 186, 197, 178, 92, 232, 224, 25, + 250, 45, 40, 66, + ], + [ + 99, 90, 215, 115, 230, 60, 68, 158, 170, 103, 88, 12, 223, 30, 63, 148, + 166, 106, 201, 68, + ], + [ + 154, 44, 20, 20, 171, 4, 185, 218, 40, 22, 67, 76, 144, 16, 13, 93, 254, + 64, 89, 253, + ], + [ + 220, 42, 129, 75, 87, 174, 179, 204, 165, 235, 34, 28, 8, 196, 200, 160, + 95, 19, 142, 96, + ], + [ + 24, 95, 247, 205, 252, 142, 137, 141, 208, 2, 4, 112, 173, 247, 4, 250, + 241, 240, 54, 32, + ], + [ + 147, 197, 168, 151, 9, 206, 139, 231, 80, 5, 249, 241, 199, 118, 111, 202, + 205, 141, 84, 110, + ], + [ + 91, 111, 85, 114, 216, 7, 254, 242, 8, 200, 54, 74, 202, 39, 18, 23, 19, + 179, 42, 20, + ], + [ + 168, 188, 82, 98, 90, 150, 74, 229, 246, 222, 38, 48, 39, 4, 24, 199, 115, + 25, 205, 255, + ], + [ + 9, 165, 84, 208, 64, 95, 204, 250, 62, 178, 250, 110, 249, 167, 46, 85, 91, + 221, 141, 13, + ], + [ + 75, 253, 175, 162, 228, 101, 25, 228, 238, 18, 86, 173, 247, 1, 206, 141, + 83, 253, 121, 129, + ], + [ + 43, 64, 149, 33, 65, 112, 161, 95, 200, 134, 57, 8, 238, 243, 51, 71, 30, + 240, 135, 55, + ], + [ + 37, 75, 217, 129, 130, 50, 201, 135, 223, 42, 45, 64, 100, 223, 211, 24, + 45, 152, 69, 249, + ], + [ + 155, 152, 46, 62, 79, 10, 99, 164, 136, 150, 149, 168, 237, 219, 127, 116, + 158, 189, 121, 140, + ], + [ + 67, 71, 49, 99, 156, 183, 185, 66, 217, 150, 252, 254, 189, 93, 215, 52, + 22, 236, 198, 2, + ], + [ + 113, 24, 16, 215, 49, 169, 237, 78, 8, 160, 176, 196, 79, 35, 17, 165, 174, + 130, 11, 252, + ], + [ + 248, 137, 74, 144, 211, 231, 132, 111, 117, 58, 73, 210, 196, 107, 183, + 220, 146, 104, 95, 130, + ], + [ + 115, 161, 159, 115, 23, 185, 225, 138, 208, 212, 86, 6, 77, 100, 60, 132, + 209, 236, 243, 238, + ], + [ + 103, 54, 198, 132, 20, 50, 89, 71, 249, 152, 17, 120, 192, 219, 25, 108, + 226, 39, 3, 197, + ], + [ + 66, 202, 130, 252, 56, 43, 13, 103, 197, 71, 183, 113, 177, 17, 20, 123, + 207, 243, 127, 162, + ], + [ + 22, 89, 138, 91, 232, 14, 52, 190, 200, 219, 88, 164, 76, 233, 167, 191, + 213, 219, 10, 124, + ], + [ + 222, 152, 137, 134, 76, 79, 246, 208, 117, 253, 140, 42, 98, 150, 188, 45, + 92, 135, 55, 241, + ], + [ + 26, 16, 125, 53, 92, 244, 29, 252, 235, 83, 145, 151, 174, 234, 57, 65, + 184, 209, 0, 31, + ], + [ + 30, 100, 167, 122, 34, 153, 110, 67, 52, 83, 202, 73, 41, 183, 161, 130, + 59, 49, 137, 92, + ], + [ + 252, 72, 68, 134, 35, 238, 39, 153, 175, 3, 120, 157, 117, 100, 118, 172, + 76, 184, 146, 236, + ], + [ + 114, 249, 126, 6, 79, 135, 60, 144, 238, 58, 239, 169, 146, 201, 54, 40, + 94, 135, 84, 109, + ], + [ + 177, 235, 229, 109, 75, 151, 38, 117, 218, 67, 50, 253, 242, 61, 83, 92, + 208, 100, 219, 188, + ], + [ + 164, 90, 236, 50, 217, 172, 209, 179, 93, 133, 137, 196, 126, 158, 104, + 127, 139, 245, 176, 47, + ], + [ + 116, 98, 154, 222, 179, 113, 245, 46, 92, 102, 122, 60, 245, 174, 244, 181, + 99, 73, 67, 44, + ], + [ + 73, 232, 231, 100, 238, 81, 75, 57, 65, 134, 172, 161, 115, 7, 233, 239, + 143, 249, 212, 125, + ], + [ + 45, 61, 244, 153, 204, 12, 93, 142, 112, 192, 42, 174, 10, 155, 246, 67, + 248, 106, 187, 44, + ], + [ + 238, 22, 206, 158, 168, 4, 39, 32, 49, 131, 28, 241, 125, 184, 146, 38, + 114, 166, 167, 83, + ], + [ + 2, 118, 114, 117, 69, 33, 247, 32, 32, 0, 212, 42, 167, 222, 27, 245, 100, + 31, 101, 126, + ], + [ + 138, 21, 133, 91, 123, 161, 204, 66, 216, 23, 122, 242, 73, 136, 37, 66, + 70, 61, 222, 18, + ], + [ + 220, 16, 218, 184, 137, 249, 143, 106, 105, 139, 81, 155, 202, 181, 209, + 241, 128, 218, 87, 217, + ], + [ + 122, 88, 114, 241, 188, 231, 248, 179, 67, 232, 144, 24, 62, 2, 22, 97, 96, + 152, 54, 147, + ], + [ + 126, 231, 202, 241, 98, 228, 75, 107, 40, 201, 110, 99, 16, 6, 45, 161, + 129, 99, 96, 222, + ], + [ + 162, 25, 7, 68, 22, 169, 132, 180, 2, 87, 254, 167, 36, 207, 196, 96, 37, + 177, 241, 91, + ], + [ + 65, 56, 177, 30, 102, 246, 37, 4, 157, 225, 125, 40, 13, 247, 94, 240, 183, + 250, 117, 217, + ], + [ + 185, 77, 212, 149, 184, 78, 151, 56, 167, 188, 71, 2, 20, 20, 90, 177, 200, + 147, 163, 75, + ], + [ + 91, 137, 131, 189, 117, 75, 134, 241, 230, 1, 120, 222, 144, 104, 183, 115, + 115, 201, 129, 197, + ], + [ + 181, 151, 214, 64, 67, 115, 78, 225, 167, 16, 34, 28, 155, 18, 114, 22, 24, + 204, 171, 135, + ], + [ + 148, 70, 139, 42, 167, 38, 52, 94, 40, 83, 46, 139, 226, 126, 143, 135, + 211, 241, 247, 193, + ], + [ + 89, 82, 162, 229, 108, 191, 136, 232, 45, 0, 185, 85, 243, 154, 220, 75, + 42, 29, 239, 172, + ], + [ + 191, 213, 221, 81, 67, 177, 255, 22, 246, 10, 18, 216, 198, 7, 57, 5, 119, + 190, 12, 8, + ], + [ + 89, 125, 176, 23, 218, 240, 48, 216, 77, 65, 8, 222, 14, 47, 176, 115, 128, + 212, 153, 167, + ], + [ + 164, 225, 147, 195, 148, 150, 35, 131, 70, 235, 253, 178, 238, 84, 119, + 153, 20, 137, 28, 46, + ], + [ + 106, 130, 240, 186, 17, 148, 129, 193, 43, 155, 97, 31, 119, 26, 177, 100, + 201, 94, 38, 197, + ], + [ + 66, 7, 206, 68, 186, 115, 118, 3, 40, 154, 250, 172, 241, 141, 50, 240, 24, + 23, 89, 209, + ], + [ + 122, 185, 80, 76, 45, 193, 236, 141, 49, 199, 190, 116, 22, 132, 9, 209, + 49, 227, 123, 87, + ], + [ + 155, 226, 36, 243, 124, 27, 130, 24, 198, 185, 154, 142, 225, 135, 91, 62, + 101, 38, 240, 221, + ], + [ + 83, 57, 141, 139, 49, 108, 226, 186, 114, 173, 250, 107, 37, 181, 23, 126, + 70, 106, 171, 103, + ], + [ + 44, 120, 122, 118, 158, 220, 138, 240, 105, 255, 128, 219, 23, 69, 98, 163, + 180, 18, 68, 76, + ], + [ + 100, 198, 45, 154, 113, 167, 39, 176, 144, 252, 163, 72, 77, 62, 240, 204, + 197, 53, 24, 49, + ], + [ + 136, 201, 75, 48, 115, 215, 20, 60, 227, 62, 46, 73, 133, 105, 63, 217, 4, + 11, 18, 147, + ], + [ + 226, 113, 63, 18, 151, 177, 27, 16, 32, 58, 163, 122, 164, 134, 56, 11, + 117, 96, 46, 126, + ], + [ + 222, 226, 5, 193, 5, 63, 89, 225, 143, 19, 156, 152, 183, 106, 138, 185, + 78, 198, 209, 102, + ], + [ + 240, 134, 231, 255, 151, 81, 79, 151, 251, 195, 100, 73, 207, 75, 27, 178, + 41, 36, 25, 17, + ], + [ + 11, 218, 243, 233, 65, 235, 154, 213, 113, 5, 194, 126, 157, 156, 252, 211, + 213, 17, 104, 63, + ], + [ + 109, 216, 157, 197, 135, 1, 230, 143, 142, 30, 181, 214, 22, 177, 102, 82, + 174, 18, 143, 190, + ], + [ + 155, 196, 231, 209, 19, 194, 45, 184, 158, 164, 106, 152, 248, 166, 189, + 173, 5, 226, 231, 203, + ], + [ + 200, 39, 206, 127, 207, 11, 32, 152, 20, 226, 191, 98, 252, 14, 162, 162, + 251, 148, 123, 189, + ], + [ + 82, 142, 1, 187, 22, 22, 193, 115, 245, 234, 125, 192, 145, 60, 95, 32, + 243, 149, 57, 86, + ], + [ + 180, 214, 50, 147, 215, 165, 142, 171, 221, 88, 136, 242, 46, 214, 218, 51, + 169, 144, 103, 243, + ], + [ + 231, 39, 3, 255, 24, 43, 211, 248, 94, 140, 34, 166, 95, 237, 146, 81, 111, + 95, 114, 118, + ], + [ + 92, 41, 49, 67, 117, 128, 48, 253, 160, 186, 55, 39, 195, 130, 102, 131, + 150, 235, 71, 39, + ], + [ + 91, 149, 0, 142, 211, 177, 58, 166, 69, 206, 117, 236, 16, 191, 167, 119, + 89, 86, 0, 159, + ], + [ + 46, 97, 38, 73, 228, 148, 32, 96, 209, 2, 61, 14, 6, 215, 198, 140, 91, 64, + 150, 129, + ], + [ + 91, 52, 214, 30, 132, 167, 105, 77, 212, 213, 238, 72, 55, 17, 18, 141, + 218, 109, 9, 115, + ], + [ + 62, 146, 34, 84, 150, 76, 163, 202, 159, 186, 238, 28, 195, 197, 136, 11, + 165, 174, 4, 12, + ], + [ + 83, 110, 191, 63, 238, 68, 250, 251, 154, 200, 238, 168, 102, 211, 207, 47, + 63, 27, 227, 230, + ], + [ + 190, 11, 116, 235, 41, 157, 46, 227, 5, 77, 43, 71, 5, 51, 202, 231, 29, + 91, 177, 68, + ], + [ + 191, 59, 90, 53, 48, 159, 135, 183, 190, 10, 189, 106, 57, 132, 25, 14, 36, + 166, 199, 160, + ], + [ + 115, 53, 3, 127, 116, 231, 166, 52, 128, 196, 91, 171, 44, 197, 210, 85, + 194, 74, 168, 74, + ], + [ + 74, 41, 71, 58, 74, 218, 121, 174, 125, 47, 173, 56, 19, 238, 119, 63, 165, + 29, 19, 22, + ], + [ + 88, 23, 1, 20, 187, 10, 114, 182, 59, 134, 112, 173, 44, 187, 78, 88, 155, + 78, 224, 123, + ], + [ + 180, 51, 233, 252, 2, 40, 230, 32, 72, 251, 189, 63, 90, 245, 90, 183, 84, + 49, 141, 176, + ], + [ + 19, 139, 34, 238, 53, 92, 241, 243, 174, 19, 30, 94, 217, 107, 80, 236, 87, + 135, 225, 81, + ], + [ + 59, 77, 118, 92, 24, 102, 107, 58, 172, 140, 252, 211, 131, 76, 107, 138, + 2, 8, 204, 135, + ], + [ + 65, 82, 97, 183, 41, 49, 33, 160, 137, 61, 214, 125, 78, 158, 111, 47, 63, + 8, 138, 254, + ], + [ + 29, 156, 76, 182, 151, 125, 53, 224, 66, 14, 153, 244, 172, 179, 56, 61, 5, + 216, 253, 14, + ], + [ + 91, 132, 229, 216, 5, 109, 58, 187, 83, 27, 0, 1, 70, 113, 45, 86, 46, 3, + 96, 135, + ], + [ + 116, 23, 74, 189, 213, 163, 133, 164, 123, 210, 171, 244, 109, 226, 160, + 80, 72, 64, 50, 224, + ], + [ + 73, 205, 175, 28, 89, 139, 222, 251, 233, 55, 114, 109, 86, 26, 4, 131, + 187, 47, 170, 158, + ], + [ + 94, 61, 255, 34, 64, 109, 175, 89, 19, 54, 94, 49, 206, 111, 237, 3, 149, + 202, 78, 213, + ], + [ + 200, 231, 95, 254, 246, 37, 223, 255, 84, 174, 6, 78, 47, 160, 47, 138, 31, + 232, 185, 120, + ], + [ + 242, 249, 203, 154, 124, 87, 184, 168, 186, 131, 57, 98, 73, 22, 101, 57, + 131, 88, 103, 44, + ], + [ + 1, 6, 109, 109, 81, 64, 158, 4, 36, 26, 211, 7, 81, 232, 63, 61, 228, 89, + 51, 126, + ], + [ + 139, 168, 24, 193, 189, 189, 145, 92, 88, 101, 52, 157, 76, 8, 71, 196, 97, + 48, 28, 76, + ], + [ + 239, 94, 131, 89, 92, 199, 82, 194, 162, 52, 183, 164, 233, 155, 91, 237, + 248, 77, 112, 97, + ], + [ + 211, 2, 197, 123, 155, 52, 192, 195, 134, 49, 255, 186, 249, 35, 62, 238, + 95, 40, 43, 222, + ], + [ + 160, 198, 198, 31, 150, 75, 157, 250, 13, 229, 36, 181, 5, 71, 119, 115, + 48, 79, 11, 178, + ], + [ + 216, 140, 77, 241, 3, 208, 111, 47, 252, 65, 176, 169, 17, 44, 26, 19, 182, + 254, 41, 40, + ], + [ + 250, 52, 145, 216, 126, 189, 213, 28, 37, 137, 5, 252, 252, 69, 177, 77, + 37, 27, 148, 83, + ], + [ + 235, 138, 86, 28, 224, 235, 125, 51, 197, 160, 160, 153, 71, 90, 150, 232, + 164, 234, 253, 207, + ], + [ + 27, 32, 169, 217, 33, 173, 211, 140, 122, 114, 99, 210, 177, 75, 78, 37, + 201, 120, 12, 44, + ], + [ + 98, 204, 13, 112, 221, 166, 57, 3, 46, 17, 216, 90, 166, 61, 14, 81, 186, + 213, 232, 69, + ], + [ + 48, 219, 99, 139, 60, 121, 181, 166, 141, 112, 228, 186, 38, 226, 207, 113, + 79, 124, 141, 173, + ], + [ + 39, 60, 126, 66, 214, 34, 159, 101, 218, 173, 109, 190, 203, 75, 47, 28, + 118, 12, 74, 243, + ], + [ + 87, 164, 83, 115, 188, 74, 51, 200, 95, 104, 75, 90, 110, 153, 186, 163, + 162, 52, 248, 86, + ], + [ + 223, 16, 79, 209, 203, 94, 3, 119, 106, 189, 53, 75, 65, 132, 110, 15, 114, + 154, 83, 45, + ], + [ + 125, 119, 188, 146, 24, 33, 44, 45, 85, 100, 165, 143, 183, 233, 173, 25, + 241, 23, 183, 23, + ], + [ + 242, 27, 246, 39, 27, 41, 226, 147, 193, 254, 76, 60, 95, 83, 63, 97, 13, + 223, 25, 156, + ], + [ + 121, 81, 238, 128, 63, 176, 213, 63, 203, 233, 57, 248, 24, 154, 220, 250, + 148, 182, 83, 94, + ], + [ + 250, 132, 230, 69, 78, 123, 143, 226, 181, 13, 168, 158, 36, 55, 161, 108, + 95, 141, 193, 2, + ], + [ + 192, 164, 41, 168, 187, 137, 231, 125, 78, 194, 7, 100, 103, 148, 231, 57, + 216, 123, 25, 137, + ], + [ + 153, 74, 232, 98, 229, 220, 175, 95, 99, 204, 79, 116, 81, 163, 138, 85, + 229, 116, 144, 84, + ], + [ + 173, 188, 181, 221, 28, 52, 21, 81, 157, 112, 136, 214, 124, 90, 225, 67, + 79, 35, 190, 34, + ], + [ + 237, 100, 12, 83, 201, 199, 170, 130, 213, 250, 219, 130, 95, 28, 111, 237, + 195, 168, 229, 165, + ], + [ + 6, 88, 176, 96, 205, 16, 218, 119, 14, 216, 86, 66, 122, 207, 226, 0, 54, + 35, 217, 151, + ], + [ + 111, 94, 100, 54, 108, 18, 74, 174, 215, 74, 132, 221, 18, 144, 87, 233, + 86, 247, 217, 119, + ], + [ + 20, 111, 23, 2, 3, 74, 29, 24, 91, 128, 129, 23, 79, 61, 34, 206, 126, 176, + 21, 55, + ], + [ + 67, 121, 209, 150, 140, 149, 121, 72, 236, 197, 73, 82, 3, 211, 76, 212, + 203, 107, 170, 152, + ], + [ + 209, 19, 152, 91, 164, 69, 146, 84, 112, 148, 192, 12, 140, 96, 103, 160, + 95, 54, 88, 171, + ], + [ + 125, 69, 169, 86, 18, 67, 235, 36, 216, 252, 24, 220, 243, 240, 110, 122, + 185, 175, 119, 170, + ], + [ + 8, 135, 21, 205, 105, 228, 247, 210, 227, 7, 52, 15, 68, 90, 80, 214, 197, + 206, 171, 60, + ], + [ + 155, 38, 93, 134, 51, 38, 124, 211, 149, 119, 215, 226, 208, 138, 216, 205, + 45, 255, 119, 196, + ], + [ + 86, 157, 108, 112, 180, 142, 40, 66, 34, 96, 58, 137, 115, 123, 61, 35, + 116, 186, 45, 245, + ], + [ + 186, 154, 182, 98, 174, 254, 182, 254, 49, 21, 103, 189, 190, 245, 170, 59, + 111, 231, 101, 68, + ], + [ + 110, 113, 100, 192, 134, 19, 236, 23, 221, 198, 168, 165, 61, 239, 167, + 130, 28, 14, 186, 13, + ], + [ + 204, 79, 196, 195, 82, 14, 38, 89, 239, 210, 11, 246, 161, 176, 193, 153, + 103, 13, 217, 193, + ], + [ + 228, 21, 162, 217, 82, 77, 210, 28, 24, 183, 101, 178, 108, 188, 2, 116, + 94, 163, 183, 229, + ], + [ + 15, 66, 23, 59, 25, 79, 120, 254, 204, 130, 180, 201, 19, 0, 161, 172, 247, + 27, 103, 63, + ], + [ + 206, 135, 234, 159, 46, 116, 62, 164, 170, 81, 184, 133, 115, 58, 54, 212, + 226, 50, 121, 67, + ], + [ + 241, 159, 133, 168, 87, 115, 96, 179, 226, 242, 225, 119, 10, 156, 114, 64, + 61, 15, 112, 214, + ], + [ + 198, 170, 78, 52, 16, 74, 209, 197, 148, 66, 147, 134, 125, 82, 181, 113, + 80, 204, 224, 54, + ], + [ + 2, 254, 44, 91, 175, 110, 143, 108, 197, 119, 173, 107, 130, 237, 16, 43, + 41, 171, 84, 90, + ], + [ + 101, 150, 4, 209, 191, 71, 78, 91, 28, 62, 239, 167, 41, 68, 140, 235, 92, + 164, 107, 170, + ], + [ + 12, 221, 6, 131, 123, 236, 230, 48, 5, 203, 105, 49, 65, 240, 135, 235, + 171, 170, 187, 232, + ], + [ + 92, 66, 254, 177, 120, 144, 192, 43, 173, 0, 11, 17, 199, 173, 70, 211, + 173, 46, 107, 26, + ], + [ + 174, 191, 81, 9, 70, 54, 81, 151, 190, 55, 226, 127, 156, 130, 240, 185, + 15, 60, 216, 233, + ], + [ + 76, 172, 50, 173, 53, 186, 126, 27, 172, 251, 76, 236, 119, 184, 146, 254, + 147, 223, 222, 170, + ], + [ + 18, 171, 81, 3, 220, 139, 3, 59, 174, 8, 151, 34, 161, 109, 251, 176, 104, + 29, 214, 243, + ], + [ + 107, 203, 116, 138, 178, 12, 201, 47, 126, 115, 181, 186, 141, 243, 194, + 160, 29, 144, 146, 111, + ], + [ + 157, 99, 167, 232, 15, 18, 49, 51, 35, 170, 215, 241, 178, 102, 228, 7, + 211, 6, 55, 138, + ], + [ + 188, 197, 238, 108, 230, 249, 92, 117, 71, 0, 22, 153, 200, 187, 61, 4, + 214, 0, 224, 155, + ], + [ + 169, 228, 9, 25, 115, 67, 221, 17, 221, 231, 166, 161, 29, 206, 145, 16, + 216, 143, 51, 154, + ], + [ + 44, 193, 109, 248, 96, 74, 145, 251, 225, 207, 156, 67, 251, 12, 30, 31, + 70, 108, 176, 212, + ], + [ + 126, 247, 11, 62, 167, 100, 6, 70, 193, 194, 136, 126, 105, 79, 124, 90, + 238, 0, 91, 3, + ], + [ + 76, 17, 218, 145, 44, 108, 184, 203, 0, 87, 92, 194, 141, 112, 62, 19, 14, + 173, 194, 162, + ], + [ + 8, 157, 103, 25, 136, 205, 213, 28, 120, 77, 1, 70, 178, 163, 25, 198, 77, + 181, 168, 121, + ], + [ + 16, 228, 6, 91, 37, 20, 233, 96, 137, 171, 207, 249, 82, 201, 237, 199, + 191, 233, 54, 128, + ], + [ + 199, 129, 24, 118, 188, 170, 203, 141, 74, 121, 220, 107, 79, 224, 119, 16, + 18, 86, 101, 137, + ], + [ + 148, 246, 243, 230, 229, 102, 29, 26, 135, 23, 125, 221, 47, 202, 127, 83, + 69, 140, 118, 61, + ], + [ + 47, 89, 239, 93, 238, 247, 251, 60, 113, 218, 41, 186, 217, 147, 118, 247, + 147, 211, 110, 71, + ], + [ + 248, 170, 177, 117, 150, 128, 130, 123, 205, 84, 86, 171, 204, 206, 197, + 72, 125, 213, 141, 33, + ], + [ + 164, 170, 4, 71, 125, 2, 65, 42, 34, 245, 139, 92, 182, 178, 201, 243, 95, + 135, 55, 172, + ], + [ + 85, 141, 48, 84, 77, 89, 218, 228, 5, 46, 110, 44, 29, 225, 123, 233, 86, + 121, 85, 164, + ], + [ + 43, 113, 63, 42, 169, 210, 138, 183, 16, 214, 38, 65, 17, 236, 100, 189, + 40, 164, 131, 97, + ], + [ + 142, 95, 29, 19, 143, 57, 108, 108, 163, 127, 117, 125, 252, 239, 50, 232, + 150, 16, 113, 83, + ], + [ + 186, 15, 234, 82, 63, 219, 212, 197, 196, 34, 118, 236, 98, 240, 196, 105, + 168, 40, 105, 29, + ], + [ + 18, 224, 72, 56, 142, 122, 233, 41, 142, 37, 4, 1, 220, 65, 184, 138, 197, + 8, 26, 47, + ], + [ + 129, 182, 106, 22, 119, 85, 17, 88, 80, 248, 17, 53, 147, 51, 154, 49, 141, + 245, 152, 123, + ], + [ + 185, 81, 255, 218, 171, 105, 200, 106, 62, 123, 245, 55, 112, 54, 55, 170, + 157, 177, 150, 227, + ], + [ + 147, 106, 143, 233, 122, 181, 142, 233, 164, 68, 169, 189, 54, 190, 122, + 248, 2, 125, 21, 134, + ], + [ + 239, 185, 83, 230, 18, 42, 86, 114, 34, 245, 255, 221, 78, 186, 112, 111, + 67, 22, 190, 171, + ], + [ + 209, 146, 18, 50, 187, 237, 175, 118, 73, 89, 224, 75, 92, 20, 146, 105, + 25, 234, 206, 29, + ], + [ + 30, 33, 57, 133, 105, 49, 203, 101, 40, 243, 45, 138, 11, 177, 101, 41, + 161, 195, 3, 124, + ], + [ + 122, 238, 28, 31, 60, 26, 102, 202, 11, 126, 46, 47, 199, 58, 106, 89, 99, + 21, 94, 176, + ], + [ + 184, 167, 138, 35, 137, 208, 139, 232, 214, 101, 244, 86, 159, 191, 223, + 67, 209, 240, 110, 131, + ], + [ + 82, 153, 26, 30, 64, 198, 66, 48, 126, 249, 81, 145, 95, 248, 125, 74, 21, + 149, 169, 158, + ], + [ + 81, 173, 157, 193, 190, 169, 69, 64, 134, 135, 98, 116, 82, 74, 213, 240, + 128, 135, 30, 162, + ], + [ + 41, 96, 163, 78, 226, 27, 34, 213, 50, 126, 78, 164, 231, 164, 224, 230, + 218, 130, 110, 248, + ], + [ + 66, 182, 121, 191, 45, 51, 122, 52, 173, 17, 169, 253, 223, 21, 54, 100, + 109, 216, 161, 167, + ], + [ + 133, 45, 4, 150, 216, 103, 61, 71, 144, 180, 94, 171, 187, 195, 244, 74, + 251, 43, 9, 31, + ], + [ + 7, 133, 72, 168, 108, 67, 98, 23, 149, 104, 13, 15, 170, 70, 244, 127, 198, + 181, 120, 166, + ], + [ + 210, 112, 157, 149, 196, 183, 9, 94, 40, 18, 75, 176, 188, 208, 52, 196, 8, + 109, 63, 114, + ], + [ + 155, 138, 204, 73, 192, 237, 241, 124, 140, 43, 155, 139, 101, 103, 186, + 40, 55, 191, 194, 115, + ], + [ + 35, 185, 12, 10, 44, 220, 81, 20, 54, 114, 94, 185, 147, 148, 138, 93, 138, + 170, 55, 8, + ], + [ + 67, 78, 142, 103, 15, 5, 145, 145, 115, 22, 137, 16, 24, 160, 23, 194, 235, + 110, 73, 248, + ], + [ + 224, 212, 156, 129, 16, 108, 254, 137, 47, 166, 196, 186, 223, 43, 59, 130, + 113, 108, 193, 161, + ], + [ + 174, 103, 195, 16, 149, 116, 35, 165, 92, 123, 100, 220, 83, 91, 131, 45, + 55, 106, 240, 233, + ], + [ + 4, 92, 105, 86, 142, 84, 213, 206, 5, 251, 4, 7, 4, 107, 240, 238, 202, 57, + 71, 140, + ], + [ + 238, 38, 255, 185, 70, 22, 45, 117, 168, 32, 117, 184, 34, 48, 251, 93, + 224, 240, 19, 144, + ], + [ + 156, 206, 14, 74, 209, 21, 218, 175, 253, 227, 250, 184, 26, 74, 236, 253, + 19, 112, 212, 213, + ], + [ + 168, 217, 141, 111, 154, 56, 73, 166, 57, 51, 96, 132, 20, 178, 162, 0, + 229, 211, 184, 221, + ], + [ + 10, 81, 129, 163, 154, 93, 189, 21, 72, 223, 88, 88, 140, 253, 31, 23, 179, + 33, 68, 41, + ], + [ + 157, 45, 89, 245, 45, 194, 234, 59, 132, 10, 1, 30, 243, 137, 95, 92, 244, + 39, 50, 221, + ], + [ + 52, 125, 15, 149, 177, 184, 204, 128, 45, 41, 204, 133, 92, 62, 107, 233, + 85, 74, 163, 186, + ], + [ + 131, 166, 93, 252, 227, 42, 65, 210, 48, 151, 4, 153, 110, 234, 191, 195, + 236, 157, 23, 100, + ], + [ + 177, 72, 95, 99, 40, 127, 101, 241, 120, 19, 138, 154, 200, 227, 142, 77, + 10, 5, 226, 96, + ], + [ + 95, 123, 244, 16, 121, 232, 63, 28, 253, 155, 47, 252, 135, 54, 170, 245, + 233, 144, 131, 27, + ], + [ + 225, 112, 207, 135, 136, 25, 241, 120, 23, 59, 174, 177, 110, 69, 170, 253, + 205, 30, 113, 7, + ], + [ + 208, 89, 164, 196, 139, 3, 56, 178, 196, 113, 97, 28, 96, 43, 43, 202, 3, + 221, 12, 114, + ], + [ + 144, 13, 62, 31, 131, 252, 165, 181, 223, 110, 84, 55, 142, 205, 146, 76, + 108, 52, 223, 2, + ], + [ + 38, 103, 38, 183, 102, 30, 246, 91, 51, 79, 200, 9, 79, 204, 88, 222, 78, + 87, 225, 254, + ], + [ + 43, 99, 6, 153, 6, 31, 193, 163, 10, 165, 141, 18, 64, 247, 103, 31, 25, + 123, 117, 160, + ], + [ + 15, 120, 243, 118, 239, 35, 246, 79, 146, 186, 106, 38, 230, 246, 169, 60, + 73, 106, 37, 30, + ], + [ + 6, 78, 87, 225, 197, 110, 224, 132, 24, 51, 19, 139, 125, 244, 229, 113, + 127, 172, 2, 137, + ], + [ + 148, 146, 96, 244, 144, 177, 206, 247, 143, 74, 29, 229, 59, 235, 232, 67, + 81, 252, 144, 58, + ], + [ + 29, 5, 222, 220, 41, 136, 219, 91, 134, 237, 73, 183, 97, 86, 128, 183, + 208, 72, 184, 152, + ], + [ + 65, 138, 40, 201, 224, 240, 20, 172, 4, 216, 201, 172, 224, 240, 132, 98, + 47, 146, 165, 24, + ], + [ + 4, 236, 211, 149, 15, 226, 187, 143, 39, 6, 173, 80, 128, 4, 182, 35, 16, + 186, 96, 5, + ], + [ + 19, 225, 171, 215, 110, 89, 70, 218, 144, 87, 196, 87, 47, 14, 124, 175, + 172, 170, 149, 148, + ], + [ + 185, 63, 43, 168, 249, 147, 181, 150, 103, 118, 142, 68, 76, 21, 117, 37, + 41, 51, 179, 227, + ], + ], + ), + } + } + + fn build_claim_tx(ts: wots32::Signature, txid: wots256::Signature) -> bitcoin::Transaction { + let mut witness = Witness::new(); + let res = execute_script(script! { + { txid.to_script() } + { ts.to_script() } + }); + for i in 0..res.final_stack.len() { + witness.push(res.final_stack.get(i)); + } + bitcoin::Transaction { + version: Version(2), + lock_time: LockTime::ZERO, + input: vec![TxIn { + previous_output: OutPoint { + txid: mock_txid(), + vout: 0, + }, + script_sig: ScriptBuf::new(), + sequence: Sequence::MAX, + witness, + }], + output: vec![], + } + } + + async fn build_assert_data_txs( + db: PublicDb, + msk: &str, + operator_id: u32, + deposit_txid: Txid, + signatures: Signatures, + ) -> [bitcoin::Transaction; NUM_ASSERT_DATA_TX] { + let network = bitcoin::Network::Regtest; + + let input = AssertDataTxInput { + pre_assert_txid: mock_txid(), + pre_assert_txouts: std::array::from_fn(|i| TxOut { + value: Amount::from_sat(i as u64), + script_pubkey: ScriptBuf::new(), + }), + }; + + let connector_a2 = ConnectorS::new(mock_x_only_public_key(), network); + + let assert_data = AssertDataTxBatch::new(input, connector_a2); + + let public_keys = db.get_wots_public_keys(operator_id, deposit_txid).await; + + let c160 = ConnectorA160Factory { + network, + public_keys: public_keys.groth16.2, + }; + let c256 = ConnectorA256Factory { + network, + public_keys: std::array::from_fn(|i| match i { + 0 => public_keys.superblock_hash, + 1 => public_keys.groth16.0[0], + _ => public_keys.groth16.1[i - 2], + }), + }; + + assert_data.finalize(c160, c256, msk, signatures) + } + + #[tokio::test] + async fn test_verify_assertions() { + let msk = "secret"; + let operator_id = 0; + let deposit_txid = Txid::from_byte_array(mock::PUBLIC_INPUTS.0); + + println!("initializing the db"); + let db = PublicDb::default(); + + db.set_wots_public_keys( + operator_id, + deposit_txid, + &generate_wots_public_keys(msk, deposit_txid), + ) + .await; + + let keypair = Keypair::new(SECP256K1, &mut rand::thread_rng()); + let agent = Agent::new(keypair, "abc", "abc", "abc"); + let context = TxBuildContext::new( + Network::Regtest, + PublickeyTable::from(BTreeMap::from([(0, keypair.public_key())])), + 0, + ); + + let mut verifier = Verifier::new(db.clone(), context, agent); + + println!("generating assertions"); + // let assertions = { + // let (proof, public_inputs) = mock::get_proof_and_public_inputs(); + + // println!("verifying_key: {:?}", mock::get_verifying_key()); + // println!("proof: {:?}", proof); + // println!("public_inputs: {:?}", public_inputs); + + // let assertions = Assertions { + // bridge_out_txid: mock::PUBLIC_INPUTS.2, + // superblock_hash: mock::PUBLIC_INPUTS.1, + // superblock_period_start_ts: mock::PUBLIC_INPUTS.3.to_le_bytes(), + // groth16: g16::generate_proof_assertions( + // mock::get_verifying_key(), + // proof, + // public_inputs, + // ), + // }; + // println!("assertions: {:?}", assertions); + // assertions + // }; + // return; + let mut assertions = mock_assertions(); + // disprove public inputs hash disprove proof + assertions.superblock_period_start_ts = [1u8; 4]; // assertions.groth16.0[0] = [0u8; 32]; + // assertions.groth16.1[0] = [0u8; 32]; // disprove proof + + let signatures = generate_wots_signatures(msk, deposit_txid, assertions); + + println!("building claim tx"); + let claim_tx = build_claim_tx( + signatures.superblock_period_start_ts, + signatures.bridge_out_txid, + ); + let (superblock_period_start_ts, bridge_out_txid) = verifier.parse_claim_tx(&claim_tx); + + println!("building assert data txs"); + let assert_data_txs = + build_assert_data_txs(db.clone(), msk, operator_id, deposit_txid, signatures).await; + + println!("parse assert data txs"); + let (superblock_hash, groth16) = verifier.parse_assert_data_txs(&assert_data_txs); + assert_eq!( + signatures, + Signatures { + superblock_hash, + superblock_period_start_ts, + bridge_out_txid, + groth16, + } + ); + + let duty = VerifierDuty::VerifyAssertions { + operator_id, + deposit_txid, + + post_assert_tx: claim_tx.clone(), /* FIXME: this should be post-assert tx (this is + * okay for + * test for now) */ + claim_tx, + assert_data_txs, + }; + + println!("verifier.process_duty"); + verifier.process_duty(duty).await; + } + + #[tokio::test] + async fn test_verify_assertions_exhaustive() { + let msk = "secret"; + let operator_id = 0; + let deposit_txid = Txid::from_byte_array(mock::PUBLIC_INPUTS.0); + + println!("initializing the db"); + let db = PublicDb::default(); + + db.set_wots_public_keys( + operator_id, + deposit_txid, + &generate_wots_public_keys(msk, deposit_txid), + ) + .await; + + let keypair = Keypair::new(SECP256K1, &mut rand::thread_rng()); + let agent = Agent::new(keypair, "", "", ""); + let context = + TxBuildContext::new(Network::Regtest, PublickeyTable::from(BTreeMap::new()), 0); + + let mut verifier = Verifier::new(db.clone(), context, agent); + + fn invalidate_groth16_assertions(assertions: &mut Assertions, i: usize, j: u8) { + match i { + 0 => assertions.groth16.0[i] = [j; 32], + 1..=g16::N_VERIFIER_FQS => assertions.groth16.1[i - 1] = [j; 32], + _ => assertions.groth16.2[i - g16::N_VERIFIER_FQS - 1] = [j; 20], + }; + } + + for i in 0..615 * 2 { + println!("ITERATION: {i}"); + // disprove public inputs hash disprove proof + let mut assertions = mock_assertions(); + let j = (i % 2) as u8; + match i { + 0 => assertions.superblock_hash = [j; 32], + 1 => assertions.bridge_out_txid = [j; 32], + 2 => assertions.superblock_period_start_ts = [j; 4], + 3.. => invalidate_groth16_assertions(&mut assertions, i - 3, j), + }; + + let signatures = generate_wots_signatures(msk, deposit_txid, assertions); + + println!("building claim tx"); + let claim_tx = build_claim_tx( + signatures.superblock_period_start_ts, + signatures.bridge_out_txid, + ); + let (superblock_period_start_ts, bridge_out_txid) = verifier.parse_claim_tx(&claim_tx); + + println!("building assert data txs"); + let assert_data_txs = + build_assert_data_txs(db.clone(), msk, operator_id, deposit_txid, signatures).await; + + let (superblock_hash, groth16) = verifier.parse_assert_data_txs(&assert_data_txs); + assert_eq!( + signatures, + Signatures { + superblock_hash, + superblock_period_start_ts, + bridge_out_txid, + groth16, + } + ); + + let duty = VerifierDuty::VerifyAssertions { + operator_id, + deposit_txid, + post_assert_tx: claim_tx.clone(), + claim_tx, + assert_data_txs, + }; + + verifier.process_duty(duty).await; + } + } +} diff --git a/crates/btcio/src/bitcoind.rs b/crates/btcio/src/bitcoind.rs index f9cca4d..efdadfa 100644 --- a/crates/btcio/src/bitcoind.rs +++ b/crates/btcio/src/bitcoind.rs @@ -7,8 +7,10 @@ use std::{ use async_trait::async_trait; use base64::{engine::general_purpose, Engine}; use bitcoin::{ - bip32::Xpriv, block::Header, consensus::encode::serialize_hex, Address, Block, BlockHash, - Network, Transaction, Txid, + bip32::Xpriv, + block::Header, + consensus::encode::{self, serialize_hex}, + Address, Block, BlockHash, Network, Transaction, Txid, }; use reqwest::{ header::{HeaderMap, AUTHORIZATION, CONTENT_TYPE}, @@ -66,7 +68,7 @@ struct Response { impl BitcoinClient { /// Creates a new [`BitcoinClient`] with the given URL, username, and password. - pub fn new(url: String, username: String, password: String) -> ClientResult { + pub fn new(url: &str, username: &str, password: &str) -> ClientResult { if username.is_empty() || password.is_empty() { return Err(ClientError::MissingUserPassword); } @@ -93,11 +95,15 @@ impl BitcoinClient { trace!(url = %url, "Created bitcoin client"); - Ok(Self { url, client, id }) + Ok(Self { + url: url.to_string(), + client, + id, + }) } fn next_id(&self) -> usize { - self.id.fetch_add(1, Ordering::AcqRel) + self.id.fetch_add(1, Ordering::SeqCst) } async fn call( @@ -107,9 +113,9 @@ impl BitcoinClient { ) -> ClientResult { let mut retries = 0; loop { - trace!(%method, ?params, %retries, "Calling bitcoin client"); - + debug!(%method, %retries, "Fetching new id"); let id = self.next_id(); + debug!(%method, ?params, %retries, %id, "Calling bitcoin client"); let response = self .client @@ -316,6 +322,17 @@ impl Reader for BitcoinClient { self.call::>("getrawmempool", &[]).await } + async fn get_raw_transaction( + &self, + txid: &Txid, + block_hash: Option<&BlockHash>, + ) -> ClientResult { + let args = [to_value(txid)?, to_value(false)?, to_value(block_hash)?]; + let hex: String = self.call("getrawtransaction", &args).await?; + + Ok(encode::deserialize_hex(&hex).map_err(|e| ClientError::Parse(e.to_string()))?) + } + async fn network(&self) -> ClientResult { Ok(self .call::("getblockchaininfo", &[]) @@ -332,7 +349,11 @@ impl Broadcaster for BitcoinClient { let txstr = serialize_hex(tx); trace!(txstr = %txstr, "Sending raw transaction"); match self - .call::("sendrawtransaction", &[to_value(txstr)?]) + .call::( + "sendrawtransaction", + // raw tx hex, maxfeerate, maxburnamount + &[to_value(txstr)?, to_value("0.10")?, to_value("0.1")?], + ) .await { Ok(txid) => { @@ -341,7 +362,7 @@ impl Broadcaster for BitcoinClient { } Err(ClientError::Server(i, s)) => match i { // Dealing with known and common errors - -27 => Ok(tx.compute_txid()), // Tx already in chain + -27 | -25 => Ok(tx.compute_txid()), // Tx already in chain _ => Err(ClientError::Server(i, s)), }, Err(e) => Err(ClientError::Other(e.to_string())), @@ -520,9 +541,9 @@ mod test { logging::init(); let bitcoind = BitcoinD::default(); - let url = bitcoind.url.to_string(); - let user = bitcoind.user.to_string(); - let password = bitcoind.password.to_string(); + let url = bitcoind.url; + let user = bitcoind.user; + let password = bitcoind.password; // setting the ENV variable `BITCOIN_XPRIV_RETRIEVABLE` to retrieve the xpriv set_var("BITCOIN_XPRIV_RETRIEVABLE", "true"); diff --git a/crates/btcio/src/esplora.rs b/crates/btcio/src/esplora.rs index 6f6cfde..f4c7385 100644 --- a/crates/btcio/src/esplora.rs +++ b/crates/btcio/src/esplora.rs @@ -184,6 +184,14 @@ impl Reader for EsploraClient { unimplemented!() } + async fn get_raw_transaction( + &self, + _txid: &Txid, + _block_hash: Option<&BlockHash>, + ) -> ClientResult { + unimplemented!() + } + async fn network(&self) -> ClientResult { Ok(self.network) } diff --git a/crates/btcio/src/traits.rs b/crates/btcio/src/traits.rs index dd629a2..c755870 100644 --- a/crates/btcio/src/traits.rs +++ b/crates/btcio/src/traits.rs @@ -81,6 +81,13 @@ pub trait Reader { /// Gets all transaction ids in mempool. async fn get_raw_mempool(&self) -> ClientResult>; + /// Get a specific transaction with a block hash (or if part of the wallet) + async fn get_raw_transaction( + &self, + txid: &Txid, + block_hash: Option<&BlockHash>, + ) -> ClientResult; + /// Gets the underlying [`Network`] information. async fn network(&self) -> ClientResult; } diff --git a/crates/db/Cargo.toml b/crates/db/Cargo.toml new file mode 100644 index 0000000..89b8628 --- /dev/null +++ b/crates/db/Cargo.toml @@ -0,0 +1,34 @@ +[package] +edition = "2021" +name = "strata-bridge-db" +version = "0.1.0" + +[lints] +rust.missing_debug_implementations = "warn" +rust.rust_2018_idioms = { level = "deny", priority = -1 } +# rust.unreachable_pub = "warn" +# rust.unused_crate_dependencies = "deny" +rust.unused_must_use = "deny" + +[dependencies] +strata-bridge-btcio.workspace = true +strata-bridge-primitives.workspace = true + +anyhow.workspace = true +async-trait.workspace = true +bincode.workspace = true +bitcoin = { workspace = true, features = ["rand-std"] } +bitcoin-script = { workspace = true } +bitcoin-scriptexec = { workspace = true } +bitvm = { workspace = true } +esplora-client.workspace = true +hex.workspace = true +lazy_static.workspace = true +musig2 = { workspace = true, features = ["serde"] } +rand.workspace = true +secp256k1 = { workspace = true, features = ["global-context", "rand-std"] } +serde.workspace = true +serde_json.workspace = true +sha2.workspace = true +tokio.workspace = true +tracing.workspace = true diff --git a/crates/db/src/connector_db.rs b/crates/db/src/connector_db.rs new file mode 100644 index 0000000..6697d28 --- /dev/null +++ b/crates/db/src/connector_db.rs @@ -0,0 +1,85 @@ +use std::fmt::Debug; + +use async_trait::async_trait; +use bitcoin::Txid; +use bitvm::{groth16::g16, treepp::*}; +use secp256k1::schnorr::Signature; +use strata_bridge_primitives::{scripts::wots, types::OperatorIdx}; + +#[async_trait] +pub trait ConnectorDb: Clone + Debug + Send + Sync { + async fn get_partial_disprove_scripts(&self) -> [Script; g16::N_TAPLEAVES]; + + async fn get_wots_public_keys(&self, operator_id: u32, deposit_txid: Txid) -> wots::PublicKeys; + + async fn set_wots_public_keys( + &self, + operator_id: u32, + deposit_txid: Txid, + public_keys: &wots::PublicKeys, + ); + + async fn get_wots_signatures(&self, operator_id: u32, deposit_txid: Txid) -> wots::Signatures; + + async fn set_wots_signatures( + &self, + operator_id: u32, + deposit_txid: Txid, + signatures: &wots::Signatures, + ); + + async fn get_signature( + &self, + operator_idx: OperatorIdx, + txid: Txid, + input_index: u32, + ) -> Signature; + + async fn register_claim_txid( + &self, + claim_txid: Txid, + operator_idx: OperatorIdx, + deposit_txid: Txid, + ); + + async fn get_operator_and_deposit_for_claim( + &self, + claim_txid: &Txid, + ) -> Option<(OperatorIdx, Txid)>; + + async fn register_post_assert_txid( + &self, + post_assert_txid: Txid, + operator_idx: OperatorIdx, + deposit_txid: Txid, + ); + + async fn get_operator_and_deposit_for_post_assert( + &self, + post_assert_txid: &Txid, + ) -> Option<(OperatorIdx, Txid)>; + + async fn register_assert_data_txids( + &self, + assert_data_txids: [Txid; 7], + operator_idx: OperatorIdx, + deposit_txid: Txid, + ); + + async fn get_operator_and_deposit_for_assert_data( + &self, + assert_data_txid: &Txid, + ) -> Option<(OperatorIdx, Txid)>; + + async fn register_pre_assert_txid( + &self, + pre_assert_data_txid: Txid, + operator_idx: OperatorIdx, + deposit_txid: Txid, + ); + + async fn get_operator_and_deposit_for_pre_assert( + &self, + pre_assert_data_txid: &Txid, + ) -> Option<(OperatorIdx, Txid)>; +} diff --git a/crates/db/src/constants.rs b/crates/db/src/constants.rs new file mode 100644 index 0000000..2552505 --- /dev/null +++ b/crates/db/src/constants.rs @@ -0,0 +1 @@ +pub const VK_SCRIPTS_FILE: &str = "strata-bridge-poc-vk.scripts"; diff --git a/crates/db/src/lib.rs b/crates/db/src/lib.rs new file mode 100644 index 0000000..53a20a6 --- /dev/null +++ b/crates/db/src/lib.rs @@ -0,0 +1,5 @@ +pub mod connector_db; +pub mod constants; +pub mod operator; +pub mod persistence; +pub mod public; diff --git a/crates/db/src/operator.rs b/crates/db/src/operator.rs new file mode 100644 index 0000000..d5da7ac --- /dev/null +++ b/crates/db/src/operator.rs @@ -0,0 +1,149 @@ +use std::collections::{BTreeMap, HashMap, HashSet}; + +use bitcoin::{Address, Amount, OutPoint, TxOut, Txid}; +use musig2::{PartialSignature, PubNonce, SecNonce}; +use tokio::sync::RwLock; + +pub(super) type OperatorIdx = u32; + +#[derive(Debug, Clone)] +pub struct KickoffInfo { + pub funding_inputs: Vec, + pub funding_utxos: Vec, + pub change_address: Address, + pub change_amt: Amount, +} + +pub type MsgHashAndOpIdToSigMap = (Vec, BTreeMap); + +#[derive(Debug, Default)] +pub struct OperatorDb { + /// Txid -> OperatorIdx -> PubNonce + collected_pubnonces: RwLock>>, + + /// Txid -> PubNonce + sec_nonces: RwLock>, + + /// (Txid, input_index) -> (Message Hash, OperatorIdx -> PartialSignature) + collected_signatures: RwLock>, + + /// OutPoints that have already been used to create KickoffTx. + selected_outpoints: RwLock>, + + /// Deposit Txid -> PegOutGraphData + peg_out_graphs: RwLock>, +} + +impl OperatorDb { + pub async fn add_pubnonce( + &self, + txid: Txid, + input_index: u32, + operator_idx: OperatorIdx, + pubnonce: PubNonce, + ) { + let mut collected_pubnonces = self.collected_pubnonces.write().await; + + if let Some(pubnonce_table) = collected_pubnonces.get_mut(&(txid, input_index)) { + pubnonce_table.insert(operator_idx, pubnonce); + } else { + let mut new_entry = BTreeMap::new(); + new_entry.insert(operator_idx, pubnonce); + + collected_pubnonces.insert((txid, input_index), new_entry); + } + } + pub async fn collected_pubnonces( + &self, + txid: Txid, + input_index: u32, + ) -> Option> { + self.collected_pubnonces + .read() + .await + .get(&(txid, input_index)) + .cloned() + } + + pub async fn add_secnonce(&self, txid: Txid, input_index: u32, secnonce: SecNonce) { + let mut sec_nonces = self.sec_nonces.write().await; + + sec_nonces.insert((txid, input_index), secnonce); + } + + pub async fn secnonce(&self, txid: Txid, input_index: u32) -> Option { + self.sec_nonces + .read() + .await + .get(&(txid, input_index)) + .cloned() + } + + pub async fn add_message_hash_and_signature( + &self, + txid: Txid, + input_index: u32, + message_sighash: Vec, + operator_idx: OperatorIdx, + signature: PartialSignature, + ) { + let mut collected_sigs = self.collected_signatures.write().await; + + if let Some(sig_entry) = collected_sigs.get_mut(&(txid, input_index)) { + sig_entry.0 = message_sighash; + sig_entry.1.insert(operator_idx, signature); + } else { + let mut new_entry = (message_sighash, BTreeMap::new()); + new_entry.1.insert(operator_idx, signature); + + collected_sigs.insert((txid, input_index), new_entry); + } + } + + /// Adds a partial signature to the map if already present. + pub async fn add_partial_signature( + &self, + txid: Txid, + input_index: u32, + operator_idx: OperatorIdx, + signature: PartialSignature, + ) { + let mut collected_sigs = self.collected_signatures.write().await; + + if let Some(sig_entry) = collected_sigs.get_mut(&(txid, input_index)) { + sig_entry.1.insert(operator_idx, signature); + } + } + + pub async fn collected_signatures_per_msg( + &self, + txid: Txid, + input_index: u32, + ) -> Option { + self.collected_signatures + .read() + .await + .get(&(txid, input_index)) + .cloned() + } + + pub async fn add_outpoint(&self, outpoint: OutPoint) -> bool { + let mut selected_outpoints = self.selected_outpoints.write().await; + + selected_outpoints.insert(outpoint) + } + + pub async fn selected_outpoints(&self) -> HashSet { + self.selected_outpoints.read().await.clone() + } + + pub async fn add_kickoff_info(&self, deposit_txid: Txid, kickoff_info: KickoffInfo) { + let mut peg_out_graph = self.peg_out_graphs.write().await; + + peg_out_graph.insert(deposit_txid, kickoff_info); + } + + pub async fn get_kickoff_info(&self, deposit_txid: Txid) -> Option { + self.peg_out_graphs.read().await.get(&deposit_txid).cloned() + } +} diff --git a/crates/db/src/persistence.rs b/crates/db/src/persistence.rs new file mode 100644 index 0000000..c3cdf99 --- /dev/null +++ b/crates/db/src/persistence.rs @@ -0,0 +1,36 @@ +// use std::collections::{BTreeMap, HashMap}; +// +// use crate::{operator::OperatorIdx, public::{OperatorIdxToTxInputSigMap, PublicDb}}; +// use bitcoin::Txid; +// use bitvm::groth16::g16; +// use secp256k1::PublicKey; +// use serde::{Serialize, Deserialize} +// use serde_json; +// use strata_bridge_primitives::scripts::wots; + +// #[derive(Debug, Clone, Serialize, Deserialize)] +// pub struct PublicData { +// musig_pubkey_table: BTreeMap, +// +// // operator_id -> deposit_txid -> WotsPublicKeys +// wots_public_keys: HashMap>, +// +// // operator_id -> deposit_txid -> WotsSignatures +// wots_signatures: HashMap>, +// +// // signature cache per txid and input index per operator +// signatures: OperatorIdxToTxInputSigMap, +// +// // reverse mapping +// claim_txid_to_operator_index_and_deposit_txid: HashMap, +// post_assert_txid_to_operator_index_and_deposit_txid: +// HashMap, +// } + +// pub fn dump_public_db(public_db: &PublicDb) { +// let data_to_dump = +// } +// +// pub fn load_public_db(public_db: &PublicDb) { +// +// } diff --git a/crates/db/src/public.rs b/crates/db/src/public.rs new file mode 100644 index 0000000..ed377f5 --- /dev/null +++ b/crates/db/src/public.rs @@ -0,0 +1,341 @@ +use std::{ + collections::{BTreeMap, HashMap}, + fs, + sync::Arc, +}; + +use async_trait::async_trait; +use bitcoin::{ScriptBuf, Txid}; +use bitvm::{ + groth16::g16::{self, N_TAPLEAVES}, + treepp::{script, Script}, +}; +use secp256k1::{schnorr::Signature, PublicKey}; +use strata_bridge_primitives::scripts::wots::{self, bridge_poc_verification_key}; +use tokio::sync::RwLock; +use tracing::{info, trace}; + +use super::operator::OperatorIdx; +use crate::{connector_db::ConnectorDb, constants::VK_SCRIPTS_FILE}; + +pub type TxInputToSignatureMap = HashMap<(Txid, u32), Signature>; +pub type OperatorIdxToTxInputSigMap = HashMap; + +// Assume that no node will update other nodes' data in this public db. +#[derive(Debug, Clone)] +pub struct PublicDb { + musig_pubkey_table: Arc>>, + + verifier_scripts: Arc>, + + // operator_id -> deposit_txid -> WotsPublicKeys + wots_public_keys: Arc>>>, + + // operator_id -> deposit_txid -> WotsSignatures + wots_signatures: Arc>>>, + + // signature cache per txid and input index per operator + signatures: Arc>, + + // reverse mapping + claim_txid_to_operator_index_and_deposit_txid: Arc>>, + + pre_assert_txid_to_operator_index_and_deposit_txid: + Arc>>, + + assert_data_txid_to_operator_index_and_deposit_txid: + Arc>>, + + post_assert_txid_to_operator_index_and_deposit_txid: + Arc>>, +} + +impl Default for PublicDb { + fn default() -> Self { + let verifier_scripts: [Script; N_TAPLEAVES] = if fs::exists(VK_SCRIPTS_FILE) + .expect("should be able to check for existence of verifier scripts file") + { + info!( + action = "loading verifier script from file cache...this will take some time", + estimated_time = "3 mins" + ); + + let contents: Vec = fs::read(VK_SCRIPTS_FILE) + .expect("should be able to read verifier scripts from file"); + let deserialized: Vec> = bincode::deserialize(&contents) + .expect("should be able to deserialize verifier scripts from file"); + + let verifier_scripts = deserialized + .iter() + .map(|de| script!().push_script(ScriptBuf::from_bytes(de.to_vec()))) + .collect::>(); + + let num_scripts = verifier_scripts.len(); + info!(event = "loaded verifier scripts", %num_scripts); + + verifier_scripts.try_into().unwrap_or_else(|_| { + panic!("number of scripts should be: {N_TAPLEAVES} not {num_scripts}",) + }) + } else { + info!( + action = "compiling verifier scripts, this will take time...", + estimated_time = "3 mins" + ); + + let verifier_scripts = g16::compile_verifier(bridge_poc_verification_key()); + + let serialized: Vec> = verifier_scripts + .clone() + .into_iter() + .map(|s| s.compile().to_bytes()) + .collect(); + + let serialized: Vec = bincode::serialize(&serialized) + .expect("should be able to serialize verifier scripts"); + + info!(action = "caching verifier scripts for later", cache_file=%VK_SCRIPTS_FILE); + fs::write(VK_SCRIPTS_FILE, serialized) + .expect("should be able to write verifier scripts to file"); + + verifier_scripts + }; + + Self { + verifier_scripts: Arc::new(RwLock::new(verifier_scripts)), + musig_pubkey_table: Default::default(), + wots_public_keys: Default::default(), + wots_signatures: Default::default(), + signatures: Default::default(), + claim_txid_to_operator_index_and_deposit_txid: Default::default(), + assert_data_txid_to_operator_index_and_deposit_txid: Default::default(), + pre_assert_txid_to_operator_index_and_deposit_txid: Default::default(), + post_assert_txid_to_operator_index_and_deposit_txid: Default::default(), + } + } +} + +impl PublicDb { + pub async fn add_verifier_scripts(&self, verifier_scripts: &[Script; g16::N_TAPLEAVES]) { + self.verifier_scripts + .write() + .await + .clone_from(verifier_scripts); + } + + pub async fn put_signature( + &self, + operator_idx: OperatorIdx, + txid: Txid, + input_index: u32, + signature: Signature, + ) { + trace!(action = "trying to acquire wlock on schnorr signatures", %operator_idx, %txid); + let mut signatures = self.signatures.write().await; + trace!(event = "acquired wlock on schnorr signatures", %operator_idx, %txid); + + if let Some(txid_and_input_index_to_signature) = signatures.get_mut(&operator_idx) { + txid_and_input_index_to_signature.insert((txid, input_index), signature); + } else { + let mut txid_and_input_index_to_signature = HashMap::new(); + txid_and_input_index_to_signature.insert((txid, input_index), signature); + + signatures.insert(operator_idx, txid_and_input_index_to_signature); + } + } + + pub async fn set_musig_pubkey_table(&self, pubkey_table: &BTreeMap) { + self.musig_pubkey_table + .write() + .await + .clone_from(pubkey_table); + } + + pub async fn get_musig_pubkey_table(&self) -> BTreeMap { + self.musig_pubkey_table.read().await.clone() + } +} + +#[async_trait] +impl ConnectorDb for PublicDb { + async fn get_partial_disprove_scripts(&self) -> [Script; g16::N_TAPLEAVES] { + self.verifier_scripts.read().await.clone() + } + + async fn get_wots_public_keys(&self, operator_id: u32, deposit_txid: Txid) -> wots::PublicKeys { + *self + .wots_public_keys + .read() + .await + .get(&operator_id) + .unwrap() + .get(&deposit_txid) + .unwrap() + } + + async fn get_wots_signatures(&self, operator_id: u32, deposit_txid: Txid) -> wots::Signatures { + *self + .wots_signatures + .read() + .await + .get(&operator_id) + .unwrap() + .get(&deposit_txid) + .unwrap() + } + + async fn set_wots_public_keys( + &self, + operator_id: u32, + deposit_txid: Txid, + public_keys: &wots::PublicKeys, + ) { + trace!(action = "trying to acquire wlock on wots public keys", %operator_id, %deposit_txid); + let mut map = self.wots_public_keys.write().await; + trace!(event = "wlock acquired on wots public keys", %operator_id, %deposit_txid); + + if let Some(op_keys) = map.get_mut(&operator_id) { + op_keys.insert(deposit_txid, *public_keys); + } else { + let mut keys = HashMap::new(); + keys.insert(deposit_txid, *public_keys); + + map.insert(operator_id, keys); + } + } + + async fn set_wots_signatures( + &self, + operator_id: u32, + deposit_txid: Txid, + signatures: &wots::Signatures, + ) { + trace!(action = "trying to acquire wlock on wots signatures", %operator_id, %deposit_txid); + let mut map = self.wots_signatures.write().await; + trace!(event = "wlock acquired on wots signatures", %operator_id, %deposit_txid); + + if let Some(op_keys) = map.get_mut(&operator_id) { + op_keys.insert(deposit_txid, *signatures); + } else { + let mut sigs_map = HashMap::new(); + sigs_map.insert(deposit_txid, *signatures); + + map.insert(operator_id, sigs_map); + } + } + + async fn get_signature( + &self, + operator_idx: OperatorIdx, + txid: Txid, + input_index: u32, + ) -> Signature { + self.signatures + .read() + .await + .get(&operator_idx) + .unwrap_or_else(|| { + panic!("operator_idx: {operator_idx} must have a signature in the database") + }) + .get(&(txid, input_index)) + .copied() + .unwrap_or_else(|| { + panic!("txid: {txid} must have a signature in the database"); + }) + } + + async fn register_claim_txid( + &self, + claim_txid: Txid, + operator_idx: OperatorIdx, + deposit_txid: Txid, + ) { + self.claim_txid_to_operator_index_and_deposit_txid + .write() + .await + .insert(claim_txid, (operator_idx, deposit_txid)); + } + + async fn get_operator_and_deposit_for_claim( + &self, + claim_txid: &Txid, + ) -> Option<(OperatorIdx, Txid)> { + self.claim_txid_to_operator_index_and_deposit_txid + .read() + .await + .get(claim_txid) + .copied() + } + + async fn register_post_assert_txid( + &self, + post_assert_txid: Txid, + operator_idx: OperatorIdx, + deposit_txid: Txid, + ) { + self.post_assert_txid_to_operator_index_and_deposit_txid + .write() + .await + .insert(post_assert_txid, (operator_idx, deposit_txid)); + } + + async fn get_operator_and_deposit_for_post_assert( + &self, + post_assert_txid: &Txid, + ) -> Option<(OperatorIdx, Txid)> { + self.post_assert_txid_to_operator_index_and_deposit_txid + .read() + .await + .get(post_assert_txid) + .copied() + } + + async fn register_assert_data_txids( + &self, + assert_data_txids: [Txid; 7], + operator_idx: OperatorIdx, + deposit_txid: Txid, + ) { + let mut db = self + .assert_data_txid_to_operator_index_and_deposit_txid + .write() + .await; + + for txid in assert_data_txids { + db.insert(txid, (operator_idx, deposit_txid)); + } + } + + async fn get_operator_and_deposit_for_assert_data( + &self, + assert_data_txid: &Txid, + ) -> Option<(OperatorIdx, Txid)> { + self.assert_data_txid_to_operator_index_and_deposit_txid + .read() + .await + .get(assert_data_txid) + .copied() + } + + async fn register_pre_assert_txid( + &self, + pre_assert_data_txid: Txid, + operator_idx: OperatorIdx, + deposit_txid: Txid, + ) { + self.pre_assert_txid_to_operator_index_and_deposit_txid + .write() + .await + .insert(pre_assert_data_txid, (operator_idx, deposit_txid)); + } + + async fn get_operator_and_deposit_for_pre_assert( + &self, + pre_assert_data_txid: &Txid, + ) -> Option<(OperatorIdx, Txid)> { + self.pre_assert_txid_to_operator_index_and_deposit_txid + .read() + .await + .get(pre_assert_data_txid) + .copied() + } +} diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml new file mode 100644 index 0000000..03e523b --- /dev/null +++ b/crates/primitives/Cargo.toml @@ -0,0 +1,41 @@ +[package] +edition = "2021" +name = "strata-bridge-primitives" +version = "0.1.0" + +[lints] +rust.missing_debug_implementations = "warn" +rust.rust_2018_idioms = { level = "deny", priority = -1 } +# rust.unreachable_pub = "warn" +# rust.unused_crate_dependencies = "deny" +rust.unused_must_use = "deny" + +[dependencies] +anyhow.workspace = true +ark-bn254 = { workspace = true } +ark-crypto-primitives = { workspace = true } +ark-ec = { workspace = true } +ark-ff = { workspace = true } +ark-groth16 = { workspace = true } +ark-r1cs-std = { workspace = true } +ark-relations = { workspace = true } +ark-std = { workspace = true } +bincode.workspace = true +bitcoin = { workspace = true, features = ["rand-std"] } +bitcoin-script = { workspace = true } +bitcoin-scriptexec = { workspace = true } +bitvm = { workspace = true } +esplora-client = { workspace = true, default-features = false, features = [ + "async-https-rustls", +] } +hex.workspace = true +lazy_static.workspace = true +musig2 = { workspace = true, features = ["serde"] } +rand.workspace = true +secp256k1 = { workspace = true, features = ["global-context", "rand-std"] } +serde.workspace = true +serde_json.workspace = true +sha2.workspace = true + +[dev-dependencies] +strata-bridge-tx-graph.workspace = true diff --git a/crates/primitives/src/bitcoin.rs b/crates/primitives/src/bitcoin.rs new file mode 100644 index 0000000..552e11c --- /dev/null +++ b/crates/primitives/src/bitcoin.rs @@ -0,0 +1,64 @@ +use bitcoin::{address::NetworkUnchecked, Address, Network}; +use serde::{de, Deserialize, Deserializer, Serialize}; + +/// A wrapper around the [`bitcoin::Address`] type created in order to implement +/// some useful traits on it such as [`serde::Deserialize`], [`borsh::BorshSerialize`] and +/// [`borsh::BorshDeserialize`]. +// TODO: implement [`arbitrary::Arbitrary`]? +#[derive(Debug, Clone, Serialize, PartialEq, Eq)] +pub struct BitcoinAddress { + /// The [`bitcoin::Network`] that this address is valid in. + network: Network, + + /// The actual [`Address`] that this type wraps. + address: Address, +} + +impl BitcoinAddress { + pub fn parse(address_str: &str, network: Network) -> anyhow::Result { + let address = address_str.parse::>()?; + + let checked_address = address.require_network(network)?; + + Ok(Self { + network, + address: checked_address, + }) + } +} + +impl BitcoinAddress { + pub fn address(&self) -> &Address { + &self.address + } + + pub fn network(&self) -> &Network { + &self.network + } +} + +impl<'de> Deserialize<'de> for BitcoinAddress { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + #[derive(Deserialize)] + struct BitcoinAddressShim { + network: Network, + address: String, + } + + let shim = BitcoinAddressShim::deserialize(deserializer)?; + let address = shim + .address + .parse::>() + .map_err(|_| de::Error::custom("invalid bitcoin address"))? + .require_network(shim.network) + .map_err(|_| de::Error::custom("address invalid for given network"))?; + + Ok(BitcoinAddress { + network: shim.network, + address, + }) + } +} diff --git a/crates/primitives/src/build_context.rs b/crates/primitives/src/build_context.rs new file mode 100644 index 0000000..615f9c1 --- /dev/null +++ b/crates/primitives/src/build_context.rs @@ -0,0 +1,99 @@ +use bitcoin::Network; +use secp256k1::XOnlyPublicKey; + +use crate::{ + scripts::general::get_aggregated_pubkey, + types::{OperatorIdx, PublickeyTable, TxSigningData}, +}; + +/// Provides methods that allows access to components required to build a transaction in the context +/// of a bitcoin MuSig2 address. +/// +/// Please refer to MuSig2 in +/// [BIP 327](/~https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki). +pub trait BuildContext { + /// Get the complete public key table. + /// + /// The whole table is required here as it enables the withdrawals to be processed + /// simultaneously by all clients even if they are not assigned. Each such client references the + /// table to get the pubkey of the assigned operator and generates the transaction that fulfills + /// the withdrawal request of the user and pays some fees to the assigned operator. + fn pubkey_table(&self) -> &PublickeyTable; + + /// Get the aggregated MuSig2 x-only pubkey used in the spending condition of the multisig. + fn aggregated_pubkey(&self) -> XOnlyPublicKey; + + /// Get the [`OperatorIdx`] associated with this client. + fn own_index(&self) -> OperatorIdx; + + /// Get the bitcoin network for which the builder constructs transactions. + fn network(&self) -> Network; +} + +/// A builder for raw transactions related to the bridge. +#[derive(Debug, Clone)] +pub struct TxBuildContext { + /// The network to build the transactions for. + network: Network, + + /// A table that maps bridge operator indexes to their respective x-only Schnorr pubkeys. + pubkey_table: PublickeyTable, + + /// The aggregated pubkey computed for the [`PublickeyTable`]. + /// + /// This is fixed for the given [`PublickeyTable`] and so we compute it only once. + aggregated_pubkey: XOnlyPublicKey, + + /// The [`OperatorIdx`] for this bridge client. + own_index: OperatorIdx, +} + +impl TxBuildContext { + /// Create a new [`TxBuildContext`] with the context required to build transactions of various + /// [`TxKind`](super::TxKind). + pub fn new(network: Network, operator_pubkeys: PublickeyTable, own_index: OperatorIdx) -> Self { + let aggregated_pubkey = get_aggregated_pubkey(operator_pubkeys.0.values().cloned()); + + Self { + network, + pubkey_table: operator_pubkeys, + aggregated_pubkey, + own_index, + } + } +} + +impl BuildContext for TxBuildContext { + /// Get the operators' pubkey table. + fn pubkey_table(&self) -> &PublickeyTable { + &self.pubkey_table + } + + /// Get the aggregated operator pubkeys. + fn aggregated_pubkey(&self) -> XOnlyPublicKey { + self.aggregated_pubkey + } + + /// Get the operator index associated with this client. + fn own_index(&self) -> OperatorIdx { + self.own_index + } + + /// Get the bitcoin network for which the builder constructs transactions. + fn network(&self) -> Network { + self.network + } +} + +/// Defines a method that any bridge transaction must implement in order to create a +/// structure that can be signed. +/// +/// This is implemented by any struct that contains bridge-specific information to create +/// transactions. +pub trait TxKind { + /// Create the [`TxSigningData`] required to create the final signed transaction. + fn construct_signing_data( + &self, + build_context: &C, + ) -> anyhow::Result; +} diff --git a/crates/primitives/src/deposit.rs b/crates/primitives/src/deposit.rs new file mode 100644 index 0000000..454b65e --- /dev/null +++ b/crates/primitives/src/deposit.rs @@ -0,0 +1,207 @@ +//! Builders related to building deposit-related transactions. +//! +//! Contains types, traits and implementations related to creating various transactions used in the +//! bridge-in dataflow. + +use anyhow::{bail, Context}; +use bitcoin::{ + key::TapTweak, + secp256k1::SECP256K1, + taproot::{self, ControlBlock}, + Address, Amount, OutPoint, Psbt, TapNodeHash, Transaction, TxOut, +}; +use serde::{Deserialize, Serialize}; + +use crate::{ + bitcoin::BitcoinAddress, + build_context::{BuildContext, TxKind}, + params::prelude::{BRIDGE_DENOMINATION, UNSPENDABLE_INTERNAL_KEY}, + scripts::{ + general::{create_tx, create_tx_ins, create_tx_outs}, + prelude::*, + taproot::{create_taproot_addr, SpendPath, TaprootWitness}, + }, + types::TxSigningData, +}; + +/// The deposit information required to create the Deposit Transaction. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct DepositInfo { + /// The deposit request transaction outpoints from the users. + deposit_request_outpoint: OutPoint, + + /// The execution layer address to mint the equivalent tokens to. + /// As of now, this is just the 20-byte EVM address. + el_address: Vec, + + /// The amount in bitcoins that the user is sending. + /// + /// This amount should be greater than the [`BRIDGE_DENOMINATION`] for the deposit to be + /// confirmed on bitcoin. The excess amount is used as miner fees for the Deposit Transaction. + total_amount: Amount, + + /// The hash of the take back leaf in the Deposit Request Transaction (DRT) as provided by the + /// user in their `OP_RETURN` output. + take_back_leaf_hash: TapNodeHash, + + /// The original taproot address in the Deposit Request Transaction (DRT) output used to + /// sanity check computation internally i.e., whether the known information (n/n script spend + /// path, [`static@UNSPENDABLE_INTERNAL_KEY`]) + the [`Self::take_back_leaf_hash`] yields the + /// same P2TR address. + original_taproot_addr: BitcoinAddress, +} + +impl TxKind for DepositInfo { + fn construct_signing_data( + &self, + build_context: &C, + ) -> anyhow::Result { + let prevouts = self.compute_prevouts(); + let spend_info = self.compute_spend_infos(build_context)?; + let unsigned_tx = self.create_unsigned_tx(build_context)?; + + let mut psbt = Psbt::from_unsigned_tx(unsigned_tx)?; + + for (i, input) in psbt.inputs.iter_mut().enumerate() { + input.witness_utxo = Some(prevouts[i].clone()); + } + + Ok(TxSigningData { + psbt, + spend_path: spend_info, + }) + } +} + +impl DepositInfo { + /// Create a new deposit info with all the necessary data required to create a deposit + /// transaction. + pub fn new( + deposit_request_outpoint: OutPoint, + el_address: Vec, + total_amount: Amount, + take_back_leaf_hash: TapNodeHash, + original_taproot_addr: BitcoinAddress, + ) -> Self { + Self { + deposit_request_outpoint, + el_address, + total_amount, + take_back_leaf_hash, + original_taproot_addr, + } + } + + /// Get the total deposit amount that needs to be bridged-in. + pub fn total_amount(&self) -> &Amount { + &self.total_amount + } + + /// Get the address in EL to mint tokens to. + pub fn el_address(&self) -> &[u8] { + &self.el_address + } + + /// Get the outpoint of the Deposit Request Transaction (DRT) that is to spent in the Deposit + /// Transaction (DT). + pub fn deposit_request_outpoint(&self) -> &OutPoint { + &self.deposit_request_outpoint + } + + /// Get the hash of the user-takes-back leaf in the taproot of the Deposit Request Transaction + /// (DRT). + pub fn take_back_leaf_hash(&self) -> &TapNodeHash { + &self.take_back_leaf_hash + } + + fn compute_spend_infos( + &self, + build_context: &impl BuildContext, + ) -> anyhow::Result { + // The Deposit Request (DT) spends the n-of-n multisig leaf + let spend_script = n_of_n_script(&build_context.aggregated_pubkey()); + let spend_script_hash = + TapNodeHash::from_script(&spend_script, taproot::LeafVersion::TapScript); + + let takeback_script_hash = self.take_back_leaf_hash(); + + let merkle_root = TapNodeHash::from_node_hashes(spend_script_hash, *takeback_script_hash); + + let address = Address::p2tr( + SECP256K1, + *UNSPENDABLE_INTERNAL_KEY, + Some(merkle_root), + build_context.network(), + ); + + let expected_addr = self.original_taproot_addr.address(); + + if address != *expected_addr { + bail!("address mismatch"); + } + + let (output_key, parity) = UNSPENDABLE_INTERNAL_KEY.tap_tweak(SECP256K1, Some(merkle_root)); + + let control_block = ControlBlock { + leaf_version: taproot::LeafVersion::TapScript, + internal_key: *UNSPENDABLE_INTERNAL_KEY, + merkle_branch: vec![*takeback_script_hash] + .try_into() + .context("invalid script hash")?, + output_key_parity: parity, + }; + + if !control_block.verify_taproot_commitment(SECP256K1, output_key.into(), &spend_script) { + bail!("control block verification"); + } + + let spend_info = TaprootWitness::Script { + script_buf: spend_script, + control_block, + }; + + Ok(spend_info) + } + + fn compute_prevouts(&self) -> Vec { + let deposit_address = self.original_taproot_addr.address(); + + vec![TxOut { + script_pubkey: deposit_address.script_pubkey(), + value: self.total_amount, + }] + } + + fn create_unsigned_tx(&self, build_context: &impl BuildContext) -> anyhow::Result { + // First, create the inputs + let outpoint = self.deposit_request_outpoint(); + let tx_ins = create_tx_ins([*outpoint]); + + // Then, create the outputs: + + // First, create the `OP_RETURN ` output + let el_addr = self.el_address(); + let el_addr: &[u8; 20] = el_addr.try_into().context("invalid el address")?; + + let metadata_script = metadata_script(el_addr); + let metadata_amount = Amount::from_int_btc(0); + + // Then create the taproot script pubkey with keypath spend for the actual deposit + let spend_path = SpendPath::KeySpend { + internal_key: build_context.aggregated_pubkey(), + }; + + let (bridge_addr, _) = create_taproot_addr(&build_context.network(), spend_path)?; + + let bridge_in_script_pubkey = bridge_addr.script_pubkey(); + + let tx_outs = create_tx_outs([ + (bridge_in_script_pubkey, BRIDGE_DENOMINATION), + (metadata_script, metadata_amount), + ]); + + let unsigned_tx = create_tx(tx_ins, tx_outs); + + Ok(unsigned_tx) + } +} diff --git a/crates/primitives/src/duties.rs b/crates/primitives/src/duties.rs new file mode 100644 index 0000000..b0f7009 --- /dev/null +++ b/crates/primitives/src/duties.rs @@ -0,0 +1,141 @@ +use serde::{Deserialize, Serialize}; + +use crate::{deposit::DepositInfo, types::OperatorIdx, withdrawal::WithdrawalInfo}; + +/// The various duties that can be assigned to an operator. +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(tag = "type", content = "payload")] +pub enum BridgeDuty { + /// The duty to create and sign a Deposit Transaction so as to move funds from the user to the + /// Bridge Address. + /// + /// This duty is created when a user deposit request comes in, and applies to all operators. + SignDeposit(DepositInfo), + + /// The duty to fulfill a withdrawal request that is assigned to a particular operator. + /// + /// This duty is created when a user requests a withdrawal by calling a precompile in the EL + /// and the [`crate::bridge_state::DepositState`] transitions to + /// [`crate::bridge_state::DepositState::Dispatched`]. + /// + /// This kicks off the withdrawal process which involves cooperative signing by the operator + /// set, or a more involved unilateral withdrawal process (in the future) if not all operators + /// cooperate in the process. + FulfillWithdrawal(WithdrawalInfo), +} + +impl From for BridgeDuty { + fn from(value: DepositInfo) -> Self { + Self::SignDeposit(value) + } +} + +impl From for BridgeDuty { + fn from(value: WithdrawalInfo) -> Self { + Self::FulfillWithdrawal(value) + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct BridgeDuties { + pub duties: Vec, + + pub start_index: u64, + + pub stop_index: u64, +} + +/// The various states a bridge duty may be in. +/// +/// The full state transition looks as follows: +/// +/// `Received` --|`CollectingNonces`|--> `CollectedNonces` --|`CollectingPartialSigs`|--> +/// `CollectedSignatures` --|`Broadcasting`|--> `Executed`. +/// +/// The duty execution might fail as well at any step in which case the status would be `Failed`. +/// +/// # Note +/// +/// This type does not dictate the exact state transition path. A transition from `Received` to +/// `Executed` is perfectly valid to allow for maximum flexibility. +// TODO: use a typestate pattern with a `next` method that does the state transition. This can +// be left as is to allow for flexible level of granularity. For example, one could just have +// `Received`, `CollectedSignatures` and `Executed`. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum BridgeDutyStatus { + /// The duty has been received. + /// + /// This usually entails collecting nonces before the corresponding transaction can be + /// partially signed. + Received, + + /// The required nonces are being collected. + CollectingNonces { + /// The number of nonces collected so far. + collected: u32, + + /// The indexes of operators that are yet to provide nonces. + remaining: Vec, + }, + + /// The required nonces have been collected. + /// + /// This state can be inferred from the previous state but might still be useful as the + /// required number of nonces is context-driven and it cannot be determined whether all + /// nonces have been collected by looking at the above variant alone. + CollectedNonces, + + /// The partial signatures are being collected. + CollectingSignatures { + /// The number of nonces collected so far. + collected: u32, + + /// The indexes of operators that are yet to provide partial signatures. + remaining: Vec, + }, + + /// The required partial signatures have been collected. + /// + /// This state can be inferred from the previous state but might still be useful as the + /// required number of signatures is context-driven and it cannot be determined whether all + /// partial signatures have been collected by looking at the above variant alone. + CollectedSignatures, + + /// The duty has been executed. + /// + /// This means that the required transaction has been fully signed and broadcasted to Bitcoin. + Executed, + + /// The duty could not be executed. + /// + /// Holds the error message as a [`String`] for context and the number of retries for a + /// particular duty. + // TODO: this should hold `strata-bridge-exec::ExecError` instead but that requires + // implementing `BorshSerialize` and `BorshDeserialize`. + Failed { + /// The error message. + error_msg: String, + + /// The number of times a duty has been retried. + num_retries: u32, + }, + + /// The duty could not be executed even after repeated tries. + /// + /// Holds the error message encountered during the last execution. + Discarded(String), +} + +impl Default for BridgeDutyStatus { + fn default() -> Self { + Self::Received + } +} + +impl BridgeDutyStatus { + /// Checks if the [`BridgeDutyStatus`] is in its final state. + pub fn is_done(&self) -> bool { + matches!(self, BridgeDutyStatus::Executed) + } +} diff --git a/crates/primitives/src/helpers.rs b/crates/primitives/src/helpers.rs new file mode 100644 index 0000000..67f95ee --- /dev/null +++ b/crates/primitives/src/helpers.rs @@ -0,0 +1,9 @@ +use sha2::{Digest, Sha256}; + +pub fn hash_to_bn254_fq(data: &[u8]) -> [u8; 32] { + let mut hasher = Sha256::new(); + hasher.update(data); + let mut hash: [u8; 32] = hasher.finalize().into(); + hash[0] &= 0b00011111; // mask 3 most significant bits + hash +} diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs new file mode 100644 index 0000000..d1eb9bf --- /dev/null +++ b/crates/primitives/src/lib.rs @@ -0,0 +1,10 @@ +pub mod bitcoin; +pub mod build_context; +pub mod deposit; +pub mod duties; +pub mod helpers; +pub mod params; +pub mod scripts; +pub mod types; +pub mod withdrawal; +pub mod wots; diff --git a/crates/tx-graph/src/connectors/constants.rs b/crates/primitives/src/params/connectors.rs similarity index 62% rename from crates/tx-graph/src/connectors/constants.rs rename to crates/primitives/src/params/connectors.rs index e32f3b5..94cff54 100644 --- a/crates/tx-graph/src/connectors/constants.rs +++ b/crates/primitives/src/params/connectors.rs @@ -1,8 +1,11 @@ +//! Params related to the bridge tx graph connectors; + pub const NUM_PKS_A256_PER_CONNECTOR: usize = 7; -pub const NUM_PKS_A256: usize = 49; +pub const NUM_PKS_A256: usize = 42; pub const NUM_CONNECTOR_A256: usize = NUM_PKS_A256 / NUM_PKS_A256_PER_CONNECTOR; +pub const NUM_PKS_A256_RESIDUAL: usize = NUM_PKS_A256 % NUM_PKS_A256_PER_CONNECTOR; pub const NUM_PKS_A160_PER_CONNECTOR: usize = 11; -pub const NUM_PKS_A160: usize = 598; +pub const NUM_PKS_A160: usize = 574; pub const NUM_CONNECTOR_A160: usize = NUM_PKS_A160 / NUM_PKS_A160_PER_CONNECTOR; pub const NUM_PKS_A160_RESIDUAL: usize = NUM_PKS_A160 % NUM_PKS_A160_PER_CONNECTOR; diff --git a/crates/primitives/src/params/mod.rs b/crates/primitives/src/params/mod.rs new file mode 100644 index 0000000..20907a8 --- /dev/null +++ b/crates/primitives/src/params/mod.rs @@ -0,0 +1,3 @@ +pub mod connectors; +pub mod prelude; +pub mod tx; diff --git a/crates/primitives/src/params/prelude.rs b/crates/primitives/src/params/prelude.rs new file mode 100644 index 0000000..c964a93 --- /dev/null +++ b/crates/primitives/src/params/prelude.rs @@ -0,0 +1 @@ +pub use super::{connectors::*, tx::*}; diff --git a/crates/tx-graph/src/constants.rs b/crates/primitives/src/params/tx.rs similarity index 53% rename from crates/tx-graph/src/constants.rs rename to crates/primitives/src/params/tx.rs index 122bfa5..3d881a4 100644 --- a/crates/tx-graph/src/constants.rs +++ b/crates/primitives/src/params/tx.rs @@ -1,8 +1,8 @@ -//! Constants related to bridge transactions. +//! Params related to the bridge transactions. -use std::str::FromStr; +use std::{str::FromStr, time::Duration}; -use bitcoin::{secp256k1::XOnlyPublicKey, Amount}; +use bitcoin::{secp256k1::XOnlyPublicKey, Amount, FeeRate}; /// The value of each UTXO in the Bridge Multisig Address. pub const BRIDGE_DENOMINATION: Amount = Amount::from_int_btc(10); @@ -13,12 +13,30 @@ pub const BRIDGE_DENOMINATION: Amount = Amount::from_int_btc(10); /// outputs. Setting this to a very high value may alleviate the need for an `anyone_can_pay` /// output. In its current configuration of `10`, the total transaction fee for withdrawal /// transaction computes to ~5.5 sats/vB (run integration tests with `RUST_LOG=warn` to verify). -pub const MIN_RELAY_FEE: Amount = Amount::from_sat(1000); +pub const MIN_RELAY_FEE: Amount = Amount::from_sat(5000); -pub const OPERATOR_STAKE: Amount = Amount::from_int_btc(2); +/// The assert data tx is almost as big as the standardness limit allows. +/// +/// So, it requires extra fees. Here, we set it to 4 times the normal. +pub const ASSERT_DATA_FEE: Amount = Amount::from_sat(4 * 1000); + +pub const ASSERT_DATA_FEE_RATE: FeeRate = + FeeRate::from_sat_per_vb_unchecked(FeeRate::DUST.to_sat_per_vb_ceil() * 80); // 80 is based on + // experiment + +pub const BTC_CONFIRM_PERIOD: Duration = Duration::from_secs(6); + +pub const OPERATOR_STAKE: Amount = Amount::from_int_btc(5); + +/// The fee charged by the operator to process a withdrawal. +/// +/// This has the type [`Amount`] for convenience. +pub const OPERATOR_FEE: Amount = Amount::from_int_btc(2); + +pub const DISPROVER_REWARD: Amount = Amount::from_int_btc(2); /// Magic bytes to add to the metadata output in transactions to help identify them. -pub const MAGIC_BYTES: &[u8; 6] = b"strata"; +pub const MAGIC_BYTES: &[u8; 11] = b"alpenstrata"; lazy_static::lazy_static! { /// This is an unspendable pubkey. diff --git a/crates/tx-graph/src/commitments.rs b/crates/primitives/src/scripts/commitments.rs similarity index 61% rename from crates/tx-graph/src/commitments.rs rename to crates/primitives/src/scripts/commitments.rs index 34ebd83..105a6d4 100644 --- a/crates/tx-graph/src/commitments.rs +++ b/crates/primitives/src/scripts/commitments.rs @@ -1,12 +1,10 @@ -use std::str::FromStr; - -use bitcoin::hashes::hash160; -use bitvm::signatures::wots::{wots160, wots256, wots32}; +use bitvm::signatures::wots::{wots256, wots32}; +use sha2::Digest; fn secret_key_from_msk(msk: &str, var: &str) -> String { - hash160::Hash::from_str(&format!("{msk}:{var}")) - .unwrap() - .to_string() + let mut hasher = sha2::Sha256::new(); + hasher.update(format!("{msk}:{var}")); + format!("{:x}", hasher.finalize()) } pub fn secret_key_for_superblock_hash(msk: &str) -> String { @@ -24,7 +22,12 @@ pub fn secret_key_for_bridge_out_txid(msk: &str) -> String { secret_key_from_msk(msk, var) } -pub fn secret_key_for_proof_element(msk: &str, id: u32) -> String { +pub fn secret_key_for_public_inputs_hash(msk: &str) -> String { + let var = "public_inputs_hash"; + secret_key_from_msk(msk, var) +} + +pub fn secret_key_for_proof_element(msk: &str, id: usize) -> String { let var = &format!("proof_element_{}", id); secret_key_from_msk(msk, var) } @@ -44,12 +47,20 @@ pub fn public_key_for_bridge_out_txid(msk: &str) -> wots256::PublicKey { wots256::generate_public_key(&secret_key) } -pub fn public_key_for_proof_element_160(msk: &str, id: u32) -> wots160::PublicKey { - let secret_key = secret_key_for_proof_element(msk, id); - wots160::generate_public_key(&secret_key) +pub fn public_key_for_public_inputs_hash(msk: &str) -> wots256::PublicKey { + let secret_key = secret_key_for_bridge_out_txid(msk); + wots256::generate_public_key(&secret_key) } -pub fn public_key_for_proof_element_256(msk: &str, id: u32) -> wots256::PublicKey { - let secret_key = secret_key_for_proof_element(msk, id); - wots256::generate_public_key(&secret_key) +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_secret_key_from_msk() { + let msk = "hello"; + let var = "world"; + + println!("{}", secret_key_from_msk(msk, var)); + } } diff --git a/crates/tx-graph/src/scripts/general.rs b/crates/primitives/src/scripts/general.rs similarity index 74% rename from crates/tx-graph/src/scripts/general.rs rename to crates/primitives/src/scripts/general.rs index cbc8111..47dbb73 100644 --- a/crates/tx-graph/src/scripts/general.rs +++ b/crates/primitives/src/scripts/general.rs @@ -1,20 +1,21 @@ use bitcoin::{ absolute::LockTime, - opcodes::{all::OP_RETURN, OP_TRUE}, + opcodes::all::OP_RETURN, script::{Builder, PushBytesBuf}, transaction, Amount, OutPoint, ScriptBuf, Transaction, TxIn, TxOut, Witness, }; use bitcoin_script::script; +use bitvm::{pseudo::NMUL, treepp::*}; use musig2::KeyAggContext; use secp256k1::{PublicKey, XOnlyPublicKey}; -use crate::constants::MAGIC_BYTES; +use crate::params::prelude::MAGIC_BYTES; /// Create a script with the spending condition that a MuSig2 aggregated signature corresponding to /// the pubkey set must be provided. pub fn n_of_n_script(aggregated_pubkey: &XOnlyPublicKey) -> ScriptBuf { script! { - { *aggregated_pubkey} + { *aggregated_pubkey } OP_CHECKSIG } .compile() @@ -31,6 +32,19 @@ pub fn n_of_n_with_timelock(aggregated_pubkey: &XOnlyPublicKey, timelock: u32) - .compile() } +pub fn op_return_nonce(data: Vec) -> ScriptBuf { + let mut push_data = PushBytesBuf::new(); + push_data + .extend_from_slice(&data[..]) + .expect("data should be within limit"); + + dbg!(&push_data); + Builder::new() + .push_opcode(OP_RETURN) + .push_slice(push_data) + .into_script() +} + /// Aggregate the pubkeys using [`musig2`] and return the resulting [`XOnlyPublicKey`]. /// /// Please refer to MuSig2 key aggregation section in @@ -57,9 +71,16 @@ pub fn metadata_script(el_address: &[u8; 20]) -> ScriptBuf { .into_script() } +pub fn anyone_can_spend_script() -> ScriptBuf { + script! { + OP_TRUE + } + .compile() +} + /// Create an output that can be spent by anyone, i.e. its script contains a single `OP_TRUE`. pub fn anyone_can_spend_txout() -> TxOut { - let script = Builder::new().push_opcode(OP_TRUE).into_script(); + let script = anyone_can_spend_script(); let script_pubkey = script.to_p2wsh(); let value = script_pubkey.minimal_non_dust(); @@ -110,3 +131,29 @@ pub fn create_tx_outs( }) .collect() } + +pub fn extract_superblock_ts_from_header() -> Script { + script! { + for i in 0..4 { { 80 - 12 + 2 * i } OP_PICK } + for _ in 1..4 { { NMUL(1 << 8) } OP_ADD } + } +} + +pub fn add_bincode_padding_bytes32() -> Script { + script! { + for b in [0; 7] { {b} } 32 + } +} + +pub fn hash_to_bn254_fq() -> Script { + script! { + for i in 1..=3 { + { 1 << (8 - i) } + OP_2DUP + OP_GREATERTHAN + OP_IF OP_SUB + OP_ELSE OP_DROP + OP_ENDIF + } + } +} diff --git a/crates/primitives/src/scripts/mod.rs b/crates/primitives/src/scripts/mod.rs new file mode 100644 index 0000000..c346d0c --- /dev/null +++ b/crates/primitives/src/scripts/mod.rs @@ -0,0 +1,8 @@ +pub mod commitments; +pub mod general; +pub mod parse_witness; +pub mod prelude; +pub mod sp1g16; +pub mod taproot; +pub mod transform; +pub mod wots; diff --git a/crates/primitives/src/scripts/parse_witness.rs b/crates/primitives/src/scripts/parse_witness.rs new file mode 100644 index 0000000..de98cdb --- /dev/null +++ b/crates/primitives/src/scripts/parse_witness.rs @@ -0,0 +1,213 @@ +use bitvm::{ + groth16::g16, + signatures::wots::{wots160, wots256, wots32}, + treepp::*, +}; + +use crate::params::prelude::{ + NUM_CONNECTOR_A160, NUM_CONNECTOR_A256, NUM_PKS_A160_PER_CONNECTOR, NUM_PKS_A160_RESIDUAL, + NUM_PKS_A256_PER_CONNECTOR, +}; + +fn parse_wots160_signatures(script: Script) -> [wots160::Signature; N_SIGS] { + let res = execute_script(script.clone()); + std::array::from_fn(|i| { + std::array::from_fn(|j| { + let k = 2 * j + i * 2 * wots160::N_DIGITS as usize; + let preimage = res.final_stack.get(k); + let digit = res.final_stack.get(k + 1); + let digit = if digit.is_empty() { 0u8 } else { digit[0] }; + (preimage.try_into().unwrap(), digit) + }) + }) +} + +fn parse_wots256_signatures(script: Script) -> [wots256::Signature; N_SIGS] { + let res = execute_script(script.clone()); + std::array::from_fn(|i| { + std::array::from_fn(|j| { + let k = 2 * j + i * 2 * wots256::N_DIGITS as usize; + let preimage = res.final_stack.get(k); + let digit = res.final_stack.get(k + 1); + let digit = if digit.is_empty() { 0u8 } else { digit[0] }; + (preimage.try_into().unwrap(), digit) + }) + }) +} + +pub fn parse_claim_witness(script: Script) -> (wots32::Signature, wots256::Signature) { + let res = execute_script(script); + ( + std::array::from_fn(|j| { + let k = 2 * j; + let preimage = res.final_stack.get(k); + let digit = res.final_stack.get(k + 1); + let digit = if digit.is_empty() { 0u8 } else { digit[0] }; + (preimage.try_into().unwrap(), digit) + }), + std::array::from_fn(|j| { + let k = 2 * wots32::N_DIGITS as usize + 2 * j; + let preimage = res.final_stack.get(k); + let digit = res.final_stack.get(k + 1); + let digit = if digit.is_empty() { 0u8 } else { digit[0] }; + (preimage.try_into().unwrap(), digit) + }), + ) +} + +pub fn parse_assertion_witnesses( + witness256: [Script; NUM_CONNECTOR_A256], + witness160: [Script; NUM_CONNECTOR_A160], + witness160_residual: Option