For general cryptographic design of peg-out authorization scheme, see: https://github.com/ElementsProject/secp256k1-zkp/blob/secp256k1-zkp/src/modules/whitelist/whitelist.md
| 257 | |
| 258 | // For general cryptographic design of peg-out authorization scheme, see: https://github.com/ElementsProject/secp256k1-zkp/blob/secp256k1-zkp/src/modules/whitelist/whitelist.md |
| 259 | RPCHelpMan initpegoutwallet() |
| 260 | { |
| 261 | return RPCHelpMan{"initpegoutwallet", |
| 262 | "\nThis call is for Liquid network initialization on the Liquid wallet. The wallet generates a new Liquid pegout authorization key (PAK) and stores it in the Liquid wallet. It then combines this with the `bitcoin_descriptor` to finally create a PAK entry for the network. This allows the user to send Liquid coins directly to a secure offline Bitcoin wallet at the derived path from the bitcoin_descriptor using the `sendtomainchain` command. Losing the Liquid PAK or offline Bitcoin root key will result in the inability to pegout funds, so immediate backup upon initialization is required.\n" + |
| 263 | wallet::HELP_REQUIRING_PASSPHRASE, |
| 264 | { |
| 265 | {"bitcoin_descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, "The Bitcoin descriptor that includes a single extended pubkey. Must be one of the following: pkh(<xpub>), sh(wpkh(<xpub>)), or wpkh(<xpub>). This is used as the destination chain for the Bitcoin destination wallet. The derivation path from the xpub is given by the descriptor, typically `0/k`, reflecting the external chain of the wallet. DEPRECATED: If a plain xpub is given, pkh(<xpub>) is assumed, with the `0/k` derivation from that xpub. See link for more details on script descriptors: https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md"}, |
| 266 | {"bip32_counter", RPCArg::Type::NUM , RPCArg::Default{0}, "The `k` in `0/k` to be set as the next address to derive from the `bitcoin_descriptor`. This will be stored in the wallet and incremented on each successful `sendtomainchain` invocation."}, |
| 267 | {"liquid_pak", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED_NAMED_ARG, "The Liquid wallet pubkey in hex to be used as the Liquid PAK for pegout authorization. The private key must be in the wallet if argument is given. If this argument is not provided one will be generated and stored in the wallet automatically and returned."} |
| 268 | }, |
| 269 | RPCResult{ |
| 270 | RPCResult::Type::OBJ, "", "", |
| 271 | { |
| 272 | {RPCResult::Type::STR, "pakentry", "PAK entry to be used at network initialization time in the form of: `pak=<bitcoin_pak>:<liquid_pak>`"}, |
| 273 | {RPCResult::Type::STR_HEX, "liquid_pak", "Liquid PAK pubkey, which is stored in the local Liquid wallet. This can be used in subsequent calls to `initpegoutwallet` to avoid generating a new `liquid_pak`"}, |
| 274 | {RPCResult::Type::STR, "liquid_pak_address", "corresponding address for `liquid_pak`. Useful for `dumpprivkey` for wallet backup or transfer"}, |
| 275 | {RPCResult::Type::ARR_FIXED, "address_lookahead", "the three next Bitcoin addresses the wallet will use for `sendtomainchain` based on `bip32_counter`", |
| 276 | {RPCResult{RPCResult::Type::STR, "", ""}}}, |
| 277 | } |
| 278 | }, |
| 279 | RPCExamples{ |
| 280 | HelpExampleCli("initpegoutwallet", "sh(wpkh(tpubDAY5hwtonH4NE8zY46ZMFf6B6F3fqMis7cwfNihXXpAg6XzBZNoHAdAzAZx2peoU8nTWFqvUncXwJ9qgE5VxcnUKxdut8F6mptVmKjfiwDQ/0/*))") |
| 281 | + HelpExampleRpc("initpegoutwallet", "sh(wpkh(tpubDAY5hwtonH4NE8zY46ZMFf6B6F3fqMis7cwfNihXXpAg6XzBZNoHAdAzAZx2peoU8nTWFqvUncXwJ9qgE5VxcnUKxdut8F6mptVmKjfiwDQ/0/*))") |
| 282 | }, |
| 283 | [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue |
| 284 | { |
| 285 | std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); |
| 286 | if (!wallet) return NullUniValue; |
| 287 | CWallet* const pwallet = wallet.get(); |
| 288 | |
| 289 | LOCK(pwallet->cs_wallet); |
| 290 | |
| 291 | LegacyScriptPubKeyMan* spk_man = pwallet->GetLegacyScriptPubKeyMan(); |
| 292 | if (!spk_man) { |
| 293 | throw JSONRPCError(RPC_WALLET_ERROR, "This type of wallet does not support this command"); |
| 294 | } |
| 295 | |
| 296 | // Check that network cares about PAK |
| 297 | if (!Params().GetEnforcePak()) { |
| 298 | throw JSONRPCError(RPC_INVALID_PARAMETER, "PAK enforcement is not enabled on this network."); |
| 299 | } |
| 300 | |
| 301 | if (!pwallet->IsLocked()) |
| 302 | pwallet->TopUpKeyPool(); |
| 303 | |
| 304 | // Generate a new key that is added to wallet or set from argument |
| 305 | CPubKey online_pubkey; |
| 306 | if (request.params.size() < 3) { |
| 307 | std::string error; |
| 308 | if (!pwallet->GetOnlinePakKey(online_pubkey, error)) { |
| 309 | throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, error); |
| 310 | } |
| 311 | } else { |
| 312 | online_pubkey = CPubKey(ParseHex(request.params[2].get_str())); |
| 313 | if (!online_pubkey.IsFullyValid()) { |
| 314 | throw JSONRPCError(RPC_WALLET_ERROR, "Error: Given liquid_pak is not valid."); |
| 315 | } |
| 316 | if (!spk_man->HaveKey(online_pubkey.GetID())) { |
nothing calls this directly
no test coverage detected