Skip to content

Commit

Permalink
xbr testnet 3 (#1240)
Browse files Browse the repository at this point in the history
* update xbr abi
* update xbr simple seller
* bump version
  • Loading branch information
oberstet committed Aug 29, 2019
1 parent 36fb47b commit c23ebe5
Show file tree
Hide file tree
Showing 17 changed files with 23,291 additions and 32,884 deletions.
2 changes: 1 addition & 1 deletion autobahn/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@
#
###############################################################################

__version__ = u'19.9.1'
__version__ = u'19.9.2'
66 changes: 39 additions & 27 deletions autobahn/xbr/_buyer.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,19 +106,19 @@ def __init__(self, market_maker_adr, buyer_key, max_price):
# market maker address
self._market_maker_adr = market_maker_adr

# buyer raw ethereum private key (32 bytes)
# buyer delegate raw ethereum private key (32 bytes)
self._pkey_raw = buyer_key

# buyer ethereum private key object
# buyer delegate ethereum private key object
self._pkey = eth_keys.keys.PrivateKey(buyer_key)

# buyer ethereum private account from raw private key
# buyer delegate ethereum private account from raw private key
self._acct = Account.privateKeyToAccount(self._pkey)

# buyer ethereum account canonical address
# buyer delegate ethereum account canonical address
self._addr = self._pkey.public_key.to_canonical_address()

# buyer ethereum account canonical checksummed address
# buyer delegate ethereum account canonical checksummed address
self._caddr = web3.Web3.toChecksumAddress(self._addr)

# FIXME: ephemeral data consumer key
Expand Down Expand Up @@ -174,14 +174,16 @@ def start(self, session, consumer_id):
address=hl('0x' + self._acct.address),
public_key=binascii.b2a_hex(self._pkey.public_key[:10]).decode())

# get the currently active (if any) payment channel for the delegate
self._channel = yield session.call('xbr.marketmaker.get_active_payment_channel', self._addr)

# get the current (off-chain) balance of the payment channel
payment_balance = yield session.call('xbr.marketmaker.get_payment_channel_balance', self._channel['channel'])
try:
# get the currently active (if any) payment channel for the delegate
assert type(self._addr) == bytes and len(self._addr) == 20
self._channel = yield session.call('xbr.marketmaker.get_active_payment_channel', self._addr)

self.log.info('Delegate has current payment channel address {payment_channel_adr}',
payment_channel_adr=hl('0x' + binascii.b2a_hex(self._channel['channel']).decode()))
# get the current (off-chain) balance of the payment channel
payment_balance = yield session.call('xbr.marketmaker.get_payment_channel_balance', self._channel['channel'])
except:
session.leave()
raise

# FIXME
self._balance = payment_balance['remaining']
Expand All @@ -190,6 +192,10 @@ def start(self, session, consumer_id):

self._seq = payment_balance['seq']

self.log.info('Delegate has current payment channel address {payment_channel_adr} (remaining balance {remaining} at sequence {seq})',
payment_channel_adr=hl('0x' + binascii.b2a_hex(self._channel['channel']).decode()),
remaining=self._balance, seq=self._seq)

return self._balance

async def stop(self):
Expand Down Expand Up @@ -311,13 +317,10 @@ async def unwrap(self, key_id, serializer, ciphertext):
# call market maker to initiate closing of payment channel
await self._session.call('xbr.marketmaker.close_channel',
tx1.channel,
tx1.pubkey,
tx1.key_id,
tx1.channel_seq,
tx1.amount,
tx1.balance,
market_maker_signature=tx2.signature,
delegate_signature=tx1.signature)
tx2.signature,
tx1.signature)

# FIXME: wait for and acquire new payment channel instead of bailing out ..

Expand All @@ -335,24 +338,32 @@ async def unwrap(self, key_id, serializer, ciphertext):

buyer_pubkey = self._receive_key.public_key.encode(encoder=nacl.encoding.RawEncoder)
channel_seq = self._seq + 1
channel_adr = bytes(self._channel['channel'])

# XBRSIG[1/8]: compute EIP712 typed data signature
signature = sign_eip712_data(self._pkey_raw, buyer_pubkey, key_id, channel_seq, amount, balance)
signature = sign_eip712_data(self._pkey_raw, channel_adr, channel_seq, balance)

# persist 1st phase of the transaction locally
self._save_transaction_phase1(self._channel['channel'], self._addr, buyer_pubkey, key_id, channel_seq, amount, balance, signature)
self._save_transaction_phase1(channel_adr, self._addr, buyer_pubkey, key_id, channel_seq, amount, balance, signature)

