diff --git a/CHANGELOG.md b/CHANGELOG.md index a78323d..2edc5d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,11 +7,31 @@ All notable changes to this project are documented here. The format follows ### Breaking -- `/wallet/sendbegin` requires `expiration_timestamp`: the recipient invoice's - absolute Unix deadline (the earliest one for a batch). A missing, past or - out-of-range value is rejected with `InvalidExpiration`. -- `/wallet/receive` invoices always expire: `duration_seconds: null` now means - the default one hour instead of "never expires". +- rgb-lib moved to `UTEXO-Protocol/rgb-lib-bfa` tag + `v0.3.0-beta.7-upstream-dev-bfa.3`. Its wallet database history diverges + from the previous `0.3.0-beta.46-bfa` tag: use a fresh `data_dir`, do not + open wallets created by the previous build. It speaks proxy protocol v1, + so the RGB proxy must be `rgb-proxy-server` 0.4.0 or later. +- BFA config needs `eth_chain_id` next to `eth_rpc_url`, and BFA is rejected on + mainnet. +- `/wallet/sendbegin` recipients carry their invoice's `public_key`, + `encryption` (default `Required`, as for an invoice without `enc`) and + `expiration_timestamp`; rgb-lib needs the key and the expiration for proxy + recipients. The transfer's `expiration_timestamp` is optional and defaults + to the earliest invoice expiration, minus 10 minutes only for non-donations. + An explicit one must be at least 5 minutes ahead and, for non-donation proxy + transfers, at least 10 minutes before the earliest invoice expiration. +- Responses follow the new rgb-lib models: BFA uses mint rights + (`MintRight` allocations, `Mint` transfers, `mint` in assignment totals) + instead of bridge and link rights; transfers gain + `sender_transport_endpoint` and lose `proxy_recipient_id`; receive data gains + `public_key`; IFA link fields are gone. +- `/wallet/burnbegin` passes `burn_recipient` to rgb-lib as the burn reason + (also accepted as `burn_reason`): 32 bytes, hex, optional `0x`. +- `/wallet/blindreceive` invoices always expire: omitted or null + `duration_seconds` uses a two-hour default. This leaves time to start a BFA + mint, which requires at least one hour remaining on the invoice. Explicit + durations are preserved. ### Added @@ -21,7 +41,8 @@ All notable changes to this project are documented here. The format follows - `/wallet/witnessreceive` creates an RGB invoice paid to a new output of the sender's transaction, so the wallet needs no colorable UTXO up front. It takes the same body as `/wallet/blindreceive`. -- Opt-in BFA support: set `bfa_enabled = true` and `eth_rpc_url` in `[wallet]`. +- Opt-in BFA support: set `bfa_enabled = true`, `eth_rpc_url` and `eth_chain_id` + in `[wallet]`. Wallets then accept the BFA schema and validate BFA consignments against the EVM RPC. BFA stays disabled without the opt-in. - IFA support on every network except mainnet, where rgb-lib does not allow @@ -30,17 +51,18 @@ All notable changes to this project are documented here. The format follows - `/wallet/inflatebegin` and `/wallet/inflateend` create additional IFA tokens using the wallet's inflation rights and a PSBT signed by the client. - `/wallet/burnbegin` and `/wallet/burnend` support burning BFA and IFA assets. - BFA burns require a 32-byte `burn_recipient`, encoded as hex. + An optional 32-byte hex burn reason is accepted as `burn_recipient` or + `burn_reason`. - `/wallet/getconsignment` returns the hex consignment of a send or burn by `asset_id` and `txid`. ### Changed - Responses follow the pinned rgb-lib models: `listassets` has a `bfa` group, - transfers can be `WaitingBroadcast`, allocations can be `BridgeRight` and + transfers can be `WaitingBroadcast`, allocations can be `MintRight` and UTXOs carry `derivation_index`. -- rgb-lib pinned to tag `0.3.0-beta.46-bfa`, with its BFA RGB crate patches - mirrored in `[patch.crates-io]`. +- rgb-lib pinned to `rgb-lib-bfa` tag `v0.3.0-beta.7-upstream-dev-bfa.3`. BFA + is in the published RGB crates, so `[patch.crates-io]` is gone. ## [0.2.0] - 2026-08-03 diff --git a/Cargo.lock b/Cargo.lock index 1a35ac3..edf6ebf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -111,13 +111,13 @@ dependencies = [ "mime", "percent-encoding", "pin-project-lite", - "rand 0.10.1", + "rand 0.10.3", "sha1", "smallvec", "tokio", "tokio-util", "tracing", - "zstd", + "zstd 0.13.3", ] [[package]] @@ -362,8 +362,6 @@ dependencies = [ "amplify_syn", "ascii", "rand 0.8.6", - "serde", - "stringly_conversions", "wasm-bindgen", ] @@ -396,7 +394,6 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afed304556696656d2d71495e1e5f2c4b524a3fb6eb0f2f3778ffc482a40b8a8" dependencies = [ - "serde", "wasm-bindgen", ] @@ -652,9 +649,6 @@ name = "ascii" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" -dependencies = [ - "serde", -] [[package]] name = "async-stream" @@ -765,6 +759,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" + [[package]] name = "base64ct" version = "1.8.3" @@ -792,11 +792,17 @@ dependencies = [ "serde", ] +[[package]] +name = "bdk_coin_select" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43064fa3dd1d3a8b24079cfcb5ede6b785857edc782277f17a1736511ccc1916" + [[package]] name = "bdk_core" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb3028782f6bf14a6df987244333d34e6b272b5a40a53e4879ec2dfd82275a3a" +checksum = "6cbb1373ac1fafacd7adab91f58ab18d455dfb6434d47a647e75315613599cad" dependencies = [ "bitcoin", "hashbrown 0.14.5", @@ -805,9 +811,9 @@ dependencies = [ [[package]] name = "bdk_electrum" -version = "0.24.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00a9846105bf6e751adbb6946b000ff919ce24a15a941cbfe68485549b552a9c" +checksum = "97455296f4f798250eaa5f7aa51fc34298a72fb661784b4c52c190d730ad04f7" dependencies = [ "bdk_core", "electrum-client", @@ -815,33 +821,34 @@ dependencies = [ [[package]] name = "bdk_esplora" -version = "0.22.2" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83986307ea92997c3d051e8c306af8115a05add601e22acb7c1903008e6b614e" +checksum = "5ac04fdd76362dccf600ccf338ff6be7ad6a0d064def5969e4b33c171718e913" dependencies = [ "bdk_core", "esplora-client", ] [[package]] -name = "bdk_file_store" -version = "0.22.0" +name = "bdk_tx" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7b9a98edb58d02b601239832674a9ca9f1465e08e1dd1fac3a0e34004372406" +checksum = "40406c9952f15ed30893826bd83b7d67b6bf11d56574c1efc3db3a5d70cdfcea" dependencies = [ - "bdk_core", - "bincode", - "serde", + "bdk_coin_select", + "miniscript", + "rand 0.8.6", + "rand_core 0.6.4", ] [[package]] name = "bdk_wallet" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1284fb23acc3e3022673712b55f4d5ce7e38aadc2c49bbef830dc3935f0a3289" +checksum = "163ec032e0a5a795ded2a7e47fc40a887c75743326483147e4465159c53abdc4" dependencies = [ "bdk_chain", - "bdk_file_store", + "bdk_tx", "bip39", "bitcoin", "miniscript", @@ -871,15 +878,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - [[package]] name = "bip39" version = "2.2.2" @@ -1446,6 +1444,32 @@ dependencies = [ "cmov", ] +[[package]] +name = "curve25519-dalek" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5eed333089e2e1c1ac8c6c0398e5e2497b4c9926ca6d0365ed1e099afa5bc23" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "curve25519-dalek-derive", + "digest 0.11.3", + "fiat-crypto", + "rustc_version", + "subtle", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "daggy" version = "0.9.0" @@ -1619,6 +1643,27 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" +[[package]] +name = "ed25519" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29fcf32e6c73d1079f83ab4d782de2d81620346a5f38c6237a86a22f8368980a" +dependencies = [ + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ebaa1a2bf1290ab3bfe5a7b771d050ebffab2711c19a81691c683a5144a25de" +dependencies = [ + "curve25519-dalek", + "ed25519", + "sha2 0.11.0", + "subtle", +] + [[package]] name = "either" version = "1.15.0" @@ -1710,7 +1755,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -1760,6 +1805,12 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +[[package]] +name = "fiat-crypto" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" + [[package]] name = "file-format" version = "0.29.0" @@ -1991,11 +2042,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi 6.0.0", "rand_core 0.10.1", "wasip2", "wasip3", + "wasm-bindgen", ] [[package]] @@ -2511,7 +2564,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -2816,7 +2869,7 @@ version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0aeb26bf5e836cc1c341c8106051b573f1766dfa05aa87f0b98be5e51b02303" dependencies = [ - "nix", + "nix 0.29.0", "serde", "winapi", ] @@ -2873,9 +2926,9 @@ dependencies = [ [[package]] name = "miniscript" -version = "12.3.6" +version = "12.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c14116d8342edd3626b0f8e84df16f4e6a76dc04799ba747493403236a1b8ac5" +checksum = "b8343cc1ef1408bd9bdbf69f7aef47017dfab7e6349ec26fddf62e0e9fb5a4cf" dependencies = [ "bech32", "bitcoin", @@ -2955,12 +3008,15 @@ dependencies = [ ] [[package]] -name = "nonasync" -version = "0.1.3" +name = "nix" +version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d81f7335a3fa37124e24461d6164485760d4c056dd92a81a70c23fc28c2b63c8" +checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" dependencies = [ - "amplify", + "bitflags 2.11.1", + "cfg-if", + "cfg_aliases", + "libc", ] [[package]] @@ -3472,7 +3528,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -3525,9 +3581,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.10.1" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" +checksum = "65c9fb96cbc91e3478eaae79a69fcd3f1ae4ad052e471fe6732fff548984b4af" dependencies = [ "chacha20", "getrandom 0.4.2", @@ -3699,16 +3755,14 @@ dependencies = [ [[package]] name = "rgb-aluvm" -version = "0.11.1-rc.4" +version = "0.11.2-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf4d92478fcff567ff0bd9f18684fd954f8426c74f16e5517254b0a2d34023a" +checksum = "21de8dc75b4ce90dffe66cff585053e9f5d2abe6436589c95e6308f0605195f4" dependencies = [ "amplify", "baid64", "blake3", - "getrandom 0.3.4", "half", - "paste", "rgb-ascii-armor", "rgb-strict-encoding", "rgb-strict-types", @@ -3720,9 +3774,9 @@ dependencies = [ [[package]] name = "rgb-ascii-armor" -version = "1.0.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcde5a129540e5911c24930fd854ea658a715b50a690d254273088f907a63516" +checksum = "0b3a739537822793adce1f9539bb3fae3d54a16f409e8353ac85281bdfcec078" dependencies = [ "amplify", "baid64", @@ -3733,8 +3787,9 @@ dependencies = [ [[package]] name = "rgb-consensus" -version = "0.11.1-rc.11" -source = "git+https://github.com/UTEXO-Protocol/rgb-consensus-s-bfa?rev=2faf6118b2f5fe30dd90dd5244d5a7dc32bcd757#2faf6118b2f5fe30dd90dd5244d5a7dc32bcd757" +version = "0.11.2-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd5d6c21f60f24976d8b983b6b5925cd87a4865af8ad6d965d45360518bbfd24" dependencies = [ "amplify", "baid64", @@ -3747,11 +3802,11 @@ dependencies = [ "hex-conservative 1.2.0", "rand 0.9.4", "rgb-aluvm", + "rgb-ascii-armor", "rgb-strict-encoding", "rgb-strict-types", "ripemd", - "secp256k1 0.31.1", - "secp256k1 0.32.0-beta.2", + "secp256k1 0.33.1", "serde", "sha2 0.11.0", "wasm-bindgen", @@ -3759,8 +3814,9 @@ dependencies = [ [[package]] name = "rgb-invoicing" -version = "0.11.1-rc.11" -source = "git+https://github.com/UTEXO-Protocol/rgb-ops-s-bfa?rev=773766b5ca7a9e705ec68c2f9ed0917f505cd6e3#773766b5ca7a9e705ec68c2f9ed0917f505cd6e3" +version = "0.11.2-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76692ae2017e3b4789d2539dcf483b5e5e9f4468030d9ffb619aabeab7cd43b4" dependencies = [ "amplify", "baid64", @@ -3777,17 +3833,20 @@ dependencies = [ [[package]] name = "rgb-lib" version = "0.3.0-beta.7" -source = "git+ssh://git@github.com/UTEXO-Protocol/rgb-lib.git?tag=0.3.0-beta.46-bfa#74a096a5bfbba7744af16c36c3595a24464c134c" +source = "git+https://github.com/UTEXO-Protocol/rgb-lib-bfa?tag=v0.3.0-beta.7-upstream-dev-bfa.3#4b78c2285b7e9d719dd5b465d186b2fe8ad6450b" dependencies = [ "amplify", - "base64 0.22.1", + "base64 0.23.1", "bdk_electrum", "bdk_esplora", "bdk_wallet", "chacha20poly1305", + "ed25519-dalek", "file-format", "hex", - "rand 0.10.1", + "hkdf", + "hmac", + "rand 0.10.3", "reqwest", "rgb-invoicing", "rgb-lib-migration", @@ -3802,6 +3861,7 @@ dependencies = [ "sea-query", "serde", "serde_json", + "sha2 0.11.0", "slog", "slog-async", "slog-term", @@ -3811,13 +3871,14 @@ dependencies = [ "tokio", "url", "walkdir", + "x25519-dalek", "zip", ] [[package]] name = "rgb-lib-migration" version = "0.3.0-beta.5" -source = "git+ssh://git@github.com/UTEXO-Protocol/rgb-lib.git?tag=0.3.0-beta.46-bfa#74a096a5bfbba7744af16c36c3595a24464c134c" +source = "git+https://github.com/UTEXO-Protocol/rgb-lib-bfa?tag=v0.3.0-beta.7-upstream-dev-bfa.3#4b78c2285b7e9d719dd5b465d186b2fe8ad6450b" dependencies = [ "sea-orm-migration", "tokio", @@ -3854,8 +3915,9 @@ dependencies = [ [[package]] name = "rgb-ops" -version = "0.11.1-rc.11" -source = "git+https://github.com/UTEXO-Protocol/rgb-ops-s-bfa?rev=773766b5ca7a9e705ec68c2f9ed0917f505cd6e3#773766b5ca7a9e705ec68c2f9ed0917f505cd6e3" +version = "0.11.2-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0b0b332a32be0e41ce328d534d559e67a0add6381d250bdc188e44eb8777ca7" dependencies = [ "amplify", "baid64", @@ -3863,8 +3925,6 @@ dependencies = [ "electrum-client", "esplora-client", "getrandom 0.3.4", - "nonasync", - "rand 0.9.4", "rgb-aluvm", "rgb-ascii-armor", "rgb-consensus", @@ -3878,9 +3938,9 @@ dependencies = [ [[package]] name = "rgb-psbt-utils" -version = "0.11.1-rc.11" +version = "0.11.2-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9df8f48e7c851814306af91411d7d6f7e8571939d509d4a786597a5eba0efbbf" +checksum = "16167fb6520c432702b42dbc6344c94573ff8873082386c129718a66bcb535ce" dependencies = [ "amplify", "getrandom 0.3.4", @@ -3892,8 +3952,9 @@ dependencies = [ [[package]] name = "rgb-schemas" -version = "0.11.1-rc.11" -source = "git+https://github.com/UTEXO-Protocol/rgb-schemas-s-bfa?rev=238dfe7a89db998107fd87d919bf2757a2746d7f#238dfe7a89db998107fd87d919bf2757a2746d7f" +version = "0.11.2-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "480f3d71e0f1b23b3c75b59255d62de178259e074f4a74410c330c734f52246f" dependencies = [ "amplify", "rgb-aluvm", @@ -3903,9 +3964,9 @@ dependencies = [ [[package]] name = "rgb-strict-encoding" -version = "1.0.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f68326e14d4b627f86634ea324510135cb551e27c5583a74811fa230b0cf34d" +checksum = "43cf06a3afea7b29f92f279a5fabb39215d1287f796a8d1a75ce77dd4f807fd4" dependencies = [ "amplify", "bitcoin", @@ -3916,9 +3977,9 @@ dependencies = [ [[package]] name = "rgb-strict-encoding-derive" -version = "1.0.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33367536cfef03cb582cd9f8f25e193c2298d3ec3112eb59018a376b465f1ed9" +checksum = "473d4bc563f8db593abf7213f82fafc5bc667125f2c39222cd66ead369e60421" dependencies = [ "amplify_syn", "heck 0.5.0", @@ -3929,9 +3990,9 @@ dependencies = [ [[package]] name = "rgb-strict-types" -version = "1.0.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e920bdf61b662a3743572a03696b0d88992eeb7d4f7afa0902915968327b3ce6" +checksum = "5efea5be04a66a22f96580fb8c7fd9558f7c5d74039c096bb024e4f325f4529a" dependencies = [ "amplify", "baid64", @@ -4091,7 +4152,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -4162,7 +4223,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -4277,9 +4338,9 @@ dependencies = [ [[package]] name = "sea-orm" -version = "2.0.2" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a334e83ced3ae3ee44db0f84d1fcf8d2087a1ad9bb9036f00f9f6067156ea197" +checksum = "40bc08e0aa1963295a30473f98d9b4c9c2b0b3a028381e0143cb2647d0a59a32" dependencies = [ "async-stream", "async-trait", @@ -4325,9 +4386,9 @@ dependencies = [ [[package]] name = "sea-orm-cli" -version = "2.0.2" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a53505884d7c907bcf4f7b4ddb1b29425e62fef8b98aea9c99e17781cceb798" +checksum = "04469c4a54e7271fe3fe2b8140102a2cd2eb7e48129a3afcd013b9909ddebc3c" dependencies = [ "chrono", "clap", @@ -4345,9 +4406,9 @@ dependencies = [ [[package]] name = "sea-orm-macros" -version = "2.0.2" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4039a86f9acc4d3b52747508b347dddc6fd725bbc429902ebeb6d26225fc2528" +checksum = "91fa8c81c20fc8cfe78d22d9a2721ca5ef151dc4a8a7b372de218aaba33733fa" dependencies = [ "heck 0.5.0", "itertools 0.14.0", @@ -4361,9 +4422,9 @@ dependencies = [ [[package]] name = "sea-orm-migration" -version = "2.0.2" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd09adbef87100d07131a60a8c5508b53d0cf2136521f654aae47af5e6a097fe" +checksum = "07970137dbbed6daf56a2429db349abfb68ed1c79206957c30d4a0e9ea808c08" dependencies = [ "async-trait", "clap", @@ -4460,24 +4521,12 @@ dependencies = [ [[package]] name = "secp256k1" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c3c81b43dc2d8877c216a3fccf76677ee1ebccd429566d3e67447290d0c42b2" -dependencies = [ - "bitcoin_hashes", - "rand 0.9.4", - "secp256k1-sys 0.11.0", - "serde", -] - -[[package]] -name = "secp256k1" -version = "0.32.0-beta.2" +version = "0.33.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5fdc7d6e800869d3fd60ff857c479bf0a83ea7bf44b389e64461e844204994" +checksum = "d7f404a8dab7a7a5a631e741d8699aa9c8e1d689fc26ccf897c7187565490b69" dependencies = [ "rand 0.9.4", - "secp256k1-sys 0.12.0", + "secp256k1-sys 0.14.1", "serde", ] @@ -4492,18 +4541,9 @@ dependencies = [ [[package]] name = "secp256k1-sys" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb913707158fadaf0d8702c2db0e857de66eb003ccfdda5924b5f5ac98efb38" -dependencies = [ - "cc", -] - -[[package]] -name = "secp256k1-sys" -version = "0.12.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d3be00697c88c00fe102af8dc316038cc2062eab8da646e7463f4c0e70ca9fd" +checksum = "f6b2992d4a3cd244539a7d5d0966aadbe5ca7fb868a5d7e38c29499b9e709bbd" dependencies = [ "cc", ] @@ -4649,16 +4689,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "serde_str_helpers" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b744a7c94f2f3785496af33a0d93857dfc0c521e25c38e993e9c5bb45f09c841" -dependencies = [ - "serde", - "serde_derive", -] - [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -4788,6 +4818,12 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d567dcbaf0049cb8ac2608a76cd95ff9e4412e1899d389ee400918ca7537f5" + [[package]] name = "simd-adler32" version = "0.3.10" @@ -5027,7 +5063,7 @@ dependencies = [ "log", "md-5", "memchr", - "rand 0.10.1", + "rand 0.10.3", "rust_decimal", "serde", "serde_json", @@ -5087,16 +5123,6 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "046d549b88a663d317cd04d39696fca5f77db4db44e1db65bfb9dee7f9e680bd" -[[package]] -name = "stringly_conversions" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff63080f492dd4d289ffcaed8d7ece38adfb423db910eb342c0e04d409536a7a" -dependencies = [ - "paste", - "serde_str_helpers", -] - [[package]] name = "stringprep" version = "0.1.5" @@ -5301,7 +5327,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix 1.1.4", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -5310,7 +5336,7 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -5703,9 +5729,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typed-path" -version = "0.12.3" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e" +checksum = "9ce898823d8cd0f028662d2f349694b1af0a5cc9be83f1d213a00d3147445e1e" [[package]] name = "typeid" @@ -6092,7 +6118,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -6177,7 +6203,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -6186,16 +6212,7 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", + "windows-targets", ] [[package]] @@ -6213,31 +6230,14 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] @@ -6246,96 +6246,48 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - [[package]] name = "winnow" version = "0.7.15" @@ -6463,6 +6415,16 @@ dependencies = [ "tap", ] +[[package]] +name = "x25519-dalek" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7e8131a03190127fb2263afc72b322ecadae46b6ff8c6f399ff5d02f5559af6" +dependencies = [ + "curve25519-dalek", + "rand_core 0.10.1", +] + [[package]] name = "yansi" version = "1.0.1" @@ -6574,15 +6536,18 @@ dependencies = [ [[package]] name = "zip" -version = "8.6.0" +version = "9.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d04a6b5381502aa6087c94c669499eb1602eb9c5e8198e534de571f7154809b" +checksum = "ec2e3161de27091f18a831d9494e9c53378a34833af5c1dda35ad85849da18d6" dependencies = [ "crc32fast", + "getrandom 0.4.2", "indexmap", "memchr", + "nix 0.31.3", "typed-path", - "zstd", + "windows-sys 0.61.2", + "zstd 0.14.3", ] [[package]] @@ -6603,7 +6568,16 @@ version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" dependencies = [ - "zstd-safe", + "zstd-safe 7.2.4", +] + +[[package]] +name = "zstd" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3ae9bf90af6e40197bef767eeefc46ad6397420fe873830bd85e5fbbfb3c70" +dependencies = [ + "zstd-safe 8.2.0", ] [[package]] @@ -6615,11 +6589,20 @@ dependencies = [ "zstd-sys", ] +[[package]] +name = "zstd-safe" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded520f16b29e1a9b771c722221b2b0a0398c2316d9bb3c42b458a5414d7434b" +dependencies = [ + "zstd-sys", +] + [[package]] name = "zstd-sys" -version = "2.0.16+zstd.1.5.7" +version = "2.1.2+zstd.1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" +checksum = "481ada9c543b2323cbb7bbdbb5249177fbf0188ed959ed2c15e4b22421c84ed3" dependencies = [ "cc", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index ef7600d..c68a942 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ resolver = "3" # different revisions — the wallet descriptors they derive must match exactly # or client-side signatures will not validate server-side. [workspace.dependencies] -rgb-lib = { git = "ssh://git@github.com/UTEXO-Protocol/rgb-lib.git", tag = "0.3.0-beta.46-bfa", default-features = false, features = ["electrum", "esplora"] } +rgb-lib = { git = "https://github.com/UTEXO-Protocol/rgb-lib-bfa", tag = "v0.3.0-beta.7-upstream-dev-bfa.3", default-features = false, features = ["electrum", "esplora"] } [dependencies] actix = "0.13.5" @@ -47,12 +47,3 @@ toml = "0.9.6" [dev-dependencies] # pins the wire format of the response models in tests serde_json = "1.0" - -# The BFA tags of rgb-lib build against BFA forks of the RGB crates through -# [patch.crates-io], which Cargo does not propagate to dependents. Keep these -# revisions in sync with the pinned rgb-lib tag. -[patch.crates-io] -rgb-consensus = { git = "https://github.com/UTEXO-Protocol/rgb-consensus-s-bfa", rev = "2faf6118b2f5fe30dd90dd5244d5a7dc32bcd757" } -rgb-ops = { git = "https://github.com/UTEXO-Protocol/rgb-ops-s-bfa", rev = "773766b5ca7a9e705ec68c2f9ed0917f505cd6e3" } -rgb-invoicing = { git = "https://github.com/UTEXO-Protocol/rgb-ops-s-bfa", rev = "773766b5ca7a9e705ec68c2f9ed0917f505cd6e3" } -rgb-schemas = { git = "https://github.com/UTEXO-Protocol/rgb-schemas-s-bfa", rev = "238dfe7a89db998107fd87d919bf2757a2746d7f" } diff --git a/README.md b/README.md index 08e12fa..f780c93 100644 --- a/README.md +++ b/README.md @@ -31,9 +31,11 @@ btc_rpc_user = "dev" btc_rpc_password = "dev" indexer_address = "tcp://127.0.0.1:50001" proxy_address = ["rpc://127.0.0.1:3000/json-rpc"] -# Optional BFA support; validation of BFA consignments reads the EVM RPC. +# Optional BFA support (not on mainnet); BFA mints are checked on the EVM RPC, +# which must be on `eth_chain_id` and serve the logs of the whole chain. # bfa_enabled = true # eth_rpc_url = "http://127.0.0.1:8545" +# eth_chain_id = 11155111 ``` Wallets are not configured in the file — they are registered at runtime via diff --git a/docker-compose.yaml b/docker-compose.yaml index f38ddab..7b5d540 100755 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -35,7 +35,7 @@ services: # Official RGB proxy — the image rgb-lib's own test suite runs against, so it # is guaranteed to speak the proxy protocol version rgb-lib requires. rgb_proxy: - image: ghcr.io/rgb-tools/rgb-proxy-server:0.3.0 + image: ghcr.io/rgb-tools/rgb-proxy-server:0.4.0 ports: - "127.0.0.1:3000:3000" restart: always diff --git a/docs/swagger/swagger.yaml b/docs/swagger/swagger.yaml index a19c30d..ffd35b7 100644 --- a/docs/swagger/swagger.yaml +++ b/docs/swagger/swagger.yaml @@ -1599,19 +1599,6 @@ components: - type: string - type: 'null' title: Reject List Url - issuance_link_right_outpoint: - $ref: '#/components/schemas/Outpoint' - description: >- - Where the link right was allocated at issuance. Omitted when the - asset has none. - linked_from_asset_id: - type: string - title: Linked From Asset Id - description: Parent asset declared at genesis. Omitted when none. - linked_to_asset_id: - type: string - title: Linked To Asset Id - description: Child asset linked to this one. Omitted when none. type: object required: - asset_id @@ -1774,8 +1761,7 @@ components: - type: string enum: - NonFungible - - BridgeRight - - LinkRight + - MintRight - Any - type: object properties: @@ -1794,8 +1780,7 @@ components: title: Assignment description: >- An RGB assignment, serialized as an externally tagged enum: unit - variants are plain strings ("NonFungible", "BridgeRight", "LinkRight", - "Any"), + variants are plain strings ("NonFungible", "MintRight", "Any"), data-carrying variants are single-key objects (e.g. {"Fungible": 100}). Balance: properties: @@ -1917,10 +1902,9 @@ components: - type: 'null' title: Burn Recipient description: >- - Hex-encoded 32 bytes (optional `0x` prefix) identifying who receives - the released funds. Required for BFA; optional and unused for IFA. - When provided, valid hex encoding and a 32-byte length are required - for both schemas. + Hex-encoded 32 bytes (optional `0x` prefix) committed in the burn as + rgb-lib's burn reason, e.g. a hash binding the burn to the receiver + of the released funds. Also accepted as `burn_reason`. fee_rate: type: integer title: Fee Rate @@ -2148,12 +2132,17 @@ components: batch_transfer_idx: type: integer title: Batch Transfer Idx + public_key: + type: string + title: Public Key + description: Recipient's Ed25519 public key (hex), as embedded in the invoice. type: object required: - invoice - recipient_id - expiration_timestamp - batch_transfer_idx + - public_key title: ReceiveData Recipient: properties: @@ -2172,6 +2161,33 @@ components: type: string type: array title: Transport Endpoints + public_key: + anyOf: + - type: string + - type: 'null' + title: Public Key + description: >- + The invoice `pk` field (hex Ed25519 key). Required when + `transport_endpoints` is not empty. + encryption: + type: string + enum: + - Required + - Unsupported + - Optional + default: Required + title: Encryption + description: >- + The invoice `enc` field: `Unsupported` for `enc=0`, `Optional` for + `enc=1`, `Required` when absent. + expiration_timestamp: + anyOf: + - type: integer + - type: 'null' + title: Expiration Timestamp + description: >- + The invoice expiration. Required when `transport_endpoints` is not + empty. type: object required: - recipient_id @@ -2237,12 +2253,15 @@ components: Amount to request, in the asset's smallest unit. Omit it, or send `0`, to accept any amount. duration_seconds: - type: integer + anyOf: + - type: integer + - type: 'null' title: Duration Seconds description: >- How long the invoice stays valid, in seconds from now. Invoices - always expire: `null` uses the default. - default: 3600 + always expire: omitted or `null` uses the two-hour default. BFA mint + must begin with at least one hour remaining on the invoice. + default: 7200 type: object title: RgbInvoiceRequestModel description: Request model for RGB invoices. @@ -2298,15 +2317,19 @@ components: title: Min Confirmations default: 1 expiration_timestamp: - type: integer + anyOf: + - type: integer + - type: 'null' title: Expiration Timestamp description: >- - Absolute Unix deadline from the recipient invoice; for a batch, the - earliest one. Must be in the future. + Absolute Unix deadline of the transfer. At least 5 minutes ahead. + Non-donation proxy transfers must also expire at least 10 minutes + before the earliest invoice expiration. Defaults to the earliest + recipient invoice expiration, minus 10 minutes only when donation + is false. type: object required: - recipient_map - - expiration_timestamp title: SendAssetBeginRequestModel SendAssetEndRequestModel: properties: @@ -2452,11 +2475,6 @@ components: - type: string - type: 'null' title: Recipient Id - proxy_recipient_id: - anyOf: - - type: string - - type: 'null' - title: Proxy Recipient Id receive_utxo: anyOf: - $ref: '#/components/schemas/Outpoint' @@ -2475,6 +2493,13 @@ components: $ref: '#/components/schemas/TransferTransportEndpoint' type: array title: Transport Endpoints + description: Recipient transport endpoints for the transfer. + sender_transport_endpoint: + anyOf: + - type: string + - type: 'null' + title: Sender Transport Endpoint + description: The sender's proxy, where recipients post their ACK. invoice_string: anyOf: - type: string @@ -2520,7 +2545,7 @@ components: - Send - Inflation - Burn - - Link + - Mint title: TransferKind TransferStatus: type: string diff --git a/src/wallet/config.rs b/src/wallet/config.rs index 1ed2359..3319c14 100644 --- a/src/wallet/config.rs +++ b/src/wallet/config.rs @@ -1,6 +1,7 @@ use rgb_lib::{AssetSchema, BitcoinNetwork}; use serde::{Deserialize, Serialize}; use serde_valid::toml::FromTomlStr; +use std::collections::HashMap; use std::path::Path; #[derive(Serialize, Deserialize, Default, Clone, Debug, serde_valid::Validate)] @@ -13,9 +14,12 @@ pub struct Config { pub btc_rpc_password: String, pub indexer_address: String, pub proxy_address: Vec, - /// EVM RPC used for BFA consignment validation. Optional for NIA wallets. + /// EVM RPC used for BFA mint validation. Optional for NIA wallets. #[serde(default)] pub eth_rpc_url: Option, + /// EIP-155 chain ID of `eth_rpc_url`; rgb-lib checks the RPC is on it. + #[serde(default)] + pub eth_chain_id: Option, /// Opt in to BFA validation; existing NIA deployments remain unchanged. #[serde(default)] pub bfa_enabled: bool, @@ -25,15 +29,26 @@ impl Config { pub fn supported_schemas(&self) -> Result, rgb_lib::Error> { let mut schemas = vec![AssetSchema::Nia]; if self.bfa_enabled { + // rgb-lib rejects the BFA schema on mainnet. + if self.net() == BitcoinNetwork::Mainnet { + return Err(rgb_lib::Error::CannotUseSchemaOnMainnet { + asset_schema: AssetSchema::Bfa, + }); + } if self .eth_rpc_url .as_deref() .is_none_or(|url| url.trim().is_empty()) { - return Err(rgb_lib::Error::InvalidEthRpcUrl { + return Err(rgb_lib::Error::InvalidEvmRpcUrl { details: "BFA requires an explicit EVM RPC".into(), }); } + if self.eth_chain_id.is_none() { + return Err(rgb_lib::Error::InvalidEvmRpcUrl { + details: "BFA requires the chain ID of the EVM RPC".into(), + }); + } schemas.push(AssetSchema::Bfa); } // rgb-lib rejects the IFA schema on mainnet. @@ -43,12 +58,11 @@ impl Config { Ok(schemas) } - /// The EVM RPC passed to `OnlineOptions`, only when BFA is enabled. - pub fn eth_rpc(&self) -> Option { - if self.bfa_enabled { - self.eth_rpc_url.clone() - } else { - None + /// The EVM RPCs passed to `OnlineOptions`, only when BFA is enabled. + pub fn evm_rpc_urls(&self) -> HashMap { + match (self.bfa_enabled, self.eth_chain_id, &self.eth_rpc_url) { + (true, Some(chain_id), Some(url)) => HashMap::from([(chain_id, url.clone())]), + _ => HashMap::new(), } } @@ -92,24 +106,40 @@ mod tests { Config::default().supported_schemas().unwrap(), vec![AssetSchema::Nia] ); - assert!(Config::default().eth_rpc().is_none()); + assert!(Config::default().evm_rpc_urls().is_empty()); let mut cfg = Config { bfa_enabled: true, + network: "mainnet".into(), + eth_rpc_url: Some("http://127.0.0.1:31014".into()), + eth_chain_id: Some(11155111), ..Default::default() }; + assert!(matches!( + cfg.supported_schemas(), + Err(rgb_lib::Error::CannotUseSchemaOnMainnet { .. }) + )); + cfg.network = "regtest".into(); + cfg.eth_rpc_url = None; + cfg.eth_chain_id = None; assert!(cfg.supported_schemas().is_err()); cfg.eth_rpc_url = Some("http://127.0.0.1:31014".into()); + assert!(cfg.supported_schemas().is_err()); + cfg.eth_chain_id = Some(11155111); assert_eq!( cfg.supported_schemas().unwrap(), - vec![AssetSchema::Nia, AssetSchema::Bfa] + vec![AssetSchema::Nia, AssetSchema::Bfa, AssetSchema::Ifa] + ); + assert_eq!( + cfg.evm_rpc_urls(), + HashMap::from([(11155111, "http://127.0.0.1:31014".to_string())]) ); - assert_eq!(cfg.eth_rpc().as_deref(), Some("http://127.0.0.1:31014")); // An RPC configured without the opt-in stays unused. let unused = Config { eth_rpc_url: Some("http://127.0.0.1:31014".into()), + eth_chain_id: Some(11155111), ..Default::default() }; - assert!(unused.eth_rpc().is_none()); + assert!(unused.evm_rpc_urls().is_empty()); } #[test] diff --git a/src/wallet/context.rs b/src/wallet/context.rs index e02645d..5b85cb4 100644 --- a/src/wallet/context.rs +++ b/src/wallet/context.rs @@ -296,7 +296,7 @@ impl WalletCtx { } pub async fn send_begin(&self, req: SendBeginReq) -> Result { - req.validate_expiration()?; + req.expiration()?; let (resp, recv) = oneshot::channel(); let cmd = WalletCmd::SendBegin { req, resp }; self.send_cmd(cmd, recv).await diff --git a/src/wallet/errors.rs b/src/wallet/errors.rs index 91e58ad..8def256 100644 --- a/src/wallet/errors.rs +++ b/src/wallet/errors.rs @@ -66,32 +66,14 @@ pub fn error_kind(err: &Error) -> String { /// silently landing in some catch-all bucket. pub fn classify(err: &Error) -> ErrorClass { match err { - Error::PsbtOperationNotFound { .. } => ErrorClass::NotFound, - Error::BatchTransferAlreadyExists { .. } - | Error::CannotProvideOutOfBandAck { .. } - | Error::CannotProvideOutOfBandConsignment { .. } - | Error::InsufficientConfirmations { .. } - | Error::InvalidPsbtOperationStatus { .. } - | Error::WalletSettingMismatch { .. } - | Error::UnexpectedTransfer { .. } - | Error::UnsafeTransferHistory { .. } => ErrorClass::Conflict, - Error::InexistentWalletManifest { .. } => ErrorClass::Internal, - Error::UnsupportedBridge { .. } - | Error::UnsupportedWalletManifestVersion { .. } => ErrorClass::Unsupported, - Error::InvalidEthRpcUrl { .. } - | Error::InvalidBurnRecipient { .. } - | Error::MissingBurnRecipient - | Error::NoBridgeRights - | Error::NoMaxAllocationsPerUtxo - | Error::WitnessOutputMismatch { .. } => ErrorClass::BadInput, - // -- the entity asked for does not exist ----------------------------- Error::AssetNotFound { .. } | Error::BatchTransferNotFound { .. } | Error::MultisigOperationNotFound { .. } | Error::UnknownTransfer { .. } | Error::NoConsignment - | Error::VssBackupNotFound => ErrorClass::NotFound, + | Error::NoMedia + | Error::OperationNotFound => ErrorClass::NotFound, // -- specific, actionable input problems ----------------------------- Error::InvalidAddress { .. } => ErrorClass::InvalidAddress, @@ -117,6 +99,7 @@ pub fn classify(err: &Error) -> ErrorClass { | Error::InvalidRecipientNetwork | Error::InvalidTransportEndpoint { .. } | Error::InvalidTransportEndpoints { .. } + | Error::InvoiceExpired | Error::NoValidTransportEndpoint | Error::RecipientIDDuplicated | Error::UnsupportedTransportType => ErrorClass::InvalidRecipient, @@ -126,22 +109,43 @@ pub fn classify(err: &Error) -> ErrorClass { | Error::CannotAbortPendingVanillaTx | Error::CannotChangeOnline | Error::CannotDeleteBatchTransfer + | Error::CannotEndTransfer { .. } | Error::CannotFailBatchTransfer + | Error::CannotProvideOutOfBandAck { .. } + | Error::CannotProvideOutOfBandConsignment { .. } | Error::FileAlreadyExists { .. } + | Error::InvoiceNotReserved + | Error::InvoiceQuotaExceeded + | Error::LastMintRight + // The mint event may still appear on the EVM chain: retry later. + | Error::MintEventNotFound { .. } + | Error::MultisigLegacyOperation { .. } | Error::MultisigOperationInProgress | Error::MultisigTransferStatusMismatch + | Error::OperationExpired + | Error::ProxyCannotChange { .. } + | Error::ProxyTooManyMedia + | Error::ProxyTxAlreadyReleased | Error::RecipientIDAlreadyUsed - | Error::WalletDirAlreadyExists { .. } => ErrorClass::Conflict, + | Error::SignedTxCleared + | Error::TransferExpired + | Error::TransferIDDeleted + | Error::TransferIDInUse + | Error::WalletDirAlreadyExists { .. } + | Error::WalletSettingMismatch { .. } => ErrorClass::Conflict, // -- asked for something this wallet cannot do ----------------------- - Error::AddressReuseDisabled - | Error::CannotUseIfaOnMainnet + Error::CannotUseSchemaOnMainnet { .. } + // The asset's bridge lives on an EVM chain this node has no RPC for. + | Error::NoEvmRpcUrl { .. } | Error::NoSupportedSchemas | Error::UnsupportedBackupVersion { .. } | Error::UnsupportedBurn { .. } | Error::UnsupportedInflation { .. } | Error::UnsupportedLayer1 { .. } - | Error::UnsupportedSchema { .. } => ErrorClass::Unsupported, + | Error::UnsupportedMint { .. } + | Error::UnsupportedSchema { .. } + | Error::UnsupportedWalletManifestVersion { .. } => ErrorClass::Unsupported, // -- a dependency is down or unreachable ----------------------------- Error::CannotEstimateFees @@ -149,36 +153,43 @@ pub fn classify(err: &Error) -> ErrorClass { | Error::FailedBroadcast { .. } | Error::FailedIssuance { .. } | Error::Indexer { .. } - | Error::MpcProvider { .. } | Error::MultisigHubService { .. } | Error::Network { .. } | Error::Offline | Error::OnlineNeeded | Error::Proxy { .. } + | Error::ProxyDraining | Error::RejectListService { .. } - | Error::RestClientBuild { .. } - | Error::VssAuth { .. } - | Error::VssError { .. } - | Error::VssVersionConflict { .. } => ErrorClass::Unavailable, + | Error::RestClientBuild { .. } => ErrorClass::Unavailable, // -- our fault ------------------------------------------------------- Error::Database { .. } | Error::Inconsistency { .. } | Error::InexistentDataDir + | Error::InexistentWalletManifest { .. } | Error::Internal { .. } | Error::IO { .. } | Error::MultisigCannotMarkOperationProcessed { .. } | Error::MultisigCannotRespondToOperation { .. } | Error::MultisigUnexpectedData { .. } - | Error::RestoredBackupInconsistent { .. } | Error::RgbInspection { .. } + // Node configuration: the EVM RPC and the proxy auth keys are ours. + | Error::DuplicateProxyAuthKey { .. } + | Error::EvmChainIdMismatch { .. } + | Error::InvalidEvmRpcUrl { .. } + | Error::InvalidProxyAuthKey { .. } + | Error::InvalidProxyAuthSignature // The node keeps watch-only wallets on purpose (signing happens in the // client), so hitting a signing-only path is a bug on our side. | Error::WatchOnly => ErrorClass::Internal, // -- everything else is invalid request data ------------------------- Error::BitcoinNetworkMismatch + | Error::ConsignmentRejected { .. } + | Error::ConsignmentValidationFailed { .. } | Error::EmptyFile { .. } + | Error::ExpirationInvalid + | Error::HashMismatch | Error::FingerprintMismatch | Error::InvalidAmountZero | Error::InvalidAssignment @@ -186,12 +197,16 @@ pub fn classify(err: &Error) -> ErrorClass { | Error::InvalidBitcoinKeys | Error::InvalidBitcoinNetwork { .. } | Error::InvalidColoringInfo { .. } + | Error::InvalidBurnReason { .. } | Error::InvalidConsignment - | Error::InvalidContractLink { .. } + | Error::InvalidContentSignature | Error::InvalidCosigner { .. } | Error::InvalidDetails { .. } | Error::InvalidElectrum { .. } + | Error::InvalidEncryptedBlob { .. } + | Error::InvalidEndpointAuthSignature | Error::InvalidEstimationBlocks + | Error::InvalidEvmContractAddress { .. } | Error::InvalidExpiration | Error::InvalidFilePath { .. } | Error::InvalidFingerprint @@ -202,22 +217,27 @@ pub fn classify(err: &Error) -> ErrorClass { | Error::InvalidPrecision { .. } | Error::InvalidPubkey { .. } | Error::InvalidRejectListUrl { .. } - | Error::InvalidRightOutpoint { .. } | Error::InvalidTicker { .. } | Error::InvalidTxid | Error::InvalidVanillaKeychain | Error::InvalidWitnessVersion { .. } + // The mint will never be confirmed. + | Error::MintEventOutOfScanWindow { .. } | Error::MultisigUserNotCosigner | Error::NoBurnAmount | Error::NoCosignersSupplied | Error::NoInflationAmounts | Error::NoIssuanceAmounts | Error::NoKeysSupplied + | Error::NoMaxAllocationsPerUtxo + | Error::NoMintRights + | Error::OffchainTxidNotFound { .. } | Error::OutputBelowDustLimit | Error::TooHighInflationAmounts | Error::TooHighIssuanceAmounts | Error::TooManyCosigners | Error::UnknownRgbSchema { .. } + | Error::WrongOperation { .. } | Error::WrongPassword => ErrorClass::BadInput, } } diff --git a/src/wallet/models.rs b/src/wallet/models.rs index 93ef8e4..f6d2266 100644 --- a/src/wallet/models.rs +++ b/src/wallet/models.rs @@ -52,8 +52,20 @@ pub struct ReceiveReq { pub min_confirmations: u8, } +// BFA mint_begin needs at least one hour left on the invoice. Leave another +// hour for the caller to start the mint after creating the invoice. +const DEFAULT_RECEIVE_DURATION_SECS: u32 = 7200; + fn default_duration_seconds() -> Option { - Some(3600) + Some(DEFAULT_RECEIVE_DURATION_SECS) +} + +impl ReceiveReq { + /// Omitted and null durations both use the default; explicit values win. + pub fn duration_seconds(&self) -> u32 { + self.duration_seconds + .unwrap_or(DEFAULT_RECEIVE_DURATION_SECS) + } } /// Which kind of RGB invoice a receive creates. @@ -143,6 +155,22 @@ pub struct SendRecipient { pub witness_data: Option, pub amount: u64, pub transport_endpoints: Vec, + /// Recipient's Ed25519 public key (hex), the invoice `pk` field. rgb-lib + /// requires it when `transport_endpoints` is not empty. + #[serde(default)] + pub public_key: Option, + /// The invoice `enc` field. Per the invoice spec, a missing `enc` means + /// encryption is required. + #[serde(default = "default_encryption")] + pub encryption: rgb_lib::Encryption, + /// The invoice expiration. rgb-lib requires it when `transport_endpoints` + /// is not empty. + #[serde(default)] + pub expiration_timestamp: Option, +} + +fn default_encryption() -> rgb_lib::Encryption { + rgb_lib::Encryption::Required } impl From for rgb_lib::wallet::Recipient { @@ -152,6 +180,9 @@ impl From for rgb_lib::wallet::Recipient { witness_data: r.witness_data, assignment: rgb_lib::Assignment::Fungible(r.amount), transport_endpoints: r.transport_endpoints, + public_key: r.public_key, + encryption: r.encryption, + expiration_timestamp: r.expiration_timestamp, } } } @@ -166,21 +197,47 @@ pub struct SendBeginReq { pub fee_rate: u64, #[serde(default = "default_min_confirmations")] pub min_confirmations: u8, - /// Absolute Unix deadline from the recipient invoice (earliest for a batch). - /// Required by the pinned RGB protocol; an arbitrary default can outlive the invoice. - pub expiration_timestamp: u64, + /// Absolute Unix deadline of the transfer. rgb-lib requires it to be at + /// least 5 minutes ahead and, for non-donation proxy transfers, at least + /// 10 minutes before the earliest invoice expiration. When omitted, it + /// defaults to the earliest invoice expiration, minus that margin only + /// for non-donations. + #[serde(default)] + pub expiration_timestamp: Option, } +/// rgb-lib's margin between the transfer and the earliest invoice expiration. +const INVOICE_EXPIRATION_MARGIN_SECS: u64 = 600; + impl SendBeginReq { - pub fn validate_expiration(&self) -> Result<(), rgb_lib::Error> { + /// The transfer deadline: the requested one, or the earliest recipient + /// invoice expiration minus rgb-lib's margin for non-donations. Donations + /// do not wait for ACKs and need no margin. Range checks beyond "in the + /// future" are left to rgb-lib, which knows the exact rules. + pub fn expiration(&self) -> Result { + let expiration = match self.expiration_timestamp { + Some(expiration) => expiration, + None => self + .recipient_map + .values() + .flatten() + .filter_map(|r| r.expiration_timestamp) + .min() + .ok_or(rgb_lib::Error::InvalidExpiration)? + .saturating_sub(if self.donation { + 0 + } else { + INVOICE_EXPIRATION_MARGIN_SECS + }), + }; let now = std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .map_err(|_| rgb_lib::Error::InvalidExpiration)? .as_secs(); - if self.expiration_timestamp <= now || self.expiration_timestamp > i64::MAX as u64 { + if expiration <= now || expiration > i64::MAX as u64 { return Err(rgb_lib::Error::InvalidExpiration); } - Ok(()) + Ok(expiration) } } @@ -189,8 +246,9 @@ impl SendBeginReq { pub struct BurnBeginReq { pub asset_id: String, pub amount: u64, - /// Hex-encoded 32-byte recipient of the released funds, required for BFA. - #[serde(default)] + /// Hex-encoded 32 bytes committed in the burn (rgb-lib's `burn_reason`), + /// e.g. the recipient of the released funds. `burn_reason` is accepted too. + #[serde(default, alias = "burn_reason")] pub burn_recipient: Option, #[serde(default = "default_fee_rate")] pub fee_rate: u64, @@ -199,18 +257,12 @@ pub struct BurnBeginReq { } impl BurnBeginReq { - /// Decode the hex recipient; the 32-byte length is enforced by rgb-lib. - pub fn burn_recipient_bytes(&self) -> Result>, rgb_lib::Error> { + /// The burn reason for rgb-lib: hex without the optional `0x` prefix. + /// rgb-lib checks the hex and the 32-byte length. + pub fn burn_reason(&self) -> Option { self.burn_recipient .as_deref() - .map(|r| { - hex::decode(r.strip_prefix("0x").unwrap_or(r)).map_err(|e| { - rgb_lib::Error::InvalidDetails { - details: format!("burn_recipient is not valid hex: {e}"), - } - }) - }) - .transpose() + .map(|r| r.strip_prefix("0x").unwrap_or(r).to_owned()) } } @@ -239,9 +291,30 @@ pub struct GetConsignmentRes { } #[cfg(test)] -mod burn_tests { +mod tests { use super::*; + #[test] + fn receive_omitted_and_null_durations_leave_time_to_start_a_mint() { + for body in [ + serde_json::json!({}), + serde_json::json!({"duration_seconds": null}), + ] { + let req: ReceiveReq = serde_json::from_value(body).unwrap(); + assert_eq!(req.duration_seconds(), 7200); + // The new library needs a full hour remaining at mint_begin, + // including when it starts after the invoice was returned. + assert!(req.duration_seconds() - 2 >= 3600); + } + for duration in [0, 540, 3600, 10800] { + let req: ReceiveReq = serde_json::from_value(serde_json::json!({ + "duration_seconds": duration, + })) + .unwrap(); + assert_eq!(req.duration_seconds(), duration); + } + } + fn burn_req(recipient: Option<&str>) -> BurnBeginReq { serde_json::from_value(serde_json::json!({ "asset_id": "rgb:x", @@ -252,22 +325,112 @@ mod burn_tests { } #[test] - fn burn_recipient_is_hex_with_optional_prefix() { - assert_eq!(burn_req(None).burn_recipient_bytes().unwrap(), None); + fn burn_reason_drops_the_optional_prefix() { + assert_eq!(burn_req(None).burn_reason(), None); let hex32 = "11".repeat(32); + assert_eq!(burn_req(Some(&hex32)).burn_reason(), Some(hex32.clone())); assert_eq!( - burn_req(Some(&hex32)).burn_recipient_bytes().unwrap(), - Some(vec![0x11; 32]) + burn_req(Some(&format!("0x{hex32}"))).burn_reason(), + Some(hex32.clone()) ); + let by_new_name: BurnBeginReq = serde_json::from_value(serde_json::json!({ + "asset_id": "rgb:x", + "amount": 1, + "burn_reason": hex32, + })) + .unwrap(); + assert_eq!(by_new_name.burn_reason(), Some("11".repeat(32))); + } + + #[test] + fn send_expiration_defaults_to_the_earliest_invoice_minus_the_margin() { + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs(); + let req = |expiration: Option, invoices: &[Option]| -> SendBeginReq { + let recipients: Vec<_> = invoices + .iter() + .map(|exp| { + serde_json::json!({ + "recipient_id": "r", + "amount": 1, + "transport_endpoints": [], + "expiration_timestamp": exp, + }) + }) + .collect(); + serde_json::from_value(serde_json::json!({ + "recipient_map": { "rgb:x": recipients }, + "expiration_timestamp": expiration, + })) + .unwrap() + }; + let (early, late) = (now + 3600, now + 7200); assert_eq!( - burn_req(Some(&format!("0x{hex32}"))) - .burn_recipient_bytes() + req(None, &[Some(late), Some(early), None]) + .expiration() .unwrap(), - Some(vec![0x11; 32]) + early - 600 + ); + assert_eq!( + req(Some(now + 900), &[Some(early)]).expiration().unwrap(), + now + 900 ); + for bad in [ + req(None, &[None]), + req(Some(now), &[]), + req(None, &[Some(now + 60)]), + ] { + assert!(matches!( + bad.expiration(), + Err(rgb_lib::Error::InvalidExpiration) + )); + } + } + + #[test] + fn send_recipient_encryption_defaults_to_required() { + let r: SendRecipient = serde_json::from_value(serde_json::json!({ + "recipient_id": "r", + "amount": 1, + "transport_endpoints": [], + })) + .unwrap(); + assert_eq!(r.encryption, rgb_lib::Encryption::Required); + } + + #[test] + fn donation_expiration_does_not_reserve_time_for_acks() { + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs(); + let mut req: SendBeginReq = serde_json::from_value(serde_json::json!({ + "donation": true, + "recipient_map": {"rgb:x": [ + {"recipient_id": "later", "amount": 1, + "transport_endpoints": ["rpc://proxy/json-rpc"], + "expiration_timestamp": now + 1200}, + {"recipient_id": "earlier", "amount": 1, + "transport_endpoints": ["rpc://proxy/json-rpc"], + "expiration_timestamp": now + 540}, + ]}, + })) + .unwrap(); + assert_eq!(req.expiration().unwrap(), now + 540); + req.expiration_timestamp = Some(now + 420); + assert_eq!(req.expiration().unwrap(), now + 420); + req.expiration_timestamp = Some(now); + assert!(matches!( + req.expiration(), + Err(rgb_lib::Error::InvalidExpiration) + )); + req.expiration_timestamp = None; + req.donation = false; assert!(matches!( - burn_req(Some("zz")).burn_recipient_bytes(), - Err(rgb_lib::Error::InvalidDetails { .. }) + req.expiration(), + Err(rgb_lib::Error::InvalidExpiration) )); } diff --git a/src/wallet/state.rs b/src/wallet/state.rs index 312ddd7..b4ab75b 100644 --- a/src/wallet/state.rs +++ b/src/wallet/state.rs @@ -1,8 +1,9 @@ -use std::collections::BTreeSet; +use std::collections::{BTreeSet, HashMap}; use rgb_lib::wallet::{ - Balance, BtcBalance, DatabaseType, Online, OnlineOptions, ReceiveData, RgbWalletOpsOffline, - RgbWalletOpsOnline, SinglesigKeys, Wallet, WalletData, + Balance, BtcBalance, DatabaseType, IndexerConfig, IndexerProtocol, Online, OnlineOptions, + ReceiveData, RgbWalletOpsOffline, RgbWalletOpsOnline, SinglesigKeys, Wallet, WalletData, + check_indexer_url, }; use super::Config; @@ -15,13 +16,30 @@ fn unix_now() -> u64 { .unwrap_or_default() } +/// rgb-lib no longer guesses the indexer protocol from the URL when going +/// online, so probe it here (electrum first, then esplora, as before). +fn indexer_config(config: &Config) -> Result { + let url = config.indexer_address.clone(); + Ok(match check_indexer_url(&url, None, config.net())? { + IndexerProtocol::Electrum => IndexerConfig::Electrum { url }, + IndexerProtocol::Esplora => IndexerConfig::Esplora { url }, + IndexerProtocol::Bitcoind => { + return Err(rgb_lib::Error::InvalidIndexer { + details: "bitcoind indexers are not supported".into(), + }); + } + }) +} + pub struct RgbWalletState { pub id: String, pub master_xpub: String, - pub wallet: rgb_lib::Wallet, + pub wallet: Wallet, pub wallet_online: Online, proxy_host: BTreeSet, + /// Our own proxy, where recipients post their ACK when we send. + pub sender_proxy: Option, } impl RgbWalletState { @@ -43,7 +61,6 @@ impl RgbWalletState { database_type: DatabaseType::Sqlite, max_allocations_per_utxo: max_allocations_per_utxo.unwrap_or(5), supported_schemas: config.supported_schemas()?, - reuse_addresses: false, }, SinglesigKeys { account_xpub_colored: xpub_colored, @@ -56,10 +73,14 @@ impl RgbWalletState { )?; let online = wallet.go_online(OnlineOptions { - indexer_url: config.indexer_address.clone(), + indexer_config: indexer_config(config)?, skip_consistency_check: false, vanilla_sync_lookback: 20, - eth_rpc_url: config.eth_rpc(), + // Keep detecting late BTC payments to failed witness receives for a week. + failed_witness_receive_grace_secs: 604800, + // Our proxies run without proxy-level authentication. + proxy_auth_keys: HashMap::new(), + evm_rpc_urls: config.evm_rpc_urls(), })?; Ok(RgbWalletState { @@ -67,6 +88,7 @@ impl RgbWalletState { wallet, wallet_online: online, proxy_host: config.proxy_address.iter().cloned().collect(), + sender_proxy: config.proxy_address.first().cloned(), master_xpub: Default::default(), }) } @@ -76,7 +98,7 @@ impl RgbWalletState { kind: ReceiveKind, asset_id: Option, assigment: rgb_lib::Assignment, - duration_seconds: Option, + duration_seconds: u32, min_confirmations: u8, ) -> Result { let transport_endpoints = self.proxy_host.iter().cloned().collect(); @@ -84,25 +106,29 @@ impl RgbWalletState { // The API takes a duration, rgb-lib wants an absolute unix timestamp: // passing the duration through unchanged lands in 1970 and rgb-lib // rejects it with InvalidExpiration. - // The pinned library requires an expiry. Omitted/null durations use the - // HTTP API's existing one-hour default; they do not mean unbounded. - let expiration_timestamp = - unix_now().saturating_add(u64::from(duration_seconds.unwrap_or(3600))); + // ReceiveReq has already resolved omitted/null durations to the default. + let expiration_timestamp = unix_now().saturating_add(u64::from(duration_seconds)); + // Online is needed to reserve the recipient ID on the proxies. + // No encryption preference: rgb-lib applies its default. let rd = match kind { ReceiveKind::Blind => self.wallet.blind_receive( + Some(self.wallet_online), asset_id, assigment, expiration_timestamp, transport_endpoints, min_confirmations, + None, )?, ReceiveKind::Witness => self.wallet.witness_receive( + Some(self.wallet_online), asset_id, assigment, expiration_timestamp, transport_endpoints, min_confirmations, + None, )?, }; diff --git a/src/wallet/wallet_thread.rs b/src/wallet/wallet_thread.rs index 5335b9b..57b91a5 100644 --- a/src/wallet/wallet_thread.rs +++ b/src/wallet/wallet_thread.rs @@ -502,6 +502,7 @@ fn handle_wallet_cmd(wallet_state: Arc>, id: String, cmd: } } WalletCmd::Receive { kind, req, resp } => { + let duration_seconds = req.duration_seconds(); // A missing amount and an explicit 0 both mean "no amount // restriction": an invoice demanding exactly 0 units is useless. let assignment = match req.amount { @@ -512,7 +513,7 @@ fn handle_wallet_cmd(wallet_state: Arc>, id: String, cmd: kind, req.asset_id, assignment, - req.duration_seconds, + duration_seconds, req.min_confirmations, ) { Ok(val) => resp.send(Ok(val)).is_ok(), @@ -553,6 +554,14 @@ fn handle_wallet_cmd(wallet_state: Arc>, id: String, cmd: } } WalletCmd::SendBegin { req, resp } => { + let expiration_timestamp = match req.expiration() { + Ok(expiration) => expiration, + Err(err) => { + log_rgb_err!(err, "send begin: wid={id} error={:?}", err); + let _ = resp.send(Err(err)); + return; + } + }; let recipient_map: std::collections::HashMap> = req .recipient_map .into_iter() @@ -562,15 +571,16 @@ fn handle_wallet_cmd(wallet_state: Arc>, id: String, cmd: .collect(); let mut w = wallet_state.lock().unwrap(); let wo = w.wallet_online; + let sender_proxy = w.sender_proxy.clone(); match w.wallet.send_begin( wo, recipient_map, req.donation, req.fee_rate, req.min_confirmations, - req.expiration_timestamp, + expiration_timestamp, + sender_proxy, false, - None, ) { Ok(val) => resp.send(Ok(val.psbt)).is_ok(), Err(err) => { @@ -591,20 +601,18 @@ fn handle_wallet_cmd(wallet_state: Arc>, id: String, cmd: } } WalletCmd::BurnBegin { req, resp } => { - let res = req.burn_recipient_bytes().and_then(|burn_recipient| { - let mut w = wallet_state.lock().unwrap(); - let wo = w.wallet_online; - w.wallet.burn_begin( - wo, - req.asset_id, - req.amount, - burn_recipient, - req.fee_rate, - req.min_confirmations, - false, - ) - }); - match res { + let burn_reason = req.burn_reason(); + let mut w = wallet_state.lock().unwrap(); + let wo = w.wallet_online; + match w.wallet.burn_begin( + wo, + req.asset_id, + req.amount, + burn_reason, + req.fee_rate, + req.min_confirmations, + false, + ) { Ok(val) => resp.send(Ok(val.psbt)).is_ok(), Err(err) => { log_rgb_err!(err, "burn begin: wid={id} error={:?}", err); @@ -653,15 +661,10 @@ fn handle_wallet_cmd(wallet_state: Arc>, id: String, cmd: WalletCmd::SendBtcBegin { req, resp } => { let mut w = wallet_state.lock().unwrap(); let wo = w.wallet_online; - match w.wallet.send_btc_begin( - wo, - req.address, - req.amount, - req.fee_rate, - false, - false, - None, - ) { + match w + .wallet + .send_btc_begin(wo, req.address, req.amount, req.fee_rate, false, false) + { Ok(val) => resp.send(Ok(val)).is_ok(), Err(err) => { log_rgb_err!(err, "send btc begin: wid={id} error={:?}", err); @@ -702,7 +705,6 @@ fn handle_wallet_cmd(wallet_state: Arc>, id: String, cmd: req.amounts, req.inflation_amounts, req.reject_list_url, - None, ); if let Err(err) = &res { log_rgb_err!(err, "issue IFA: wid={id} error={:?}", err); diff --git a/wallet-cli/src/main.rs b/wallet-cli/src/main.rs index 89e73df..869295e 100644 --- a/wallet-cli/src/main.rs +++ b/wallet-cli/src/main.rs @@ -270,7 +270,6 @@ fn signing_wallet(mnemonic: &str, network: BitcoinNetwork, data_dir: &PathBuf) - database_type: DatabaseType::Sqlite, max_allocations_per_utxo: MAX_ALLOCATIONS_PER_UTXO, supported_schemas: vec![AssetSchema::Nia], - reuse_addresses: false, }, SinglesigKeys { account_xpub_colored: keys.account_xpub_colored,