Permalink
Browse files

Merge #9347: [0.13.2] wallet/rpc backports

49a612f [qa] Don't set unknown rpcserialversion (MarcoFalke)
c365556 Complain when unknown rpcserialversion is specified (Pieter Wuille)
f5d606e Return txid even if ATMP fails for new transaction (Pieter Wuille)
35174a0 Make RelayWalletTransaction attempt to AcceptToMemoryPool. (Gregory Maxwell)
a0f7ece Update for OpenSSL 1.1 API (Gregory Maxwell)
43bcfca [Wallet] Bugfix: FRT: don't terminate when keypool is empty (Jonas Schnelli)
0cc07f8 [QA] add fundrawtransaction test on a locked wallet with empty keypool (Jonas Schnelli)
  • Loading branch information...
2 parents 03b6f62 + 49a612f commit b71139089584c0ab14d1c7fc51653a5dec333b28 @laanwj laanwj committed Dec 19, 2016
@@ -484,6 +484,23 @@ def run_test(self):
self.is_network_split=False
self.sync_all()
+ # drain the keypool
+ self.nodes[1].getnewaddress()
+ inputs = []
+ outputs = {self.nodes[0].getnewaddress():1.1}
+ rawTx = self.nodes[1].createrawtransaction(inputs, outputs)
+ # fund a transaction that requires a new key for the change output
+ # creating the key must be impossible because the wallet is locked
+ try:
+ fundedTx = self.nodes[1].fundrawtransaction(rawTx)
+ raise AssertionError("Wallet unlocked without passphrase")
+ except JSONRPCException as e:
+ assert('Keypool ran out' in e.error['message'])
+
+ #refill the keypool
+ self.nodes[1].walletpassphrase("test", 100)
+ self.nodes[1].walletlock()
+
try:
self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1.2)
raise AssertionError("Wallet unlocked without passphrase")
@@ -85,7 +85,7 @@ def setup_chain(self):
def setup_network(self):
self.nodes = []
self.nodes.append(start_node(0, self.options.tmpdir, ["-logtimemicros", "-debug", "-walletprematurewitness", "-rpcserialversion=0"]))
- self.nodes.append(start_node(1, self.options.tmpdir, ["-logtimemicros", "-debug", "-blockversion=4", "-promiscuousmempoolflags=517", "-prematurewitness", "-walletprematurewitness", "-rpcserialversion=2"]))
+ self.nodes.append(start_node(1, self.options.tmpdir, ["-logtimemicros", "-debug", "-blockversion=4", "-promiscuousmempoolflags=517", "-prematurewitness", "-walletprematurewitness", "-rpcserialversion=1"]))
self.nodes.append(start_node(2, self.options.tmpdir, ["-logtimemicros", "-debug", "-blockversion=536870915", "-promiscuousmempoolflags=517", "-prematurewitness", "-walletprematurewitness"]))
connect_nodes(self.nodes[1], 0)
connect_nodes(self.nodes[2], 1)
@@ -215,7 +215,6 @@ def run_test(self):
assert_equal(len(segwit_tx_list), 5)
print("Verify block and transaction serialization rpcs return differing serializations depending on rpc serialization flag")
- # Note: node1 has version 2, which is simply >0 and will catch future upgrades in tests
assert(self.nodes[2].getblock(block[0], False) != self.nodes[0].getblock(block[0], False))
assert(self.nodes[1].getblock(block[0], False) == self.nodes[2].getblock(block[0], False))
for i in range(len(segwit_tx_list)):
View
@@ -364,7 +364,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-port=<port>", strprintf(_("Listen for connections on <port> (default: %u or testnet: %u)"), Params(CBaseChainParams::MAIN).GetDefaultPort(), Params(CBaseChainParams::TESTNET).GetDefaultPort()));
strUsage += HelpMessageOpt("-proxy=<ip:port>", _("Connect through SOCKS5 proxy"));
strUsage += HelpMessageOpt("-proxyrandomize", strprintf(_("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)"), DEFAULT_PROXYRANDOMIZE));
- strUsage += HelpMessageOpt("-rpcserialversion", strprintf(_("Sets the serialization of raw transaction or block hex returned in non-verbose mode, non-segwit(0) or segwit(>0) (default: %d)"), DEFAULT_RPC_SERIALIZE_VERSION));
+ strUsage += HelpMessageOpt("-rpcserialversion", strprintf(_("Sets the serialization of raw transaction or block hex returned in non-verbose mode, non-segwit(0) or segwit(1) (default: %d)"), DEFAULT_RPC_SERIALIZE_VERSION));
strUsage += HelpMessageOpt("-seednode=<ip>", _("Connect to a node to retrieve peer addresses, and disconnect"));
strUsage += HelpMessageOpt("-timeout=<n>", strprintf(_("Specify connection timeout in milliseconds (minimum: 1, default: %d)"), DEFAULT_CONNECT_TIMEOUT));
strUsage += HelpMessageOpt("-torcontrol=<ip>:<port>", strprintf(_("Tor control port to use if onion listening enabled (default: %s)"), DEFAULT_TOR_CONTROL));
@@ -986,6 +986,9 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
if (GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) < 0)
return InitError("rpcserialversion must be non-negative.");
+ if (GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) > 1)
+ return InitError("unknown rpcserialversion requested.");
+
nMaxTipAge = GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE);
fEnableReplacement = GetBoolArg("-mempoolreplacement", DEFAULT_ENABLE_REPLACEMENT);
@@ -159,14 +159,24 @@ bool PaymentRequestPlus::getMerchant(X509_STORE* certStore, QString& merchant) c
std::string data_to_verify; // Everything but the signature
rcopy.SerializeToString(&data_to_verify);
- EVP_MD_CTX ctx;
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ EVP_MD_CTX *ctx = EVP_MD_CTX_new();
+ if (!ctx) throw SSLVerifyError("Error allocating OpenSSL context.");
+#else
+ EVP_MD_CTX _ctx;
+ EVP_MD_CTX *ctx;
+ ctx = &_ctx;
+#endif
EVP_PKEY *pubkey = X509_get_pubkey(signing_cert);
- EVP_MD_CTX_init(&ctx);
- if (!EVP_VerifyInit_ex(&ctx, digestAlgorithm, NULL) ||
- !EVP_VerifyUpdate(&ctx, data_to_verify.data(), data_to_verify.size()) ||
- !EVP_VerifyFinal(&ctx, (const unsigned char*)paymentRequest.signature().data(), (unsigned int)paymentRequest.signature().size(), pubkey)) {
+ EVP_MD_CTX_init(ctx);
+ if (!EVP_VerifyInit_ex(ctx, digestAlgorithm, NULL) ||
+ !EVP_VerifyUpdate(ctx, data_to_verify.data(), data_to_verify.size()) ||
+ !EVP_VerifyFinal(ctx, (const unsigned char*)paymentRequest.signature().data(), (unsigned int)paymentRequest.signature().size(), pubkey)) {
throw SSLVerifyError("Bad signature, invalid payment request.");
}
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ EVP_MD_CTX_free(ctx);
+#endif
// OpenSSL API for getting human printable strings from certs is baroque.
int textlen = X509_NAME_get_text_by_NID(certname, NID_commonName, NULL, 0);
@@ -42,15 +42,19 @@ bool OldEncrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned char>
int nCLen = nLen + AES_BLOCK_SIZE, nFLen = 0;
vchCiphertext = std::vector<unsigned char> (nCLen);
- EVP_CIPHER_CTX ctx;
+ EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
+
+ if (!ctx) return false;
bool fOk = true;
- EVP_CIPHER_CTX_init(&ctx);
- if (fOk) fOk = EVP_EncryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, chKey, chIV) != 0;
- if (fOk) fOk = EVP_EncryptUpdate(&ctx, &vchCiphertext[0], &nCLen, &vchPlaintext[0], nLen) != 0;
- if (fOk) fOk = EVP_EncryptFinal_ex(&ctx, (&vchCiphertext[0]) + nCLen, &nFLen) != 0;
- EVP_CIPHER_CTX_cleanup(&ctx);
+ EVP_CIPHER_CTX_init(ctx);
+ if (fOk) fOk = EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, chKey, chIV) != 0;
+ if (fOk) fOk = EVP_EncryptUpdate(ctx, &vchCiphertext[0], &nCLen, &vchPlaintext[0], nLen) != 0;
+ if (fOk) fOk = EVP_EncryptFinal_ex(ctx, (&vchCiphertext[0]) + nCLen, &nFLen) != 0;
+ EVP_CIPHER_CTX_cleanup(ctx);
+
+ EVP_CIPHER_CTX_free(ctx);
if (!fOk) return false;
@@ -66,15 +70,19 @@ bool OldDecrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingMaterial
vchPlaintext = CKeyingMaterial(nPLen);
- EVP_CIPHER_CTX ctx;
+ EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
+
+ if (!ctx) return false;
bool fOk = true;
- EVP_CIPHER_CTX_init(&ctx);
- if (fOk) fOk = EVP_DecryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, chKey, chIV) != 0;
- if (fOk) fOk = EVP_DecryptUpdate(&ctx, &vchPlaintext[0], &nPLen, &vchCiphertext[0], nLen) != 0;
- if (fOk) fOk = EVP_DecryptFinal_ex(&ctx, (&vchPlaintext[0]) + nPLen, &nFLen) != 0;
- EVP_CIPHER_CTX_cleanup(&ctx);
+ EVP_CIPHER_CTX_init(ctx);
+ if (fOk) fOk = EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, chKey, chIV) != 0;
+ if (fOk) fOk = EVP_DecryptUpdate(ctx, &vchPlaintext[0], &nPLen, &vchCiphertext[0], nLen) != 0;
+ if (fOk) fOk = EVP_DecryptFinal_ex(ctx, (&vchPlaintext[0]) + nPLen, &nFLen) != 0;
+ EVP_CIPHER_CTX_cleanup(ctx);
+
+ EVP_CIPHER_CTX_free(ctx);
if (!fOk) return false;
View
@@ -1442,16 +1442,19 @@ void CWallet::ReacceptWalletTransactions()
CWalletTx& wtx = *(item.second);
LOCK(mempool.cs);
- wtx.AcceptToMemoryPool(false, maxTxFee);
+ CValidationState state;
+ wtx.AcceptToMemoryPool(false, maxTxFee, state);
}
}
bool CWalletTx::RelayWalletTransaction()
{
assert(pwallet->GetBroadcastTransactions());
- if (!IsCoinBase())
+ if (!IsCoinBase() && !isAbandoned() && GetDepthInMainChain() == 0)
{
- if (GetDepthInMainChain() == 0 && !isAbandoned() && InMempool()) {
+ CValidationState state;
+ /* GetDepthInMainChain already catches known conflicts. */
+ if (InMempool() || AcceptToMemoryPool(false, maxTxFee, state)) {
LogPrintf("Relaying wtx %s\n", GetHash().ToString());
RelayTransaction((CTransaction)*this);
return true;
@@ -2290,7 +2293,11 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
CPubKey vchPubKey;
bool ret;
ret = reservekey.GetReservedKey(vchPubKey);
- assert(ret); // should never fail, as we just unlocked
+ if (!ret)
+ {
+ strFailReason = _("Keypool ran out, please call keypoolrefill first");
+ return false;
+ }
scriptChange = GetScriptForDestination(vchPubKey.GetID());
}
@@ -2477,14 +2484,14 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey)
if (fBroadcastTransactions)
{
+ CValidationState state;
// Broadcast
- if (!wtxNew.AcceptToMemoryPool(false, maxTxFee))
- {
- // This must not fail. The transaction has already been signed and recorded.
- LogPrintf("CommitTransaction(): Error: Transaction not valid\n");
- return false;
+ if (!wtxNew.AcceptToMemoryPool(false, maxTxFee, state)) {
+ LogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", state.GetRejectReason());
+ // TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure.
+ } else {
+ wtxNew.RelayWalletTransaction();
}
- wtxNew.RelayWalletTransaction();
}
}
return true;
@@ -3590,8 +3597,7 @@ int CMerkleTx::GetBlocksToMaturity() const
}
-bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, CAmount nAbsurdFee)
+bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, CAmount nAbsurdFee, CValidationState& state)
{
- CValidationState state;
return ::AcceptToMemoryPool(mempool, state, *this, fLimitFree, NULL, false, nAbsurdFee);
}
View
@@ -213,7 +213,7 @@ class CMerkleTx : public CTransaction
bool IsInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet) > 0; }
int GetBlocksToMaturity() const;
/** Pass this transaction to the mempool. Fails if absolute fee exceeds absurd fee. */
- bool AcceptToMemoryPool(bool fLimitFree, const CAmount nAbsurdFee);
+ bool AcceptToMemoryPool(bool fLimitFree, const CAmount nAbsurdFee, CValidationState& state);
bool hashUnset() const { return (hashBlock.IsNull() || hashBlock == ABANDON_HASH); }
bool isAbandoned() const { return (hashBlock == ABANDON_HASH); }
void setAbandoned() { hashBlock = ABANDON_HASH; }

0 comments on commit b711390

Please sign in to comment.