Skip to content

Commit

Permalink
sync (#1243)
Browse files Browse the repository at this point in the history
* release 19.9.3
  • Loading branch information
oberstet committed Sep 9, 2019
1 parent d4a2b7b commit 55c1eea
Show file tree
Hide file tree
Showing 18 changed files with 6,879 additions and 6,834 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.2'
__version__ = u'19.9.3'
4 changes: 3 additions & 1 deletion autobahn/xbr/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def process_type(type_str):
from autobahn.xbr._buyer import SimpleBuyer
from autobahn.xbr._seller import SimpleSeller, KeySeries
from autobahn.xbr._interfaces import IMarketMaker, IProvider, IConsumer, ISeller, IBuyer
from autobahn.xbr._util import sign_eip712_data, recover_eip712_signer
from autobahn.xbr._util import sign_eip712_data, recover_eip712_signer, pack_uint256, unpack_uint256


xbrtoken = None
Expand Down Expand Up @@ -164,6 +164,8 @@ class ActorType(object):
'xbrnetwork',
'sign_eip712_data',
'recover_eip712_signer',
'pack_uint256',
'unpack_uint256',

'MemberLevel',
'ActorType',
Expand Down
56 changes: 31 additions & 25 deletions autobahn/xbr/_buyer.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
import txaio
from autobahn.wamp.exception import ApplicationError
from autobahn.wamp.protocol import ApplicationSession
from ._util import unpack_uint256
from ._util import unpack_uint256, pack_uint256

import web3
import eth_keys
Expand Down Expand Up @@ -118,7 +118,7 @@ def __init__(self, market_maker_adr, buyer_key, max_price):
# buyer delegate ethereum account canonical checksummed address
self._caddr = web3.Web3.toChecksumAddress(self._addr)

# FIXME: ephemeral data consumer key
# ephemeral data consumer key
self._receive_key = nacl.public.PrivateKey.generate()

# maximum price per key we are willing to pay
Expand Down Expand Up @@ -291,12 +291,12 @@ async def unwrap(self, key_id, serializer, ciphertext):
# get (current) price for key we want to buy
quote = await self._session.call('xbr.marketmaker.get_quote', key_id)

if quote['price'] > self._max_price:
raise ApplicationError('xbr.error.max_price_exceeded',
'{}.unwrap() - key {} needed cannot be bought: price {} exceeds maximum price of {}'.format(self.__class__.__name__, uuid.UUID(bytes=key_id), int(quote['price'] / 10 ** 18), int(self._max_price / 10 ** 18)))

# set price we pay set to the (current) quoted price
amount = quote['price']
amount = unpack_uint256(quote['price'])

if amount > self._max_price:
raise ApplicationError('xbr.error.max_price_exceeded',
'{}.unwrap() - key {} needed cannot be bought: price {} exceeds maximum price of {}'.format(self.__class__.__name__, uuid.UUID(bytes=key_id), int(amount / 10 ** 18), int(self._max_price / 10 ** 18)))

# check (locally) we have enough balance left in the payment channel to buy the key
balance = self._balance - amount
Expand All @@ -323,8 +323,14 @@ async def unwrap(self, key_id, serializer, ciphertext):

signature = sign_eip712_data(self._pkey_raw, close_adr, close_seq, close_balance, close_is_final)

self.log.info('auto-closing payment channel {close_adr} [close_seq={close_seq}, close_balance={close_balance}, close_is_final={close_is_final}]',
close_adr=binascii.b2a_hex(close_adr).decode(),
close_seq=close_seq,
close_balance=int(close_balance / 10**18),
close_is_final=close_is_final)

# call market maker to initiate closing of payment channel
await self._session.call('xbr.marketmaker.close_channel', close_adr, close_seq, close_balance, close_is_final, signature)
await self._session.call('xbr.marketmaker.close_channel', close_adr, close_seq, pack_uint256(close_balance), close_is_final, signature)

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

Expand All @@ -333,12 +339,11 @@ async def unwrap(self, key_id, serializer, ciphertext):
uuid.UUID(bytes=key_id),
binascii.b2a_hex(close_adr).decode(),
int(close_balance / 10 ** 18)))
else:
raise ApplicationError('xbr.error.insufficient_balance',
'{}.unwrap() - key {} cannot be bought: insufficient balance {} in payment channel for amount {}'.format(self.__class__.__name__,
uuid.UUID(bytes=key_id),
int(self._balance / 10 ** 18),
int(amount / 10 ** 18)))
raise ApplicationError('xbr.error.insufficient_balance',
'{}.unwrap() - key {} cannot be bought: insufficient balance {} in payment channel for amount {}'.format(self.__class__.__name__,
uuid.UUID(bytes=key_id),
int(self._balance / 10 ** 18),
int(amount / 10 ** 18)))

