Skip to content

Commit

Permalink
expand XBR CLI and adjust for XBR 20.5.1 (#1387)
Browse files Browse the repository at this point in the history
* update to XBR v20.5.1
* more EIP712 stuff
* consistency
* update changelog; bump version
  • Loading branch information
oberstet committed Jun 8, 2020
1 parent effc130 commit 2bd8255
Show file tree
Hide file tree
Showing 27 changed files with 1,119 additions and 313 deletions.
2 changes: 1 addition & 1 deletion autobahn/_version.py
Expand Up @@ -24,4 +24,4 @@
#
###############################################################################

__version__ = '20.5.1.dev1'
__version__ = '20.6.1'
18 changes: 18 additions & 0 deletions autobahn/xbr/README.md
@@ -0,0 +1,18 @@
# XBR

## EIP712

* [x] [Base](_eip712_base.py)
* [x] [EIP712MemberRegister](_eip712_member_register.py)
* [x] [EIP712MemberUnregister](_eip712_member_unregister.py)
* [x] [EIP712MemberLogin](_eip712_member_login.py)
* [x] [EIP712DomainCreate](_eip712_domain_create.py)
* [x] [EIP712NodePair](_eip712_node_pair.py)
* [x] [EIP712CatalogCreate](_eip712_catalog_create.py)
* [x] [EIP712ApiPublish](_eip712_api_publish.py)
* [x] [EIP712MarketCreate](_eip712_market_create.py)
* [x] [EIP712MarketJoin](_eip712_market_join.py)
* [x] [EIP712MarketLeave](_eip712_market_leave.py)
* [x] [EIP712Consent](_eip712_consent.py)
* [x] [EIP712ChannelOpen](_eip712_channel_open.py)
* [x] [EIP712ChannelClose](_eip712_channel_close.py)
55 changes: 35 additions & 20 deletions autobahn/xbr/_abi.py
Expand Up @@ -38,7 +38,7 @@

#
# Set default XBR contract addresses to
# XBR v20.4.2 @ Rinkeby (https://github.com/crossbario/xbr-protocol/issues/106)
# XBR v20.5.1. @ Rinkeby (https://github.com/crossbario/xbr-protocol/issues/127)
#
if 'XBR_DEBUG_TOKEN_ADDR' in os.environ:
_token_adr = os.environ['XBR_DEBUG_TOKEN_ADDR']
Expand All @@ -48,9 +48,9 @@
except Exception as e:
raise RuntimeError('could not parse Ethereum address for XBR_DEBUG_TOKEN_ADDR={} - {}'.format(_token_adr, e))
XBR_DEBUG_TOKEN_ADDR = _token_adr
XBR_DEBUG_TOKEN_ADDR_SRC = 'env'
XBR_DEBUG_TOKEN_ADDR_SRC = 'envvar $XBR_DEBUG_TOKEN_ADDR'
else:
XBR_DEBUG_TOKEN_ADDR = '0x8d41eF64D49eA1550B4b41a8959D856601441503'
XBR_DEBUG_TOKEN_ADDR = '0xaCef957D54c639575f4DB68b1992B36504f33FEA'
XBR_DEBUG_TOKEN_ADDR_SRC = 'builtin'

if 'XBR_DEBUG_NETWORK_ADDR' in os.environ:
Expand All @@ -61,23 +61,23 @@
except Exception as e:
raise RuntimeError('could not parse Ethereum address for XBR_DEBUG_NETWORK_ADDR={} - {}'.format(_netw_adr, e))
XBR_DEBUG_NETWORK_ADDR = _netw_adr
XBR_DEBUG_NETWORK_ADDR_SRC = 'env'
XBR_DEBUG_NETWORK_ADDR_SRC = 'envvar $XBR_DEBUG_NETWORK_ADDR'
else:
XBR_DEBUG_NETWORK_ADDR = '0xBfB616f885D581328FC6c3ad53481231Cc9b1bcf'
XBR_DEBUG_NETWORK_ADDR = '0x7A3d22c59e8F8f1b88ba7205f3f5a65Bc86D04Bc'
XBR_DEBUG_NETWORK_ADDR_SRC = 'builtin'

if 'XBR_DEBUG_MARKET_ADDR' in os.environ:
_mrkt_adr = os.environ['XBR_DEBUG_MARKET_ADDR']
if 'XBR_DEBUG_DOMAIN_ADDR' in os.environ:
_domain_adr = os.environ['XBR_DEBUG_DOMAIN_ADDR']
try:
_mrkt_adr = binascii.a2b_hex(_mrkt_adr[2:])
_mrkt_adr = web3.Web3.toChecksumAddress(_mrkt_adr)
_domain_adr = binascii.a2b_hex(_domain_adr[2:])
_domain_adr = web3.Web3.toChecksumAddress(_domain_adr)
except Exception as e:
raise RuntimeError('could not parse Ethereum address for XBR_DEBUG_MARKET_ADDR={} - {}'.format(_mrkt_adr, e))
XBR_DEBUG_MARKET_ADDR = _mrkt_adr
XBR_DEBUG_MARKET_ADDR_SRC = 'env'
raise RuntimeError('could not parse Ethereum address for XBR_DEBUG_DOMAIN_ADDR={} - {}'.format(_domain_adr, e))
XBR_DEBUG_DOMAIN_ADDR = _domain_adr
XBR_DEBUG_DOMAIN_ADDR_SRC = 'envvar $XBR_DEBUG_DOMAIN_ADDR'
else:
XBR_DEBUG_MARKET_ADDR = '0x27d4E6534134d9B1b5E2190cf8Ea170C8D05fb66'
XBR_DEBUG_MARKET_ADDR_SRC = 'builtin'
XBR_DEBUG_DOMAIN_ADDR = '0xf5fb56886f033855C1a36F651E927551749361bC'
XBR_DEBUG_DOMAIN_ADDR_SRC = 'builtin'

if 'XBR_DEBUG_CATALOG_ADDR' in os.environ:
_ctlg_adr = os.environ['XBR_DEBUG_CATALOG_ADDR']
Expand All @@ -87,11 +87,24 @@
except Exception as e:
raise RuntimeError('could not parse Ethereum address for XBR_DEBUG_CATALOG_ADDR={} - {}'.format(_ctlg_adr, e))
XBR_DEBUG_CATALOG_ADDR = _ctlg_adr
XBR_DEBUG_CATALOG_ADDR_SRC = 'env'
XBR_DEBUG_CATALOG_ADDR_SRC = 'envvar $XBR_DEBUG_CATALOG_ADDR'
else:
XBR_DEBUG_CATALOG_ADDR = '0x96284C34bD2A805589F9673F2534ED691672cAa0'
XBR_DEBUG_CATALOG_ADDR = '0x2C77E46Ea9502B363343e8c826c41c7fdb25Db66'
XBR_DEBUG_CATALOG_ADDR_SRC = 'builtin'

if 'XBR_DEBUG_MARKET_ADDR' in os.environ:
_mrkt_adr = os.environ['XBR_DEBUG_MARKET_ADDR']
try:
_mrkt_adr = binascii.a2b_hex(_mrkt_adr[2:])
_mrkt_adr = web3.Web3.toChecksumAddress(_mrkt_adr)
except Exception as e:
raise RuntimeError('could not parse Ethereum address for XBR_DEBUG_MARKET_ADDR={} - {}'.format(_mrkt_adr, e))
XBR_DEBUG_MARKET_ADDR = _mrkt_adr
XBR_DEBUG_MARKET_ADDR_SRC = 'envvar $XBR_DEBUG_MARKET_ADDR'
else:
XBR_DEBUG_MARKET_ADDR = '0x0DcF924ab0846101d31514E9fb3adf5070d4B83d'
XBR_DEBUG_MARKET_ADDR_SRC = 'builtin'

if 'XBR_DEBUG_CHANNEL_ADDR' in os.environ:
_chnl_adr = os.environ['XBR_DEBUG_CHANNEL_ADDR']
try:
Expand All @@ -100,9 +113,9 @@
except Exception as e:
raise RuntimeError('could not parse Ethereum address for XBR_DEBUG_CHANNEL_ADDR={} - {}'.format(_chnl_adr, e))
XBR_DEBUG_CHANNEL_ADDR = _chnl_adr
XBR_DEBUG_CHANNEL_ADDR_SRC = 'env'
XBR_DEBUG_CHANNEL_ADDR_SRC = 'envvar $XBR_DEBUG_CHANNEL_ADDR'
else:
XBR_DEBUG_CHANNEL_ADDR = '0xA20C8bA0e86606cCBEE14A50acA0604Ce667F508'
XBR_DEBUG_CHANNEL_ADDR = '0x670497A012322B99a5C18B8463940996141Cb952'
XBR_DEBUG_CHANNEL_ADDR_SRC = 'builtin'


Expand All @@ -118,8 +131,9 @@ def _load_json(contract_name):
#
XBR_TOKEN_FN = pkg_resources.resource_filename('autobahn', 'xbr/contracts/XBRToken.json')
XBR_NETWORK_FN = pkg_resources.resource_filename('autobahn', 'xbr/contracts/XBRNetwork.json')
XBR_MARKET_FN = pkg_resources.resource_filename('autobahn', 'xbr/contracts/XBRMarket.json')
XBR_DOMAIN_FN = pkg_resources.resource_filename('autobahn', 'xbr/contracts/XBRDomain.json')
XBR_CATALOG_FN = pkg_resources.resource_filename('autobahn', 'xbr/contracts/XBRCatalog.json')
XBR_MARKET_FN = pkg_resources.resource_filename('autobahn', 'xbr/contracts/XBRMarket.json')
XBR_CHANNEL_FN = pkg_resources.resource_filename('autobahn', 'xbr/contracts/XBRChannel.json')


Expand All @@ -128,6 +142,7 @@ def _load_json(contract_name):
#
XBR_TOKEN_ABI = _load_json('XBRToken')['abi']
XBR_NETWORK_ABI = _load_json('XBRNetwork')['abi']
XBR_MARKET_ABI = _load_json('XBRMarket')['abi']
XBR_DOMAIN_ABI = _load_json('XBRDomain')['abi']
XBR_CATALOG_ABI = _load_json('XBRCatalog')['abi']
XBR_MARKET_ABI = _load_json('XBRMarket')['abi']
XBR_CHANNEL_ABI = _load_json('XBRChannel')['abi']
29 changes: 16 additions & 13 deletions autobahn/xbr/_cli.py
Expand Up @@ -33,11 +33,11 @@
print("For example, \"pip install autobahn[xbr]\".")
sys.exit(1)

from autobahn.xbr._abi import XBR_DEBUG_TOKEN_ADDR, XBR_DEBUG_NETWORK_ADDR, XBR_DEBUG_MARKET_ADDR, \
XBR_DEBUG_CATALOG_ADDR, XBR_DEBUG_CHANNEL_ADDR
from autobahn.xbr._abi import XBR_DEBUG_TOKEN_ADDR, XBR_DEBUG_NETWORK_ADDR, XBR_DEBUG_DOMAIN_ADDR, \
XBR_DEBUG_CATALOG_ADDR, XBR_DEBUG_MARKET_ADDR, XBR_DEBUG_CHANNEL_ADDR

from autobahn.xbr._abi import XBR_DEBUG_TOKEN_ADDR_SRC, XBR_DEBUG_NETWORK_ADDR_SRC, XBR_DEBUG_MARKET_ADDR_SRC, \
XBR_DEBUG_CATALOG_ADDR_SRC, XBR_DEBUG_CHANNEL_ADDR_SRC
from autobahn.xbr._abi import XBR_DEBUG_TOKEN_ADDR_SRC, XBR_DEBUG_NETWORK_ADDR_SRC, XBR_DEBUG_DOMAIN_ADDR_SRC, \
XBR_DEBUG_CATALOG_ADDR_SRC, XBR_DEBUG_MARKET_ADDR_SRC, XBR_DEBUG_CHANNEL_ADDR_SRC

import uuid
import binascii
Expand Down Expand Up @@ -911,23 +911,26 @@ def _main():

args = parser.parse_args()

# read or create a user profile
profile = load_or_create_profile()

if args.command == 'version':
print('')
print(' XBR CLI v{}\n'.format(__version__))
print(' XBRToken contract address: {} [source: {}]'.format(hlid(XBR_DEBUG_TOKEN_ADDR), XBR_DEBUG_TOKEN_ADDR_SRC))
print(' XBRNetwork contract address: {} [source: {}]'.format(hlid(XBR_DEBUG_NETWORK_ADDR), XBR_DEBUG_NETWORK_ADDR_SRC))
print(' XBRMarket contract address: {} [source: {}]'.format(hlid(XBR_DEBUG_MARKET_ADDR), XBR_DEBUG_MARKET_ADDR_SRC))
print(' XBRCatalog contract address: {} [source: {}]'.format(hlid(XBR_DEBUG_CATALOG_ADDR), XBR_DEBUG_CATALOG_ADDR_SRC))
print(' XBRChannel contract address: {} [source: {}]'.format(hlid(XBR_DEBUG_CHANNEL_ADDR), XBR_DEBUG_CHANNEL_ADDR_SRC))
print(' XBR CLI {}\n'.format(hlval('v' + __version__)))
print(' Profile {profile} loaded from {path}\n'.format(profile=hlval(profile.name), path=hlval(profile.path)))
print(' Contract addresses:\n')
print(' XBRToken : {} [source: {}]'.format(hlid(XBR_DEBUG_TOKEN_ADDR), XBR_DEBUG_TOKEN_ADDR_SRC))
print(' XBRNetwork : {} [source: {}]'.format(hlid(XBR_DEBUG_NETWORK_ADDR), XBR_DEBUG_NETWORK_ADDR_SRC))
print(' XBRDomain : {} [source: {}]'.format(hlid(XBR_DEBUG_DOMAIN_ADDR), XBR_DEBUG_DOMAIN_ADDR_SRC))
print(' XBRCatalog : {} [source: {}]'.format(hlid(XBR_DEBUG_CATALOG_ADDR), XBR_DEBUG_CATALOG_ADDR_SRC))
print(' XBRMarket : {} [source: {}]'.format(hlid(XBR_DEBUG_MARKET_ADDR), XBR_DEBUG_MARKET_ADDR_SRC))
print(' XBRChannel : {} [source: {}]'.format(hlid(XBR_DEBUG_CHANNEL_ADDR), XBR_DEBUG_CHANNEL_ADDR_SRC))
print('')
else:
if args.command is None or args.command == 'noop':
print('no command given. select from: {}'.format(', '.join(_COMMANDS)))
sys.exit(0)

# read or create a user profile
profile = load_or_create_profile()

# only start txaio logging after above, which runs click (interactively)
if args.debug:
txaio.start_logging(level='debug')
Expand Down
4 changes: 2 additions & 2 deletions autobahn/xbr/_config.py
Expand Up @@ -286,8 +286,8 @@ def load_or_create_profile(dotdir=None, profile=None):
market_realm = click.prompt('enter the WAMP realm of the XBR data market', type=str)
ethkey = prompt_for_key('your private Etherum key', 32)
cskey = prompt_for_key('your private WAMP client key', 32)
infura_network = click.prompt('enter your Infura gateway URL', type=str, default='rinkeby')
infura_url = click.prompt('enter your Infura gateway URL', type=str)
infura_network = click.prompt('enter Ethereum network to use', type=str, default='rinkeby')
infura_url = click.prompt('enter Infura gateway URL', type=str)
infura_key = click.prompt('your Infura gateway key', type=str)
infura_secret = click.prompt('your Infura gateway secret', type=str)
f.write(_DEFAULT_CONFIG.format(market_url=market_url, market_realm=market_realm, ethkey=ethkey,
Expand Down
20 changes: 12 additions & 8 deletions autobahn/xbr/_eip712_api_publish.py
Expand Up @@ -24,11 +24,13 @@
#
###############################################################################

from ._eip712_base import sign, recover, is_address, is_bytes16
from typing import Optional
from ._eip712_base import sign, recover, is_address, is_bytes16, is_block_number, \
is_chain_id, is_eth_privkey, is_signature


def _create_eip712_api_publish(chainId: int, verifyingContract: bytes, member: bytes, published: int,
catalogId: bytes, apiId: bytes, schema: str, meta: str) -> dict:
catalogId: bytes, apiId: bytes, schema: str, meta: Optional[str]) -> dict:
"""
:param chainId:
Expand All @@ -41,10 +43,10 @@ def _create_eip712_api_publish(chainId: int, verifyingContract: bytes, member: b
:param meta:
:return:
"""
assert type(chainId) == int
assert is_chain_id(chainId)
assert is_address(verifyingContract)
assert is_address(member)
assert type(published) == int
assert is_block_number(published)
assert is_bytes16(catalogId)
assert is_bytes16(apiId)
assert type(schema) == str
Expand Down Expand Up @@ -118,7 +120,7 @@ def _create_eip712_api_publish(chainId: int, verifyingContract: bytes, member: b


def sign_eip712_api_publish(eth_privkey: bytes, chainId: int, verifyingContract: bytes, member: bytes,
published: int, catalogId: bytes, apiId: bytes, schema: str, meta: str) -> bytes:
published: int, catalogId: bytes, apiId: bytes, schema: str, meta: Optional[str]) -> bytes:
"""
:param eth_privkey: Ethereum address of buyer (a raw 20 bytes Ethereum address).
Expand All @@ -127,22 +129,24 @@ def sign_eip712_api_publish(eth_privkey: bytes, chainId: int, verifyingContract:
:return: The signature according to EIP712 (32+32+1 raw bytes).
:rtype: bytes
"""
# create EIP712 typed data object
assert is_eth_privkey(eth_privkey)

data = _create_eip712_api_publish(chainId, verifyingContract, member, published, catalogId, apiId, schema,
meta)
return sign(eth_privkey, data)


def recover_eip712_api_publish(chainId: int, verifyingContract: bytes, member: bytes, published: int,
catalogId: bytes, apiId: bytes, schema: str, meta: str,
catalogId: bytes, apiId: bytes, schema: str, meta: Optional[str],
signature: bytes) -> bytes:
"""
Recover the signer address the given EIP712 signature was signed with.
:return: The (computed) signer address the signature was signed with.
:rtype: bytes
"""
# recreate EIP712 typed data object
assert is_signature(signature)

data = _create_eip712_api_publish(chainId, verifyingContract, member, published, catalogId, apiId, schema,
meta)
return recover(data, signature)
81 changes: 78 additions & 3 deletions autobahn/xbr/_eip712_base.py
Expand Up @@ -31,26 +31,101 @@


def sign(eth_privkey, data):
# FIXME: this fails on PyPy (but ot on CPy!) with
# Unknown format b'%M\xff\xcd2w\xc0\xb1f\x0fmB\xef\xbbuN\xda\xba\xbc+', attempted to normalize to 0x254dffcd3277c0b1660f6d42efbb754edababc2b
_args = signing.sign_typed_data(data, eth_privkey)
"""
Sign the given data using the given Ethereum private key.
:param eth_privkey: Signing key.
:param data: Data to sign.
:return: Signature.
"""
_args = signing.sign_typed_data(data, eth_privkey)
signature = signing.v_r_s_to_signature(*_args)
assert len(signature) == _EIP712_SIG_LEN

return signature


def recover(data, signature):
"""
Recover the Ethereum address of the signer, given the data and signature.
:param data: Signed data.
:param signature: Signature.
:return: Signing address.
"""
assert type(signature) == bytes and len(signature) == _EIP712_SIG_LEN
signer_address = signing.recover_typed_data(data, *signing.signature_to_v_r_s(signature))

return a2b_hex(signer_address[2:])


def is_address(provided):
"""
Check if the value is a proper Ethereum address.
:param provided: The value to check.
:return: True iff the value is of correct type.
"""
return type(provided) == bytes and len(provided) == 20


def is_bytes16(provided):
"""
Check if the value is a proper (binary) UUID.
:param provided: The value to check.
:return: True iff the value is of correct type.
"""
return type(provided) == bytes and len(provided) == 16


def is_signature(provided):
"""
Check if the value is a proper Ethereum signature.
:param provided: The value to check.
:return: True iff the value is of correct type.
"""
return type(provided) == bytes and len(provided) == _EIP712_SIG_LEN


def is_eth_privkey(provided):
"""
Check if the value is a proper WAMP-cryptosign private key.
:param provided: The value to check.
:return: True iff the value is of correct type.
"""
return type(provided) == bytes and len(provided) == 32


def is_cs_pubkey(provided):
"""
Check if the value is a proper WAMP-cryptosign public key.
:param provided: The value to check.
:return: True iff the value is of correct type.
"""
return type(provided) == bytes and len(provided) == 32


def is_block_number(provided):
"""
Check if the value is a proper Ethereum block number.
:param provided: The value to check.
:return: True iff the value is of correct type.
"""
return type(provided) == int


def is_chain_id(provided):
"""
Check if the value is a proper Ethereum chain ID.
:param provided: The value to check.
:return: True iff the value is of correct type.
"""
# here is a list of public networks: https://chainid.network/
# note: we allow any positive integer to account for private networks
return type(provided) == int and provided > 0

0 comments on commit 2bd8255

Please sign in to comment.