commit
2afd32391a
@ -0,0 +1 @@ |
|||||||
|
target |
@ -0,0 +1,960 @@ |
|||||||
|
# This file is automatically @generated by Cargo. |
||||||
|
# It is not intended for manual editing. |
||||||
|
[[package]] |
||||||
|
name = "anyhow" |
||||||
|
version = "1.0.38" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "arrayvec" |
||||||
|
version = "0.5.2" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "async-trait" |
||||||
|
version = "0.1.42" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "8d3a45e77e34375a7923b1e8febb049bb011f064714a8e17a1a616fef01da13d" |
||||||
|
dependencies = [ |
||||||
|
"proc-macro2", |
||||||
|
"quote", |
||||||
|
"syn", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "bitvec" |
||||||
|
version = "0.17.4" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "41262f11d771fd4a61aa3ce019fca363b4b6c282fca9da2a31186d3965a47a5c" |
||||||
|
dependencies = [ |
||||||
|
"either", |
||||||
|
"radium 0.3.0", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "bitvec" |
||||||
|
version = "0.18.4" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "1d2838fdd79e8776dbe07a106c784b0f8dda571a21b2750a092cc4cbaa653c8e" |
||||||
|
dependencies = [ |
||||||
|
"funty", |
||||||
|
"radium 0.4.1", |
||||||
|
"wyz", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "block-buffer" |
||||||
|
version = "0.9.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" |
||||||
|
dependencies = [ |
||||||
|
"block-padding", |
||||||
|
"generic-array", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "block-padding" |
||||||
|
version = "0.2.1" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "byte-slice-cast" |
||||||
|
version = "0.3.5" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "b0a5e3906bcbf133e33c1d4d95afc664ad37fbdb9f6568d8043e7ea8c27d93d3" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "byteorder" |
||||||
|
version = "1.4.2" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "bytes" |
||||||
|
version = "1.0.1" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" |
||||||
|
dependencies = [ |
||||||
|
"serde", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "cfg-if" |
||||||
|
version = "1.0.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "cpuid-bool" |
||||||
|
version = "0.1.2" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "crunchy" |
||||||
|
version = "0.2.2" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "crypto-mac" |
||||||
|
version = "0.10.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "4857fd85a0c34b3c3297875b747c1e02e06b6a0ea32dd892d8192b9ce0813ea6" |
||||||
|
dependencies = [ |
||||||
|
"generic-array", |
||||||
|
"subtle", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "digest" |
||||||
|
version = "0.9.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" |
||||||
|
dependencies = [ |
||||||
|
"generic-array", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "ecdsa" |
||||||
|
version = "0.10.2" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "41fbdb4ff710acb4db8ca29f93b897529ea6d6a45626d5183b47e012aa6ae7e4" |
||||||
|
dependencies = [ |
||||||
|
"elliptic-curve", |
||||||
|
"hmac", |
||||||
|
"signature", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "either" |
||||||
|
version = "1.6.1" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "elliptic-curve" |
||||||
|
version = "0.8.4" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "592b1c857559479c056b73a3053c717108a70e4dce320ad28c79c63f5c2e62ba" |
||||||
|
dependencies = [ |
||||||
|
"bitvec 0.18.4", |
||||||
|
"digest", |
||||||
|
"ff", |
||||||
|
"generic-array", |
||||||
|
"group", |
||||||
|
"rand_core 0.5.1", |
||||||
|
"subtle", |
||||||
|
"zeroize", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "eth-keystore" |
||||||
|
version = "0.1.0" |
||||||
|
source = "git+https://github.com/roynalnaruto/eth-keystore-rs#6e99e64d341a345f4fed24c97fa356eec6941843" |
||||||
|
dependencies = [ |
||||||
|
"hex", |
||||||
|
"rand 0.7.3", |
||||||
|
"rust-crypto", |
||||||
|
"serde", |
||||||
|
"serde_json", |
||||||
|
"thiserror", |
||||||
|
"uuid", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "ethabi-next" |
||||||
|
version = "13.0.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "f4bb43ca3464de349cd4f94bb37113eb4938dfb7df1a28320049bbbe3e9ffc6a" |
||||||
|
dependencies = [ |
||||||
|
"anyhow", |
||||||
|
"ethereum-types", |
||||||
|
"hex", |
||||||
|
"serde", |
||||||
|
"serde_json", |
||||||
|
"sha3", |
||||||
|
"thiserror", |
||||||
|
"uint", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "ethbloom" |
||||||
|
version = "0.10.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "22a621dcebea74f2a6f2002d0a885c81ccf6cbdf86760183316a7722b5707ca4" |
||||||
|
dependencies = [ |
||||||
|
"crunchy", |
||||||
|
"fixed-hash", |
||||||
|
"impl-rlp", |
||||||
|
"impl-serde", |
||||||
|
"tiny-keccak", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "ethereum-types" |
||||||
|
version = "0.10.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "05dc5f0df4915fa6dff7f975a8366ecfaaa8959c74235469495153e7bb1b280e" |
||||||
|
dependencies = [ |
||||||
|
"ethbloom", |
||||||
|
"fixed-hash", |
||||||
|
"impl-rlp", |
||||||
|
"impl-serde", |
||||||
|
"primitive-types", |
||||||
|
"uint", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "ethers-core" |
||||||
|
version = "0.1.3" |
||||||
|
source = "git+https://github.com/gakonst/ethers-rs#177f0c3eb90dc634276536dc928c956b2a939400" |
||||||
|
dependencies = [ |
||||||
|
"arrayvec", |
||||||
|
"bytes", |
||||||
|
"ecdsa", |
||||||
|
"elliptic-curve", |
||||||
|
"ethabi-next", |
||||||
|
"ethereum-types", |
||||||
|
"generic-array", |
||||||
|
"glob", |
||||||
|
"hex", |
||||||
|
"k256", |
||||||
|
"rand 0.7.3", |
||||||
|
"rlp", |
||||||
|
"serde", |
||||||
|
"serde_json", |
||||||
|
"thiserror", |
||||||
|
"tiny-keccak", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "ethers-signers" |
||||||
|
version = "0.1.3" |
||||||
|
source = "git+https://github.com/gakonst/ethers-rs#177f0c3eb90dc634276536dc928c956b2a939400" |
||||||
|
dependencies = [ |
||||||
|
"async-trait", |
||||||
|
"elliptic-curve", |
||||||
|
"eth-keystore", |
||||||
|
"ethers-core", |
||||||
|
"futures-executor", |
||||||
|
"futures-util", |
||||||
|
"hex", |
||||||
|
"rand 0.7.3", |
||||||
|
"serde", |
||||||
|
"sha2", |
||||||
|
"thiserror", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "ff" |
||||||
|
version = "0.8.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "01646e077d4ebda82b73f1bca002ea1e91561a77df2431a9e79729bcc31950ef" |
||||||
|
dependencies = [ |
||||||
|
"bitvec 0.18.4", |
||||||
|
"rand_core 0.5.1", |
||||||
|
"subtle", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "fixed-hash" |
||||||
|
version = "0.7.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" |
||||||
|
dependencies = [ |
||||||
|
"byteorder", |
||||||
|
"rand 0.8.1", |
||||||
|
"rustc-hex", |
||||||
|
"static_assertions", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "fuchsia-cprng" |
||||||
|
version = "0.1.1" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "funty" |
||||||
|
version = "1.1.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "futures-core" |
||||||
|
version = "0.3.9" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "db8d3b0917ff63a2a96173133c02818fac4a746b0a57569d3baca9ec0e945e08" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "futures-executor" |
||||||
|
version = "0.3.9" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "9ee9ca2f7eb4475772cf39dd1cd06208dce2670ad38f4d9c7262b3e15f127068" |
||||||
|
dependencies = [ |
||||||
|
"futures-core", |
||||||
|
"futures-task", |
||||||
|
"futures-util", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "futures-macro" |
||||||
|
version = "0.3.9" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "0f8719ca0e1f3c5e34f3efe4570ef2c0610ca6da85ae7990d472e9cbfba13664" |
||||||
|
dependencies = [ |
||||||
|
"proc-macro-hack", |
||||||
|
"proc-macro2", |
||||||
|
"quote", |
||||||
|
"syn", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "futures-task" |
||||||
|
version = "0.3.9" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "a92a0843a2ff66823a8f7c77bffe9a09be2b64e533562c412d63075643ec0038" |
||||||
|
dependencies = [ |
||||||
|
"once_cell", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "futures-util" |
||||||
|
version = "0.3.9" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "036a2107cdeb57f6d7322f1b6c363dad67cd63ca3b7d1b925bdf75bd5d96cda9" |
||||||
|
dependencies = [ |
||||||
|
"futures-core", |
||||||
|
"futures-macro", |
||||||
|
"futures-task", |
||||||
|
"pin-project-lite", |
||||||
|
"pin-utils", |
||||||
|
"proc-macro-hack", |
||||||
|
"proc-macro-nested", |
||||||
|
"slab", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "gcc" |
||||||
|
version = "0.3.55" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" |
||||||
|
|
||||||
|
[[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.1.16" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" |
||||||
|
dependencies = [ |
||||||
|
"cfg-if", |
||||||
|
"libc", |
||||||
|
"wasi 0.9.0+wasi-snapshot-preview1", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "getrandom" |
||||||
|
version = "0.2.1" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "4060f4657be78b8e766215b02b18a2e862d83745545de804638e2b545e81aee6" |
||||||
|
dependencies = [ |
||||||
|
"cfg-if", |
||||||
|
"libc", |
||||||
|
"wasi 0.10.1+wasi-snapshot-preview1", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "glob" |
||||||
|
version = "0.3.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "group" |
||||||
|
version = "0.8.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "cc11f9f5fbf1943b48ae7c2bf6846e7d827a512d1be4f23af708f5ca5d01dde1" |
||||||
|
dependencies = [ |
||||||
|
"ff", |
||||||
|
"rand_core 0.5.1", |
||||||
|
"subtle", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "hex" |
||||||
|
version = "0.4.2" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "hmac" |
||||||
|
version = "0.10.1" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" |
||||||
|
dependencies = [ |
||||||
|
"crypto-mac", |
||||||
|
"digest", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "impl-codec" |
||||||
|
version = "0.4.2" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "1be51a921b067b0eaca2fad532d9400041561aa922221cc65f95a85641c6bf53" |
||||||
|
dependencies = [ |
||||||
|
"parity-scale-codec", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "impl-rlp" |
||||||
|
version = "0.3.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" |
||||||
|
dependencies = [ |
||||||
|
"rlp", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "impl-serde" |
||||||
|
version = "0.3.1" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "b47ca4d2b6931707a55fce5cf66aff80e2178c8b63bbb4ecb5695cbc870ddf6f" |
||||||
|
dependencies = [ |
||||||
|
"serde", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "itoa" |
||||||
|
version = "0.4.7" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "k256" |
||||||
|
version = "0.7.1" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "e443f962f64bcd3753b09e9d6c3733986a81e49b2a36c3c2faaedbe2c3a8e7dd" |
||||||
|
dependencies = [ |
||||||
|
"cfg-if", |
||||||
|
"ecdsa", |
||||||
|
"elliptic-curve", |
||||||
|
"sha3", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "keccak" |
||||||
|
version = "0.1.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "lazy_static" |
||||||
|
version = "1.4.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "libc" |
||||||
|
version = "0.2.82" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "once_cell" |
||||||
|
version = "1.5.2" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "opaque-debug" |
||||||
|
version = "0.3.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "optics-core" |
||||||
|
version = "0.1.0" |
||||||
|
dependencies = [ |
||||||
|
"ethers-core", |
||||||
|
"ethers-signers", |
||||||
|
"lazy_static", |
||||||
|
"sha3", |
||||||
|
"thiserror", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "parity-scale-codec" |
||||||
|
version = "1.3.6" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "79602888a81ace83e3d1d4b2873286c1f5f906c84db667594e8db8da3506c383" |
||||||
|
dependencies = [ |
||||||
|
"arrayvec", |
||||||
|
"bitvec 0.17.4", |
||||||
|
"byte-slice-cast", |
||||||
|
"serde", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "pin-project-lite" |
||||||
|
version = "0.2.4" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "439697af366c49a6d0a010c56a0d97685bc140ce0d377b13a2ea2aa42d64a827" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "pin-utils" |
||||||
|
version = "0.1.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "ppv-lite86" |
||||||
|
version = "0.2.10" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "primitive-types" |
||||||
|
version = "0.8.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "b3824ae2c5e27160113b9e029a10ec9e3f0237bad8029f69c7724393c9fdefd8" |
||||||
|
dependencies = [ |
||||||
|
"fixed-hash", |
||||||
|
"impl-codec", |
||||||
|
"impl-rlp", |
||||||
|
"impl-serde", |
||||||
|
"uint", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "proc-macro-hack" |
||||||
|
version = "0.5.19" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "proc-macro-nested" |
||||||
|
version = "0.1.6" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "proc-macro2" |
||||||
|
version = "1.0.24" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" |
||||||
|
dependencies = [ |
||||||
|
"unicode-xid", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "quote" |
||||||
|
version = "1.0.8" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" |
||||||
|
dependencies = [ |
||||||
|
"proc-macro2", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "radium" |
||||||
|
version = "0.3.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "def50a86306165861203e7f84ecffbbdfdea79f0e51039b33de1e952358c47ac" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "radium" |
||||||
|
version = "0.4.1" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "64de9a0c5361e034f1aefc9f71a86871ec870e766fe31a009734a989b329286a" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "rand" |
||||||
|
version = "0.3.23" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" |
||||||
|
dependencies = [ |
||||||
|
"libc", |
||||||
|
"rand 0.4.6", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "rand" |
||||||
|
version = "0.4.6" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" |
||||||
|
dependencies = [ |
||||||
|
"fuchsia-cprng", |
||||||
|
"libc", |
||||||
|
"rand_core 0.3.1", |
||||||
|
"rdrand", |
||||||
|
"winapi", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "rand" |
||||||
|
version = "0.7.3" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" |
||||||
|
dependencies = [ |
||||||
|
"getrandom 0.1.16", |
||||||
|
"libc", |
||||||
|
"rand_chacha 0.2.2", |
||||||
|
"rand_core 0.5.1", |
||||||
|
"rand_hc", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "rand" |
||||||
|
version = "0.8.1" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "c24fcd450d3fa2b592732565aa4f17a27a61c65ece4726353e000939b0edee34" |
||||||
|
dependencies = [ |
||||||
|
"libc", |
||||||
|
"rand_chacha 0.3.0", |
||||||
|
"rand_core 0.6.1", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "rand_chacha" |
||||||
|
version = "0.2.2" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" |
||||||
|
dependencies = [ |
||||||
|
"ppv-lite86", |
||||||
|
"rand_core 0.5.1", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "rand_chacha" |
||||||
|
version = "0.3.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" |
||||||
|
dependencies = [ |
||||||
|
"ppv-lite86", |
||||||
|
"rand_core 0.6.1", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "rand_core" |
||||||
|
version = "0.3.1" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" |
||||||
|
dependencies = [ |
||||||
|
"rand_core 0.4.2", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "rand_core" |
||||||
|
version = "0.4.2" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "rand_core" |
||||||
|
version = "0.5.1" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" |
||||||
|
dependencies = [ |
||||||
|
"getrandom 0.1.16", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "rand_core" |
||||||
|
version = "0.6.1" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "c026d7df8b298d90ccbbc5190bd04d85e159eaf5576caeacf8741da93ccbd2e5" |
||||||
|
dependencies = [ |
||||||
|
"getrandom 0.2.1", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "rand_hc" |
||||||
|
version = "0.2.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" |
||||||
|
dependencies = [ |
||||||
|
"rand_core 0.5.1", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "rdrand" |
||||||
|
version = "0.4.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" |
||||||
|
dependencies = [ |
||||||
|
"rand_core 0.3.1", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "rlp" |
||||||
|
version = "0.5.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "e54369147e3e7796c9b885c7304db87ca3d09a0a98f72843d532868675bbfba8" |
||||||
|
dependencies = [ |
||||||
|
"bytes", |
||||||
|
"rustc-hex", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "rust-crypto" |
||||||
|
version = "0.2.36" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" |
||||||
|
dependencies = [ |
||||||
|
"gcc", |
||||||
|
"libc", |
||||||
|
"rand 0.3.23", |
||||||
|
"rustc-serialize", |
||||||
|
"time", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "rustc-hex" |
||||||
|
version = "2.1.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "rustc-serialize" |
||||||
|
version = "0.3.24" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "ryu" |
||||||
|
version = "1.0.5" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "serde" |
||||||
|
version = "1.0.119" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "9bdd36f49e35b61d49efd8aa7fc068fd295961fd2286d0b2ee9a4c7a14e99cc3" |
||||||
|
dependencies = [ |
||||||
|
"serde_derive", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "serde_derive" |
||||||
|
version = "1.0.119" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "552954ce79a059ddd5fd68c271592374bd15cab2274970380c000118aeffe1cd" |
||||||
|
dependencies = [ |
||||||
|
"proc-macro2", |
||||||
|
"quote", |
||||||
|
"syn", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "serde_json" |
||||||
|
version = "1.0.61" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a" |
||||||
|
dependencies = [ |
||||||
|
"itoa", |
||||||
|
"ryu", |
||||||
|
"serde", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "sha2" |
||||||
|
version = "0.9.2" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "6e7aab86fe2149bad8c507606bdb3f4ef5e7b2380eb92350f56122cca72a42a8" |
||||||
|
dependencies = [ |
||||||
|
"block-buffer", |
||||||
|
"cfg-if", |
||||||
|
"cpuid-bool", |
||||||
|
"digest", |
||||||
|
"opaque-debug", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "sha3" |
||||||
|
version = "0.9.1" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" |
||||||
|
dependencies = [ |
||||||
|
"block-buffer", |
||||||
|
"digest", |
||||||
|
"keccak", |
||||||
|
"opaque-debug", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "signature" |
||||||
|
version = "1.2.2" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "29f060a7d147e33490ec10da418795238fd7545bba241504d6b31a409f2e6210" |
||||||
|
dependencies = [ |
||||||
|
"digest", |
||||||
|
"rand_core 0.5.1", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "slab" |
||||||
|
version = "0.4.2" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "static_assertions" |
||||||
|
version = "1.1.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "subtle" |
||||||
|
version = "2.4.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "syn" |
||||||
|
version = "1.0.58" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "cc60a3d73ea6594cd712d830cc1f0390fd71542d8c8cd24e70cc54cdfd5e05d5" |
||||||
|
dependencies = [ |
||||||
|
"proc-macro2", |
||||||
|
"quote", |
||||||
|
"unicode-xid", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "thiserror" |
||||||
|
version = "1.0.23" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "76cc616c6abf8c8928e2fdcc0dbfab37175edd8fb49a4641066ad1364fdab146" |
||||||
|
dependencies = [ |
||||||
|
"thiserror-impl", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "thiserror-impl" |
||||||
|
version = "1.0.23" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1" |
||||||
|
dependencies = [ |
||||||
|
"proc-macro2", |
||||||
|
"quote", |
||||||
|
"syn", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "time" |
||||||
|
version = "0.1.43" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" |
||||||
|
dependencies = [ |
||||||
|
"libc", |
||||||
|
"winapi", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "tiny-keccak" |
||||||
|
version = "2.0.2" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" |
||||||
|
dependencies = [ |
||||||
|
"crunchy", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "typenum" |
||||||
|
version = "1.12.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "uint" |
||||||
|
version = "0.9.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "e11fe9a9348741cf134085ad57c249508345fe16411b3d7fb4ff2da2f1d6382e" |
||||||
|
dependencies = [ |
||||||
|
"byteorder", |
||||||
|
"crunchy", |
||||||
|
"hex", |
||||||
|
"static_assertions", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "unicode-xid" |
||||||
|
version = "0.2.1" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "uuid" |
||||||
|
version = "0.8.2" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" |
||||||
|
dependencies = [ |
||||||
|
"getrandom 0.2.1", |
||||||
|
"serde", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "version_check" |
||||||
|
version = "0.9.2" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "wasi" |
||||||
|
version = "0.9.0+wasi-snapshot-preview1" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "wasi" |
||||||
|
version = "0.10.1+wasi-snapshot-preview1" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "93c6c3420963c5c64bca373b25e77acb562081b9bb4dd5bb864187742186cea9" |
||||||
|
|
||||||
|
[[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 = "wyz" |
||||||
|
version = "0.2.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "zeroize" |
||||||
|
version = "1.2.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "81a974bcdd357f0dca4d41677db03436324d45a4c9ed2d0b873a5a360ce41c36" |
@ -0,0 +1,14 @@ |
|||||||
|
[package] |
||||||
|
name = "optics-core" |
||||||
|
version = "0.1.0" |
||||||
|
authors = ["James Prestwich <prestwich@clabs.co>"] |
||||||
|
edition = "2018" |
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
||||||
|
|
||||||
|
[dependencies] |
||||||
|
ethers-core = { git = "https://github.com/gakonst/ethers-rs" } |
||||||
|
ethers-signers = { git = "https://github.com/gakonst/ethers-rs" } |
||||||
|
sha3 = "0.9.1" |
||||||
|
lazy_static = "*" |
||||||
|
thiserror = "*" |
@ -0,0 +1,159 @@ |
|||||||
|
use crate::{ |
||||||
|
merkle::{verify_merkle_proof, MerkleTree, ZERO_HASHES}, |
||||||
|
*, |
||||||
|
}; |
||||||
|
use ethers_core::types::H256; |
||||||
|
use sha3::Keccak256; |
||||||
|
|
||||||
|
pub const TREE_DEPTH: usize = 32; |
||||||
|
pub const MAX_MESSAGES: u32 = u32::MAX; |
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub struct Proof { |
||||||
|
leaf: H256, |
||||||
|
index: usize, |
||||||
|
path: [H256; TREE_DEPTH], |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub struct IncrementalMerkle { |
||||||
|
branch: [H256; TREE_DEPTH], |
||||||
|
count: u32, |
||||||
|
} |
||||||
|
|
||||||
|
fn hash_concat(left: impl AsRef<[u8]>, right: impl AsRef<[u8]>) -> H256 { |
||||||
|
let mut k = Keccak256::new(); |
||||||
|
k.update(left.as_ref()); |
||||||
|
k.update(right.as_ref()); |
||||||
|
let digest = k.finalize(); |
||||||
|
H256::from_slice(digest.as_slice()) |
||||||
|
} |
||||||
|
|
||||||
|
impl Default for IncrementalMerkle { |
||||||
|
fn default() -> Self { |
||||||
|
let mut branch: [H256; TREE_DEPTH] = Default::default(); |
||||||
|
branch |
||||||
|
.iter_mut() |
||||||
|
.enumerate() |
||||||
|
.for_each(|(i, elem)| *elem = ZERO_HASHES[i]); |
||||||
|
Self { branch, count: 0 } |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl IncrementalMerkle { |
||||||
|
pub fn ingest(&mut self, element: H256) { |
||||||
|
let mut node = element; |
||||||
|
self.count += 1; |
||||||
|
let mut size = self.count; |
||||||
|
for i in 0..TREE_DEPTH { |
||||||
|
if (size & 1) == 1 { |
||||||
|
self.branch[i] = node; |
||||||
|
return; |
||||||
|
} |
||||||
|
node = hash_concat(self.branch[i], node); |
||||||
|
size /= 2; |
||||||
|
} |
||||||
|
unreachable!() |
||||||
|
} |
||||||
|
|
||||||
|
pub fn root(&self) -> H256 { |
||||||
|
let mut node: H256 = Default::default(); |
||||||
|
let mut size = self.count; |
||||||
|
|
||||||
|
self.branch.iter().enumerate().for_each(|(i, elem)| { |
||||||
|
node = if (size & 1) == 1 { |
||||||
|
hash_concat(elem, node) |
||||||
|
} else { |
||||||
|
hash_concat(node, ZERO_HASHES[i]) |
||||||
|
}; |
||||||
|
size /= 2; |
||||||
|
}); |
||||||
|
|
||||||
|
node |
||||||
|
} |
||||||
|
|
||||||
|
pub fn count(&self) -> u32 { |
||||||
|
self.count |
||||||
|
} |
||||||
|
|
||||||
|
pub fn branch(&self) -> &[H256; TREE_DEPTH] { |
||||||
|
&self.branch |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Debug)] |
||||||
|
pub struct IncrementalMerkleProver { |
||||||
|
light: IncrementalMerkle, |
||||||
|
full: MerkleTree, |
||||||
|
} |
||||||
|
|
||||||
|
impl Default for IncrementalMerkleProver { |
||||||
|
fn default() -> Self { |
||||||
|
let light = IncrementalMerkle::default(); |
||||||
|
let full = MerkleTree::create(&[], TREE_DEPTH); |
||||||
|
Self { light, full } |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl IncrementalMerkleProver { |
||||||
|
pub fn ingest(&mut self, element: H256) -> H256 { |
||||||
|
self.light.ingest(element); |
||||||
|
self.full.push_leaf(element, TREE_DEPTH).unwrap(); |
||||||
|
debug_assert_eq!(self.light.root(), self.full.hash()); |
||||||
|
self.full.hash() |
||||||
|
} |
||||||
|
|
||||||
|
pub fn root(&self) -> H256 { |
||||||
|
self.full.hash() |
||||||
|
} |
||||||
|
|
||||||
|
pub fn count(&self) -> u32 { |
||||||
|
self.light.count() |
||||||
|
} |
||||||
|
|
||||||
|
pub fn prove(&self, index: usize) -> Result<Proof, ()> { |
||||||
|
if index > u32::MAX as usize { |
||||||
|
return Err(()); |
||||||
|
} |
||||||
|
let (leaf, hashes) = self.full.generate_proof(index, TREE_DEPTH); |
||||||
|
let mut path = [H256::zero(); 32]; |
||||||
|
path.copy_from_slice(&hashes[..32]); |
||||||
|
Ok(Proof { leaf, index, path }) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn verify(&self, proof: &Proof) -> Result<(), ()> { |
||||||
|
if verify_merkle_proof( |
||||||
|
proof.leaf, |
||||||
|
proof.path.as_ref(), |
||||||
|
TREE_DEPTH, |
||||||
|
proof.index, |
||||||
|
self.root(), |
||||||
|
) { |
||||||
|
Ok(()) |
||||||
|
} else { |
||||||
|
Err(()) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[cfg(test)] |
||||||
|
mod test { |
||||||
|
use super::*; |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn it_test() { |
||||||
|
let mut tree = IncrementalMerkleProver::default(); |
||||||
|
|
||||||
|
let elements: Vec<_> = (1..32).map(|i| H256::repeat_byte(i as u8)).collect(); |
||||||
|
tree.ingest(elements[0]); |
||||||
|
tree.ingest(elements[1]); |
||||||
|
tree.ingest(elements[2]); |
||||||
|
|
||||||
|
assert_eq!(tree.count(), 3); |
||||||
|
|
||||||
|
let idx = 1; |
||||||
|
let proof = tree.prove(idx).unwrap(); |
||||||
|
dbg!(&proof); |
||||||
|
tree.verify(&proof).unwrap(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,157 @@ |
|||||||
|
use ethers_core::types::H256; |
||||||
|
use std::{collections::VecDeque, io::Write}; |
||||||
|
|
||||||
|
use crate::{accumulator::*, *}; |
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone)] |
||||||
|
pub struct Waiting { |
||||||
|
queue: VecDeque<H256>, |
||||||
|
accumulator: IncrementalMerkle, |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Debug, Clone)] |
||||||
|
pub struct Failed { |
||||||
|
queue: VecDeque<H256>, |
||||||
|
accumulator: IncrementalMerkle, |
||||||
|
} |
||||||
|
|
||||||
|
impl Waiting { |
||||||
|
pub fn queue(&self) -> &VecDeque<H256> { |
||||||
|
&self.queue |
||||||
|
} |
||||||
|
|
||||||
|
pub fn accumulator(&self) -> &IncrementalMerkle { |
||||||
|
&self.accumulator |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Failed { |
||||||
|
pub fn queue(&self) -> &VecDeque<H256> { |
||||||
|
&self.queue |
||||||
|
} |
||||||
|
|
||||||
|
pub fn accumulator(&self) -> &IncrementalMerkle { |
||||||
|
&self.accumulator |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fn format_message( |
||||||
|
origin: u32, |
||||||
|
sender: H256, |
||||||
|
destination: u32, |
||||||
|
recipient: H256, |
||||||
|
body: &[u8], |
||||||
|
) -> Vec<u8> { |
||||||
|
let mut buf = vec![]; |
||||||
|
buf.write_all(&origin.to_be_bytes()).unwrap(); |
||||||
|
buf.write_all(sender.as_ref()).unwrap(); |
||||||
|
buf.write_all(&destination.to_be_bytes()).unwrap(); |
||||||
|
buf.write_all(recipient.as_ref()).unwrap(); |
||||||
|
buf.write_all(&body).unwrap(); |
||||||
|
buf |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Debug, Clone)] |
||||||
|
pub struct Home<S> { |
||||||
|
origin: u32, |
||||||
|
updater: Address, |
||||||
|
current_root: H256, |
||||||
|
state: S, |
||||||
|
} |
||||||
|
|
||||||
|
impl<S> Home<S> { |
||||||
|
pub fn origin(&self) -> u32 { |
||||||
|
self.origin |
||||||
|
} |
||||||
|
|
||||||
|
pub fn updater(&self) -> Address { |
||||||
|
self.updater |
||||||
|
} |
||||||
|
|
||||||
|
pub fn state(&self) -> &S { |
||||||
|
&self.state |
||||||
|
} |
||||||
|
|
||||||
|
fn check_sig(&self, update: &SignedUpdate) -> Result<(), ()> { |
||||||
|
let signer = update.recover()?; |
||||||
|
if signer == self.updater { |
||||||
|
Ok(()) |
||||||
|
} else { |
||||||
|
Err(()) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<Home<Waiting>> for Home<Failed> { |
||||||
|
fn from(h: Home<Waiting>) -> Self { |
||||||
|
Self { |
||||||
|
origin: h.origin, |
||||||
|
updater: h.updater, |
||||||
|
current_root: h.current_root, |
||||||
|
state: Failed { |
||||||
|
accumulator: h.state.accumulator, |
||||||
|
queue: h.state.queue, |
||||||
|
}, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Home<Waiting> { |
||||||
|
pub fn init(origin: u32, updater: Address) -> Home<Waiting> { |
||||||
|
Self { |
||||||
|
origin, |
||||||
|
updater, |
||||||
|
current_root: Default::default(), |
||||||
|
state: Waiting::default(), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub fn enqueue(&mut self, sender: H256, destination: u32, recipient: H256, body: &[u8]) { |
||||||
|
let message = format_message(self.origin, sender, destination, recipient, body); |
||||||
|
let message_hash = keccak256(message); |
||||||
|
self.state.accumulator.ingest(message_hash); |
||||||
|
self.state.queue.push_back(self.state.accumulator.root()); |
||||||
|
} |
||||||
|
|
||||||
|
fn _update(&mut self, update: &Update) -> Result<(), ()> { |
||||||
|
if update.previous_root != self.current_root { |
||||||
|
return Err(()); |
||||||
|
} |
||||||
|
|
||||||
|
if self.state.queue.contains(&update.new_root) { |
||||||
|
loop { |
||||||
|
let item = self.state.queue.pop_front().unwrap(); |
||||||
|
if item == update.new_root { |
||||||
|
return Ok(()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Err(()) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn update(&mut self, update: &SignedUpdate) -> Result<(), ()> { |
||||||
|
self.check_sig(update)?; |
||||||
|
self._update(&update.update) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn double_update( |
||||||
|
self, |
||||||
|
first: &SignedUpdate, |
||||||
|
second: &SignedUpdate, |
||||||
|
) -> Result<Home<Failed>, Home<Waiting>> { |
||||||
|
if first == second || self.check_sig(first).is_err() || self.check_sig(second).is_err() { |
||||||
|
Err(self) |
||||||
|
} else { |
||||||
|
Ok(self.into()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub fn improper_update(self, update: &SignedUpdate) -> Result<Home<Failed>, Home<Waiting>> { |
||||||
|
if self.check_sig(update).is_err() || self.state.queue.contains(&update.update.new_root) { |
||||||
|
Err(self) |
||||||
|
} else { |
||||||
|
Ok(self.into()) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,135 @@ |
|||||||
|
pub mod accumulator; |
||||||
|
pub mod home; |
||||||
|
mod merkle; |
||||||
|
pub mod replica; |
||||||
|
|
||||||
|
use ethers_core::types::{Address, Signature, H256}; |
||||||
|
use ethers_signers::Signer; |
||||||
|
use sha3::{Digest, Keccak256}; |
||||||
|
|
||||||
|
pub trait Encode { |
||||||
|
fn write_to<W>(&self, writer: &mut W) -> std::io::Result<usize> |
||||||
|
where |
||||||
|
W: std::io::Write; |
||||||
|
} |
||||||
|
|
||||||
|
impl Encode for Signature { |
||||||
|
fn write_to<W>(&self, writer: &mut W) -> std::io::Result<usize> |
||||||
|
where |
||||||
|
W: std::io::Write, |
||||||
|
{ |
||||||
|
writer.write_all(&self.to_vec())?; |
||||||
|
Ok(64) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fn keccak256(buf: impl AsRef<[u8]>) -> H256 { |
||||||
|
H256::from_slice(Keccak256::digest(buf.as_ref()).as_slice()) |
||||||
|
} |
||||||
|
|
||||||
|
fn domain_hash(origin_slip44_id: u32) -> H256 { |
||||||
|
H256::from_slice( |
||||||
|
Keccak256::new() |
||||||
|
.chain(origin_slip44_id.to_be_bytes()) |
||||||
|
.chain("OPTICS".as_bytes()) |
||||||
|
.finalize() |
||||||
|
.as_slice() |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Debug, Clone)] |
||||||
|
pub struct Message { |
||||||
|
origin: u32, // 4 SLIP-44 ID
|
||||||
|
sender: H256, // 32 Address in origin convention
|
||||||
|
destination: u32, // 4 SLIP-44 ID
|
||||||
|
recipient: H256, // 32 Address in destination convention
|
||||||
|
sequence: u32, // 4 Count of all previous messages to destination
|
||||||
|
body: Vec<u8>, // 0+ Message contents
|
||||||
|
} |
||||||
|
|
||||||
|
impl Encode for Message { |
||||||
|
fn write_to<W>(&self, writer: &mut W) -> std::io::Result<usize> |
||||||
|
where |
||||||
|
W: std::io::Write, |
||||||
|
{ |
||||||
|
writer.write_all(&self.origin.to_be_bytes())?; |
||||||
|
writer.write_all(self.sender.as_ref())?; |
||||||
|
writer.write_all(&self.destination.to_be_bytes())?; |
||||||
|
writer.write_all(self.recipient.as_ref())?; |
||||||
|
writer.write_all(&self.sequence.to_be_bytes())?; |
||||||
|
Ok(36 + 36 + 4 + self.body.len()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)] |
||||||
|
pub struct Update { |
||||||
|
origin_chain: u32, |
||||||
|
previous_root: H256, |
||||||
|
new_root: H256, |
||||||
|
} |
||||||
|
|
||||||
|
impl Encode for Update { |
||||||
|
fn write_to<W>(&self, writer: &mut W) -> std::io::Result<usize> |
||||||
|
where |
||||||
|
W: std::io::Write, |
||||||
|
{ |
||||||
|
writer.write_all(&self.origin_chain.to_be_bytes())?; |
||||||
|
writer.write_all(self.previous_root.as_ref())?; |
||||||
|
writer.write_all(self.new_root.as_ref())?; |
||||||
|
Ok(4 + 32 + 32) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Update { |
||||||
|
fn signing_hash(&self) -> H256 { |
||||||
|
// sign:
|
||||||
|
// domain(origin) || previous_root || new_root
|
||||||
|
H256::from_slice( |
||||||
|
Keccak256::new() |
||||||
|
.chain(domain_hash(self.origin_chain)) |
||||||
|
.chain(self.previous_root) |
||||||
|
.chain(self.new_root) |
||||||
|
.finalize() |
||||||
|
.as_slice(), |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
pub async fn sign_update<S>(self, signer: S) -> Result<SignedUpdate, S::Error> |
||||||
|
where |
||||||
|
S: Signer, |
||||||
|
{ |
||||||
|
let signature = signer.sign_message(self.signing_hash()).await?; |
||||||
|
Ok(SignedUpdate { |
||||||
|
update: self, |
||||||
|
signature, |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// 129 bytes.
|
||||||
|
// serialized as tightly-packed, sig in RSV format
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)] |
||||||
|
pub struct SignedUpdate { |
||||||
|
update: Update, |
||||||
|
signature: Signature, |
||||||
|
} |
||||||
|
|
||||||
|
impl Encode for SignedUpdate { |
||||||
|
fn write_to<W>(&self, writer: &mut W) -> std::io::Result<usize> |
||||||
|
where |
||||||
|
W: std::io::Write, |
||||||
|
{ |
||||||
|
let mut written = 0; |
||||||
|
written += self.update.write_to(writer)?; |
||||||
|
written += self.signature.write_to(writer)?; |
||||||
|
Ok(written) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl SignedUpdate { |
||||||
|
pub fn recover(&self) -> Result<Address, ()> { |
||||||
|
self.signature |
||||||
|
.recover(self.update.signing_hash()) |
||||||
|
.map_err(|_| ()) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,634 @@ |
|||||||
|
use ethers_core::types::H256; |
||||||
|
use lazy_static::lazy_static; |
||||||
|
use sha3::{Digest, Keccak256}; |
||||||
|
use thiserror::Error; |
||||||
|
|
||||||
|
// Some code has been derived from
|
||||||
|
// https://github.com/sigp/lighthouse/blob/c6baa0eed131c5e8ecc5860778ffc7d4a4c18d2d/consensus/merkle_proof/src/lib.rs#L25
|
||||||
|
// It has been modified as follows:
|
||||||
|
// - improve legibility
|
||||||
|
// - remove eth2-specific features.
|
||||||
|
// - use keccak256
|
||||||
|
// - remove ring dependency
|
||||||
|
// In accordance with its license terms, the apache2 license is reproduced below
|
||||||
|
|
||||||
|
const MAX_TREE_DEPTH: usize = 32; |
||||||
|
const EMPTY_SLICE: &[H256] = &[]; |
||||||
|
|
||||||
|
fn hash_concat(left: impl AsRef<[u8]>, right: impl AsRef<[u8]>) -> H256 { |
||||||
|
H256::from_slice( |
||||||
|
Keccak256::new() |
||||||
|
.chain(left.as_ref()) |
||||||
|
.chain(right.as_ref()) |
||||||
|
.finalize() |
||||||
|
.as_slice(), |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
lazy_static! { |
||||||
|
/// Zero nodes to act as "synthetic" left and right subtrees of other zero nodes.
|
||||||
|
pub static ref ZERO_NODES: Vec<MerkleTree> = { |
||||||
|
(0..=MAX_TREE_DEPTH).map(MerkleTree::Zero).collect() |
||||||
|
}; |
||||||
|
|
||||||
|
pub static ref ZERO_HASHES: [H256; MAX_TREE_DEPTH + 1] = { |
||||||
|
let mut hashes = [H256::zero(); MAX_TREE_DEPTH + 1]; |
||||||
|
for i in 0..MAX_TREE_DEPTH { |
||||||
|
hashes[i + 1] = hash_concat(hashes[i], hashes[i]); |
||||||
|
} |
||||||
|
hashes |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
/// Right-sparse Merkle tree.
|
||||||
|
///
|
||||||
|
/// Efficiently represents a Merkle tree of fixed depth where only the first N
|
||||||
|
/// indices are populated by non-zero leaves (perfect for the deposit contract tree).
|
||||||
|
#[derive(Debug, PartialEq)] |
||||||
|
pub enum MerkleTree { |
||||||
|
/// Leaf node with the hash of its content.
|
||||||
|
Leaf(H256), |
||||||
|
/// Internal node with hash, left subtree and right subtree.
|
||||||
|
Node(H256, Box<Self>, Box<Self>), |
||||||
|
/// Zero subtree of a given depth.
|
||||||
|
///
|
||||||
|
/// It represents a Merkle tree of 2^depth zero leaves.
|
||||||
|
Zero(usize), |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, Error)] |
||||||
|
pub enum MerkleTreeError { |
||||||
|
// Trying to push in a leaf
|
||||||
|
#[error("Trying to push in a leaf")] |
||||||
|
LeafReached, |
||||||
|
// No more space in the MerkleTree
|
||||||
|
#[error("No more space in the MerkleTree")] |
||||||
|
MerkleTreeFull, |
||||||
|
// MerkleTree is invalid
|
||||||
|
#[error("MerkleTree is invalid")] |
||||||
|
Invalid, |
||||||
|
// Incorrect Depth provided
|
||||||
|
#[error("Incorrect Depth provided")] |
||||||
|
DepthTooSmall, |
||||||
|
} |
||||||
|
|
||||||
|
impl MerkleTree { |
||||||
|
/// Retrieve the root hash of this Merkle tree.
|
||||||
|
pub fn hash(&self) -> H256 { |
||||||
|
match *self { |
||||||
|
MerkleTree::Leaf(h) => h, |
||||||
|
MerkleTree::Node(h, _, _) => h, |
||||||
|
MerkleTree::Zero(depth) => ZERO_HASHES[depth], |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Create a new Merkle tree from a list of leaves and a fixed depth.
|
||||||
|
pub fn create(leaves: &[H256], depth: usize) -> Self { |
||||||
|
use MerkleTree::*; |
||||||
|
|
||||||
|
if leaves.is_empty() { |
||||||
|
return Zero(depth); |
||||||
|
} |
||||||
|
|
||||||
|
match depth { |
||||||
|
0 => { |
||||||
|
debug_assert_eq!(leaves.len(), 1); |
||||||
|
Leaf(leaves[0]) |
||||||
|
} |
||||||
|
_ => { |
||||||
|
// Split leaves into left and right subtrees
|
||||||
|
let subtree_capacity = 2usize.pow(depth as u32 - 1); |
||||||
|
let (left_leaves, right_leaves) = if leaves.len() <= subtree_capacity { |
||||||
|
(leaves, EMPTY_SLICE) |
||||||
|
} else { |
||||||
|
leaves.split_at(subtree_capacity) |
||||||
|
}; |
||||||
|
|
||||||
|
let left_subtree = MerkleTree::create(left_leaves, depth - 1); |
||||||
|
let right_subtree = MerkleTree::create(right_leaves, depth - 1); |
||||||
|
let hash = hash_concat(left_subtree.hash(), right_subtree.hash()); |
||||||
|
|
||||||
|
Node(hash, Box::new(left_subtree), Box::new(right_subtree)) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Push an element in the MerkleTree.
|
||||||
|
/// MerkleTree and depth must be correct, as the algorithm expects valid data.
|
||||||
|
pub fn push_leaf(&mut self, elem: H256, depth: usize) -> Result<(), MerkleTreeError> { |
||||||
|
use MerkleTree::*; |
||||||
|
|
||||||
|
if depth == 0 { |
||||||
|
return Err(MerkleTreeError::DepthTooSmall); |
||||||
|
} |
||||||
|
|
||||||
|
match self { |
||||||
|
Leaf(_) => return Err(MerkleTreeError::LeafReached), |
||||||
|
Zero(_) => { |
||||||
|
*self = MerkleTree::create(&[elem], depth); |
||||||
|
} |
||||||
|
Node(ref mut hash, ref mut left, ref mut right) => { |
||||||
|
let left: &mut MerkleTree = &mut *left; |
||||||
|
let right: &mut MerkleTree = &mut *right; |
||||||
|
match (&*left, &*right) { |
||||||
|
// Tree is full
|
||||||
|
(Leaf(_), Leaf(_)) => return Err(MerkleTreeError::MerkleTreeFull), |
||||||
|
// There is a right node so insert in right node
|
||||||
|
(Node(_, _, _), Node(_, _, _)) => { |
||||||
|
if let Err(e) = right.push_leaf(elem, depth - 1) { |
||||||
|
return Err(e); |
||||||
|
} |
||||||
|
} |
||||||
|
// Both branches are zero, insert in left one
|
||||||
|
(Zero(_), Zero(_)) => { |
||||||
|
*left = MerkleTree::create(&[elem], depth - 1); |
||||||
|
} |
||||||
|
// Leaf on left branch and zero on right branch, insert on right side
|
||||||
|
(Leaf(_), Zero(_)) => { |
||||||
|
*right = MerkleTree::create(&[elem], depth - 1); |
||||||
|
} |
||||||
|
// Try inserting on the left node -> if it fails because it is full, insert in right side.
|
||||||
|
(Node(_, _, _), Zero(_)) => { |
||||||
|
match left.push_leaf(elem, depth - 1) { |
||||||
|
Ok(_) => (), |
||||||
|
// Left node is full, insert in right node
|
||||||
|
Err(MerkleTreeError::MerkleTreeFull) => { |
||||||
|
*right = MerkleTree::create(&[elem], depth - 1); |
||||||
|
} |
||||||
|
Err(e) => return Err(e), |
||||||
|
}; |
||||||
|
} |
||||||
|
// All other possibilities are invalid MerkleTrees
|
||||||
|
(_, _) => return Err(MerkleTreeError::Invalid), |
||||||
|
}; |
||||||
|
hash.assign_from_slice(hash_concat(left.hash(), right.hash()).as_ref()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
/// Get a reference to the left and right subtrees if they exist.
|
||||||
|
pub fn left_and_right_branches(&self) -> Option<(&Self, &Self)> { |
||||||
|
match *self { |
||||||
|
MerkleTree::Leaf(_) | MerkleTree::Zero(0) => None, |
||||||
|
MerkleTree::Node(_, ref l, ref r) => Some((l, r)), |
||||||
|
MerkleTree::Zero(depth) => Some((&ZERO_NODES[depth - 1], &ZERO_NODES[depth - 1])), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Is this Merkle tree a leaf?
|
||||||
|
pub fn is_leaf(&self) -> bool { |
||||||
|
matches!(self, MerkleTree::Leaf(_)) |
||||||
|
} |
||||||
|
|
||||||
|
/// Return the leaf at `index` and a Merkle proof of its inclusion.
|
||||||
|
///
|
||||||
|
/// The Merkle proof is in "bottom-up" order, starting with a leaf node
|
||||||
|
/// and moving up the tree. Its length will be exactly equal to `depth`.
|
||||||
|
pub fn generate_proof(&self, index: usize, depth: usize) -> (H256, Vec<H256>) { |
||||||
|
let mut proof = vec![]; |
||||||
|
let mut current_node = self; |
||||||
|
let mut current_depth = depth; |
||||||
|
while current_depth > 0 { |
||||||
|
let ith_bit = (index >> (current_depth - 1)) & 0x01; |
||||||
|
// Note: unwrap is safe because leaves are only ever constructed at depth == 0.
|
||||||
|
let (left, right) = current_node.left_and_right_branches().unwrap(); |
||||||
|
|
||||||
|
// Go right, include the left branch in the proof.
|
||||||
|
if ith_bit == 1 { |
||||||
|
proof.push(left.hash()); |
||||||
|
current_node = right; |
||||||
|
} else { |
||||||
|
proof.push(right.hash()); |
||||||
|
current_node = left; |
||||||
|
} |
||||||
|
current_depth -= 1; |
||||||
|
} |
||||||
|
|
||||||
|
debug_assert_eq!(proof.len(), depth); |
||||||
|
debug_assert!(current_node.is_leaf()); |
||||||
|
|
||||||
|
// Put proof in bottom-up order.
|
||||||
|
proof.reverse(); |
||||||
|
|
||||||
|
(current_node.hash(), proof) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Verify a proof that `leaf` exists at `index` in a Merkle tree rooted at `root`.
|
||||||
|
///
|
||||||
|
/// The `branch` argument is the main component of the proof: it should be a list of internal
|
||||||
|
/// node hashes such that the root can be reconstructed (in bottom-up order).
|
||||||
|
pub fn verify_merkle_proof( |
||||||
|
leaf: H256, |
||||||
|
branch: &[H256], |
||||||
|
depth: usize, |
||||||
|
index: usize, |
||||||
|
root: H256, |
||||||
|
) -> bool { |
||||||
|
if branch.len() == depth { |
||||||
|
merkle_root_from_branch(leaf, branch, depth, index) == root |
||||||
|
} else { |
||||||
|
false |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Compute a root hash from a leaf and a Merkle proof.
|
||||||
|
fn merkle_root_from_branch(leaf: H256, branch: &[H256], depth: usize, index: usize) -> H256 { |
||||||
|
assert_eq!(branch.len(), depth, "proof length should equal depth"); |
||||||
|
|
||||||
|
let mut merkle_root = leaf; |
||||||
|
|
||||||
|
for (i, leaf) in branch.iter().enumerate().take(depth) { |
||||||
|
let ith_bit = (index >> i) & 0x01; |
||||||
|
if ith_bit == 1 { |
||||||
|
merkle_root = hash_concat(leaf, merkle_root); |
||||||
|
} else { |
||||||
|
merkle_root = hash_concat(merkle_root, leaf); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
merkle_root |
||||||
|
} |
||||||
|
|
||||||
|
#[cfg(test)] |
||||||
|
mod tests { |
||||||
|
use super::*; |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn sparse_zero_correct() { |
||||||
|
let depth = 2; |
||||||
|
let zero = H256::from([0x00; 32]); |
||||||
|
let dense_tree = MerkleTree::create(&[zero, zero, zero, zero], depth); |
||||||
|
let sparse_tree = MerkleTree::create(&[], depth); |
||||||
|
assert_eq!(dense_tree.hash(), sparse_tree.hash()); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn create_small_example() { |
||||||
|
// Construct a small merkle tree manually and check that it's consistent with
|
||||||
|
// the MerkleTree type.
|
||||||
|
let leaf_b00 = H256::from([0xAA; 32]); |
||||||
|
let leaf_b01 = H256::from([0xBB; 32]); |
||||||
|
let leaf_b10 = H256::from([0xCC; 32]); |
||||||
|
let leaf_b11 = H256::from([0xDD; 32]); |
||||||
|
|
||||||
|
let node_b0x = hash_concat(leaf_b00, leaf_b01); |
||||||
|
let node_b1x = hash_concat(leaf_b10, leaf_b11); |
||||||
|
|
||||||
|
let root = hash_concat(node_b0x, node_b1x); |
||||||
|
|
||||||
|
let tree = MerkleTree::create(&[leaf_b00, leaf_b01, leaf_b10, leaf_b11], 2); |
||||||
|
assert_eq!(tree.hash(), root); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn verify_small_example() { |
||||||
|
// Construct a small merkle tree manually
|
||||||
|
let leaf_b00 = H256::from([0xAA; 32]); |
||||||
|
let leaf_b01 = H256::from([0xBB; 32]); |
||||||
|
let leaf_b10 = H256::from([0xCC; 32]); |
||||||
|
let leaf_b11 = H256::from([0xDD; 32]); |
||||||
|
|
||||||
|
let node_b0x = hash_concat(leaf_b00, leaf_b01); |
||||||
|
let node_b1x = hash_concat(leaf_b10, leaf_b11); |
||||||
|
|
||||||
|
let root = hash_concat(node_b0x, node_b1x); |
||||||
|
|
||||||
|
// Run some proofs
|
||||||
|
assert!(verify_merkle_proof( |
||||||
|
leaf_b00, |
||||||
|
&[leaf_b01, node_b1x], |
||||||
|
2, |
||||||
|
0b00, |
||||||
|
root |
||||||
|
)); |
||||||
|
assert!(verify_merkle_proof( |
||||||
|
leaf_b01, |
||||||
|
&[leaf_b00, node_b1x], |
||||||
|
2, |
||||||
|
0b01, |
||||||
|
root |
||||||
|
)); |
||||||
|
assert!(verify_merkle_proof( |
||||||
|
leaf_b10, |
||||||
|
&[leaf_b11, node_b0x], |
||||||
|
2, |
||||||
|
0b10, |
||||||
|
root |
||||||
|
)); |
||||||
|
assert!(verify_merkle_proof( |
||||||
|
leaf_b11, |
||||||
|
&[leaf_b10, node_b0x], |
||||||
|
2, |
||||||
|
0b11, |
||||||
|
root |
||||||
|
)); |
||||||
|
assert!(verify_merkle_proof( |
||||||
|
leaf_b11, |
||||||
|
&[leaf_b10], |
||||||
|
1, |
||||||
|
0b11, |
||||||
|
node_b1x |
||||||
|
)); |
||||||
|
|
||||||
|
// Ensure that incorrect proofs fail
|
||||||
|
// Zero-length proof
|
||||||
|
assert!(!verify_merkle_proof(leaf_b01, &[], 2, 0b01, root)); |
||||||
|
// Proof in reverse order
|
||||||
|
assert!(!verify_merkle_proof( |
||||||
|
leaf_b01, |
||||||
|
&[node_b1x, leaf_b00], |
||||||
|
2, |
||||||
|
0b01, |
||||||
|
root |
||||||
|
)); |
||||||
|
// Proof too short
|
||||||
|
assert!(!verify_merkle_proof(leaf_b01, &[leaf_b00], 2, 0b01, root)); |
||||||
|
// Wrong index
|
||||||
|
assert!(!verify_merkle_proof( |
||||||
|
leaf_b01, |
||||||
|
&[leaf_b00, node_b1x], |
||||||
|
2, |
||||||
|
0b10, |
||||||
|
root |
||||||
|
)); |
||||||
|
// Wrong root
|
||||||
|
assert!(!verify_merkle_proof( |
||||||
|
leaf_b01, |
||||||
|
&[leaf_b00, node_b1x], |
||||||
|
2, |
||||||
|
0b01, |
||||||
|
node_b1x |
||||||
|
)); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn verify_zero_depth() { |
||||||
|
let leaf = H256::from([0xD6; 32]); |
||||||
|
let junk = H256::from([0xD7; 32]); |
||||||
|
assert!(verify_merkle_proof(leaf, &[], 0, 0, leaf)); |
||||||
|
assert!(!verify_merkle_proof(leaf, &[], 0, 7, junk)); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn push_complete_example() { |
||||||
|
let depth = 2; |
||||||
|
let mut tree = MerkleTree::create(&[], depth); |
||||||
|
|
||||||
|
let leaf_b00 = H256::from([0xAA; 32]); |
||||||
|
|
||||||
|
let res = tree.push_leaf(leaf_b00, 0); |
||||||
|
assert_eq!(res, Err(MerkleTreeError::DepthTooSmall)); |
||||||
|
let expected_tree = MerkleTree::create(&[], depth); |
||||||
|
assert_eq!(tree.hash(), expected_tree.hash()); |
||||||
|
|
||||||
|
tree.push_leaf(leaf_b00, depth) |
||||||
|
.expect("Pushing in empty tree failed"); |
||||||
|
let expected_tree = MerkleTree::create(&[leaf_b00], depth); |
||||||
|
assert_eq!(tree.hash(), expected_tree.hash()); |
||||||
|
|
||||||
|
let leaf_b01 = H256::from([0xBB; 32]); |
||||||
|
tree.push_leaf(leaf_b01, depth) |
||||||
|
.expect("Pushing in left then right node failed"); |
||||||
|
let expected_tree = MerkleTree::create(&[leaf_b00, leaf_b01], depth); |
||||||
|
assert_eq!(tree.hash(), expected_tree.hash()); |
||||||
|
|
||||||
|
let leaf_b10 = H256::from([0xCC; 32]); |
||||||
|
tree.push_leaf(leaf_b10, depth) |
||||||
|
.expect("Pushing in right then left node failed"); |
||||||
|
let expected_tree = MerkleTree::create(&[leaf_b00, leaf_b01, leaf_b10], depth); |
||||||
|
assert_eq!(tree.hash(), expected_tree.hash()); |
||||||
|
|
||||||
|
let leaf_b11 = H256::from([0xDD; 32]); |
||||||
|
tree.push_leaf(leaf_b11, depth) |
||||||
|
.expect("Pushing in outtermost leaf failed"); |
||||||
|
let expected_tree = MerkleTree::create(&[leaf_b00, leaf_b01, leaf_b10, leaf_b11], depth); |
||||||
|
assert_eq!(tree.hash(), expected_tree.hash()); |
||||||
|
|
||||||
|
let leaf_b12 = H256::from([0xEE; 32]); |
||||||
|
let res = tree.push_leaf(leaf_b12, depth); |
||||||
|
assert_eq!(res, Err(MerkleTreeError::MerkleTreeFull)); |
||||||
|
assert_eq!(tree.hash(), expected_tree.hash()); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn big_test() { |
||||||
|
let leaves: Vec<_> = (0..64).map(|i| H256::from_low_u64_be(i)).collect(); |
||||||
|
|
||||||
|
let mut tree = MerkleTree::create(&[], 32); |
||||||
|
leaves.iter().for_each(|leaf| { |
||||||
|
tree.push_leaf(*leaf, 32).unwrap(); |
||||||
|
}); |
||||||
|
|
||||||
|
leaves.iter().enumerate().for_each(|(i, leaf)| { |
||||||
|
let (l, proof) = tree.generate_proof(i, 32); |
||||||
|
assert_eq!(l, *leaf); |
||||||
|
assert!(verify_merkle_proof(*leaf, &proof, 32, i, tree.hash())); |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
Apache License |
||||||
|
Version 2.0, January 2004 |
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION |
||||||
|
|
||||||
|
1. Definitions. |
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction, |
||||||
|
and distribution as defined by Sections 1 through 9 of this document. |
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by |
||||||
|
the copyright owner that is granting the License. |
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all |
||||||
|
other entities that control, are controlled by, or are under common |
||||||
|
control with that entity. For the purposes of this definition, |
||||||
|
"control" means (i) the power, direct or indirect, to cause the |
||||||
|
direction or management of such entity, whether by contract or |
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the |
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity. |
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity |
||||||
|
exercising permissions granted by this License. |
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications, |
||||||
|
including but not limited to software source code, documentation |
||||||
|
source, and configuration files. |
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical |
||||||
|
transformation or translation of a Source form, including but |
||||||
|
not limited to compiled object code, generated documentation, |
||||||
|
and conversions to other media types. |
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or |
||||||
|
Object form, made available under the License, as indicated by a |
||||||
|
copyright notice that is included in or attached to the work |
||||||
|
(an example is provided in the Appendix below). |
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object |
||||||
|
form, that is based on (or derived from) the Work and for which the |
||||||
|
editorial revisions, annotations, elaborations, or other modifications |
||||||
|
represent, as a whole, an original work of authorship. For the purposes |
||||||
|
of this License, Derivative Works shall not include works that remain |
||||||
|
separable from, or merely link (or bind by name) to the interfaces of, |
||||||
|
the Work and Derivative Works thereof. |
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including |
||||||
|
the original version of the Work and any modifications or additions |
||||||
|
to that Work or Derivative Works thereof, that is intentionally |
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner |
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of |
||||||
|
the copyright owner. For the purposes of this definition, "submitted" |
||||||
|
means any form of electronic, verbal, or written communication sent |
||||||
|
to the Licensor or its representatives, including but not limited to |
||||||
|
communication on electronic mailing lists, source code control systems, |
||||||
|
and issue tracking systems that are managed by, or on behalf of, the |
||||||
|
Licensor for the purpose of discussing and improving the Work, but |
||||||
|
excluding communication that is conspicuously marked or otherwise |
||||||
|
designated in writing by the copyright owner as "Not a Contribution." |
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity |
||||||
|
on behalf of whom a Contribution has been received by Licensor and |
||||||
|
subsequently incorporated within the Work. |
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of |
||||||
|
this License, each Contributor hereby grants to You a perpetual, |
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
||||||
|
copyright license to reproduce, prepare Derivative Works of, |
||||||
|
publicly display, publicly perform, sublicense, and distribute the |
||||||
|
Work and such Derivative Works in Source or Object form. |
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of |
||||||
|
this License, each Contributor hereby grants to You a perpetual, |
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
||||||
|
(except as stated in this section) patent license to make, have made, |
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work, |
||||||
|
where such license applies only to those patent claims licensable |
||||||
|
by such Contributor that are necessarily infringed by their |
||||||
|
Contribution(s) alone or by combination of their Contribution(s) |
||||||
|
with the Work to which such Contribution(s) was submitted. If You |
||||||
|
institute patent litigation against any entity (including a |
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work |
||||||
|
or a Contribution incorporated within the Work constitutes direct |
||||||
|
or contributory patent infringement, then any patent licenses |
||||||
|
granted to You under this License for that Work shall terminate |
||||||
|
as of the date such litigation is filed. |
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the |
||||||
|
Work or Derivative Works thereof in any medium, with or without |
||||||
|
modifications, and in Source or Object form, provided that You |
||||||
|
meet the following conditions: |
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or |
||||||
|
Derivative Works a copy of this License; and |
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices |
||||||
|
stating that You changed the files; and |
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works |
||||||
|
that You distribute, all copyright, patent, trademark, and |
||||||
|
attribution notices from the Source form of the Work, |
||||||
|
excluding those notices that do not pertain to any part of |
||||||
|
the Derivative Works; and |
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its |
||||||
|
distribution, then any Derivative Works that You distribute must |
||||||
|
include a readable copy of the attribution notices contained |
||||||
|
within such NOTICE file, excluding those notices that do not |
||||||
|
pertain to any part of the Derivative Works, in at least one |
||||||
|
of the following places: within a NOTICE text file distributed |
||||||
|
as part of the Derivative Works; within the Source form or |
||||||
|
documentation, if provided along with the Derivative Works; or, |
||||||
|
within a display generated by the Derivative Works, if and |
||||||
|
wherever such third-party notices normally appear. The contents |
||||||
|
of the NOTICE file are for informational purposes only and |
||||||
|
do not modify the License. You may add Your own attribution |
||||||
|
notices within Derivative Works that You distribute, alongside |
||||||
|
or as an addendum to the NOTICE text from the Work, provided |
||||||
|
that such additional attribution notices cannot be construed |
||||||
|
as modifying the License. |
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and |
||||||
|
may provide additional or different license terms and conditions |
||||||
|
for use, reproduction, or distribution of Your modifications, or |
||||||
|
for any such Derivative Works as a whole, provided Your use, |
||||||
|
reproduction, and distribution of the Work otherwise complies with |
||||||
|
the conditions stated in this License. |
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise, |
||||||
|
any Contribution intentionally submitted for inclusion in the Work |
||||||
|
by You to the Licensor shall be under the terms and conditions of |
||||||
|
this License, without any additional terms or conditions. |
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify |
||||||
|
the terms of any separate license agreement you may have executed |
||||||
|
with Licensor regarding such Contributions. |
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade |
||||||
|
names, trademarks, service marks, or product names of the Licensor, |
||||||
|
except as required for reasonable and customary use in describing the |
||||||
|
origin of the Work and reproducing the content of the NOTICE file. |
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or |
||||||
|
agreed to in writing, Licensor provides the Work (and each |
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS, |
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
||||||
|
implied, including, without limitation, any warranties or conditions |
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A |
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the |
||||||
|
appropriateness of using or redistributing the Work and assume any |
||||||
|
risks associated with Your exercise of permissions under this License. |
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory, |
||||||
|
whether in tort (including negligence), contract, or otherwise, |
||||||
|
unless required by applicable law (such as deliberate and grossly |
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be |
||||||
|
liable to You for damages, including any direct, indirect, special, |
||||||
|
incidental, or consequential damages of any character arising as a |
||||||
|
result of this License or out of the use or inability to use the |
||||||
|
Work (including but not limited to damages for loss of goodwill, |
||||||
|
work stoppage, computer failure or malfunction, or any and all |
||||||
|
other commercial damages or losses), even if such Contributor |
||||||
|
has been advised of the possibility of such damages. |
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing |
||||||
|
the Work or Derivative Works thereof, You may choose to offer, |
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity, |
||||||
|
or other liability obligations and/or rights consistent with this |
||||||
|
License. However, in accepting such obligations, You may act only |
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf |
||||||
|
of any other Contributor, and only if You agree to indemnify, |
||||||
|
defend, and hold each Contributor harmless for any liability |
||||||
|
incurred by, or claims asserted against, such Contributor by reason |
||||||
|
of your accepting any such warranty or additional liability. |
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS |
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work. |
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following |
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]" |
||||||
|
replaced with your own identifying information. (Don't include |
||||||
|
the brackets!) The text should be enclosed in the appropriate |
||||||
|
comment syntax for the file format. We also recommend that a |
||||||
|
file or class name and description of purpose be included on the |
||||||
|
same "printed page" as the copyright notice for easier |
||||||
|
identification within third-party archives. |
||||||
|
|
||||||
|
Copyright 2018 Sigma Prime Pty Ltd |
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
you may not use this file except in compliance with the License. |
||||||
|
You may obtain a copy of the License at |
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software |
||||||
|
distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
See the License for the specific language governing permissions and |
||||||
|
limitations under the License. |
||||||
|
*/ |
@ -0,0 +1,127 @@ |
|||||||
|
use crate::SignedUpdate; |
||||||
|
use ethers_core::types::{Address, H256, U256}; |
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Default)] |
||||||
|
pub struct Waiting { |
||||||
|
root: H256, |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub struct Pending { |
||||||
|
root: H256, |
||||||
|
new_root: H256, |
||||||
|
timeout: U256, |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub struct Failed {} |
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Default)] |
||||||
|
pub struct Replica<S> { |
||||||
|
origin: u32, |
||||||
|
local: u32, |
||||||
|
updater: Address, |
||||||
|
optimistic_wait: U256, |
||||||
|
state: S, |
||||||
|
} |
||||||
|
|
||||||
|
impl<S> Replica<S> { |
||||||
|
pub fn origin(&self) -> u32 { |
||||||
|
self.origin |
||||||
|
} |
||||||
|
|
||||||
|
pub fn local(&self) -> u32 { |
||||||
|
self.local |
||||||
|
} |
||||||
|
|
||||||
|
pub fn updater(&self) -> Address { |
||||||
|
self.updater |
||||||
|
} |
||||||
|
|
||||||
|
pub fn wait(&self) -> U256 { |
||||||
|
self.optimistic_wait |
||||||
|
} |
||||||
|
|
||||||
|
pub fn state(&self) -> &S { |
||||||
|
&self.state |
||||||
|
} |
||||||
|
|
||||||
|
fn check_sig(&self, update: &SignedUpdate) -> Result<(), ()> { |
||||||
|
let signer = update.recover()?; |
||||||
|
if signer == self.updater { |
||||||
|
Ok(()) |
||||||
|
} else { |
||||||
|
Err(()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub fn double_update( |
||||||
|
self, |
||||||
|
first: &SignedUpdate, |
||||||
|
second: &SignedUpdate, |
||||||
|
) -> Result<Replica<Failed>, Self> { |
||||||
|
if first == second || self.check_sig(first).is_err() || self.check_sig(second).is_err() { |
||||||
|
Err(self) |
||||||
|
} else { |
||||||
|
Ok(Replica { |
||||||
|
origin: self.origin, |
||||||
|
local: self.local, |
||||||
|
updater: self.updater, |
||||||
|
optimistic_wait: self.optimistic_wait, |
||||||
|
state: Failed {}, |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Replica<Waiting> { |
||||||
|
pub fn init(origin: u32, local: u32, updater: Address, optimistic_wait: U256) -> Self { |
||||||
|
Self { |
||||||
|
origin, |
||||||
|
local, |
||||||
|
updater, |
||||||
|
optimistic_wait, |
||||||
|
state: Waiting::default(), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub fn update( |
||||||
|
self, |
||||||
|
update: &SignedUpdate, |
||||||
|
now: impl FnOnce() -> U256, |
||||||
|
) -> Result<Replica<Pending>, Replica<Waiting>> { |
||||||
|
if self.check_sig(update).is_err() { |
||||||
|
return Err(self); |
||||||
|
} |
||||||
|
|
||||||
|
Ok(Replica { |
||||||
|
origin: self.origin, |
||||||
|
local: self.local, |
||||||
|
updater: self.updater, |
||||||
|
optimistic_wait: self.optimistic_wait, |
||||||
|
state: Pending { |
||||||
|
root: self.state.root, |
||||||
|
new_root: update.update.new_root, |
||||||
|
timeout: now() + self.optimistic_wait, |
||||||
|
}, |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Replica<Pending> { |
||||||
|
pub fn confirm_update(self, now: impl FnOnce() -> U256) -> Result<Replica<Waiting>, Self> { |
||||||
|
if self.state.timeout < now() { |
||||||
|
return Err(self); |
||||||
|
} |
||||||
|
|
||||||
|
Ok(Replica { |
||||||
|
origin: self.origin, |
||||||
|
local: self.local, |
||||||
|
updater: self.updater, |
||||||
|
optimistic_wait: self.optimistic_wait, |
||||||
|
state: Waiting { |
||||||
|
root: self.state.new_root, |
||||||
|
}, |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,3 @@ |
|||||||
|
node_modules/ |
||||||
|
cache/ |
||||||
|
artifacts/ |
@ -0,0 +1,15 @@ |
|||||||
|
{ |
||||||
|
"overrides": [ |
||||||
|
{ |
||||||
|
"files": "*.sol", |
||||||
|
"options": { |
||||||
|
"printWidth": 80, |
||||||
|
"tabWidth": 4, |
||||||
|
"useTabs": false, |
||||||
|
"singleQuote": false, |
||||||
|
"bracketSpacing": false, |
||||||
|
"explicitTypes": "always" |
||||||
|
} |
||||||
|
} |
||||||
|
] |
||||||
|
} |
@ -0,0 +1,115 @@ |
|||||||
|
// SPDX-License-Identifier: MIT OR Apache-2.0 |
||||||
|
pragma solidity >=0.6.11; |
||||||
|
|
||||||
|
import "@openzeppelin/contracts/cryptography/ECDSA.sol"; |
||||||
|
|
||||||
|
import "./Merkle.sol"; |
||||||
|
import "./Queue.sol"; |
||||||
|
|
||||||
|
contract Home is MerkleTreeManager, QueueManager { |
||||||
|
using QueueLib for QueueLib.Queue; |
||||||
|
using MerkleLib for MerkleLib.Tree; |
||||||
|
|
||||||
|
enum States {WAITING, FAILED} |
||||||
|
|
||||||
|
States public state; |
||||||
|
uint32 public immutable originSLIP44; |
||||||
|
bytes32 public immutable DOMAIN_HASH; |
||||||
|
address public updater; |
||||||
|
event DoubleUpdate(); |
||||||
|
event ImproperUpdate(); |
||||||
|
|
||||||
|
modifier notFailed() { |
||||||
|
require(state == States.WAITING); |
||||||
|
_; |
||||||
|
} |
||||||
|
|
||||||
|
constructor(uint32 _originSLIP44, address _updater) |
||||||
|
MerkleTreeManager() |
||||||
|
QueueManager() |
||||||
|
{ |
||||||
|
DOMAIN_HASH = keccak256(abi.encodePacked(_originSLIP44, "OPTICS")); |
||||||
|
updater = _updater; |
||||||
|
originSLIP44 = _originSLIP44; |
||||||
|
state = States.WAITING; |
||||||
|
} |
||||||
|
|
||||||
|
// TODO |
||||||
|
function fail() internal { |
||||||
|
state = States.FAILED; |
||||||
|
require(false, "not implemented: slashing"); |
||||||
|
} |
||||||
|
|
||||||
|
function checkSig( |
||||||
|
bytes32 _newRoot, |
||||||
|
bytes32 _oldRoot, |
||||||
|
bytes memory _signature |
||||||
|
) internal view returns (bool) { |
||||||
|
bytes32 _digest = |
||||||
|
keccak256(abi.encodePacked(DOMAIN_HASH, _oldRoot, _newRoot)); |
||||||
|
_digest = ECDSA.toEthSignedMessageHash(_digest); |
||||||
|
return ECDSA.recover(_digest, _signature) == updater; |
||||||
|
} |
||||||
|
|
||||||
|
function enqueue( |
||||||
|
uint32 destination, |
||||||
|
bytes32 recipient, |
||||||
|
bytes memory body |
||||||
|
) external notFailed { |
||||||
|
bytes32 _digest = |
||||||
|
keccak256( |
||||||
|
abi.encodePacked( |
||||||
|
originSLIP44, |
||||||
|
bytes32(uint256(uint160(msg.sender))), |
||||||
|
destination, |
||||||
|
recipient, |
||||||
|
body |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
tree.insert(_digest); |
||||||
|
queue.enqueue(root()); |
||||||
|
} |
||||||
|
|
||||||
|
function update( |
||||||
|
bytes32 _newRoot, |
||||||
|
bytes32 _oldRoot, |
||||||
|
bytes memory _signature |
||||||
|
) external notFailed { |
||||||
|
if (improperUpdate(_newRoot, _oldRoot, _signature)) return; |
||||||
|
while (true) { |
||||||
|
bytes32 next = queue.dequeue(); |
||||||
|
if (next == _newRoot) break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function doubleUpdate( |
||||||
|
bytes32[2] calldata _newRoot, |
||||||
|
bytes32[2] calldata _oldRoot, |
||||||
|
bytes calldata _signature, |
||||||
|
bytes calldata _signature2 |
||||||
|
) external notFailed { |
||||||
|
if ( |
||||||
|
checkSig(_newRoot[0], _oldRoot[0], _signature) && |
||||||
|
checkSig(_newRoot[1], _oldRoot[1], _signature2) && |
||||||
|
(_newRoot[0] != _newRoot[1] || _oldRoot[0] != _oldRoot[1]) |
||||||
|
) { |
||||||
|
fail(); |
||||||
|
emit DoubleUpdate(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function improperUpdate( |
||||||
|
bytes32 _newRoot, |
||||||
|
bytes32 _oldRoot, |
||||||
|
bytes memory _signature |
||||||
|
) public notFailed returns (bool) { |
||||||
|
require(checkSig(_newRoot, _oldRoot, _signature), "bad sig"); |
||||||
|
if (!queue.contains(_newRoot)) { |
||||||
|
fail(); |
||||||
|
emit ImproperUpdate(); |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,67 @@ |
|||||||
|
// SPDX-License-Identifier: MIT OR Apache-2.0 |
||||||
|
|
||||||
|
// work based on eth2 deposit contract, which is used under CC0-1.0 |
||||||
|
|
||||||
|
pragma solidity >=0.6.11; |
||||||
|
|
||||||
|
library MerkleLib { |
||||||
|
uint256 constant TREE_DEPTH = 32; |
||||||
|
uint256 constant MAX_LEAVES = 2**TREE_DEPTH - 1; |
||||||
|
|
||||||
|
struct Tree { |
||||||
|
bytes32[TREE_DEPTH] branch; |
||||||
|
uint256 count; |
||||||
|
} |
||||||
|
|
||||||
|
function root(Tree storage _tree, bytes32[TREE_DEPTH] storage zero_hashes) |
||||||
|
internal |
||||||
|
view |
||||||
|
returns (bytes32 node) |
||||||
|
{ |
||||||
|
uint256 size = _tree.count; |
||||||
|
for (uint256 i = 0; i < TREE_DEPTH; i++) { |
||||||
|
if ((size & 1) == 1) |
||||||
|
node = sha256(abi.encodePacked(_tree.branch[i], node)); |
||||||
|
else node = sha256(abi.encodePacked(node, zero_hashes[i])); |
||||||
|
size /= 2; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function insert(Tree storage _tree, bytes32 node) internal { |
||||||
|
require(_tree.count < MAX_LEAVES, "merkle tree full"); |
||||||
|
|
||||||
|
_tree.count += 1; |
||||||
|
uint256 size = _tree.count; |
||||||
|
for (uint256 i = 0; i < TREE_DEPTH; i++) { |
||||||
|
if ((size & 1) == 1) { |
||||||
|
_tree.branch[i] = node; |
||||||
|
return; |
||||||
|
} |
||||||
|
node = sha256(abi.encodePacked(_tree.branch[i], node)); |
||||||
|
size /= 2; |
||||||
|
} |
||||||
|
// As the loop should always end prematurely with the `return` statement, |
||||||
|
// this code should be unreachable. We assert `false` just to be safe. |
||||||
|
assert(false); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
contract MerkleTreeManager { |
||||||
|
using MerkleLib for MerkleLib.Tree; |
||||||
|
uint256 constant TREE_DEPTH = 32; |
||||||
|
|
||||||
|
bytes32[TREE_DEPTH] internal zero_hashes; |
||||||
|
MerkleLib.Tree public tree; |
||||||
|
|
||||||
|
constructor() { |
||||||
|
// Compute hashes in empty sparse Merkle tree |
||||||
|
for (uint256 i = 0; i < MerkleLib.TREE_DEPTH - 1; i++) |
||||||
|
zero_hashes[i + 1] = sha256( |
||||||
|
abi.encodePacked(zero_hashes[i], zero_hashes[i]) |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
function root() public view returns (bytes32) { |
||||||
|
return tree.root(zero_hashes); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,57 @@ |
|||||||
|
// SPDX-License-Identifier: MIT OR Apache-2.0 |
||||||
|
pragma solidity >=0.6.11; |
||||||
|
|
||||||
|
library QueueLib { |
||||||
|
struct Queue { |
||||||
|
uint256 first; |
||||||
|
uint256 last; |
||||||
|
mapping(uint256 => bytes32) queue; |
||||||
|
} |
||||||
|
|
||||||
|
function init(Queue storage _q) internal { |
||||||
|
if (_q.first == 0) { |
||||||
|
_q.first = 1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function contains(Queue storage _q, bytes32 _item) |
||||||
|
internal |
||||||
|
view |
||||||
|
returns (bool) |
||||||
|
{ |
||||||
|
for (uint256 i = _q.first; i < _q.last; i++) { |
||||||
|
if (_q.queue[i] == _item) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
function enqueue(Queue storage _q, bytes32 _item) internal { |
||||||
|
uint256 _last = _q.last + 1; |
||||||
|
_q.last = _last; |
||||||
|
_q.queue[_last] = _item; |
||||||
|
} |
||||||
|
|
||||||
|
function dequeue(Queue storage _q) internal returns (bytes32 item) { |
||||||
|
uint256 _first = _q.first; |
||||||
|
require(_q.last >= _first, "Empty"); |
||||||
|
item = _q.queue[_first]; |
||||||
|
delete _q.queue[_first]; |
||||||
|
_q.first = _first + 1; |
||||||
|
} |
||||||
|
|
||||||
|
function length(Queue storage _q) internal view returns (uint256) { |
||||||
|
// Cannot underflow unless state is corrupted |
||||||
|
return _q.first - _q.last - 1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
contract QueueManager { |
||||||
|
using QueueLib for QueueLib.Queue; |
||||||
|
QueueLib.Queue internal queue; |
||||||
|
|
||||||
|
constructor() { |
||||||
|
queue.init(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,94 @@ |
|||||||
|
// SPDX-License-Identifier: MIT OR Apache-2.0 |
||||||
|
pragma solidity >=0.6.11; |
||||||
|
|
||||||
|
import "@openzeppelin/contracts/cryptography/ECDSA.sol"; |
||||||
|
|
||||||
|
contract Replica { |
||||||
|
enum States {WAITING, FAILED} |
||||||
|
|
||||||
|
States public state; |
||||||
|
|
||||||
|
uint32 public immutable originSLIP44; |
||||||
|
uint32 public immutable ownSLIP44; |
||||||
|
bytes32 public immutable DOMAIN_HASH; |
||||||
|
address public updater; |
||||||
|
uint256 public optimisticSeconds; |
||||||
|
|
||||||
|
bytes32 current; |
||||||
|
bytes32 pending; |
||||||
|
uint256 confirmAt; |
||||||
|
|
||||||
|
event DoubleUpdate(); |
||||||
|
|
||||||
|
modifier notFailed() { |
||||||
|
require(state == States.WAITING); |
||||||
|
_; |
||||||
|
} |
||||||
|
|
||||||
|
constructor( |
||||||
|
uint32 _originSLIP44, |
||||||
|
uint32 _ownSLIP44, |
||||||
|
address _updater, |
||||||
|
uint256 _optimisticSeconds, |
||||||
|
bytes32 _start |
||||||
|
) { |
||||||
|
DOMAIN_HASH = keccak256(abi.encodePacked(_originSLIP44, "OPTICS")); |
||||||
|
updater = _updater; |
||||||
|
originSLIP44 = _originSLIP44; |
||||||
|
ownSLIP44 = _ownSLIP44; |
||||||
|
state = States.WAITING; |
||||||
|
optimisticSeconds = _optimisticSeconds; |
||||||
|
current = _start; |
||||||
|
} |
||||||
|
|
||||||
|
function fail() internal { |
||||||
|
state = States.FAILED; |
||||||
|
} |
||||||
|
|
||||||
|
function checkSig( |
||||||
|
bytes32 _newRoot, |
||||||
|
bytes32 _oldRoot, |
||||||
|
bytes memory _signature |
||||||
|
) internal view returns (bool) { |
||||||
|
bytes32 _digest = |
||||||
|
keccak256(abi.encodePacked(DOMAIN_HASH, _oldRoot, _newRoot)); |
||||||
|
_digest = ECDSA.toEthSignedMessageHash(_digest); |
||||||
|
return ECDSA.recover(_digest, _signature) == updater; |
||||||
|
} |
||||||
|
|
||||||
|
function update( |
||||||
|
bytes32 _newRoot, |
||||||
|
bytes32 _oldRoot, |
||||||
|
bytes memory _signature |
||||||
|
) external notFailed { |
||||||
|
require(current == _oldRoot, "Not current update"); |
||||||
|
require(checkSig(_newRoot, _oldRoot, _signature), "Bad sig"); |
||||||
|
|
||||||
|
confirmAt = block.timestamp + optimisticSeconds; |
||||||
|
pending = _newRoot; |
||||||
|
} |
||||||
|
|
||||||
|
function confirm() external notFailed { |
||||||
|
require(confirmAt != 0, "No pending"); |
||||||
|
require(block.timestamp >= confirmAt, "Not yet"); |
||||||
|
current = pending; |
||||||
|
delete pending; |
||||||
|
delete confirmAt; |
||||||
|
} |
||||||
|
|
||||||
|
function doubleUpdate( |
||||||
|
bytes32[2] calldata _newRoot, |
||||||
|
bytes32[2] calldata _oldRoot, |
||||||
|
bytes calldata _signature, |
||||||
|
bytes calldata _signature2 |
||||||
|
) external notFailed { |
||||||
|
if ( |
||||||
|
checkSig(_newRoot[0], _oldRoot[0], _signature) && |
||||||
|
checkSig(_newRoot[1], _oldRoot[1], _signature2) && |
||||||
|
(_newRoot[0] != _newRoot[1] || _oldRoot[0] != _oldRoot[1]) |
||||||
|
) { |
||||||
|
fail(); |
||||||
|
emit DoubleUpdate(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,8 @@ |
|||||||
|
require("@nomiclabs/hardhat-waffle"); |
||||||
|
require("@nomiclabs/hardhat-ethers"); |
||||||
|
/** |
||||||
|
* @type import('hardhat/config').HardhatUserConfig |
||||||
|
*/ |
||||||
|
module.exports = { |
||||||
|
solidity: "0.7.6", |
||||||
|
}; |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,26 @@ |
|||||||
|
{ |
||||||
|
"name": "@celo-org/optics-sol", |
||||||
|
"devDependencies": { |
||||||
|
"@nomiclabs/hardhat-ethers": "^2.0.1", |
||||||
|
"@nomiclabs/hardhat-waffle": "^2.0.1", |
||||||
|
"@openzeppelin/contracts": "^3.3.0", |
||||||
|
"ethereum-waffle": "^3.2.1", |
||||||
|
"ethers": "^5.0.25", |
||||||
|
"hardhat": "^2.0.7", |
||||||
|
"prettier": "2.2.1", |
||||||
|
"prettier-plugin-solidity": "^1.0.0-beta.3" |
||||||
|
}, |
||||||
|
"version": "0.0.0", |
||||||
|
"description": "Optimistic Interchain Communications", |
||||||
|
"main": " ", |
||||||
|
"directories": { |
||||||
|
"test": "test" |
||||||
|
}, |
||||||
|
"dependencies": {}, |
||||||
|
"scripts": { |
||||||
|
"compile": "hardhat compile", |
||||||
|
"test": "hardhat test" |
||||||
|
}, |
||||||
|
"author": "James Prestwich", |
||||||
|
"license": "MIT OR Apache-2.0" |
||||||
|
} |
Loading…
Reference in new issue