mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-22 15:04:44 +01: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>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@SOURCE_FILES@
|
@SOURCE_FILES@
|
||||||
|
<ClCompile Include="..\..\src\wallet\init.cpp">
|
||||||
|
<ObjectFileName>$(IntDir)wallet_init.obj</ObjectFileName>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="..\..\src\arith_uint256.cpp" />
|
<ClCompile Include="..\..\src\arith_uint256.cpp" />
|
||||||
<ClCompile Include="..\..\src\consensus\merkle.cpp" />
|
<ClCompile Include="..\..\src\consensus\merkle.cpp" />
|
||||||
|
<ClCompile Include="..\..\src\consensus\tx_check.cpp" />
|
||||||
<ClCompile Include="..\..\src\crypto\aes.cpp" />
|
<ClCompile Include="..\..\src\crypto\aes.cpp" />
|
||||||
<ClCompile Include="..\..\src\crypto\chacha20.cpp" />
|
<ClCompile Include="..\..\src\crypto\chacha20.cpp" />
|
||||||
<ClCompile Include="..\..\src\crypto\hmac_sha256.cpp" />
|
<ClCompile Include="..\..\src\crypto\hmac_sha256.cpp" />
|
||||||
|
|
|
@ -124,6 +124,7 @@ BITCOIN_CORE_H = \
|
||||||
compat/sanity.h \
|
compat/sanity.h \
|
||||||
compressor.h \
|
compressor.h \
|
||||||
consensus/consensus.h \
|
consensus/consensus.h \
|
||||||
|
consensus/tx_check.h \
|
||||||
consensus/tx_verify.h \
|
consensus/tx_verify.h \
|
||||||
core_io.h \
|
core_io.h \
|
||||||
core_memusage.h \
|
core_memusage.h \
|
||||||
|
@ -155,6 +156,7 @@ BITCOIN_CORE_H = \
|
||||||
netbase.h \
|
netbase.h \
|
||||||
netmessagemaker.h \
|
netmessagemaker.h \
|
||||||
node/coin.h \
|
node/coin.h \
|
||||||
|
node/psbt.h \
|
||||||
node/transaction.h \
|
node/transaction.h \
|
||||||
noui.h \
|
noui.h \
|
||||||
optional.h \
|
optional.h \
|
||||||
|
@ -163,6 +165,7 @@ BITCOIN_CORE_H = \
|
||||||
policy/fees.h \
|
policy/fees.h \
|
||||||
policy/policy.h \
|
policy/policy.h \
|
||||||
policy/rbf.h \
|
policy/rbf.h \
|
||||||
|
policy/settings.h \
|
||||||
pow.h \
|
pow.h \
|
||||||
protocol.h \
|
protocol.h \
|
||||||
psbt.h \
|
psbt.h \
|
||||||
|
@ -174,7 +177,7 @@ BITCOIN_CORE_H = \
|
||||||
rpc/mining.h \
|
rpc/mining.h \
|
||||||
rpc/protocol.h \
|
rpc/protocol.h \
|
||||||
rpc/server.h \
|
rpc/server.h \
|
||||||
rpc/rawtransaction.h \
|
rpc/rawtransaction_util.h \
|
||||||
rpc/register.h \
|
rpc/register.h \
|
||||||
rpc/util.h \
|
rpc/util.h \
|
||||||
scheduler.h \
|
scheduler.h \
|
||||||
|
@ -201,10 +204,15 @@ BITCOIN_CORE_H = \
|
||||||
undo.h \
|
undo.h \
|
||||||
util/bip32.h \
|
util/bip32.h \
|
||||||
util/bytevectorhash.h \
|
util/bytevectorhash.h \
|
||||||
|
util/error.h \
|
||||||
|
util/fees.h \
|
||||||
util/system.h \
|
util/system.h \
|
||||||
util/memory.h \
|
util/memory.h \
|
||||||
util/moneystr.h \
|
util/moneystr.h \
|
||||||
|
util/rbf.h \
|
||||||
util/time.h \
|
util/time.h \
|
||||||
|
util/url.h \
|
||||||
|
util/validation.h \
|
||||||
validation.h \
|
validation.h \
|
||||||
validationinterface.h \
|
validationinterface.h \
|
||||||
versionbits.h \
|
versionbits.h \
|
||||||
|
@ -215,6 +223,7 @@ BITCOIN_CORE_H = \
|
||||||
wallet/db.h \
|
wallet/db.h \
|
||||||
wallet/feebumper.h \
|
wallet/feebumper.h \
|
||||||
wallet/fees.h \
|
wallet/fees.h \
|
||||||
|
wallet/load.h \
|
||||||
wallet/psbtwallet.h \
|
wallet/psbtwallet.h \
|
||||||
wallet/rpcwallet.h \
|
wallet/rpcwallet.h \
|
||||||
wallet/wallet.h \
|
wallet/wallet.h \
|
||||||
|
@ -237,13 +246,15 @@ obj/build.h: FORCE
|
||||||
libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h
|
libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h
|
||||||
|
|
||||||
# server: shared between bitcoind and bitcoin-qt
|
# 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_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
|
||||||
libbitcoin_server_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
libbitcoin_server_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||||
libbitcoin_server_a_SOURCES = \
|
libbitcoin_server_a_SOURCES = \
|
||||||
addrdb.cpp \
|
addrdb.cpp \
|
||||||
addrman.cpp \
|
addrman.cpp \
|
||||||
banman.cpp \
|
banman.cpp \
|
||||||
bloom.cpp \
|
|
||||||
blockencodings.cpp \
|
blockencodings.cpp \
|
||||||
blockfilter.cpp \
|
blockfilter.cpp \
|
||||||
chain.cpp \
|
chain.cpp \
|
||||||
|
@ -255,21 +266,19 @@ libbitcoin_server_a_SOURCES = \
|
||||||
index/base.cpp \
|
index/base.cpp \
|
||||||
index/txindex.cpp \
|
index/txindex.cpp \
|
||||||
interfaces/chain.cpp \
|
interfaces/chain.cpp \
|
||||||
interfaces/handler.cpp \
|
|
||||||
interfaces/node.cpp \
|
interfaces/node.cpp \
|
||||||
init.cpp \
|
init.cpp \
|
||||||
dbwrapper.cpp \
|
dbwrapper.cpp \
|
||||||
merkleblock.cpp \
|
|
||||||
miner.cpp \
|
miner.cpp \
|
||||||
net.cpp \
|
net.cpp \
|
||||||
net_processing.cpp \
|
net_processing.cpp \
|
||||||
node/coin.cpp \
|
node/coin.cpp \
|
||||||
|
node/psbt.cpp \
|
||||||
node/transaction.cpp \
|
node/transaction.cpp \
|
||||||
noui.cpp \
|
noui.cpp \
|
||||||
outputtype.cpp \
|
|
||||||
policy/fees.cpp \
|
policy/fees.cpp \
|
||||||
policy/policy.cpp \
|
|
||||||
policy/rbf.cpp \
|
policy/rbf.cpp \
|
||||||
|
policy/settings.cpp \
|
||||||
pow.cpp \
|
pow.cpp \
|
||||||
rest.cpp \
|
rest.cpp \
|
||||||
rpc/blockchain.cpp \
|
rpc/blockchain.cpp \
|
||||||
|
@ -278,7 +287,6 @@ libbitcoin_server_a_SOURCES = \
|
||||||
rpc/net.cpp \
|
rpc/net.cpp \
|
||||||
rpc/rawtransaction.cpp \
|
rpc/rawtransaction.cpp \
|
||||||
rpc/server.cpp \
|
rpc/server.cpp \
|
||||||
rpc/util.cpp \
|
|
||||||
script/sigcache.cpp \
|
script/sigcache.cpp \
|
||||||
shutdown.cpp \
|
shutdown.cpp \
|
||||||
timedata.cpp \
|
timedata.cpp \
|
||||||
|
@ -291,6 +299,9 @@ libbitcoin_server_a_SOURCES = \
|
||||||
versionbits.cpp \
|
versionbits.cpp \
|
||||||
$(BITCOIN_CORE_H)
|
$(BITCOIN_CORE_H)
|
||||||
|
|
||||||
|
if ENABLE_WALLET
|
||||||
|
libbitcoin_server_a_SOURCES += wallet/init.cpp
|
||||||
|
endif
|
||||||
if !ENABLE_WALLET
|
if !ENABLE_WALLET
|
||||||
libbitcoin_server_a_SOURCES += dummywallet.cpp
|
libbitcoin_server_a_SOURCES += dummywallet.cpp
|
||||||
endif
|
endif
|
||||||
|
@ -317,7 +328,7 @@ libbitcoin_wallet_a_SOURCES = \
|
||||||
wallet/db.cpp \
|
wallet/db.cpp \
|
||||||
wallet/feebumper.cpp \
|
wallet/feebumper.cpp \
|
||||||
wallet/fees.cpp \
|
wallet/fees.cpp \
|
||||||
wallet/init.cpp \
|
wallet/load.cpp \
|
||||||
wallet/psbtwallet.cpp \
|
wallet/psbtwallet.cpp \
|
||||||
wallet/rpcdump.cpp \
|
wallet/rpcdump.cpp \
|
||||||
wallet/rpcwallet.cpp \
|
wallet/rpcwallet.cpp \
|
||||||
|
@ -391,6 +402,7 @@ libbitcoin_consensus_a_SOURCES = \
|
||||||
consensus/merkle.cpp \
|
consensus/merkle.cpp \
|
||||||
consensus/merkle.h \
|
consensus/merkle.h \
|
||||||
consensus/params.h \
|
consensus/params.h \
|
||||||
|
consensus/tx_check.cpp \
|
||||||
consensus/validation.h \
|
consensus/validation.h \
|
||||||
hash.cpp \
|
hash.cpp \
|
||||||
hash.h \
|
hash.h \
|
||||||
|
@ -423,6 +435,7 @@ libbitcoin_common_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||||
libbitcoin_common_a_SOURCES = \
|
libbitcoin_common_a_SOURCES = \
|
||||||
base58.cpp \
|
base58.cpp \
|
||||||
bech32.cpp \
|
bech32.cpp \
|
||||||
|
bloom.cpp \
|
||||||
chainparams.cpp \
|
chainparams.cpp \
|
||||||
coins.cpp \
|
coins.cpp \
|
||||||
compressor.cpp \
|
compressor.cpp \
|
||||||
|
@ -431,11 +444,16 @@ libbitcoin_common_a_SOURCES = \
|
||||||
key.cpp \
|
key.cpp \
|
||||||
key_io.cpp \
|
key_io.cpp \
|
||||||
keystore.cpp \
|
keystore.cpp \
|
||||||
|
merkleblock.cpp \
|
||||||
netaddress.cpp \
|
netaddress.cpp \
|
||||||
netbase.cpp \
|
netbase.cpp \
|
||||||
|
outputtype.cpp \
|
||||||
policy/feerate.cpp \
|
policy/feerate.cpp \
|
||||||
|
policy/policy.cpp \
|
||||||
protocol.cpp \
|
protocol.cpp \
|
||||||
psbt.cpp \
|
psbt.cpp \
|
||||||
|
rpc/rawtransaction_util.cpp \
|
||||||
|
rpc/util.cpp \
|
||||||
scheduler.cpp \
|
scheduler.cpp \
|
||||||
script/descriptor.cpp \
|
script/descriptor.cpp \
|
||||||
script/ismine.cpp \
|
script/ismine.cpp \
|
||||||
|
@ -458,6 +476,7 @@ libbitcoin_util_a_SOURCES = \
|
||||||
compat/glibcxx_sanity.cpp \
|
compat/glibcxx_sanity.cpp \
|
||||||
compat/strnlen.cpp \
|
compat/strnlen.cpp \
|
||||||
fs.cpp \
|
fs.cpp \
|
||||||
|
interfaces/handler.cpp \
|
||||||
logging.cpp \
|
logging.cpp \
|
||||||
random.cpp \
|
random.cpp \
|
||||||
rpc/protocol.cpp \
|
rpc/protocol.cpp \
|
||||||
|
@ -466,10 +485,15 @@ libbitcoin_util_a_SOURCES = \
|
||||||
threadinterrupt.cpp \
|
threadinterrupt.cpp \
|
||||||
util/bip32.cpp \
|
util/bip32.cpp \
|
||||||
util/bytevectorhash.cpp \
|
util/bytevectorhash.cpp \
|
||||||
|
util/error.cpp \
|
||||||
|
util/fees.cpp \
|
||||||
util/system.cpp \
|
util/system.cpp \
|
||||||
util/moneystr.cpp \
|
util/moneystr.cpp \
|
||||||
|
util/rbf.cpp \
|
||||||
util/strencodings.cpp \
|
util/strencodings.cpp \
|
||||||
util/time.cpp \
|
util/time.cpp \
|
||||||
|
util/url.cpp \
|
||||||
|
util/validation.cpp \
|
||||||
$(BITCOIN_CORE_H)
|
$(BITCOIN_CORE_H)
|
||||||
|
|
||||||
if GLIBC_BACK_COMPAT
|
if GLIBC_BACK_COMPAT
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <script/script.h>
|
#include <script/script.h>
|
||||||
#include <script/sign.h>
|
#include <script/sign.h>
|
||||||
#include <univalue.h>
|
#include <univalue.h>
|
||||||
|
#include <util/rbf.h>
|
||||||
#include <util/system.h>
|
#include <util/system.h>
|
||||||
#include <util/moneystr.h>
|
#include <util/moneystr.h>
|
||||||
#include <util/strencodings.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;
|
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)
|
bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee)
|
||||||
{
|
{
|
||||||
// are the actual inputs available?
|
// are the actual inputs available?
|
||||||
|
|
|
@ -17,9 +17,6 @@ class CValidationState;
|
||||||
|
|
||||||
/** Transaction validation functions */
|
/** Transaction validation functions */
|
||||||
|
|
||||||
/** Context-independent validity checks */
|
|
||||||
bool CheckTransaction(const CTransaction& tx, CValidationState& state, bool fCheckDuplicateInputs=true);
|
|
||||||
|
|
||||||
namespace Consensus {
|
namespace Consensus {
|
||||||
/**
|
/**
|
||||||
* Check whether all inputs of this transaction are valid (no double spends and amounts)
|
* 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);
|
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;
|
struct event* ev;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string urlDecode(const std::string &urlEncoded);
|
|
||||||
|
|
||||||
#endif // BITCOIN_HTTPSERVER_H
|
#endif // BITCOIN_HTTPSERVER_H
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#include <policy/feerate.h>
|
#include <policy/feerate.h>
|
||||||
#include <policy/fees.h>
|
#include <policy/fees.h>
|
||||||
#include <policy/policy.h>
|
#include <policy/policy.h>
|
||||||
|
#include <policy/settings.h>
|
||||||
#include <rpc/server.h>
|
#include <rpc/server.h>
|
||||||
#include <rpc/register.h>
|
#include <rpc/register.h>
|
||||||
#include <rpc/blockchain.h>
|
#include <rpc/blockchain.h>
|
||||||
|
@ -46,6 +47,7 @@
|
||||||
#include <ui_interface.h>
|
#include <ui_interface.h>
|
||||||
#include <util/system.h>
|
#include <util/system.h>
|
||||||
#include <util/moneystr.h>
|
#include <util/moneystr.h>
|
||||||
|
#include <util/validation.h>
|
||||||
#include <validationinterface.h>
|
#include <validationinterface.h>
|
||||||
#include <warnings.h>
|
#include <warnings.h>
|
||||||
#include <walletinitinterface.h>
|
#include <walletinitinterface.h>
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <policy/fees.h>
|
#include <policy/fees.h>
|
||||||
#include <policy/policy.h>
|
#include <policy/policy.h>
|
||||||
#include <policy/rbf.h>
|
#include <policy/rbf.h>
|
||||||
|
#include <policy/settings.h>
|
||||||
#include <primitives/block.h>
|
#include <primitives/block.h>
|
||||||
#include <primitives/transaction.h>
|
#include <primitives/transaction.h>
|
||||||
#include <protocol.h>
|
#include <protocol.h>
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <policy/feerate.h>
|
#include <policy/feerate.h>
|
||||||
#include <policy/fees.h>
|
#include <policy/fees.h>
|
||||||
#include <policy/policy.h>
|
#include <policy/policy.h>
|
||||||
|
#include <policy/settings.h>
|
||||||
#include <primitives/block.h>
|
#include <primitives/block.h>
|
||||||
#include <rpc/server.h>
|
#include <rpc/server.h>
|
||||||
#include <scheduler.h>
|
#include <scheduler.h>
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include <wallet/feebumper.h>
|
#include <wallet/feebumper.h>
|
||||||
#include <wallet/fees.h>
|
#include <wallet/fees.h>
|
||||||
#include <wallet/rpcwallet.h>
|
#include <wallet/rpcwallet.h>
|
||||||
|
#include <wallet/load.h>
|
||||||
#include <wallet/wallet.h>
|
#include <wallet/wallet.h>
|
||||||
#include <wallet/walletutil.h>
|
#include <wallet/walletutil.h>
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include <timedata.h>
|
#include <timedata.h>
|
||||||
#include <util/moneystr.h>
|
#include <util/moneystr.h>
|
||||||
#include <util/system.h>
|
#include <util/system.h>
|
||||||
|
#include <util/validation.h>
|
||||||
#include <validationinterface.h>
|
#include <validationinterface.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include <util/system.h>
|
#include <util/system.h>
|
||||||
#include <util/moneystr.h>
|
#include <util/moneystr.h>
|
||||||
#include <util/strencodings.h>
|
#include <util/strencodings.h>
|
||||||
|
#include <util/validation.h>
|
||||||
|
|
||||||
#include <memory>
|
#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 <consensus/validation.h>
|
||||||
#include <net.h>
|
#include <net.h>
|
||||||
#include <txmempool.h>
|
#include <txmempool.h>
|
||||||
|
#include <util/validation.h>
|
||||||
#include <validation.h>
|
#include <validation.h>
|
||||||
#include <validationinterface.h>
|
#include <validationinterface.h>
|
||||||
#include <node/transaction.h>
|
#include <node/transaction.h>
|
||||||
|
|
||||||
#include <future>
|
#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)
|
TransactionError BroadcastTransaction(const CTransactionRef tx, uint256& hashTx, std::string& err_string, const CAmount& highfee)
|
||||||
{
|
{
|
||||||
std::promise<void> promise;
|
std::promise<void> promise;
|
||||||
|
|
|
@ -8,20 +8,7 @@
|
||||||
#include <attributes.h>
|
#include <attributes.h>
|
||||||
#include <primitives/transaction.h>
|
#include <primitives/transaction.h>
|
||||||
#include <uint256.h>
|
#include <uint256.h>
|
||||||
|
#include <util/error.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);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Broadcast a transaction
|
* Broadcast a transaction
|
||||||
|
|
|
@ -27,40 +27,6 @@ std::string StringForFeeEstimateHorizon(FeeEstimateHorizon horizon) {
|
||||||
return horizon_string->second;
|
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
|
* 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
|
* included in a block. We will lump transactions into a bucket according to their
|
||||||
|
|
|
@ -46,8 +46,6 @@ enum class FeeReason {
|
||||||
MAXTXFEE,
|
MAXTXFEE,
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string StringForFeeReason(FeeReason reason);
|
|
||||||
|
|
||||||
/* Used to determine type of fee estimation requested */
|
/* Used to determine type of fee estimation requested */
|
||||||
enum class FeeEstimateMode {
|
enum class FeeEstimateMode {
|
||||||
UNSET, //!< Use default settings based on other criteria
|
UNSET, //!< Use default settings based on other criteria
|
||||||
|
@ -55,8 +53,6 @@ enum class FeeEstimateMode {
|
||||||
CONSERVATIVE, //!< Force estimateSmartFee to use conservative estimates
|
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 */
|
/* Used to return detailed information about a feerate bucket */
|
||||||
struct EstimatorBucket
|
struct EstimatorBucket
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
#include <policy/policy.h>
|
#include <policy/policy.h>
|
||||||
|
|
||||||
#include <consensus/validation.h>
|
#include <consensus/validation.h>
|
||||||
#include <validation.h>
|
|
||||||
#include <coins.h>
|
#include <coins.h>
|
||||||
|
#include <policy/settings.h>
|
||||||
#include <tinyformat.h>
|
#include <tinyformat.h>
|
||||||
#include <util/system.h>
|
#include <util/system.h>
|
||||||
#include <util/strencodings.h>
|
#include <util/strencodings.h>
|
||||||
|
@ -239,10 +239,6 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
|
||||||
return true;
|
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)
|
int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost)
|
||||||
{
|
{
|
||||||
return (std::max(nWeight, nSigOpCost * nBytesPerSigOp) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR;
|
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;
|
static const unsigned int DEFAULT_INCREMENTAL_RELAY_FEE = 1000;
|
||||||
/** Default for -bytespersigop */
|
/** Default for -bytespersigop */
|
||||||
static const unsigned int DEFAULT_BYTES_PER_SIGOP = 20;
|
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 */
|
/** The maximum number of witness stack items in a standard P2WSH script */
|
||||||
static const unsigned int MAX_STANDARD_P2WSH_STACK_ITEMS = 100;
|
static const unsigned int MAX_STANDARD_P2WSH_STACK_ITEMS = 100;
|
||||||
/** The maximum size of each witness stack item in a standard P2WSH script */
|
/** 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);
|
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). */
|
/** Compute the virtual transaction size (weight reinterpreted as bytes). */
|
||||||
int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost);
|
int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost);
|
||||||
int64_t GetVirtualTransactionSize(const CTransaction& tx, int64_t nSigOpCost = 0);
|
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.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
#include <policy/rbf.h>
|
#include <policy/rbf.h>
|
||||||
|
#include <util/rbf.h>
|
||||||
bool SignalsOptInRBF(const CTransaction &tx)
|
|
||||||
{
|
|
||||||
for (const CTxIn &txin : tx.vin) {
|
|
||||||
if (txin.nSequence <= MAX_BIP125_RBF_SEQUENCE) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool)
|
RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool)
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,18 +7,12 @@
|
||||||
|
|
||||||
#include <txmempool.h>
|
#include <txmempool.h>
|
||||||
|
|
||||||
static const uint32_t MAX_BIP125_RBF_SEQUENCE = 0xfffffffd;
|
|
||||||
|
|
||||||
enum class RBFTransactionState {
|
enum class RBFTransactionState {
|
||||||
UNKNOWN,
|
UNKNOWN,
|
||||||
REPLACEABLE_BIP125,
|
REPLACEABLE_BIP125,
|
||||||
FINAL
|
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
|
// Determine whether an in-mempool transaction is signaling opt-in to RBF
|
||||||
// according to BIP 125
|
// according to BIP 125
|
||||||
// This involves checking sequence numbers of the transaction, as well
|
// 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 DecodeBase64PSBT(PartiallySignedTransaction& psbt, const std::string& base64_tx, std::string& error)
|
||||||
{
|
{
|
||||||
bool invalid;
|
bool invalid;
|
||||||
|
|
33
src/psbt.h
33
src/psbt.h
|
@ -557,31 +557,6 @@ enum class PSBTRole {
|
||||||
EXTRACTOR
|
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);
|
std::string PSBTRoleName(PSBTRole role);
|
||||||
|
|
||||||
/** Checks whether a PSBTInput is already signed. */
|
/** 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);
|
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
|
//! Decode a base64ed PSBT into a PartiallySignedTransaction
|
||||||
NODISCARD bool DecodeBase64PSBT(PartiallySignedTransaction& decoded_psbt, const std::string& base64_psbt, std::string& error);
|
NODISCARD bool DecodeBase64PSBT(PartiallySignedTransaction& decoded_psbt, const std::string& base64_psbt, std::string& error);
|
||||||
//! Decode a raw (binary blob) PSBT into a PartiallySignedTransaction
|
//! Decode a raw (binary blob) PSBT into a PartiallySignedTransaction
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
#include <qt/walletmodel.h>
|
#include <qt/walletmodel.h>
|
||||||
|
|
||||||
#include <key_io.h>
|
#include <key_io.h>
|
||||||
#include <validation.h> // For strMessageMagic
|
#include <util/validation.h> // For strMessageMagic
|
||||||
#include <wallet/wallet.h>
|
#include <wallet/wallet.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include <txmempool.h>
|
#include <txmempool.h>
|
||||||
#include <util/strencodings.h>
|
#include <util/strencodings.h>
|
||||||
#include <util/system.h>
|
#include <util/system.h>
|
||||||
|
#include <util/validation.h>
|
||||||
#include <validation.h>
|
#include <validation.h>
|
||||||
#include <validationinterface.h>
|
#include <validationinterface.h>
|
||||||
#include <versionbitsinfo.h>
|
#include <versionbitsinfo.h>
|
||||||
|
|
|
@ -21,8 +21,10 @@
|
||||||
#include <rpc/util.h>
|
#include <rpc/util.h>
|
||||||
#include <shutdown.h>
|
#include <shutdown.h>
|
||||||
#include <txmempool.h>
|
#include <txmempool.h>
|
||||||
|
#include <util/fees.h>
|
||||||
#include <util/strencodings.h>
|
#include <util/strencodings.h>
|
||||||
#include <util/system.h>
|
#include <util/system.h>
|
||||||
|
#include <util/validation.h>
|
||||||
#include <validation.h>
|
#include <validation.h>
|
||||||
#include <validationinterface.h>
|
#include <validationinterface.h>
|
||||||
#include <versionbitsinfo.h>
|
#include <versionbitsinfo.h>
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <timedata.h>
|
#include <timedata.h>
|
||||||
#include <util/system.h>
|
#include <util/system.h>
|
||||||
#include <util/strencodings.h>
|
#include <util/strencodings.h>
|
||||||
|
#include <util/validation.h>
|
||||||
#include <warnings.h>
|
#include <warnings.h>
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <net_processing.h>
|
#include <net_processing.h>
|
||||||
#include <netbase.h>
|
#include <netbase.h>
|
||||||
#include <policy/policy.h>
|
#include <policy/policy.h>
|
||||||
|
#include <policy/settings.h>
|
||||||
#include <rpc/protocol.h>
|
#include <rpc/protocol.h>
|
||||||
#include <rpc/util.h>
|
#include <rpc/util.h>
|
||||||
#include <sync.h>
|
#include <sync.h>
|
||||||
|
|
|
@ -15,12 +15,13 @@
|
||||||
#include <key_io.h>
|
#include <key_io.h>
|
||||||
#include <keystore.h>
|
#include <keystore.h>
|
||||||
#include <merkleblock.h>
|
#include <merkleblock.h>
|
||||||
|
#include <node/psbt.h>
|
||||||
#include <node/transaction.h>
|
#include <node/transaction.h>
|
||||||
#include <policy/policy.h>
|
#include <policy/policy.h>
|
||||||
#include <policy/rbf.h>
|
#include <policy/rbf.h>
|
||||||
#include <primitives/transaction.h>
|
#include <primitives/transaction.h>
|
||||||
#include <psbt.h>
|
#include <psbt.h>
|
||||||
#include <rpc/rawtransaction.h>
|
#include <rpc/rawtransaction_util.h>
|
||||||
#include <rpc/server.h>
|
#include <rpc/server.h>
|
||||||
#include <rpc/util.h>
|
#include <rpc/util.h>
|
||||||
#include <script/script.h>
|
#include <script/script.h>
|
||||||
|
@ -359,119 +360,6 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request)
|
||||||
return res;
|
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)
|
static UniValue createrawtransaction(const JSONRPCRequest& request)
|
||||||
{
|
{
|
||||||
if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) {
|
if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) {
|
||||||
|
@ -717,23 +605,6 @@ static UniValue decodescript(const JSONRPCRequest& request)
|
||||||
return r;
|
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)
|
static UniValue combinerawtransaction(const JSONRPCRequest& request)
|
||||||
{
|
{
|
||||||
if (request.fHelp || request.params.size() != 1)
|
if (request.fHelp || request.params.size() != 1)
|
||||||
|
@ -818,152 +689,6 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request)
|
||||||
return EncodeHexTx(CTransaction(mergedTx));
|
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)
|
static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
|
||||||
{
|
{
|
||||||
if (request.fHelp || request.params.size() < 2 || request.params.size() > 4)
|
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
|
// Distributed under the MIT software license, see the accompanying
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
#ifndef BITCOIN_RPC_RAWTRANSACTION_H
|
#ifndef BITCOIN_RPC_RAWTRANSACTION_UTIL_H
|
||||||
#define BITCOIN_RPC_RAWTRANSACTION_H
|
#define BITCOIN_RPC_RAWTRANSACTION_UTIL_H
|
||||||
|
|
||||||
class CBasicKeyStore;
|
class CBasicKeyStore;
|
||||||
struct CMutableTransaction;
|
|
||||||
class UniValue;
|
class UniValue;
|
||||||
|
struct CMutableTransaction;
|
||||||
|
|
||||||
namespace interfaces {
|
namespace interfaces {
|
||||||
class Chain;
|
class Chain;
|
||||||
|
@ -19,4 +19,4 @@ UniValue SignTransaction(interfaces::Chain& chain, CMutableTransaction& mtx, con
|
||||||
/** Create a transaction from univalue parameters */
|
/** Create a transaction from univalue parameters */
|
||||||
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf);
|
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);
|
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 CRPCTable::help(const std::string& strCommand, const JSONRPCRequest& helpreq) const
|
||||||
{
|
{
|
||||||
std::string strRet;
|
std::string strRet;
|
||||||
|
@ -593,17 +500,6 @@ std::vector<std::string> CRPCTable::listCommands() const
|
||||||
return commandList;
|
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)
|
void RPCSetTimerInterfaceIfUnset(RPCTimerInterface *iface)
|
||||||
{
|
{
|
||||||
if (!timerInterface)
|
if (!timerInterface)
|
||||||
|
|
|
@ -27,15 +27,6 @@ namespace RPCServer
|
||||||
void OnStopped(std::function<void ()> slot);
|
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
|
class JSONRPCRequest
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -65,26 +56,6 @@ void SetRPCWarmupFinished();
|
||||||
/* returns the current warmup state. */
|
/* returns the current warmup state. */
|
||||||
bool RPCIsInWarmup(std::string *outStatus);
|
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.
|
/** Opaque base class for timers returned by NewTimerFunc.
|
||||||
* This provides no methods at the moment, but makes sure that delete
|
* This provides no methods at the moment, but makes sure that delete
|
||||||
* cleans up the whole state.
|
* cleans up the whole state.
|
||||||
|
@ -204,19 +175,6 @@ bool IsDeprecatedRPCEnabled(const std::string& method);
|
||||||
|
|
||||||
extern CRPCTable tableRPC;
|
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 StartRPC();
|
||||||
void InterruptRPC();
|
void InterruptRPC();
|
||||||
void StopRPC();
|
void StopRPC();
|
||||||
|
|
104
src/rpc/util.cpp
104
src/rpc/util.cpp
|
@ -10,6 +10,110 @@
|
||||||
|
|
||||||
InitInterfaces* g_rpc_interfaces = nullptr;
|
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
|
// Converts a hex string to a public key if possible
|
||||||
CPubKey HexToPubKey(const std::string& hex_in)
|
CPubKey HexToPubKey(const std::string& hex_in)
|
||||||
{
|
{
|
||||||
|
|
|
@ -26,6 +26,48 @@ struct InitInterfaces;
|
||||||
//! state to RPC method implementations.
|
//! state to RPC method implementations.
|
||||||
extern InitInterfaces* g_rpc_interfaces;
|
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 HexToPubKey(const std::string& hex_in);
|
||||||
CPubKey AddrToPubKey(CKeyStore* const keystore, const std::string& addr_in);
|
CPubKey AddrToPubKey(CKeyStore* const keystore, const std::string& addr_in);
|
||||||
CScript CreateMultisigRedeemscript(const int required, const std::vector<CPubKey>& pubkeys);
|
CScript CreateMultisigRedeemscript(const int required, const std::vector<CPubKey>& pubkeys);
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include <policy/policy.h>
|
#include <policy/policy.h>
|
||||||
#include <script/script.h>
|
#include <script/script.h>
|
||||||
#include <script/script_error.h>
|
#include <script/script_error.h>
|
||||||
|
#include <policy/settings.h>
|
||||||
#include <script/sign.h>
|
#include <script/sign.h>
|
||||||
#include <script/ismine.h>
|
#include <script/ismine.h>
|
||||||
#include <test/test_bitcoin.h>
|
#include <test/test_bitcoin.h>
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#include <util/system.h>
|
#include <util/system.h>
|
||||||
#include <util/strencodings.h>
|
#include <util/strencodings.h>
|
||||||
#include <test/test_bitcoin.h>
|
#include <test/test_bitcoin.h>
|
||||||
#include <rpc/server.h>
|
#include <rpc/util.h>
|
||||||
|
|
||||||
#if defined(HAVE_CONSENSUS_LIB)
|
#if defined(HAVE_CONSENSUS_LIB)
|
||||||
#include <script/bitcoinconsensus.h>
|
#include <script/bitcoinconsensus.h>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// Distributed under the MIT software license, see the accompanying
|
// Distributed under the MIT software license, see the accompanying
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// 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 <consensus/validation.h>
|
||||||
#include <test/data/sighash.json.h>
|
#include <test/data/sighash.json.h>
|
||||||
#include <hash.h>
|
#include <hash.h>
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include <script/sigcache.h>
|
#include <script/sigcache.h>
|
||||||
#include <streams.h>
|
#include <streams.h>
|
||||||
#include <ui_interface.h>
|
#include <ui_interface.h>
|
||||||
|
#include <util/validation.h>
|
||||||
#include <validation.h>
|
#include <validation.h>
|
||||||
|
|
||||||
const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
|
const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
|
||||||
|
|
|
@ -8,13 +8,14 @@
|
||||||
|
|
||||||
#include <clientversion.h>
|
#include <clientversion.h>
|
||||||
#include <checkqueue.h>
|
#include <checkqueue.h>
|
||||||
#include <consensus/tx_verify.h>
|
#include <consensus/tx_check.h>
|
||||||
#include <consensus/validation.h>
|
#include <consensus/validation.h>
|
||||||
#include <core_io.h>
|
#include <core_io.h>
|
||||||
#include <key.h>
|
#include <key.h>
|
||||||
#include <keystore.h>
|
#include <keystore.h>
|
||||||
#include <validation.h>
|
#include <validation.h>
|
||||||
#include <policy/policy.h>
|
#include <policy/policy.h>
|
||||||
|
#include <policy/settings.h>
|
||||||
#include <script/script.h>
|
#include <script/script.h>
|
||||||
#include <script/sign.h>
|
#include <script/sign.h>
|
||||||
#include <script/script_error.h>
|
#include <script/script_error.h>
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <validation.h>
|
#include <validation.h>
|
||||||
#include <policy/policy.h>
|
#include <policy/policy.h>
|
||||||
#include <policy/fees.h>
|
#include <policy/fees.h>
|
||||||
|
#include <policy/settings.h>
|
||||||
#include <reverse_iterator.h>
|
#include <reverse_iterator.h>
|
||||||
#include <streams.h>
|
#include <streams.h>
|
||||||
#include <timedata.h>
|
#include <timedata.h>
|
||||||
|
|
|
@ -69,13 +69,3 @@ void InitWarning(const std::string& str)
|
||||||
{
|
{
|
||||||
uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_WARNING);
|
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 **/
|
/** Show error message **/
|
||||||
bool InitError(const std::string& str);
|
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;
|
extern CClientUIInterface uiInterface;
|
||||||
|
|
||||||
#endif // BITCOIN_UI_INTERFACE_H
|
#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 <checkqueue.h>
|
||||||
#include <consensus/consensus.h>
|
#include <consensus/consensus.h>
|
||||||
#include <consensus/merkle.h>
|
#include <consensus/merkle.h>
|
||||||
|
#include <consensus/tx_check.h>
|
||||||
#include <consensus/tx_verify.h>
|
#include <consensus/tx_verify.h>
|
||||||
#include <consensus/validation.h>
|
#include <consensus/validation.h>
|
||||||
#include <cuckoocache.h>
|
#include <cuckoocache.h>
|
||||||
|
@ -21,6 +22,7 @@
|
||||||
#include <policy/fees.h>
|
#include <policy/fees.h>
|
||||||
#include <policy/policy.h>
|
#include <policy/policy.h>
|
||||||
#include <policy/rbf.h>
|
#include <policy/rbf.h>
|
||||||
|
#include <policy/settings.h>
|
||||||
#include <pow.h>
|
#include <pow.h>
|
||||||
#include <primitives/block.h>
|
#include <primitives/block.h>
|
||||||
#include <primitives/transaction.h>
|
#include <primitives/transaction.h>
|
||||||
|
@ -37,8 +39,10 @@
|
||||||
#include <ui_interface.h>
|
#include <ui_interface.h>
|
||||||
#include <undo.h>
|
#include <undo.h>
|
||||||
#include <util/moneystr.h>
|
#include <util/moneystr.h>
|
||||||
|
#include <util/rbf.h>
|
||||||
#include <util/strencodings.h>
|
#include <util/strencodings.h>
|
||||||
#include <util/system.h>
|
#include <util/system.h>
|
||||||
|
#include <util/validation.h>
|
||||||
#include <validationinterface.h>
|
#include <validationinterface.h>
|
||||||
#include <warnings.h>
|
#include <warnings.h>
|
||||||
|
|
||||||
|
@ -236,7 +240,6 @@ std::atomic_bool fImporting(false);
|
||||||
std::atomic_bool fReindex(false);
|
std::atomic_bool fReindex(false);
|
||||||
bool fHavePruned = false;
|
bool fHavePruned = false;
|
||||||
bool fPruneMode = false;
|
bool fPruneMode = false;
|
||||||
bool fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG;
|
|
||||||
bool fRequireStandard = true;
|
bool fRequireStandard = true;
|
||||||
bool fCheckBlockIndex = false;
|
bool fCheckBlockIndex = false;
|
||||||
bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED;
|
bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED;
|
||||||
|
@ -258,8 +261,6 @@ std::atomic_bool g_is_mempool_loaded{false};
|
||||||
/** Constant stuff for coinbase transactions we create: */
|
/** Constant stuff for coinbase transactions we create: */
|
||||||
CScript COINBASE_FLAGS;
|
CScript COINBASE_FLAGS;
|
||||||
|
|
||||||
const std::string strMessageMagic = "Bitcoin Signed Message:\n";
|
|
||||||
|
|
||||||
// Internal stuff
|
// Internal stuff
|
||||||
namespace {
|
namespace {
|
||||||
CBlockIndex *&pindexBestInvalid = g_chainstate.pindexBestInvalid;
|
CBlockIndex *&pindexBestInvalid = g_chainstate.pindexBestInvalid;
|
||||||
|
@ -461,15 +462,6 @@ static void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age)
|
||||||
pcoinsTip->Uncache(removed);
|
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)
|
static bool IsCurrentForFeeEstimation() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
||||||
{
|
{
|
||||||
AssertLockHeld(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 */
|
/** 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;
|
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_CHECKPOINTS_ENABLED = true;
|
||||||
static const bool DEFAULT_TXINDEX = false;
|
static const bool DEFAULT_TXINDEX = false;
|
||||||
static const unsigned int DEFAULT_BANSCORE_THRESHOLD = 100;
|
static const unsigned int DEFAULT_BANSCORE_THRESHOLD = 100;
|
||||||
|
@ -152,14 +150,12 @@ extern CTxMemPool mempool;
|
||||||
extern std::atomic_bool g_is_mempool_loaded;
|
extern std::atomic_bool g_is_mempool_loaded;
|
||||||
typedef std::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap;
|
typedef std::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap;
|
||||||
extern BlockMap& mapBlockIndex GUARDED_BY(cs_main);
|
extern BlockMap& mapBlockIndex GUARDED_BY(cs_main);
|
||||||
extern const std::string strMessageMagic;
|
|
||||||
extern Mutex g_best_block_mutex;
|
extern Mutex g_best_block_mutex;
|
||||||
extern std::condition_variable g_best_block_cv;
|
extern std::condition_variable g_best_block_cv;
|
||||||
extern uint256 g_best_block;
|
extern uint256 g_best_block;
|
||||||
extern std::atomic_bool fImporting;
|
extern std::atomic_bool fImporting;
|
||||||
extern std::atomic_bool fReindex;
|
extern std::atomic_bool fReindex;
|
||||||
extern int nScriptCheckThreads;
|
extern int nScriptCheckThreads;
|
||||||
extern bool fIsBareMultisigStd;
|
|
||||||
extern bool fRequireStandard;
|
extern bool fRequireStandard;
|
||||||
extern bool fCheckBlockIndex;
|
extern bool fCheckBlockIndex;
|
||||||
extern bool fCheckpointsEnabled;
|
extern bool fCheckpointsEnabled;
|
||||||
|
@ -301,9 +297,6 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
||||||
bool* pfMissingInputs, std::list<CTransactionRef>* plTxnReplaced,
|
bool* pfMissingInputs, std::list<CTransactionRef>* plTxnReplaced,
|
||||||
bool bypass_limits, const CAmount nAbsurdFee, bool test_accept=false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
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. */
|
/** Get the BIP9 state for a given deployment at the current tip. */
|
||||||
ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos);
|
ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos);
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,9 @@
|
||||||
#include <validation.h> //for mempool access
|
#include <validation.h> //for mempool access
|
||||||
#include <txmempool.h>
|
#include <txmempool.h>
|
||||||
#include <util/moneystr.h>
|
#include <util/moneystr.h>
|
||||||
|
#include <util/rbf.h>
|
||||||
#include <util/system.h>
|
#include <util/system.h>
|
||||||
|
#include <util/validation.h>
|
||||||
#include <net.h>
|
#include <net.h>
|
||||||
|
|
||||||
//! Check whether transaction has descendant in wallet or mempool, or has been
|
//! Check whether transaction has descendant in wallet or mempool, or has been
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <net.h>
|
#include <net.h>
|
||||||
#include <scheduler.h>
|
#include <scheduler.h>
|
||||||
#include <outputtype.h>
|
#include <outputtype.h>
|
||||||
|
#include <util/error.h>
|
||||||
#include <util/system.h>
|
#include <util/system.h>
|
||||||
#include <util/moneystr.h>
|
#include <util/moneystr.h>
|
||||||
#include <validation.h>
|
#include <validation.h>
|
||||||
|
@ -130,58 +131,6 @@ bool WalletInit::ParameterInteraction() const
|
||||||
return true;
|
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
|
void WalletInit::Construct(InitInterfaces& interfaces) const
|
||||||
{
|
{
|
||||||
if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
|
if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
|
||||||
|
@ -191,52 +140,3 @@ void WalletInit::Construct(InitInterfaces& interfaces) const
|
||||||
gArgs.SoftSetArg("-wallet", "");
|
gArgs.SoftSetArg("-wallet", "");
|
||||||
interfaces.chain_clients.emplace_back(interfaces::MakeWalletClient(*interfaces.chain, gArgs.GetArgs("-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 <chain.h>
|
||||||
#include <consensus/validation.h>
|
#include <consensus/validation.h>
|
||||||
#include <core_io.h>
|
#include <core_io.h>
|
||||||
#include <httpserver.h>
|
|
||||||
#include <init.h>
|
#include <init.h>
|
||||||
#include <interfaces/chain.h>
|
#include <interfaces/chain.h>
|
||||||
#include <validation.h>
|
#include <validation.h>
|
||||||
|
@ -19,7 +18,7 @@
|
||||||
#include <policy/fees.h>
|
#include <policy/fees.h>
|
||||||
#include <policy/policy.h>
|
#include <policy/policy.h>
|
||||||
#include <policy/rbf.h>
|
#include <policy/rbf.h>
|
||||||
#include <rpc/rawtransaction.h>
|
#include <rpc/rawtransaction_util.h>
|
||||||
#include <rpc/server.h>
|
#include <rpc/server.h>
|
||||||
#include <rpc/util.h>
|
#include <rpc/util.h>
|
||||||
#include <script/descriptor.h>
|
#include <script/descriptor.h>
|
||||||
|
@ -27,8 +26,11 @@
|
||||||
#include <shutdown.h>
|
#include <shutdown.h>
|
||||||
#include <timedata.h>
|
#include <timedata.h>
|
||||||
#include <util/bip32.h>
|
#include <util/bip32.h>
|
||||||
|
#include <util/fees.h>
|
||||||
#include <util/system.h>
|
#include <util/system.h>
|
||||||
#include <util/moneystr.h>
|
#include <util/moneystr.h>
|
||||||
|
#include <util/url.h>
|
||||||
|
#include <util/validation.h>
|
||||||
#include <wallet/coincontrol.h>
|
#include <wallet/coincontrol.h>
|
||||||
#include <wallet/feebumper.h>
|
#include <wallet/feebumper.h>
|
||||||
#include <wallet/psbtwallet.h>
|
#include <wallet/psbtwallet.h>
|
||||||
|
|
|
@ -29,7 +29,11 @@
|
||||||
#include <timedata.h>
|
#include <timedata.h>
|
||||||
#include <txmempool.h>
|
#include <txmempool.h>
|
||||||
#include <util/bip32.h>
|
#include <util/bip32.h>
|
||||||
|
#include <util/error.h>
|
||||||
|
#include <util/fees.h>
|
||||||
#include <util/moneystr.h>
|
#include <util/moneystr.h>
|
||||||
|
#include <util/rbf.h>
|
||||||
|
#include <util/validation.h>
|
||||||
#include <wallet/fees.h>
|
#include <wallet/fees.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
|
@ -35,26 +35,6 @@
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#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.
|
//! Explicitly unload and delete the wallet.
|
||||||
//! Blocks the current thread after signaling the unload intent so that all
|
//! Blocks the current thread after signaling the unload intent so that all
|
||||||
//! wallet clients release the wallet.
|
//! wallet clients release the wallet.
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
#include <wallet/walletdb.h>
|
#include <wallet/walletdb.h>
|
||||||
|
|
||||||
#include <consensus/tx_verify.h>
|
#include <consensus/tx_check.h>
|
||||||
#include <consensus/validation.h>
|
#include <consensus/validation.h>
|
||||||
#include <fs.h>
|
#include <fs.h>
|
||||||
#include <key_io.h>
|
#include <key_io.h>
|
||||||
|
|
|
@ -13,7 +13,7 @@ EXPECTED_CIRCULAR_DEPENDENCIES=(
|
||||||
"checkpoints -> validation -> checkpoints"
|
"checkpoints -> validation -> checkpoints"
|
||||||
"index/txindex -> validation -> index/txindex"
|
"index/txindex -> validation -> index/txindex"
|
||||||
"policy/fees -> txmempool -> policy/fees"
|
"policy/fees -> txmempool -> policy/fees"
|
||||||
"policy/policy -> validation -> policy/policy"
|
"policy/policy -> policy/settings -> policy/policy"
|
||||||
"qt/addresstablemodel -> qt/walletmodel -> qt/addresstablemodel"
|
"qt/addresstablemodel -> qt/walletmodel -> qt/addresstablemodel"
|
||||||
"qt/bantablemodel -> qt/clientmodel -> qt/bantablemodel"
|
"qt/bantablemodel -> qt/clientmodel -> qt/bantablemodel"
|
||||||
"qt/bitcoingui -> qt/utilitydialog -> qt/bitcoingui"
|
"qt/bitcoingui -> qt/utilitydialog -> qt/bitcoingui"
|
||||||
|
@ -30,7 +30,7 @@ EXPECTED_CIRCULAR_DEPENDENCIES=(
|
||||||
"wallet/coincontrol -> wallet/wallet -> wallet/coincontrol"
|
"wallet/coincontrol -> wallet/wallet -> wallet/coincontrol"
|
||||||
"wallet/fees -> wallet/wallet -> wallet/fees"
|
"wallet/fees -> wallet/wallet -> wallet/fees"
|
||||||
"wallet/wallet -> wallet/walletdb -> wallet/wallet"
|
"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"
|
"policy/rbf -> txmempool -> validation -> policy/rbf"
|
||||||
"qt/addressbookpage -> qt/bitcoingui -> qt/walletview -> qt/addressbookpage"
|
"qt/addressbookpage -> qt/bitcoingui -> qt/walletview -> qt/addressbookpage"
|
||||||
"qt/guiutil -> qt/walletmodel -> qt/optionsmodel -> qt/guiutil"
|
"qt/guiutil -> qt/walletmodel -> qt/optionsmodel -> qt/guiutil"
|
||||||
|
|
Loading…
Add table
Reference in a new issue