Skip to content

Commit 5f1be9a

Browse files
committed
Add WitnessV1Point as a destination
1 parent a39d5e0 commit 5f1be9a

File tree

9 files changed

+155
-9
lines changed

9 files changed

+155
-9
lines changed

src/key_io.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,14 @@ class DestinationEncoder : public boost::static_visitor<std::string>
5656
return bech32::Encode(m_params.Bech32HRP(), data);
5757
}
5858

59+
std::string operator()(const WitnessV1Point& id) const
60+
{
61+
std::vector<unsigned char> data = {1};
62+
data.reserve(53);
63+
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, id.begin(), id.end());
64+
return bech32::Encode(m_params.Bech32HRP(), data);
65+
}
66+
5967
std::string operator()(const WitnessUnknown& id) const
6068
{
6169
if (id.version < 1 || id.version > 16 || id.length < 2 || id.length > 40) {

src/rpc/util.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,16 @@ class DescribeAddressVisitor : public boost::static_visitor<UniValue>
232232
return obj;
233233
}
234234

235+
UniValue operator()(const WitnessV1Point& id) const
236+
{
237+
//TODO: What other data should be here?
238+
UniValue obj(UniValue::VOBJ);
239+
obj.pushKV("iswitness", true);
240+
obj.pushKV("witness_version", 1);
241+
obj.pushKV("witness_program", HexStr(id.begin(), id.end()));
242+
return obj;
243+
}
244+
235245
UniValue operator()(const WitnessUnknown& id) const
236246
{
237247
UniValue obj(UniValue::VOBJ);

src/script/script.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@ std::vector<unsigned char> ToByteVector(const T& in)
5959
return std::vector<unsigned char>(in.begin(), in.end());
6060
}
6161

62+
63+
template <typename I, typename O>
64+
void ExtendByteVector(const I& in, std::vector<O>& out)
65+
{
66+
out.reserve(out.size() + in.size());
67+
out.insert(out.end(), in.begin(), in.end());
68+
}
69+
6270
/** Script opcodes */
6371
enum opcodetype
6472
{

src/script/sign.cpp

Lines changed: 85 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,9 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
168168
sigdata.missing_witness_script = uint256(vSolutions[0]);
169169
return false;
170170

171+
case TX_WITNESS_V1_TAPROOT:
172+
ret.push_back(vSolutions[0]);
173+
return true;
171174
default:
172175
return false;
173176
}
@@ -188,6 +191,26 @@ static CScript PushAll(const std::vector<valtype>& values)
188191
return result;
189192
}
190193

