mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 12:02:48 +01:00
Merge pull request #5278 from knst/bc-bp-v20-missing-1
backport: bitcoin#13072, #14096, #14161, #15831, #16250, #16251, #17049, #17056, #17138, #18032 (rpc_createmultisig.py and some extra)
This commit is contained in:
commit
b0f7612f2c
@ -1,5 +1,5 @@
|
||||
---
|
||||
name: "dash-linux-19"
|
||||
name: "dash-linux-20"
|
||||
enable_cache: true
|
||||
distro: "ubuntu"
|
||||
suites:
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
name: "dash-osx-19"
|
||||
name: "dash-osx-20"
|
||||
enable_cache: true
|
||||
distro: "ubuntu"
|
||||
suites:
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
name: "dash-win-19"
|
||||
name: "dash-win-20"
|
||||
enable_cache: true
|
||||
distro: "ubuntu"
|
||||
suites:
|
||||
|
@ -17,6 +17,7 @@ Supporting RPCs are:
|
||||
(`regtest` only, since v0.19).
|
||||
- `utxoupdatepsbt` takes as input descriptors to add information to the psbt
|
||||
(since v0.19).
|
||||
- `createmultisig` and `addmultisigaddress` return descriptors as well (since v0.20)
|
||||
|
||||
This document describes the language. For the specifics on usage, see the RPC
|
||||
documentation for the functions mentioned above.
|
||||
@ -28,15 +29,18 @@ Output descriptors currently support:
|
||||
- Pay-to-pubkey-hash scripts (P2PKH), through the `pkh` function.
|
||||
- Pay-to-script-hash scripts (P2SH), through the `sh` function.
|
||||
- Multisig scripts, through the `multi` function.
|
||||
- Multisig scripts where the public keys are sorted lexicographically, through the `sortedmulti` function.
|
||||
- Any type of supported address through the `addr` function.
|
||||
- Raw hex scripts through the `raw` function.
|
||||
- Public keys (compressed and uncompressed) in hex notation, or BIP32 extended pubkeys with derivation paths.
|
||||
|
||||
## Examples
|
||||
|
||||
- `pk(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)` represents a P2PK output.
|
||||
- `multi(1,022f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4,025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc)` represents a bare *1-of-2* multisig.
|
||||
- `pkh(xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw/1'/2)` refers to a single P2PKH output, using child key *1'/2* of the specified xpub.
|
||||
- `pk(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)` describes a P2PK output with the specified public key.
|
||||
- `combo(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)` describes any P2PK, P2PKH with the specified public key.
|
||||
- `multi(1,022f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4,025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc)` describes a bare *1-of-2* multisig with the specified public key.
|
||||
- `sortedmulti(1,022f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4,025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc)` describes a bare *1-of-2* multisig with keys sorted lexicographically in the resulting redeemScript.
|
||||
- `pkh(xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw/1'/2)` describes a P2PKH output with child key *1'/2* of the specified xpub.
|
||||
- `pkh([d34db33f/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*)` describes a set of P2PKH outputs, but additionally specifies that the specified xpub is a child of a master with fingerprint `d34db33f`, and derived using path `44'/0'/0'`.
|
||||
|
||||
## Reference
|
||||
@ -49,6 +53,7 @@ Descriptors consist of several types of expressions. The top level expression is
|
||||
- `sh(SCRIPT)` (top level only): P2SH embed the argument.
|
||||
- `combo(KEY)` (top level only): an alias for the collection of `pk(KEY)` and `pkh(KEY)`.
|
||||
- `multi(k,KEY_1,KEY_2,...,KEY_n)` (anywhere): k-of-n multisig script.
|
||||
- `sortedmulti(k,KEY_1,KEY_2,...,KEY_n)` (anywhere): k-of-n multisig script with keys sorted lexicographically in the resulting script.
|
||||
- `addr(ADDR)` (top level only): the script which ADDR expands to.
|
||||
- `raw(HEX)` (top level only): the script whose hex encoding is HEX.
|
||||
|
||||
@ -91,10 +96,24 @@ not contain "p2" for brevity.
|
||||
|
||||
Several pieces of software use multi-signature (multisig) scripts based
|
||||
on Bitcoin's OP_CHECKMULTISIG opcode. To support these, we introduce the
|
||||
`multi(k,key_1,key_2,...,key_n)` function. It represents a *k-of-n*
|
||||
`multi(k,key_1,key_2,...,key_n)` and `sortedmulti(k,key_1,key_2,...,key_n)`
|
||||
functions. They represents a *k-of-n*
|
||||
multisig policy, where any *k* out of the *n* provided public keys must
|
||||
sign.
|
||||
|
||||
Key order is significant for `multi()`. A `multi()` expression describes a multisig script
|
||||
with keys in the specified order, and in a search for TXOs, it will not match
|
||||
outputs with multisig scriptPubKeys that have the same keys in a different
|
||||
order. Also, to prevent a combinatorial explosion of the search space, if more
|
||||
than one of the `multi()` key arguments is a BIP32 wildcard path ending in `/*`
|
||||
or `*'`, the `multi()` expression only matches multisig scripts with the `i`th
|
||||
child key from each wildcard path in lockstep, rather than scripts with any
|
||||
combination of child keys from each wildcard path.
|
||||
|
||||
Key order does not matter for `sortedmulti()`. `sortedmulti()` behaves in the same way
|
||||
as `multi()` does but the keys are reordered in the resulting script such that they
|
||||
are lexicographically ordered as described in BIP67.
|
||||
|
||||
### BIP32 derived keys and chains
|
||||
|
||||
Most modern wallet software and hardware uses keys that are derived using
|
||||
@ -105,7 +124,7 @@ path consists of a sequence of 0 or more integers (in the range
|
||||
*0..2<sup>31</sup>-1*) each optionally followed by `'` or `h`, and
|
||||
separated by `/` characters. The string may optionally end with the
|
||||
literal `/*` or `/*'` (or `/*h`) to refer to all unhardened or hardened
|
||||
child keys instead.
|
||||
child keys in a configurable range (by default `0-1000`, inclusive).
|
||||
|
||||
Whenever a public key is described using a hardened derivation step, the
|
||||
script cannot be computed without access to the corresponding private
|
||||
@ -150,7 +169,7 @@ steps, or for dumping wallet descriptors including private key material.
|
||||
|
||||
In order to easily represent the sets of scripts currently supported by
|
||||
existing Dash Core wallets, a convenience function `combo` is
|
||||
provided, which takes as input a public key, and constructs the P2PK and
|
||||
provided, which takes as input a public key, and describes a set of P2PK and
|
||||
P2PKH scripts for that key.
|
||||
|
||||
### Checksums
|
||||
|
4
doc/release-notes-17056.md
Normal file
4
doc/release-notes-17056.md
Normal file
@ -0,0 +1,4 @@
|
||||
Low-level RPC Changes
|
||||
===
|
||||
|
||||
- A new descriptor type `sortedmulti(...)` has been added to support multisig scripts where the public keys are sorted lexicographically in the resulting script.
|
@ -255,6 +255,7 @@ static UniValue createmultisig(const JSONRPCRequest& request)
|
||||
{
|
||||
{RPCResult::Type::STR, "address", "The value of the new multisig address."},
|
||||
{RPCResult::Type::STR_HEX, "redeemScript", "The string value of the hex-encoded redemption script."},
|
||||
{RPCResult::Type::STR, "descriptor", "The descriptor for this multisig."},
|
||||
}
|
||||
},
|
||||
RPCExamples{
|
||||
@ -283,9 +284,13 @@ static UniValue createmultisig(const JSONRPCRequest& request)
|
||||
CScript inner;
|
||||
const CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, keystore, inner);
|
||||
|
||||
// Make the descriptor
|
||||
std::unique_ptr<Descriptor> descriptor = InferDescriptor(GetScriptForDestination(dest), keystore);
|
||||
|
||||
UniValue result(UniValue::VOBJ);
|
||||
result.pushKV("address", EncodeDestination(dest));
|
||||
result.pushKV("redeemScript", HexStr(inner));
|
||||
result.pushKV("descriptor", descriptor->ToString());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -605,15 +605,23 @@ public:
|
||||
PKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Singleton(std::move(prov)), {}, "pkh") {}
|
||||
};
|
||||
|
||||
/** A parsed multi(...) descriptor. */
|
||||
/** A parsed multi(...) or sortedmulti(...) descriptor */
|
||||
class MultisigDescriptor final : public DescriptorImpl
|
||||
{
|
||||
const int m_threshold;
|
||||
const bool m_sorted;
|
||||
protected:
|
||||
std::string ToStringExtra() const override { return strprintf("%i", m_threshold); }
|
||||
std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider&) const override { return Singleton(GetScriptForMultisig(m_threshold, keys)); }
|
||||
std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider&) const override {
|
||||
if (m_sorted) {
|
||||
std::vector<CPubKey> sorted_keys(keys);
|
||||
std::sort(sorted_keys.begin(), sorted_keys.end());
|
||||
return Singleton(GetScriptForMultisig(m_threshold, sorted_keys));
|
||||
}
|
||||
return Singleton(GetScriptForMultisig(m_threshold, keys));
|
||||
}
|
||||
public:
|
||||
MultisigDescriptor(int threshold, std::vector<std::unique_ptr<PubkeyProvider>> providers) : DescriptorImpl(std::move(providers), {}, "multi"), m_threshold(threshold) {}
|
||||
MultisigDescriptor(int threshold, std::vector<std::unique_ptr<PubkeyProvider>> providers, bool sorted = false) : DescriptorImpl(std::move(providers), {}, sorted ? "sortedmulti" : "multi"), m_threshold(threshold), m_sorted(sorted) {}
|
||||
};
|
||||
|
||||
/** A parsed sh(...) descriptor. */
|
||||
@ -784,6 +792,7 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t key_exp_index, Span<const c
|
||||
using namespace spanparsing;
|
||||
|
||||
auto expr = Expr(sp);
|
||||
bool sorted_multi = false;
|
||||
if (Func("pk", expr)) {
|
||||
auto pubkey = ParsePubkey(key_exp_index, expr, ctx != ParseScriptContext::P2SH, out, error);
|
||||
if (!pubkey) return nullptr;
|
||||
@ -802,7 +811,7 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t key_exp_index, Span<const c
|
||||
error = "Cannot have combo in non-top level";
|
||||
return nullptr;
|
||||
}
|
||||
if (Func("multi", expr)) {
|
||||
if ((sorted_multi = Func("sortedmulti", expr)) || Func("multi", expr)) {
|
||||
auto threshold = Expr(expr);
|
||||
uint32_t thres;
|
||||
std::vector<std::unique_ptr<PubkeyProvider>> providers;
|
||||
@ -845,7 +854,7 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t key_exp_index, Span<const c
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return std::make_unique<MultisigDescriptor>(thres, std::move(providers));
|
||||
return std::make_unique<MultisigDescriptor>(thres, std::move(providers), sorted_multi);
|
||||
}
|
||||
if (ctx == ParseScriptContext::TOP && Func("sh", expr)) {
|
||||
auto desc = ParseScript(key_exp_index, expr, ParseScriptContext::P2SH, out, error);
|
||||
|
@ -304,7 +304,11 @@ BOOST_AUTO_TEST_CASE(descriptor_test)
|
||||
|
||||
// Multisig constructions
|
||||
Check("multi(1,XJvEUEcFWCHCyruc8ZX5exPZaGe4UR7gC5FHrhwPnQGDs1uWCsT2,7sH936MDoVPFVk2VoaCM5yW8P3BfPyffnZECyaHrZwfLgWpS13e)", "multi(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}});
|
||||
Check("sortedmulti(1,XJvEUEcFWCHCyruc8ZX5exPZaGe4UR7gC5FHrhwPnQGDs1uWCsT2,7sH936MDoVPFVk2VoaCM5yW8P3BfPyffnZECyaHrZwfLgWpS13e)", "sortedmulti(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}});
|
||||
Check("sortedmulti(1,XJvEUEcFWCHCyruc8ZX5exPZaGe4UR7gC5FHrhwPnQGDs1uWCsT2,7sH936MDoVPFVk2VoaCM5yW8P3BfPyffnZECyaHrZwfLgWpS13e)", "sortedmulti(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}});
|
||||
Check("sortedmulti(1,7sH936MDoVPFVk2VoaCM5yW8P3BfPyffnZECyaHrZwfLgWpS13e,XJvEUEcFWCHCyruc8ZX5exPZaGe4UR7gC5FHrhwPnQGDs1uWCsT2)", "sortedmulti(1,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}});
|
||||
Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, {{0x8000006FUL,222},{0}});
|
||||
Check("sortedmulti(2,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/*,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0/0/*)", "sortedmulti(2,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/*,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0/0/*)", RANGE, {{"5221025d5fc65ebb8d44a5274b53bac21ff8307fec2334a32df05553459f8b1f7fe1b62102fbd47cc8034098f0e6a94c6aeee8528abf0a2153a5d8e46d325b7284c046784652ae"}, {"52210264fd4d1f5dea8ded94c61e9641309349b62f27fbffe807291f664e286bfbe6472103f4ece6dfccfa37b211eb3d0af4d0c61dba9ef698622dc17eecdf764beeb005a652ae"}, {"5221022ccabda84c30bad578b13c89eb3b9544ce149787e5b538175b1d1ba259cbb83321024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c52ae"}}, {{0}, {1}, {2}, {0, 0, 0}, {0, 0, 1}, {0, 0, 2}});
|
||||
CheckUnparsable("sh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9))","sh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232))", "P2SH script is too large, 547 bytes is larger than 520 bytes"); // P2SH does not fit 16 compressed pubkeys in a redeemscript
|
||||
CheckUnparsable("multi(a,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(a,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "Multi threshold 'a' is not valid"); // Invalid threshold
|
||||
CheckUnparsable("multi(0,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(0,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "Multisig threshold cannot be 0, must be at least 1"); // Threshold of 0
|
||||
|
@ -980,6 +980,7 @@ UniValue addmultisigaddress(const JSONRPCRequest& request)
|
||||
{
|
||||
{RPCResult::Type::STR, "address", "The value of the new multisig address"},
|
||||
{RPCResult::Type::STR_HEX, "redeemScript", "The string value of the hex-encoded redemption script"},
|
||||
{RPCResult::Type::STR, "descriptor", "The descriptor for this multisig."},
|
||||
}
|
||||
},
|
||||
RPCExamples{
|
||||
@ -1020,9 +1021,13 @@ UniValue addmultisigaddress(const JSONRPCRequest& request)
|
||||
CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, spk_man, inner);
|
||||
pwallet->SetAddressBook(dest, label, "send");
|
||||
|
||||
// Make the descriptor
|
||||
std::unique_ptr<Descriptor> descriptor = InferDescriptor(GetScriptForDestination(dest), spk_man);
|
||||
|
||||
UniValue result(UniValue::VOBJ);
|
||||
result.pushKV("address", EncodeDestination(dest));
|
||||
result.pushKV("redeemScript", HexStr(inner));
|
||||
result.pushKV("descriptor", descriptor->ToString());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,6 @@
|
||||
#include <util/string.h>
|
||||
#include <util/translation.h>
|
||||
#include <util/validation.h>
|
||||
#include <validation.h>
|
||||
#ifdef USE_BDB
|
||||
#include <wallet/bdb.h>
|
||||
#endif
|
||||
@ -3150,6 +3149,7 @@ static bool IsCurrentForAntiFeeSniping(interfaces::Chain& chain)
|
||||
*/
|
||||
static uint32_t GetLocktimeForNewTransaction(interfaces::Chain& chain)
|
||||
{
|
||||
uint32_t const height = chain.getHeight().value_or(-1);
|
||||
uint32_t locktime;
|
||||
// Discourage fee sniping.
|
||||
//
|
||||
@ -3172,7 +3172,7 @@ static uint32_t GetLocktimeForNewTransaction(interfaces::Chain& chain)
|
||||
// now we ensure code won't be written that makes assumptions about
|
||||
// nLockTime that preclude a fix later.
|
||||
if (IsCurrentForAntiFeeSniping(chain)) {
|
||||
locktime = chain.getHeight().value_or(-1);
|
||||
locktime = height;
|
||||
|
||||
// Secondly occasionally randomly pick a nLockTime even further back, so
|
||||
// that transactions that are delayed after signing for whatever reason,
|
||||
@ -3187,7 +3187,7 @@ static uint32_t GetLocktimeForNewTransaction(interfaces::Chain& chain)
|
||||
locktime = 0;
|
||||
}
|
||||
|
||||
assert(locktime <= (unsigned int)::ChainActive().Height());
|
||||
assert(locktime <= height);
|
||||
assert(locktime < LOCKTIME_THRESHOLD);
|
||||
return locktime;
|
||||
}
|
||||
|
58
test/functional/data/rpc_bip67.json
Normal file
58
test/functional/data/rpc_bip67.json
Normal file
@ -0,0 +1,58 @@
|
||||
[
|
||||
{
|
||||
"keys": [
|
||||
"02ff12471208c14bd580709cb2358d98975247d8765f92bc25eab3b2763ed605f8",
|
||||
"02fe6f0a5a297eb38c391581c4413e084773ea23954d93f7753db7dc0adc188b2f"
|
||||
],
|
||||
"sorted_keys": [
|
||||
"02fe6f0a5a297eb38c391581c4413e084773ea23954d93f7753db7dc0adc188b2f",
|
||||
"02ff12471208c14bd580709cb2358d98975247d8765f92bc25eab3b2763ed605f8"
|
||||
],
|
||||
"script": "522102fe6f0a5a297eb38c391581c4413e084773ea23954d93f7753db7dc0adc188b2f2102ff12471208c14bd580709cb2358d98975247d8765f92bc25eab3b2763ed605f852ae",
|
||||
"address": "8nL86iHTC8K4eVZ6YwyhRWMLhSauj8XQAK"
|
||||
},
|
||||
{
|
||||
"keys": [
|
||||
"02632b12f4ac5b1d1b72b2a3b508c19172de44f6f46bcee50ba33f3f9291e47ed0",
|
||||
"027735a29bae7780a9755fae7a1c4374c656ac6a69ea9f3697fda61bb99a4f3e77",
|
||||
"02e2cc6bd5f45edd43bebe7cb9b675f0ce9ed3efe613b177588290ad188d11b404"
|
||||
],
|
||||
"sorted_keys": [
|
||||
"02632b12f4ac5b1d1b72b2a3b508c19172de44f6f46bcee50ba33f3f9291e47ed0",
|
||||
"027735a29bae7780a9755fae7a1c4374c656ac6a69ea9f3697fda61bb99a4f3e77",
|
||||
"02e2cc6bd5f45edd43bebe7cb9b675f0ce9ed3efe613b177588290ad188d11b404"
|
||||
],
|
||||
"script": "522102632b12f4ac5b1d1b72b2a3b508c19172de44f6f46bcee50ba33f3f9291e47ed021027735a29bae7780a9755fae7a1c4374c656ac6a69ea9f3697fda61bb99a4f3e772102e2cc6bd5f45edd43bebe7cb9b675f0ce9ed3efe613b177588290ad188d11b40453ae",
|
||||
"address": "8q3jFFMMtiego4tNXEZckxw6ZzVy95pnPr"
|
||||
},
|
||||
{
|
||||
"keys": [
|
||||
"030000000000000000000000000000000000004141414141414141414141414141",
|
||||
"020000000000000000000000000000000000004141414141414141414141414141",
|
||||
"020000000000000000000000000000000000004141414141414141414141414140",
|
||||
"030000000000000000000000000000000000004141414141414141414141414140"
|
||||
],
|
||||
"sorted_keys": [
|
||||
"020000000000000000000000000000000000004141414141414141414141414140",
|
||||
"020000000000000000000000000000000000004141414141414141414141414141",
|
||||
"030000000000000000000000000000000000004141414141414141414141414140",
|
||||
"030000000000000000000000000000000000004141414141414141414141414141"
|
||||
],
|
||||
"script": "522102000000000000000000000000000000000000414141414141414141414141414021020000000000000000000000000000000000004141414141414141414141414141210300000000000000000000000000000000000041414141414141414141414141402103000000000000000000000000000000000000414141414141414141414141414154ae",
|
||||
"address": "8fDZsErDoEeG4j8dqnUxih81wRs6PKRwEs"
|
||||
},
|
||||
{
|
||||
"keys": [
|
||||
"022df8750480ad5b26950b25c7ba79d3e37d75f640f8e5d9bcd5b150a0f85014da",
|
||||
"03e3818b65bcc73a7d64064106a859cc1a5a728c4345ff0b641209fba0d90de6e9",
|
||||
"021f2f6e1e50cb6a953935c3601284925decd3fd21bc445712576873fb8c6ebc18"
|
||||
],
|
||||
"sorted_keys": [
|
||||
"021f2f6e1e50cb6a953935c3601284925decd3fd21bc445712576873fb8c6ebc18",
|
||||
"022df8750480ad5b26950b25c7ba79d3e37d75f640f8e5d9bcd5b150a0f85014da",
|
||||
"03e3818b65bcc73a7d64064106a859cc1a5a728c4345ff0b641209fba0d90de6e9"
|
||||
],
|
||||
"script": "5221021f2f6e1e50cb6a953935c3601284925decd3fd21bc445712576873fb8c6ebc1821022df8750480ad5b26950b25c7ba79d3e37d75f640f8e5d9bcd5b150a0f85014da2103e3818b65bcc73a7d64064106a859cc1a5a728c4345ff0b641209fba0d90de6e953ae",
|
||||
"address": "92oK2d4x6UMuHxybE36T9xdRW1KsfkXF4Q"
|
||||
}
|
||||
]
|
173
test/functional/rpc_createmultisig.py
Executable file
173
test/functional/rpc_createmultisig.py
Executable file
@ -0,0 +1,173 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2015-2019 The Bitcoin Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
"""Test multisig RPCs"""
|
||||
|
||||
from test_framework.descriptors import descsum_create, drop_origins
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import (
|
||||
assert_raises_rpc_error,
|
||||
assert_equal,
|
||||
)
|
||||
from test_framework.key import ECPubKey
|
||||
|
||||
import binascii
|
||||
import decimal
|
||||
import itertools
|
||||
import json
|
||||
import os
|
||||
|
||||
class RpcCreateMultiSigTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.setup_clean_chain = True
|
||||
self.num_nodes = 3
|
||||
self.supports_cli = False
|
||||
|
||||
def skip_test_if_missing_module(self):
|
||||
self.skip_if_no_wallet()
|
||||
|
||||
def get_keys(self):
|
||||
node0, node1, node2 = self.nodes
|
||||
add = [node1.getnewaddress() for _ in range(self.nkeys)]
|
||||
self.pub = [node1.getaddressinfo(a)["pubkey"] for a in add]
|
||||
self.priv = [node1.dumpprivkey(a) for a in add]
|
||||
self.final = node2.getnewaddress()
|
||||
|
||||
def run_test(self):
|
||||
node0, node1, node2 = self.nodes
|
||||
|
||||
self.check_addmultisigaddress_errors()
|
||||
|
||||
self.log.info('Generating blocks ...')
|
||||
node0.generate(149)
|
||||
self.sync_all()
|
||||
|
||||
self.moved = 0
|
||||
for self.nkeys in [3, 5]:
|
||||
for self.nsigs in [2, 3]:
|
||||
self.get_keys()
|
||||
self.do_multisig()
|
||||
|
||||
self.checkbalances()
|
||||
|
||||
# Test mixed compressed and uncompressed pubkeys
|
||||
self.log.info('Mixed compressed and uncompressed multisigs are not allowed')
|
||||
pk0 = node0.getaddressinfo(node0.getnewaddress())['pubkey']
|
||||
pk1 = node1.getaddressinfo(node1.getnewaddress())['pubkey']
|
||||
pk2 = node2.getaddressinfo(node2.getnewaddress())['pubkey']
|
||||
|
||||
# decompress pk2
|
||||
pk_obj = ECPubKey()
|
||||
pk_obj.set(binascii.unhexlify(pk2))
|
||||
pk_obj.compressed = False
|
||||
pk2 = binascii.hexlify(pk_obj.get_bytes()).decode()
|
||||
|
||||
# Check all permutations of keys because order matters apparently
|
||||
for keys in itertools.permutations([pk0, pk1, pk2]):
|
||||
# Results should be the same as this legacy one
|
||||
legacy_addr = node0.createmultisig(2, keys)['address']
|
||||
assert_equal(legacy_addr, node0.addmultisigaddress(2, keys, '')['address'])
|
||||
|
||||
self.log.info('Testing sortedmulti descriptors with BIP 67 test vectors')
|
||||
with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data/rpc_bip67.json'), encoding='utf-8') as f:
|
||||
vectors = json.load(f)
|
||||
|
||||
for t in vectors:
|
||||
key_str = ','.join(t['keys'])
|
||||
desc = descsum_create('sh(sortedmulti(2,{}))'.format(key_str))
|
||||
assert_equal(self.nodes[0].deriveaddresses(desc)[0], t['address'])
|
||||
sorted_key_str = ','.join(t['sorted_keys'])
|
||||
sorted_key_desc = descsum_create('sh(multi(2,{}))'.format(sorted_key_str))
|
||||
assert_equal(self.nodes[0].deriveaddresses(sorted_key_desc)[0], t['address'])
|
||||
|
||||
def check_addmultisigaddress_errors(self):
|
||||
self.log.info('Check that addmultisigaddress fails when the private keys are missing')
|
||||
addresses = [self.nodes[1].getnewaddress() for _ in range(2)]
|
||||
assert_raises_rpc_error(-5, 'no full public key for address', lambda: self.nodes[0].addmultisigaddress(nrequired=1, keys=addresses))
|
||||
for a in addresses:
|
||||
# Importing all addresses should not change the result
|
||||
self.nodes[0].importaddress(a)
|
||||
assert_raises_rpc_error(-5, 'no full public key for address', lambda: self.nodes[0].addmultisigaddress(nrequired=1, keys=addresses))
|
||||
|
||||
def checkbalances(self):
|
||||
node0, node1, node2 = self.nodes
|
||||
node0.generate(1)
|
||||
self.sync_all()
|
||||
|
||||
bal0 = node0.getbalance()
|
||||
bal1 = node1.getbalance()
|
||||
bal2 = node2.getbalance()
|
||||
|
||||
height = node0.getblockchaininfo()["blocks"]
|
||||
assert 150 < height < 350
|
||||
total = (height - 99 - 1) * 500 - decimal.Decimal("0.00001223") * 4
|
||||
assert bal1 == 0
|
||||
assert bal2 == self.moved
|
||||
|
||||
assert bal0 + bal1 + bal2 == total
|
||||
|
||||
def do_multisig(self):
|
||||
node0, node1, node2 = self.nodes
|
||||
|
||||
# Construct the expected descriptor
|
||||
desc = 'multi({},{})'.format(self.nsigs, ','.join(self.pub))
|
||||
desc = 'sh({})'.format(desc)
|
||||
desc = descsum_create(desc)
|
||||
|
||||
msig = node2.createmultisig(self.nsigs, self.pub)
|
||||
madd = msig["address"]
|
||||
mredeem = msig["redeemScript"]
|
||||
assert_equal(desc, msig['descriptor'])
|
||||
|
||||
# compare against addmultisigaddress
|
||||
msigw = node1.addmultisigaddress(self.nsigs, self.pub, None)
|
||||
maddw = msigw["address"]
|
||||
mredeemw = msigw["redeemScript"]
|
||||
assert_equal(desc, drop_origins(msigw['descriptor']))
|
||||
# addmultisigiaddress and createmultisig work the same
|
||||
assert maddw == madd
|
||||
assert mredeemw == mredeem
|
||||
|
||||
txid = node0.sendtoaddress(madd, 40)
|
||||
|
||||
tx = node0.getrawtransaction(txid, True)
|
||||
vout = [v["n"] for v in tx["vout"] if madd in v["scriptPubKey"].get("addresses", [])]
|
||||
assert len(vout) == 1
|
||||
vout = vout[0]
|
||||
scriptPubKey = tx["vout"][vout]["scriptPubKey"]["hex"]
|
||||
value = tx["vout"][vout]["value"]
|
||||
prevtxs = [{"txid": txid, "vout": vout, "scriptPubKey": scriptPubKey, "redeemScript": mredeem, "amount": value}]
|
||||
|
||||
node0.generate(1)
|
||||
|
||||
outval = value - decimal.Decimal("0.00001000")
|
||||
rawtx = node2.createrawtransaction([{"txid": txid, "vout": vout}], [{self.final: outval}])
|
||||
|
||||
prevtx_err = dict(prevtxs[0])
|
||||
del prevtx_err["redeemScript"]
|
||||
|
||||
assert_raises_rpc_error(-3, "Missing redeemScript", node2.signrawtransactionwithkey, rawtx, self.priv[0:self.nsigs-1], [prevtx_err])
|
||||
|
||||
# if redeemScript specified, all ok
|
||||
prevtx_err["redeemScript"] = prevtxs[0]["redeemScript"]
|
||||
node2.signrawtransactionwithkey(rawtx, self.priv[0:self.nsigs-1], [prevtx_err])
|
||||
|
||||
# redeemScript does not match scriptPubKey
|
||||
prevtx_err["redeemScript"] = "6a" # OP_RETURN
|
||||
assert_raises_rpc_error(-8, "redeemScript does not match scriptPubKey", node2.signrawtransactionwithkey, rawtx, self.priv[0:self.nsigs-1], [prevtx_err])
|
||||
|
||||
rawtx2 = node2.signrawtransactionwithkey(rawtx, self.priv[0:self.nsigs - 1], prevtxs)
|
||||
rawtx3 = node2.signrawtransactionwithkey(rawtx2["hex"], [self.priv[-1]], prevtxs)
|
||||
|
||||
self.moved += outval
|
||||
tx = node0.sendrawtransaction(rawtx3["hex"], 0)
|
||||
blk = node0.generate(1)[0]
|
||||
assert tx in node0.getblock(blk)["tx"]
|
||||
|
||||
txinfo = node0.getrawtransaction(tx, True, blk)
|
||||
self.log.info("n/m=%d/%d size=%d" % (self.nsigs, self.nkeys, txinfo["size"]))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
RpcCreateMultiSigTest().main()
|
@ -4,6 +4,8 @@
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
"""Utility functions related to output descriptors"""
|
||||
|
||||
import re
|
||||
|
||||
INPUT_CHARSET = "0123456789()[],'/*abcdefgh@:$%{}IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~ijklmnopqrstuvwxyzABCDEFGH`#\"\\ "
|
||||
CHECKSUM_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
|
||||
GENERATOR = [0xf5dee51989, 0xa9fdca3312, 0x1bab10e32d, 0x3706b1677a, 0x644d626ffd]
|
||||
@ -53,3 +55,10 @@ def descsum_check(s, require=True):
|
||||
return False
|
||||
symbols = descsum_expand(s[:-9]) + [CHECKSUM_CHARSET.find(x) for x in s[-8:]]
|
||||
return descsum_polymod(symbols) == 1
|
||||
|
||||
def drop_origins(s):
|
||||
'''Drop the key origins from a descriptor'''
|
||||
desc = re.sub(r'\[.+?\]', '', s)
|
||||
if '#' in s:
|
||||
desc = desc[:desc.index('#')]
|
||||
return descsum_create(desc)
|
||||
|
@ -197,6 +197,7 @@ BASE_SCRIPTS = [
|
||||
'feature_utxo_set_hash.py',
|
||||
'mempool_packages.py',
|
||||
'mempool_package_onemore.py',
|
||||
'rpc_createmultisig.py',
|
||||
'feature_versionbits_warning.py',
|
||||
'rpc_preciousblock.py',
|
||||
'wallet_importprunedfunds.py',
|
||||
|
Loading…
Reference in New Issue
Block a user