Merge #362: 2WP: Allow other elements chains as parent chains for pegs

3f62b32 QA: Test 2wp using the test framework (Jorge Timón)
2de5765 2WP: QA: Introduce -con_parent_chain_signblockscript (Jorge Timón)
781953b QA: Optionally allow other chains (Jorge Timón)
This commit is contained in:
Jorge Timón 2018-09-07 00:16:19 +02:00
commit cd1a9e2e89
No known key found for this signature in database
GPG key ID: A4F5D141C01A0387
12 changed files with 461 additions and 32 deletions

View file

@ -104,6 +104,7 @@ testScripts = [
'signed_blockchain.py',
'initial_reissuance_token.py',
'feature_blocksign.py',
'feature_fedpeg.py',
'default_asset_name.py',
'assetdir.py',

340
qa/rpc-tests/feature_fedpeg.py Executable file
View file

@ -0,0 +1,340 @@
#!/usr/bin/env python3
from decimal import Decimal
import json
import time
from test_framework.authproxy import JSONRPCException
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
connect_nodes_bi,
rpc_auth_pair,
rpc_port,
start_node,
start_nodes,
stop_node,
)
# Sync mempool, make a block, sync blocks
def sync_all(sidechain, sidechain2, makeblock=True):
block = ""
timeout = 20
while len(sidechain.getrawmempool()) != len(sidechain2.getrawmempool()):
time.sleep(1)
timeout -= 1
if timeout == 0:
raise Exception("Peg-in has failed to propagate.")
if makeblock:
block = sidechain2.generate(1)
while sidechain.getblockcount() != sidechain2.getblockcount():
time.sleep(1)
timeout -= 1
if timeout == 0:
raise Exception("Blocks are not propagating.")
return block
def get_new_unconfidential_address(node):
addr = node.getnewaddress()
val_addr = node.validateaddress(addr)
if 'unconfidential' in val_addr:
return val_addr['unconfidential']
return val_addr['address']
class FedPegTest(BitcoinTestFramework):
def __init__(self):
super().__init__()
self.setup_clean_chain = True
self.num_nodes = 4
def setup_network(self, split=False):
# Parent chain args
self.extra_args = [[
# '-printtoconsole',
'-validatepegin=0',
'-anyonecanspendaremine',
'-initialfreecoins=2100000000000000',
]] * 2
self.nodes = start_nodes(2, self.options.tmpdir, self.extra_args[:2], chain='parent')
connect_nodes_bi(self.nodes, 0, 1)
self.parentgenesisblockhash = self.nodes[0].getblockhash(0)
print('parentgenesisblockhash', self.parentgenesisblockhash)
parent_pegged_asset = self.nodes[0].getsidechaininfo()['pegged_asset']
# Sidechain args
parent_chain_signblockscript = '51'
for n in range(2):
rpc_u, rpc_p = rpc_auth_pair(n)
self.extra_args.append([
# '-printtoconsole',
'-parentgenesisblockhash=%s' % self.parentgenesisblockhash,
'-validatepegin=1',
'-anyonecanspendaremine=0',
'-initialfreecoins=0',
'-peginconfirmationdepth=10',
'-mainchainrpchost=127.0.0.1',
'-mainchainrpcport=%s' % rpc_port(n),
'-mainchainrpcuser=%s' % rpc_u,
'-mainchainrpcpassword=%s' % rpc_p,
'-parentpubkeyprefix=235',
'-parentscriptprefix=75',
'-con_parent_chain_signblockscript=%s' % parent_chain_signblockscript,
'-con_parent_pegged_asset=%s' % parent_pegged_asset,
])
self.nodes.append(start_node(n + 2, self.options.tmpdir, self.extra_args[n + 2], chain='sidechain'))
connect_nodes_bi(self.nodes, 2, 3)
self.is_network_split = True
self.sync_all()
def test_pegout(self, parent_chain_addr, sidechain):
pegout_txid = sidechain.sendtomainchain(parent_chain_addr, 1)
raw_pegout = sidechain.getrawtransaction(pegout_txid, True)
assert 'vout' in raw_pegout and len(raw_pegout['vout']) > 0
pegout_tested = False
for output in raw_pegout['vout']:
scriptPubKey = output['scriptPubKey']
if 'type' in scriptPubKey and scriptPubKey['type'] == 'nulldata':
assert ('pegout_hex' in scriptPubKey and 'pegout_asm' in scriptPubKey and 'pegout_type' in scriptPubKey and
'pegout_chain' in scriptPubKey and 'pegout_reqSigs' in scriptPubKey and 'pegout_addresses' in scriptPubKey)
assert scriptPubKey['pegout_chain'] == self.parentgenesisblockhash
assert scriptPubKey['pegout_reqSigs'] == 1
assert parent_chain_addr in scriptPubKey['pegout_addresses']
pegout_tested = True
break
assert pegout_tested
def run_test(self):
parent = self.nodes[0]
parent2 = self.nodes[1]
sidechain = self.nodes[2]
sidechain2 = self.nodes[3]
parent.generate(101)
sidechain.generate(101)
addrs = sidechain.getpeginaddress()
addr = parent.validateaddress(addrs["mainchain_address"])
print('addrs', addrs)
print('addr', addr)
txid1 = parent.sendtoaddress(addrs["mainchain_address"], 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)
print('raw', parent.getrawtransaction(txid1, True))
print("Attempting peg-in")
# First attempt fails the consensus check but gives useful result
try:
pegtxid = sidechain.claimpegin(raw, proof)
raise Exception("Peg-in should not be mature enough yet, need another block.")
except JSONRPCException as e:
print('ERROR:', e.error)
assert("Peg-in Bitcoin transaction needs more confirmations to be sent." in e.error["message"])
# Second attempt simply doesn't hit mempool bar
parent.generate(10)
try:
pegtxid = sidechain.claimpegin(raw, proof)
raise Exception("Peg-in should not be mature enough yet, need another block.")
except JSONRPCException as e:
assert("Peg-in Bitcoin transaction needs more confirmations to be sent." in e.error["message"])
# Should fail due to non-witness
try:
pegtxid = sidechain.claimpegin(raw, proof, get_new_unconfidential_address(parent))
raise Exception("Peg-in with non-matching claim_script should fail.")
except JSONRPCException as e:
print(e.error["message"])
assert("Given or recovered script is not a witness program." in e.error["message"])
# # Should fail due to non-matching wallet address
# try:
# pegtxid = sidechain.claimpegin(raw, proof, get_new_unconfidential_address(sidechain))
# raise Exception("Peg-in with non-matching claim_script should fail.")
# except JSONRPCException as e:
# print(e.error["message"])
# assert("Given claim_script does not match the given Bitcoin transaction." in e.error["message"])
# 12 confirms allows in mempool
parent.generate(1)
# Should succeed via wallet lookup for address match, and when given
pegtxid1 = sidechain.claimpegin(raw, proof)
# Will invalidate the block that confirms this transaction later
sync_all(parent, parent2)
blockhash = sync_all(sidechain, sidechain2)
sidechain.generate(5)
tx1 = sidechain.gettransaction(pegtxid1)
print('tx1', tx1)
if "confirmations" in tx1 and tx1["confirmations"] == 6:
print("Peg-in is confirmed: Success!")
else:
raise Exception("Peg-in confirmation has failed.")
# Look at pegin fields
decoded = sidechain.decoderawtransaction(tx1["hex"])
assert decoded["vin"][0]["is_pegin"] == True
assert len(decoded["vin"][0]["pegin_witness"]) > 0
# Check that there's sufficient fee for the peg-in
vsize = decoded["vsize"]
fee_output = decoded["vout"][1]
fallbackfee_pervbyte = Decimal("0.00001")/Decimal("1000")
assert fee_output["scriptPubKey"]["type"] == "fee"
assert fee_output["value"] >= fallbackfee_pervbyte*vsize
# Quick reorg checks of pegs
sidechain.invalidateblock(blockhash[0])
if sidechain.gettransaction(pegtxid1)["confirmations"] != 0:
raise Exception("Peg-in didn't unconfirm after invalidateblock call.")
# Re-enters block
sidechain.generate(1)
if sidechain.gettransaction(pegtxid1)["confirmations"] != 1:
raise Exception("Peg-in should have one confirm on side block.")
sidechain.reconsiderblock(blockhash[0])
if sidechain.gettransaction(pegtxid1)["confirmations"] != 6:
raise Exception("Peg-in should be back to 6 confirms.")
# Do many claims in mempool
n_claims = 5
print("Flooding mempool with many small claims")
pegtxs = []
sidechain.generate(101)
for i in range(n_claims):
addrs = sidechain.getpeginaddress()
txid = parent.sendtoaddress(addrs["mainchain_address"], 1)
parent.generate(12)
proof = parent.gettxoutproof([txid])
raw = parent.getrawtransaction(txid)
pegtxs += [sidechain.claimpegin(raw, proof)]
sync_all(parent, parent2)
sync_all(sidechain, sidechain2)
sidechain2.generate(1)
for pegtxid in pegtxs:
tx = sidechain.gettransaction(pegtxid)
if "confirmations" not in tx or tx["confirmations"] == 0:
raise Exception("Peg-in confirmation has failed.")
print("Test pegout")
self.test_pegout(get_new_unconfidential_address(parent), sidechain)
print("Test pegout P2SH")
parent_chain_addr = get_new_unconfidential_address(parent)
parent_pubkey = parent.validateaddress(parent_chain_addr)["pubkey"]
parent_chain_p2sh_addr = parent.createmultisig(1, [parent_pubkey])["address"]
self.test_pegout(parent_chain_p2sh_addr, sidechain)
print("Test pegout Garbage")
parent_chain_addr = "garbage"
try:
self.test_pegout(parent_chain_addr, sidechain)
raise Exception("A garbage address should fail.")
except JSONRPCException as e:
assert("Invalid Bitcoin address" in e.error["message"])
print("Test pegout Garbage valid")
prev_txid = sidechain.sendtoaddress(sidechain.getnewaddress(), 1)
sidechain.generate(1)
pegout_chain = 'a' * 64
pegout_hex = 'b' * 500
inputs = [{"txid": prev_txid, "vout": 0}]
outputs = {"vdata": [pegout_chain, pegout_hex]}
rawtx = sidechain.createrawtransaction(inputs, outputs)
raw_pegout = sidechain.decoderawtransaction(rawtx)
assert 'vout' in raw_pegout and len(raw_pegout['vout']) > 0
pegout_tested = False
for output in raw_pegout['vout']:
scriptPubKey = output['scriptPubKey']
if 'type' in scriptPubKey and scriptPubKey['type'] == 'nulldata':
assert ('pegout_hex' in scriptPubKey and 'pegout_asm' in scriptPubKey and 'pegout_type' in scriptPubKey and
'pegout_chain' in scriptPubKey and 'pegout_reqSigs' not in scriptPubKey and 'pegout_addresses' not in scriptPubKey)
assert scriptPubKey['pegout_type'] == 'nonstandard'
assert scriptPubKey['pegout_chain'] == pegout_chain
assert scriptPubKey['pegout_hex'] == pegout_hex
pegout_tested = True
break
assert pegout_tested
print ("Now test failure to validate peg-ins based on intermittant bitcoind rpc failure")
stop_node(self.nodes[1], 1)
txid = parent.sendtoaddress(addrs["mainchain_address"], 1)
parent.generate(12)
proof = parent.gettxoutproof([txid])
raw = parent.getrawtransaction(txid)
stuck_peg = sidechain.claimpegin(raw, proof)
sidechain.generate(1)
print("Waiting to ensure block is being rejected by sidechain2")
time.sleep(5)
assert(sidechain.getblockcount() != sidechain2.getblockcount())
print("Restarting parent2")
self.nodes[1] = start_node(1, self.options.tmpdir, self.extra_args[1], chain='parent')
parent2 = self.nodes[1]
connect_nodes_bi(self.nodes, 0, 1)
time.sleep(5)
# Don't make a block, race condition when pegin-invalid block
# is awaiting further validation, nodes reject subsequent blocks
# even ones they create
sync_all(sidechain, sidechain2, makeblock=False)
print("Now send funds out in two stages, partial, and full")
some_btc_addr = get_new_unconfidential_address(parent)
bal_1 = sidechain.getwalletinfo()["balance"]["bitcoin"]
try:
sidechain.sendtomainchain(some_btc_addr, bal_1 + 1)
raise Exception("Sending out too much; should have failed")
except JSONRPCException as e:
assert("Insufficient funds" in e.error["message"])
assert(sidechain.getwalletinfo()["balance"]["bitcoin"] == bal_1)
try:
sidechain.sendtomainchain(some_btc_addr+"b", bal_1 - 1)
raise Exception("Sending to invalid address; should have failed")
except JSONRPCException as e:
assert("Invalid Bitcoin address" in e.error["message"])
assert(sidechain.getwalletinfo()["balance"]["bitcoin"] == bal_1)
try:
sidechain.sendtomainchain("1Nro9WkpaKm9axmcfPVp79dAJU1Gx7VmMZ", bal_1 - 1)
raise Exception("Sending to mainchain address when should have been testnet; should have failed")
except JSONRPCException as e:
assert("Invalid Bitcoin address" in e.error["message"])
assert(sidechain.getwalletinfo()["balance"]["bitcoin"] == bal_1)
peg_out_txid = sidechain.sendtomainchain(some_btc_addr, 1)
peg_out_details = sidechain.decoderawtransaction(sidechain.getrawtransaction(peg_out_txid))
# peg-out, change
assert(len(peg_out_details["vout"]) == 3)
found_pegout_value = False
for output in peg_out_details["vout"]:
if "value" in output and output["value"] == 1:
found_pegout_value = True
assert(found_pegout_value)
bal_2 = sidechain.getwalletinfo()["balance"]["bitcoin"]
# Make sure balance went down
assert(bal_2 + 1 < bal_1)
sidechain.sendtomainchain(some_btc_addr, bal_2, True)
assert("bitcoin" not in sidechain.getwalletinfo()["balance"])
print('Success!')
if __name__ == '__main__':
FedPegTest().main()