194+
bool IsTaprootAllowed(txnouttype type) {
195+
switch(type)
196+
{
197+
case TX_WITNESS_V1_TAPROOT:
198+
case TX_WITNESS_V0_KEYHASH:
199+
case TX_WITNESS_V0_SCRIPTHASH:
200+
case TX_SCRIPTHASH:
201+
case TX_NULL_DATA:
202+
case TX_WITNESS_UNKNOWN:
203+
return false;
204+
205+
case TX_PUBKEY:
206+
case TX_PUBKEYHASH:
207+
case TX_NONSTANDARD:
208+
case TX_MULTISIG:
209+
return true;
210+
}
211+
}
212+
213+
191214
bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& fromPubKey, SignatureData& sigdata)
192215
{
193216
if (sigdata.complete) return true;
@@ -230,6 +253,50 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
230253
sigdata.scriptWitness.stack = result;
231254
sigdata.witness = true;
232255
result.clear();
256+
} else if (solved && whichType == TX_WITNESS_V1_TAPROOT) {
257+
std::vector<unsigned char> address_vec(result[0]);
258+
address_vec[0] += 2;
259+
CPubKey address{address_vec}, base;
260+
uint256 tweak;
261+
txnouttype sub_type;
262+
// First see if we control the point key in the witness program.
263+
if (SignStep(provider, creator, (CScript() << address_vec << OP_CHECKSIG), result, sub_type, SigVersion::TAPROOT, sigdata)) {
264+
// If not see if the signing provider have a Pay-To-Contract tweak for this key.
265+
} else if(provider.GetP2CTweaks(address.GetID(), base, tweak)) {
266+
std::vector<unsigned char> base_vec(base.begin(), base.end());
267+
std::vector<unsigned char> control_block;
268+
std::vector<ScriptPath> paths;
269+
sigdata.p2c_tweaks.emplace(base, tweak);
270+
base_vec[0] -= 2;
271+
// TODO: Should I assume that if I can't sign fo`r the tweaked key then I can't sign for the base key?. if so the next if can be removed.
272+
// Check if we're in control with the base key.
273+
if (!SignStep(provider, creator, (CScript() << base_vec << OP_CHECKSIG), result, sub_type, SigVersion::TAPROOT, sigdata)) {
274+
if(provider.GetScriptPaths(address.GetID(), paths)) {
275+
bool can_sign_any_path = false;
276+
for (auto& path : paths) {
277+
// Check if we can sign for any of the paths scripts.
278+
if (SignStep(provider, creator, path.leaf, result, sub_type, SigVersion::TAPROOT, sigdata) && IsTaprootAllowed(sub_type)) {
279+
ExtendByteVector(base_vec, control_block);
280+
for (auto& hash : path.path) {
281+
ExtendByteVector(hash, control_block);
282+
}
283+
sigdata.taproot_script_path = path;
284+
can_sign_any_path = true;
285+
break; // break out the second we found a script we can sign for.
286+
}
287+
}
288+
solved = solved && can_sign_any_path;
289+
} else {
290+
solved = false;
291+
}
292+
if (!control_block.empty()) result.emplace_back(control_block);
293+
}
294+
} else {
295+
solved = false;
296+
}
297+
sigdata.witness = true;
298+
sigdata.scriptWitness.stack = result;
299+
result.clear();
233300
} else if (solved && whichType == TX_WITNESS_UNKNOWN) {
234301
sigdata.witness = true;
235302
}
@@ -238,7 +305,6 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
238305
result.push_back(std::vector<unsigned char>(subscript.begin(), subscript.end()));
239306
}
240307
sigdata.scriptSig = PushAll(result);
241-
242308
// Test solution
243309
sigdata.complete = solved && VerifyScript(sigdata.scriptSig, fromPubKey, &sigdata.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker());
244310
return sigdata.complete;
@@ -454,14 +520,25 @@ bool IsSegWitOutput(const SigningProvider& provider, const CScript& script)
454520
{
455521
std::vector<valtype> solutions;
456522
auto whichtype = Solver(script, solutions);
457-
if (whichtype == TX_WITNESS_V0_SCRIPTHASH || whichtype == TX_WITNESS_V0_KEYHASH || whichtype == TX_WITNESS_UNKNOWN) return true;
458-
if (whichtype == TX_SCRIPTHASH) {
523+
switch (whichtype) {
524+
case TX_WITNESS_V0_SCRIPTHASH:
525+
case TX_WITNESS_V0_KEYHASH:
526+
case TX_WITNESS_V1_TAPROOT:
527+
case TX_WITNESS_UNKNOWN:
528+
return true;
529+
case TX_NULL_DATA:
530+
case TX_PUBKEY:
531+
case TX_PUBKEYHASH:
532+
case TX_NONSTANDARD:
533+
case TX_MULTISIG:
534+
return false;
535+
case TX_SCRIPTHASH: {
459536
auto h160 = uint160(solutions[0]);
460537
CScript subscript;
461-
if (provider.GetCScript(h160, subscript)) {
462-
whichtype = Solver(subscript, solutions);
463-
if (whichtype == TX_WITNESS_V0_SCRIPTHASH || whichtype == TX_WITNESS_V0_KEYHASH || whichtype == TX_WITNESS_UNKNOWN) return true;
464-
}
538+
if (provider.GetCScript(h160, subscript))
539+
return IsSegWitOutput(provider, subscript);
540+
else
541+
return false;
542+
}
465543
}
466-
return false;
467544
}

src/script/sign.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#ifndef BITCOIN_SCRIPT_SIGN_H
77
#define BITCOIN_SCRIPT_SIGN_H
88

9+
#include "signingprovider.h"
910
#include <boost/optional.hpp>
1011
#include <hash.h>
1112
#include <pubkey.h>
@@ -62,6 +63,8 @@ struct SignatureData {
6263
CScript scriptSig; ///< The scriptSig of an input. Contains complete signatures or the traditional partial signatures format
6364
CScript redeem_script; ///< The redeemScript (if any) for the input
6465
CScript witness_script; ///< The witnessScript (if any) for the input. witnessScripts are used in P2WSH outputs.
66+
ScriptPath taproot_script_path; ///< The (if any) tapscript + merkle path for the control block. This is used in SegwitV1(Taproot) outputs.
67+
std::map<CPubKey, uint256> p2c_tweaks; ///< The Pay-To-Contract tweaks (if any), needed to contruct the control block. This is used in SegwitV1(Taproot) outputs
6568
CScriptWitness scriptWitness; ///< The scriptWitness of an input. Contains complete signatures or the traditional partial signatures format. scriptWitness is part of a transaction input per BIP 144.
6669
std::map<CKeyID, SigPair> signatures; ///< BIP 174 style partial signatures for the input. May contain all signatures necessary for producing a final scriptSig or scriptWitness.
6770
std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> misc_pubkeys;

src/script/standard.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const char* GetTxnOutputType(txnouttype t)
3737
case TX_NULL_DATA: return "nulldata";
3838
case TX_WITNESS_V0_KEYHASH: return "witness_v0_keyhash";
3939
case TX_WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash";
40+
case TX_WITNESS_V1_TAPROOT: return "witness_v1_taproot";
4041
case TX_WITNESS_UNKNOWN: return "witness_unknown";
4142
}
4243
return nullptr;
@@ -112,6 +113,10 @@ txnouttype Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned
112113
vSolutionsRet.push_back(witnessprogram);
113114
return TX_WITNESS_V0_SCRIPTHASH;
114115
}
116+
if (witnessversion == 1 && witnessprogram.size() == WITNESS_V1_TAPROOT_SIZE) {
117+
vSolutionsRet.push_back(witnessprogram);
118+
return TX_WITNESS_V1_TAPROOT;
119+
}
115120
if (witnessversion != 0) {
116121
vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion});
117122
vSolutionsRet.push_back(std::move(witnessprogram));
@@ -185,6 +190,10 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
185190
std::copy(vSolutions[0].begin(), vSolutions[0].end(), hash.begin());
186191
addressRet = hash;
187192
return true;
193+
} else if (whichType == TX_WITNESS_V1_TAPROOT) {
194+
vSolutions[0][0] += 2;
195+
addressRet = WitnessV1Point(vSolutions[0]);
196+
return true;
188197
} else if (whichType == TX_WITNESS_UNKNOWN) {
189198
WitnessUnknown unk;
190199
unk.version = vSolutions[0][0];
@@ -270,6 +279,15 @@ class CScriptVisitor : public boost::static_visitor<bool>
270279
return true;
271280
}
272281

