Skip to content

Commit d889c03

Browse files
committed
Merge #11403: SegWit wallet support
b224a47 Add address_types test (Pieter Wuille) 7ee54fd Support downgrading after recovered keypool witness keys (Pieter Wuille) 940a219 SegWit wallet support (Pieter Wuille) f37c64e Implicitly know about P2WPKH redeemscripts (Pieter Wuille) 57273f2 [test] Serialize CTransaction with witness by default (Pieter Wuille) cf2c0b6 Support P2WPKH and P2SH-P2WPKH in dumpprivkey (Pieter Wuille) 37c03d3 Support P2WPKH addresses in create/addmultisig (Pieter Wuille) 3eaa003 Extend validateaddress information for P2SH-embedded witness (Pieter Wuille) 30a27dc Expose method to find key for a single-key destination (Pieter Wuille) 985c795 Improve witness destination types and use them more (Pieter Wuille) cbe1974 [refactor] GetAccount{PubKey,Address} -> GetAccountDestination (Pieter Wuille) 0c8ea63 Abstract out IsSolvable from Witnessifier (Pieter Wuille) Pull request description: This implements a minimum viable implementation of SegWit wallet support, based on top of #11389, and includes part of the functionality from #11089. Two new configuration options are added: * `-addresstype`, with options `legacy`, `p2sh`, and `bech32`. It controls what kind of addresses are produced by `getnewaddress`, `getaccountaddress`, and `createmultisigaddress`. * `-changetype`, with the same options, and by default equal to `-addresstype`, that controls what kind of change is used. All wallet private and public keys can be used for any type of address. Support for address types dependent on different derivation paths will need a major overhaul of how our internal detection of outputs work. I expect that that will happen for a next major version. The above also applies to imported keys, as having a distinction there but not for normal operations is a disaster for testing, and probably for comprehension of users. This has some ugly effects, like needing to associate the provided label to `importprivkey` with each style address for the corresponding key. To deal with witness outputs requiring a corresponding redeemscript in wallet, three approaches are used: * All SegWit addresses created through `getnewaddress` or multisig RPCs explicitly get their redeemscripts added to the wallet file. This means that downgrading after creating a witness address will work, as long as the wallet file is up to date. * All SegWit keys in the wallet get an _implicit_ redeemscript added, without it being written to the file. This means recovery of an old backup will work, as long as you use new software. * All keypool keys that are seen used in transactions explicitly get their redeemscripts added to the wallet files. This means that downgrading after recovering from a backup that includes a witness address will work. These approaches correspond to solutions 3a, 1a, and 5a respectively from https://gist.github.com/sipa/125cfa1615946d0c3f3eec2ad7f250a2. As argued there, there is no full solution for dealing with the case where you both downgrade and restore a backup, so that's also not implemented. `dumpwallet`, `importwallet`, `importmulti`, `signmessage` and `verifymessage` don't work with SegWit addresses yet. They're remaining TODOs, for this PR or a follow-up. Because of that, several tests unexpectedly run with `-addresstype=legacy` for now. Tree-SHA512: d425dbe517c0422061ab8dacdc3a6ae47da071450932ed992c79559d922dff7b2574a31a8c94feccd3761c1dffb6422c50055e6dca8e3cf94a169bc95e39e959
2 parents b0d626d + b224a47 commit d889c03

37 files changed

+730
-181
lines changed

src/keystore.cpp

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,31 @@ bool CKeyStore::AddKey(const CKey &key) {
1111
return AddKeyPubKey(key, key.GetPubKey());
1212
}
1313

14+
void CBasicKeyStore::ImplicitlyLearnRelatedKeyScripts(const CPubKey& pubkey)
15+
{
16+
AssertLockHeld(cs_KeyStore);
17+
CKeyID key_id = pubkey.GetID();
18+
// We must actually know about this key already.
19+
assert(HaveKey(key_id) || mapWatchKeys.count(key_id));
20+
// This adds the redeemscripts necessary to detect P2WPKH and P2SH-P2WPKH
21+
// outputs. Technically P2WPKH outputs don't have a redeemscript to be
22+
// spent. However, our current IsMine logic requires the corresponding
23+
// P2SH-P2WPKH redeemscript to be present in the wallet in order to accept
24+
// payment even to P2WPKH outputs.
25+
// Also note that having superfluous scripts in the keystore never hurts.
26+
// They're only used to guide recursion in signing and IsMine logic - if
27+
// a script is present but we can't do anything with it, it has no effect.
28+
// "Implicitly" refers to fact that scripts are derived automatically from
29+
// existing keys, and are present in memory, even without being explicitly
30+
// loaded (e.g. from a file).
31+
if (pubkey.IsCompressed()) {
32+
CScript script = GetScriptForDestination(WitnessV0KeyHash(key_id));
33+
// This does not use AddCScript, as it may be overridden.
34+
CScriptID id(script);
35+
mapScripts[id] = std::move(script);
36+
}
37+
}
38+
1439
bool CBasicKeyStore::GetPubKey(const CKeyID &address, CPubKey &vchPubKeyOut) const
1540
{
1641
CKey key;
@@ -31,6 +56,7 @@ bool CBasicKeyStore::AddKeyPubKey(const CKey& key, const CPubKey &pubkey)
3156
{
3257
LOCK(cs_KeyStore);
3358
mapKeys[pubkey.GetID()] = key;
59+
ImplicitlyLearnRelatedKeyScripts(pubkey);
3460
return true;
3561
}
3662

@@ -120,8 +146,10 @@ bool CBasicKeyStore::AddWatchOnly(const CScript &dest)
120146
LOCK(cs_KeyStore);
121147
setWatchOnly.insert(dest);
122148
CPubKey pubKey;
123-
if (ExtractPubKey(dest, pubKey))
149+
if (ExtractPubKey(dest, pubKey)) {
124150
mapWatchKeys[pubKey.GetID()] = pubKey;
151+
ImplicitlyLearnRelatedKeyScripts(pubKey);
152+
}
125153
return true;
126154
}
127155