View file

@ -187,7 +187,6 @@ def initialize_datadir(dirname, n):
os.makedirs(datadir)
rpc_u, rpc_p = rpc_auth_pair(n)
with open(os.path.join(datadir, "elements.conf"), 'w', encoding='utf8') as f:
f.write("regtest=1\n")
f.write("rpcuser=" + rpc_u + "\n")
f.write("rpcpassword=" + rpc_p + "\n")
f.write("port="+str(p2p_port(n))+"\n")
@ -334,14 +333,14 @@ def _rpchost_to_args(rpchost):
rv += ['-rpcport=' + rpcport]
return rv
def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=None):
def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=None, chain='elementsregtest'):
"""
Start a bitcoind and return RPC connection to it
"""
datadir = os.path.join(dirname, "node"+str(i))
if binary is None:
binary = os.getenv("ELEMENTSD", "elementsd")
args = [ binary, "-datadir="+datadir, "-server", "-keypool=1", "-discover=0", "-rest", "-mocktime="+str(get_mocktime()) ]
args = [ binary, '-chain='+chain, "-datadir="+datadir, "-server", "-keypool=1", "-discover=0", "-rest", "-mocktime="+str(get_mocktime()) ]
if extra_args is not None: args.extend(extra_args)
bitcoind_processes[i] = subprocess.Popen(args)
if os.getenv("PYTHON_DEBUG", ""):
@ -357,7 +356,7 @@ def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=
return proxy
def start_nodes(num_nodes, dirname, extra_args=None, rpchost=None, timewait=None, binary=None):
def start_nodes(num_nodes, dirname, extra_args=None, rpchost=None, timewait=None, binary=None, chain='elementsregtest'):
"""
Start multiple bitcoinds, return RPC connections to them
"""
@ -366,7 +365,7 @@ def start_nodes(num_nodes, dirname, extra_args=None, rpchost=None, timewait=None
rpcs = []
try:
for i in range(num_nodes):
rpcs.append(start_node(i, dirname, extra_args[i], rpchost, timewait=timewait, binary=binary[i]))
rpcs.append(start_node(i, dirname, extra_args[i], rpchost, timewait=timewait, binary=binary[i], chain=chain))
except: # If one node failed to start, stop the others
stop_nodes(rpcs)
raise

View file

@ -130,6 +130,9 @@ protected:
consensus.defaultAssumeValid = uint256S(GetArg("-con_defaultassumevalid", "0x00"));
consensus.pegin_min_depth = GetArg("-peginconfirmationdepth", DEFAULT_PEGIN_CONFIRMATION_DEPTH);
consensus.mandatory_coinbase_destination = StrHexToScriptWithDefault(GetArg("-con_mandatorycoinbase", ""), CScript()); // Blank script allows any coinbase destination
consensus.parent_chain_signblockscript = StrHexToScriptWithDefault(GetArg("-con_parent_chain_signblockscript", ""), CScript());
consensus.parent_pegged_asset.SetHex(GetArg("-con_parent_pegged_asset", "0x00"));
// bitcoin regtest is the parent chain by default
parentGenesisBlockHash = uint256S(GetArg("-parentgenesisblockhash", "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206"));
initialFreeCoins = GetArg("-initialfreecoins", 0);

View file

@ -73,6 +73,9 @@ struct Params {
CScript mandatory_coinbase_destination;
CScript signblockscript;
bool has_parent_chain;
CScript parent_chain_signblockscript;
CAsset parent_pegged_asset;
bool ParentChainHasPow() const { return parent_chain_signblockscript == CScript();}
};
} // namespace Consensus

