mirror of
https://github.com/bigchaindb/bigchaindb.git
synced 2024-10-13 13:34:05 +00:00
Merge pull request #211 from bigchaindb/feat/127/crypto-conditions-ilp-bigchain-integration-hashlock
Feat/127/crypto conditions ilp bigchain integration hashlock
This commit is contained in:
commit
bbdc7c86fc
@ -203,11 +203,20 @@ def create_tx(current_owners, new_owners, inputs, operation, payload=None):
|
||||
},
|
||||
}
|
||||
"""
|
||||
# validate arguments (owners and inputs should be lists)
|
||||
# validate arguments (owners and inputs should be lists or None)
|
||||
|
||||
# The None case appears on fulfilling a hashlock
|
||||
if current_owners is None:
|
||||
current_owners = []
|
||||
if not isinstance(current_owners, list):
|
||||
current_owners = [current_owners]
|
||||
|
||||
# The None case appears on assigning a hashlock
|
||||
if new_owners is None:
|
||||
new_owners = []
|
||||
if not isinstance(new_owners, list):
|
||||
new_owners = [new_owners]
|
||||
|
||||
if not isinstance(inputs, list):
|
||||
inputs = [inputs]
|
||||
|
||||
@ -247,20 +256,30 @@ def create_tx(current_owners, new_owners, inputs, operation, payload=None):
|
||||
# handle outputs
|
||||
conditions = []
|
||||
for fulfillment in fulfillments:
|
||||
|
||||
# threshold condition
|
||||
if len(new_owners) > 1:
|
||||
condition = cc.ThresholdSha256Fulfillment(threshold=len(new_owners))
|
||||
for new_owner in new_owners:
|
||||
condition.add_subfulfillment(cc.Ed25519Fulfillment(public_key=new_owner))
|
||||
|
||||
# simple signature condition
|
||||
elif len(new_owners) == 1:
|
||||
condition = cc.Ed25519Fulfillment(public_key=new_owners[0])
|
||||
conditions.append({
|
||||
'new_owners': new_owners,
|
||||
'condition': {
|
||||
'details': json.loads(condition.serialize_json()),
|
||||
'uri': condition.condition.serialize_uri()
|
||||
},
|
||||
'cid': fulfillment['fid']
|
||||
})
|
||||
|
||||
# to be added later (hashlock conditions)
|
||||
else:
|
||||
condition = None
|
||||
|
||||
if condition:
|
||||
conditions.append({
|
||||
'new_owners': new_owners,
|
||||
'condition': {
|
||||
'details': json.loads(condition.serialize_json()),
|
||||
'uri': condition.condition.serialize_uri()
|
||||
},
|
||||
'cid': fulfillment['fid']
|
||||
})
|
||||
|
||||
tx = {
|
||||
'fulfillments': fulfillments,
|
||||
|
@ -746,3 +746,142 @@ threshold_tx_transfer
|
||||
"version":1
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Hash-locked Conditions
|
||||
|
||||
By creating a hash of a difficult-to-guess 256-bit random or pseudo-random integer it is possible to create a condition which the creator can trivially fulfill by publishing the random value. However, for anyone else, the condition is cryptographically hard to fulfill, because they would have to find a preimage for the given condition hash.
|
||||
|
||||
One possible usecase might be to redeem a digital voucher when given a secret (voucher code).
|
||||
|
||||
```python
|
||||
# Create a hash-locked asset without any new_owners
|
||||
hashlock_tx = b.create_transaction(b.me, None, None, 'CREATE')
|
||||
|
||||
# Define a secret that will be hashed - fulfillments need to guess the secret
|
||||
secret = b'much secret! wow!'
|
||||
first_tx_condition = cc.PreimageSha256Fulfillment(preimage=secret)
|
||||
|
||||
# The conditions list is empty, so we need to append a new condition
|
||||
hashlock_tx['transaction']['conditions'].append({
|
||||
'condition': {
|
||||
'uri': first_tx_condition.condition.serialize_uri()
|
||||
},
|
||||
'cid': 0,
|
||||
'new_owners': None
|
||||
})
|
||||
|
||||
# Conditions have been updated, so hash needs updating
|
||||
hashlock_tx['id'] = util.get_hash_data(hashlock_tx)
|
||||
|
||||
# The asset needs to be signed by the current_owner
|
||||
hashlock_tx_signed = b.sign_transaction(hashlock_tx, b.me_private)
|
||||
|
||||
# Some validations
|
||||
assert b.validate_transaction(hashlock_tx_signed) == hashlock_tx_signed
|
||||
assert b.is_valid_transaction(hashlock_tx_signed) == hashlock_tx_signed
|
||||
|
||||
b.write_transaction(hashlock_tx_signed)
|
||||
hashlock_tx_signed
|
||||
```
|
||||
|
||||
```python
|
||||
{
|
||||
"assignee":"FmLm6MxCABc8TsiZKdeYaZKo5yZWMM6Vty7Q1B6EgcP2",
|
||||
"id":"604c520244b7ff63604527baf269e0cbfb887122f503703120fd347d6b99a237",
|
||||
"transaction":{
|
||||
"conditions":[
|
||||
{
|
||||
"cid":0,
|
||||
"condition":{
|
||||
"uri":"cc:0:3:nsW2IiYgk9EUtsg4uBe3pBnOgRoAEX2IIsPgjqZz47U:17"
|
||||
},
|
||||
"new_owners":None
|
||||
}
|
||||
],
|
||||
"data":None,
|
||||
"fulfillments":[
|
||||
{
|
||||
"current_owners":[
|
||||
"FmLm6MxCABc8TsiZKdeYaZKo5yZWMM6Vty7Q1B6EgcP2"
|
||||
],
|
||||
"fid":0,
|
||||
"fulfillment":"cf:4:21-D-LfNhIQhvY5914ArFTUGpgPKc7EVC1ZtJqqOTHGx1p9FuRr9tRfkbdqtX2MZWh7sRVUmMnwp7I1-xZbCnCkeADf69IwDHbZvNS6aTr1CpekREsV9ZG8m_wjlZiUN",
|
||||
"input":None
|
||||
}
|
||||
],
|
||||
"operation":"CREATE",
|
||||
"timestamp":"1461250387.910102"
|
||||
},
|
||||
"version":1
|
||||
}
|
||||
```
|
||||
|
||||
In order to redeem the asset, one needs to create a fulfillment the correct secret as a preimage:
|
||||
|
||||
```python
|
||||
hashlockuser_priv, hashlockuser_pub = crypto.generate_key_pair()
|
||||
|
||||
# create hashlock fulfillment tx
|
||||
hashlock_fulfill_tx = b.create_transaction(None, hashlockuser_pub, {'txid': hashlock_tx['id'], 'cid': 0}, 'TRANSFER')
|
||||
|
||||
# provide a wrong secret
|
||||
hashlock_fulfill_tx_fulfillment = cc.PreimageSha256Fulfillment(preimage=b'')
|
||||
hashlock_fulfill_tx['transaction']['fulfillments'][0]['fulfillment'] = \
|
||||
hashlock_fulfill_tx_fulfillment.serialize_uri()
|
||||
|
||||
assert b.is_valid_transaction(hashlock_fulfill_tx) == False
|
||||
|
||||
# provide the right secret
|
||||
hashlock_fulfill_tx_fulfillment = cc.PreimageSha256Fulfillment(preimage=secret)
|
||||
hashlock_fulfill_tx['transaction']['fulfillments'][0]['fulfillment'] = \
|
||||
hashlock_fulfill_tx_fulfillment.serialize_uri()
|
||||
|
||||
assert b.validate_transaction(hashlock_fulfill_tx) == hashlock_fulfill_tx
|
||||
assert b.is_valid_transaction(hashlock_fulfill_tx) == hashlock_fulfill_tx
|
||||
|
||||
b.write_transaction(hashlock_fulfill_tx)
|
||||
hashlock_fulfill_tx
|
||||
```
|
||||
|
||||
```python
|
||||
{
|
||||
"assignee":"FmLm6MxCABc8TsiZKdeYaZKo5yZWMM6Vty7Q1B6EgcP2",
|
||||
"id":"fe6871bf3ca62eb61c52c5555cec2e07af51df817723f0cb76e5cf6248f449d2",
|
||||
"transaction":{
|
||||
"conditions":[
|
||||
{
|
||||
"cid":0,
|
||||
"condition":{
|
||||
"details":{
|
||||
"bitmask":32,
|
||||
"public_key":"EiqCKxnBCmmNb83qyGch48tULK9RLaEt4xFA43UVCVDb",
|
||||
"signature":None,
|
||||
"type":"fulfillment",
|
||||
"type_id":4
|
||||
},
|
||||
"uri":"cc:4:20:y9884Md2YI_wdnGSTJGhwvFaNsKLe8sqwimqk-2JLSI:96"
|
||||
},
|
||||
"new_owners":[
|
||||
"EiqCKxnBCmmNb83qyGch48tULK9RLaEt4xFA43UVCVDb"
|
||||
]
|
||||
}
|
||||
],
|
||||
"data":None,
|
||||
"fulfillments":[
|
||||
{
|
||||
"current_owners":[],
|
||||
"fid":0,
|
||||
"fulfillment":"cf:0:bXVjaCBzZWNyZXQhIHdvdyE",
|
||||
"input":{
|
||||
"cid":0,
|
||||
"txid":"604c520244b7ff63604527baf269e0cbfb887122f503703120fd347d6b99a237"
|
||||
}
|
||||
}
|
||||
],
|
||||
"operation":"TRANSFER",
|
||||
"timestamp":"1461250397.944510"
|
||||
},
|
||||
"version":1
|
||||
}
|
||||
```
|
@ -1544,6 +1544,136 @@ class TestCryptoconditions(object):
|
||||
|
||||
assert b.verify_signature(tx_transfer_signed) is True
|
||||
|
||||
def test_create_asset_with_hashlock_condition(self, b):
|
||||
hashlock_tx = b.create_transaction(b.me, None, None, 'CREATE')
|
||||
|
||||
secret = b'much secret! wow!'
|
||||
first_tx_condition = cc.PreimageSha256Fulfillment(preimage=secret)
|
||||
|
||||
hashlock_tx['transaction']['conditions'].append({
|
||||
'condition': {
|
||||
'details': json.loads(first_tx_condition.serialize_json()),
|
||||
'uri': first_tx_condition.condition.serialize_uri()
|
||||
},
|
||||
'cid': 0,
|
||||
'new_owners': None
|
||||
})
|
||||
# conditions have been updated, so hash needs updating
|
||||
hashlock_tx['id'] = util.get_hash_data(hashlock_tx)
|
||||
|
||||
hashlock_tx_signed = b.sign_transaction(hashlock_tx, b.me_private)
|
||||
|
||||
assert b.validate_transaction(hashlock_tx_signed) == hashlock_tx_signed
|
||||
assert b.is_valid_transaction(hashlock_tx_signed) == hashlock_tx_signed
|
||||
|
||||
b.write_transaction(hashlock_tx_signed)
|
||||
|
||||
# create and write block to bigchain
|
||||
block = b.create_block([hashlock_tx_signed])
|
||||
b.write_block(block, durability='hard')
|
||||
|
||||
@pytest.mark.usefixtures('inputs')
|
||||
def test_transfer_asset_with_hashlock_condition(self, b, user_vk, user_sk):
|
||||
first_input_tx = b.get_owned_ids(user_vk).pop()
|
||||
|
||||
hashlock_tx = b.create_transaction(user_vk, None, first_input_tx, 'TRANSFER')
|
||||
|
||||
secret = b'much secret! wow!'
|
||||
first_tx_condition = cc.PreimageSha256Fulfillment(preimage=secret)
|
||||
|
||||
hashlock_tx['transaction']['conditions'].append({
|
||||
'condition': {
|
||||
'details': json.loads(first_tx_condition.serialize_json()),
|
||||
'uri': first_tx_condition.condition.serialize_uri()
|
||||
},
|
||||
'cid': 0,
|
||||
'new_owners': None
|
||||
})
|
||||
# conditions have been updated, so hash needs updating
|
||||
hashlock_tx['id'] = util.get_hash_data(hashlock_tx)
|
||||
|
||||
hashlock_tx_signed = b.sign_transaction(hashlock_tx, user_sk)
|
||||
|
||||
assert b.validate_transaction(hashlock_tx_signed) == hashlock_tx_signed
|
||||
assert b.is_valid_transaction(hashlock_tx_signed) == hashlock_tx_signed
|
||||
assert len(b.get_owned_ids(user_vk)) == 1
|
||||
|
||||
b.write_transaction(hashlock_tx_signed)
|
||||
|
||||
# create and write block to bigchain
|
||||
block = b.create_block([hashlock_tx_signed])
|
||||
b.write_block(block, durability='hard')
|
||||
|
||||
assert len(b.get_owned_ids(user_vk)) == 0
|
||||
|
||||
def test_create_and_fulfill_asset_with_hashlock_condition(self, b, user_vk):
|
||||
hashlock_tx = b.create_transaction(b.me, None, None, 'CREATE')
|
||||
|
||||
secret = b'much secret! wow!'
|
||||
first_tx_condition = cc.PreimageSha256Fulfillment(preimage=secret)
|
||||
|
||||
hashlock_tx['transaction']['conditions'].append({
|
||||
'condition': {
|
||||
'details': json.loads(first_tx_condition.serialize_json()),
|
||||
'uri': first_tx_condition.condition.serialize_uri()
|
||||
},
|
||||
'cid': 0,
|
||||
'new_owners': None
|
||||
})
|
||||
# conditions have been updated, so hash needs updating
|
||||
hashlock_tx['id'] = util.get_hash_data(hashlock_tx)
|
||||
|
||||
hashlock_tx_signed = b.sign_transaction(hashlock_tx, b.me_private)
|
||||
|
||||
assert b.validate_transaction(hashlock_tx_signed) == hashlock_tx_signed
|
||||
assert b.is_valid_transaction(hashlock_tx_signed) == hashlock_tx_signed
|
||||
|
||||
b.write_transaction(hashlock_tx_signed)
|
||||
|
||||
# create and write block to bigchain
|
||||
block = b.create_block([hashlock_tx_signed])
|
||||
b.write_block(block, durability='hard')
|
||||
|
||||
assert len(b.get_owned_ids(b.me)) == 0
|
||||
|
||||
# create hashlock fulfillment tx
|
||||
hashlock_fulfill_tx = b.create_transaction(None, user_vk, {'txid': hashlock_tx['id'], 'cid': 0}, 'TRANSFER')
|
||||
|
||||
hashlock_fulfill_tx_fulfillment = cc.PreimageSha256Fulfillment(preimage=b'')
|
||||
hashlock_fulfill_tx['transaction']['fulfillments'][0]['fulfillment'] = \
|
||||
hashlock_fulfill_tx_fulfillment.serialize_uri()
|
||||
|
||||
with pytest.raises(exceptions.InvalidSignature):
|
||||
b.validate_transaction(hashlock_fulfill_tx)
|
||||
assert b.is_valid_transaction(hashlock_fulfill_tx) == False
|
||||
|
||||
hashlock_fulfill_tx_fulfillment = cc.PreimageSha256Fulfillment(preimage=secret)
|
||||
hashlock_fulfill_tx['transaction']['fulfillments'][0]['fulfillment'] = \
|
||||
hashlock_fulfill_tx_fulfillment.serialize_uri()
|
||||
|
||||
assert b.validate_transaction(hashlock_fulfill_tx) == hashlock_fulfill_tx
|
||||
assert b.is_valid_transaction(hashlock_fulfill_tx) == hashlock_fulfill_tx
|
||||
|
||||
b.write_transaction(hashlock_fulfill_tx)
|
||||
|
||||
# create and write block to bigchain
|
||||
block = b.create_block([hashlock_fulfill_tx])
|
||||
b.write_block(block, durability='hard')
|
||||
|
||||
assert len(b.get_owned_ids(b.me)) == 0
|
||||
assert len(b.get_owned_ids(user_vk)) == 1
|
||||
|
||||
# try doublespending
|
||||
user2_sk, user2_vk = crypto.generate_key_pair()
|
||||
hashlock_doublespend_tx = b.create_transaction(None, user2_vk, {'txid': hashlock_tx['id'], 'cid': 0}, 'TRANSFER')
|
||||
|
||||
hashlock_doublespend_tx_fulfillment = cc.PreimageSha256Fulfillment(preimage=secret)
|
||||
hashlock_doublespend_tx['transaction']['fulfillments'][0]['fulfillment'] = \
|
||||
hashlock_doublespend_tx_fulfillment.serialize_uri()
|
||||
|
||||
with pytest.raises(exceptions.DoubleSpend):
|
||||
b.validate_transaction(hashlock_doublespend_tx)
|
||||
|
||||
def test_get_subcondition_from_vk(self, b, user_sk, user_vk):
|
||||
user2_sk, user2_vk = crypto.generate_key_pair()
|
||||
user3_sk, user3_vk = crypto.generate_key_pair()
|
||||
|
@ -233,4 +233,60 @@ b.write_transaction(threshold_tx_transfer)
|
||||
|
||||
print(json.dumps(threshold_tx_transfer, sort_keys=True, indent=4, separators=(',', ':')))
|
||||
|
||||
"""
|
||||
Hashlocked Conditions
|
||||
"""
|
||||
|
||||
# Create a hash-locked asset without any new_owners
|
||||
hashlock_tx = b.create_transaction(b.me, None, None, 'CREATE')
|
||||
|
||||
# Define a secret that will be hashed - fulfillments need to guess the secret
|
||||
secret = b'much secret! wow!'
|
||||
first_tx_condition = cc.PreimageSha256Fulfillment(preimage=secret)
|
||||
|
||||
# The conditions list is empty, so we need to append a new condition
|
||||
hashlock_tx['transaction']['conditions'].append({
|
||||
'condition': {
|
||||
'uri': first_tx_condition.condition.serialize_uri()
|
||||
},
|
||||
'cid': 0,
|
||||
'new_owners': None
|
||||
})
|
||||
|
||||
# Conditions have been updated, so hash needs updating
|
||||
hashlock_tx['id'] = util.get_hash_data(hashlock_tx)
|
||||
|
||||
# The asset needs to be signed by the current_owner
|
||||
hashlock_tx_signed = b.sign_transaction(hashlock_tx, b.me_private)
|
||||
|
||||
# Some validations
|
||||
assert b.validate_transaction(hashlock_tx_signed) == hashlock_tx_signed
|
||||
assert b.is_valid_transaction(hashlock_tx_signed) == hashlock_tx_signed
|
||||
|
||||
b.write_transaction(hashlock_tx_signed)
|
||||
print(json.dumps(hashlock_tx_signed, sort_keys=True, indent=4, separators=(',', ':')))
|
||||
|
||||
sleep(10)
|
||||
|
||||
hashlockuser_priv, hashlockuser_pub = crypto.generate_key_pair()
|
||||
|
||||
# create hashlock fulfillment tx
|
||||
hashlock_fulfill_tx = b.create_transaction(None, hashlockuser_priv, {'txid': hashlock_tx['id'], 'cid': 0}, 'TRANSFER')
|
||||
|
||||
# try a wrong secret
|
||||
hashlock_fulfill_tx_fulfillment = cc.PreimageSha256Fulfillment(preimage=b'')
|
||||
hashlock_fulfill_tx['transaction']['fulfillments'][0]['fulfillment'] = \
|
||||
hashlock_fulfill_tx_fulfillment.serialize_uri()
|
||||
|
||||
assert b.is_valid_transaction(hashlock_fulfill_tx) == False
|
||||
|
||||
# provide the right secret
|
||||
hashlock_fulfill_tx_fulfillment = cc.PreimageSha256Fulfillment(preimage=secret)
|
||||
hashlock_fulfill_tx['transaction']['fulfillments'][0]['fulfillment'] = \
|
||||
hashlock_fulfill_tx_fulfillment.serialize_uri()
|
||||
|
||||
assert b.validate_transaction(hashlock_fulfill_tx) == hashlock_fulfill_tx
|
||||
assert b.is_valid_transaction(hashlock_fulfill_tx) == hashlock_fulfill_tx
|
||||
|
||||
b.write_transaction(hashlock_fulfill_tx)
|
||||
print(json.dumps(hashlock_fulfill_tx, sort_keys=True, indent=4, separators=(',', ':')))
|
||||
|
Loading…
x
Reference in New Issue
Block a user