# call the market maker to buy the key
try:
receipt = await self._session.call('xbr.marketmaker.buy_key',
self._addr,
buyer_pubkey,
key_id,
channel_adr,
channel_seq,
amount,
balance,
signature)
except ApplicationError as e:
if e.error == 'xbr.error.channel_closed':
self.stop()
raise e
except Exception as e:
self.log.error('Encountered error while calling market maker to buy key!')
self.log.failure()
self._keys[key_id] = e
raise e
else:
Expand All @@ -365,9 +376,7 @@ async def unwrap(self, key_id, serializer, ciphertext):
marketmaker_channel_seq = receipt['channel_seq']
marketmaker_remaining = receipt['remaining']

signer_address = recover_eip712_signer(self._market_maker_adr, buyer_pubkey, key_id,
marketmaker_channel_seq, marketmaker_amount_paid,
marketmaker_remaining, marketmaker_signature)
signer_address = recover_eip712_signer(channel_adr, marketmaker_channel_seq, marketmaker_remaining, marketmaker_signature)
if signer_address != self._market_maker_adr:
self.log.warn('{klass}.unwrap()::XBRSIG[8/8] - EIP712 signature invalid: signer_address={signer_address}, delegate_adr={delegate_adr}',
klass=self.__class__.__name__,
Expand All @@ -379,7 +388,7 @@ async def unwrap(self, key_id, serializer, ciphertext):
# FIXME: compare above to what we know locally

# persist 2nd phase of the transaction locally
self._save_transaction_phase2(self._channel['channel'], self._market_maker_adr, buyer_pubkey, key_id, marketmaker_channel_seq,
self._save_transaction_phase2(channel_adr, self._market_maker_adr, buyer_pubkey, key_id, marketmaker_channel_seq,
marketmaker_amount_paid, marketmaker_remaining, marketmaker_signature)

# unseal the data encryption key
Expand Down Expand Up @@ -512,10 +521,13 @@ def past_transactions(self, filter_complete=True, limit=1):
n = 0
res = []
while n < limit:
tx = self._transactions[-n]
if not filter_complete or (tx[0] and tx[1]):
res.append(tx)
n += 1
if len(self._transactions) > n:
tx = self._transactions[-n]
if not filter_complete or (tx[0] and tx[1]):
res.append(tx)
n += 1
else:
break
return res

def count_transactions(self):
Expand Down
48 changes: 37 additions & 11 deletions autobahn/xbr/_seller.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,16 @@ def _rotate(self):
yield self._on_rotate(self)


class PayingChannel(object):
def __init__(self, adr, seq, balance):
assert type(adr) == bytes and len(adr) == 20
assert type(seq) == int and seq > 0
assert type(balance) == int and balance >= 0
self._adr = adr
self._seq = seq
self._balance = balance


class SimpleSeller(object):
"""
Simple XBR seller component. This component can be used by a XBR seller delegate to
Expand Down Expand Up @@ -255,6 +265,8 @@ def __init__(self, market_maker_adr, seller_key, provider_id=None):
# seller provider ID
self._provider_id = provider_id or str(self._pkey.public_key)

self._channels = {}

# will be filled with on-chain payment channel contract, once started
self._channel = None

Expand Down Expand Up @@ -394,24 +406,28 @@ def start(self, session):
key_series.start()

# get the currently active (if any) paying channel for the delegate
self._channel = yield session.call('xbr.marketmaker.get_active_paying_channel', self._addr)
channel = yield session.call('xbr.marketmaker.get_active_paying_channel', self._addr)

# get the current (off-chain) balance of the paying channel
paying_balance = yield session.call('xbr.marketmaker.get_paying_channel_balance', self._channel['channel'])
paying_balance = yield session.call('xbr.marketmaker.get_paying_channel_balance', channel['channel'])
# FIXME
if type(paying_balance['remaining']) == bytes:
paying_balance['remaining'] = unpack_uint256(paying_balance['remaining'])

self.log.info('Delegate has currently active paying channel address {paying_channel_adr}',
paying_channel_adr=hl('0x' + binascii.b2a_hex(self._channel['channel']).decode()))
paying_channel_adr=hl('0x' + binascii.b2a_hex(channel['channel']).decode()))

self._channels[channel['channel']] = PayingChannel(channel['channel'], paying_balance['seq'], paying_balance['remaining'])
self._state = SimpleSeller.STATE_STARTED

# FIXME
self._channel = channel
self._balance = paying_balance['remaining']
if type(self._balance) == bytes:
self._balance = unpack_uint256(self._balance)

self._seq = paying_balance['seq']

self._state = SimpleSeller.STATE_STARTED

return self._balance
return paying_balance['remaining']

def stop(self):
"""
Expand Down Expand Up @@ -490,7 +506,7 @@ async def wrap(self, api_id, uri, payload):

return key_id, serializer, ciphertext

def sell(self, market_maker_adr, buyer_pubkey, key_id, channel_seq, amount, balance, signature, details=None):
def sell(self, market_maker_adr, buyer_pubkey, key_id, channel_adr, channel_seq, amount, balance, signature, details=None):
"""
Called by a XBR Market Maker to buy a data encyption key. The XBR Market Maker here is
acting for (triggered by) the XBR buyer delegate.
Expand All @@ -505,6 +521,9 @@ def sell(self, market_maker_adr, buyer_pubkey, key_id, channel_seq, amount, bala
:param key_id: The UUID of the data encryption key to buy.
:type key_id: bytes of length 16
:param channel_adr: The on-chain channel contract address.
:type channel_adr: bytes of length 20
:param channel_seq: Paying channel sequence off-chain transaction number.
:type channel_seq: int
Expand All @@ -517,7 +536,7 @@ def sell(self, market_maker_adr, buyer_pubkey, key_id, channel_seq, amount, bala
:param signature: Signature over the supplied buying information, using the Ethereum
private key of the market maker (which is the delegate of the marker operator).
:type signature: bytes
:type signature: bytes of length 65
:param details: Caller details. The call will come from the XBR Market Maker.
:type details: :class:`autobahn.wamp.types.CallDetails`
Expand All @@ -528,6 +547,7 @@ def sell(self, market_maker_adr, buyer_pubkey, key_id, channel_seq, amount, bala
assert type(market_maker_adr) == bytes and len(market_maker_adr) == 20, 'delegate_adr must be bytes[20]'
assert type(buyer_pubkey) == bytes and len(buyer_pubkey) == 32, 'buyer_pubkey must be bytes[32]'
assert type(key_id) == bytes and len(key_id) == 16, 'key_id must be bytes[16]'
assert type(channel_adr) == bytes and len(channel_adr) == 20, 'channel_adr must be bytes[20]'
assert type(channel_seq) == int, 'channel_seq must be int'
assert type(amount) == int and amount >= 0, 'amount_paid must be int (>= 0)'
assert type(balance) == int and balance >= 0, 'post_balance must be int (>= 0)'
Expand All @@ -544,6 +564,12 @@ def sell(self, market_maker_adr, buyer_pubkey, key_id, channel_seq, amount, bala
raise ApplicationError('crossbar.error.no_such_object', '{}.sell() - no key with ID "{}"'.format(self.__class__.__name__, key_id))
key_series = self._keys_map[key_id]

# FIXME: must be the currently active channel .. and we need to track all of these
if channel_adr != self._channel['channel']:
self._session.leave()
raise ApplicationError('xbr.error.unexpected_channel_adr',
'{}.sell() - unexpected paying channel address: expected 0x{}, but got 0x{}'.format(self.__class__.__name__, binascii.b2a_hex(self._channel['channel']).decode(), binascii.b2a_hex(channel_adr).decode()))

# channel sequence number: check we have consensus on off-chain channel state with peer (which is the market maker)
if channel_seq != self._seq + 1:
raise ApplicationError('xbr.error.unexpected_channel_seq',
Expand All @@ -555,7 +581,7 @@ def sell(self, market_maker_adr, buyer_pubkey, key_id, channel_seq, amount, bala
'{}.sell() - unexpected channel (after tx) balance: expected {}, but got {}'.format(self.__class__.__name__, self._balance - amount, balance))

# XBRSIG[4/8]: check the signature (over all input data for the buying of the key)
signer_address = xbr.recover_eip712_signer(market_maker_adr, buyer_pubkey, key_id, channel_seq, amount, balance, signature)
signer_address = xbr.recover_eip712_signer(channel_adr, channel_seq, balance, signature)
if signer_address != market_maker_adr:
self.log.warn('{klass}.sell()::XBRSIG[4/8] - EIP712 signature invalid: signer_address={signer_address}, delegate_adr={delegate_adr}',
klass=self.__class__.__name__,
Expand All @@ -574,7 +600,7 @@ def sell(self, market_maker_adr, buyer_pubkey, key_id, channel_seq, amount, bala
assert type(sealed_key) == bytes and len(sealed_key) == 80, '{}.sell() - unexpected sealed key computed (expected bytes[80]): {}'.format(self.__class__.__name__, sealed_key)

# XBRSIG[5/8]: compute EIP712 typed data signature
seller_signature = xbr.sign_eip712_data(self._pkey_raw, buyer_pubkey, key_id, self._seq, amount, self._balance)
seller_signature = xbr.sign_eip712_data(self._pkey_raw, self._channel['channel'], self._seq, self._balance)

receipt = {
# key ID that has been bought
Expand Down

0 comments on commit c23ebe5

Please sign in to comment.