You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

233 lines
9.0 KiB

#! /usr/bin/env python
'''Tests of Proof of discrete log equivalence commitments.'''
import os
import jmbitcoin as bitcoin
import binascii
import struct
import json
import pytest
import copy
from jmbase import get_log
from jmclient import load_test_config, jm_single, generate_podle,\
generate_podle_error_string, get_commitment_file, PoDLE,\
get_podle_commitments, add_external_commitments, update_commitments
from jmclient.podle import verify_all_NUMS, verify_podle, PoDLEError
from commontest import make_wallets
log = get_log()
def test_commitments_empty(setup_podle):
"""Ensure that empty commitments file
results in {}
"""
assert get_podle_commitments() == ([], {})
def test_commitment_retries(setup_podle):
"""Assumes no external commitments available.
Generate pretend priv/utxo pairs and check that they can be used
taker_utxo_retries times.
"""
allowed = jm_single().config.getint("POLICY", "taker_utxo_retries")
#make some pretend commitments
dummy_priv_utxo_pairs = [(bitcoin.sha256(os.urandom(10)),
bitcoin.sha256(os.urandom(10))+":0") for _ in range(10)]
#test a single commitment request of all 10
for x in dummy_priv_utxo_pairs:
p = generate_podle([x], allowed)
assert p
#At this point slot 0 has been taken by all 10.
for i in range(allowed-1):
p = generate_podle(dummy_priv_utxo_pairs[:1], allowed)
assert p
p = generate_podle(dummy_priv_utxo_pairs[:1], allowed)
assert p is None
def generate_single_podle_sig(priv, i):
"""Make a podle entry for key priv at index i, using a dummy utxo value.
This calls the underlying 'raw' code based on the class PoDLE, not the
library 'generate_podle' which intelligently searches and updates commitments.
"""
dummy_utxo = bitcoin.sha256(priv) + ":3"
podle = PoDLE(dummy_utxo, binascii.hexlify(priv).decode('ascii'))
r = podle.generate_podle(i)
return (r['P'], r['P2'], r['sig'],
r['e'], r['commit'])
def test_rand_commitments(setup_podle):
for i in range(20):
priv = os.urandom(32)
Pser, P2ser, s, e, commitment = generate_single_podle_sig(priv, 1 + i%5)
assert verify_podle(Pser, P2ser, s, e, commitment)
#tweak commitments to verify failure
tweaked = [x[::-1] for x in [Pser, P2ser, s, e, commitment]]
for i in range(5):
#Check failure on garbling of each parameter
y = [Pser, P2ser, s, e, commitment]
y[i] = tweaked[i]
fail = False
try:
fail = verify_podle(*y)
except:
pass
finally:
assert not fail
def test_nums_verify(setup_podle):
"""Check that the NUMS precomputed values are
valid according to the code; assertion check
implicit.
"""
verify_all_NUMS(True)
def test_external_commitments(setup_podle):
"""Add this generated commitment to the external list
{txid:N:{'P':pubkey, 'reveal':{1:{'P2':P2,'s':s,'e':e}, 2:{..},..}}}
Note we do this *after* the sendpayment test so that the external
commitments will not erroneously used (they are fake).
"""
#ensure the file exists even if empty
update_commitments()
ecs = {}
tries = jm_single().config.getint("POLICY","taker_utxo_retries")
for i in range(10):
priv = os.urandom(32)
dummy_utxo = bitcoin.sha256(priv)+":2"
ecs[dummy_utxo] = {}
ecs[dummy_utxo]['reveal']={}
for j in range(tries):
P, P2, s, e, commit = generate_single_podle_sig(priv, j)
if 'P' not in ecs[dummy_utxo]:
ecs[dummy_utxo]['P']=P
ecs[dummy_utxo]['reveal'][j] = {'P2':P2, 's':s, 'e':e}
add_external_commitments(ecs)
used, external = get_podle_commitments()
for u in external:
assert external[u]['P'] == ecs[u]['P']
for i in range(tries):
for x in ['P2', 's', 'e']:
assert external[u]['reveal'][str(i)][x] == ecs[u]['reveal'][i][x]
#add a dummy used commitment, then try again
update_commitments(commitment="ab"*32)
ecs = {}
known_commits = []
known_utxos = []
tries = 3
for i in range(1, 6):
u = binascii.hexlify(struct.pack(b'B', i)*32).decode('ascii')
known_utxos.append(u)
priv = struct.pack(b'B', i)*32+b"\x01"
ecs[u] = {}
ecs[u]['reveal']={}
for j in range(tries):
P, P2, s, e, commit = generate_single_podle_sig(priv, j)
known_commits.append(commit)
if 'P' not in ecs[u]:
ecs[u]['P'] = P
ecs[u]['reveal'][j] = {'P2':P2, 's':s, 'e':e}
add_external_commitments(ecs)
#simulate most of those external being already used
for c in known_commits[:-1]:
update_commitments(commitment=c)
#this should find the remaining one utxo and return from it
assert generate_podle([], max_tries=tries, allow_external=known_utxos)
#test commitment removal
to_remove = ecs[binascii.hexlify(struct.pack(b'B', 3)*32).decode('ascii')]
update_commitments(external_to_remove={binascii.hexlify(struct.pack(b'B', 3)*32).decode('ascii'):to_remove})
#test that an incorrectly formatted file raises
with open(get_commitment_file(), "rb") as f:
validjson = json.loads(f.read().decode('utf-8'))
corruptjson = copy.deepcopy(validjson)
del corruptjson['used']
with open(get_commitment_file(), "wb") as f:
f.write(json.dumps(corruptjson, indent=4).encode('utf-8'))
with pytest.raises(PoDLEError) as e_info:
get_podle_commitments()
#clean up
with open(get_commitment_file(), "wb") as f:
f.write(json.dumps(validjson, indent=4).encode('utf-8'))
def test_podle_constructor(setup_podle):
"""Tests rules about construction of PoDLE object
are conformed to.
"""
priv = "aa"*32
#pub and priv together not allowed
with pytest.raises(PoDLEError) as e_info:
p = PoDLE(priv=priv, P="dummypub")
#no pub or priv is allowed, i forget if this is useful for something
p = PoDLE()
#create from priv
p = PoDLE(priv=priv+"01", u="dummyutxo")
pdict = p.generate_podle(2)
assert all([k in pdict for k in ['used', 'utxo', 'P', 'P2', 'commit', 'sig', 'e']])
#using the valid data, serialize/deserialize test
deser = p.deserialize_revelation(p.serialize_revelation())
assert all([deser[x] == pdict[x] for x in ['utxo', 'P', 'P2', 'sig', 'e']])
#deserialization must fail for wrong number of items
with pytest.raises(PoDLEError) as e_info:
p.deserialize_revelation(':'.join([str(x) for x in range(4)]), separator=':')
#reveal() must work without pre-generated commitment
p.commitment = None
pdict2 = p.reveal()
assert pdict2 == pdict
#corrupt P2, cannot commit:
p.P2 = "blah"
with pytest.raises(PoDLEError) as e_info:
p.get_commitment()
#generation fails without a utxo
p = PoDLE(priv=priv)
with pytest.raises(PoDLEError) as e_info:
p.generate_podle(0)
#Test construction from pubkey
pub = bitcoin.privkey_to_pubkey(priv+"01")
p = PoDLE(P=pub)
with pytest.raises(PoDLEError) as e_info:
p.get_commitment()
with pytest.raises(PoDLEError) as e_info:
p.verify("dummycommitment", range(3))
def test_podle_error_string(setup_podle):
priv_utxo_pairs = [('fakepriv1', 'fakeutxo1'),
('fakepriv2', 'fakeutxo2')]
to = ['tooold1', 'tooold2']
ts = ['toosmall1', 'toosmall2']
wallet_service = make_wallets(1, [[1, 0, 0, 0, 0]])[0]['wallet']
cjamt = 100
tua = "3"
tuamtper = "20"
errmgsheader, errmsg = generate_podle_error_string(priv_utxo_pairs,
to,
ts,
wallet_service,
cjamt,
tua,
tuamtper)
assert errmgsheader == ("Failed to source a commitment; this debugging information"
" may help:\n\n")
y = [x[1] for x in priv_utxo_pairs]
assert all([errmsg.find(x) != -1 for x in to + ts + y])
#ensure OK with nothing
errmgsheader, errmsg = generate_podle_error_string([], [], [], wallet_service,
cjamt, tua, tuamtper)
@pytest.fixture(scope="module")
def setup_podle(request):
load_test_config()
if not os.path.exists("cmtdata"):
os.mkdir("cmtdata")
prev_commits = False
#back up any existing commitments
pcf = get_commitment_file()
log.debug("Podle file: " + pcf)
if os.path.exists(pcf):
os.rename(pcf, pcf + ".bak")
prev_commits = True
def teardown():
if prev_commits:
os.rename(pcf + ".bak", pcf)
else:
if os.path.exists(pcf):
os.remove(pcf)
request.addfinalizer(teardown)