Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revamp peg-in transactions #260

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
65ed777
remove legacy peg-in/withdrawlocks
instagibbs Sep 15, 2017
9a9ac25
genesis block has actual fee issuance in regtest
instagibbs Sep 15, 2017
36b3ab1
add serialization flag support for peg-in inputs
instagibbs Sep 15, 2017
16973cd
add pegin_witness to witness inputs
instagibbs Sep 15, 2017
34d7cd0
add pegin witness validation and extraction helper functions
instagibbs Sep 15, 2017
0b9c5eb
begin consensus support for peg-ins
instagibbs Sep 15, 2017
6bad4e9
add blocks that have pegins rejected to the reconsider queue
instagibbs Sep 15, 2017
ff5e9cd
mempool and policy support for peg-in inputs
instagibbs Sep 15, 2017
7633839
add raw rpc support for peg-in functionality
instagibbs Sep 15, 2017
6e8e6b2
add wallet support for pegging functionality
instagibbs Sep 15, 2017
3ba146d
update assets tutorial to support new peg-in format
instagibbs Sep 15, 2017
be735cf
re-add pegging python test
instagibbs Sep 15, 2017
4aad513
Add unit tests for peg-in authorization
instagibbs Sep 18, 2017
d4d00da
basic functional test for peg-in reorgs in sidechain
instagibbs Sep 18, 2017
6534bd3
Add is_pegin response for raw transaction RPCs
instagibbs Sep 29, 2017
13c3dad
de-dupe calculate_contract code
instagibbs Oct 4, 2017
851a30b
Don't restrict pegin witness to 'witness program' definition
instagibbs Oct 16, 2017
abafbac
change rpc test witness_program to claim_script
instagibbs Oct 16, 2017
7dc4f9d
remove CNumScript from pegin witness logic
instagibbs Oct 18, 2017
b401ff0
change explanation of policy asset seeding in python tutorial
instagibbs Oct 19, 2017
2940c4e
remove tabs from IsValidPegin
instagibbs Oct 21, 2017
da6960d
validation variables changed to not assume witness program for pegin
instagibbs Oct 21, 2017
6cc380a
don't require claim_script to be witness program for raw claim pegin
instagibbs Oct 21, 2017
3ff4aab
update unit test
instagibbs Nov 2, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 8 additions & 16 deletions contrib/assets_tutorial/assets_tutorial.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ def sync_all(e1, e2):
time.sleep(1)
return


## Preparations

# Make data directories for each daemon
Expand Down Expand Up @@ -91,20 +90,19 @@ def sync_all(e1, e2):
# Alternatively, you can set validatepegin=0 in their configs and not
# run the bitcoin node, but it is necessary for fully validating the two way peg.

# At beginning "immature balance" holds all funds until genesis is mature
# Regtest chain starts with 21M elements coins as OP_TRUE which the wallet
# understands. This is useful for testing basic functionality.
# In Elements there is no block subsidy. All 21M output value in a mainnet
# pegged sidechain are locked for pegging.
# Regtest chain starts with 21M bitcoins as OP_TRUE which the wallet
# understands. This is useful for testing basic functionality and for
# blockchains that have no pegging functionality. A fee currency is required
# for anti-DoS purposes as well as asset issuance, which consumes inputs for entropy.
# In Elements there is no block subsidy. In a production sidechain it can
# be configured to start with no outputs, necessitating peg-in functionality
# for asset issuance.
e1.getwalletinfo()

# In regtest mining "target" is OP_TRUE since we have not set `-signblockscript` argument
# Generate simply works.
e1.generate(101)
sync_all(e1, e2)
# Now we have 21M OP_TRUE value in each Elements wallet.
e1.getwalletinfo()
e2.getwalletinfo()

######## WALLET ###########

Expand Down Expand Up @@ -419,12 +417,6 @@ def sync_all(e1, e2):
bitcoin.generate(101)
sync_all(e1, e2)

# We have to lock up some of the funds first. Regtest(what we're running) has all funds as OP_TRUE
# but this is not the case in testnet/production. Doesn't matter where we send it
# inside Bitcoin, this is just a hack to lock some funds up.
# We can immediately grab this output from the mempool on subsequent steps
e1.sendtomainchain(bitcoin.getnewaddress(), 50)

# Now we can actually start pegging in. Examine the pegin address fields
e1.getpeginaddress()
# Changes each time as it's a new sidechain address as well as new "tweak" for the watchmen keys
Expand All @@ -448,7 +440,7 @@ def sync_all(e1, e2):
raw = bitcoin.getrawtransaction(txid)

