Skip to content

Commit

Permalink
Port wallet delegation
Browse files Browse the repository at this point in the history
  • Loading branch information
timemarkovqtum committed Jun 27, 2024
1 parent 60c834e commit 78ebf10
Show file tree
Hide file tree
Showing 11 changed files with 721 additions and 27 deletions.
2 changes: 1 addition & 1 deletion src/wallet/bdb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ bool BerkeleyDatabase::Verify(bilingual_str& errorStr)
const std::string strFile = fs::PathToString(m_filename);
int result = db.verify(strFile.c_str(), nullptr, nullptr, 0);
if (result != 0) {
errorStr = strprintf(_("%s corrupt. Try using the wallet tool bitcoin-wallet to salvage or restoring a backup."), fs::quoted(fs::PathToString(file_path)));
errorStr = strprintf(_("%s corrupt. Try using the wallet tool qtum-wallet to salvage or restoring a backup."), fs::quoted(fs::PathToString(file_path)));
return false;
}
}
Expand Down
9 changes: 7 additions & 2 deletions src/wallet/coinselection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -937,10 +937,15 @@ const std::set<std::shared_ptr<COutput>>& SelectionResult::GetInputSet() const
return m_selected_inputs;
}

std::vector<std::shared_ptr<COutput>> SelectionResult::GetShuffledInputVector() const
void SelectionResult::SetInputSet(const std::set<std::shared_ptr<COutput>>& selected_inputs)
{
m_selected_inputs = selected_inputs;
}

std::vector<std::shared_ptr<COutput>> SelectionResult::GetShuffledInputVector(int shuffleOffset) const
{
std::vector<std::shared_ptr<COutput>> coins(m_selected_inputs.begin(), m_selected_inputs.end());
Shuffle(coins.begin(), coins.end(), FastRandomContext());
Shuffle(coins.begin() + shuffleOffset, coins.end(), FastRandomContext());
return coins;
}

Expand Down
4 changes: 3 additions & 1 deletion src/wallet/coinselection.h
Original file line number Diff line number Diff line change
Expand Up @@ -413,8 +413,10 @@ struct SelectionResult

/** Get m_selected_inputs */
const std::set<std::shared_ptr<COutput>>& GetInputSet() const;
/** Set m_selected_inputs */
void SetInputSet(const std::set<std::shared_ptr<COutput>>& selected_inputs);
/** Get the vector of COutputs that will be used to fill in a CTransaction's vin */
std::vector<std::shared_ptr<COutput>> GetShuffledInputVector() const;
std::vector<std::shared_ptr<COutput>> GetShuffledInputVector(int shuffleOffset = 0) const;

bool operator<(SelectionResult other) const;

Expand Down
2 changes: 1 addition & 1 deletion src/wallet/dump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ bool CreateFromDump(const ArgsManager& args, const std::string& name, const fs::
return false;
}
if (ver != DUMP_VERSION) {
error = strprintf(_("Error: Dumpfile version is not supported. This version of bitcoin-wallet only supports version 1 dumpfiles. Got dumpfile with version %s"), version_value);
error = strprintf(_("Error: Dumpfile version is not supported. This version of qtum-wallet only supports version 1 dumpfiles. Got dumpfile with version %s"), version_value);
dump_file.close();
return false;
}
Expand Down
2 changes: 1 addition & 1 deletion src/wallet/external_signer_scriptpubkeyman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ bool ExternalSignerScriptPubKeyMan::SetupDescriptor(WalletBatch& batch, std::uni

ExternalSigner ExternalSignerScriptPubKeyMan::GetExternalSigner() {
const std::string command = gArgs.GetArg("-signer", "");
if (command == "") throw std::runtime_error(std::string(__func__) + ": restart bitcoind with -signer=<cmd>");
if (command == "") throw std::runtime_error(std::string(__func__) + ": restart qtumd with -signer=<cmd>");
std::vector<ExternalSigner> signers;
ExternalSigner::Enumerate(command, signers, Params().GetChainTypeString());
if (signers.empty()) throw std::runtime_error(std::string(__func__) + ": No external signers found");
Expand Down
111 changes: 97 additions & 14 deletions src/wallet/scriptpubkeyman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,10 @@ IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& s
}
break;
}
} // no default case, so the compiler can warn about missing cases