View file

@ -518,7 +518,8 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-defaultpeggedassetname", strprintf("The name of the default asset created in the genesis block. (default: bitcoin)"));
strUsage += HelpMessageOpt("-parentpubkeyprefix", strprintf(_("The byte prefix, in decimal, of the parent chain's base58 pubkey address. (default: %d)"), 111));
strUsage += HelpMessageOpt("-parentscriptprefix", strprintf(_("The byte prefix, in decimal, of the parent chain's base58 script address. (default: %d)"), 196));
strUsage += HelpMessageOpt("-con_parent_chain_signblockscript", _("Whether parent chain uses pow or signed blocks. If the parent chain uses signed blocks, the challenge (scriptPubKey) script. If not, an empty string. (default: empty script [ie parent uses pow])"));
strUsage += HelpMessageOpt("-con_parent_pegged_asset=<hex>", _("Asset ID (hex) for pegged asset for when parent chain has CA. (default: 0x00)"));
}
strUsage += HelpMessageOpt("-validatepegin", strprintf(_("Validate peg-in claims. An RPC connection will be attempted to the trusted bitcoind using the `mainchain*` settings below. All functionaries must run this enabled. (default: %u)"), DEFAULT_VALIDATE_PEGIN));
strUsage += HelpMessageOpt("-mainchainrpchost=<addr>", strprintf("The address which the daemon will try to connect to the trusted bitcoind to validate peg-ins, if enabled. (default: cookie auth)"));
@ -1024,7 +1025,7 @@ bool AppInitParameterInteraction()
nConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
policyAsset = CAsset(uint256S(GetArg("-feeasset", chainparams.GetConsensus().pegged_asset.GetHex())));
// Fee-per-kilobyte amount considered the same as "free"
// If you are mining, be careful setting this:
// if you set it to zero then