@@ -130,8 +158,11 @@ bool CBasicKeyStore::RemoveWatchOnly(const CScript &dest)
130158
LOCK(cs_KeyStore);
131159
setWatchOnly.erase(dest);
132160
CPubKey pubKey;
133-
if (ExtractPubKey(dest, pubKey))
161+
if (ExtractPubKey(dest, pubKey)) {
134162
mapWatchKeys.erase(pubKey.GetID());
163+
}
164+
// Related CScripts are not removed; having superfluous scripts around is
165+
// harmless (see comment in ImplicitlyLearnRelatedKeyScripts).
135166
return true;
136167
}
137168

@@ -146,3 +177,25 @@ bool CBasicKeyStore::HaveWatchOnly() const
146177
LOCK(cs_KeyStore);
147178
return (!setWatchOnly.empty());
148179
}
180+
181+
CKeyID GetKeyForDestination(const CKeyStore& store, const CTxDestination& dest)
182+
{
183+
// Only supports destinations which map to single public keys, i.e. P2PKH,
184+
// P2WPKH, and P2SH-P2WPKH.
185+
if (auto id = boost::get<CKeyID>(&dest)) {
186+
return *id;
187+
}
188+
if (auto witness_id = boost::get<WitnessV0KeyHash>(&dest)) {
189+
return CKeyID(*witness_id);
190+
}
191+
if (auto script_id = boost::get<CScriptID>(&dest)) {
192+
CScript script;
193+
CTxDestination inner_dest;
194+
if (store.GetCScript(*script_id, script) && ExtractDestination(script, inner_dest)) {
195+
if (auto inner_witness_id = boost::get<WitnessV0KeyHash>(&inner_dest)) {
196+
return CKeyID(*inner_witness_id);
197+
}
198+
}
199+
}
200+
return CKeyID();
201+
}

src/keystore.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ class CBasicKeyStore : public CKeyStore
6060
ScriptMap mapScripts;
6161
WatchOnlySet setWatchOnly;
6262

63+
void ImplicitlyLearnRelatedKeyScripts(const CPubKey& pubkey);
64+
6365
public:
6466
bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override;
6567
bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override;
@@ -80,4 +82,7 @@ class CBasicKeyStore : public CKeyStore
8082
typedef std::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial;
8183
typedef std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char> > > CryptedKeyMap;
8284

85+
/** Return the CKeyID of the key involved in a script (if there is a unique one). */
86+
CKeyID GetKeyForDestination(const CKeyStore& store, const CTxDestination& dest);
87+
8388
#endif // BITCOIN_KEYSTORE_H

src/policy/policy.h

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -49,28 +49,28 @@ static const unsigned int DUST_RELAY_TX_FEE = 3000;
4949
* with. However scripts violating these flags may still be present in valid
5050
* blocks and we must accept those blocks.
5151
*/
52-
static const unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY_FLAGS |
53-
SCRIPT_VERIFY_DERSIG |
54-
SCRIPT_VERIFY_STRICTENC |
55-
SCRIPT_VERIFY_MINIMALDATA |
56-
SCRIPT_VERIFY_NULLDUMMY |
57-
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS |
58-
SCRIPT_VERIFY_CLEANSTACK |
59-
SCRIPT_VERIFY_MINIMALIF |
60-
SCRIPT_VERIFY_NULLFAIL |
61-
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY |
62-
SCRIPT_VERIFY_CHECKSEQUENCEVERIFY |
63-
SCRIPT_VERIFY_LOW_S |
64-
SCRIPT_VERIFY_WITNESS |
65-
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM |
66-
SCRIPT_VERIFY_WITNESS_PUBKEYTYPE;
52+
static constexpr unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY_FLAGS |
53+
SCRIPT_VERIFY_DERSIG |
54+
SCRIPT_VERIFY_STRICTENC |
55+
SCRIPT_VERIFY_MINIMALDATA |
56+
SCRIPT_VERIFY_NULLDUMMY |
57+
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS |
58+
SCRIPT_VERIFY_CLEANSTACK |
59+
SCRIPT_VERIFY_MINIMALIF |
60+
SCRIPT_VERIFY_NULLFAIL |
61+
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY |
62+
SCRIPT_VERIFY_CHECKSEQUENCEVERIFY |
63+
SCRIPT_VERIFY_LOW_S |
64+
SCRIPT_VERIFY_WITNESS |
65+
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM |
66+
SCRIPT_VERIFY_WITNESS_PUBKEYTYPE;
6767

