commit 21f0a4befc6336aeea39149149a0e1d5cfc66654
parent 487fc93c99317f0fbedf2a91550e2dcf1ecdffc9
Author: Antoine A <>
Date: Thu, 11 Nov 2021 23:45:56 +0100
Setup GPG sign
Diffstat:
| A | .gitignore | | | 1 | + |
| A | Cargo.lock | | | 663 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | Cargo.toml | | | 18 | ++++++++++++++++++ |
| A | research.md | | | 136 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/main.rs | | | 338 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | test.js | | | 4 | ++++ |
6 files changed, 1160 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/Cargo.lock b/Cargo.lock
@@ -0,0 +1,663 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "argh"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f023c76cd7975f9969f8e29f0e461decbdc7f51048ce43427107a3d192f1c9bf"
+dependencies = [
+ "argh_derive",
+ "argh_shared",
+]
+
+[[package]]
+name = "argh_derive"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48ad219abc0c06ca788aface2e3a1970587e3413ab70acd20e54b6ec524c1f8f"
+dependencies = [
+ "argh_shared",
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "argh_shared"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38de00daab4eac7d753e97697066238d67ce9d7e2d823ab4f72fe14af29f3f33"
+
+[[package]]
+name = "autocfg"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
+
+[[package]]
+name = "base64-compat"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a8d4d2746f89841e49230dd26917df1876050f95abafafbe34f47cb534b88d7"
+dependencies = [
+ "byteorder",
+]
+
+[[package]]
+name = "bech32"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf9ff0bbfd639f15c74af777d81383cf53efb7c93613f6cab67c6c11e05bbf8b"
+
+[[package]]
+name = "bitcoin"
+version = "0.27.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a41df6ad9642c5c15ae312dd3d074de38fd3eb7cc87ad4ce10f90292a83fe4d"
+dependencies = [
+ "bech32",
+ "bitcoin_hashes",
+ "secp256k1",
+ "serde",
+]
+
+[[package]]
+name = "bitcoin-bech32"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "754eb4c7f35c031f33c95cc257b4c4192a5c9d3de637d3ee78ab052a3f35da57"
+dependencies = [
+ "bech32",
+]
+
+[[package]]
+name = "bitcoin_hashes"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "006cc91e1a1d99819bc5b8214be3555c1f0611b169f527a1fdc54ed1f2b745b0"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "bitcoincore-rpc"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b8d99d58466295cb2bf72c6959b784d59f8f0d6977458d2ba3eb75c834f36c3"
+dependencies = [
+ "bitcoincore-rpc-json",
+ "jsonrpc",
+ "log",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "bitcoincore-rpc-json"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dce91de73c61f5776cf938bfa88378c5b404a70e3369b761dacbe6024fea79dd"
+dependencies = [
+ "bitcoin",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "bitflags"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
+
+[[package]]
+name = "block-buffer"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "bs58"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3"
+
+[[package]]
+name = "byteorder"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+
+[[package]]
+name = "cc"
+version = "1.0.71"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "cfg_aliases"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
+
+[[package]]
+name = "clipboard-win"
+version = "4.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3db8340083d28acb43451166543b98c838299b7e0863621be53a338adceea0ed"
+dependencies = [
+ "error-code",
+ "str-buf",
+ "winapi",
+]
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "depolymerization"
+version = "0.1.0"
+dependencies = [
+ "argh",
+ "bitcoin-bech32",
+ "bitcoincore-rpc",
+ "bs58",
+ "digest",
+ "fastrand",
+ "rustyline",
+ "sha2",
+ "static_init",
+]
+
+[[package]]
+name = "digest"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "dirs-next"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
+dependencies = [
+ "cfg-if",
+ "dirs-sys-next",
+]
+
+[[package]]
+name = "dirs-sys-next"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
+dependencies = [
+ "libc",
+ "redox_users",
+ "winapi",
+]
+
+[[package]]
+name = "endian-type"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
+
+[[package]]
+name = "error-code"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5115567ac25674e0043e472be13d14e537f37ea8aa4bdc4aef0c89add1db1ff"
+dependencies = [
+ "libc",
+ "str-buf",
+]
+
+[[package]]
+name = "fastrand"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b394ed3d285a429378d3b384b9eb1285267e7df4b166df24b7a6939a04dc392e"
+dependencies = [
+ "instant",
+]
+
+[[package]]
+name = "fd-lock"
+version = "3.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8806dd91a06a7a403a8e596f9bfbfb34e469efbc363fc9c9713e79e26472e36"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "heck"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
+dependencies = [
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "instant"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "itoa"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
+
+[[package]]
+name = "jsonrpc"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad24d69a8a0698db8ffb9048e937e8ae3ee3bc45772a5d7b6979b1d2d5b6a9f7"
+dependencies = [
+ "base64-compat",
+ "serde",
+ "serde_derive",
+ "serde_json",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.107"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219"
+
+[[package]]
+name = "lock_api"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109"
+dependencies = [
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "memchr"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
+
+[[package]]
+name = "memoffset"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "nibble_vec"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43"
+dependencies = [
+ "smallvec",
+]
+
+[[package]]
+name = "nix"
+version = "0.22.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3bb9a13fa32bc5aeb64150cd3f32d6cf4c748f8f8a417cce5d2eb976a8370ba"
+dependencies = [
+ "bitflags",
+ "cc",
+ "cfg-if",
+ "libc",
+ "memoffset",
+]
+
+[[package]]
+name = "opaque-debug"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
+
+[[package]]
+name = "parking_lot"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
+dependencies = [
+ "instant",
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
+dependencies = [
+ "cfg-if",
+ "instant",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "winapi",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
+dependencies = [
+ "unicode-xid",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "radix_trie"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd"
+dependencies = [
+ "endian-type",
+ "nibble_vec",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "redox_users"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
+dependencies = [
+ "getrandom",
+ "redox_syscall",
+]
+
+[[package]]
+name = "rustyline"
+version = "9.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "790487c3881a63489ae77126f57048b42d62d3b2bafbf37453ea19eedb6340d6"
+dependencies = [
+ "bitflags",
+ "cfg-if",
+ "clipboard-win",
+ "dirs-next",
+ "fd-lock",
+ "libc",
+ "log",
+ "memchr",
+ "nix",
+ "radix_trie",
+ "scopeguard",
+ "smallvec",
+ "unicode-segmentation",
+ "unicode-width",
+ "utf8parse",
+ "winapi",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
+
+[[package]]
+name = "scopeguard"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+
+[[package]]
+name = "secp256k1"
+version = "0.20.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97d03ceae636d0fed5bae6a7f4f664354c5f4fcedf6eef053fef17e49f837d0a"
+dependencies = [
+ "secp256k1-sys",
+ "serde",
+]
+
+[[package]]
+name = "secp256k1-sys"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "827cb7cce42533829c792fc51b82fbf18b125b45a702ef2c8be77fce65463a7b"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.130"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.130"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e466864e431129c7e0d3476b92f20458e5879919a0596c6472738d9fa2d342f8"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "sha2"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa"
+dependencies = [
+ "block-buffer",
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+ "opaque-debug",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309"
+
+[[package]]
+name = "static_init"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a12a91dc56a0b47b48a3150157803b02cf82aa4c15ed499cb9b7ee0ff39941b1"
+dependencies = [
+ "bitflags",
+ "cfg_aliases",
+ "libc",
+ "parking_lot",
+ "parking_lot_core",
+ "static_init_macro",
+ "winapi",
+]
+
+[[package]]
+name = "static_init_macro"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b3fb6e118f42ea83548bb2194a79f5cbc8866fc38d87f6548139a7fac10e732"
+dependencies = [
+ "cfg_aliases",
+ "memchr",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "str-buf"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d44a3643b4ff9caf57abcee9c2c621d6c03d9135e0d8b589bd9afb5992cb176a"
+
+[[package]]
+name = "syn"
+version = "1.0.81"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-xid",
+]
+
+[[package]]
+name = "typenum"
+version = "1.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec"
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
+
+[[package]]
+name = "utf8parse"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
+
+[[package]]
+name = "version_check"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
+
+[[package]]
+name = "wasi"
+version = "0.10.2+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
+
+[[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"
diff --git a/Cargo.toml b/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "depolymerization"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+bitcoincore-rpc = "0.14.0"
+sha2 = "0.9.8"
+bs58 = "0.4.0"
+fastrand = "1.5.0"
+digest = "0.9.0"
+argh = "0.1.6"
+rustyline = "9.0.0"
+bitcoin-bech32 = "0.12.1"
+static_init = "1.0.1"
+
diff --git a/research.md b/research.md
@@ -0,0 +1,136 @@
+# Depolymerization
+
+## The problem
+
+We want to store metadata in the blockchain to identify taler wire transaction.
+
+## Bitcoin
+
+### Methods
+
+#### Fake legacy address
+
+The good:
+
+- Work with common wallets
+- Multiple payload of 20B
+
+The bad:
+
+- Two fees: higher weight fee and a a dust to burn (546 satoshis for a legacy
+ address)
+- Pollute the blockchain by bloating UTXO (Unspent Transaction Outputs)
+- Bitcoin URI does not support multiple recipients
+
+Assuming people using cryptocurrencies are techies, asking them to enter
+multiple recipients manually doe no sound that bad.
+
+#### Fake SegWit address
+
+Same as fake legacy address but:
+
+- dust burn reduced from 546sa/20B to 294sa/20B (0.3247€ to 0.1748€)
+- for huge address 330sa/32B (0.1784€) but against standard
+
+The good:
+
+- Work with common wallets
+- Multiple payload of 20B
+
+The bad:
+
+- Two fees: higher weight fee and a a dust to burn (546 satoshis for a legacy
+ address)
+- Pollute the blockchain by bloating UTXO (Unspent Transaction Outputs)
+- Bitcoin URI does not support multiple recipients
+
+Assuming people using cryptocurrencies are techies, asking them to enter
+multiple recipients manually doe no sound that bad.
+
+#### OP_RETURN
+
+The good:
+
+- Recommended way to store metadata
+- No blockchain abuse
+- Only cost is a higher weight fee
+
+The bad:
+
+- Require custom raw transaction
+- Require custom client (need more research)
+- Max 40B or 80B payload (one OP_RETURN by transaction)
+
+Solution:
+
+- Create a custom online wallet middleware (https://coinb.in/#txinputs)
+- Good user interface
+- Lot of complexity
+
+#### Unique address
+
+The good:
+
+- The normal way to do transaction
+
+The bad:
+
+- We need to store state in a database
+- We need to add a new API routine
+- Complicated to do correctly
+
+`sendmany` shuffle outputs for privacy reasons in RPC since v0.17, and the order
+cannot be relied upon for other wallets.
+
+Solutions:
+
+- Encode order in payload
+
+### Blockchain trust
+
+We can virtually never be sure that a transaction have been made in durable
+manner. By waiting for 6 blocks (~1h) we ca, have a significant assurance.
+
+- Make the number of confirmation blocks configurable
+
+TODO:
+
+- Find a way to detect extended forks using bitcoin alert (-alertnotify or
+ getnetworkinfo RPC)
+- Or detect it ourselves by reading row block using RPC
+- How to handle removed transaction ? Do the wire history have to be consistent
+ ?
+
+### Refunds
+
+To refund we could send bitcoins back to the sender address (minus some fee of
+course). However some people use wallet solutions using a shared address for
+multiple users, in that case the refund will go to the solution but not to the
+designated user.
+
+Do we care ?
+
+### Sources
+
+Pompianu, Livio & Bartoletti, Massimo & Bellomy, Bryn. (2019). A Journey into
+Bitcoin Metadata. Journal of Grid Computing. 10.1007/s10723-019-09473-3.
+
+## Out
+
+OP_RETURN test
+
+## Ethereum
+
+- A transaction is from one address to another
+- Have a arbitrary data payload for smart contract
+
+Can we use smart contract to read metadata ?
+
+Rust client library OpenEthereum - JSON RCP API
+
+## TODO
+
+- Magic bit detection
+- Optimize magic bit
+- lower level WitnessProgram alternative ?
+- OP_RETURN logic
diff --git a/src/main.rs b/src/main.rs
@@ -0,0 +1,338 @@
+use std::{
+ collections::{BTreeMap, HashSet},
+ iter::repeat_with,
+ path::PathBuf,
+};
+
+use bitcoin_bech32::{constants::Network, u5, WitnessProgram};
+use bitcoincore_rpc::{
+ bitcoin::{Address, Amount, Txid},
+ jsonrpc::serde_json::Value,
+ Auth, Client, RpcApi,
+};
+use static_init::dynamic;
+
+// https://github.com/bitcoin/bitcoin/blob/master/src/policy/policy.cpp
+#[dynamic]
+static LEGACY_MIN_AMOUNT: Amount = Amount::from_sat(546);
+#[dynamic]
+static SEGWIT_MIN_AMOUNT: Amount = Amount::from_sat(294);
+
+const CLIENT: &str = "client";
+const WIRE: &str = "wire";
+const DATA_PATH: &str = "C:/Users/antoi/AppData/Roaming/Bitcoin/regtest";
+const RPC_URL: &str = "http://localhost:18443";
+
+#[derive(argh::FromArgs)]
+/// Bitcoin metadata tester
+struct Args {
+ /// start as a sender
+ #[argh(switch, short = 'c')]
+ client: bool,
+
+ /// start as a receiver
+ #[argh(switch, short = 'w')]
+ wire: bool,
+
+ /// a message to send
+ #[argh(option)]
+ msg: Option<String>,
+}
+
+fn encode_segwit_address(
+ network: Network,
+ is_first: bool,
+ magic_id: &[u8; 4],
+ key_half: &[u8; 16],
+) -> String {
+ // Combine magic_it and the key half
+ let mut buf = vec![0u8; 20];
+ buf[..4].copy_from_slice(magic_id);
+ buf[4..].copy_from_slice(key_half);
+ // Toggle first bit for ordering
+ if is_first {
+ buf[0] &= 0b0111_1111 // Unset first bit
+ } else {
+ buf[0] |= 0b1000_0000 // Set first bit
+ }
+ // Encode into an fake segwit address
+ WitnessProgram::new(u5::try_from_u8(0).unwrap(), buf, network)
+ .unwrap()
+ .to_address()
+}
+
+fn encode_segwit_key(network: Network, key: &[u8; 32]) -> [String; 2] {
+ // Generate a random magic identifier
+ let mut magic_id = [0; 4];
+ // TODO use secure os based random
+ magic_id.fill_with(|| fastrand::u8(..));
+ // Split key in half;
+ let mut split = ([0; 16], [0; 16]);
+ split.0.copy_from_slice(&key[..16]);
+ split.1.copy_from_slice(&key[16..]);
+ [
+ encode_segwit_address(network, true, &magic_id, &split.0),
+ encode_segwit_address(network, false, &magic_id, &split.1),
+ ]
+}
+
+#[derive(Debug, Clone)]
+pub enum DecodeError {
+ MissingSeqWitAddress,
+ MagicIdCollision,
+}
+
+fn decode_segwit_msg(segwit_addrs: &[&str]) -> Result<[u8; 32], DecodeError> {
+ if segwit_addrs.len() < 2 {
+ return Err(DecodeError::MissingSeqWitAddress);
+ }
+
+ let decoded: Vec<[u8; 20]> = segwit_addrs
+ .into_iter()
+ .filter_map(|addr| {
+ WitnessProgram::from_address(addr).ok().and_then(|wp| {
+ let pg = wp.program();
+ if pg.len() == 20 {
+ let mut buf = [0; 20];
+ buf.copy_from_slice(pg);
+ Some(buf)
+ } else {
+ None
+ }
+ })
+ })
+ .collect();
+
+ if decoded.len() < 2 {
+ return Err(DecodeError::MissingSeqWitAddress);
+ }
+
+ let mut parts: Vec<(bool, [u8; 4], &[u8; 16])> = decoded
+ .iter()
+ .map(|c| {
+ let mut magic_id: [u8; 4] = c[..4].try_into().unwrap();
+ let key_half: &[u8; 16] = c[4..].try_into().unwrap();
+ let is_first = !c[0] & 0b1000_0000 == 0;
+ // Clear first bit
+ magic_id[0] &= 0b0111_1111;
+ (is_first, magic_id, key_half)
+ })
+ .collect();
+ let mut map = BTreeMap::new();
+
+ for (_, magic, _) in &parts {
+ match map.get_mut(magic) {
+ Some(prev) => *prev = true,
+ None => {
+ map.insert(*magic, false);
+ }
+ }
+ }
+ map.retain(|_, many| *many);
+ assert_eq!(map.len(), 1, "Two possible magic id");
+ let magic_id = map.into_keys().next().unwrap();
+ parts.retain(|(_, magic, _)| *magic == magic_id);
+ assert_eq!(parts.len(), 2, "Magic ID collision");
+
+ let mut key = [0; 32];
+ for (is_first, _, half) in parts {
+ key[is_first as usize * 16..][..16].copy_from_slice(half);
+ }
+ Ok(key)
+}
+
+#[test]
+fn test_shuffle() {
+ for _ in 0..1000 {
+ let mut key = [0; 32];
+ key.fill_with(|| fastrand::u8(..));
+ let mut addresses = encode_segwit_key(Network::Bitcoin, &key);
+ fastrand::shuffle(&mut addresses);
+ let decoded =
+ decode_segwit_msg(&addresses.iter().map(|s| s.as_str()).collect::<Vec<&str>>())
+ .unwrap();
+ assert_eq!(key, decoded);
+ }
+}
+
+#[test]
+fn test_shuffle_many() {
+ for _ in 0..1000 {
+ let mut rng_address: Vec<String> = std::iter::repeat_with(|| {
+ let key: Vec<u8> = repeat_with(|| fastrand::u8(..)).take(20).collect();
+ WitnessProgram::new(u5::try_from_u8(0).unwrap(), key, Network::Bitcoin)
+ .unwrap()
+ .to_address()
+ })
+ .take(2)
+ .collect();
+
+ let mut key = [0; 32];
+ key.fill_with(|| fastrand::u8(..));
+ let mut addresses = encode_segwit_key(Network::Bitcoin, &key).to_vec();
+ addresses.append(&mut rng_address);
+ fastrand::shuffle(&mut addresses);
+ let decoded =
+ decode_segwit_msg(&addresses.iter().map(|s| s.as_str()).collect::<Vec<&str>>())
+ .unwrap();
+ assert_eq!(key, decoded);
+ }
+}
+
+fn common_rpc() -> Client {
+ Client::new(
+ RPC_URL,
+ Auth::CookieFile(PathBuf::from(DATA_PATH).join(".cookie")),
+ )
+ .expect("Failed to open common client")
+}
+fn wallet_rpc(wallet: &str) -> Client {
+ Client::new(
+ &format!("{}/wallet/{}", RPC_URL, wallet),
+ Auth::CookieFile(PathBuf::from(DATA_PATH).join(".cookie")),
+ )
+ .expect(&format!("Failed to open wallet '{}' client", wallet))
+}
+
+fn send_many(client: &Client, recipients: Vec<(String, Amount)>) -> bitcoincore_rpc::Result<Txid> {
+ let amounts = Value::Object(
+ recipients
+ .into_iter()
+ .map(|(addr, amount)| (addr, amount.as_btc().into()))
+ .collect(),
+ );
+ client.call(
+ "sendmany",
+ &[
+ "".into(), // dummy
+ amounts, // amounts
+ 0.into(), // minconf
+ "".into(), // comment
+ Value::Null, // substractfeefrom
+ false.into(), // replaceable
+ Value::Null, // conf_target
+ Value::Null, // estimate mode
+ 1.into(), // fee rate
+ false.into(), // verbose
+ ],
+ )
+}
+
+fn send_with_metadata(
+ rpc: &Client,
+ to: &Address,
+ amount: Amount,
+ metadata: &[u8],
+) -> bitcoincore_rpc::Result<Txid> {
+ let addresses = encode_segwit_key(Network::Regtest, &metadata.try_into().unwrap());
+ let mut recipients = vec![(to.to_string(), amount)];
+ recipients.extend(addresses.into_iter().map(|addr| (addr, *SEGWIT_MIN_AMOUNT)));
+ send_many(rpc, recipients)
+}
+
+fn last_metadata(rpc: &Client) -> bitcoincore_rpc::Result<Vec<u8>> {
+ let txs = rpc.list_transactions(None, None, None, None)?;
+ let last = txs.last().unwrap();
+
+ let info: Value = rpc.call(
+ "gettransaction",
+ &[last.info.txid.to_string().into(), Value::Null, true.into()],
+ )?;
+ let addresses: Vec<&str> = info["decoded"]["vout"]
+ .as_array()
+ .unwrap()
+ .into_iter()
+ .filter_map(|it| {
+ if it["value"].as_f64().unwrap() == SEGWIT_MIN_AMOUNT.as_btc() {
+ Some(it["scriptPubKey"]["address"].as_str().unwrap())
+ } else {
+ None
+ }
+ })
+ .collect();
+ Ok(decode_segwit_msg(&addresses).unwrap().to_vec())
+}
+
+fn main() {
+ {
+ let existing_wallets: HashSet<String> =
+ std::fs::read_dir(PathBuf::from(DATA_PATH).join("wallets"))
+ .unwrap()
+ .filter_map(|it| it.ok())
+ .map(|it| it.file_name().to_string_lossy().to_string())
+ .collect();
+
+ let rpc = common_rpc();
+ if !existing_wallets.contains(CLIENT) || !existing_wallets.contains(WIRE) {
+ println!("Generate new wallets");
+ // Create wallets
+ rpc.create_wallet(&WIRE, None, None, None, None).unwrap();
+ rpc.create_wallet(&CLIENT, None, None, None, None).unwrap();
+ // Add 50 BTC to client wallet
+ let rpc = wallet_rpc(CLIENT);
+ let addr = rpc.get_new_address(None, None).unwrap();
+ rpc.generate_to_address(101 /* Need 100 blocks to validate */, &addr)
+ .unwrap();
+ } else {
+ rpc.load_wallet(&WIRE).ok();
+ rpc.load_wallet(&CLIENT).ok();
+ }
+ }
+
+ println!("Initial state:");
+ let client_rpc = wallet_rpc(CLIENT);
+ let wire_rpc = wallet_rpc(WIRE);
+ let client_addr = client_rpc.get_new_address(None, None).unwrap();
+ let wire_addr = wire_rpc.get_new_address(None, None).unwrap();
+ println!(
+ "{} {} {}",
+ WIRE,
+ wire_addr,
+ wire_rpc.get_balance(None, None).unwrap()
+ );
+ println!(
+ "{} {} {}",
+ CLIENT,
+ client_addr,
+ client_rpc.get_balance(None, None).unwrap()
+ );
+
+ let args: Args = argh::from_env();
+
+ if args.client {
+ println!("Start client");
+ let mut rl = rustyline::Editor::<()>::new();
+ loop {
+ let rl = rl.readline(">> ");
+ match rl {
+ Ok(line) => {
+ send_with_metadata(
+ &client_rpc,
+ &wire_addr,
+ *LEGACY_MIN_AMOUNT,
+ line.as_bytes(),
+ )
+ .unwrap();
+ client_rpc.generate_to_address(1, &client_addr).unwrap();
+ }
+ Err(_) => break,
+ }
+ }
+ } else if args.wire {
+ println!("Start wire");
+ loop {
+ wire_rpc.wait_for_new_block(60 * 60 * 1000).ok();
+ let decoded = last_metadata(&wire_rpc).unwrap();
+ println!(">> {}", String::from_utf8_lossy(&decoded));
+ }
+ } else {
+ // Send metadata
+ let metadata: Vec<u8> = repeat_with(|| fastrand::u8(..)).take(32).collect();
+ send_with_metadata(&client_rpc, &wire_addr, *LEGACY_MIN_AMOUNT, &metadata).unwrap();
+ // Mine one block
+ client_rpc.generate_to_address(1, &client_addr).unwrap();
+ // Read metadata
+ let decoded = last_metadata(&wire_rpc).unwrap();
+ assert_eq!(metadata, decoded);
+ }
+}
diff --git a/test.js b/test.js
@@ -0,0 +1,4 @@
+import { createRemote } from 'https://deno.land/x/gentle_rpc@v2.9/mod.ts';
+const remote = createRemote('http://127.0.0.1:18443');
+const info = await remote.call('getbestblockhash');
+console.log(info);