282+
bool operator()(const WitnessV1Point& id) const
283+
{
284+
script->clear();
285+
std::vector<unsigned char> point = ToByteVector(id);
286+
point[0] -= 2; // According to BIP-Taproot;
287+
*script << OP_1 << point;
288+
return true;
289+
}
290+
273291
bool operator()(const WitnessV0ScriptHash& id) const
274292
{
275293
script->clear();

src/script/standard.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include <boost/variant.hpp>
1313

14+
#include <pubkey.h>
1415
#include <stdint.h>
1516

1617
static const bool DEFAULT_ACCEPT_DATACARRIER = true;
@@ -64,6 +65,7 @@ enum txnouttype
6465
TX_NULL_DATA, //!< unspendable OP_RETURN script that carries data
6566
TX_WITNESS_V0_SCRIPTHASH,
6667
TX_WITNESS_V0_KEYHASH,
68+
TX_WITNESS_V1_TAPROOT,
6769
TX_WITNESS_UNKNOWN, //!< Only for Witness versions not already defined above
6870
};
6971

@@ -97,6 +99,13 @@ struct WitnessV0ScriptHash : public uint256
9799
using uint256::uint256;
98100
};
99101

102+
struct WitnessV1Point : public CPubKey
103+
{
104+
WitnessV1Point() : CPubKey() {}
105+
explicit WitnessV1Point(const CPubKey& key) : CPubKey(key) {}
106+
using CPubKey::CPubKey;
107+
};
108+
100109
struct WitnessV0KeyHash : public uint160
101110
{
102111
WitnessV0KeyHash() : uint160() {}
@@ -134,9 +143,10 @@ struct WitnessUnknown
134143
* * WitnessV0ScriptHash: TX_WITNESS_V0_SCRIPTHASH destination (P2WSH)
135144
* * WitnessV0KeyHash: TX_WITNESS_V0_KEYHASH destination (P2WPKH)
136145
* * WitnessUnknown: TX_WITNESS_UNKNOWN destination (P2W???)
146+
* * WitnessV1Point: Taproot destination
137147
* A CTxDestination is the internal data type encoded in a bitcoin address
138148
*/
139-
typedef boost::variant<CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessUnknown> CTxDestination;
149+
typedef boost::variant<CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessV1Point, WitnessUnknown> CTxDestination;
140150

141151
/** Check whether a CTxDestination is a CNoDestination. */
142152
bool IsValidDestination(const CTxDestination& dest);

src/wallet/ismine.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ IsMineResult IsMineInner(const CWallet& keystore, const CScript& scriptPubKey, I
6868
case TX_NONSTANDARD:
6969
case TX_NULL_DATA:
7070
case TX_WITNESS_UNKNOWN:
71+
case TX_WITNESS_V1_TAPROOT:
7172
break;
7273
case TX_PUBKEY:
7374
keyID = CPubKey(vSolutions[0]).GetID();

src/wallet/rpcwallet.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3593,6 +3593,17 @@ class DescribeWalletAddressVisitor : public boost::static_visitor<UniValue>
35933593
return obj;
35943594
}
35953595

3596+
UniValue operator()(const WitnessV1Point& id) const
3597+
{
3598+
UniValue obj(UniValue::VOBJ);
3599+
CPubKey pubkey;
3600+
// TODO: Add tweak and script paths?
3601+
if (pwallet && pwallet->GetPubKey(CKeyID(id.GetID()), pubkey)) {
3602+
obj.pushKV("taproot internal-key", HexStr(pubkey));
3603+
}
3604+
return obj;
3605+
}
3606+
35963607
UniValue operator()(const WitnessUnknown& id) const { return UniValue(UniValue::VOBJ); }
35973608
};
35983609

0 commit comments

Comments
 (0)