buyer_pubkey = self._receive_key.public_key.encode(encoder=nacl.encoding.RawEncoder)
channel_seq = self._seq + 1
Expand All @@ -357,8 +362,8 @@ async def unwrap(self, key_id, serializer, ciphertext):
key_id,
channel_adr,
channel_seq,
amount,
balance,
pack_uint256(amount),
pack_uint256(balance),
signature)
except ApplicationError as e:
if e.error == 'xbr.error.channel_closed':
Expand All @@ -372,9 +377,10 @@ async def unwrap(self, key_id, serializer, ciphertext):

# XBRSIG[8/8]: check market maker signature
marketmaker_signature = receipt['signature']
marketmaker_amount_paid = receipt['amount_paid']
marketmaker_channel_seq = receipt['channel_seq']
marketmaker_remaining = receipt['remaining']
marketmaker_amount_paid = unpack_uint256(receipt['amount_paid'])
marketmaker_remaining = unpack_uint256(receipt['remaining'])
marketmaker_inflight = unpack_uint256(receipt['inflight'])

signer_address = recover_eip712_signer(channel_adr, marketmaker_channel_seq, marketmaker_remaining, False, marketmaker_signature)
if signer_address != self._market_maker_adr:
Expand All @@ -385,16 +391,16 @@ async def unwrap(self, key_id, serializer, ciphertext):
raise ApplicationError('xbr.error.invalid_signature',
'{}.unwrap()::XBRSIG[8/8] - EIP712 signature invalid or not signed by market maker'.format(self.__class__.__name__))

if self._seq + 1 != receipt['channel_seq']:
if self._seq + 1 != marketmaker_channel_seq:
raise ApplicationError('xbr.error.invalid_transaction',
'{}.buy_key(): invalid transaction (channel sequence number mismatch - expected {}, but got {})'.format(self.__class__.__name__, self._seq, receipt['channel_seq']))

if self._balance - amount != receipt['remaining']:
if self._balance - amount != marketmaker_remaining:
raise ApplicationError('xbr.error.invalid_transaction',
'{}.buy_key(): invalid transaction (channel remaining amount mismatch - expected {}, but got {})'.format(self.__class__.__name__, self._balance - amount, receipt['remaining']))

self._seq = receipt['channel_seq']
self._balance = receipt['remaining']
self._seq = marketmaker_channel_seq
self._balance = marketmaker_remaining

# persist 2nd phase of the transaction locally
self._save_transaction_phase2(channel_adr, self._market_maker_adr, buyer_pubkey, key_id, marketmaker_channel_seq,
Expand All @@ -418,10 +424,10 @@ async def unwrap(self, key_id, serializer, ciphertext):
klass=self.__class__.__name__,
tx_type=hl('XBR BUY ', color='magenta'),
key_id=hl(uuid.UUID(bytes=key_id)),
amount_paid=hl(str(int(receipt['amount_paid'] / 10 ** 18)) + ' XBR', color='magenta'),
amount_paid=hl(str(int(marketmaker_amount_paid / 10 ** 18)) + ' XBR', color='magenta'),
payment_channel=hl(binascii.b2a_hex(receipt['payment_channel']).decode()),
remaining=hl(int(receipt['remaining'] / 10 ** 18)),
inflight=hl(int(receipt['inflight'] / 10 ** 18)),
remaining=hl(int(marketmaker_remaining / 10 ** 18)),
inflight=hl(int(marketmaker_inflight / 10 ** 18)),
buyer_pubkey=hl(binascii.b2a_hex(buyer_pubkey).decode()),
transactions=transactions_count)

Expand Down
36 changes: 20 additions & 16 deletions autobahn/xbr/_seller.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
from autobahn.wamp.types import RegisterOptions, CallDetails
from autobahn.wamp.exception import ApplicationError, TransportLost
from autobahn.wamp.protocol import ApplicationSession
from ._util import unpack_uint256
from ._util import unpack_uint256, pack_uint256
from zlmdb import time_ns

import cbor2
Expand Down Expand Up @@ -299,7 +299,7 @@ async def on_rotate(key_series):
delegate,
signature,
privkey=None,
price=price,
price=pack_uint256(price) if price is not None else None,
categories=categories,
expires=None,
copies=None,
Expand All @@ -310,7 +310,7 @@ async def on_rotate(key_series):
tx_type=hl('XBR OFFER ', color='magenta'),
key_id=hl(uuid.UUID(bytes=key_id)),
api_id=hl(uuid.UUID(bytes=api_id)),
price=hl(str(int(price / 10 ** 18)) + ' XBR', color='magenta'),
price=hl(str(int(price / 10 ** 18) if price is not None else 0) + ' XBR', color='magenta'),
delegate=hl(binascii.b2a_hex(delegate).decode()),
prefix=hl(prefix))