# Attempt claim!
claimtxid = e1.claimpegin(raw, proof, addrs["sidechain_address"])
claimtxid = e1.claimpegin(raw, proof, addrs["claim_script"])
sync_all(e1, e2)

# Other node should accept to mempool and mine
Expand Down
23 changes: 4 additions & 19 deletions contrib/assets_tutorial/assets_tutorial.sh
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,11 @@ e2-dae
# run the bitcoin node, but it is necessary for the two way peg parts of
# this tutorial.

# Prime the chain, see "immature balance" holds all funds until genesis is mature
e1-cli getwalletinfo

# Mining for now is OP_TRUE
e1-cli generate 101

# Now we have 21M OP_TRUE value
# We have 21M OP_TRUE value in each wallet
# This is useful for testing and non-sidechain applications of Elements
e1-cli getwalletinfo
e2-cli getwalletinfo

# Primed and ready

######## WALLET ###########


Expand Down Expand Up @@ -311,19 +304,11 @@ e2-dae $FEDPEGARG
e1-cli generate 101
b-cli generate 101

# We have to lock up some of the funds first. Regtest(what we're running) has all funds as OP_TRUE
# but this is not the case in testnet/production. Doesn't matter where we send it
# inside Bitcoin, this is just a hack to lock some funds up.
e1-cli sendtomainchain $(b-cli getnewaddress) 50

# Mature the pegout
e1-cli generate 101

# Now we can actually start pegging in. Examine the pegin address fields
e1-cli getpeginaddress
# Changes each time as it's a new sidechain address as well as new "tweak" for the watchmen keys
# mainchain_address : where you send your bitcoin from Bitcoin network
# sidechain_address : where the bitcoin will end up on the sidechain after pegging in
# claim_script: what script will have to be satisfied to spent the peg-in input

# Each call of this takes the pubkeys defined in the config file, adds a random number to them
# that is essetially the hash of the sidechain_address and other information,
Expand All @@ -335,7 +320,7 @@ e1-cli getpeginaddress
ADDRS=$(e1-cli getpeginaddress)

MAINCHAIN=$(echo $ADDRS | python3 -c "import sys, json; print(json.load(sys.stdin)['mainchain_address'])")
SIDECHAIN=$(echo $ADDRS | python3 -c "import sys, json; print(json.load(sys.stdin)['sidechain_address'])")
SIDECHAIN=$(echo $ADDRS | python3 -c "import sys, json; print(json.load(sys.stdin)['claim_script'])")

#Send funds to unique watchmen P2SH address
TXID=$(b-cli sendtoaddress $MAINCHAIN 1)
Expand Down
8 changes: 6 additions & 2 deletions qa/rpc-tests/confidential_transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,10 @@ def run_test(self):

print("Assets tests...")

# Bitcoin is the first issuance
assert_equal(self.nodes[0].listissuances()[0]["assetlabel"], "bitcoin")
assert_equal(len(self.nodes[0].listissuances()), 1)

# Unblinded issuance of asset
issued = self.nodes[0].issueasset(1, 1, False)
assert_equal(self.nodes[0].getwalletinfo()["balance"][issued["asset"]], 1)
Expand Down Expand Up @@ -351,13 +355,13 @@ def run_test(self):
addr2 = txdet2[len(txdet2)-1]["address"]
addr3 = txdet3[len(txdet3)-1]["address"]

assert_equal(len(self.nodes[0].listissuances()), 5);
assert_equal(len(self.nodes[0].listissuances()), 6);
self.nodes[0].importaddress(addr1)
self.nodes[0].importaddress(addr2)
self.nodes[0].importaddress(addr3)

issuances = self.nodes[0].listissuances()
assert_equal(len(issuances), 8)
assert_equal(len(issuances), 9)

for issue in issuances:
if issue['txid'] == redata1["txid"] and issue['vin'] == redata1["vin"]:
Expand Down
95 changes: 47 additions & 48 deletions qa/rpc-tests/pegging.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,22 @@
print(sys.argv[1])
print(sys.argv[2])

# Sync mempool, make a block, sync blocks
def sync_all(sidechain, sidechain2):
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.")
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

fedpeg_key="cPxqWyf1HDGpGFH1dnfjz8HbiWxvwG8WXyetbuAiw4thKXUdXLpR"
fedpeg_pubkey="512103dff4923d778550cc13ce0d887d737553b4b58f4e8e886507fc39f5e447b2186451ae"