View file

@ -57,7 +57,7 @@ bool CheckBitcoinProof(uint256 hash, unsigned int nBits)
return true;
}
bool CheckProof(const CBlockHeader& block, const Consensus::Params& params)
static bool CheckProofGeneric(const CBlockHeader& block, const Consensus::Params& params, const CScript& challenge)
{
if (block.GetHash() == params.hashGenesisBlock)
return true;
@ -82,7 +82,17 @@ bool CheckProof(const CBlockHeader& block, const Consensus::Params& params)
| SCRIPT_VERIFY_LOW_S // Stop easiest signature fiddling
| SCRIPT_VERIFY_WITNESS // Required for cleanstack eval in VerifyScript
| SCRIPT_NO_SIGHASH_BYTE; // non-Check(Multi)Sig signatures will not have sighash byte
return GenericVerifyScript(block.proof.solution, params.signblockscript, proof_flags, block);
return GenericVerifyScript(block.proof.solution, challenge, proof_flags, block);
}
bool CheckProofSignedParent(const CBlockHeader& block, const Consensus::Params& params)
{
return CheckProofGeneric(block, params, params.parent_chain_signblockscript);
}
bool CheckProof(const CBlockHeader& block, const Consensus::Params& params)
{
return CheckProofGeneric(block, params, params.signblockscript);
}
bool MaybeGenerateProof(const Consensus::Params& params, CBlockHeader *pblock, CWallet *pwallet)