6868
/** For convenience, standard but not mandatory verify flags. */
69-
static const unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS;
69+
static constexpr unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS;
7070

7171
/** Used as the flags parameter to sequence and nLocktime checks in non-consensus code. */
72-
static const unsigned int STANDARD_LOCKTIME_VERIFY_FLAGS = LOCKTIME_VERIFY_SEQUENCE |
73-
LOCKTIME_MEDIAN_TIME_PAST;
72+
static constexpr unsigned int STANDARD_LOCKTIME_VERIFY_FLAGS = LOCKTIME_VERIFY_SEQUENCE |
73+
LOCKTIME_MEDIAN_TIME_PAST;
7474

7575
CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFee);
7676

src/qt/addresstablemodel.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,8 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con
384384
return QString();
385385
}
386386
}
387-
strAddress = EncodeDestination(newKey.GetID());
387+
wallet->LearnRelatedScripts(newKey, g_address_type);
388+
strAddress = EncodeDestination(GetDestinationForKey(newKey, g_address_type));
388389
}
389390
else
390391
{

src/qt/paymentserver.cpp

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -636,27 +636,24 @@ void PaymentServer::fetchPaymentACK(CWallet* wallet, const SendCoinsRecipient& r
636636
// Create a new refund address, or re-use:
637637
QString account = tr("Refund from %1").arg(recipient.authenticatedMerchant);
638638
std::string strAccount = account.toStdString();
639-
std::set<CTxDestination> refundAddresses = wallet->GetAccountAddresses(strAccount);
640-
if (!refundAddresses.empty()) {
641-
CScript s = GetScriptForDestination(*refundAddresses.begin());
639+
CPubKey newKey;
640+
if (wallet->GetKeyFromPool(newKey)) {
641+
// BIP70 requests encode the scriptPubKey directly, so we are not restricted to address
642+
// types supported by the receiver. As a result, we choose the address format we also
643+
// use for change. Despite an actual payment and not change, this is a close match:
644+
// it's the output type we use subject to privacy issues, but not restricted by what
645+
// other software supports.
646+
wallet->LearnRelatedScripts(newKey, g_change_type);
647+
CTxDestination dest = GetDestinationForKey(newKey, g_change_type);
648+
wallet->SetAddressBook(dest, strAccount, "refund");
649+
650+
CScript s = GetScriptForDestination(dest);
642651
payments::Output* refund_to = payment.add_refund_to();
643652
refund_to->set_script(&s[0], s.size());
644-
}
645-
else {
646-
CPubKey newKey;
647-
if (wallet->GetKeyFromPool(newKey)) {
648-
CKeyID keyID = newKey.GetID();
649-
wallet->SetAddressBook(keyID, strAccount, "refund");
650-
651-
CScript s = GetScriptForDestination(keyID);
652-
payments::Output* refund_to = payment.add_refund_to();
653-
refund_to->set_script(&s[0], s.size());
654-
}
655-
else {
656-
// This should never happen, because sending coins should have
657-
// just unlocked the wallet and refilled the keypool.
658-
qWarning() << "PaymentServer::fetchPaymentACK: Error getting refund key, refund_to not set";
659-
}
653+
} else {
654+
// This should never happen, because sending coins should have
655+
// just unlocked the wallet and refilled the keypool.
656+
qWarning() << "PaymentServer::fetchPaymentACK: Error getting refund key, refund_to not set";
660657
}
661658

662659
int length = payment.ByteSize();

src/qt/test/wallettests.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,9 @@ void BumpFee(TransactionView& view, const uint256& txid, bool expectDisabled, st
149149
// src/qt/test/test_bitcoin-qt -platform cocoa # macOS
150150
void TestGUI()
151151
{
152+
g_address_type = OUTPUT_TYPE_P2SH_SEGWIT;
153+
g_change_type = OUTPUT_TYPE_P2SH_SEGWIT;
154+
152155
// Set up wallet and chain with 105 blocks (5 mature blocks for spending).
153156
TestChain100Setup test;
154157
for (int i = 0; i < 5; ++i) {
@@ -161,7 +164,7 @@ void TestGUI()
161164
wallet.LoadWallet(firstRun);
162165
{
163166
LOCK(wallet.cs_wallet);
164-
wallet.SetAddressBook(test.coinbaseKey.GetPubKey().GetID(), "", "receive");
167+
wallet.SetAddressBook(GetDestinationForKey(test.coinbaseKey.GetPubKey(), g_address_type), "", "receive");
165168
wallet.AddKeyPubKey(test.coinbaseKey, test.coinbaseKey.GetPubKey());
166169
}
167170
{

0 commit comments

Comments
 (0)