Expand Down Expand Up @@ -88,7 +104,7 @@

try:

# Default is 8, meaning 8+2 confirms for mempool acceptance normally
# Default is 8, meaning 8+2 confirms for wallet acceptance normally
# this will require 10+2.
sidechain_args = " -peginconfirmationdepth=10 "

Expand Down Expand Up @@ -116,16 +132,8 @@

addr = bitcoin.getnewaddress()

# Lockup some funds to unlock later
sidechain.sendtomainchain(addr, 50)
# Tests withdrawlock tracking in database
sidechain.generate(1)
# Tests withdrawlock in mempool
sidechain.sendtomainchain(addr, 50)

addrs = sidechain.getpeginaddress()
txid1 = bitcoin.sendtoaddress(addrs["mainchain_address"], 24)
txid2 = bitcoin.sendtoaddress(addrs["mainchain_address"], 24)
# 10+2 confirms required to get into mempool and confirm
bitcoin.generate(11)
time.sleep(2)
Expand All @@ -137,59 +145,55 @@
pegtxid = sidechain.claimpegin(raw, proof)
raise Exception("Peg-in should not mature enough yet, need another block.")
except JSONRPCException as e:
assert("Withdraw proof validation failed" in e.error["message"])
assert("Peg-in Bitcoin transaction needs more confirmations to be sent." in e.error["message"])
pass

# Should fail due to non-matching wallet address
try:
pegtxid = sidechain.claimpegin(raw, proof, sidechain.getnewaddress())
raise Exception("Peg-in with non-matching address should fail.")
raise Exception("Peg-in with non-matching claim_script should fail.")
except JSONRPCException as e:
assert("Failed to find output in bitcoinTx to the mainchain_address" in e.error["message"])
assert("Given claim_script does not match the given Bitcoin transaction." in e.error["message"])
pass

# 12 confirms allows in mempool
bitcoin.generate(1)

timeout = 20
# Both should succeed via wallet lookup for address match, and when given
# Should succeed via wallet lookup for address match, and when given
pegtxid1 = sidechain.claimpegin(raw, proof)

proof = bitcoin.gettxoutproof([txid2])
raw = bitcoin.getrawtransaction(txid2)
pegtxid2 = sidechain.claimpegin(raw, proof, addrs["sidechain_address"])

while len(sidechain.getrawmempool()) != len(sidechain2.getrawmempool()):
time.sleep(1)
timeout -= 1
if timeout == 0:
raise Exception("Peg-in has failed to propagate.")
sidechain2.generate(1)
while sidechain.getblockcount() != sidechain2.getblockcount():
time.sleep(1)
timeout -= 1
if timeout == 0:
raise Exception("Blocks are not propagating.")

# Will invalidate the block that confirms this transaction later
blockhash = sync_all(sidechain, sidechain2)
sidechain.generate(5)

tx1 = sidechain.gettransaction(pegtxid1)
tx2 = sidechain.gettransaction(pegtxid2)

if "confirmations" in tx1 and tx1["confirmations"] > 0 and "confirmations" in tx2 and tx2["confirmations"] > 0:
if "confirmations" in tx1 and tx1["confirmations"] == 6:
print("Peg-in is confirmed: Success!")
else:
raise Exception("Peg-in confirmation has failed.")

# Make a few large locks, then do many claims in mempool
n_locks = 10
n_claims = 30
# Look at pegin fields
decoded = sidechain.decoderawtransaction(tx1["hex"])
assert decoded["vin"][0]["is_pegin"] == True
assert len(decoded["vin"][0]["pegin_witness"]) > 0

# 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 = 100

print("Flooding mempool with many small claims")
pegtxs = []
for i in range(n_locks):
# Lockup some funds to unlock later
sidechain.sendtomainchain(addr, 50)
sidechain.generate(1)
sidechain.generate(101)

for i in range(n_claims):
Expand All @@ -200,21 +204,16 @@
raw = bitcoin.getrawtransaction(txid)
pegtxs += [sidechain.claimpegin(raw, proof)]

sidechain.generate(1)
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("Success!")

# Testing sidechain info RPC
sideinfo = sidechain.getsidechaininfo()
assert sideinfo["fedpegscript"] == fedpeg_pubkey
assert sideinfo["pegged_asset"] == sidechain.dumpassetlabels()["bitcoin"]
assert sideinfo["min_peg_diff"] == "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
assert sideinfo["parent_blockhash"] == "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206"

