From 9b916a5a0bb016c69cf09d654f13d28a8b38ab07 Mon Sep 17 00:00:00 2001 From: Sander Bosma Date: Mon, 24 Jul 2023 15:39:49 +0200 Subject: [PATCH] fix: provide coinbase proof in addition to the usertx proof --- Cargo.lock | 187 ++++++++----------- Cargo.toml | 5 +- bitcoin/src/electrs/error.rs | 3 + bitcoin/src/electrs/mod.rs | 8 + bitcoin/src/error.rs | 2 + bitcoin/src/iter.rs | 2 +- bitcoin/src/lib.rs | 35 +++- bitcoin/src/light/mod.rs | 25 ++- docker-compose.yml | 2 +- runtime/Cargo.toml | 9 +- runtime/metadata-parachain-interlay.scale | Bin 278918 -> 277198 bytes runtime/metadata-parachain-kintsugi.scale | Bin 278918 -> 296702 bytes runtime/src/integration/bitcoin_simulator.rs | 49 ++++- runtime/src/integration/mod.rs | 3 +- runtime/src/lib.rs | 12 +- runtime/src/rpc.rs | 91 +++++---- runtime/src/tests.rs | 9 +- runtime/src/types.rs | 5 +- vault/src/cancellation.rs | 12 +- vault/src/execution.rs | 39 ++-- vault/src/issue.rs | 11 +- vault/src/metrics.rs | 17 +- vault/src/replace.rs | 4 +- vault/tests/vault_integration_tests.rs | 10 +- 24 files changed, 284 insertions(+), 256 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a95cee548..b53a88a61 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -191,13 +191,13 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.71" +version = "0.1.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf" +checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -367,19 +367,17 @@ dependencies = [ [[package]] name = "bitcoin" version = "1.2.0" -source = "git+https://github.com/interlay/interbtc?rev=2ec5b959a547f28d7bab7799568321c9cff40343#2ec5b959a547f28d7bab7799568321c9cff40343" +source = "git+https://github.com/interlay/interbtc?rev=1b90616692dcce55749616b4c60bdab228279909#1b90616692dcce55749616b4c60bdab228279909" dependencies = [ "bitcoin_hashes 0.7.6", - "frame-support", "hex", "impl-serde 0.3.2", "parity-scale-codec", + "primitive-types", "scale-info", "secp256k1 0.20.1", "serde", "sha2 0.8.2", - "sp-core", - "sp-std 5.0.0", "spin 0.7.1", ] @@ -549,7 +547,7 @@ checksum = "bd769563b4ea2953e2825c9e6b7470a5f55f67e0be00030bf3e390a2a6071f64" [[package]] name = "btc-relay" version = "1.2.0" -source = "git+https://github.com/interlay/interbtc?rev=2ec5b959a547f28d7bab7799568321c9cff40343#2ec5b959a547f28d7bab7799568321c9cff40343" +source = "git+https://github.com/interlay/interbtc?rev=1b90616692dcce55749616b4c60bdab228279909#1b90616692dcce55749616b4c60bdab228279909" dependencies = [ "bitcoin 1.2.0", "frame-benchmarking", @@ -680,9 +678,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.17" +version = "4.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0827b011f6f8ab38590295339817b0d26f344aa4932c3ced71b45b0c54b4a9" +checksum = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d" dependencies = [ "clap_builder", "clap_derive", @@ -691,9 +689,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.3.17" +version = "4.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9441b403be87be858db6a23edb493e7f694761acdc3343d5a0fcaafd304cbc9e" +checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1" dependencies = [ "anstream", "anstyle", @@ -710,7 +708,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -957,7 +955,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -979,7 +977,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core 0.20.3", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -1117,9 +1115,9 @@ checksum = "304e6508efa593091e97a9abbc10f90aa7ca635b6d2784feff3c89d41dd12272" [[package]] name = "ecdsa" -version = "0.16.7" +version = "0.16.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0997c976637b606099b9985693efa3581e84e41f5c11ba5255f88711058ad428" +checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" dependencies = [ "der", "digest 0.10.7", @@ -1166,9 +1164,9 @@ dependencies = [ [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "elliptic-curve" @@ -1303,12 +1301,9 @@ checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" [[package]] name = "fastrand" -version = "1.9.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" [[package]] name = "faucet" @@ -1507,7 +1502,7 @@ dependencies = [ "proc-macro-warning", "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -1519,7 +1514,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -1529,7 +1524,7 @@ source = "git+https://github.com/paritytech//substrate?branch=polkadot-v0.9.42#f dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -1641,7 +1636,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -2257,19 +2252,17 @@ dependencies = [ [[package]] name = "interbtc-primitives" version = "1.2.0" -source = "git+https://github.com/interlay/interbtc?rev=2ec5b959a547f28d7bab7799568321c9cff40343#2ec5b959a547f28d7bab7799568321c9cff40343" +source = "git+https://github.com/interlay/interbtc?rev=1b90616692dcce55749616b4c60bdab228279909#1b90616692dcce55749616b4c60bdab228279909" dependencies = [ "bitcoin 1.2.0", "bstringify", "parity-scale-codec", + "primitive-types", "scale-decode", "scale-encode", "scale-info", "serde", - "sp-core", "sp-runtime", - "sp-std 5.0.0", - "xcm", ] [[package]] @@ -2704,9 +2697,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.9" +version = "1.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ee889ecc9568871456d42f603d6a0ce59ff328d291063a45cbdf0036baf6db" +checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b" dependencies = [ "cc", "pkg-config", @@ -3209,9 +3202,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", "libm", @@ -3289,7 +3282,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -3336,12 +3329,11 @@ dependencies = [ [[package]] name = "oracle-rpc-runtime-api" version = "1.2.0" -source = "git+https://github.com/interlay/interbtc?rev=2ec5b959a547f28d7bab7799568321c9cff40343#2ec5b959a547f28d7bab7799568321c9cff40343" +source = "git+https://github.com/interlay/interbtc?rev=1b90616692dcce55749616b4c60bdab228279909#1b90616692dcce55749616b4c60bdab228279909" dependencies = [ "frame-support", + "interbtc-primitives", "parity-scale-codec", - "scale-info", - "serde", "sp-api", "sp-std 5.0.0", ] @@ -3504,7 +3496,7 @@ checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -3631,7 +3623,7 @@ checksum = "0e99670bafb56b9a106419397343bdbc8b8742c3cc449fec6345f86173f47cd4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -3713,9 +3705,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.31" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] @@ -3901,22 +3893,22 @@ dependencies = [ [[package]] name = "ref-cast" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1641819477c319ef452a075ac34a4be92eb9ba09f6841f62d594d50fdcf0bf6b" +checksum = "61ef7e18e8841942ddb1cf845054f8008410030a3997875d9e49b7a363063df1" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68bf53dad9b6086826722cdc99140793afd9f62faa14a1ad07eb4f955e7a7216" +checksum = "2dfaf0c85b766276c797f3791f5bc6d5bd116b41d53049af2789666b0c0bc9fa" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -4489,7 +4481,7 @@ dependencies = [ [[package]] name = "security" version = "1.2.0" -source = "git+https://github.com/interlay/interbtc?rev=2ec5b959a547f28d7bab7799568321c9cff40343#2ec5b959a547f28d7bab7799568321c9cff40343" +source = "git+https://github.com/interlay/interbtc?rev=1b90616692dcce55749616b4c60bdab228279909#1b90616692dcce55749616b4c60bdab228279909" dependencies = [ "frame-benchmarking", "frame-support", @@ -4505,9 +4497,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.9.1" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ "bitflags 1.3.2", "core-foundation", @@ -4518,9 +4510,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", @@ -4540,22 +4532,22 @@ checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" [[package]] name = "serde" -version = "1.0.173" +version = "1.0.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91f70896d6720bc714a4a57d22fc91f1db634680e65c8efe13323f1fa38d53f" +checksum = "5d25439cd7397d044e2748a6fe2432b5e85db703d6d097bd014b3c0ad1ebff0b" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.173" +version = "1.0.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6250dde8342e0232232be9ca3db7aa40aceb5a3e5dd9bddbc00d99a007cde49" +checksum = "b23f7ade6f110613c0d63858ddb8b94c1041f550eab58a16b371bdf2c9c80ab4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -4629,7 +4621,7 @@ checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -4899,7 +4891,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -5010,7 +5002,7 @@ dependencies = [ "proc-macro2", "quote", "sp-core-hashing 5.0.0", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -5020,7 +5012,7 @@ source = "git+https://github.com/paritytech//substrate?branch=polkadot-v0.9.42#f dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -5169,7 +5161,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -5304,7 +5296,7 @@ dependencies = [ "parity-scale-codec", "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -5498,7 +5490,7 @@ dependencies = [ "quote", "scale-info", "subxt-metadata", - "syn 2.0.26", + "syn 2.0.27", "thiserror", "tokio", ] @@ -5512,7 +5504,7 @@ dependencies = [ "darling 0.20.3", "proc-macro-error", "subxt-codegen", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -5541,9 +5533,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.26" +version = "2.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45c3457aacde3c65315de5031ec191ce46604304d2446e803d71ade03308d970" +checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" dependencies = [ "proc-macro2", "quote", @@ -5604,15 +5596,14 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.6.0" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" +checksum = "5486094ee78b2e5038a6382ed7645bc084dc2ec433426ca4c3cb61e2007b8998" dependencies = [ - "autocfg", "cfg-if 1.0.0", "fastrand", "redox_syscall 0.3.5", - "rustix 0.37.23", + "rustix 0.38.4", "windows-sys 0.48.0", ] @@ -5633,22 +5624,22 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" -version = "1.0.43" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" +checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.43" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" +checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -5733,7 +5724,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -5873,7 +5864,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -6235,7 +6226,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", "wasm-bindgen-shared", ] @@ -6269,7 +6260,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6683,9 +6674,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fac9742fd1ad1bd9643b991319f72dd031016d44b77039a26977eb667141e7" +checksum = "25b5872fa2e10bd067ae946f927e726d7d603eaeb6e02fa6a350e0722d2b8c11" dependencies = [ "memchr", ] @@ -6708,32 +6699,6 @@ dependencies = [ "tap", ] -[[package]] -name = "xcm" -version = "0.9.42" -source = "git+https://github.com/paritytech//polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" -dependencies = [ - "bounded-collections", - "derivative", - "impl-trait-for-tuples", - "log", - "parity-scale-codec", - "scale-info", - "sp-weights", - "xcm-procedural", -] - -[[package]] -name = "xcm-procedural" -version = "0.9.42" -source = "git+https://github.com/paritytech//polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" -dependencies = [ - "Inflector", - "proc-macro2", - "quote", - "syn 2.0.26", -] - [[package]] name = "yap" version = "0.10.0" @@ -6757,5 +6722,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] diff --git a/Cargo.toml b/Cargo.toml index 5966bedbd..4b8ebbe4b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,4 @@ sp-tracing = { git = "https://github.com/paritytech//substrate", branch = "polka sp-trie = { git = "https://github.com/paritytech//substrate", branch = "polkadot-v0.9.42" } sp-version = { git = "https://github.com/paritytech//substrate", branch = "polkadot-v0.9.42" } sp-wasm-interface = { git = "https://github.com/paritytech//substrate", branch = "polkadot-v0.9.42" } -sp-weights = { git = "https://github.com/paritytech//substrate", branch = "polkadot-v0.9.42" } - -[patch."https://github.com/paritytech/polkadot"] -xcm = { git = "https://github.com/paritytech//polkadot", branch = "release-v0.9.42" } \ No newline at end of file +sp-weights = { git = "https://github.com/paritytech//substrate", branch = "polkadot-v0.9.42" } \ No newline at end of file diff --git a/bitcoin/src/electrs/error.rs b/bitcoin/src/electrs/error.rs index 39bef49b8..156631b2a 100644 --- a/bitcoin/src/electrs/error.rs +++ b/bitcoin/src/electrs/error.rs @@ -35,6 +35,9 @@ pub enum Error { TryFromIntError(#[from] TryFromIntError), #[error("ParseIntError: {0}")] ParseIntError(#[from] ParseIntError), + + #[error("No txids in block")] + EmptyBlock, } impl Error { diff --git a/bitcoin/src/electrs/mod.rs b/bitcoin/src/electrs/mod.rs index 7973d77b8..536d3defe 100644 --- a/bitcoin/src/electrs/mod.rs +++ b/bitcoin/src/electrs/mod.rs @@ -136,6 +136,14 @@ impl ElectrsClient { Ok(txs) } + pub(crate) async fn get_coinbase_txid(&self, block_hash: &BlockHash) -> Result { + self.get_and_decode::>(&format!("/block/{block_hash}/txids")) + .await? + .first() + .ok_or(Error::EmptyBlock) + .and_then(|raw_txid| Ok(Txid::from_str(raw_txid)?)) + } + pub(crate) async fn get_block(&self, hash: &BlockHash) -> Result { let (header, txdata) = try_join(self.get_block_header(hash), self.get_transactions_in_block(hash)).await?; Ok(Block { header, txdata }) diff --git a/bitcoin/src/error.rs b/bitcoin/src/error.rs index 6835732f7..119cfb8c9 100644 --- a/bitcoin/src/error.rs +++ b/bitcoin/src/error.rs @@ -77,6 +77,8 @@ pub enum Error { FailedToConstructWalletName, #[error("AddressError: {0}")] AddressError(#[from] AddressError), + #[error("Failed to fetch coinbase tx")] + CoinbaseFetchingFailure, } impl Error { diff --git a/bitcoin/src/iter.rs b/bitcoin/src/iter.rs index 6bbd137d7..7f50ca51a 100644 --- a/bitcoin/src/iter.rs +++ b/bitcoin/src/iter.rs @@ -292,7 +292,7 @@ mod tests { Block { txdata: transactions.into_iter().map(dummy_tx).collect(), header: BlockHeader { - version: Version::from_consensus(4), + version: Version::from_consensus(2), bits: CompactTarget::from_consensus(0), nonce: 0, time: 0, diff --git a/bitcoin/src/lib.rs b/bitcoin/src/lib.rs index 536ec32aa..cfc29f405 100644 --- a/bitcoin/src/lib.rs +++ b/bitcoin/src/lib.rs @@ -113,14 +113,21 @@ fn get_exponential_backoff() -> ExponentialBackoff { } } +#[derive(PartialEq, Eq, PartialOrd, Clone, Debug)] +pub struct RawTransactionProof { + pub user_tx_proof: Vec, + pub raw_user_tx: Vec, + pub coinbase_tx_proof: Vec, + pub raw_coinbase_tx: Vec, +} + #[derive(PartialEq, Eq, PartialOrd, Clone, Copy, Debug)] pub struct SatPerVbyte(pub u64); #[derive(Debug, Clone)] pub struct TransactionMetadata { pub txid: Txid, - pub proof: Vec, - pub raw_tx: Vec, + pub proof: RawTransactionProof, pub block_height: u32, pub block_hash: BlockHash, pub fee: Option, @@ -885,19 +892,29 @@ impl BitcoinCoreApi for BitcoinCore { .await?; let proof = retry(get_exponential_backoff(), || async { - Ok(self.get_proof(txid, &block_hash).await?) - }) - .await?; - - let raw_tx = retry(get_exponential_backoff(), || async { - Ok(self.get_raw_tx(&txid, &block_hash).await?) + // fetch coinbase info.. + let block = self.get_block(&block_hash).await?; + let coinbase_tx = block.coinbase().ok_or(Error::CoinbaseFetchingFailure)?; + let coinbase_txid = coinbase_tx.txid(); + let coinbase_tx_proof = self.get_proof(coinbase_txid, &block_hash).await?; + let raw_coinbase_tx = self.get_raw_tx(&coinbase_txid, &block_hash).await?; + + // fetch user tx info.. + let raw_user_tx = self.get_raw_tx(&txid, &block_hash).await?; + let user_tx_proof = self.get_proof(txid, &block_hash).await?; + + Ok(RawTransactionProof { + raw_coinbase_tx, + coinbase_tx_proof, + raw_user_tx, + user_tx_proof, + }) }) .await?; Ok(TransactionMetadata { txid, proof, - raw_tx, block_height, block_hash, fee, diff --git a/bitcoin/src/light/mod.rs b/bitcoin/src/light/mod.rs index 9b94ef711..b432a7467 100644 --- a/bitcoin/src/light/mod.rs +++ b/bitcoin/src/light/mod.rs @@ -7,7 +7,7 @@ pub use error::Error; use async_trait::async_trait; use backoff::future::retry; -use futures::future::{join_all, try_join, try_join_all}; +use futures::future::{join_all, try_join, try_join4, try_join_all}; use std::{convert::TryFrom, sync::Arc, time::Duration}; use tokio::{sync::Mutex, time::sleep}; @@ -82,6 +82,10 @@ impl BitcoinLight { let txid = self.electrs.send_transaction(transaction.transaction).await?; Ok(txid) } + + async fn get_coinbase_txid(&self, block_hash: &BlockHash) -> Result { + Ok(self.electrs.get_coinbase_txid(&block_hash).await?) + } } #[async_trait] @@ -236,15 +240,28 @@ impl BitcoinCoreApi for BitcoinLight { }) .await?; - let (proof, raw_tx) = retry(get_exponential_backoff(), || async { - Ok(try_join(self.get_proof(txid, &block_hash), self.get_raw_tx(&txid, &block_hash)).await?) + let proof = retry(get_exponential_backoff(), || async { + let coinbase_txid = self.get_coinbase_txid(&block_hash).await?; + + let (coinbase_tx_proof, raw_coinbase_tx, user_tx_proof, raw_user_tx) = try_join4( + self.get_proof(coinbase_txid, &block_hash), + self.get_raw_tx(&coinbase_txid, &block_hash), + self.get_proof(txid, &block_hash), + self.get_raw_tx(&txid, &block_hash), + ) + .await?; + Ok(RawTransactionProof { + coinbase_tx_proof, + raw_coinbase_tx, + user_tx_proof, + raw_user_tx, + }) }) .await?; Ok(TransactionMetadata { txid, proof, - raw_tx, block_height, block_hash, fee: Some(fee), diff --git a/docker-compose.yml b/docker-compose.yml index 5d4db6d44..d99f652ca 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: "3.8" services: interbtc: - image: "interlayhq/interbtc:1.25.0-rc1" + image: "interlayhq/interbtc:1.25.0-rc4" command: - --rpc-external - --ws-external diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index cdff1a98c..c3d7a1d4d 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -52,22 +52,23 @@ rand = { version = "0.7", optional = true } [dependencies.primitives] git = "https://github.com/interlay/interbtc" -rev = "2ec5b959a547f28d7bab7799568321c9cff40343" +rev = "1b90616692dcce55749616b4c60bdab228279909" package = "interbtc-primitives" [dependencies.module-bitcoin] git = "https://github.com/interlay/interbtc" -rev = "2ec5b959a547f28d7bab7799568321c9cff40343" +rev = "1b90616692dcce55749616b4c60bdab228279909" package = "bitcoin" +features = ["parser"] [dependencies.module-btc-relay] git = "https://github.com/interlay/interbtc" -rev = "2ec5b959a547f28d7bab7799568321c9cff40343" +rev = "1b90616692dcce55749616b4c60bdab228279909" package = "btc-relay" [dependencies.module-oracle-rpc-runtime-api] git = "https://github.com/interlay/interbtc" -rev = "2ec5b959a547f28d7bab7799568321c9cff40343" +rev = "1b90616692dcce55749616b4c60bdab228279909" package = "oracle-rpc-runtime-api" [dev-dependencies] diff --git a/runtime/metadata-parachain-interlay.scale b/runtime/metadata-parachain-interlay.scale index 28dffb7de1102927a18a3a5bff7e98b4d75012b5..2ed1ce0d5ddb0c039d0bcc3cd31c2d6a2e22026b 100644 GIT binary patch delta 15854 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 delta 17138 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 Tew(&kJT^fcYH6@<-f90oJU#|c diff --git a/runtime/metadata-parachain-kintsugi.scale b/runtime/metadata-parachain-kintsugi.scale index 4582982a2a52ed421dada4ed142c22239b38aab3..7ffb1859d12b7f450b47b103a7c6580d4f381202 100644 GIT binary patch delta 33053 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 Transaction { let address = ScriptBuf::from(address.to_script_pub_key().as_bytes().to_vec()); + // construct height: see https://github.com/bitcoin/bips/blob/master/bip-0034.mediawiki + // first byte is number of bytes in the number (will be 0x03 on main net for the next + // 150 or so years with 223-1 blocks), following bytes are little-endian representation + // of the number (including a sign bit) + let mut height_bytes = height.to_le_bytes().to_vec(); + for i in (1..4).rev() { + // remove trailing zeroes, but always keep first byte even if it's zero + if height_bytes[i] == 0 { + height_bytes.remove(i); + } else { + break; + } + } + height_bytes.insert(0, height_bytes.len() as u8); + // note that we set lock_time to height, otherwise we might generate blocks with // identical block hashes Transaction { input: vec![TxIn { previous_output: OutPoint::null(), // coinbase witness: Witness::from_slice::<&[u8]>(&[]), - script_sig: Default::default(), + script_sig: ScriptBuf::from(height_bytes), sequence: Sequence(u32::max_value()), }], output: vec![TxOut { @@ -360,7 +375,16 @@ impl BitcoinCoreApi for MockBitcoinCore { // part two: info about the transactions (we assume the txid is at index 1) let txids = block.txdata.iter().map(|x| x.txid()).collect::>(); - let partial_merkle_tree = PartialMerkleTree::from_txids(&txids, &[false, true]); + assert_eq!(txids.len(), 2); // expect coinbase and user tx + + let partial_merkle_tree = if txids[0] == txid { + PartialMerkleTree::from_txids(&txids, &[true, false]) + } else if txids[1] == txid { + PartialMerkleTree::from_txids(&txids, &[false, true]) + } else { + panic!("txid not in block") + }; + proof.append(&mut serialize(&partial_merkle_tree)); Ok(proof) @@ -441,13 +465,20 @@ impl BitcoinCoreApi for MockBitcoinCore { tokio::time::sleep(Duration::from_secs(1)).await; }; let block_hash = block.block_hash(); - let proof = self.get_proof(txid, &block_hash).await.unwrap(); - let raw_tx = self.get_raw_tx(&txid, &block_hash).await.unwrap(); + let coinbase_txid = block.coinbase().unwrap().txid(); + let coinbase_tx_proof = self.get_proof(coinbase_txid, &block_hash).await.unwrap(); + let raw_coinbase_tx = self.get_raw_tx(&coinbase_txid, &block_hash).await.unwrap(); + let user_tx_proof = self.get_proof(txid, &block_hash).await.unwrap(); + let raw_user_tx = self.get_raw_tx(&txid, &block_hash).await.unwrap(); Ok(TransactionMetadata { block_hash, - proof, - raw_tx, + proof: RawTransactionProof { + user_tx_proof, + raw_user_tx, + coinbase_tx_proof, + raw_coinbase_tx, + }, txid, block_height: block_height as u32, fee: None, diff --git a/runtime/src/integration/mod.rs b/runtime/src/integration/mod.rs index a7041cd3d..121ee9594 100644 --- a/runtime/src/integration/mod.rs +++ b/runtime/src/integration/mod.rs @@ -33,6 +33,7 @@ pub use bitcoin_simulator::MockBitcoinCore; pub async fn default_root_provider(key: AccountKeyring) -> (InterBtcParachain, TempDir) { let tmp = TempDir::new("btc-parachain-").expect("failed to create tempdir"); let root_provider = setup_provider(key).await; + try_join( root_provider.set_bitcoin_confirmations(1), root_provider.set_parachain_confirmations(1), @@ -81,7 +82,7 @@ pub async fn assert_issue( .unwrap(); parachain_rpc - .execute_issue(*issue.issue_id, &metadata.proof, &metadata.raw_tx) + .execute_issue(*issue.issue_id, &metadata.proof) .await .unwrap(); } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index b7836b7b7..d5dd78e0a 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -71,8 +71,6 @@ pub const DISABLE_DIFFICULTY_CHECK: &str = "DisableDifficultyCheck"; ), derive_for_type(path = "interbtc_primitives::VaultCurrencyPair", derive = "Eq, PartialEq"), derive_for_type(path = "interbtc_primitives::VaultId", derive = "Eq, PartialEq"), - derive_for_type(path = "security::types::ErrorCode", derive = "Eq, PartialEq, Ord, PartialOrd"), - derive_for_type(path = "security::types::StatusCode", derive = "Eq, PartialEq"), substitute_type(path = "primitive_types::H256", with = "::subxt::utils::Static"), substitute_type(path = "primitive_types::U256", with = "::subxt::utils::Static"), substitute_type(path = "primitive_types::H160", with = "::subxt::utils::Static"), @@ -106,6 +104,10 @@ pub const DISABLE_DIFFICULTY_CHECK: &str = "DisableDifficultyCheck"; path = "bitcoin::types::Transaction", with = "::subxt::utils::Static<::module_bitcoin::types::Transaction>" ), + substitute_type( + path = "bitcoin::types::FullTransactionProof", + with = "::subxt::utils::Static<::module_bitcoin::types::FullTransactionProof>" + ), ) )] #[cfg_attr( @@ -123,8 +125,6 @@ pub const DISABLE_DIFFICULTY_CHECK: &str = "DisableDifficultyCheck"; ), derive_for_type(path = "interbtc_primitives::VaultCurrencyPair", derive = "Eq, PartialEq"), derive_for_type(path = "interbtc_primitives::VaultId", derive = "Eq, PartialEq"), - derive_for_type(path = "security::types::ErrorCode", derive = "Eq, PartialEq, Ord, PartialOrd"), - derive_for_type(path = "security::types::StatusCode", derive = "Eq, PartialEq"), substitute_type(path = "primitive_types::H256", with = "::subxt::utils::Static"), substitute_type(path = "primitive_types::U256", with = "::subxt::utils::Static"), substitute_type(path = "primitive_types::H160", with = "::subxt::utils::Static"), @@ -158,6 +158,10 @@ pub const DISABLE_DIFFICULTY_CHECK: &str = "DisableDifficultyCheck"; path = "bitcoin::types::Transaction", with = "::subxt::utils::Static<::module_bitcoin::types::Transaction>" ), + substitute_type( + path = "bitcoin::types::FullTransactionProof", + with = "::subxt::utils::Static<::module_bitcoin::types::FullTransactionProof>" + ), ) )] diff --git a/runtime/src/rpc.rs b/runtime/src/rpc.rs index 8fbd3cbce..ac01627f4 100644 --- a/runtime/src/rpc.rs +++ b/runtime/src/rpc.rs @@ -7,16 +7,18 @@ use crate::{ AccountId, AssetRegistry, CurrencyId, Error, InterBtcRuntime, InterBtcSigner, RetryPolicy, RichH256Le, SubxtError, }; use async_trait::async_trait; +use bitcoin::RawTransactionProof; use codec::{Decode, Encode}; use futures::{future::join_all, stream::StreamExt, FutureExt, SinkExt, Stream}; use module_bitcoin::{ - merkle::MerkleProof, + merkle::{MerkleProof, PartialTransactionProof}, parser::{parse_block_header, parse_transaction}, + types::FullTransactionProof, }; use module_oracle_rpc_runtime_api::BalanceWrapper; use primitives::UnsignedFixedPoint; use serde_json::Value; -use std::{future::Future, ops::Range, sync::Arc, time::Duration}; +use std::{convert::TryInto, future::Future, ops::Range, sync::Arc, time::Duration}; use subxt::{ blocks::ExtrinsicEvents, client::OnlineClient, @@ -31,6 +33,7 @@ use tokio::{ sync::RwLock, time::{sleep, timeout}, }; + // timeout before retrying parachain calls (5 minutes) const TRANSACTION_TIMEOUT: Duration = Duration::from_secs(300); @@ -890,9 +893,8 @@ pub trait ReplacePallet { /// /// * `&self` - sender of the transaction: the old vault /// * `replace_id` - the ID of the replacement request - /// * 'merkle_proof' - the merkle root of the block - /// * `raw_tx` - the transaction id in bytes - async fn execute_replace(&self, replace_id: H256, merkle_proof: &[u8], raw_tx: &[u8]) -> Result<(), Error>; + /// * `raw_proof` - raw tx and proofs of coinbase and user tx + async fn execute_replace(&self, replace_id: H256, raw_proof: &RawTransactionProof) -> Result<(), Error>; /// Cancel vault replacement /// @@ -966,13 +968,12 @@ impl ReplacePallet for InterBtcParachain { Ok(()) } - async fn execute_replace(&self, replace_id: H256, merkle_proof: &[u8], raw_tx: &[u8]) -> Result<(), Error> { - self.with_unique_signer(metadata::tx().replace().execute_replace( - Static(replace_id), - Static(MerkleProof::parse(merkle_proof)?), - Static(parse_transaction(raw_tx)?), - raw_tx.len() as u32, - )) + async fn execute_replace(&self, replace_id: H256, raw_proof: &RawTransactionProof) -> Result<(), Error> { + self.with_unique_signer( + metadata::tx() + .replace() + .execute_replace(Static(replace_id), build_full_tx_proof(raw_proof)?), + ) .await?; Ok(()) } @@ -1168,31 +1169,12 @@ impl OraclePallet for InterBtcParachain { #[async_trait] pub trait SecurityPallet { - async fn get_parachain_status(&self) -> Result; - - async fn get_error_codes(&self) -> Result, Error>; - /// Gets the current active block number of the parachain async fn get_current_active_block_number(&self) -> Result; } #[async_trait] impl SecurityPallet for InterBtcParachain { - /// Get the current security status of the parachain. - /// Should be one of; `Running`, `Error` or `Shutdown`. - async fn get_parachain_status(&self) -> Result { - self.query_finalized_or_error(metadata::storage().security().parachain_status()) - .await - } - - /// Return any `ErrorCode`s set in the security module. - async fn get_error_codes(&self) -> Result, Error> { - Ok(self - .query_finalized_or_error(metadata::storage().security().errors()) - .await? - .0) - } - /// Gets the current active block number of the parachain async fn get_current_active_block_number(&self) -> Result { self.query_finalized_or_default(metadata::storage().security().active_block_count()) @@ -1206,7 +1188,7 @@ pub trait IssuePallet { async fn request_issue(&self, amount: u128, vault_id: &VaultId) -> Result; /// Execute a issue request by providing a Bitcoin transaction inclusion proof - async fn execute_issue(&self, issue_id: H256, merkle_proof: &[u8], raw_tx: &[u8]) -> Result<(), Error>; + async fn execute_issue(&self, issue_id: H256, raw_proof: &RawTransactionProof) -> Result<(), Error>; /// Cancel an ongoing issue request async fn cancel_issue(&self, issue_id: H256) -> Result<(), Error>; @@ -1234,13 +1216,12 @@ impl IssuePallet for InterBtcParachain { .ok_or(Error::RequestIssueIDNotFound) } - async fn execute_issue(&self, issue_id: H256, merkle_proof: &[u8], raw_tx: &[u8]) -> Result<(), Error> { - self.with_unique_signer(metadata::tx().issue().execute_issue( - Static(issue_id), - Static(MerkleProof::parse(merkle_proof)?), - Static(parse_transaction(raw_tx)?), - raw_tx.len() as u32, - )) + async fn execute_issue(&self, issue_id: H256, raw_proof: &RawTransactionProof) -> Result<(), Error> { + self.with_unique_signer( + metadata::tx() + .issue() + .execute_issue(Static(issue_id), build_full_tx_proof(raw_proof)?), + ) .await?; Ok(()) } @@ -1310,7 +1291,7 @@ pub trait RedeemPallet { async fn request_redeem(&self, amount: u128, btc_address: BtcAddress, vault_id: &VaultId) -> Result; /// Execute a redeem request by providing a Bitcoin transaction inclusion proof - async fn execute_redeem(&self, redeem_id: H256, merkle_proof: &[u8], raw_tx: &[u8]) -> Result<(), Error>; + async fn execute_redeem(&self, redeem_id: H256, raw_proof: &RawTransactionProof) -> Result<(), Error>; /// Cancel an ongoing redeem request async fn cancel_redeem(&self, redeem_id: H256, reimburse: bool) -> Result<(), Error>; @@ -1341,13 +1322,12 @@ impl RedeemPallet for InterBtcParachain { Ok(*redeem_event.redeem_id) } - async fn execute_redeem(&self, redeem_id: H256, merkle_proof: &[u8], raw_tx: &[u8]) -> Result<(), Error> { - self.with_unique_signer(metadata::tx().redeem().execute_redeem( - Static(redeem_id), - Static(MerkleProof::parse(merkle_proof)?), - Static(parse_transaction(raw_tx)?), - raw_tx.len() as u32, - )) + async fn execute_redeem(&self, redeem_id: H256, raw_proof: &RawTransactionProof) -> Result<(), Error> { + self.with_unique_signer( + metadata::tx() + .redeem() + .execute_redeem(Static(redeem_id), build_full_tx_proof(raw_proof)?), + ) .await?; Ok(()) } @@ -1694,7 +1674,7 @@ impl VaultRegistryPallet for InterBtcParachain { self.with_unique_signer( metadata::tx() .nomination() - .withdraw_collateral(vault_id.clone(), amount, None), + .withdraw_collateral(vault_id.clone(), Some(amount), None), ) .await?; Ok(()) @@ -1957,3 +1937,18 @@ impl SudoPallet for InterBtcParachain { .await } } + +pub fn build_full_tx_proof(raw_proof: &RawTransactionProof) -> Result, Error> { + Ok(Static(FullTransactionProof { + user_tx_proof: PartialTransactionProof { + transaction: parse_transaction(&raw_proof.raw_user_tx[..])?, + tx_encoded_len: raw_proof.raw_user_tx.len().try_into()?, + merkle_proof: MerkleProof::parse(&raw_proof.user_tx_proof[..])?, + }, + coinbase_proof: PartialTransactionProof { + transaction: parse_transaction(&raw_proof.raw_coinbase_tx[..])?, + tx_encoded_len: raw_proof.raw_coinbase_tx.len().try_into()?, + merkle_proof: MerkleProof::parse(&raw_proof.coinbase_tx_proof[..])?, + }, + })) +} diff --git a/runtime/src/tests.rs b/runtime/src/tests.rs index bda0b3c80..28059507c 100644 --- a/runtime/src/tests.rs +++ b/runtime/src/tests.rs @@ -4,8 +4,8 @@ const DEFAULT_TESTING_CURRENCY: CurrencyId = Token(KSM); use super::{ BtcAddress, BtcPublicKey, BtcRelayPallet, CollateralBalancesPallet, CurrencyId, FixedPointNumber, FixedU128, - OraclePallet, RawBlockHeader, ReplacePallet, SecurityPallet, StatusCode, SudoPallet, Token, TryFromSymbol, - VaultRegistryPallet, KBTC, KINT, KSM, + OraclePallet, RawBlockHeader, ReplacePallet, SecurityPallet, SudoPallet, Token, TryFromSymbol, VaultRegistryPallet, + KBTC, KINT, KSM, }; use crate::{ integration::*, utils::account_id::AccountId32, FeedValuesEvent, OracleKey, RuntimeCurrencyInfo, VaultId, H160, @@ -47,14 +47,11 @@ async fn test_getters() { async { assert_eq!(parachain_rpc.get_free_balance(Token(KINT)).await.unwrap(), 1 << 60); }, - async { - assert_eq!(parachain_rpc.get_parachain_status().await.unwrap(), StatusCode::Error); - }, async { assert!(parachain_rpc.get_replace_dust_amount().await.unwrap() > 0); }, async { - assert!(parachain_rpc.get_current_active_block_number().await.unwrap() == 0); + assert!(parachain_rpc.get_current_active_block_number().await.is_ok()); } ); parachain_runner.kill().unwrap(); diff --git a/runtime/src/types.rs b/runtime/src/types.rs index a45028457..abcb9df71 100644 --- a/runtime/src/types.rs +++ b/runtime/src/types.rs @@ -45,10 +45,7 @@ mod metadata_aliases { pub use metadata::runtime_types::interbtc_primitives::oracle::Key as OracleKey; - pub use metadata::runtime_types::{ - security::types::{ErrorCode, StatusCode}, - vault_registry::types::VaultStatus, - }; + pub use metadata::runtime_types::vault_registry::types::VaultStatus; pub type InterBtcVault = metadata::runtime_types::vault_registry::types::Vault; pub type InterBtcVaultStatic = metadata::runtime_types::vault_registry::types::Vault< diff --git a/vault/src/cancellation.rs b/vault/src/cancellation.rs index ab0deafb9..56e2cc143 100644 --- a/vault/src/cancellation.rs +++ b/vault/src/cancellation.rs @@ -315,12 +315,12 @@ impl Cancel mod tests { use super::*; use async_trait::async_trait; + use bitcoin::RawTransactionProof; use futures::channel::mpsc; use jsonrpc_core::serde_json::{Map, Value}; use runtime::{ - subxt::utils::Static, AccountId, AssetMetadata, BtcAddress, BtcPublicKey, CurrencyId, ErrorCode, - InterBtcIssueRequest, InterBtcReplaceRequest, IssueRequestStatus, RequestIssueEvent, StatusCode, Token, - VaultId, DOT, IBTC, INTR, + subxt::utils::Static, AccountId, AssetMetadata, BtcAddress, BtcPublicKey, CurrencyId, InterBtcIssueRequest, + InterBtcReplaceRequest, IssueRequestStatus, RequestIssueEvent, Token, VaultId, DOT, IBTC, INTR, }; macro_rules! assert_err { @@ -339,7 +339,7 @@ mod tests { #[async_trait] pub trait IssuePallet { async fn request_issue(&self, amount: u128, vault_id: &VaultId) -> Result; - async fn execute_issue(&self, issue_id: H256, merkle_proof: &[u8], raw_tx: &[u8]) -> Result<(), RuntimeError>; + async fn execute_issue(&self, issue_id: H256, raw_proof: &RawTransactionProof,) -> Result<(), RuntimeError>; async fn cancel_issue(&self, issue_id: H256) -> Result<(), RuntimeError>; async fn get_issue_request(&self, issue_id: H256) -> Result; async fn get_vault_issue_requests(&self, account_id: AccountId) -> Result, RuntimeError>; @@ -353,7 +353,7 @@ mod tests { async fn request_replace(&self, vault_id: &VaultId, amount: u128) -> Result<(), RuntimeError>; async fn withdraw_replace(&self, vault_id: &VaultId, amount: u128) -> Result<(), RuntimeError>; async fn accept_replace(&self, new_vault: &VaultId, old_vault: &VaultId, amount_btc: u128, collateral: u128, btc_address: BtcAddress) -> Result<(), RuntimeError>; - async fn execute_replace(&self, replace_id: H256, merkle_proof: &[u8], raw_tx: &[u8]) -> Result<(), RuntimeError>; + async fn execute_replace(&self, replace_id: H256, raw_proof: &RawTransactionProof) -> Result<(), RuntimeError>; async fn cancel_replace(&self, replace_id: H256) -> Result<(), RuntimeError>; async fn get_new_vault_replace_requests(&self, account_id: AccountId) -> Result, RuntimeError>; async fn get_old_vault_replace_requests(&self, account_id: AccountId) -> Result, RuntimeError>; @@ -376,8 +376,6 @@ mod tests { #[async_trait] pub trait SecurityPallet { - async fn get_parachain_status(&self) -> Result; - async fn get_error_codes(&self) -> Result, RuntimeError>; async fn get_current_active_block_number(&self) -> Result; } } diff --git a/vault/src/execution.rs b/vault/src/execution.rs index ac2dccc48..2619714f4 100644 --- a/vault/src/execution.rs +++ b/vault/src/execution.rs @@ -404,7 +404,7 @@ impl Request { // Retry until success or timeout, explicitly handle the cases // where the redeem has expired or the rpc has disconnected runtime::notify_retry( - || (execute)(¶chain_rpc, self.hash, &tx_metadata.proof, &tx_metadata.raw_tx), + || (execute)(¶chain_rpc, self.hash, &tx_metadata.proof), |result| async { match result { Ok(ok) => Ok(ok), @@ -665,15 +665,15 @@ mod tests { use async_trait::async_trait; use bitcoin::{ json, Address, Amount, BitcoinCoreApi, Block, BlockHash, BlockHeader, Error as BitcoinError, Hash, Network, - PrivateKey, PublicKey, Transaction, TransactionMetadata, Txid, + PrivateKey, PublicKey, RawTransactionProof, Transaction, TransactionMetadata, Txid, }; use jsonrpc_core::serde_json::{Map, Value}; use runtime::{ sp_core::H160, AccountId, AssetMetadata, BitcoinBlockHeight, BlockNumber, BtcPublicKey, CurrencyId, - Error as RuntimeError, ErrorCode, FeeRateUpdateReceiver, InterBtcRichBlockHeader, InterBtcVault, OracleKey, - RawBlockHeader, StatusCode, Token, DOT, IBTC, + Error as RuntimeError, FeeRateUpdateReceiver, InterBtcRichBlockHeader, InterBtcVault, OracleKey, + RawBlockHeader, Token, DOT, IBTC, }; - use std::{collections::BTreeSet, sync::Arc}; + use std::sync::Arc; macro_rules! assert_ok { ( $x:expr $(,)? ) => { @@ -724,7 +724,7 @@ mod tests { #[async_trait] pub trait RedeemPallet { async fn request_redeem(&self, amount: u128, btc_address: BtcAddress, vault_id: &VaultId) -> Result; - async fn execute_redeem(&self, redeem_id: H256, merkle_proof: &[u8], raw_tx: &[u8]) -> Result<(), RuntimeError>; + async fn execute_redeem(&self, redeem_id: H256, raw_proof: &RawTransactionProof) -> Result<(), RuntimeError>; async fn cancel_redeem(&self, redeem_id: H256, reimburse: bool) -> Result<(), RuntimeError>; async fn get_redeem_request(&self, redeem_id: H256) -> Result; async fn get_vault_redeem_requests(&self, account_id: AccountId) -> Result, RuntimeError>; @@ -736,7 +736,7 @@ mod tests { async fn request_replace(&self, vault_id: &VaultId, amount: u128) -> Result<(), RuntimeError>; async fn withdraw_replace(&self, vault_id: &VaultId, amount: u128) -> Result<(), RuntimeError>; async fn accept_replace(&self, new_vault: &VaultId, old_vault: &VaultId, amount_btc: u128, collateral: u128, btc_address: BtcAddress) -> Result<(), RuntimeError>; - async fn execute_replace(&self, replace_id: H256, merkle_proof: &[u8], raw_tx: &[u8]) -> Result<(), RuntimeError>; + async fn execute_replace(&self, replace_id: H256, raw_proof: &RawTransactionProof) -> Result<(), RuntimeError>; async fn cancel_replace(&self, replace_id: H256) -> Result<(), RuntimeError>; async fn get_new_vault_replace_requests(&self, account_id: AccountId) -> Result, RuntimeError>; async fn get_old_vault_replace_requests(&self, account_id: AccountId) -> Result, RuntimeError>; @@ -762,8 +762,6 @@ mod tests { #[async_trait] pub trait SecurityPallet { - async fn get_parachain_status(&self) -> Result; - async fn get_error_codes(&self) -> Result, RuntimeError>; async fn get_current_active_block_number(&self) -> Result; } @@ -910,7 +908,7 @@ mod tests { parachain_rpc .expect_get_current_active_block_number() .returning(move || Ok(current_parachain_height)); - parachain_rpc.expect_execute_redeem().returning(|_, _, _| Ok(())); + parachain_rpc.expect_execute_redeem().returning(|_, _| Ok(())); parachain_rpc.expect_wait_for_block_in_relay().returning(|_, _| Ok(())); parachain_rpc @@ -928,8 +926,12 @@ mod tests { mock_bitcoin.expect_wait_for_transaction_metadata().returning(|_, _| { Ok(TransactionMetadata { txid: Txid::all_zeros(), - proof: vec![], - raw_tx: vec![], + proof: RawTransactionProof { + coinbase_tx_proof: vec![], + raw_coinbase_tx: vec![], + raw_user_tx: vec![], + user_tx_proof: vec![], + }, block_height: 0, block_hash: BlockHash::all_zeros(), fee: None, @@ -1042,10 +1044,7 @@ mod tests { .expect_get_current_active_block_number() .times(1) .returning(|| Ok(50)); - parachain_rpc - .expect_execute_replace() - .times(1) - .returning(|_, _, _| Ok(())); + parachain_rpc.expect_execute_replace().times(1).returning(|_, _| Ok(())); parachain_rpc .expect_wait_for_block_in_relay() .times(1) @@ -1062,8 +1061,12 @@ mod tests { mock_bitcoin.expect_wait_for_transaction_metadata().returning(|_, _| { Ok(TransactionMetadata { txid: Txid::all_zeros(), - proof: vec![], - raw_tx: vec![], + proof: RawTransactionProof { + coinbase_tx_proof: vec![], + raw_coinbase_tx: vec![], + raw_user_tx: vec![], + user_tx_proof: vec![], + }, block_height: 0, block_hash: BlockHash::all_zeros(), fee: None, diff --git a/vault/src/issue.rs b/vault/src/issue.rs index a621df503..e8b063461 100644 --- a/vault/src/issue.rs +++ b/vault/src/issue.rs @@ -296,12 +296,9 @@ async fn process_transaction_and_execute_issue( return Ok(()); } - // found tx, submit proof - let txid = transaction.txid(); - - // bitcoin core is currently blocking, no need to try_join - let raw_tx = bitcoin_core.get_raw_tx(&txid, &block_hash).await?; - let proof = bitcoin_core.get_proof(txid, &block_hash).await?; + let tx_metadata = bitcoin_core + .wait_for_transaction_metadata(transaction.txid(), num_confirmations) + .await?; tracing::info!( "Executing issue #{:?} on behalf of user {:?} with vault {:?}", @@ -309,7 +306,7 @@ async fn process_transaction_and_execute_issue( issue.requester.pretty_print(), issue.vault.pretty_print() ); - match btc_parachain.execute_issue(issue_id, &proof, &raw_tx).await { + match btc_parachain.execute_issue(issue_id, &tx_metadata.proof).await { Ok(_) => (), Err(err) if err.is_issue_completed() => { tracing::info!("Issue #{} has already been completed", issue_id); diff --git a/vault/src/metrics.rs b/vault/src/metrics.rs index a62186473..a227e103c 100644 --- a/vault/src/metrics.rs +++ b/vault/src/metrics.rs @@ -661,7 +661,7 @@ mod tests { use async_trait::async_trait; use bitcoin::{ json, Address, Amount, BitcoinCoreApi, Block, BlockHash, BlockHeader, Error as BitcoinError, Network, - PrivateKey, PublicKey, SatPerVbyte, Transaction, TransactionMetadata, Txid, + PrivateKey, PublicKey, RawTransactionProof, SatPerVbyte, Transaction, TransactionMetadata, Txid, }; use jsonrpc_core::serde_json::{Map, Value}; use runtime::{ @@ -669,9 +669,8 @@ mod tests { subxt::utils::Static, AccountId, AssetMetadata, AssetRegistry, Balance, BlockNumber, BtcAddress, BtcPublicKey, CurrencyId::{self, ForeignAsset, LendToken}, - Error as RuntimeError, ErrorCode, InterBtcIssueRequest, InterBtcRedeemRequest, InterBtcReplaceRequest, - InterBtcVault, LendingAssets, RequestIssueEvent, StatusCode, Token, VaultId, VaultStatus, DOT, H256, IBTC, - INTR, + Error as RuntimeError, InterBtcIssueRequest, InterBtcRedeemRequest, InterBtcReplaceRequest, InterBtcVault, + LendingAssets, RequestIssueEvent, Token, VaultId, VaultStatus, DOT, H256, IBTC, INTR, }; mockall::mock! { @@ -692,7 +691,7 @@ mod tests { #[async_trait] pub trait IssuePallet { async fn request_issue(&self, amount: u128, vault_id: &VaultId) -> Result; - async fn execute_issue(&self, issue_id: H256, merkle_proof: &[u8], raw_tx: &[u8]) -> Result<(), RuntimeError>; + async fn execute_issue(&self, issue_id: H256, raw_proof: &RawTransactionProof) -> Result<(), RuntimeError>; async fn cancel_issue(&self, issue_id: H256) -> Result<(), RuntimeError>; async fn get_issue_request(&self, issue_id: H256) -> Result; async fn get_vault_issue_requests(&self, account_id: AccountId) -> Result, RuntimeError>; @@ -703,7 +702,7 @@ mod tests { #[async_trait] pub trait RedeemPallet { async fn request_redeem(&self, amount: u128, btc_address: BtcAddress, vault_id: &VaultId) -> Result; - async fn execute_redeem(&self, redeem_id: H256, merkle_proof: &[u8], raw_tx: &[u8]) -> Result<(), RuntimeError>; + async fn execute_redeem(&self, redeem_id: H256, raw_proof: &RawTransactionProof) -> Result<(), RuntimeError>; async fn cancel_redeem(&self, redeem_id: H256, reimburse: bool) -> Result<(), RuntimeError>; async fn get_redeem_request(&self, redeem_id: H256) -> Result; async fn get_vault_redeem_requests(&self, account_id: AccountId) -> Result, RuntimeError>; @@ -742,7 +741,7 @@ mod tests { async fn request_replace(&self, vault_id: &VaultId, amount: u128) -> Result<(), RuntimeError>; async fn withdraw_replace(&self, vault_id: &VaultId, amount: u128) -> Result<(), RuntimeError>; async fn accept_replace(&self, new_vault: &VaultId, old_vault: &VaultId, amount_btc: u128, collateral: u128, btc_address: BtcAddress) -> Result<(), RuntimeError>; - async fn execute_replace(&self, replace_id: H256, merkle_proof: &[u8], raw_tx: &[u8]) -> Result<(), RuntimeError>; + async fn execute_replace(&self, replace_id: H256, raw_proof: &RawTransactionProof) -> Result<(), RuntimeError>; async fn cancel_replace(&self, replace_id: H256) -> Result<(), RuntimeError>; async fn get_new_vault_replace_requests(&self, account_id: AccountId) -> Result, RuntimeError>; async fn get_old_vault_replace_requests(&self, account_id: AccountId) -> Result, RuntimeError>; @@ -753,10 +752,6 @@ mod tests { #[async_trait] pub trait SecurityPallet { - async fn get_parachain_status(&self) -> Result; - - async fn get_error_codes(&self) -> Result, RuntimeError>; - /// Gets the current active block number of the parachain async fn get_current_active_block_number(&self) -> Result; } diff --git a/vault/src/replace.rs b/vault/src/replace.rs index b45897b52..b5ee4d0ae 100644 --- a/vault/src/replace.rs +++ b/vault/src/replace.rs @@ -210,7 +210,7 @@ mod tests { use async_trait::async_trait; use bitcoin::{ json, Address, Amount, BitcoinCoreApi, Block, BlockHash, BlockHeader, Error as BitcoinError, Network, - PrivateKey, PublicKey, SatPerVbyte, Transaction, TransactionMetadata, Txid, + PrivateKey, PublicKey, RawTransactionProof, SatPerVbyte, Transaction, TransactionMetadata, Txid, }; use runtime::{ AccountId, Balance, BtcAddress, BtcPublicKey, CurrencyId, Error as RuntimeError, InterBtcReplaceRequest, @@ -329,7 +329,7 @@ mod tests { async fn request_replace(&self, vault_id: &VaultId, amount: u128) -> Result<(), RuntimeError>; async fn withdraw_replace(&self, vault_id: &VaultId, amount: u128) -> Result<(), RuntimeError>; async fn accept_replace(&self, new_vault: &VaultId, old_vault: &VaultId, amount_btc: u128, collateral: u128, btc_address: BtcAddress) -> Result<(), RuntimeError>; - async fn execute_replace(&self, replace_id: H256, merkle_proof: &[u8], raw_tx: &[u8]) -> Result<(), RuntimeError>; + async fn execute_replace(&self, replace_id: H256, raw_proof: &RawTransactionProof) -> Result<(), RuntimeError>; async fn cancel_replace(&self, replace_id: H256) -> Result<(), RuntimeError>; async fn get_new_vault_replace_requests(&self, account_id: AccountId) -> Result, RuntimeError>; async fn get_old_vault_replace_requests(&self, account_id: AccountId) -> Result, RuntimeError>; diff --git a/vault/tests/vault_integration_tests.rs b/vault/tests/vault_integration_tests.rs index 11aec80d3..103a26596 100644 --- a/vault/tests/vault_integration_tests.rs +++ b/vault/tests/vault_integration_tests.rs @@ -85,7 +85,7 @@ where #[tokio::test(flavor = "multi_thread")] #[serial] async fn test_redeem_succeeds() { - test_with_vault(|client, vault_id, vault_provider| async move { + test_with_vault(|_client, vault_id, vault_provider| async move { let relayer_provider = setup_provider(AccountKeyring::Bob).await; let user_provider = setup_provider(AccountKeyring::Dave).await; @@ -149,7 +149,7 @@ async fn test_redeem_succeeds() { #[tokio::test(flavor = "multi_thread")] #[serial] async fn test_replace_succeeds() { - test_with_vault(|client, old_vault_id, old_vault_provider| async move { + test_with_vault(|_client, old_vault_id, old_vault_provider| async move { let relayer_provider = setup_provider(AccountKeyring::Bob).await; let new_vault_provider = setup_provider(AccountKeyring::Eve).await; let new_vault_id = VaultId::new( @@ -262,7 +262,7 @@ async fn test_replace_succeeds() { #[tokio::test(flavor = "multi_thread")] #[serial] async fn test_withdraw_replace_succeeds() { - test_with_vault(|client, old_vault_id, old_vault_provider| async move { + test_with_vault(|_client, old_vault_id, old_vault_provider| async move { let relayer_provider = setup_provider(AccountKeyring::Bob).await; let new_vault_provider = setup_provider(AccountKeyring::Eve).await; let new_vault_id = VaultId::new( @@ -595,7 +595,7 @@ async fn test_issue_overpayment_succeeds() { } }), user_provider - .execute_issue(*issue.issue_id, &metadata.proof, &metadata.raw_tx) + .execute_issue(*issue.issue_id, &metadata.proof) .map(Result::unwrap), ) .await; @@ -1033,7 +1033,7 @@ mod test_with_bitcoind { .unwrap(); parachain_rpc - .execute_issue(*issue.issue_id, &metadata.proof, &metadata.raw_tx) + .execute_issue(*issue.issue_id, &metadata.proof) .await .unwrap(); }