Merge branch 'master' into catchup

This commit is contained in:
Steven Roose 2019-05-14 15:11:48 +01:00
commit dc900eb7d7
No known key found for this signature in database
GPG key ID: 2F2A88D7F8D68E87
10 changed files with 137 additions and 58 deletions

View file

@ -3,9 +3,9 @@ AC_PREREQ([2.60])
define(_CLIENT_VERSION_MAJOR, 0)
define(_CLIENT_VERSION_MINOR, 17)
define(_CLIENT_VERSION_REVISION, 0)
define(_CLIENT_VERSION_BUILD, 0)
define(_CLIENT_VERSION_BUILD, 1)
define(_CLIENT_VERSION_IS_RELEASE, true)
define(_COPYRIGHT_YEAR, 2018)
define(_COPYRIGHT_YEAR, 2019)
define(_COPYRIGHT_HOLDERS,[The %s developers])
define(_COPYRIGHT_HOLDERS_SUBSTITUTION,[[Elements Project]])
AC_INIT([Elements Core],[_CLIENT_VERSION_MAJOR._CLIENT_VERSION_MINOR._CLIENT_VERSION_REVISION],[https://github.com/ElementsProject/elements/issues],[elements],[https://elementsproject.org/])

View file

@ -185,6 +185,9 @@ script: |
cd $BUILD_DIR/windeploy
mkdir unsigned
cp $OUTDIR/elements-*setup-unsigned.exe unsigned/
cd unsigned
rename 's/elements-/liquid-/s' *-setup-unsigned.exe
cd ..
find . | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-win-unsigned.tar.gz
mv ${OUTDIR}/${DISTNAME}-x86_64-*-debug.zip ${OUTDIR}/${DISTNAME}-win64-debug.zip
mv ${OUTDIR}/${DISTNAME}-i686-*-debug.zip ${OUTDIR}/${DISTNAME}-win32-debug.zip

View file

@ -1,2 +1,2 @@
11D43A27826A421212108BF66BE2CED14A9917BC
85758DBCF05A48A4DE9EAA127FC91380BB4CE800
DE10E82629A8CAD55B700B972F2A88D7F8D68E87

View file

@ -1879,11 +1879,6 @@ bool AppInitMain(InitInterfaces& interfaces)
SetRPCWarmupFinished();
// ELEMENTS:
CScheduler::Function f2 = boost::bind(&MainchainRPCCheck, false);
unsigned int check_rpc_every = gArgs.GetArg("-recheckpeginblockinterval", 120);
if (check_rpc_every) {
scheduler.scheduleEvery(f2, check_rpc_every);
}
uiInterface.InitMessage(_("Awaiting mainchain RPC warmup"));
if (!MainchainRPCCheck(true)) { //Initial check, fail immediately
return InitError(_("ERROR: elementsd is set to verify pegins but cannot get valid response from the mainchain daemon. Please check debug.log for more information.")
@ -1891,6 +1886,12 @@ bool AppInitMain(InitInterfaces& interfaces)
+ strprintf(_("If you haven't setup a %s please get the latest stable version from %s or if you do not need to validate pegins set in your elements configuration %s"), "bitcoind", "https://bitcoincore.org/en/download/", "validatepegin=0"));
}
CScheduler::Function f2 = boost::bind(&MainchainRPCCheck, false);
unsigned int check_rpc_every = gArgs.GetArg("-recheckpeginblockinterval", 120);
if (check_rpc_every) {
scheduler.scheduleEvery(f2, check_rpc_every);
}
uiInterface.InitMessage(_("Done loading"));
for (const auto& client : interfaces.chain_clients) {

View file

@ -1034,7 +1034,7 @@ UniValue combineblocksigs(const JSONRPCRequest& request)
" \"complete\": true|false (numeric) If block is complete \n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("combineblocksigs", "<hex> [\"signature1\", \"signature2\", ...]")
+ HelpExampleCli("combineblocksigs", "<hex> '[{\"pubkey\":\"hex\",\"sig\":\"hex\"}, ...]'")
);
if (!g_signed_blocks) {

View file

@ -10,7 +10,6 @@
#include <consensus/validation.h>
#include <core_io.h>
#include <index/txindex.h>
#include <keystore.h>
#include <init.h>
#include <key_io.h>
#include <keystore.h>
@ -35,7 +34,6 @@
#include <confidential_validation.h>
#include <blind.h>
#include <issuance.h>
#include <rpc/util.h>
#include <future>
#include <stdint.h>
@ -1844,13 +1842,13 @@ UniValue converttopsbt(const JSONRPCRequest& request)
// Remove all scriptSigs and scriptWitnesses from inputs
for (CTxIn& input : tx.vin) {
if ((!input.scriptSig.empty()) && (request.params[1].isNull() || (!request.params[1].isNull() && request.params[1].get_bool()))) {
if (!input.scriptSig.empty() && !permitsigdata) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Inputs must not have scriptSigs");
}
input.scriptSig.clear();
}
for (CTxInWitness& witness: tx.witness.vtxinwit) {
if ((!witness.scriptWitness.IsNull()) && (request.params[1].isNull() || (!request.params[1].isNull() && request.params[1].get_bool()))) {
if (!witness.scriptWitness.IsNull() && !permitsigdata) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Inputs must not have scriptWitnesses");
}
}

View file

@ -682,10 +682,27 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
}
// do all inputs exist?
for (const CTxIn& txin : tx.vin) {
for (unsigned int i = 0; i < tx.vin.size(); i++) {
const CTxIn& txin = tx.vin[i];
// ELEMENTS:
// Don't look for coins that only exist in parent chain
// For pegin inputs check whether the pegins have already been claimed before.
// This only checks the UTXO set for already claimed pegins. For mempool conflicts,
// we rely on the GetConflictTx check done above.
if (txin.m_is_pegin) {
// Quick sanity check on witness first.
if (tx.witness.vtxinwit.size() <= i ||
tx.witness.vtxinwit[i].m_pegin_witness.stack.size() < 6 ||
uint256(tx.witness.vtxinwit[i].m_pegin_witness.stack[2]).IsNull() ||
tx.vin[i].prevout.hash.IsNull()) {
return state.Invalid(false, REJECT_INVALID, "pegin-no-witness");
}
std::pair<uint256, COutPoint> pegin = std::make_pair(uint256(tx.witness.vtxinwit[i].m_pegin_witness.stack[2]), tx.vin[i].prevout);
// This assumes non-null prevout and genesis block hash
if (view.IsPeginSpent(pegin)) {
return state.Invalid(false, REJECT_INVALID, "pegin-already-claimed");
}
continue;
}
@ -5165,7 +5182,8 @@ bool MainchainRPCCheck(const bool init)
if (!error.isNull()) {
// On the first call, it's possible to node is still in
// warmup; in that case, just wait and retry.
if (error["code"].get_int() == RPC_IN_WARMUP) {
// If this is not the initial call, just report failure.
if (init && error["code"].get_int() == RPC_IN_WARMUP) {
MilliSleep(1000);
continue;
}

View file

@ -4672,33 +4672,34 @@ UniValue getpeginaddress(const JSONRPCRequest& request)
+ HelpExampleRpc("getpeginaddress", "")
);
//Creates new address for receiving unlocked utxos
JSONRPCRequest req;
CTxDestination address = DecodeDestination(getnewaddress(req).get_str());
Witnessifier w(pwallet);
bool ret = boost::apply_visitor(w, address);
if (!ret) {
throw JSONRPCError(RPC_WALLET_ERROR, "Public key or redeemscript not known to wallet, or the key is uncompressed");
if (!pwallet->IsLocked()) {
pwallet->TopUpKeyPool();
}
pwallet->SetAddressBook(w.result, "", "receive");
// Generate a new key that is added to wallet
CPubKey newKey;
if (!pwallet->GetKeyFromPool(newKey)) {
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
}
CScript destScript = GetScriptForDestination(address);
CScript witProg = GetScriptForWitness(destScript);
// Use native witness destination
CTxDestination dest = GetDestinationForKey(newKey, OutputType::BECH32);
pwallet->SetAddressBook(dest, "", "receive");
CScript dest_script = GetScriptForDestination(dest);
// Also add raw scripts to index to recognize later.
pwallet->AddCScript(witProg);
pwallet->AddCScript(destScript);
pwallet->AddCScript(dest_script);
//Call contracthashtool, get deposit address on mainchain.
CTxDestination destAddr(ScriptHash(GetScriptForWitness(calculate_contract(Params().GetConsensus().fedpegScript, witProg))));
// Get P2CH deposit address on mainchain.
CTxDestination mainchain_dest(ScriptHash(GetScriptForWitness(calculate_contract(Params().GetConsensus().fedpegScript, dest_script))));
UniValue fundinginfo(UniValue::VOBJ);
UniValue ret(UniValue::VOBJ);
fundinginfo.pushKV("mainchain_address", EncodeParentDestination(destAddr));
fundinginfo.pushKV("claim_script", HexStr(witProg));
return fundinginfo;
ret.pushKV("mainchain_address", EncodeParentDestination(mainchain_dest));
ret.pushKV("claim_script", HexStr(dest_script));
return ret;
}
//! Derive BIP32 tweak from master xpub to child pubkey.
@ -5289,7 +5290,7 @@ static UniValue createrawpegin(const JSONRPCRequest& request, T_tx_ref& txBTCRef
if (txHashes.size() != 1 || txHashes[0] != txBTC.GetHash())
throw JSONRPCError(RPC_INVALID_PARAMETER, "The txoutproof must contain bitcoinTx and only bitcoinTx");
CScript witnessProgScript;
CScript witness_script;
unsigned int nOut = txBTC.vout.size();
if (request.params.size() > 2) {
const std::string claim_script = request.params[2].get_str();
@ -5298,26 +5299,19 @@ static UniValue createrawpegin(const JSONRPCRequest& request, T_tx_ref& txBTCRef
}
// If given manually, no need for it to be a witness script
std::vector<unsigned char> witnessBytes(ParseHex(claim_script));
witnessProgScript = CScript(witnessBytes.begin(), witnessBytes.end());
nOut = GetPeginTxnOutputIndex(txBTC, witnessProgScript);
witness_script = CScript(witnessBytes.begin(), witnessBytes.end());
nOut = GetPeginTxnOutputIndex(txBTC, witness_script);
if (nOut == txBTC.vout.size()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Given claim_script does not match the given Bitcoin transaction.");
}
}
else {
// Look through address book for pegin contract value by extracting the unlderlying witness program from p2sh-p2wpkh
// Look for known wpkh address in wallet
for (std::map<CTxDestination, CAddressBookData>::const_iterator iter = pwallet->mapAddressBook.begin(); iter != pwallet->mapAddressBook.end(); ++iter) {
CTxDestination sidechainAddress(CTxDestination(iter->first));
CScript witnessProgramScript = GetScriptForWitness(GetScriptForDestination(sidechainAddress));
int version;
std::vector<unsigned char> witnessProgram;
// Only process witness v0 programs
if (!witnessProgramScript.IsWitnessProgram(version, witnessProgram) || version != 0) {
continue;
}
nOut = GetPeginTxnOutputIndex(txBTC, witnessProgramScript);
CScript dest_script = GetScriptForDestination(iter->first);
nOut = GetPeginTxnOutputIndex(txBTC, dest_script);
if (nOut != txBTC.vout.size()) {
witnessProgScript = witnessProgramScript;
witness_script = dest_script;
break;
}
}
@ -5325,12 +5319,12 @@ static UniValue createrawpegin(const JSONRPCRequest& request, T_tx_ref& txBTCRef
if (nOut == txBTC.vout.size()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Failed to find output in bitcoinTx to the mainchain_address from getpeginaddress");
}
assert(witnessProgScript != CScript());
assert(witness_script != CScript());
int version = -1;
std::vector<unsigned char> witnessProgram;
if (!witnessProgScript.IsWitnessProgram(version, witnessProgram)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Given or recovered script is not a witness program.");
std::vector<unsigned char> witness_program;
if (!witness_script.IsWitnessProgram(version, witness_program) || version != 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Given or recovered script is not a v0 witness program.");
}
CAmount value = 0;
@ -5380,7 +5374,7 @@ static UniValue createrawpegin(const JSONRPCRequest& request, T_tx_ref& txBTCRef
stack.push_back(value_bytes);
stack.push_back(std::vector<unsigned char>(Params().GetConsensus().pegged_asset.begin(), Params().GetConsensus().pegged_asset.end()));
stack.push_back(std::vector<unsigned char>(genesisBlockHash.begin(), genesisBlockHash.end()));
stack.push_back(std::vector<unsigned char>(witnessProgScript.begin(), witnessProgScript.end()));
stack.push_back(std::vector<unsigned char>(witness_script.begin(), witness_script.end()));
stack.push_back(txData);
stack.push_back(txOutProofData);
@ -5388,7 +5382,7 @@ static UniValue createrawpegin(const JSONRPCRequest& request, T_tx_ref& txBTCRef
// We re-check depth before returning with more descriptive result
std::string err;
if (!IsValidPeginWitness(pegin_witness, mtx.vin[0].prevout, err, false)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Constructed peg-in witness is invalid.");
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Constructed peg-in witness is invalid: %s", err));
}
// Put input witness in transaction
@ -5497,6 +5491,15 @@ UniValue claimpegin(const JSONRPCRequest& request)
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
}
// To check if it's not double spending an existing pegin UTXO, we check mempool acceptance.
CValidationState acceptState;
bool accepted = ::AcceptToMemoryPool(mempool, acceptState, MakeTransactionRef(mtx), nullptr /* pfMissingInputs */,
nullptr /* plTxnReplaced */, false /* bypass_limits */, maxTxFee, true /* test_accept */);
if (!accepted) {
std::string strError = strprintf("Error: The transaction was rejected! Reason given: %s", FormatStateMessage(acceptState));
throw JSONRPCError(RPC_WALLET_ERROR, strError);
}
// Send it
CValidationState state;
mapValue_t mapValue;

View file

@ -12,12 +12,18 @@ from test_framework.util import (
p2p_port,
assert_raises_rpc_error,
assert_equal,
bytes_to_hex_str,
)
from test_framework import util
from test_framework.messages import (
CBlock,
CTransaction,
CTxInWitness,
FromHex,
)
from test_framework.blocktools import (
add_witness_commitment,
)
from decimal import Decimal
def get_new_unconfidential_address(node, addr_type="p2sh-segwit"):
@ -172,6 +178,7 @@ class FedPegTest(BitcoinTestFramework):
sidechain2 = self.nodes[3]
for node in self.nodes:
node.importprivkey(privkey=node.get_deterministic_priv_key().key, label="mining")
util.node_fastmerkle = sidechain
parent.generate(101)
sidechain.generate(101)
@ -179,13 +186,14 @@ class FedPegTest(BitcoinTestFramework):
addrs = sidechain.getpeginaddress()
addr = addrs["mainchain_address"]
assert_equal(sidechain.decodescript(addrs["claim_script"])["type"], "witness_v0_keyhash")
txid1 = parent.sendtoaddress(addr, 24)
# 10+2 confirms required to get into mempool and confirm
parent.generate(1)
time.sleep(2)
proof = parent.gettxoutproof([txid1])
raw = parent.getrawtransaction(txid1)
raw = parent.gettransaction(txid1)["hex"]
print("Attempting peg-ins")
# First attempt fails the consensus check but gives useful result
@ -219,6 +227,22 @@ class FedPegTest(BitcoinTestFramework):
# 12 confirms allows in mempool
parent.generate(1)
# Make sure that a tx with a duplicate pegin claim input gets rejected.
raw_pegin = sidechain.createrawpegin(raw, proof)["hex"]
raw_pegin = FromHex(CTransaction(), raw_pegin)
raw_pegin.vin.append(raw_pegin.vin[0]) # duplicate the pegin input
raw_pegin = sidechain.signrawtransactionwithwallet(raw_pegin.serialize().hex())["hex"]
assert_raises_rpc_error(-26, "bad-txns-inputs-duplicate", sidechain.sendrawtransaction, raw_pegin)
# Also try including this tx in a block manually and submitting it.
doublespendblock = FromHex(CBlock(), sidechain.getnewblockhex())
doublespendblock.vtx.append(FromHex(CTransaction(), raw_pegin))
doublespendblock.hashMerkleRoot = doublespendblock.calc_merkle_root()
add_witness_commitment(doublespendblock)
doublespendblock.solve()
block_hex = bytes_to_hex_str(doublespendblock.serialize(True))
assert_raises_rpc_error(-25, "bad-txns-inputs-duplicate", sidechain.testproposedblock, block_hex, True)
# Should succeed via wallet lookup for address match, and when given
raw_pegin = sidechain.createrawpegin(raw, proof)['hex']
signed_pegin = sidechain.signrawtransactionwithwallet(raw_pegin)
@ -230,6 +254,9 @@ class FedPegTest(BitcoinTestFramework):
sample_pegin_witness = sample_pegin_struct.wit.vtxinwit[0].peginWitness
pegtxid1 = sidechain.claimpegin(raw, proof)
# Make sure a second pegin claim does not get accepted in the mempool when
# another mempool tx already claims that pegin.
assert_raises_rpc_error(-4, "txn-mempool-conflict", sidechain.claimpegin, raw, proof)
# Will invalidate the block that confirms this transaction later
self.sync_all(self.node_groups)
@ -259,6 +286,21 @@ class FedPegTest(BitcoinTestFramework):
sidechain.invalidateblock(blockhash[0])
if sidechain.gettransaction(pegtxid1)["confirmations"] != 0:
raise Exception("Peg-in didn't unconfirm after invalidateblock call.")
# Create duplicate claim, put it in block along with current one in mempool
# to test duplicate-in-block claims between two txs that are in the same block.
raw_pegin = sidechain.createrawpegin(raw, proof)["hex"]
raw_pegin = sidechain.signrawtransactionwithwallet(raw_pegin)["hex"]
raw_pegin = FromHex(CTransaction(), raw_pegin)
doublespendblock = FromHex(CBlock(), sidechain.getnewblockhex())
assert(len(doublespendblock.vtx) == 2) # coinbase and pegin
doublespendblock.vtx.append(raw_pegin)
doublespendblock.hashMerkleRoot = doublespendblock.calc_merkle_root()
add_witness_commitment(doublespendblock)
doublespendblock.solve()
block_hex = bytes_to_hex_str(doublespendblock.serialize(True))
assert_raises_rpc_error(-25, "bad-txns-double-pegin", sidechain.testproposedblock, block_hex, True)
# Re-enters block
sidechain.generate(1)
if sidechain.gettransaction(pegtxid1)["confirmations"] != 1:
@ -267,6 +309,19 @@ class FedPegTest(BitcoinTestFramework):
if sidechain.gettransaction(pegtxid1)["confirmations"] != 6:
raise Exception("Peg-in should be back to 6 confirms.")
# Now the pegin is already claimed in a confirmed tx.
# In that case, a duplicate claim should (1) not be accepted in the mempool
# and (2) not be accepted in a block.
assert_raises_rpc_error(-4, "pegin-already-claimed", sidechain.claimpegin, raw, proof)
# For case (2), manually craft a block and include the tx.
doublespendblock = FromHex(CBlock(), sidechain.getnewblockhex())
doublespendblock.vtx.append(raw_pegin)
doublespendblock.hashMerkleRoot = doublespendblock.calc_merkle_root()
add_witness_commitment(doublespendblock)
doublespendblock.solve()
block_hex = bytes_to_hex_str(doublespendblock.serialize(True))
assert_raises_rpc_error(-25, "bad-txns-double-pegin", sidechain.testproposedblock, block_hex, True)
# Do multiple claims in mempool
n_claims = 6
@ -281,7 +336,7 @@ class FedPegTest(BitcoinTestFramework):
txid = parent.sendtoaddress(addrs["mainchain_address"], 1)
parent.generate(1)
proof = parent.gettxoutproof([txid])
raw = parent.getrawtransaction(txid)
raw = parent.gettransaction(txid)["hex"]
if i % 2 == 0:
parent.generate(11)
pegtxs += [sidechain.claimpegin(raw, proof)]
@ -362,7 +417,7 @@ class FedPegTest(BitcoinTestFramework):
txid = parent.sendtoaddress(addr, 1)
parent.generate(12)
proof = parent.gettxoutproof([txid])
raw = parent.getrawtransaction(txid)
raw = parent.gettransaction(txid)["hex"]
sidechain.claimpegin(raw, proof) # stuck peg
sidechain.generate(1)
print("Waiting to ensure block is being rejected by sidechain2")

View file

@ -145,6 +145,7 @@ class PSBTTest(BitcoinTestFramework):
# Make sure that a psbt with signatures cannot be converted
signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx['hex'])
assert_raises_rpc_error(-22, "Inputs must not have scriptWitnesses", self.nodes[0].converttopsbt, signedtx['hex'], False)
assert_raises_rpc_error(-22, "Inputs must not have scriptWitnesses", self.nodes[0].converttopsbt, signedtx['hex'])
assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].converttopsbt, signedtx['hex'])
assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].converttopsbt, signedtx['hex'], False)