mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-03 09:56:38 -05:00
Merge #15638: Move-only: Pull wallet code out of libbitcoin_server
4d074e84a2
[build] Move AnalyzePSBT from psbt.cpp to node/psbt.cpp (Russell Yanofsky)fd509bd1f7
[docs] Document src subdirectories and different libraries (John Newbery)9eaeb7fb8d
[build] Move wallet load functions to wallet/load unit (John Newbery)91a25d1e71
[build] Add several util units (John Newbery)99517866b6
[build] Move several units into common libraries (John Newbery)0509465542
[build] Move rpc rawtransaction util functions to rpc/rawtransaction_util.cpp (John Newbery)1acc61f874
[build] Move rpc utility methods to rpc/util (John Newbery)4a75c9d651
[build] Move policy settings to new src/policy/settings unit (John Newbery)fdf8888b6f
[build] Move CheckTransaction from lib_server to lib_consensus (John Newbery) Pull request description: This is a move-only commit. No code is changing and the moves can be easily verified with: ```sh git log -p -n1 --color-moved=dimmed_zebra ``` This commit moves functions and variables that wallet code depends on out of libbitcoin_server.a, so the bitcoin-wallet tool can be built without libbitcoin_server.a in #15639, and attempting to access server state from wallet code will result in link errors instead of silently broken code. List of moves: - `CheckTransaction` moves from `consensus/tx_verify.cpp` to `consensus/tx_check.cpp` - `urlDecode` moves from `httpserver.cpp` to `util/url.cpp` - `TransactionErrorString` moves from `node/transaction.cpp` to `util/error.cpp` - `StringForFeeReason` and `FeeModeFromString` move from `policy/fees.cpp` to `util/fees.cpp` - `incrementalRelayFee` `dustRelayFee` and `nBytesPerSigOp` move from `policy/policy.cpp` to `policy/settings.cpp` - `SignalsOptInRBF` moves from `policy/rbf.cpp` to `util/rbf.cpp` - `fIsBareMultisigStd` moves from `validation.cpp` to `policy/settings.cpp` - `ConstructTransaction` `TxInErrorToJSON` and `SignTransaction` move from `rpc/rawtransaction.cpp` to `rpc/rawtransaction_util.cpp` - `RPCTypeCheck` `RPCTypeCheckArgument` `RPCTypeCheckObj` `AmountFromValue` `ParseHashV``ParseHashO` `ParseHexV` `ParseHexO` `HelpExampleCli` and `HelpExampleRpc` move from `rpc/server.cpp` to `rpc/util.cpp` - `AmountHighWarn` and `AmountErrMsg` move from `ui_interface.cpp` to `util/error.cpp` - `FormatStateMessage` and `strMessageMagic` move from `validation.cpp` to `util/validation.cpp` - `VerifyWallets` `LoadWallets` `StartWallets` `FlushWallets` `StopWallets` and `UnloadWallets` move from `wallet/init.cpp` to `wallet/node.cpp` ACKs for commit 4d074e: jnewbery: utACK4d074e84a2
(checked by doing the rebase myself and verifying no difference between my branch and4d074e84a2
) Tree-SHA512: 5e1604a9fb06475f2b96da0de0baa8330f4dda834dc20a0183ef11e1e4c27631d1d1bbb9abf0054efc03d56945fdf9920f63366b6a4f200f665b742a479ff75c
This commit is contained in:
commit
6a135fbe5b
72 changed files with 1228 additions and 923 deletions
|
@ -151,6 +151,9 @@
|
|||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
@SOURCE_FILES@
|
||||
<ClCompile Include="..\..\src\wallet\init.cpp">
|
||||
<ObjectFileName>$(IntDir)wallet_init.obj</ObjectFileName>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
<ItemGroup>
|
||||
<ClCompile Include="..\..\src\arith_uint256.cpp" />
|
||||
<ClCompile Include="..\..\src\consensus\merkle.cpp" />
|
||||
<ClCompile Include="..\..\src\consensus\tx_check.cpp" />
|
||||
<ClCompile Include="..\..\src\crypto\aes.cpp" />
|
||||
<ClCompile Include="..\..\src\crypto\chacha20.cpp" />
|
||||
<ClCompile Include="..\..\src\crypto\hmac_sha256.cpp" />
|
||||
|
|
|
@ -124,6 +124,7 @@ BITCOIN_CORE_H = \
|
|||
compat/sanity.h \
|
||||
compressor.h \
|
||||
consensus/consensus.h \
|
||||
consensus/tx_check.h \
|
||||
consensus/tx_verify.h \
|
||||
core_io.h \
|
||||
core_memusage.h \
|
||||
|
@ -155,6 +156,7 @@ BITCOIN_CORE_H = \
|
|||
netbase.h \
|
||||
netmessagemaker.h \
|
||||
node/coin.h \
|
||||
node/psbt.h \
|
||||
node/transaction.h \
|
||||
noui.h \
|
||||
optional.h \
|
||||
|
@ -163,6 +165,7 @@ BITCOIN_CORE_H = \
|
|||
policy/fees.h \
|
||||
policy/policy.h \
|
||||
policy/rbf.h \
|
||||
policy/settings.h \
|
||||
pow.h \
|
||||
protocol.h \
|
||||
psbt.h \
|
||||
|
@ -174,7 +177,7 @@ BITCOIN_CORE_H = \
|
|||
rpc/mining.h \
|
||||
rpc/protocol.h \
|
||||
rpc/server.h \
|
||||
rpc/rawtransaction.h \
|
||||
rpc/rawtransaction_util.h \
|
||||
rpc/register.h \
|
||||
rpc/util.h \
|
||||
scheduler.h \
|
||||
|
@ -201,10 +204,15 @@ BITCOIN_CORE_H = \
|
|||
undo.h \
|
||||
util/bip32.h \
|
||||
util/bytevectorhash.h \
|
||||
util/error.h \
|
||||
util/fees.h \
|
||||
util/system.h \
|
||||
util/memory.h \
|
||||
util/moneystr.h \
|
||||
util/rbf.h \
|
||||
util/time.h \
|
||||
util/url.h \
|
||||
util/validation.h \
|
||||
validation.h \
|
||||
validationinterface.h \
|
||||
versionbits.h \
|
||||
|
@ -215,6 +223,7 @@ BITCOIN_CORE_H = \
|
|||
wallet/db.h \
|
||||
wallet/feebumper.h \
|
||||
wallet/fees.h \
|
||||
wallet/load.h \
|
||||
wallet/psbtwallet.h \
|
||||
wallet/rpcwallet.h \
|
||||
wallet/wallet.h \
|
||||
|
@ -237,13 +246,15 @@ obj/build.h: FORCE
|
|||
libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h
|
||||
|
||||
# server: shared between bitcoind and bitcoin-qt
|
||||
# Contains code accessing mempool and chain state that is meant to be separated
|
||||
# from wallet and gui code (see node/README.md). Shared code should go in
|
||||
# libbitcoin_common or libbitcoin_util libraries, instead.
|
||||
libbitcoin_server_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
|
||||
libbitcoin_server_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||
libbitcoin_server_a_SOURCES = \
|
||||
addrdb.cpp \
|
||||
addrman.cpp \
|
||||
banman.cpp \
|
||||
bloom.cpp \
|
||||
blockencodings.cpp \
|
||||
blockfilter.cpp \
|
||||
chain.cpp \
|
||||
|
@ -255,21 +266,19 @@ libbitcoin_server_a_SOURCES = \
|
|||
index/base.cpp \
|
||||
index/txindex.cpp \
|
||||
interfaces/chain.cpp \
|
||||
interfaces/handler.cpp \
|
||||
interfaces/node.cpp \
|
||||
init.cpp \
|
||||
dbwrapper.cpp \
|
||||
merkleblock.cpp \
|
||||
miner.cpp \
|
||||
net.cpp \
|
||||
net_processing.cpp \
|
||||
node/coin.cpp \
|
||||
node/psbt.cpp \
|
||||
node/transaction.cpp \
|
||||
noui.cpp \
|
||||
outputtype.cpp \
|
||||
policy/fees.cpp \
|
||||
policy/policy.cpp \
|
||||
policy/rbf.cpp \
|
||||
policy/settings.cpp \
|
||||
pow.cpp \
|
||||
rest.cpp \
|
||||
rpc/blockchain.cpp \
|
||||
|
@ -278,7 +287,6 @@ libbitcoin_server_a_SOURCES = \
|
|||
rpc/net.cpp \
|
||||
rpc/rawtransaction.cpp \
|
||||
rpc/server.cpp \
|
||||
rpc/util.cpp \
|
||||
script/sigcache.cpp \
|
||||
shutdown.cpp \
|
||||
timedata.cpp \
|
||||
|
@ -291,6 +299,9 @@ libbitcoin_server_a_SOURCES = \
|
|||
versionbits.cpp \
|
||||
$(BITCOIN_CORE_H)
|
||||
|
||||
if ENABLE_WALLET
|
||||
libbitcoin_server_a_SOURCES += wallet/init.cpp
|
||||
endif
|
||||
if !ENABLE_WALLET
|
||||
libbitcoin_server_a_SOURCES += dummywallet.cpp
|
||||
endif
|
||||
|
@ -317,7 +328,7 @@ libbitcoin_wallet_a_SOURCES = \
|
|||
wallet/db.cpp \
|
||||
wallet/feebumper.cpp \
|
||||
wallet/fees.cpp \
|
||||
wallet/init.cpp \
|
||||
wallet/load.cpp \
|
||||
wallet/psbtwallet.cpp \
|
||||
wallet/rpcdump.cpp \
|
||||
wallet/rpcwallet.cpp \
|
||||
|
@ -391,6 +402,7 @@ libbitcoin_consensus_a_SOURCES = \
|
|||
consensus/merkle.cpp \
|
||||
consensus/merkle.h \
|
||||
consensus/params.h \
|
||||
consensus/tx_check.cpp \
|
||||
consensus/validation.h \
|
||||
hash.cpp \
|
||||
hash.h \
|
||||
|
@ -423,6 +435,7 @@ libbitcoin_common_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
|||
libbitcoin_common_a_SOURCES = \
|
||||
base58.cpp \
|
||||
bech32.cpp \
|
||||
bloom.cpp \
|
||||
chainparams.cpp \
|
||||
coins.cpp \
|
||||
compressor.cpp \
|
||||
|
@ -431,11 +444,16 @@ libbitcoin_common_a_SOURCES = \
|
|||
key.cpp \
|
||||
key_io.cpp \
|
||||
keystore.cpp \
|
||||
merkleblock.cpp \
|
||||
netaddress.cpp \
|
||||
netbase.cpp \
|
||||
outputtype.cpp \
|
||||
policy/feerate.cpp \
|
||||
policy/policy.cpp \
|
||||
protocol.cpp \
|
||||
psbt.cpp \
|
||||
rpc/rawtransaction_util.cpp \
|
||||
rpc/util.cpp \
|
||||
scheduler.cpp \
|
||||
script/descriptor.cpp \
|
||||
script/ismine.cpp \
|
||||
|
@ -458,6 +476,7 @@ libbitcoin_util_a_SOURCES = \
|
|||
compat/glibcxx_sanity.cpp \
|
||||
compat/strnlen.cpp \
|
||||
fs.cpp \
|
||||
interfaces/handler.cpp \
|
||||
logging.cpp \
|
||||
random.cpp \
|
||||
rpc/protocol.cpp \
|
||||
|
@ -466,10 +485,15 @@ libbitcoin_util_a_SOURCES = \
|
|||
threadinterrupt.cpp \
|
||||
util/bip32.cpp \
|
||||
util/bytevectorhash.cpp \
|
||||
util/error.cpp \
|
||||
util/fees.cpp \
|
||||
util/system.cpp \
|
||||
util/moneystr.cpp \
|
||||
util/rbf.cpp \
|
||||
util/strencodings.cpp \
|
||||
util/time.cpp \
|
||||
util/url.cpp \
|
||||
util/validation.cpp \
|
||||
$(BITCOIN_CORE_H)
|
||||
|
||||
if GLIBC_BACK_COMPAT
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <script/script.h>
|
||||
#include <script/sign.h>
|
||||
#include <univalue.h>
|
||||
#include <util/rbf.h>
|
||||
#include <util/system.h>
|
||||
#include <util/moneystr.h>
|
||||
#include <util/strencodings.h>
|
||||
|
|
57
src/consensus/tx_check.cpp
Normal file
57
src/consensus/tx_check.cpp
Normal file
|
@ -0,0 +1,57 @@
|
|||
// Copyright (c) 2017-2018 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <consensus/tx_check.h>
|
||||
|
||||
#include <primitives/transaction.h>
|
||||
#include <consensus/validation.h>
|
||||
|
||||
bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fCheckDuplicateInputs)
|
||||
{
|
||||
// Basic checks that don't depend on any context
|
||||
if (tx.vin.empty())
|
||||
return state.DoS(10, false, REJECT_INVALID, "bad-txns-vin-empty");
|
||||
if (tx.vout.empty())
|
||||
return state.DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty");
|
||||
// Size limits (this doesn't take the witness into account, as that hasn't been checked for malleability)
|
||||
if (::GetSerializeSize(tx, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT)
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize");
|
||||
|
||||
// Check for negative or overflow output values
|
||||
CAmount nValueOut = 0;
|
||||
for (const auto& txout : tx.vout)
|
||||
{
|
||||
if (txout.nValue < 0)
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-negative");
|
||||
if (txout.nValue > MAX_MONEY)
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-toolarge");
|
||||
nValueOut += txout.nValue;
|
||||
if (!MoneyRange(nValueOut))
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge");
|
||||
}
|
||||
|
||||
// Check for duplicate inputs - note that this check is slow so we skip it in CheckBlock
|
||||
if (fCheckDuplicateInputs) {
|
||||
std::set<COutPoint> vInOutPoints;
|
||||
for (const auto& txin : tx.vin)
|
||||
{
|
||||
if (!vInOutPoints.insert(txin.prevout).second)
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-duplicate");
|
||||
}
|
||||
}
|
||||
|
||||
if (tx.IsCoinBase())
|
||||
{
|
||||
if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100)
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-cb-length");
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto& txin : tx.vin)
|
||||
if (txin.prevout.IsNull())
|
||||
return state.DoS(10, false, REJECT_INVALID, "bad-txns-prevout-null");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
20
src/consensus/tx_check.h
Normal file
20
src/consensus/tx_check.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
// Copyright (c) 2017-2018 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_CONSENSUS_TX_CHECK_H
|
||||
#define BITCOIN_CONSENSUS_TX_CHECK_H
|
||||
|
||||
/**
|
||||
* Context-independent transaction checking code that can be called outside the
|
||||
* bitcoin server and doesn't depend on chain or mempool state. Transaction
|
||||
* verification code that does call server functions or depend on server state
|
||||
* belongs in tx_verify.h/cpp instead.
|
||||
*/
|
||||
|
||||
class CTransaction;
|
||||
class CValidationState;
|
||||
|
||||
bool CheckTransaction(const CTransaction& tx, CValidationState& state, bool fCheckDuplicateInputs=true);
|
||||
|
||||
#endif // BITCOIN_CONSENSUS_TX_CHECK_H
|
|
@ -156,55 +156,6 @@ int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& i
|
|||
return nSigOps;
|
||||
}
|
||||
|
||||
bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fCheckDuplicateInputs)
|
||||
{
|
||||
// Basic checks that don't depend on any context
|
||||
if (tx.vin.empty())
|
||||
return state.DoS(10, false, REJECT_INVALID, "bad-txns-vin-empty");
|
||||
if (tx.vout.empty())
|
||||
return state.DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty");
|
||||
// Size limits (this doesn't take the witness into account, as that hasn't been checked for malleability)
|
||||
if (::GetSerializeSize(tx, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT)
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize");
|
||||
|
||||
// Check for negative or overflow output values
|
||||
CAmount nValueOut = 0;
|
||||
for (const auto& txout : tx.vout)
|
||||
{
|
||||
if (txout.nValue < 0)
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-negative");
|
||||
if (txout.nValue > MAX_MONEY)
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-toolarge");
|
||||
nValueOut += txout.nValue;
|
||||
if (!MoneyRange(nValueOut))
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge");
|
||||
}
|
||||
|
||||
// Check for duplicate inputs - note that this check is slow so we skip it in CheckBlock
|
||||
if (fCheckDuplicateInputs) {
|
||||
std::set<COutPoint> vInOutPoints;
|
||||
for (const auto& txin : tx.vin)
|
||||
{
|
||||
if (!vInOutPoints.insert(txin.prevout).second)
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-duplicate");
|
||||
}
|
||||
}
|
||||
|
||||
if (tx.IsCoinBase())
|
||||
{
|
||||
if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100)
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-cb-length");
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto& txin : tx.vin)
|
||||
if (txin.prevout.IsNull())
|
||||
return state.DoS(10, false, REJECT_INVALID, "bad-txns-prevout-null");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee)
|
||||
{
|
||||
// are the actual inputs available?
|
||||
|
|
|
@ -17,9 +17,6 @@ class CValidationState;
|
|||
|
||||
/** Transaction validation functions */
|
||||
|
||||
/** Context-independent validity checks */
|
||||
bool CheckTransaction(const CTransaction& tx, CValidationState& state, bool fCheckDuplicateInputs=true);
|
||||
|
||||
namespace Consensus {
|
||||
/**
|
||||
* Check whether all inputs of this transaction are valid (no double spends and amounts)
|
||||
|
|
|
@ -655,15 +655,3 @@ void UnregisterHTTPHandler(const std::string &prefix, bool exactMatch)
|
|||
pathHandlers.erase(i);
|
||||
}
|
||||
}
|
||||
|
||||
std::string urlDecode(const std::string &urlEncoded) {
|
||||
std::string res;
|
||||
if (!urlEncoded.empty()) {
|
||||
char *decoded = evhttp_uridecode(urlEncoded.c_str(), false, nullptr);
|
||||
if (decoded) {
|
||||
res = std::string(decoded);
|
||||
free(decoded);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -148,6 +148,4 @@ private:
|
|||
struct event* ev;
|
||||
};
|
||||
|
||||
std::string urlDecode(const std::string &urlEncoded);
|
||||
|
||||
#endif // BITCOIN_HTTPSERVER_H
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <policy/feerate.h>
|
||||
#include <policy/fees.h>
|
||||
#include <policy/policy.h>
|
||||
#include <policy/settings.h>
|
||||
#include <rpc/server.h>
|
||||
#include <rpc/register.h>
|
||||
#include <rpc/blockchain.h>
|
||||
|
@ -46,6 +47,7 @@
|
|||
#include <ui_interface.h>
|
||||
#include <util/system.h>
|
||||
#include <util/moneystr.h>
|
||||
#include <util/validation.h>
|
||||
#include <validationinterface.h>
|
||||
#include <warnings.h>
|
||||
#include <walletinitinterface.h>
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <policy/fees.h>
|
||||
#include <policy/policy.h>
|
||||
#include <policy/rbf.h>
|
||||
#include <policy/settings.h>
|
||||
#include <primitives/block.h>
|
||||
#include <primitives/transaction.h>
|
||||
#include <protocol.h>
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <policy/feerate.h>
|
||||
#include <policy/fees.h>
|
||||
#include <policy/policy.h>
|
||||
#include <policy/settings.h>
|
||||
#include <primitives/block.h>
|
||||
#include <rpc/server.h>
|
||||
#include <scheduler.h>
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <wallet/feebumper.h>
|
||||
#include <wallet/fees.h>
|
||||
#include <wallet/rpcwallet.h>
|
||||
#include <wallet/load.h>
|
||||
#include <wallet/wallet.h>
|
||||
#include <wallet/walletutil.h>
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <timedata.h>
|
||||
#include <util/moneystr.h>
|
||||
#include <util/system.h>
|
||||
#include <util/validation.h>
|
||||
#include <validationinterface.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <util/system.h>
|
||||
#include <util/moneystr.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <util/validation.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
|
|
22
src/node/README.md
Normal file
22
src/node/README.md
Normal file
|
@ -0,0 +1,22 @@
|
|||
# src/node/
|
||||
|
||||
The [`src/node/`](./) directory contains code that needs to access node state
|
||||
(state in `CChain`, `CBlockIndex`, `CCoinsView`, `CTxMemPool`, and similar
|
||||
classes).
|
||||
|
||||
Code in [`src/node/`](./) is meant to be segregated from code in
|
||||
[`src/wallet/`](../wallet/) and [`src/qt/`](../qt/), to ensure wallet and GUI
|
||||
code changes don't interfere with node operation, to allow wallet and GUI code
|
||||
to run in separate processes, and to perhaps eventually allow wallet and GUI
|
||||
code to be maintained in separate source repositories.
|
||||
|
||||
As a rule of thumb, code in one of the [`src/node/`](./),
|
||||
[`src/wallet/`](../wallet/), or [`src/qt/`](../qt/) directories should avoid
|
||||
calling code in the other directories directly, and only invoke it indirectly
|
||||
through the more limited [`src/interfaces/`](../interfaces/) classes.
|
||||
|
||||
The [`src/node/`](./) directory is a new directory introduced in
|
||||
[#14978](https://github.com/bitcoin/bitcoin/pull/14978) and at the moment is
|
||||
sparsely populated. Eventually more substantial files like
|
||||
[`src/validation.cpp`](../validation.cpp) and
|
||||
[`src/txmempool.cpp`](../txmempool.cpp) might be moved there.
|
134
src/node/psbt.cpp
Normal file
134
src/node/psbt.cpp
Normal file
|
@ -0,0 +1,134 @@
|
|||
// Copyright (c) 2009-2018 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <coins.h>
|
||||
#include <consensus/tx_verify.h>
|
||||
#include <node/psbt.h>
|
||||
#include <policy/policy.h>
|
||||
#include <policy/settings.h>
|
||||
|
||||
#include <numeric>
|
||||
|
||||
PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
|
||||
{
|
||||
// Go through each input and build status
|
||||
PSBTAnalysis result;
|
||||
|
||||
bool calc_fee = true;
|
||||
bool all_final = true;
|
||||
bool only_missing_sigs = true;
|
||||
bool only_missing_final = false;
|
||||
CAmount in_amt = 0;
|
||||
|
||||
result.inputs.resize(psbtx.tx->vin.size());
|
||||
|
||||
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
|
||||
PSBTInput& input = psbtx.inputs[i];
|
||||
PSBTInputAnalysis& input_analysis = result.inputs[i];
|
||||
|
||||
// Check for a UTXO
|
||||
CTxOut utxo;
|
||||
if (psbtx.GetInputUTXO(utxo, i)) {
|
||||
in_amt += utxo.nValue;
|
||||
input_analysis.has_utxo = true;
|
||||
} else {
|
||||
input_analysis.has_utxo = false;
|
||||
input_analysis.is_final = false;
|
||||
input_analysis.next = PSBTRole::UPDATER;
|
||||
calc_fee = false;
|
||||
}
|
||||
|
||||
// Check if it is final
|
||||
if (!utxo.IsNull() && !PSBTInputSigned(input)) {
|
||||
input_analysis.is_final = false;
|
||||
all_final = false;
|
||||
|
||||
// Figure out what is missing
|
||||
SignatureData outdata;
|
||||
bool complete = SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, 1, &outdata);
|
||||
|
||||
// Things are missing
|
||||
if (!complete) {
|
||||
input_analysis.missing_pubkeys = outdata.missing_pubkeys;
|
||||
input_analysis.missing_redeem_script = outdata.missing_redeem_script;
|
||||
input_analysis.missing_witness_script = outdata.missing_witness_script;
|
||||
input_analysis.missing_sigs = outdata.missing_sigs;
|
||||
|
||||
// If we are only missing signatures and nothing else, then next is signer
|
||||
if (outdata.missing_pubkeys.empty() && outdata.missing_redeem_script.IsNull() && outdata.missing_witness_script.IsNull() && !outdata.missing_sigs.empty()) {
|
||||
input_analysis.next = PSBTRole::SIGNER;
|
||||
} else {
|
||||
only_missing_sigs = false;
|
||||
input_analysis.next = PSBTRole::UPDATER;
|
||||
}
|
||||
} else {
|
||||
only_missing_final = true;
|
||||
input_analysis.next = PSBTRole::FINALIZER;
|
||||
}
|
||||
} else if (!utxo.IsNull()){
|
||||
input_analysis.is_final = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (all_final) {
|
||||
only_missing_sigs = false;
|
||||
result.next = PSBTRole::EXTRACTOR;
|
||||
}
|
||||
if (calc_fee) {
|
||||
// Get the output amount
|
||||
CAmount out_amt = std::accumulate(psbtx.tx->vout.begin(), psbtx.tx->vout.end(), CAmount(0),
|
||||
[](CAmount a, const CTxOut& b) {
|
||||
return a += b.nValue;
|
||||
}
|
||||
);
|
||||
|
||||
// Get the fee
|
||||
CAmount fee = in_amt - out_amt;
|
||||
result.fee = fee;
|
||||
|
||||
// Estimate the size
|
||||
CMutableTransaction mtx(*psbtx.tx);
|
||||
CCoinsView view_dummy;
|
||||
CCoinsViewCache view(&view_dummy);
|
||||
bool success = true;
|
||||
|
||||
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
|
||||
PSBTInput& input = psbtx.inputs[i];
|
||||
Coin newcoin;
|
||||
|
||||
if (!SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, 1, nullptr, true) || !psbtx.GetInputUTXO(newcoin.out, i)) {
|
||||
success = false;
|
||||
break;
|
||||
} else {
|
||||
mtx.vin[i].scriptSig = input.final_script_sig;
|
||||
mtx.vin[i].scriptWitness = input.final_script_witness;
|
||||
newcoin.nHeight = 1;
|
||||
view.AddCoin(psbtx.tx->vin[i].prevout, std::move(newcoin), true);
|
||||
}
|
||||
}
|
||||
|
||||
if (success) {
|
||||
CTransaction ctx = CTransaction(mtx);
|
||||
size_t size = GetVirtualTransactionSize(ctx, GetTransactionSigOpCost(ctx, view, STANDARD_SCRIPT_VERIFY_FLAGS));
|
||||
result.estimated_vsize = size;
|
||||
// Estimate fee rate
|
||||
CFeeRate feerate(fee, size);
|
||||
result.estimated_feerate = feerate;
|
||||
}
|
||||
|
||||
if (only_missing_sigs) {
|
||||
result.next = PSBTRole::SIGNER;
|
||||
} else if (only_missing_final) {
|
||||
result.next = PSBTRole::FINALIZER;
|
||||
} else if (all_final) {
|
||||
result.next = PSBTRole::EXTRACTOR;
|
||||
} else {
|
||||
result.next = PSBTRole::UPDATER;
|
||||
}
|
||||
} else {
|
||||
result.next = PSBTRole::UPDATER;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
43
src/node/psbt.h
Normal file
43
src/node/psbt.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
// Copyright (c) 2009-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.
|
||||
|
||||
#ifndef BITCOIN_NODE_PSBT_H
|
||||
#define BITCOIN_NODE_PSBT_H
|
||||
|
||||
#include <psbt.h>
|
||||
|
||||
/**
|
||||
* Holds an analysis of one input from a PSBT
|
||||
*/
|
||||
struct PSBTInputAnalysis {
|
||||
bool has_utxo; //!< Whether we have UTXO information for this input
|
||||
bool is_final; //!< Whether the input has all required information including signatures
|
||||
PSBTRole next; //!< Which of the BIP 174 roles needs to handle this input next
|
||||
|
||||
std::vector<CKeyID> missing_pubkeys; //!< Pubkeys whose BIP32 derivation path is missing
|
||||
std::vector<CKeyID> missing_sigs; //!< Pubkeys whose signatures are missing
|
||||
uint160 missing_redeem_script; //!< Hash160 of redeem script, if missing
|
||||
uint256 missing_witness_script; //!< SHA256 of witness script, if missing
|
||||
};
|
||||
|
||||
/**
|
||||
* Holds the results of AnalyzePSBT (miscellaneous information about a PSBT)
|
||||
*/
|
||||
struct PSBTAnalysis {
|
||||
Optional<size_t> estimated_vsize; //!< Estimated weight of the transaction
|
||||
Optional<CFeeRate> estimated_feerate; //!< Estimated feerate (fee / weight) of the transaction
|
||||
Optional<CAmount> fee; //!< Amount of fee being paid by the transaction
|
||||
std::vector<PSBTInputAnalysis> inputs; //!< More information about the individual inputs of the transaction
|
||||
PSBTRole next; //!< Which of the BIP 174 roles needs to handle the transaction next
|
||||
};
|
||||
|
||||
/**
|
||||
* Provides helpful miscellaneous information about where a PSBT is in the signing workflow.
|
||||
*
|
||||
* @param[in] psbtx the PSBT to analyze
|
||||
* @return A PSBTAnalysis with information about the provided PSBT.
|
||||
*/
|
||||
PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx);
|
||||
|
||||
#endif // BITCOIN_NODE_PSBT_H
|
|
@ -6,38 +6,13 @@
|
|||
#include <consensus/validation.h>
|
||||
#include <net.h>
|
||||
#include <txmempool.h>
|
||||
#include <util/validation.h>
|
||||
#include <validation.h>
|
||||
#include <validationinterface.h>
|
||||
#include <node/transaction.h>
|
||||
|
||||
#include <future>
|
||||
|
||||
std::string TransactionErrorString(const TransactionError err)
|
||||
{
|
||||
switch (err) {
|
||||
case TransactionError::OK:
|
||||
return "No error";
|
||||
case TransactionError::MISSING_INPUTS:
|
||||
return "Missing inputs";
|
||||
case TransactionError::ALREADY_IN_CHAIN:
|
||||
return "Transaction already in block chain";
|
||||
case TransactionError::P2P_DISABLED:
|
||||
return "Peer-to-peer functionality missing or disabled";
|
||||
case TransactionError::MEMPOOL_REJECTED:
|
||||
return "Transaction rejected by AcceptToMemoryPool";
|
||||
case TransactionError::MEMPOOL_ERROR:
|
||||
return "AcceptToMemoryPool failed";
|
||||
case TransactionError::INVALID_PSBT:
|
||||
return "PSBT is not sane";
|
||||
case TransactionError::PSBT_MISMATCH:
|
||||
return "PSBTs not compatible (different transactions)";
|
||||
case TransactionError::SIGHASH_MISMATCH:
|
||||
return "Specified sighash value does not match existing value";
|
||||
// no default case, so the compiler can warn about missing cases
|
||||
}
|
||||
assert(false);
|
||||
}
|
||||
|
||||
TransactionError BroadcastTransaction(const CTransactionRef tx, uint256& hashTx, std::string& err_string, const CAmount& highfee)
|
||||
{
|
||||
std::promise<void> promise;
|
||||
|
|
|
@ -8,20 +8,7 @@
|
|||
#include <attributes.h>
|
||||
#include <primitives/transaction.h>
|
||||
#include <uint256.h>
|
||||
|
||||
enum class TransactionError {
|
||||
OK, //!< No error
|
||||
MISSING_INPUTS,
|
||||
ALREADY_IN_CHAIN,
|
||||
P2P_DISABLED,
|
||||
MEMPOOL_REJECTED,
|
||||
MEMPOOL_ERROR,
|
||||
INVALID_PSBT,
|
||||
PSBT_MISMATCH,
|
||||
SIGHASH_MISMATCH,
|
||||
};
|
||||
|
||||
std::string TransactionErrorString(const TransactionError error);
|
||||
#include <util/error.h>
|
||||
|
||||
/**
|
||||
* Broadcast a transaction
|
||||
|
|
|
@ -27,40 +27,6 @@ std::string StringForFeeEstimateHorizon(FeeEstimateHorizon horizon) {
|
|||
return horizon_string->second;
|
||||
}
|
||||
|
||||
std::string StringForFeeReason(FeeReason reason) {
|
||||
static const std::map<FeeReason, std::string> fee_reason_strings = {
|
||||
{FeeReason::NONE, "None"},
|
||||
{FeeReason::HALF_ESTIMATE, "Half Target 60% Threshold"},
|
||||
{FeeReason::FULL_ESTIMATE, "Target 85% Threshold"},
|
||||
{FeeReason::DOUBLE_ESTIMATE, "Double Target 95% Threshold"},
|
||||
{FeeReason::CONSERVATIVE, "Conservative Double Target longer horizon"},
|
||||
{FeeReason::MEMPOOL_MIN, "Mempool Min Fee"},
|
||||
{FeeReason::PAYTXFEE, "PayTxFee set"},
|
||||
{FeeReason::FALLBACK, "Fallback fee"},
|
||||
{FeeReason::REQUIRED, "Minimum Required Fee"},
|
||||
{FeeReason::MAXTXFEE, "MaxTxFee limit"}
|
||||
};
|
||||
auto reason_string = fee_reason_strings.find(reason);
|
||||
|
||||
if (reason_string == fee_reason_strings.end()) return "Unknown";
|
||||
|
||||
return reason_string->second;
|
||||
}
|
||||
|
||||
bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_estimate_mode) {
|
||||
static const std::map<std::string, FeeEstimateMode> fee_modes = {
|
||||
{"UNSET", FeeEstimateMode::UNSET},
|
||||
{"ECONOMICAL", FeeEstimateMode::ECONOMICAL},
|
||||
{"CONSERVATIVE", FeeEstimateMode::CONSERVATIVE},
|
||||
};
|
||||
auto mode = fee_modes.find(mode_string);
|
||||
|
||||
if (mode == fee_modes.end()) return false;
|
||||
|
||||
fee_estimate_mode = mode->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* We will instantiate an instance of this class to track transactions that were
|
||||
* included in a block. We will lump transactions into a bucket according to their
|
||||
|
|
|
@ -46,8 +46,6 @@ enum class FeeReason {
|
|||
MAXTXFEE,
|
||||
};
|
||||
|
||||
std::string StringForFeeReason(FeeReason reason);
|
||||
|
||||
/* Used to determine type of fee estimation requested */
|
||||
enum class FeeEstimateMode {
|
||||
UNSET, //!< Use default settings based on other criteria
|
||||
|
@ -55,8 +53,6 @@ enum class FeeEstimateMode {
|
|||
CONSERVATIVE, //!< Force estimateSmartFee to use conservative estimates
|
||||
};
|
||||
|
||||
bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_estimate_mode);
|
||||
|
||||
/* Used to return detailed information about a feerate bucket */
|
||||
struct EstimatorBucket
|
||||
{
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
#include <policy/policy.h>
|
||||
|
||||
#include <consensus/validation.h>
|
||||
#include <validation.h>
|
||||
#include <coins.h>
|
||||
#include <policy/settings.h>
|
||||
#include <tinyformat.h>
|
||||
#include <util/system.h>
|
||||
#include <util/strencodings.h>
|
||||
|
@ -239,10 +239,6 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
|
|||
return true;
|
||||
}
|
||||
|
||||
CFeeRate incrementalRelayFee = CFeeRate(DEFAULT_INCREMENTAL_RELAY_FEE);
|
||||
CFeeRate dustRelayFee = CFeeRate(DUST_RELAY_TX_FEE);
|
||||
unsigned int nBytesPerSigOp = DEFAULT_BYTES_PER_SIGOP;
|
||||
|
||||
int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost)
|
||||
{
|
||||
return (std::max(nWeight, nSigOpCost * nBytesPerSigOp) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR;
|
||||
|
|
|
@ -34,6 +34,8 @@ static const unsigned int DEFAULT_MAX_MEMPOOL_SIZE = 300;
|
|||
static const unsigned int DEFAULT_INCREMENTAL_RELAY_FEE = 1000;
|
||||
/** Default for -bytespersigop */
|
||||
static const unsigned int DEFAULT_BYTES_PER_SIGOP = 20;
|
||||
/** Default for -permitbaremultisig */
|
||||
static const bool DEFAULT_PERMIT_BAREMULTISIG = true;
|
||||
/** The maximum number of witness stack items in a standard P2WSH script */
|
||||
static const unsigned int MAX_STANDARD_P2WSH_STACK_ITEMS = 100;
|
||||
/** The maximum size of each witness stack item in a standard P2WSH script */
|
||||
|
@ -98,10 +100,6 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
|
|||
*/
|
||||
bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs);
|
||||
|
||||
extern CFeeRate incrementalRelayFee;
|
||||
extern CFeeRate dustRelayFee;
|
||||
extern unsigned int nBytesPerSigOp;
|
||||
|
||||
/** Compute the virtual transaction size (weight reinterpreted as bytes). */
|
||||
int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost);
|
||||
int64_t GetVirtualTransactionSize(const CTransaction& tx, int64_t nSigOpCost = 0);
|
||||
|
|
|
@ -3,16 +3,7 @@
|
|||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <policy/rbf.h>
|
||||
|
||||
bool SignalsOptInRBF(const CTransaction &tx)
|
||||
{
|
||||
for (const CTxIn &txin : tx.vin) {
|
||||
if (txin.nSequence <= MAX_BIP125_RBF_SEQUENCE) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#include <util/rbf.h>
|
||||
|
||||
RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool)
|
||||
{
|
||||
|
|
|
@ -7,18 +7,12 @@
|
|||
|
||||
#include <txmempool.h>
|
||||
|
||||
static const uint32_t MAX_BIP125_RBF_SEQUENCE = 0xfffffffd;
|
||||
|
||||
enum class RBFTransactionState {
|
||||
UNKNOWN,
|
||||
REPLACEABLE_BIP125,
|
||||
FINAL
|
||||
};
|
||||
|
||||
// Check whether the sequence numbers on this transaction are signaling
|
||||
// opt-in to replace-by-fee, according to BIP 125
|
||||
bool SignalsOptInRBF(const CTransaction &tx);
|
||||
|
||||
// Determine whether an in-mempool transaction is signaling opt-in to RBF
|
||||
// according to BIP 125
|
||||
// This involves checking sequence numbers of the transaction, as well
|
||||
|
|
14
src/policy/settings.cpp
Normal file
14
src/policy/settings.cpp
Normal file
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-2018 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <policy/settings.h>
|
||||
|
||||
#include <policy/feerate.h>
|
||||
#include <policy/policy.h>
|
||||
|
||||
bool fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG;
|
||||
CFeeRate incrementalRelayFee = CFeeRate(DEFAULT_INCREMENTAL_RELAY_FEE);
|
||||
CFeeRate dustRelayFee = CFeeRate(DUST_RELAY_TX_FEE);
|
||||
unsigned int nBytesPerSigOp = DEFAULT_BYTES_PER_SIGOP;
|
17
src/policy/settings.h
Normal file
17
src/policy/settings.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-2018 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_POLICY_SETTINGS_H
|
||||
#define BITCOIN_POLICY_SETTINGS_H
|
||||
|
||||
class CFeeRate;
|
||||
|
||||
// Policy settings which are configurable at runtime.
|
||||
extern CFeeRate incrementalRelayFee;
|
||||
extern CFeeRate dustRelayFee;
|
||||
extern unsigned int nBytesPerSigOp;
|
||||
extern bool fIsBareMultisigStd;
|
||||
|
||||
#endif // BITCOIN_POLICY_SETTINGS_H
|
123
src/psbt.cpp
123
src/psbt.cpp
|
@ -340,129 +340,6 @@ std::string PSBTRoleName(PSBTRole role) {
|
|||
}
|
||||
}
|
||||
|
||||
PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
|
||||
{
|
||||
// Go through each input and build status
|
||||
PSBTAnalysis result;
|
||||
|
||||
bool calc_fee = true;
|
||||
bool all_final = true;
|
||||
bool only_missing_sigs = true;
|
||||
bool only_missing_final = false;
|
||||
CAmount in_amt = 0;
|
||||
|
||||
result.inputs.resize(psbtx.tx->vin.size());
|
||||
|
||||
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
|
||||
PSBTInput& input = psbtx.inputs[i];
|
||||
PSBTInputAnalysis& input_analysis = result.inputs[i];
|
||||
|
||||
// Check for a UTXO
|
||||
CTxOut utxo;
|
||||
if (psbtx.GetInputUTXO(utxo, i)) {
|
||||
in_amt += utxo.nValue;
|
||||
input_analysis.has_utxo = true;
|
||||
} else {
|
||||
input_analysis.has_utxo = false;
|
||||
input_analysis.is_final = false;
|
||||
input_analysis.next = PSBTRole::UPDATER;
|
||||
calc_fee = false;
|
||||
}
|
||||
|
||||
// Check if it is final
|
||||
if (!utxo.IsNull() && !PSBTInputSigned(input)) {
|
||||
input_analysis.is_final = false;
|
||||
all_final = false;
|
||||
|
||||
// Figure out what is missing
|
||||
SignatureData outdata;
|
||||
bool complete = SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, 1, &outdata);
|
||||
|
||||
// Things are missing
|
||||
if (!complete) {
|
||||
input_analysis.missing_pubkeys = outdata.missing_pubkeys;
|
||||
input_analysis.missing_redeem_script = outdata.missing_redeem_script;
|
||||
input_analysis.missing_witness_script = outdata.missing_witness_script;
|
||||
input_analysis.missing_sigs = outdata.missing_sigs;
|
||||
|
||||
// If we are only missing signatures and nothing else, then next is signer
|
||||
if (outdata.missing_pubkeys.empty() && outdata.missing_redeem_script.IsNull() && outdata.missing_witness_script.IsNull() && !outdata.missing_sigs.empty()) {
|
||||
input_analysis.next = PSBTRole::SIGNER;
|
||||
} else {
|
||||
only_missing_sigs = false;
|
||||
input_analysis.next = PSBTRole::UPDATER;
|
||||
}
|
||||
} else {
|
||||
only_missing_final = true;
|
||||
input_analysis.next = PSBTRole::FINALIZER;
|
||||
}
|
||||
} else if (!utxo.IsNull()){
|
||||
input_analysis.is_final = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (all_final) {
|
||||
only_missing_sigs = false;
|
||||
result.next = PSBTRole::EXTRACTOR;
|
||||
}
|
||||
if (calc_fee) {
|
||||
// Get the output amount
|
||||
CAmount out_amt = std::accumulate(psbtx.tx->vout.begin(), psbtx.tx->vout.end(), CAmount(0),
|
||||
[](CAmount a, const CTxOut& b) {
|
||||
return a += b.nValue;
|
||||
}
|
||||
);
|
||||
|
||||
// Get the fee
|
||||
CAmount fee = in_amt - out_amt;
|
||||
result.fee = fee;
|
||||
|
||||
// Estimate the size
|
||||
CMutableTransaction mtx(*psbtx.tx);
|
||||
CCoinsView view_dummy;
|
||||
CCoinsViewCache view(&view_dummy);
|
||||
bool success = true;
|
||||
|
||||
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
|
||||
PSBTInput& input = psbtx.inputs[i];
|
||||
Coin newcoin;
|
||||
|
||||
if (!SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, 1, nullptr, true) || !psbtx.GetInputUTXO(newcoin.out, i)) {
|
||||
success = false;
|
||||
break;
|
||||
} else {
|
||||
mtx.vin[i].scriptSig = input.final_script_sig;
|
||||
mtx.vin[i].scriptWitness = input.final_script_witness;
|
||||
newcoin.nHeight = 1;
|
||||
view.AddCoin(psbtx.tx->vin[i].prevout, std::move(newcoin), true);
|
||||
}
|
||||
}
|
||||
|
||||
if (success) {
|
||||
CTransaction ctx = CTransaction(mtx);
|
||||
size_t size = GetVirtualTransactionSize(ctx, GetTransactionSigOpCost(ctx, view, STANDARD_SCRIPT_VERIFY_FLAGS));
|
||||
result.estimated_vsize = size;
|
||||
// Estimate fee rate
|
||||
CFeeRate feerate(fee, size);
|
||||
result.estimated_feerate = feerate;
|
||||
}
|
||||
|
||||
if (only_missing_sigs) {
|
||||
result.next = PSBTRole::SIGNER;
|
||||
} else if (only_missing_final) {
|
||||
result.next = PSBTRole::FINALIZER;
|
||||
} else if (all_final) {
|
||||
result.next = PSBTRole::EXTRACTOR;
|
||||
} else {
|
||||
result.next = PSBTRole::UPDATER;
|
||||
}
|
||||
} else {
|
||||
result.next = PSBTRole::UPDATER;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool DecodeBase64PSBT(PartiallySignedTransaction& psbt, const std::string& base64_tx, std::string& error)
|
||||
{
|
||||
bool invalid;
|
||||
|
|
33
src/psbt.h
33
src/psbt.h
|
@ -557,31 +557,6 @@ enum class PSBTRole {
|
|||
EXTRACTOR
|
||||
};
|
||||
|
||||
/**
|
||||
* Holds an analysis of one input from a PSBT
|
||||
*/
|
||||
struct PSBTInputAnalysis {
|
||||
bool has_utxo; //!< Whether we have UTXO information for this input
|
||||
bool is_final; //!< Whether the input has all required information including signatures
|
||||
PSBTRole next; //!< Which of the BIP 174 roles needs to handle this input next
|
||||
|
||||
std::vector<CKeyID> missing_pubkeys; //!< Pubkeys whose BIP32 derivation path is missing
|
||||
std::vector<CKeyID> missing_sigs; //!< Pubkeys whose signatures are missing
|
||||
uint160 missing_redeem_script; //!< Hash160 of redeem script, if missing
|
||||
uint256 missing_witness_script; //!< SHA256 of witness script, if missing
|
||||
};
|
||||
|
||||
/**
|
||||
* Holds the results of AnalyzePSBT (miscellaneous information about a PSBT)
|
||||
*/
|
||||
struct PSBTAnalysis {
|
||||
Optional<size_t> estimated_vsize; //!< Estimated weight of the transaction
|
||||
Optional<CFeeRate> estimated_feerate; //!< Estimated feerate (fee / weight) of the transaction
|
||||
Optional<CAmount> fee; //!< Amount of fee being paid by the transaction
|
||||
std::vector<PSBTInputAnalysis> inputs; //!< More information about the individual inputs of the transaction
|
||||
PSBTRole next; //!< Which of the BIP 174 roles needs to handle the transaction next
|
||||
};
|
||||
|
||||
std::string PSBTRoleName(PSBTRole role);
|
||||
|
||||
/** Checks whether a PSBTInput is already signed. */
|
||||
|
@ -616,14 +591,6 @@ bool FinalizeAndExtractPSBT(PartiallySignedTransaction& psbtx, CMutableTransacti
|
|||
*/
|
||||
NODISCARD TransactionError CombinePSBTs(PartiallySignedTransaction& out, const std::vector<PartiallySignedTransaction>& psbtxs);
|
||||
|
||||
/**
|
||||
* Provides helpful miscellaneous information about where a PSBT is in the signing workflow.
|
||||
*
|
||||
* @param[in] psbtx the PSBT to analyze
|
||||
* @return A PSBTAnalysis with information about the provided PSBT.
|
||||
*/
|
||||
PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx);
|
||||
|
||||
//! Decode a base64ed PSBT into a PartiallySignedTransaction
|
||||
NODISCARD bool DecodeBase64PSBT(PartiallySignedTransaction& decoded_psbt, const std::string& base64_psbt, std::string& error);
|
||||
//! Decode a raw (binary blob) PSBT into a PartiallySignedTransaction
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include <qt/walletmodel.h>
|
||||
|
||||
#include <key_io.h>
|
||||
#include <validation.h> // For strMessageMagic
|
||||
#include <util/validation.h> // For strMessageMagic
|
||||
#include <wallet/wallet.h>
|
||||
|
||||
#include <string>
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <txmempool.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <util/system.h>
|
||||
#include <util/validation.h>
|
||||
#include <validation.h>
|
||||
#include <validationinterface.h>
|
||||
#include <versionbitsinfo.h>
|
||||
|
|
|
@ -21,8 +21,10 @@
|
|||
#include <rpc/util.h>
|
||||
#include <shutdown.h>
|
||||
#include <txmempool.h>
|
||||
#include <util/fees.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <util/system.h>
|
||||
#include <util/validation.h>
|
||||
#include <validation.h>
|
||||
#include <validationinterface.h>
|
||||
#include <versionbitsinfo.h>
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <timedata.h>
|
||||
#include <util/system.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <util/validation.h>
|
||||
#include <warnings.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <net_processing.h>
|
||||
#include <netbase.h>
|
||||
#include <policy/policy.h>
|
||||
#include <policy/settings.h>
|
||||
#include <rpc/protocol.h>
|
||||
#include <rpc/util.h>
|
||||
#include <sync.h>
|
||||
|
|
|
@ -15,12 +15,13 @@
|
|||
#include <key_io.h>
|
||||
#include <keystore.h>
|
||||
#include <merkleblock.h>
|
||||
#include <node/psbt.h>
|
||||
#include <node/transaction.h>
|
||||
#include <policy/policy.h>
|
||||
#include <policy/rbf.h>
|
||||
#include <primitives/transaction.h>
|
||||
#include <psbt.h>
|
||||
#include <rpc/rawtransaction.h>
|
||||
#include <rpc/rawtransaction_util.h>
|
||||
#include <rpc/server.h>
|
||||
#include <rpc/util.h>
|
||||
#include <script/script.h>
|
||||
|
@ -359,119 +360,6 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request)
|
|||
return res;
|
||||
}
|
||||
|
||||
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf)
|
||||
{
|
||||
if (inputs_in.isNull() || outputs_in.isNull())
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null");
|
||||
|
||||
UniValue inputs = inputs_in.get_array();
|
||||
const bool outputs_is_obj = outputs_in.isObject();
|
||||
UniValue outputs = outputs_is_obj ? outputs_in.get_obj() : outputs_in.get_array();
|
||||
|
||||
CMutableTransaction rawTx;
|
||||
|
||||
if (!locktime.isNull()) {
|
||||
int64_t nLockTime = locktime.get_int64();
|
||||
if (nLockTime < 0 || nLockTime > LOCKTIME_MAX)
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range");
|
||||
rawTx.nLockTime = nLockTime;
|
||||
}
|
||||
|
||||
bool rbfOptIn = rbf.isTrue();
|
||||
|
||||
for (unsigned int idx = 0; idx < inputs.size(); idx++) {
|
||||
const UniValue& input = inputs[idx];
|
||||
const UniValue& o = input.get_obj();
|
||||
|
||||
uint256 txid = ParseHashO(o, "txid");
|
||||
|
||||
const UniValue& vout_v = find_value(o, "vout");
|
||||
if (!vout_v.isNum())
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key");
|
||||
int nOutput = vout_v.get_int();
|
||||
if (nOutput < 0)
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
|
||||
|
||||
uint32_t nSequence;
|
||||
if (rbfOptIn) {
|
||||
nSequence = MAX_BIP125_RBF_SEQUENCE; /* CTxIn::SEQUENCE_FINAL - 2 */
|
||||
} else if (rawTx.nLockTime) {
|
||||
nSequence = CTxIn::SEQUENCE_FINAL - 1;
|
||||
} else {
|
||||
nSequence = CTxIn::SEQUENCE_FINAL;
|
||||
}
|
||||
|
||||
// set the sequence number if passed in the parameters object
|
||||
const UniValue& sequenceObj = find_value(o, "sequence");
|
||||
if (sequenceObj.isNum()) {
|
||||
int64_t seqNr64 = sequenceObj.get_int64();
|
||||
if (seqNr64 < 0 || seqNr64 > CTxIn::SEQUENCE_FINAL) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, sequence number is out of range");
|
||||
} else {
|
||||
nSequence = (uint32_t)seqNr64;
|
||||
}
|
||||
}
|
||||
|
||||
CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence);
|
||||
|
||||
rawTx.vin.push_back(in);
|
||||
}
|
||||
|
||||
if (!outputs_is_obj) {
|
||||
// Translate array of key-value pairs into dict
|
||||
UniValue outputs_dict = UniValue(UniValue::VOBJ);
|
||||
for (size_t i = 0; i < outputs.size(); ++i) {
|
||||
const UniValue& output = outputs[i];
|
||||
if (!output.isObject()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, key-value pair not an object as expected");
|
||||
}
|
||||
if (output.size() != 1) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, key-value pair must contain exactly one key");
|
||||
}
|
||||
outputs_dict.pushKVs(output);
|
||||
}
|
||||
outputs = std::move(outputs_dict);
|
||||
}
|
||||
|
||||
// Duplicate checking
|
||||
std::set<CTxDestination> destinations;
|
||||
bool has_data{false};
|
||||
|
||||
for (const std::string& name_ : outputs.getKeys()) {
|
||||
if (name_ == "data") {
|
||||
if (has_data) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, duplicate key: data");
|
||||
}
|
||||
has_data = true;
|
||||
std::vector<unsigned char> data = ParseHexV(outputs[name_].getValStr(), "Data");
|
||||
|
||||
CTxOut out(0, CScript() << OP_RETURN << data);
|
||||
rawTx.vout.push_back(out);
|
||||
} else {
|
||||
CTxDestination destination = DecodeDestination(name_);
|
||||
if (!IsValidDestination(destination)) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + name_);
|
||||
}
|
||||
|
||||
if (!destinations.insert(destination).second) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + name_);
|
||||
}
|
||||
|
||||
CScript scriptPubKey = GetScriptForDestination(destination);
|
||||
CAmount nAmount = AmountFromValue(outputs[name_]);
|
||||
|
||||
CTxOut out(nAmount, scriptPubKey);
|
||||
rawTx.vout.push_back(out);
|
||||
}
|
||||
}
|
||||
|
||||
if (!rbf.isNull() && rawTx.vin.size() > 0 && rbfOptIn != SignalsOptInRBF(CTransaction(rawTx))) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict replaceable option");
|
||||
}
|
||||
|
||||
return rawTx;
|
||||
}
|
||||
|
||||
static UniValue createrawtransaction(const JSONRPCRequest& request)
|
||||
{
|
||||
if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) {
|
||||
|
@ -717,23 +605,6 @@ static UniValue decodescript(const JSONRPCRequest& request)
|
|||
return r;
|
||||
}
|
||||
|
||||
/** Pushes a JSON object for script verification or signing errors to vErrorsRet. */
|
||||
static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::string& strMessage)
|
||||
{
|
||||
UniValue entry(UniValue::VOBJ);
|
||||
entry.pushKV("txid", txin.prevout.hash.ToString());
|
||||
entry.pushKV("vout", (uint64_t)txin.prevout.n);
|
||||
UniValue witness(UniValue::VARR);
|
||||
for (unsigned int i = 0; i < txin.scriptWitness.stack.size(); i++) {
|
||||
witness.push_back(HexStr(txin.scriptWitness.stack[i].begin(), txin.scriptWitness.stack[i].end()));
|
||||
}
|
||||
entry.pushKV("witness", witness);
|
||||
entry.pushKV("scriptSig", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()));
|
||||
entry.pushKV("sequence", (uint64_t)txin.nSequence);
|
||||
entry.pushKV("error", strMessage);
|
||||
vErrorsRet.push_back(entry);
|
||||
}
|
||||
|
||||
static UniValue combinerawtransaction(const JSONRPCRequest& request)
|
||||
{
|
||||
if (request.fHelp || request.params.size() != 1)
|
||||
|
@ -818,152 +689,6 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request)
|
|||
return EncodeHexTx(CTransaction(mergedTx));
|
||||
}
|
||||
|
||||
// TODO(https://github.com/bitcoin/bitcoin/pull/10973#discussion_r267084237):
|
||||
// This function is called from both wallet and node rpcs
|
||||
// (signrawtransactionwithwallet and signrawtransactionwithkey). It should be
|
||||
// moved to a util file so wallet code doesn't need to link against node code.
|
||||
// Also the dependency on interfaces::Chain should be removed, so
|
||||
// signrawtransactionwithkey doesn't need access to a Chain instance.
|
||||
UniValue SignTransaction(interfaces::Chain& chain, CMutableTransaction& mtx, const UniValue& prevTxsUnival, CBasicKeyStore *keystore, bool is_temp_keystore, const UniValue& hashType)
|
||||
{
|
||||
// Fetch previous transactions (inputs):
|
||||
std::map<COutPoint, Coin> coins;
|
||||
for (const CTxIn& txin : mtx.vin) {
|
||||
coins[txin.prevout]; // Create empty map entry keyed by prevout.
|
||||
}
|
||||
chain.findCoins(coins);
|
||||
|
||||
// Add previous txouts given in the RPC call:
|
||||
if (!prevTxsUnival.isNull()) {
|
||||
UniValue prevTxs = prevTxsUnival.get_array();
|
||||
for (unsigned int idx = 0; idx < prevTxs.size(); ++idx) {
|
||||
const UniValue& p = prevTxs[idx];
|
||||
if (!p.isObject()) {
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}");
|
||||
}
|
||||
|
||||
UniValue prevOut = p.get_obj();
|
||||
|
||||
RPCTypeCheckObj(prevOut,
|
||||
{
|
||||
{"txid", UniValueType(UniValue::VSTR)},
|
||||
{"vout", UniValueType(UniValue::VNUM)},
|
||||
{"scriptPubKey", UniValueType(UniValue::VSTR)},
|
||||
});
|
||||
|
||||
uint256 txid = ParseHashO(prevOut, "txid");
|
||||
|
||||
int nOut = find_value(prevOut, "vout").get_int();
|
||||
if (nOut < 0) {
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive");
|
||||
}
|
||||
|
||||
COutPoint out(txid, nOut);
|
||||
std::vector<unsigned char> pkData(ParseHexO(prevOut, "scriptPubKey"));
|
||||
CScript scriptPubKey(pkData.begin(), pkData.end());
|
||||
|
||||
{
|
||||
auto coin = coins.find(out);
|
||||
if (coin != coins.end() && !coin->second.IsSpent() && coin->second.out.scriptPubKey != scriptPubKey) {
|
||||
std::string err("Previous output scriptPubKey mismatch:\n");
|
||||
err = err + ScriptToAsmStr(coin->second.out.scriptPubKey) + "\nvs:\n"+
|
||||
ScriptToAsmStr(scriptPubKey);
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err);
|
||||
}
|
||||
Coin newcoin;
|
||||
newcoin.out.scriptPubKey = scriptPubKey;
|
||||
newcoin.out.nValue = MAX_MONEY;
|
||||
if (prevOut.exists("amount")) {
|
||||
newcoin.out.nValue = AmountFromValue(find_value(prevOut, "amount"));
|
||||
}
|
||||
newcoin.nHeight = 1;
|
||||
coins[out] = std::move(newcoin);
|
||||
}
|
||||
|
||||
// if redeemScript and private keys were given, add redeemScript to the keystore so it can be signed
|
||||
if (is_temp_keystore && (scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsPayToWitnessScriptHash())) {
|
||||
RPCTypeCheckObj(prevOut,
|
||||
{
|
||||
{"redeemScript", UniValueType(UniValue::VSTR)},
|
||||
{"witnessScript", UniValueType(UniValue::VSTR)},
|
||||
}, true);
|
||||
UniValue rs = find_value(prevOut, "redeemScript");
|
||||
if (!rs.isNull()) {
|
||||
std::vector<unsigned char> rsData(ParseHexV(rs, "redeemScript"));
|
||||
CScript redeemScript(rsData.begin(), rsData.end());
|
||||
keystore->AddCScript(redeemScript);
|
||||
// Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH).
|
||||
// This is only for compatibility, it is encouraged to use the explicit witnessScript field instead.
|
||||
keystore->AddCScript(GetScriptForWitness(redeemScript));
|
||||
}
|
||||
UniValue ws = find_value(prevOut, "witnessScript");
|
||||
if (!ws.isNull()) {
|
||||
std::vector<unsigned char> wsData(ParseHexV(ws, "witnessScript"));
|
||||
CScript witnessScript(wsData.begin(), wsData.end());
|
||||
keystore->AddCScript(witnessScript);
|
||||
// Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH).
|
||||
keystore->AddCScript(GetScriptForWitness(witnessScript));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int nHashType = ParseSighashString(hashType);
|
||||
|
||||
bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
|
||||
|
||||
// Script verification errors
|
||||
UniValue vErrors(UniValue::VARR);
|
||||
|
||||
// Use CTransaction for the constant parts of the
|
||||
// transaction to avoid rehashing.
|
||||
const CTransaction txConst(mtx);
|
||||
// Sign what we can:
|
||||
for (unsigned int i = 0; i < mtx.vin.size(); i++) {
|
||||
CTxIn& txin = mtx.vin[i];
|
||||
auto coin = coins.find(txin.prevout);
|
||||
if (coin == coins.end() || coin->second.IsSpent()) {
|
||||
TxInErrorToJSON(txin, vErrors, "Input not found or already spent");
|
||||
continue;
|
||||
}
|
||||
const CScript& prevPubKey = coin->second.out.scriptPubKey;
|
||||
const CAmount& amount = coin->second.out.nValue;
|
||||
|
||||
SignatureData sigdata = DataFromTransaction(mtx, i, coin->second.out);
|
||||
// Only sign SIGHASH_SINGLE if there's a corresponding output:
|
||||
if (!fHashSingle || (i < mtx.vout.size())) {
|
||||
ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, nHashType), prevPubKey, sigdata);
|
||||
}
|
||||
|
||||
UpdateInput(txin, sigdata);
|
||||
|
||||
// amount must be specified for valid segwit signature
|
||||
if (amount == MAX_MONEY && !txin.scriptWitness.IsNull()) {
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing amount for %s", coin->second.out.ToString()));
|
||||
}
|
||||
|
||||
ScriptError serror = SCRIPT_ERR_OK;
|
||||
if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) {
|
||||
if (serror == SCRIPT_ERR_INVALID_STACK_OPERATION) {
|
||||
// Unable to sign input and verification failed (possible attempt to partially sign).
|
||||
TxInErrorToJSON(txin, vErrors, "Unable to sign input, invalid stack size (possibly missing key)");
|
||||
} else {
|
||||
TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror));
|
||||
}
|
||||
}
|
||||
}
|
||||
bool fComplete = vErrors.empty();
|
||||
|
||||
UniValue result(UniValue::VOBJ);
|
||||
result.pushKV("hex", EncodeHexTx(CTransaction(mtx)));
|
||||
result.pushKV("complete", fComplete);
|
||||
if (!vErrors.empty()) {
|
||||
result.pushKV("errors", vErrors);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
|
||||
{
|
||||
if (request.fHelp || request.params.size() < 2 || request.params.size() > 4)
|
||||
|
|
293
src/rpc/rawtransaction_util.cpp
Normal file
293
src/rpc/rawtransaction_util.cpp
Normal file
|
@ -0,0 +1,293 @@
|
|||
// Copyright (c) 2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-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.
|
||||
|
||||
#include <rpc/rawtransaction_util.h>
|
||||
|
||||
#include <coins.h>
|
||||
#include <core_io.h>
|
||||
#include <interfaces/chain.h>
|
||||
#include <key_io.h>
|
||||
#include <keystore.h>
|
||||
#include <policy/policy.h>
|
||||
#include <primitives/transaction.h>
|
||||
#include <rpc/protocol.h>
|
||||
#include <rpc/util.h>
|
||||
#include <tinyformat.h>
|
||||
#include <univalue.h>
|
||||
#include <util/rbf.h>
|
||||
#include <util/strencodings.h>
|
||||
|
||||
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf)
|
||||
{
|
||||
if (inputs_in.isNull() || outputs_in.isNull())
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null");
|
||||
|
||||
UniValue inputs = inputs_in.get_array();
|
||||
const bool outputs_is_obj = outputs_in.isObject();
|
||||
UniValue outputs = outputs_is_obj ? outputs_in.get_obj() : outputs_in.get_array();
|
||||
|
||||
CMutableTransaction rawTx;
|
||||
|
||||
if (!locktime.isNull()) {
|
||||
int64_t nLockTime = locktime.get_int64();
|
||||
if (nLockTime < 0 || nLockTime > LOCKTIME_MAX)
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range");
|
||||
rawTx.nLockTime = nLockTime;
|
||||
}
|
||||
|
||||
bool rbfOptIn = rbf.isTrue();
|
||||
|
||||
for (unsigned int idx = 0; idx < inputs.size(); idx++) {
|
||||
const UniValue& input = inputs[idx];
|
||||
const UniValue& o = input.get_obj();
|
||||
|
||||
uint256 txid = ParseHashO(o, "txid");
|
||||
|
||||
const UniValue& vout_v = find_value(o, "vout");
|
||||
if (!vout_v.isNum())
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key");
|
||||
int nOutput = vout_v.get_int();
|
||||
if (nOutput < 0)
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
|
||||
|
||||
uint32_t nSequence;
|
||||
if (rbfOptIn) {
|
||||
nSequence = MAX_BIP125_RBF_SEQUENCE; /* CTxIn::SEQUENCE_FINAL - 2 */
|
||||
} else if (rawTx.nLockTime) {
|
||||
nSequence = CTxIn::SEQUENCE_FINAL - 1;
|
||||
} else {
|
||||
nSequence = CTxIn::SEQUENCE_FINAL;
|
||||
}
|
||||
|
||||
// set the sequence number if passed in the parameters object
|
||||
const UniValue& sequenceObj = find_value(o, "sequence");
|
||||
if (sequenceObj.isNum()) {
|
||||
int64_t seqNr64 = sequenceObj.get_int64();
|
||||
if (seqNr64 < 0 || seqNr64 > CTxIn::SEQUENCE_FINAL) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, sequence number is out of range");
|
||||
} else {
|
||||
nSequence = (uint32_t)seqNr64;
|
||||
}
|
||||
}
|
||||
|
||||
CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence);
|
||||
|
||||
rawTx.vin.push_back(in);
|
||||
}
|
||||
|
||||
if (!outputs_is_obj) {
|
||||
// Translate array of key-value pairs into dict
|
||||
UniValue outputs_dict = UniValue(UniValue::VOBJ);
|
||||
for (size_t i = 0; i < outputs.size(); ++i) {
|
||||
const UniValue& output = outputs[i];
|
||||
if (!output.isObject()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, key-value pair not an object as expected");
|
||||
}
|
||||
if (output.size() != 1) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, key-value pair must contain exactly one key");
|
||||
}
|
||||
outputs_dict.pushKVs(output);
|
||||
}
|
||||
outputs = std::move(outputs_dict);
|
||||
}
|
||||
|
||||
// Duplicate checking
|
||||
std::set<CTxDestination> destinations;
|
||||
bool has_data{false};
|
||||
|
||||
for (const std::string& name_ : outputs.getKeys()) {
|
||||
if (name_ == "data") {
|
||||
if (has_data) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, duplicate key: data");
|
||||
}
|
||||
has_data = true;
|
||||
std::vector<unsigned char> data = ParseHexV(outputs[name_].getValStr(), "Data");
|
||||
|
||||
CTxOut out(0, CScript() << OP_RETURN << data);
|
||||
rawTx.vout.push_back(out);
|
||||
} else {
|
||||
CTxDestination destination = DecodeDestination(name_);
|
||||
if (!IsValidDestination(destination)) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + name_);
|
||||
}
|
||||
|
||||
if (!destinations.insert(destination).second) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + name_);
|
||||
}
|
||||
|
||||
CScript scriptPubKey = GetScriptForDestination(destination);
|
||||
CAmount nAmount = AmountFromValue(outputs[name_]);
|
||||
|
||||
CTxOut out(nAmount, scriptPubKey);
|
||||
rawTx.vout.push_back(out);
|
||||
}
|
||||
}
|
||||
|
||||
if (!rbf.isNull() && rawTx.vin.size() > 0 && rbfOptIn != SignalsOptInRBF(CTransaction(rawTx))) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict replaceable option");
|
||||
}
|
||||
|
||||
return rawTx;
|
||||
}
|
||||
|
||||
/** Pushes a JSON object for script verification or signing errors to vErrorsRet. */
|
||||
static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::string& strMessage)
|
||||
{
|
||||
UniValue entry(UniValue::VOBJ);
|
||||
entry.pushKV("txid", txin.prevout.hash.ToString());
|
||||
entry.pushKV("vout", (uint64_t)txin.prevout.n);
|
||||
UniValue witness(UniValue::VARR);
|
||||
for (unsigned int i = 0; i < txin.scriptWitness.stack.size(); i++) {
|
||||
witness.push_back(HexStr(txin.scriptWitness.stack[i].begin(), txin.scriptWitness.stack[i].end()));
|
||||
}
|
||||
entry.pushKV("witness", witness);
|
||||
entry.pushKV("scriptSig", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()));
|
||||
entry.pushKV("sequence", (uint64_t)txin.nSequence);
|
||||
entry.pushKV("error", strMessage);
|
||||
vErrorsRet.push_back(entry);
|
||||
}
|
||||
|
||||
// TODO(https://github.com/bitcoin/bitcoin/pull/10973#discussion_r267084237):
|
||||
// The dependency on interfaces::Chain should be removed, so
|
||||
// signrawtransactionwithkey doesn't need access to a Chain instance.
|
||||
UniValue SignTransaction(interfaces::Chain& chain, CMutableTransaction& mtx, const UniValue& prevTxsUnival, CBasicKeyStore *keystore, bool is_temp_keystore, const UniValue& hashType)
|
||||
{
|
||||
// Fetch previous transactions (inputs):
|
||||
std::map<COutPoint, Coin> coins;
|
||||
for (const CTxIn& txin : mtx.vin) {
|
||||
coins[txin.prevout]; // Create empty map entry keyed by prevout.
|
||||
}
|
||||
chain.findCoins(coins);
|
||||
|
||||
// Add previous txouts given in the RPC call:
|
||||
if (!prevTxsUnival.isNull()) {
|
||||
UniValue prevTxs = prevTxsUnival.get_array();
|
||||
for (unsigned int idx = 0; idx < prevTxs.size(); ++idx) {
|
||||
const UniValue& p = prevTxs[idx];
|
||||
if (!p.isObject()) {
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}");
|
||||
}
|
||||
|
||||
UniValue prevOut = p.get_obj();
|
||||
|
||||
RPCTypeCheckObj(prevOut,
|
||||
{
|
||||
{"txid", UniValueType(UniValue::VSTR)},
|
||||
{"vout", UniValueType(UniValue::VNUM)},
|
||||
{"scriptPubKey", UniValueType(UniValue::VSTR)},
|
||||
});
|
||||
|
||||
uint256 txid = ParseHashO(prevOut, "txid");
|
||||
|
||||
int nOut = find_value(prevOut, "vout").get_int();
|
||||
if (nOut < 0) {
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive");
|
||||
}
|
||||
|
||||
COutPoint out(txid, nOut);
|
||||
std::vector<unsigned char> pkData(ParseHexO(prevOut, "scriptPubKey"));
|
||||
CScript scriptPubKey(pkData.begin(), pkData.end());
|
||||
|
||||
{
|
||||
auto coin = coins.find(out);
|
||||
if (coin != coins.end() && !coin->second.IsSpent() && coin->second.out.scriptPubKey != scriptPubKey) {
|
||||
std::string err("Previous output scriptPubKey mismatch:\n");
|
||||
err = err + ScriptToAsmStr(coin->second.out.scriptPubKey) + "\nvs:\n"+
|
||||
ScriptToAsmStr(scriptPubKey);
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err);
|
||||
}
|
||||
Coin newcoin;
|
||||
newcoin.out.scriptPubKey = scriptPubKey;
|
||||
newcoin.out.nValue = MAX_MONEY;
|
||||
if (prevOut.exists("amount")) {
|
||||
newcoin.out.nValue = AmountFromValue(find_value(prevOut, "amount"));
|
||||
}
|
||||
newcoin.nHeight = 1;
|
||||
coins[out] = std::move(newcoin);
|
||||
}
|
||||
|
||||
// if redeemScript and private keys were given, add redeemScript to the keystore so it can be signed
|
||||
if (is_temp_keystore && (scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsPayToWitnessScriptHash())) {
|
||||
RPCTypeCheckObj(prevOut,
|
||||
{
|
||||
{"redeemScript", UniValueType(UniValue::VSTR)},
|
||||
{"witnessScript", UniValueType(UniValue::VSTR)},
|
||||
}, true);
|
||||
UniValue rs = find_value(prevOut, "redeemScript");
|
||||
if (!rs.isNull()) {
|
||||
std::vector<unsigned char> rsData(ParseHexV(rs, "redeemScript"));
|
||||
CScript redeemScript(rsData.begin(), rsData.end());
|
||||
keystore->AddCScript(redeemScript);
|
||||
// Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH).
|
||||
// This is only for compatibility, it is encouraged to use the explicit witnessScript field instead.
|
||||
keystore->AddCScript(GetScriptForWitness(redeemScript));
|
||||
}
|
||||
UniValue ws = find_value(prevOut, "witnessScript");
|
||||
if (!ws.isNull()) {
|
||||
std::vector<unsigned char> wsData(ParseHexV(ws, "witnessScript"));
|
||||
CScript witnessScript(wsData.begin(), wsData.end());
|
||||
keystore->AddCScript(witnessScript);
|
||||
// Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH).
|
||||
keystore->AddCScript(GetScriptForWitness(witnessScript));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int nHashType = ParseSighashString(hashType);
|
||||
|
||||
bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
|
||||
|
||||
// Script verification errors
|
||||
UniValue vErrors(UniValue::VARR);
|
||||
|
||||
// Use CTransaction for the constant parts of the
|
||||
// transaction to avoid rehashing.
|
||||
const CTransaction txConst(mtx);
|
||||
// Sign what we can:
|
||||
for (unsigned int i = 0; i < mtx.vin.size(); i++) {
|
||||
CTxIn& txin = mtx.vin[i];
|
||||
auto coin = coins.find(txin.prevout);
|
||||
if (coin == coins.end() || coin->second.IsSpent()) {
|
||||
TxInErrorToJSON(txin, vErrors, "Input not found or already spent");
|
||||
continue;
|
||||
}
|
||||
const CScript& prevPubKey = coin->second.out.scriptPubKey;
|
||||
const CAmount& amount = coin->second.out.nValue;
|
||||
|
||||
SignatureData sigdata = DataFromTransaction(mtx, i, coin->second.out);
|
||||
// Only sign SIGHASH_SINGLE if there's a corresponding output:
|
||||
if (!fHashSingle || (i < mtx.vout.size())) {
|
||||
ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, nHashType), prevPubKey, sigdata);
|
||||
}
|
||||
|
||||
UpdateInput(txin, sigdata);
|
||||
|
||||
// amount must be specified for valid segwit signature
|
||||
if (amount == MAX_MONEY && !txin.scriptWitness.IsNull()) {
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing amount for %s", coin->second.out.ToString()));
|
||||
}
|
||||
|
||||
ScriptError serror = SCRIPT_ERR_OK;
|
||||
if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) {
|
||||
if (serror == SCRIPT_ERR_INVALID_STACK_OPERATION) {
|
||||
// Unable to sign input and verification failed (possible attempt to partially sign).
|
||||
TxInErrorToJSON(txin, vErrors, "Unable to sign input, invalid stack size (possibly missing key)");
|
||||
} else {
|
||||
TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror));
|
||||
}
|
||||
}
|
||||
}
|
||||
bool fComplete = vErrors.empty();
|
||||
|
||||
UniValue result(UniValue::VOBJ);
|
||||
result.pushKV("hex", EncodeHexTx(CTransaction(mtx)));
|
||||
result.pushKV("complete", fComplete);
|
||||
if (!vErrors.empty()) {
|
||||
result.pushKV("errors", vErrors);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
|
@ -2,12 +2,12 @@
|
|||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_RPC_RAWTRANSACTION_H
|
||||
#define BITCOIN_RPC_RAWTRANSACTION_H
|
||||
#ifndef BITCOIN_RPC_RAWTRANSACTION_UTIL_H
|
||||
#define BITCOIN_RPC_RAWTRANSACTION_UTIL_H
|
||||
|
||||
class CBasicKeyStore;
|
||||
struct CMutableTransaction;
|
||||
class UniValue;
|
||||
struct CMutableTransaction;
|
||||
|
||||
namespace interfaces {
|
||||
class Chain;
|
||||
|
@ -19,4 +19,4 @@ UniValue SignTransaction(interfaces::Chain& chain, CMutableTransaction& mtx, con
|
|||
/** Create a transaction from univalue parameters */
|
||||
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf);
|
||||
|
||||
#endif // BITCOIN_RPC_RAWTRANSACTION_H
|
||||
#endif // BITCOIN_RPC_RAWTRANSACTION_UTIL_H
|
|
@ -77,99 +77,6 @@ void RPCServer::OnStopped(std::function<void ()> slot)
|
|||
g_rpcSignals.Stopped.connect(slot);
|
||||
}
|
||||
|
||||
void RPCTypeCheck(const UniValue& params,
|
||||
const std::list<UniValueType>& typesExpected,
|
||||
bool fAllowNull)
|
||||
{
|
||||
unsigned int i = 0;
|
||||
for (const UniValueType& t : typesExpected) {
|
||||
if (params.size() <= i)
|
||||
break;
|
||||
|
||||
const UniValue& v = params[i];
|
||||
if (!(fAllowNull && v.isNull())) {
|
||||
RPCTypeCheckArgument(v, t);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void RPCTypeCheckArgument(const UniValue& value, const UniValueType& typeExpected)
|
||||
{
|
||||
if (!typeExpected.typeAny && value.type() != typeExpected.type) {
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Expected type %s, got %s", uvTypeName(typeExpected.type), uvTypeName(value.type())));
|
||||
}
|
||||
}
|
||||
|
||||
void RPCTypeCheckObj(const UniValue& o,
|
||||
const std::map<std::string, UniValueType>& typesExpected,
|
||||
bool fAllowNull,
|
||||
bool fStrict)
|
||||
{
|
||||
for (const auto& t : typesExpected) {
|
||||
const UniValue& v = find_value(o, t.first);
|
||||
if (!fAllowNull && v.isNull())
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first));
|
||||
|
||||
if (!(t.second.typeAny || v.type() == t.second.type || (fAllowNull && v.isNull()))) {
|
||||
std::string err = strprintf("Expected type %s for %s, got %s",
|
||||
uvTypeName(t.second.type), t.first, uvTypeName(v.type()));
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, err);
|
||||
}
|
||||
}
|
||||
|
||||
if (fStrict)
|
||||
{
|
||||
for (const std::string& k : o.getKeys())
|
||||
{
|
||||
if (typesExpected.count(k) == 0)
|
||||
{
|
||||
std::string err = strprintf("Unexpected key %s", k);
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CAmount AmountFromValue(const UniValue& value)
|
||||
{
|
||||
if (!value.isNum() && !value.isStr())
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string");
|
||||
CAmount amount;
|
||||
if (!ParseFixedPoint(value.getValStr(), 8, &amount))
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
|
||||
if (!MoneyRange(amount))
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range");
|
||||
return amount;
|
||||
}
|
||||
|
||||
uint256 ParseHashV(const UniValue& v, std::string strName)
|
||||
{
|
||||
std::string strHex(v.get_str());
|
||||
if (64 != strHex.length())
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be of length %d (not %d, for '%s')", strName, 64, strHex.length(), strHex));
|
||||
if (!IsHex(strHex)) // Note: IsHex("") is false
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
|
||||
return uint256S(strHex);
|
||||
}
|
||||
uint256 ParseHashO(const UniValue& o, std::string strKey)
|
||||
{
|
||||
return ParseHashV(find_value(o, strKey), strKey);
|
||||
}
|
||||
std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strName)
|
||||
{
|
||||
std::string strHex;
|
||||
if (v.isStr())
|
||||
strHex = v.get_str();
|
||||
if (!IsHex(strHex))
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
|
||||
return ParseHex(strHex);
|
||||
}
|
||||
std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey)
|
||||
{
|
||||
return ParseHexV(find_value(o, strKey), strKey);
|
||||
}
|
||||
|
||||
std::string CRPCTable::help(const std::string& strCommand, const JSONRPCRequest& helpreq) const
|
||||
{
|
||||
std::string strRet;
|
||||
|
@ -593,17 +500,6 @@ std::vector<std::string> CRPCTable::listCommands() const
|
|||
return commandList;
|
||||
}
|
||||
|
||||
std::string HelpExampleCli(const std::string& methodname, const std::string& args)
|
||||
{
|
||||
return "> bitcoin-cli " + methodname + " " + args + "\n";
|
||||
}
|
||||
|
||||
std::string HelpExampleRpc(const std::string& methodname, const std::string& args)
|
||||
{
|
||||
return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", "
|
||||
"\"method\": \"" + methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n";
|
||||
}
|
||||
|
||||
void RPCSetTimerInterfaceIfUnset(RPCTimerInterface *iface)
|
||||
{
|
||||
if (!timerInterface)
|
||||
|
|
|
@ -27,15 +27,6 @@ namespace RPCServer
|
|||
void OnStopped(std::function<void ()> slot);
|
||||
}
|
||||
|
||||
/** Wrapper for UniValue::VType, which includes typeAny:
|
||||
* Used to denote don't care type. */
|
||||
struct UniValueType {
|
||||
UniValueType(UniValue::VType _type) : typeAny(false), type(_type) {}
|
||||
UniValueType() : typeAny(true) {}
|
||||
bool typeAny;
|
||||
UniValue::VType type;
|
||||
};
|
||||
|
||||
class JSONRPCRequest
|
||||
{
|
||||
public:
|
||||
|
@ -65,26 +56,6 @@ void SetRPCWarmupFinished();
|
|||
/* returns the current warmup state. */
|
||||
bool RPCIsInWarmup(std::string *outStatus);
|
||||
|
||||
/**
|
||||
* Type-check arguments; throws JSONRPCError if wrong type given. Does not check that
|
||||
* the right number of arguments are passed, just that any passed are the correct type.
|
||||
*/
|
||||
void RPCTypeCheck(const UniValue& params,
|
||||
const std::list<UniValueType>& typesExpected, bool fAllowNull=false);
|
||||
|
||||
/**
|
||||
* Type-check one argument; throws JSONRPCError if wrong type given.
|
||||
*/
|
||||
void RPCTypeCheckArgument(const UniValue& value, const UniValueType& typeExpected);
|
||||
|
||||
/*
|
||||
Check for expected keys/value types in an Object.
|
||||
*/
|
||||
void RPCTypeCheckObj(const UniValue& o,
|
||||
const std::map<std::string, UniValueType>& typesExpected,
|
||||
bool fAllowNull = false,
|
||||
bool fStrict = false);
|
||||
|
||||
/** Opaque base class for timers returned by NewTimerFunc.
|
||||
* This provides no methods at the moment, but makes sure that delete
|
||||
* cleans up the whole state.
|
||||
|
@ -204,19 +175,6 @@ bool IsDeprecatedRPCEnabled(const std::string& method);
|
|||
|
||||
extern CRPCTable tableRPC;
|
||||
|
||||
/**
|
||||
* Utilities: convert hex-encoded Values
|
||||
* (throws error if not hex).
|
||||
*/
|
||||
extern uint256 ParseHashV(const UniValue& v, std::string strName);
|
||||
extern uint256 ParseHashO(const UniValue& o, std::string strKey);
|
||||
extern std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strName);
|
||||
extern std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey);
|
||||
|
||||
extern CAmount AmountFromValue(const UniValue& value);
|
||||
extern std::string HelpExampleCli(const std::string& methodname, const std::string& args);
|
||||
extern std::string HelpExampleRpc(const std::string& methodname, const std::string& args);
|
||||
|
||||
void StartRPC();
|
||||
void InterruptRPC();
|
||||
void StopRPC();
|
||||
|
|
104
src/rpc/util.cpp
104
src/rpc/util.cpp
|
@ -10,6 +10,110 @@
|
|||
|
||||
InitInterfaces* g_rpc_interfaces = nullptr;
|
||||
|
||||
void RPCTypeCheck(const UniValue& params,
|
||||
const std::list<UniValueType>& typesExpected,
|
||||
bool fAllowNull)
|
||||
{
|
||||
unsigned int i = 0;
|
||||
for (const UniValueType& t : typesExpected) {
|
||||
if (params.size() <= i)
|
||||
break;
|
||||
|
||||
const UniValue& v = params[i];
|
||||
if (!(fAllowNull && v.isNull())) {
|
||||
RPCTypeCheckArgument(v, t);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void RPCTypeCheckArgument(const UniValue& value, const UniValueType& typeExpected)
|
||||
{
|
||||
if (!typeExpected.typeAny && value.type() != typeExpected.type) {
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Expected type %s, got %s", uvTypeName(typeExpected.type), uvTypeName(value.type())));
|
||||
}
|
||||
}
|
||||
|
||||
void RPCTypeCheckObj(const UniValue& o,
|
||||
const std::map<std::string, UniValueType>& typesExpected,
|
||||
bool fAllowNull,
|
||||
bool fStrict)
|
||||
{
|
||||
for (const auto& t : typesExpected) {
|
||||
const UniValue& v = find_value(o, t.first);
|
||||
if (!fAllowNull && v.isNull())
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first));
|
||||
|
||||
if (!(t.second.typeAny || v.type() == t.second.type || (fAllowNull && v.isNull()))) {
|
||||
std::string err = strprintf("Expected type %s for %s, got %s",
|
||||
uvTypeName(t.second.type), t.first, uvTypeName(v.type()));
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, err);
|
||||
}
|
||||
}
|
||||
|
||||
if (fStrict)
|
||||
{
|
||||
for (const std::string& k : o.getKeys())
|
||||
{
|
||||
if (typesExpected.count(k) == 0)
|
||||
{
|
||||
std::string err = strprintf("Unexpected key %s", k);
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CAmount AmountFromValue(const UniValue& value)
|
||||
{
|
||||
if (!value.isNum() && !value.isStr())
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string");
|
||||
CAmount amount;
|
||||
if (!ParseFixedPoint(value.getValStr(), 8, &amount))
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
|
||||
if (!MoneyRange(amount))
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range");
|
||||
return amount;
|
||||
}
|
||||
|
||||
uint256 ParseHashV(const UniValue& v, std::string strName)
|
||||
{
|
||||
std::string strHex(v.get_str());
|
||||
if (64 != strHex.length())
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be of length %d (not %d, for '%s')", strName, 64, strHex.length(), strHex));
|
||||
if (!IsHex(strHex)) // Note: IsHex("") is false
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
|
||||
return uint256S(strHex);
|
||||
}
|
||||
uint256 ParseHashO(const UniValue& o, std::string strKey)
|
||||
{
|
||||
return ParseHashV(find_value(o, strKey), strKey);
|
||||
}
|
||||
std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strName)
|
||||
{
|
||||
std::string strHex;
|
||||
if (v.isStr())
|
||||
strHex = v.get_str();
|
||||
if (!IsHex(strHex))
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
|
||||
return ParseHex(strHex);
|
||||
}
|
||||
std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey)
|
||||
{
|
||||
return ParseHexV(find_value(o, strKey), strKey);
|
||||
}
|
||||
|
||||
std::string HelpExampleCli(const std::string& methodname, const std::string& args)
|
||||
{
|
||||
return "> bitcoin-cli " + methodname + " " + args + "\n";
|
||||
}
|
||||
|
||||
std::string HelpExampleRpc(const std::string& methodname, const std::string& args)
|
||||
{
|
||||
return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", "
|
||||
"\"method\": \"" + methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n";
|
||||
}
|
||||
|
||||
// Converts a hex string to a public key if possible
|
||||
CPubKey HexToPubKey(const std::string& hex_in)
|
||||
{
|
||||
|
|
|
@ -26,6 +26,48 @@ struct InitInterfaces;
|
|||
//! state to RPC method implementations.
|
||||
extern InitInterfaces* g_rpc_interfaces;
|
||||
|
||||
/** Wrapper for UniValue::VType, which includes typeAny:
|
||||
* Used to denote don't care type. */
|
||||
struct UniValueType {
|
||||
UniValueType(UniValue::VType _type) : typeAny(false), type(_type) {}
|
||||
UniValueType() : typeAny(true) {}
|
||||
bool typeAny;
|
||||
UniValue::VType type;
|
||||
};
|
||||
|
||||
/**
|
||||
* Type-check arguments; throws JSONRPCError if wrong type given. Does not check that
|
||||
* the right number of arguments are passed, just that any passed are the correct type.
|
||||
*/
|
||||
void RPCTypeCheck(const UniValue& params,
|
||||
const std::list<UniValueType>& typesExpected, bool fAllowNull=false);
|
||||
|
||||
/**
|
||||
* Type-check one argument; throws JSONRPCError if wrong type given.
|
||||
*/
|
||||
void RPCTypeCheckArgument(const UniValue& value, const UniValueType& typeExpected);
|
||||
|
||||
/*
|
||||
Check for expected keys/value types in an Object.
|
||||
*/
|
||||
void RPCTypeCheckObj(const UniValue& o,
|
||||
const std::map<std::string, UniValueType>& typesExpected,
|
||||
bool fAllowNull = false,
|
||||
bool fStrict = false);
|
||||
|
||||
/**
|
||||
* Utilities: convert hex-encoded Values
|
||||
* (throws error if not hex).
|
||||
*/
|
||||
extern uint256 ParseHashV(const UniValue& v, std::string strName);
|
||||
extern uint256 ParseHashO(const UniValue& o, std::string strKey);
|
||||
extern std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strName);
|
||||
extern std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey);
|
||||
|
||||
extern CAmount AmountFromValue(const UniValue& value);
|
||||
extern std::string HelpExampleCli(const std::string& methodname, const std::string& args);
|
||||
extern std::string HelpExampleRpc(const std::string& methodname, const std::string& args);
|
||||
|
||||
CPubKey HexToPubKey(const std::string& hex_in);
|
||||
CPubKey AddrToPubKey(CKeyStore* const keystore, const std::string& addr_in);
|
||||
CScript CreateMultisigRedeemscript(const int required, const std::vector<CPubKey>& pubkeys);
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <policy/policy.h>
|
||||
#include <script/script.h>
|
||||
#include <script/script_error.h>
|
||||
#include <policy/settings.h>
|
||||
#include <script/sign.h>
|
||||
#include <script/ismine.h>
|
||||
#include <test/test_bitcoin.h>
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
#include <util/system.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <test/test_bitcoin.h>
|
||||
#include <rpc/server.h>
|
||||
#include <rpc/util.h>
|
||||
|
||||
#if defined(HAVE_CONSENSUS_LIB)
|
||||
#include <script/bitcoinconsensus.h>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <consensus/tx_verify.h>
|
||||
#include <consensus/tx_check.h>
|
||||
#include <consensus/validation.h>
|
||||
#include <test/data/sighash.json.h>
|
||||
#include <hash.h>
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <script/sigcache.h>
|
||||
#include <streams.h>
|
||||
#include <ui_interface.h>
|
||||
#include <util/validation.h>
|
||||
#include <validation.h>
|
||||
|
||||
const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
|
||||
|
|
|
@ -8,13 +8,14 @@
|
|||
|
||||
#include <clientversion.h>
|
||||
#include <checkqueue.h>
|
||||
#include <consensus/tx_verify.h>
|
||||
#include <consensus/tx_check.h>
|
||||
#include <consensus/validation.h>
|
||||
#include <core_io.h>
|
||||
#include <key.h>
|
||||
#include <keystore.h>
|
||||
#include <validation.h>
|
||||
#include <policy/policy.h>
|
||||
#include <policy/settings.h>
|
||||
#include <script/script.h>
|
||||
#include <script/sign.h>
|
||||
#include <script/script_error.h>
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <validation.h>
|
||||
#include <policy/policy.h>
|
||||
#include <policy/fees.h>
|
||||
#include <policy/settings.h>
|
||||
#include <reverse_iterator.h>
|
||||
#include <streams.h>
|
||||
#include <timedata.h>
|
||||
|
|
|
@ -69,13 +69,3 @@ void InitWarning(const std::string& str)
|
|||
{
|
||||
uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_WARNING);
|
||||
}
|
||||
|
||||
std::string AmountHighWarn(const std::string& optname)
|
||||
{
|
||||
return strprintf(_("%s is set very high!"), optname);
|
||||
}
|
||||
|
||||
std::string AmountErrMsg(const char* const optname, const std::string& strValue)
|
||||
{
|
||||
return strprintf(_("Invalid amount for -%s=<amount>: '%s'"), optname, strValue);
|
||||
}
|
||||
|
|
|
@ -129,10 +129,6 @@ void InitWarning(const std::string& str);
|
|||
/** Show error message **/
|
||||
bool InitError(const std::string& str);
|
||||
|
||||
std::string AmountHighWarn(const std::string& optname);
|
||||
|
||||
std::string AmountErrMsg(const char* const optname, const std::string& strValue);
|
||||
|
||||
extern CClientUIInterface uiInterface;
|
||||
|
||||
#endif // BITCOIN_UI_INTERFACE_H
|
||||
|
|
43
src/util/error.cpp
Normal file
43
src/util/error.cpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
// Copyright (c) 2010-2018 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <util/error.h>
|
||||
|
||||
#include <util/system.h>
|
||||
|
||||
std::string TransactionErrorString(const TransactionError err)
|
||||
{
|
||||
switch (err) {
|
||||
case TransactionError::OK:
|
||||
return "No error";
|
||||
case TransactionError::MISSING_INPUTS:
|
||||
return "Missing inputs";
|
||||
case TransactionError::ALREADY_IN_CHAIN:
|
||||
return "Transaction already in block chain";
|
||||
case TransactionError::P2P_DISABLED:
|
||||
return "Peer-to-peer functionality missing or disabled";
|
||||
case TransactionError::MEMPOOL_REJECTED:
|
||||
return "Transaction rejected by AcceptToMemoryPool";
|
||||
case TransactionError::MEMPOOL_ERROR:
|
||||
return "AcceptToMemoryPool failed";
|
||||
case TransactionError::INVALID_PSBT:
|
||||
return "PSBT is not sane";
|
||||
case TransactionError::PSBT_MISMATCH:
|
||||
return "PSBTs not compatible (different transactions)";
|
||||
case TransactionError::SIGHASH_MISMATCH:
|
||||
return "Specified sighash value does not match existing value";
|
||||
// no default case, so the compiler can warn about missing cases
|
||||
}
|
||||
assert(false);
|
||||
}
|
||||
|
||||
std::string AmountHighWarn(const std::string& optname)
|
||||
{
|
||||
return strprintf(_("%s is set very high!"), optname);
|
||||
}
|
||||
|
||||
std::string AmountErrMsg(const char* const optname, const std::string& strValue)
|
||||
{
|
||||
return strprintf(_("Invalid amount for -%s=<amount>: '%s'"), optname, strValue);
|
||||
}
|
38
src/util/error.h
Normal file
38
src/util/error.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
// Copyright (c) 2010-2018 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_UTIL_ERROR_H
|
||||
#define BITCOIN_UTIL_ERROR_H
|
||||
|
||||
/**
|
||||
* util/error.h is a common place for definitions of simple error types and
|
||||
* string functions. Types and functions defined here should not require any
|
||||
* outside dependencies.
|
||||
*
|
||||
* Error types defined here can be used in different parts of the bitcoin
|
||||
* codebase, to avoid the need to write boilerplate code catching and
|
||||
* translating errors passed across wallet/node/rpc/gui code boundaries.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
|
||||
enum class TransactionError {
|
||||
OK, //!< No error
|
||||
MISSING_INPUTS,
|
||||
ALREADY_IN_CHAIN,
|
||||
P2P_DISABLED,
|
||||
MEMPOOL_REJECTED,
|
||||
MEMPOOL_ERROR,
|
||||
INVALID_PSBT,
|
||||
PSBT_MISMATCH,
|
||||
SIGHASH_MISMATCH,
|
||||
};
|
||||
|
||||
std::string TransactionErrorString(const TransactionError error);
|
||||
|
||||
std::string AmountHighWarn(const std::string& optname);
|
||||
|
||||
std::string AmountErrMsg(const char* const optname, const std::string& strValue);
|
||||
|
||||
#endif // BITCOIN_UTIL_ERROR_H
|
42
src/util/fees.cpp
Normal file
42
src/util/fees.cpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-2018 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <policy/fees.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
std::string StringForFeeReason(FeeReason reason) {
|
||||
static const std::map<FeeReason, std::string> fee_reason_strings = {
|
||||
{FeeReason::NONE, "None"},
|
||||
{FeeReason::HALF_ESTIMATE, "Half Target 60% Threshold"},
|
||||
{FeeReason::FULL_ESTIMATE, "Target 85% Threshold"},
|
||||
{FeeReason::DOUBLE_ESTIMATE, "Double Target 95% Threshold"},
|
||||
{FeeReason::CONSERVATIVE, "Conservative Double Target longer horizon"},
|
||||
{FeeReason::MEMPOOL_MIN, "Mempool Min Fee"},
|
||||
{FeeReason::PAYTXFEE, "PayTxFee set"},
|
||||
{FeeReason::FALLBACK, "Fallback fee"},
|
||||
{FeeReason::REQUIRED, "Minimum Required Fee"},
|
||||
{FeeReason::MAXTXFEE, "MaxTxFee limit"}
|
||||
};
|
||||
auto reason_string = fee_reason_strings.find(reason);
|
||||
|
||||
if (reason_string == fee_reason_strings.end()) return "Unknown";
|
||||
|
||||
return reason_string->second;
|
||||
}
|
||||
|
||||
bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_estimate_mode) {
|
||||
static const std::map<std::string, FeeEstimateMode> fee_modes = {
|
||||
{"UNSET", FeeEstimateMode::UNSET},
|
||||
{"ECONOMICAL", FeeEstimateMode::ECONOMICAL},
|
||||
{"CONSERVATIVE", FeeEstimateMode::CONSERVATIVE},
|
||||
};
|
||||
auto mode = fee_modes.find(mode_string);
|
||||
|
||||
if (mode == fee_modes.end()) return false;
|
||||
|
||||
fee_estimate_mode = mode->second;
|
||||
return true;
|
||||
}
|
16
src/util/fees.h
Normal file
16
src/util/fees.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-2018 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
#ifndef BITCOIN_UTIL_FEES_H
|
||||
#define BITCOIN_UTIL_FEES_H
|
||||
|
||||
#include <string>
|
||||
|
||||
enum class FeeEstimateMode;
|
||||
enum class FeeReason;
|
||||
|
||||
bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_estimate_mode);
|
||||
std::string StringForFeeReason(FeeReason reason);
|
||||
|
||||
#endif // BITCOIN_UTIL_FEES_H
|
17
src/util/rbf.cpp
Normal file
17
src/util/rbf.cpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) 2016-2018 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <util/rbf.h>
|
||||
|
||||
#include <primitives/transaction.h>
|
||||
|
||||
bool SignalsOptInRBF(const CTransaction &tx)
|
||||
{
|
||||
for (const CTxIn &txin : tx.vin) {
|
||||
if (txin.nSequence <= MAX_BIP125_RBF_SEQUENCE) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
18
src/util/rbf.h
Normal file
18
src/util/rbf.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) 2016-2018 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_UTIL_RBF_H
|
||||
#define BITCOIN_UTIL_RBF_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class CTransaction;
|
||||
|
||||
static const uint32_t MAX_BIP125_RBF_SEQUENCE = 0xfffffffd;
|
||||
|
||||
// Check whether the sequence numbers on this transaction are signaling
|
||||
// opt-in to replace-by-fee, according to BIP 125
|
||||
bool SignalsOptInRBF(const CTransaction &tx);
|
||||
|
||||
#endif // BITCOIN_UTIL_RBF_H
|
21
src/util/url.cpp
Normal file
21
src/util/url.cpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
// Copyright (c) 2015-2018 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <util/url.h>
|
||||
|
||||
#include <event2/http.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
|
||||
std::string urlDecode(const std::string &urlEncoded) {
|
||||
std::string res;
|
||||
if (!urlEncoded.empty()) {
|
||||
char *decoded = evhttp_uridecode(urlEncoded.c_str(), false, nullptr);
|
||||
if (decoded) {
|
||||
res = std::string(decoded);
|
||||
free(decoded);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
12
src/util/url.h
Normal file
12
src/util/url.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
// Copyright (c) 2015-2018 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_UTIL_URL_H
|
||||
#define BITCOIN_UTIL_URL_H
|
||||
|
||||
#include <string>
|
||||
|
||||
std::string urlDecode(const std::string &urlEncoded);
|
||||
|
||||
#endif // BITCOIN_UTIL_URL_H
|
20
src/util/validation.cpp
Normal file
20
src/util/validation.cpp
Normal file
|
@ -0,0 +1,20 @@
|
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-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.
|
||||
|
||||
#include <util/validation.h>
|
||||
|
||||
#include <consensus/validation.h>
|
||||
#include <tinyformat.h>
|
||||
|
||||
/** Convert CValidationState to a human-readable message for logging */
|
||||
std::string FormatStateMessage(const CValidationState &state)
|
||||
{
|
||||
return strprintf("%s%s (code %i)",
|
||||
state.GetRejectReason(),
|
||||
state.GetDebugMessage().empty() ? "" : ", "+state.GetDebugMessage(),
|
||||
state.GetRejectCode());
|
||||
}
|
||||
|
||||
const std::string strMessageMagic = "Bitcoin Signed Message:\n";
|
18
src/util/validation.h
Normal file
18
src/util/validation.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-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.
|
||||
|
||||
#ifndef BITCOIN_UTIL_VALIDATION_H
|
||||
#define BITCOIN_UTIL_VALIDATION_H
|
||||
|
||||
#include <string>
|
||||
|
||||
class CValidationState;
|
||||
|
||||
/** Convert CValidationState to a human-readable message for logging */
|
||||
std::string FormatStateMessage(const CValidationState &state);
|
||||
|
||||
extern const std::string strMessageMagic;
|
||||
|
||||
#endif // BITCOIN_UTIL_VALIDATION_H
|
|
@ -12,6 +12,7 @@
|
|||
#include <checkqueue.h>
|
||||
#include <consensus/consensus.h>
|
||||
#include <consensus/merkle.h>
|
||||
#include <consensus/tx_check.h>
|
||||
#include <consensus/tx_verify.h>
|
||||
#include <consensus/validation.h>
|
||||
#include <cuckoocache.h>
|
||||
|
@ -21,6 +22,7 @@
|
|||
#include <policy/fees.h>
|
||||
#include <policy/policy.h>
|
||||
#include <policy/rbf.h>
|
||||
#include <policy/settings.h>
|
||||
#include <pow.h>
|
||||
#include <primitives/block.h>
|
||||
#include <primitives/transaction.h>
|
||||
|
@ -37,8 +39,10 @@
|
|||
#include <ui_interface.h>
|
||||
#include <undo.h>
|
||||
#include <util/moneystr.h>
|
||||
#include <util/rbf.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <util/system.h>
|
||||
#include <util/validation.h>
|
||||
#include <validationinterface.h>
|
||||
#include <warnings.h>
|
||||
|
||||
|
@ -236,7 +240,6 @@ std::atomic_bool fImporting(false);
|
|||
std::atomic_bool fReindex(false);
|
||||
bool fHavePruned = false;
|
||||
bool fPruneMode = false;
|
||||
bool fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG;
|
||||
bool fRequireStandard = true;
|
||||
bool fCheckBlockIndex = false;
|
||||
bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED;
|
||||
|
@ -258,8 +261,6 @@ std::atomic_bool g_is_mempool_loaded{false};
|
|||
/** Constant stuff for coinbase transactions we create: */
|
||||
CScript COINBASE_FLAGS;
|
||||
|
||||
const std::string strMessageMagic = "Bitcoin Signed Message:\n";
|
||||
|
||||
// Internal stuff
|
||||
namespace {
|
||||
CBlockIndex *&pindexBestInvalid = g_chainstate.pindexBestInvalid;
|
||||
|
@ -461,15 +462,6 @@ static void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age)
|
|||
pcoinsTip->Uncache(removed);
|
||||
}
|
||||
|
||||
/** Convert CValidationState to a human-readable message for logging */
|
||||
std::string FormatStateMessage(const CValidationState &state)
|
||||
{
|
||||
return strprintf("%s%s (code %i)",
|
||||
state.GetRejectReason(),
|
||||
state.GetDebugMessage().empty() ? "" : ", "+state.GetDebugMessage(),
|
||||
state.GetRejectCode());
|
||||
}
|
||||
|
||||
static bool IsCurrentForFeeEstimation() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
||||
{
|
||||
AssertLockHeld(cs_main);
|
||||
|
|
|
@ -114,8 +114,6 @@ static const int64_t DEFAULT_MAX_TIP_AGE = 24 * 60 * 60;
|
|||
/** Maximum age of our tip in seconds for us to be considered current for fee estimation */
|
||||
static const int64_t MAX_FEE_ESTIMATION_TIP_AGE = 3 * 60 * 60;
|
||||
|
||||
/** Default for -permitbaremultisig */
|
||||
static const bool DEFAULT_PERMIT_BAREMULTISIG = true;
|
||||
static const bool DEFAULT_CHECKPOINTS_ENABLED = true;
|
||||
static const bool DEFAULT_TXINDEX = false;
|
||||
static const unsigned int DEFAULT_BANSCORE_THRESHOLD = 100;
|
||||
|
@ -152,14 +150,12 @@ extern CTxMemPool mempool;
|
|||
extern std::atomic_bool g_is_mempool_loaded;
|
||||
typedef std::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap;
|
||||
extern BlockMap& mapBlockIndex GUARDED_BY(cs_main);
|
||||
extern const std::string strMessageMagic;
|
||||
extern Mutex g_best_block_mutex;
|
||||
extern std::condition_variable g_best_block_cv;
|
||||
extern uint256 g_best_block;
|
||||
extern std::atomic_bool fImporting;
|
||||
extern std::atomic_bool fReindex;
|
||||
extern int nScriptCheckThreads;
|
||||
extern bool fIsBareMultisigStd;
|
||||
extern bool fRequireStandard;
|
||||
extern bool fCheckBlockIndex;
|
||||
extern bool fCheckpointsEnabled;
|
||||
|
@ -301,9 +297,6 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
|||
bool* pfMissingInputs, std::list<CTransactionRef>* plTxnReplaced,
|
||||
bool bypass_limits, const CAmount nAbsurdFee, bool test_accept=false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
|
||||
/** Convert CValidationState to a human-readable message for logging */
|
||||
std::string FormatStateMessage(const CValidationState &state);
|
||||
|
||||
/** Get the BIP9 state for a given deployment at the current tip. */
|
||||
ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos);
|
||||
|
||||
|
|
|
@ -14,7 +14,9 @@
|
|||
#include <validation.h> //for mempool access
|
||||
#include <txmempool.h>
|
||||
#include <util/moneystr.h>
|
||||
#include <util/rbf.h>
|
||||
#include <util/system.h>
|
||||
#include <util/validation.h>
|
||||
#include <net.h>
|
||||
|
||||
//! Check whether transaction has descendant in wallet or mempool, or has been
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <net.h>
|
||||
#include <scheduler.h>
|
||||
#include <outputtype.h>
|
||||
#include <util/error.h>
|
||||
#include <util/system.h>
|
||||
#include <util/moneystr.h>
|
||||
#include <validation.h>
|
||||
|
@ -130,58 +131,6 @@ bool WalletInit::ParameterInteraction() const
|
|||
return true;
|
||||
}
|
||||
|
||||
bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files)
|
||||
{
|
||||
if (gArgs.IsArgSet("-walletdir")) {
|
||||
fs::path wallet_dir = gArgs.GetArg("-walletdir", "");
|
||||
boost::system::error_code error;
|
||||
// The canonical path cleans the path, preventing >1 Berkeley environment instances for the same directory
|
||||
fs::path canonical_wallet_dir = fs::canonical(wallet_dir, error);
|
||||
if (error || !fs::exists(wallet_dir)) {
|
||||
chain.initError(strprintf(_("Specified -walletdir \"%s\" does not exist"), wallet_dir.string()));
|
||||
return false;
|
||||
} else if (!fs::is_directory(wallet_dir)) {
|
||||
chain.initError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), wallet_dir.string()));
|
||||
return false;
|
||||
// The canonical path transforms relative paths into absolute ones, so we check the non-canonical version
|
||||
} else if (!wallet_dir.is_absolute()) {
|
||||
chain.initError(strprintf(_("Specified -walletdir \"%s\" is a relative path"), wallet_dir.string()));
|
||||
return false;
|
||||
}
|
||||
gArgs.ForceSetArg("-walletdir", canonical_wallet_dir.string());
|
||||
}
|
||||
|
||||
LogPrintf("Using wallet directory %s\n", GetWalletDir().string());
|
||||
|
||||
chain.initMessage(_("Verifying wallet(s)..."));
|
||||
|
||||
// Parameter interaction code should have thrown an error if -salvagewallet
|
||||
// was enabled with more than wallet file, so the wallet_files size check
|
||||
// here should have no effect.
|
||||
bool salvage_wallet = gArgs.GetBoolArg("-salvagewallet", false) && wallet_files.size() <= 1;
|
||||
|
||||
// Keep track of each wallet absolute path to detect duplicates.
|
||||
std::set<fs::path> wallet_paths;
|
||||
|
||||
for (const auto& wallet_file : wallet_files) {
|
||||
WalletLocation location(wallet_file);
|
||||
|
||||
if (!wallet_paths.insert(location.GetPath()).second) {
|
||||
chain.initError(strprintf(_("Error loading wallet %s. Duplicate -wallet filename specified."), wallet_file));
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string error_string;
|
||||
std::string warning_string;
|
||||
bool verify_success = CWallet::Verify(chain, location, salvage_wallet, error_string, warning_string);
|
||||
if (!error_string.empty()) chain.initError(error_string);
|
||||
if (!warning_string.empty()) chain.initWarning(warning_string);
|
||||
if (!verify_success) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WalletInit::Construct(InitInterfaces& interfaces) const
|
||||
{
|
||||
if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
|
||||
|
@ -191,52 +140,3 @@ void WalletInit::Construct(InitInterfaces& interfaces) const
|
|||
gArgs.SoftSetArg("-wallet", "");
|
||||
interfaces.chain_clients.emplace_back(interfaces::MakeWalletClient(*interfaces.chain, gArgs.GetArgs("-wallet")));
|
||||
}
|
||||
|
||||
bool LoadWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files)
|
||||
{
|
||||
for (const std::string& walletFile : wallet_files) {
|
||||
std::shared_ptr<CWallet> pwallet = CWallet::CreateWalletFromFile(chain, WalletLocation(walletFile));
|
||||
if (!pwallet) {
|
||||
return false;
|
||||
}
|
||||
AddWallet(pwallet);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void StartWallets(CScheduler& scheduler)
|
||||
{
|
||||
for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
|
||||
pwallet->postInitProcess();
|
||||
}
|
||||
|
||||
// Schedule periodic wallet flushes and tx rebroadcasts
|
||||
scheduler.scheduleEvery(MaybeCompactWalletDB, 500);
|
||||
scheduler.scheduleEvery(MaybeResendWalletTxs, 1000);
|
||||
}
|
||||
|
||||
void FlushWallets()
|
||||
{
|
||||
for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
|
||||
pwallet->Flush(false);
|
||||
}
|
||||
}
|
||||
|
||||
void StopWallets()
|
||||
{
|
||||
for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
|
||||
pwallet->Flush(true);
|
||||
}
|
||||
}
|
||||
|
||||
void UnloadWallets()
|
||||
{
|
||||
auto wallets = GetWallets();
|
||||
while (!wallets.empty()) {
|
||||
auto wallet = wallets.back();
|
||||
wallets.pop_back();
|
||||
RemoveWallet(wallet);
|
||||
UnloadWallet(std::move(wallet));
|
||||
}
|
||||
}
|
||||
|
|
112
src/wallet/load.cpp
Normal file
112
src/wallet/load.cpp
Normal file
|
@ -0,0 +1,112 @@
|
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-2018 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <wallet/load.h>
|
||||
|
||||
#include <interfaces/chain.h>
|
||||
#include <scheduler.h>
|
||||
#include <util/system.h>
|
||||
#include <wallet/wallet.h>
|
||||
|
||||
bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files)
|
||||
{
|
||||
if (gArgs.IsArgSet("-walletdir")) {
|
||||
fs::path wallet_dir = gArgs.GetArg("-walletdir", "");
|
||||
boost::system::error_code error;
|
||||
// The canonical path cleans the path, preventing >1 Berkeley environment instances for the same directory
|
||||
fs::path canonical_wallet_dir = fs::canonical(wallet_dir, error);
|
||||
if (error || !fs::exists(wallet_dir)) {
|
||||
chain.initError(strprintf(_("Specified -walletdir \"%s\" does not exist"), wallet_dir.string()));
|
||||
return false;
|
||||
} else if (!fs::is_directory(wallet_dir)) {
|
||||
chain.initError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), wallet_dir.string()));
|
||||
return false;
|
||||
// The canonical path transforms relative paths into absolute ones, so we check the non-canonical version
|
||||
} else if (!wallet_dir.is_absolute()) {
|
||||
chain.initError(strprintf(_("Specified -walletdir \"%s\" is a relative path"), wallet_dir.string()));
|
||||
return false;
|
||||
}
|
||||
gArgs.ForceSetArg("-walletdir", canonical_wallet_dir.string());
|
||||
}
|
||||
|
||||
LogPrintf("Using wallet directory %s\n", GetWalletDir().string());
|
||||
|
||||
chain.initMessage(_("Verifying wallet(s)..."));
|
||||
|
||||
// Parameter interaction code should have thrown an error if -salvagewallet
|
||||
// was enabled with more than wallet file, so the wallet_files size check
|
||||
// here should have no effect.
|
||||
bool salvage_wallet = gArgs.GetBoolArg("-salvagewallet", false) && wallet_files.size() <= 1;
|
||||
|
||||
// Keep track of each wallet absolute path to detect duplicates.
|
||||
std::set<fs::path> wallet_paths;
|
||||
|
||||
for (const auto& wallet_file : wallet_files) {
|
||||
WalletLocation location(wallet_file);
|
||||
|
||||
if (!wallet_paths.insert(location.GetPath()).second) {
|
||||
chain.initError(strprintf(_("Error loading wallet %s. Duplicate -wallet filename specified."), wallet_file));
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string error_string;
|
||||
std::string warning_string;
|
||||
bool verify_success = CWallet::Verify(chain, location, salvage_wallet, error_string, warning_string);
|
||||
if (!error_string.empty()) chain.initError(error_string);
|
||||
if (!warning_string.empty()) chain.initWarning(warning_string);
|
||||
if (!verify_success) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files)
|
||||
{
|
||||
for (const std::string& walletFile : wallet_files) {
|
||||
std::shared_ptr<CWallet> pwallet = CWallet::CreateWalletFromFile(chain, WalletLocation(walletFile));
|
||||
if (!pwallet) {
|
||||
return false;
|
||||
}
|
||||
AddWallet(pwallet);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void StartWallets(CScheduler& scheduler)
|
||||
{
|
||||
for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
|
||||
pwallet->postInitProcess();
|
||||
}
|
||||
|
||||
// Schedule periodic wallet flushes and tx rebroadcasts
|
||||
scheduler.scheduleEvery(MaybeCompactWalletDB, 500);
|
||||
scheduler.scheduleEvery(MaybeResendWalletTxs, 1000);
|
||||
}
|
||||
|
||||
void FlushWallets()
|
||||
{
|
||||
for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
|
||||
pwallet->Flush(false);
|
||||
}
|
||||
}
|
||||
|
||||
void StopWallets()
|
||||
{
|
||||
for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
|
||||
pwallet->Flush(true);
|
||||
}
|
||||
}
|
||||
|
||||
void UnloadWallets()
|
||||
{
|
||||
auto wallets = GetWallets();
|
||||
while (!wallets.empty()) {
|
||||
auto wallet = wallets.back();
|
||||
wallets.pop_back();
|
||||
RemoveWallet(wallet);
|
||||
UnloadWallet(std::move(wallet));
|
||||
}
|
||||
}
|
38
src/wallet/load.h
Normal file
38
src/wallet/load.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-2018 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_WALLET_LOAD_H
|
||||
#define BITCOIN_WALLET_LOAD_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class CScheduler;
|
||||
|
||||
namespace interfaces {
|
||||
class Chain;
|
||||
} // namespace interfaces
|
||||
|
||||
//! Responsible for reading and validating the -wallet arguments and verifying the wallet database.
|
||||
//! This function will perform salvage on the wallet if requested, as long as only one wallet is
|
||||
//! being loaded (WalletParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet).
|
||||
bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files);
|
||||
|
||||
//! Load wallet databases.
|
||||
bool LoadWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files);
|
||||
|
||||
//! Complete startup of wallets.
|
||||
void StartWallets(CScheduler& scheduler);
|
||||
|
||||
//! Flush all wallets in preparation for shutdown.
|
||||
void FlushWallets();
|
||||
|
||||
//! Stop all wallets. Wallets will be flushed first.
|
||||
void StopWallets();
|
||||
|
||||
//! Close all wallets.
|
||||
void UnloadWallets();
|
||||
|
||||
#endif // BITCOIN_WALLET_LOAD_H
|
|
@ -7,7 +7,6 @@
|
|||
#include <chain.h>
|
||||
#include <consensus/validation.h>
|
||||
#include <core_io.h>
|
||||
#include <httpserver.h>
|
||||
#include <init.h>
|
||||
#include <interfaces/chain.h>
|
||||
#include <validation.h>
|
||||
|
@ -19,7 +18,7 @@
|
|||
#include <policy/fees.h>
|
||||
#include <policy/policy.h>
|
||||
#include <policy/rbf.h>
|
||||
#include <rpc/rawtransaction.h>
|
||||
#include <rpc/rawtransaction_util.h>
|
||||
#include <rpc/server.h>
|
||||
#include <rpc/util.h>
|
||||
#include <script/descriptor.h>
|
||||
|
@ -27,8 +26,11 @@
|
|||
#include <shutdown.h>
|
||||
#include <timedata.h>
|
||||
#include <util/bip32.h>
|
||||
#include <util/fees.h>
|
||||
#include <util/system.h>
|
||||
#include <util/moneystr.h>
|
||||
#include <util/url.h>
|
||||
#include <util/validation.h>
|
||||
#include <wallet/coincontrol.h>
|
||||
#include <wallet/feebumper.h>
|
||||
#include <wallet/psbtwallet.h>
|
||||
|
|
|
@ -29,7 +29,11 @@
|
|||
#include <timedata.h>
|
||||
#include <txmempool.h>
|
||||
#include <util/bip32.h>
|
||||
#include <util/error.h>
|
||||
#include <util/fees.h>
|
||||
#include <util/moneystr.h>
|
||||
#include <util/rbf.h>
|
||||
#include <util/validation.h>
|
||||
#include <wallet/fees.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
|
|
@ -35,26 +35,6 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
//! Responsible for reading and validating the -wallet arguments and verifying the wallet database.
|
||||
//! This function will perform salvage on the wallet if requested, as long as only one wallet is
|
||||
//! being loaded (WalletParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet).
|
||||
bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files);
|
||||
|
||||
//! Load wallet databases.
|
||||
bool LoadWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files);
|
||||
|
||||
//! Complete startup of wallets.
|
||||
void StartWallets(CScheduler& scheduler);
|
||||
|
||||
//! Flush all wallets in preparation for shutdown.
|
||||
void FlushWallets();
|
||||
|
||||
//! Stop all wallets. Wallets will be flushed first.
|
||||
void StopWallets();
|
||||
|
||||
//! Close all wallets.
|
||||
void UnloadWallets();
|
||||
|
||||
//! Explicitly unload and delete the wallet.
|
||||
//! Blocks the current thread after signaling the unload intent so that all
|
||||
//! wallet clients release the wallet.
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
#include <wallet/walletdb.h>
|
||||
|
||||
#include <consensus/tx_verify.h>
|
||||
#include <consensus/tx_check.h>
|
||||
#include <consensus/validation.h>
|
||||
#include <fs.h>
|
||||
#include <key_io.h>
|
||||
|
|
|
@ -13,7 +13,7 @@ EXPECTED_CIRCULAR_DEPENDENCIES=(
|
|||
"checkpoints -> validation -> checkpoints"
|
||||
"index/txindex -> validation -> index/txindex"
|
||||
"policy/fees -> txmempool -> policy/fees"
|
||||
"policy/policy -> validation -> policy/policy"
|
||||
"policy/policy -> policy/settings -> policy/policy"
|
||||
"qt/addresstablemodel -> qt/walletmodel -> qt/addresstablemodel"
|
||||
"qt/bantablemodel -> qt/clientmodel -> qt/bantablemodel"
|
||||
"qt/bitcoingui -> qt/utilitydialog -> qt/bitcoingui"
|
||||
|
@ -30,7 +30,7 @@ EXPECTED_CIRCULAR_DEPENDENCIES=(
|
|||
"wallet/coincontrol -> wallet/wallet -> wallet/coincontrol"
|
||||
"wallet/fees -> wallet/wallet -> wallet/fees"
|
||||
"wallet/wallet -> wallet/walletdb -> wallet/wallet"
|
||||
"policy/fees -> policy/policy -> validation -> policy/fees"
|
||||
"policy/fees -> txmempool -> validation -> policy/fees"
|
||||
"policy/rbf -> txmempool -> validation -> policy/rbf"
|
||||
"qt/addressbookpage -> qt/bitcoingui -> qt/walletview -> qt/addressbookpage"
|
||||
"qt/guiutil -> qt/walletmodel -> qt/optionsmodel -> qt/guiutil"
|
||||
|
|
Loading…
Add table
Reference in a new issue