View file

@ -21,6 +21,7 @@ class uint256;
/** Check whether a block hash satisfies the proof-of-work requirement specified by nBits */
bool CheckBitcoinProof(uint256 hash, unsigned int nBits);
bool CheckProofSignedParent(const CBlockHeader& block, const Consensus::Params& params);
bool CheckProof(const CBlockHeader& block, const Consensus::Params&);
/** Scans nonces looking for a hash with at least some zero bits */
bool MaybeGenerateProof(const Consensus::Params& params, CBlockHeader* pblock, CWallet* pwallet);

View file

@ -63,7 +63,7 @@ UniValue blockheaderToJSON(const CBlockIndex* blockindex)
result.push_back(Pair("time", (int64_t)blockindex->nTime));
result.push_back(Pair("mediantime", (int64_t)blockindex->GetMedianTimePast()));
result.push_back(Pair("signblock_witness_asm", ScriptToAsmStr(blockindex->proof.solution)));
result.push_back(Pair("signblock_witness_hex", HexStr(blockindex->proof.solution.begin(), blockindex->proof.solution.end())));
result.push_back(Pair("signblock_witness_hex", HexStr(blockindex->proof.solution)));
if (blockindex->pprev)
result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex()));
@ -105,7 +105,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx
result.push_back(Pair("time", block.GetBlockTime()));
result.push_back(Pair("mediantime", (int64_t)blockindex->GetMedianTimePast()));
result.push_back(Pair("signblock_witness_asm", ScriptToAsmStr(blockindex->proof.solution)));
result.push_back(Pair("signblock_witness_hex", HexStr(blockindex->proof.solution.begin(), blockindex->proof.solution.end())));
result.push_back(Pair("signblock_witness_hex", HexStr(blockindex->proof.solution)));
if (blockindex->pprev)
result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex()));
@ -1105,10 +1105,14 @@ UniValue getsidechaininfo(const JSONRPCRequest& request)
"Returns an object containing various state info regarding sidechain functionality.\n"
"\nResult:\n"
"{\n"
" \"fedpegscript\": \"xxxx\", (string) The fedpegscript in hex\n"
" \"fedpegscript\": \"xxxx\", (string) The fedpegscript in hex\n"
" \"pegged_asset\" : \"xxxx\", (string) Pegged asset type in hex\n"
" \"min_peg_diff\" : \"xxxx\", (string) The minimum difficulty parent chain header target. Peg-in headers that have less work will be rejected as an anti-Dos measure.\n"
" \"parent_blockhash\" : \"xxxx\", (string) The parent genesis blockhash as source of pegged-in funds.\n"
" \"parent_chain_has_pow\": \"xxxx\", (boolean) Whether parent chain has pow or signed blocks.\n"
" \"parent_chain_signblockscript_asm\": \"xxxx\", (string) If the parent chain has signed blocks, its signblockscript in ASM.\n"
" \"parent_chain_signblockscript_hex\": \"xxxx\", (string) If the parent chain has signed blocks, its signblockscript in hex.\n"
" \"parent_pegged_asset\": \"xxxx\", (boolean) If the parent chain has Confidential Assets, the asset id of the pegged asset in that chain.\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("getsidechaininfo", "")
@ -1125,6 +1129,12 @@ UniValue getsidechaininfo(const JSONRPCRequest& request)
obj.push_back(Pair("pegged_asset", consensus.pegged_asset.GetHex()));
obj.push_back(Pair("min_peg_diff", consensus.parentChainPowLimit.GetHex()));
obj.push_back(Pair("parent_blockhash", parent_blockhash.GetHex()));
obj.push_back(Pair("parent_chain_has_pow", consensus.ParentChainHasPow()));
if (!consensus.ParentChainHasPow()) {
obj.push_back(Pair("parent_chain_signblockscript_asm", ScriptToAsmStr(consensus.parent_chain_signblockscript)));
obj.push_back(Pair("parent_chain_signblockscript_hex", HexStr(consensus.parent_chain_signblockscript)));
obj.push_back(Pair("parent_pegged_asset", HexStr(consensus.parent_pegged_asset)));
}
return obj;
}