Expand Down Expand Up @@ -476,12 +476,12 @@ def close_channel(self, market_maker_adr, channel_adr, channel_seq, channel_bala
"""
Called by a XBR Market Maker to close a paying channel.
"""
assert type(market_maker_adr) == bytes and len(market_maker_adr) == 20, 'market_maker_adr must be bytes[20]'
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(channel_balance) == int and channel_balance >= 0, 'post_balance must be int (>= 0)'
assert type(channel_is_final) == bool, 'channel_is_final must be bool'
assert type(marketmaker_signature) == bytes and len(marketmaker_signature) == (32 + 32 + 1), 'marketmaker_signature must be bytes[65]'
assert type(market_maker_adr) == bytes and len(market_maker_adr) == 20, 'market_maker_adr must be bytes[20], but was {}'.format(type(market_maker_adr))
assert type(channel_adr) == bytes and len(channel_adr) == 20, 'channel_adr must be bytes[20], but was {}'.format(type(channel_adr))
assert type(channel_seq) == int, 'channel_seq must be int, but was {}'.format(type(channel_seq))
assert type(channel_balance) == bytes and len(channel_balance) == 32, 'channel_balance must be bytes[32], but was {}'.format(type(channel_balance))
assert type(channel_is_final) == bool, 'channel_is_final must be bool, but was {}'.format(type(channel_is_final))
assert type(marketmaker_signature) == bytes and len(marketmaker_signature) == (32 + 32 + 1), 'marketmaker_signature must be bytes[65], but was {}'.format(type(marketmaker_signature))
assert details is None or isinstance(details, CallDetails), 'details must be autobahn.wamp.types.CallDetails'

# check that the delegate_adr fits what we expect for the market maker
Expand All @@ -501,6 +501,7 @@ def close_channel(self, market_maker_adr, channel_adr, channel_seq, channel_bala
'{}.sell() - unexpected channel (after tx) sequence number: expected {}, but got {}'.format(self.__class__.__name__, self._seq + 1, channel_seq))

# channel balance: check we have consensus on off-chain channel state with peer (which is the market maker)
channel_balance = unpack_uint256(channel_balance)
if channel_balance != self._balance:
raise ApplicationError('xbr.error.unexpected_channel_balance',
'{}.sell() - unexpected channel (after tx) balance: expected {}, but got {}'.format(self.__class__.__name__, self._balance, channel_balance))
Expand All @@ -520,7 +521,7 @@ def close_channel(self, market_maker_adr, channel_adr, channel_seq, channel_bala
receipt = {
'delegate': self._addr,
'seq': channel_seq,
'balance': channel_balance,
'balance': pack_uint256(channel_balance),
'is_final': channel_is_final,
'signature': seller_signature,
}
Expand Down Expand Up @@ -558,11 +559,11 @@ def sell(self, market_maker_adr, buyer_pubkey, key_id, channel_adr, channel_seq,
:type channel_seq: int
:param amount: The amount paid by the XBR Buyer via the XBR Market Maker.
:type amount: int
:type amount: bytes
:param balance: Balance remaining in the payment channel (from the market maker to the
seller) after successfully buying the key.
:type balance: int
:type balance: bytes
: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).
Expand All @@ -579,15 +580,18 @@ def sell(self, market_maker_adr, buyer_pubkey, key_id, channel_adr, channel_seq,
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)'
assert type(amount) == bytes and len(amount) == 32, 'amount_paid must be bytes[32], but was {}'.format(type(amount))
assert type(balance) == bytes and len(amount) == 32, 'post_balance must be bytes[32], but was {}'.format(type(balance))
assert type(signature) == bytes and len(signature) == (32 + 32 + 1), 'signature must be bytes[65]'
assert details is None or isinstance(details, CallDetails), 'details must be autobahn.wamp.types.CallDetails'

amount = unpack_uint256(amount)
balance = unpack_uint256(balance)

# check that the delegate_adr fits what we expect for the market maker
if market_maker_adr != self._market_maker_adr:
raise ApplicationError('xbr.error.unexpected_delegate_adr',
'{}.sell() - unexpected market maker (delegate) address: expected 0x{}, but got 0x{}'.format(self.__class__.__name__, binascii.b2a_hex(self._market_maker_adr).decode(), binascii.b2a_hex(market_maker_adr).decode()))
raise ApplicationError('xbr.error.unexpected_marketmaker_adr',
'{}.sell() - unexpected market maker address: expected 0x{}, but got 0x{}'.format(self.__class__.__name__, binascii.b2a_hex(self._market_maker_adr).decode(), binascii.b2a_hex(market_maker_adr).decode()))

# get the key series given the key_id
if key_id not in self._keys_map:
Expand Down
7 changes: 3 additions & 4 deletions autobahn/xbr/_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,6 @@

import click
import web3
# from autobahn.xbr import XBR_DEBUG_NETWORK_ADDR

# import eth_keys
from py_eth_sig_utils import signing

_EIP712_SIG_LEN = 32 + 32 + 1
Expand All @@ -57,13 +54,15 @@ def pack_uint128(value):
return b'\x00' * 16


# FIXME: possibly use https://eth-abi.readthedocs.io/en/stable/decoding.html

def unpack_uint256(data):
assert data is None or type(data) == bytes, 'data must by bytes, was {}'.format(type(data))
if data and type(data) == bytes:
assert len(data) == 32, 'data must be bytes[32], but was bytes[{}]'.format(len(data))

if data:
return web3.Web3.toInt(data)
return int(web3.Web3.toInt(data))
else:
return 0

Expand Down

0 comments on commit 55c1eea

Please sign in to comment.