except JSONRPCException as e:
print("Pegging testing failed, aborting:")
print(e.error)
Expand Down
16 changes: 6 additions & 10 deletions qa/rpc-tests/wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,15 @@ def setup_network(self, split=False):

def run_test (self):

# Check that there's no UTXO on none of the nodes
assert_equal(len(self.nodes[0].listunspent()), 0)
assert_equal(len(self.nodes[1].listunspent()), 0)
assert_equal(len(self.nodes[2].listunspent()), 0)
# Check that there's 100 UTXOs on each of the nodes
assert_equal(len(self.nodes[0].listunspent()), 100)
assert_equal(len(self.nodes[1].listunspent()), 100)
assert_equal(len(self.nodes[2].listunspent()), 100)

print("Mining blocks...")

self.nodes[0].generate(1)
walletinfo = self.nodes[0].getwalletinfo()
assert_equal(walletinfo['immature_balance']["bitcoin"], 21000000)
assert("bitcoin" not in walletinfo['balance'])
assert_equal(walletinfo['balance']["bitcoin"], 21000000)

self.sync_all()
print("Mining blocks...")
self.nodes[1].generate(101)
self.sync_all()

Expand Down
4 changes: 3 additions & 1 deletion src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,8 @@ libbitcoin_wallet_a_SOURCES = \
wallet/wallet.cpp \
wallet/walletdb.cpp \
policy/rbf.cpp \
primitives/bitcoin/merkleblock.cpp \
primitives/bitcoin/block.cpp \
$(BITCOIN_CORE_H)

# crypto primitives library
Expand Down Expand Up @@ -458,7 +460,7 @@ endif

libelementsconsensus_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined -version-info 1:0:0 $(RELDFLAGS)
libelementsconsensus_la_LIBADD = $(LIBSECP256K1)
libelementsconsensus_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(builddir)/obj -I$(srcdir)/secp256k1/include -DBUILD_BITCOIN_INTERNAL -DBITCOIN_SCRIPT_NO_CALLRPC
libelementsconsensus_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(builddir)/obj -I$(srcdir)/secp256k1/include -DBUILD_BITCOIN_INTERNAL
libelementsconsensus_la_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)

endif
Expand Down
2 changes: 1 addition & 1 deletion src/Makefile.test.include
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ BITCOIN_TESTS =\
test/hash_tests.cpp \
test/key_tests.cpp \
test/limitedmap_tests.cpp \
test/lockedutxo_tests.cpp \
test/withdrawspent_tests.cpp \
test/dbwrapper_tests.cpp \
test/main_tests.cpp \
Expand All @@ -110,6 +109,7 @@ BITCOIN_TESTS =\
test/multisig_tests.cpp \
test/net_tests.cpp \
test/netbase_tests.cpp \
test/pegin_witness_tests.cpp \
test/pmt_tests.cpp \
test/policyestimator_tests.cpp \
test/pow_tests.cpp \
Expand Down
4 changes: 2 additions & 2 deletions src/bench/mempool_eviction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ static void AddTx(const CTransaction& tx, const CAmount& nFee, CTxMemPool& pool)
unsigned int nHeight = 1;
bool spendsCoinbase = false;
unsigned int sigOpCost = 4;
std::set<std::pair<uint256, COutPoint> > setWithdrawsSpent;
std::set<std::pair<uint256, COutPoint> > setPeginsSpent;
LockPoints lp;
pool.addUnchecked(tx.GetHash(), CTxMemPoolEntry(
MakeTransactionRef(tx), nFee, nTime, dPriority, nHeight,
0, spendsCoinbase, sigOpCost, lp, setWithdrawsSpent));
0, spendsCoinbase, sigOpCost, lp, setPeginsSpent));
}

// Right now this is only testing eviction performance in an extremely small
Expand Down
2 changes: 1 addition & 1 deletion src/bench/verify_script.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ static void VerifyScriptBench(benchmark::State& state)
txCredit.vout[0].scriptPubKey,
&txSpend.wit.vtxinwit[0].scriptWitness,
flags,
MutableTransactionNoWithdrawsSignatureChecker(&txSpend, 0, txCredit.vout[0].nValue),
MutableTransactionSignatureChecker(&txSpend, 0, txCredit.vout[0].nValue),
&err);
assert(err == SCRIPT_ERR_OK);
assert(success);
Expand Down
Loading