aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--contrib/guix/libexec/build.sh42
-rw-r--r--src/rpc/rawtransaction.cpp5
-rw-r--r--src/rpc/rawtransaction_util.cpp8
-rw-r--r--src/rpc/rawtransaction_util.h14
-rw-r--r--src/validation.cpp45
-rw-r--r--src/wallet/rpcwallet.cpp5
-rwxr-xr-xtest/functional/mempool_package_onemore.py10
7 files changed, 100 insertions, 29 deletions
diff --git a/contrib/guix/libexec/build.sh b/contrib/guix/libexec/build.sh
index 56b972a5cb..ee207a957c 100644
--- a/contrib/guix/libexec/build.sh
+++ b/contrib/guix/libexec/build.sh
@@ -30,23 +30,38 @@ fi
# Given a package name and an output name, return the path of that output in our
# current guix environment
store_path() {
- grep --extended-regexp "/[^-]{32}-${1}-cross-${HOST}-[^-]+${2:+-${2}}" "${GUIX_ENVIRONMENT}/manifest" \
+ grep --extended-regexp "/[^-]{32}-${1}-[^-]+${2:+-${2}}" "${GUIX_ENVIRONMENT}/manifest" \
| head --lines=1 \
| sed --expression='s|^[[:space:]]*"||' \
--expression='s|"[[:space:]]*$||'
}
# Determine output paths to use in CROSS_* environment variables
-CROSS_GLIBC="$(store_path glibc)"
-CROSS_GLIBC_STATIC="$(store_path glibc static)"
-CROSS_KERNEL="$(store_path linux-libre-headers)"
-CROSS_GCC="$(store_path gcc)"
+CROSS_GLIBC="$(store_path glibc-cross-${HOST})"
+CROSS_GLIBC_STATIC="$(store_path glibc-cross-${HOST} static)"
+CROSS_KERNEL="$(store_path linux-libre-headers-cross-${HOST})"
+CROSS_GCC="$(store_path gcc-cross-${HOST})"
+CROSS_GCC_LIBS=( "${CROSS_GCC}/lib/gcc/${HOST}"/* ) # This expands to an array of directories...
+CROSS_GCC_LIB="${CROSS_GCC_LIBS[0]}" # ...we just want the first one (there should only be one)
# Set environment variables to point Guix's cross-toolchain to the right
# includes/libs for $HOST
-export CROSS_C_INCLUDE_PATH="${CROSS_GCC}/include:${CROSS_GLIBC}/include:${CROSS_KERNEL}/include"
-export CROSS_CPLUS_INCLUDE_PATH="${CROSS_GCC}/include/c++:${CROSS_GLIBC}/include:${CROSS_KERNEL}/include"
-export CROSS_LIBRARY_PATH="${CROSS_GLIBC}/lib:${CROSS_GLIBC_STATIC}/lib:${CROSS_GCC}/lib:${CROSS_GCC}/${HOST}/lib:${CROSS_KERNEL}/lib"
+#
+# NOTE: CROSS_C_INCLUDE_PATH is missing ${CROSS_GCC_LIB}/include-fixed, because
+# the limits.h in it is missing a '#include_next <limits.h>'
+#
+export CROSS_C_INCLUDE_PATH="${CROSS_GCC_LIB}/include:${CROSS_GLIBC}/include:${CROSS_KERNEL}/include"
+export CROSS_CPLUS_INCLUDE_PATH="${CROSS_GCC}/include/c++:${CROSS_GCC}/include/c++/${HOST}:${CROSS_GCC}/include/c++/backward:${CROSS_C_INCLUDE_PATH}"
+export CROSS_LIBRARY_PATH="${CROSS_GCC}/lib:${CROSS_GCC}/${HOST}/lib:${CROSS_GCC_LIB}:${CROSS_GLIBC}/lib:${CROSS_GLIBC_STATIC}/lib"
+
+# Sanity check CROSS_*_PATH directories
+IFS=':' read -ra PATHS <<< "${CROSS_C_INCLUDE_PATH}:${CROSS_CPLUS_INCLUDE_PATH}:${CROSS_LIBRARY_PATH}"
+for p in "${PATHS[@]}"; do
+ if [ ! -d "$p" ]; then
+ echo "'$p' doesn't exist or isn't a directory... Aborting..."
+ exit 1
+ fi
+done
# Disable Guix ld auto-rpath behavior
export GUIX_LD_WRAPPER_DISABLE_RPATH=yes
@@ -121,17 +136,10 @@ DISTNAME="$(basename "$SOURCEDIST" '.tar.gz')"
# Binary Tarball Building #
###########################
-# Create a spec file to normalize ssp linking behaviour
-spec_file="$(mktemp)"
-cat << EOF > "$spec_file"
-*link_ssp:
-%{fstack-protector|fstack-protector-all|fstack-protector-strong|fstack-protector-explicit:}
-EOF
-
# Similar flags to Gitian
CONFIGFLAGS="--enable-glibc-back-compat --enable-reduce-exports --disable-bench --disable-gui-tests"
-HOST_CFLAGS="-O2 -g -specs=${spec_file} -ffile-prefix-map=${PWD}=."
-HOST_CXXFLAGS="-O2 -g -specs=${spec_file} -ffile-prefix-map=${PWD}=."
+HOST_CFLAGS="-O2 -g -ffile-prefix-map=${PWD}=."
+HOST_CXXFLAGS="-O2 -g -ffile-prefix-map=${PWD}=."
HOST_LDFLAGS="-Wl,--as-needed -Wl,--dynamic-linker=$glibc_dynamic_linker -static-libstdc++"
# Make $HOST-specific native binaries from depends available in $PATH
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index ffbad45714..fb8ea8c227 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -758,7 +758,10 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
}
FindCoins(coins);
- return SignTransaction(mtx, request.params[2], &keystore, coins, true, request.params[3]);
+ // Parse the prevtxs array
+ ParsePrevouts(request.params[2], &keystore, coins);
+
+ return SignTransaction(mtx, &keystore, coins, request.params[3]);
}
static UniValue sendrawtransaction(const JSONRPCRequest& request)
diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp
index 55425cca35..697c6d45c4 100644
--- a/src/rpc/rawtransaction_util.cpp
+++ b/src/rpc/rawtransaction_util.cpp
@@ -147,9 +147,8 @@ static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::
vErrorsRet.push_back(entry);
}
-UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival, FillableSigningProvider* keystore, std::map<COutPoint, Coin>& coins, bool is_temp_keystore, const UniValue& hashType)
+void ParsePrevouts(const UniValue& prevTxsUnival, FillableSigningProvider* keystore, std::map<COutPoint, Coin>& 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) {
@@ -197,7 +196,7 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival
}
// 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())) {
+ if (keystore && (scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsPayToWitnessScriptHash())) {
RPCTypeCheckObj(prevOut,
{
{"redeemScript", UniValueType(UniValue::VSTR)},
@@ -226,7 +225,10 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival
}
}
}
+}
+UniValue SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, std::map<COutPoint, Coin>& coins, const UniValue& hashType)
+{
int nHashType = ParseSighashString(hashType);
bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
diff --git a/src/rpc/rawtransaction_util.h b/src/rpc/rawtransaction_util.h
index c85593e71e..b35e6da4ca 100644
--- a/src/rpc/rawtransaction_util.h
+++ b/src/rpc/rawtransaction_util.h
@@ -12,19 +12,27 @@ class UniValue;
struct CMutableTransaction;
class Coin;
class COutPoint;
+class SigningProvider;
/**
* Sign a transaction with the given keystore and previous transactions
*
* @param mtx The transaction to-be-signed
- * @param prevTxs Array of previous txns outputs that tx depends on but may not yet be in the block chain
* @param keystore Temporary keystore containing signing keys
* @param coins Map of unspent outputs - coins in mempool and current chain UTXO set, may be extended by previous txns outputs after call
- * @param tempKeystore Whether to use temporary keystore
* @param hashType The signature hash type
* @returns JSON object with details of signed transaction
*/
-UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxs, FillableSigningProvider* keystore, std::map<COutPoint, Coin>& coins, bool tempKeystore, const UniValue& hashType);
+UniValue SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, std::map<COutPoint, Coin>& coins, const UniValue& hashType);
+
+/**
+ * Parse a prevtxs UniValue array and get the map of coins from it
+ *
+ * @param prevTxs Array of previous txns outputs that tx depends on but may not yet be in the block chain
+ * @param keystore A pointer to the temprorary keystore if there is one
+ * @param coins Map of unspent outputs - coins in mempool and current chain UTXO set, may be extended by previous txns outputs after call
+ */
+void ParsePrevouts(const UniValue& prevTxsUnival, FillableSigningProvider* keystore, std::map<COutPoint, Coin>& coins);
/** Create a transaction from univalue parameters */
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, bool rbf);
diff --git a/src/validation.cpp b/src/validation.cpp
index cbf8c90392..d470fd5b6e 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -615,17 +615,55 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
REJECT_HIGHFEE, "absurdly-high-fee",
strprintf("%d > %d", nFees, nAbsurdFee));
+ const CTxMemPool::setEntries setIterConflicting = pool.GetIterSet(setConflicts);
// Calculate in-mempool ancestors, up to a limit.
CTxMemPool::setEntries setAncestors;
size_t nLimitAncestors = gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
size_t nLimitAncestorSize = gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000;
size_t nLimitDescendants = gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT);
size_t nLimitDescendantSize = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000;
+
+ if (setConflicts.size() == 1) {
+ // In general, when we receive an RBF transaction with mempool conflicts, we want to know whether we
+ // would meet the chain limits after the conflicts have been removed. However, there isn't a practical
+ // way to do this short of calculating the ancestor and descendant sets with an overlay cache of
+ // changed mempool entries. Due to both implementation and runtime complexity concerns, this isn't
+ // very realistic, thus we only ensure a limited set of transactions are RBF'able despite mempool
+ // conflicts here. Importantly, we need to ensure that some transactions which were accepted using
+ // the below carve-out are able to be RBF'ed, without impacting the security the carve-out provides
+ // for off-chain contract systems (see link in the comment below).
+ //
+ // Specifically, the subset of RBF transactions which we allow despite chain limits are those which
+ // conflict directly with exactly one other transaction (but may evict children of said transaction),
+ // and which are not adding any new mempool dependencies. Note that the "no new mempool dependencies"
+ // check is accomplished later, so we don't bother doing anything about it here, but if BIP 125 is
+ // amended, we may need to move that check to here instead of removing it wholesale.
+ //
+ // Such transactions are clearly not merging any existing packages, so we are only concerned with
+ // ensuring that (a) no package is growing past the package size (not count) limits and (b) we are
+ // not allowing something to effectively use the (below) carve-out spot when it shouldn't be allowed
+ // to.
+ //
+ // To check these we first check if we meet the RBF criteria, above, and increment the descendant
+ // limits by the direct conflict and its descendants (as these are recalculated in
+ // CalculateMempoolAncestors by assuming the new transaction being added is a new descendant, with no
+ // removals, of each parent's existing dependant set). The ancestor count limits are unmodified (as
+ // the ancestor limits should be the same for both our new transaction and any conflicts).
+ // We don't bother incrementing nLimitDescendants by the full removal count as that limit never comes
+ // into force here (as we're only adding a single transaction).
+ assert(setIterConflicting.size() == 1);
+ CTxMemPool::txiter conflict = *setIterConflicting.begin();
+
+ nLimitDescendants += 1;
+ nLimitDescendantSize += conflict->GetSizeWithDescendants();
+ }
+
std::string errString;
if (!pool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) {
setAncestors.clear();
// If CalculateMemPoolAncestors fails second time, we want the original error string.
std::string dummy_err_string;
+ // Contracting/payment channels CPFP carve-out:
// If the new transaction is relatively small (up to 40k weight)
// and has at most one ancestor (ie ancestor limit of 2, including
// the new transaction), allow it if its parent has exactly the
@@ -674,7 +712,6 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
CFeeRate newFeeRate(nModifiedFees, nSize);
std::set<uint256> setConflictsParents;
const int maxDescendantsToVisit = 100;
- const CTxMemPool::setEntries setIterConflicting = pool.GetIterSet(setConflicts);
for (const auto& mi : setIterConflicting) {
// Don't allow the replacement to reduce the feerate of the
// mempool.
@@ -734,6 +771,11 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
// feerate junk to be mined first. Ideally we'd keep track of
// the ancestor feerates and make the decision based on that,
// but for now requiring all new inputs to be confirmed works.
+ //
+ // Note that if you relax this to make RBF a little more useful,
+ // this may break the CalculateMempoolAncestors RBF relaxation,
+ // above. See the comment above the first CalculateMempoolAncestors
+ // call for more info.
if (!setConflictsParents.count(tx.vin[j].prevout.hash))
{
// Rather than check the UTXO set - potentially expensive -
@@ -3398,7 +3440,6 @@ bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, CValidatio
}
}
if (NotifyHeaderTip()) {
- LOCK(cs_main);
if (::ChainstateActive().IsInitialBlockDownload() && ppindex && *ppindex) {
LogPrintf("Synchronizing blockheaders, height: %d (~%.2f%%)\n", (*ppindex)->nHeight, 100.0/((*ppindex)->nHeight+(GetAdjustedTime() - (*ppindex)->GetBlockTime()) / Params().GetConsensus().nPowTargetSpacing) * (*ppindex)->nHeight);
}
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 22a5f7e249..b88aabd0fa 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -3280,7 +3280,10 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
}
pwallet->chain().findCoins(coins);
- return SignTransaction(mtx, request.params[1], pwallet, coins, false, request.params[2]);
+ // Parse the prevtxs array
+ ParsePrevouts(request.params[1], nullptr, coins);
+
+ return SignTransaction(mtx, pwallet, coins, request.params[2]);
}
static UniValue bumpfee(const JSONRPCRequest& request)
diff --git a/test/functional/mempool_package_onemore.py b/test/functional/mempool_package_onemore.py
index 30f851fb8e..0739d7e29b 100755
--- a/test/functional/mempool_package_onemore.py
+++ b/test/functional/mempool_package_onemore.py
@@ -33,7 +33,7 @@ class MempoolPackagesTest(BitcoinTestFramework):
outputs = {}
for i in range(num_outputs):
outputs[node.getnewaddress()] = send_value
- rawtx = node.createrawtransaction(inputs, outputs)
+ rawtx = node.createrawtransaction(inputs, outputs, 0, True)
signedtx = node.signrawtransactionwithwallet(rawtx)
txid = node.sendrawtransaction(signedtx['hex'])
fulltx = node.getrawtransaction(txid, 1)
@@ -75,10 +75,16 @@ class MempoolPackagesTest(BitcoinTestFramework):
# ...especially if its > 40k weight
assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", self.chain_transaction, self.nodes[0], [chain[0][0]], [1], chain[0][1], fee, 350)
# But not if it chains directly off the first transaction
- self.chain_transaction(self.nodes[0], [chain[0][0]], [1], chain[0][1], fee, 1)
+ (replacable_txid, replacable_orig_value) = self.chain_transaction(self.nodes[0], [chain[0][0]], [1], chain[0][1], fee, 1)
# and the second chain should work just fine
self.chain_transaction(self.nodes[0], [second_chain], [0], second_chain_value, fee, 1)
+ # Make sure we can RBF the chain which used our carve-out rule
+ second_tx_outputs = {self.nodes[0].getrawtransaction(replacable_txid, True)["vout"][0]['scriptPubKey']['addresses'][0]: replacable_orig_value - (Decimal(1) / Decimal(100))}
+ second_tx = self.nodes[0].createrawtransaction([{'txid': chain[0][0], 'vout': 1}], second_tx_outputs)
+ signed_second_tx = self.nodes[0].signrawtransactionwithwallet(second_tx)
+ self.nodes[0].sendrawtransaction(signed_second_tx['hex'])
+
# Finally, check that we added two transactions
assert_equal(len(self.nodes[0].getrawmempool(True)), MAX_ANCESTORS + 3)