View file

@ -17,6 +17,7 @@
#include "crypto/hmac_sha256.h"
#include "init.h"
#include "issuance.h"
#include "merkleblock.h"
#include "policy/fees.h"
#include "policy/policy.h"
#include "pow.h"
@ -2374,6 +2375,21 @@ bool GetAmountFromParentChainPegin(CAmount& amount, const Sidechain::Bitcoin::CT
return true;
}
bool GetAmountFromParentChainPegin(CAmount& amount, const CTransaction& txBTC, unsigned int nOut)
{
if (!txBTC.vout[nOut].nValue.IsExplicit()) {
return false;
}
if (!txBTC.vout[nOut].nAsset.IsExplicit()) {
return false;
}
if (txBTC.vout[nOut].nAsset.GetAsset() != Params().GetConsensus().parent_pegged_asset) {
return false;
}
amount = txBTC.vout[nOut].nValue.GetAmount();
return true;
}
template<typename T>
static bool GetBlockAndTxFromMerkleBlock(uint256& block_hash, uint256& tx_hash, T& merkle_block, const std::vector<unsigned char>& merkle_block_raw)
{
@ -2497,20 +2513,36 @@ bool IsValidPeginWitness(const CScriptWitness& pegin_witness, const COutPoint& p
uint256 block_hash;
uint256 tx_hash;
// Get txout proof
Sidechain::Bitcoin::CMerkleBlock merkle_block;
if (!GetBlockAndTxFromMerkleBlock(block_hash, tx_hash, merkle_block, stack[5])) {
return false;
}
if (!CheckBitcoinProof(block_hash, merkle_block.header.nBits)) {
return false;
}
if (Params().GetConsensus().ParentChainHasPow()) {
// Get serialized transaction
Sidechain::Bitcoin::CTransactionRef pegtx;
if (!CheckPeginTx(stack[4], pegtx, prevout, value, claim_script)) {
return false;
Sidechain::Bitcoin::CMerkleBlock merkle_block_pow;
if (!GetBlockAndTxFromMerkleBlock(block_hash, tx_hash, merkle_block_pow, stack[5])) {
return false;
}
if (!CheckBitcoinProof(block_hash, merkle_block_pow.header.nBits)) {
return false;
}
Sidechain::Bitcoin::CTransactionRef pegtx;
if (!CheckPeginTx(stack[4], pegtx, prevout, value, claim_script)) {
return false;
}
} else {
CMerkleBlock merkle_block;
if (!GetBlockAndTxFromMerkleBlock(block_hash, tx_hash, merkle_block, stack[5])) {
return false;
}
if (!CheckProofSignedParent(merkle_block.header, Params().GetConsensus())) {
return false;
}
CTransactionRef pegtx;
if (!CheckPeginTx(stack[4], pegtx, prevout, value, claim_script)) {
return false;
}
}
// Check that the merkle proof corresponds to the txid

View file

@ -267,6 +267,7 @@ void ThreadScriptCheck();
/** Check if bitcoind connection via RPC is correctly working*/
bool BitcoindRPCCheck(bool init);
bool GetAmountFromParentChainPegin(CAmount& amount, const Sidechain::Bitcoin::CTransaction& txBTC, unsigned int nOut);
bool GetAmountFromParentChainPegin(CAmount& amount, const CTransaction& txBTC, unsigned int nOut);
/** Checks pegin witness for validity */
bool IsValidPeginWitness(const CScriptWitness& pegin_witness, const COutPoint& prevout, bool check_depth = true);
/** Extracts an output from pegin witness for evaluation as a normal output */

View file

@ -2082,7 +2082,7 @@ UniValue gettransaction(const JSONRPCRequest& request)
" \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n"
" 'send' category of transactions.\n"
" \"abandoned\": xxx (bool) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n"
" 'send' category of transactions.\n"
" 'send' category of transactions.\n"
" }\n"
" ,...\n"
" ],\n"
@ -3512,7 +3512,8 @@ UniValue sendtomainchain(const JSONRPCRequest& request)
extern UniValue signrawtransaction(const JSONRPCRequest& request);
extern UniValue sendrawtransaction(const JSONRPCRequest& request);
unsigned int GetPeginTxnOutputIndex(const Sidechain::Bitcoin::CTransaction& txn, const CScript& witnessProgram)
template<typename T_tx>
unsigned int GetPeginTxnOutputIndex(const T_tx& txn, const CScript& witnessProgram)
{
unsigned int nOut = 0;
//Call contracthashtool
@ -3523,7 +3524,8 @@ unsigned int GetPeginTxnOutputIndex(const Sidechain::Bitcoin::CTransaction& txn,
return nOut;
}
UniValue createrawpegin(const JSONRPCRequest& request)
template<typename T_tx_ref, typename T_tx, typename T_merkle_block>
static UniValue createrawpegin(const JSONRPCRequest& request, T_tx_ref& txBTCRef, T_tx& tx_aux, T_merkle_block& merkleBlock)
{
if (request.fHelp || request.params.size() < 2 || request.params.size() > 3)
throw std::runtime_error(
@ -3552,18 +3554,16 @@ UniValue createrawpegin(const JSONRPCRequest& request)
std::vector<unsigned char> txData = ParseHex(request.params[0].get_str());
CDataStream ssTx(txData, SER_NETWORK, PROTOCOL_VERSION);
Sidechain::Bitcoin::CTransactionRef txBTCRef;
try {
ssTx >> txBTCRef;
}
catch (...) {
throw JSONRPCError(RPC_TYPE_ERROR, "The included bitcoinTx is malformed. Are you sure that is the whole string?");
}
Sidechain::Bitcoin::CTransaction txBTC(*txBTCRef);
T_tx txBTC(*txBTCRef);
std::vector<unsigned char> txOutProofData = ParseHex(request.params[1].get_str());
CDataStream ssTxOutProof(txOutProofData, SER_NETWORK, PROTOCOL_VERSION);
Sidechain::Bitcoin::CMerkleBlock merkleBlock;
try {
ssTxOutProof >> merkleBlock;
}
@ -3571,8 +3571,9 @@ UniValue createrawpegin(const JSONRPCRequest& request)
throw JSONRPCError(RPC_TYPE_ERROR, "The included txoutproof is malformed. Are you sure that is the whole string?");
}
if (!ssTxOutProof.empty() || !CheckBitcoinProof(merkleBlock.header.GetHash(), merkleBlock.header.nBits))
if (!ssTxOutProof.empty()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid tx out proof");
}
std::vector<uint256> txHashes;
std::vector<unsigned int> txIndices;
@ -3622,7 +3623,11 @@ UniValue createrawpegin(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Given or recovered script is not a witness program.");
}
CAmount value = txBTC.vout[nOut].nValue;
CAmount value = 0;
if (!GetAmountFromParentChainPegin(value, txBTC, nOut)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Amounts to pegin must be explicit and asset must be %s",
Params().GetConsensus().parent_pegged_asset.GetHex()));
}
CDataStream stream(0, 0);
try {
@ -3703,6 +3708,29 @@ UniValue createrawpegin(const JSONRPCRequest& request)
return ret;
}
UniValue createrawpegin(const JSONRPCRequest& request)
{
UniValue ret(UniValue::VOBJ);
if (Params().GetConsensus().ParentChainHasPow()) {
Sidechain::Bitcoin::CTransactionRef txBTCRef;
Sidechain::Bitcoin::CTransaction tx_aux;
Sidechain::Bitcoin::CMerkleBlock merkleBlock;
ret = createrawpegin(request, txBTCRef, tx_aux, merkleBlock);
if (!CheckBitcoinProof(merkleBlock.header.GetHash(), merkleBlock.header.nBits)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid tx out proof");
}
} else {
CTransactionRef txBTCRef;
CTransaction tx_aux;
CMerkleBlock merkleBlock;
ret = createrawpegin(request, txBTCRef, tx_aux, merkleBlock);
if (!CheckProofSignedParent(merkleBlock.header, Params().GetConsensus())) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid tx out proof");
}
}
return ret;
}
UniValue claimpegin(const JSONRPCRequest& request)
{