default:
break;
}

if (ret == IsMineResult::NO && keystore.HaveWatchOnly(scriptPubKey)) {
ret = std::max(ret, IsMineResult::WATCH_ONLY);
Expand Down Expand Up @@ -1101,12 +1104,12 @@ static void DeriveExtKey(CExtKey& key_in, unsigned int index, CExtKey& key_out)

void LegacyScriptPubKeyMan::DeriveNewChildKey(WalletBatch &batch, CKeyMetadata& metadata, CKey& secret, CHDChain& hd_chain, bool internal)
{
// for now we use a fixed keypath scheme of m/0'/0'/k
// for now we use a fixed keypath scheme of m/88'/0'/k
CKey seed; //seed (256bit)
CExtKey masterKey; //hd master key
CExtKey accountKey; //key at m/0'
CExtKey chainChildKey; //key at m/0'/0' (external) or m/0'/1' (internal)
CExtKey childKey; //key at m/0'/0'/<n>'
CExtKey accountKey; //key at m/88'
CExtKey chainChildKey; //key at m/88'/0' (external) or m/88'/1' (internal)
CExtKey childKey; //key at m/88'/0'/<n>'

// try to get the seed
if (!GetKey(hd_chain.seed_id, seed))
Expand All @@ -1118,7 +1121,7 @@ void LegacyScriptPubKeyMan::DeriveNewChildKey(WalletBatch &batch, CKeyMetadata&
// use hardened derivation (child keys >= 0x80000000 are hardened after bip32)
DeriveExtKey(masterKey, BIP32_HARDENED_KEY_LIMIT, accountKey);

// derive m/0'/0' (external chain) OR m/0'/1' (internal chain)
// derive m/0'/0' (external chain) OR m/88'/1' (internal chain)
assert(internal ? m_storage.CanSupportFeature(FEATURE_HD_SPLIT) : true);
DeriveExtKey(accountKey, BIP32_HARDENED_KEY_LIMIT+(internal ? 1 : 0), chainChildKey);

Expand All @@ -1129,16 +1132,16 @@ void LegacyScriptPubKeyMan::DeriveNewChildKey(WalletBatch &batch, CKeyMetadata&
// example: 1 | BIP32_HARDENED_KEY_LIMIT == 0x80000001 == 2147483649
if (internal) {
DeriveExtKey(chainChildKey, hd_chain.nInternalChainCounter | BIP32_HARDENED_KEY_LIMIT, childKey);
metadata.hdKeypath = "m/0'/1'/" + ToString(hd_chain.nInternalChainCounter) + "'";
metadata.key_origin.path.push_back(0 | BIP32_HARDENED_KEY_LIMIT);
metadata.hdKeypath = "m/88'/1'/" + ToString(hd_chain.nInternalChainCounter) + "'";
metadata.key_origin.path.push_back(88 | BIP32_HARDENED_KEY_LIMIT);
metadata.key_origin.path.push_back(1 | BIP32_HARDENED_KEY_LIMIT);
metadata.key_origin.path.push_back(hd_chain.nInternalChainCounter | BIP32_HARDENED_KEY_LIMIT);
hd_chain.nInternalChainCounter++;
}
else {
DeriveExtKey(chainChildKey, hd_chain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT, childKey);
metadata.hdKeypath = "m/0'/0'/" + ToString(hd_chain.nExternalChainCounter) + "'";
metadata.key_origin.path.push_back(0 | BIP32_HARDENED_KEY_LIMIT);
metadata.hdKeypath = "m/88'/0'/" + ToString(hd_chain.nExternalChainCounter) + "'";
metadata.key_origin.path.push_back(88 | BIP32_HARDENED_KEY_LIMIT);
metadata.key_origin.path.push_back(0 | BIP32_HARDENED_KEY_LIMIT);
metadata.key_origin.path.push_back(hd_chain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT);
hd_chain.nExternalChainCounter++;
Expand Down Expand Up @@ -2013,7 +2016,12 @@ util::Result<CTxDestination> DescriptorScriptPubKeyMan::GetNewDestination(const
assert(m_wallet_descriptor.descriptor->IsSingleType()); // This is a combo descriptor which should not be an active descriptor
std::optional<OutputType> desc_addr_type = m_wallet_descriptor.descriptor->GetOutputType();
assert(desc_addr_type);
if (type != *desc_addr_type) {
bool isInconsistent = type != *desc_addr_type;
if(type == OutputType::P2PK) {
// Address type is LEGACY for valid P2PK output
isInconsistent = *desc_addr_type !=OutputType::LEGACY;
}
if (isInconsistent) {
throw std::runtime_error(std::string(__func__) + ": Types are inconsistent. Stored type does not match type of newly generated address");
}

Expand All @@ -2032,7 +2040,7 @@ util::Result<CTxDestination> DescriptorScriptPubKeyMan::GetNewDestination(const
}

CTxDestination dest;
if (!ExtractDestination(scripts_temp[0], dest)) {
if (!ExtractDestination(scripts_temp[0], dest, nullptr, true)) {
return util::Error{_("Error: Cannot extract destination from the generated scriptpubkey")}; // shouldn't happen
}
m_wallet_descriptor.next_index++;
Expand Down Expand Up @@ -2321,6 +2329,10 @@ bool DescriptorScriptPubKeyMan::SetupDescriptorGeneration(WalletBatch& batch, co
desc_prefix = "tr(" + xpub + "/86h";
break;
}
case OutputType::P2PK: {
desc_prefix = "pk(" + xpub + "/44h";
break;
}
case OutputType::UNKNOWN: {
// We should never have a DescriptorScriptPubKeyMan for an UNKNOWN OutputType,
// so if we get to this point something is wrong
Expand All @@ -2331,9 +2343,9 @@ bool DescriptorScriptPubKeyMan::SetupDescriptorGeneration(WalletBatch& batch, co

// Mainnet derives at 0', testnet and regtest derive at 1'
if (Params().IsTestChain()) {
desc_prefix += "/1h";
desc_prefix += "/88h";
} else {
desc_prefix += "/0h";
desc_prefix += "/88h";
}

std::string internal_path = internal ? "/1" : "/0";
Expand Down Expand Up @@ -2798,4 +2810,75 @@ bool DescriptorScriptPubKeyMan::CanUpdateToWalletDescriptor(const WalletDescript

return true;
}

bool LegacyScriptPubKeyMan::SignTransactionOutput(CMutableTransaction &tx, int sighash, std::map<int, std::string> &output_errors) const
{
return ::SignTransactionOutput(tx, this, sighash, output_errors);
}

bool DescriptorScriptPubKeyMan::SignTransactionOutput(CMutableTransaction &tx, int sighash, std::map<int, std::string> &output_errors) const
{
std::unique_ptr<FlatSigningProvider> keys = std::make_unique<FlatSigningProvider>();
for (CTxOut& output : tx.vout)
{
if(output.scriptPubKey.HasOpSender())
{
CScript scriptPubKey;
if(GetSenderPubKey(output.scriptPubKey, scriptPubKey))
{
std::unique_ptr<FlatSigningProvider> coin_keys = GetSigningProvider(scriptPubKey, true);
if (!coin_keys) {
return false;
}
keys->Merge(std::move(*coin_keys));
}
}
}

return ::SignTransactionOutput(tx, keys.get(), sighash, output_errors);
}

bool LegacyScriptPubKeyMan::SignTransactionStake(CMutableTransaction &tx, const std::vector<std::pair<CTxOut, unsigned int> > &coins) const
{
return ::SignTransactionStake(tx, this, coins);
}

bool DescriptorScriptPubKeyMan::SignTransactionStake(CMutableTransaction &tx, const std::vector<std::pair<CTxOut, unsigned int> > &coins) const
{
std::unique_ptr<FlatSigningProvider> keys = std::make_unique<FlatSigningProvider>();
for (const auto& coin_pair : coins) {
std::unique_ptr<FlatSigningProvider> coin_keys = GetSigningProvider(coin_pair.first.scriptPubKey, true);
if (!coin_keys) {
continue;
}
keys->Merge(std::move(*coin_keys));
}

return ::SignTransactionStake(tx, keys.get(), coins);
}

bool LegacyScriptPubKeyMan::SignBlockStake(CBlock &block, const PKHash &pkhash, bool compact) const
{
CKey key;
if (!GetKey(ToKeyID(pkhash), key)) {
return false;
}

return ::SignBlockStake(block, key, compact);
}

bool DescriptorScriptPubKeyMan::SignBlockStake(CBlock &block, const PKHash &pkhash, bool compact) const
{
std::unique_ptr<FlatSigningProvider> keys = GetSigningProvider(GetScriptForDestination(pkhash), true);
if (!keys) {
return false;
}

CKey key;
if (!keys->GetKey(ToKeyID(pkhash), key)) {
return false;
}

return ::SignBlockStake(block, key, compact);
}
} // namespace wallet
12 changes: 12 additions & 0 deletions src/wallet/scriptpubkeyman.h
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,12 @@ class ScriptPubKeyMan
virtual SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const { return SigningResult::SIGNING_FAILED; };
/** Adds script and derivation path information to a PSBT, and optionally signs it. */
virtual TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = SIGHASH_DEFAULT, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr, bool finalize = true) const { return TransactionError::INVALID_PSBT; }
/** Creates new output signatures and adds them to the transaction. Returns whether all op_sender outputs were signed */
virtual bool SignTransactionOutput(CMutableTransaction& tx, int sighash, std::map<int, std::string>& output_errors) const { return false; }
/** Creates new coinstake signatures and adds them to the transaction. Returns whether all op_sender outputs were signed */
virtual bool SignTransactionStake(CMutableTransaction& tx, const std::vector<std::pair<CTxOut,unsigned int>>& coins) const { return false; }
/** Creates new coinstake block signature and adds it to the header. Returns whether the block was signed */
virtual bool SignBlockStake(CBlock& block, const PKHash& pkhash, bool compact) const { return false; }

virtual uint256 GetID() const { return uint256(); }

Expand Down Expand Up @@ -423,6 +429,9 @@ class LegacyScriptPubKeyMan : public ScriptPubKeyMan, public FillableSigningProv
bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, bilingual_str>& input_errors) const override;
SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const override;
TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = SIGHASH_DEFAULT, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr, bool finalize = true) const override;
bool SignTransactionOutput(CMutableTransaction& tx, int sighash, std::map<int, std::string>& output_errors) const override;
bool SignTransactionStake(CMutableTransaction& tx, const std::vector<std::pair<CTxOut,unsigned int>>& coins) const override;
bool SignBlockStake(CBlock& block, const PKHash& pkhash, bool compact) const override;

uint256 GetID() const override;

Expand Down Expand Up @@ -650,6 +659,9 @@ class DescriptorScriptPubKeyMan : public ScriptPubKeyMan
bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, bilingual_str>& input_errors) const override;
SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const override;
TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = SIGHASH_DEFAULT, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr, bool finalize = true) const override;
bool SignTransactionOutput(CMutableTransaction& tx, int sighash, std::map<int, std::string>& output_errors) const override;
bool SignTransactionStake(CMutableTransaction& tx, const std::vector<std::pair<CTxOut,unsigned int>>& coins) const override;
bool SignBlockStake(CBlock& block, const PKHash& pkhash, bool compact) const override;

uint256 GetID() const override;

Expand Down
1 change: 1 addition & 0 deletions src/wallet/transaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ struct TxStateConfirmed {
uint256 confirmed_block_hash;
int confirmed_block_height;
int position_in_block;
bool has_delegation;

explicit TxStateConfirmed(const uint256& block_hash, int height, int index) : confirmed_block_hash(block_hash), confirmed_block_height(height), position_in_block(index) {}
std::string toString() const { return strprintf("Confirmed (block=%s, height=%i, index=%i)", confirmed_block_hash.ToString(), confirmed_block_height, position_in_block); }
Expand Down
Loading

0 comments on commit 78ebf10

Please sign in to comment.