Rehabilitate token index

This commit is contained in:
nolash 2021-03-21 10:21:23 +01:00
parent 24a23e97b3
commit 4095c28221
Signed by: lash
GPG Key ID: 21D2E7BB88C2A746
12 changed files with 171 additions and 142 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"bytes32","name":"_key","type":"bytes32"}],"name":"addressOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_idx","type":"uint256"}],"name":"entry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_key","type":"bytes32"},{"internalType":"address","name":"_token","type":"address"}],"name":"register","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"registry","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"registryCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_interfaceCode","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}] [{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"bytes32","name":"_key","type":"bytes32"}],"name":"addressOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_idx","type":"uint256"}],"name":"entry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"entryCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"register","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"registry","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_interfaceCode","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]

View File

@ -9,37 +9,41 @@ import json
import os import os
import hashlib import hashlib
# external imports
from chainlib.eth.contract import (
ABIContractEncoder,
ABIContractType,
abi_decode_single,
)
from chainlib.eth.tx import (
TxFactory,
TxFormat,
)
from chainlib.jsonrpc import jsonrpc_template
from chainlib.eth.constant import ZERO_ADDRESS
from hexathon import (
add_0x,
)
logg = logging.getLogger(__name__) logg = logging.getLogger(__name__)
moddir = os.path.dirname(__file__) moddir = os.path.dirname(__file__)
datadir = os.path.join(moddir, 'data') datadir = os.path.join(moddir, 'data')
class TokenUniqueSymbolIndex: def to_identifier(s):
h = hashlib.new('sha256')
h.update(s.encode('utf-8'))
return h.digest().hex()
class TokenUniqueSymbolIndex(TxFactory):
__abi = None __abi = None
__bytecode = None __bytecode = None
__address = None __address = None
__erc20_abi = None __erc20_abi = None
def __init__(self, w3, address, signer_address=None):
abi = TokenUniqueSymbolIndex.abi()
TokenUniqueSymbolIndex.bytecode()
self.__address = address
self.contract = w3.eth.contract(abi=abi, address=address)
self.w3 = w3
if signer_address != None:
self.signer_address = signer_address
else:
if type(self.w3.eth.defaultAccount).__name__ == 'Empty':
self.w3.eth.defaultAccount = self.w3.eth.accounts[0]
self.signer_address = self.w3.eth.defaultAccount
f = open(os.path.join(datadir, 'ERC20.json'), 'r')
TokenUniqueSymbolIndex.__erc20_abi = json.load(f)
f.close()
@staticmethod @staticmethod
def abi(): def abi():
if TokenUniqueSymbolIndex.__abi == None: if TokenUniqueSymbolIndex.__abi == None:
@ -58,27 +62,90 @@ class TokenUniqueSymbolIndex:
return TokenUniqueSymbolIndex.__bytecode return TokenUniqueSymbolIndex.__bytecode
def add(self, address): def constructor(self, sender_address):
c = self.w3.eth.contract(abi=TokenUniqueSymbolIndex.__erc20_abi, address=address) code = TokenUniqueSymbolIndex.bytecode()
s = c.functions.symbol().call() tx = self.template(sender_address, None, use_nonce=True)
h = to_ref(s) tx = self.set_code(tx, code)
return self.contract.functions.register(h, address).transact({'from':self.signer_address}) return self.build(tx)
def count(self): def register(self, contract_address, sender_address, address, tx_format=TxFormat.JSONRPC):
return self.contract.functions.registryCount().call() enc = ABIContractEncoder()
enc.method('register')
enc.typ(ABIContractType.ADDRESS)
enc.address(address)
data = enc.get()
tx = self.template(sender_address, contract_address, use_nonce=True)
tx = self.set_code(tx, data)
tx = self.finalize(tx, tx_format)
return tx
def get_index(self, idx): def address_of(self, contract_address, token_symbol, sender_address=ZERO_ADDRESS):
return self.contract.functions.entry(idx).call() o = jsonrpc_template()
o['method'] = 'eth_call'
enc = ABIContractEncoder()
enc.method('addressOf')
enc.typ(ABIContractType.BYTES32)
token_symbol_digest = to_identifier(token_symbol)
enc.bytes32(token_symbol_digest)
data = add_0x(enc.get())
tx = self.template(sender_address, contract_address)
tx = self.set_code(tx, data)
o['params'].append(self.normalize(tx))
return o
def get_token_by_symbol(self, symbol): def entry(self, contract_address, idx, sender_address=ZERO_ADDRESS):
ref = to_ref(symbol) o = jsonrpc_template()
return self.contract.functions.addressOf(symbol).call() o['method'] = 'eth_call'
enc = ABIContractEncoder()
enc.method('entry')
enc.typ(ABIContractType.UINT256)
enc.uint256(idx)
data = add_0x(enc.get())
tx = self.template(sender_address, contract_address)
tx = self.set_code(tx, data)
o['params'].append(self.normalize(tx))
return o
def to_ref(s): def entry_count(self, contract_address, sender_address=ZERO_ADDRESS):
h = hashlib.new('sha256') o = jsonrpc_template()
h.update(s.encode('utf-8')) o['method'] = 'eth_call'
return h.digest().hex() enc = ABIContractEncoder()
enc.method('entryCount')
data = add_0x(enc.get())
tx = self.template(sender_address, contract_address)
tx = self.set_code(tx, data)
o['params'].append(self.normalize(tx))
return o
@classmethod
def parse_address_of(self, v):
return abi_decode_single(ABIContractType.ADDRESS, v)
@classmethod
def parse_entry(self, v):
return abi_decode_single(ABIContractType.ADDRESS, v)
@classmethod
def parse_entry_count(self, v):
return abi_decode_single(ABIContractType.UINT256, v)
# def count(self):
# return self.contract.functions.registryCount().call()
#
#
# def get_index(self, idx):
# return self.contract.functions.entry(idx).call()
#
#
# def get_token_by_symbol(self, symbol):
# ref = to_ref(symbol)
# return self.contract.functions.addressOf(symbol).call()

1
python/requirements.txt Normal file
View File

@ -0,0 +1 @@
chainlib==0.0.1a27

View File

@ -1,6 +1,6 @@
[metadata] [metadata]
name = eth-address-index name = eth-address-index
version = 0.1.0a11 version = 0.1.0a13
description = Signed metadata declarations for ethereum addresses description = Signed metadata declarations for ethereum addresses
author = Louis Holbrook author = Louis Holbrook
author_email = dev@holbrook.no author_email = dev@holbrook.no
@ -57,3 +57,4 @@ console_scripts =
eth-address-declarator-add = eth_address_declarator.runnable.add:main eth-address-declarator-add = eth_address_declarator.runnable.add:main
eth-token-index-deploy = eth_token_index.runnable.deploy:main eth-token-index-deploy = eth_token_index.runnable.deploy:main
eth-token-index-add = eth_token_index.runnable.add:main eth-token-index-add = eth_token_index.runnable.add:main
eth-token-index-list = eth_token_index.runnable.list:main

View File

@ -1,2 +1,3 @@
eth-tester==0.5.0b2 eth-tester==0.5.0b2
py-evm==0.3.0a20 py-evm==0.3.0a20
giftable-erc20-token==0.0.7b13

View File

@ -1,12 +1,20 @@
# standard imports
import os import os
import unittest import unittest
import json import json
import logging import logging
import hashlib import hashlib
import web3 # external imports
import eth_tester from chainlib.eth.unittest.ethtester import EthTesterCase
import eth_abi from chainlib.eth.nonce import RPCNonceOracle
from chainlib.eth.tx import receipt
from giftable_erc20_token import GiftableToken
from chainlib.eth.tx import unpack
from hexathon import strip_0x
# local imports
from eth_token_index import TokenUniqueSymbolIndex
logging.basicConfig(level=logging.DEBUG) logging.basicConfig(level=logging.DEBUG)
logg = logging.getLogger() logg = logging.getLogger()
@ -17,107 +25,59 @@ logging.getLogger('eth.vm').setLevel(logging.WARNING)
testdir = os.path.dirname(__file__) testdir = os.path.dirname(__file__)
class Test(unittest.TestCase): class Test(EthTesterCase):
contract = None
def setUp(self): def setUp(self):
eth_params = eth_tester.backends.pyevm.main.get_default_genesis_params({ super(Test, self).setUp()
'gas_limit': 9000000, nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
}) c = TokenUniqueSymbolIndex(signer=self.signer, nonce_oracle=nonce_oracle, chain_id=self.chain_spec.chain_id())
(tx_hash_hex, o) = c.constructor(self.accounts[0])
self.rpc.do(o)
# create store of used accounts o = receipt(tx_hash_hex)
#f = open(os.path.join(testdir, '../eth_token_index/data/TokenUniqueSymbolIndex.bin'), 'r') r = self.rpc.do(o)
f = open(os.path.join(testdir, '../eth_token_index/data/TokenUniqueSymbolIndex.bin'), 'r') self.assertEqual(r['status'], 1)
bytecode = f.read()
f.close()
#f = open(os.path.join(testdir, '../eth_token_index/data/TokenUniqueSymbolIndex.json'), 'r') self.address = r['contract_address']
f = open(os.path.join(testdir, '../eth_token_index/data/TokenUniqueSymbolIndex.json'), 'r')
self.abi = json.load(f) c = GiftableToken(signer=self.signer, nonce_oracle=nonce_oracle, chain_id=self.chain_spec.chain_id())
f.close() (tx_hash_hex, o) = c.constructor(self.accounts[0], 'FooToken', 'FOO', 6)
self.rpc.do(o)
o = receipt(tx_hash_hex)
r = self.rpc.do(o)
self.assertEqual(r['status'], 1)
self.token_address = r['contract_address']
backend = eth_tester.PyEVMBackend(eth_params) def test_register(self):
self.eth_tester = eth_tester.EthereumTester(backend) nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
provider = web3.Web3.EthereumTesterProvider(self.eth_tester) c = TokenUniqueSymbolIndex(signer=self.signer, nonce_oracle=nonce_oracle, chain_id=self.chain_spec.chain_id())
self.w3 = web3.Web3(provider)
c = self.w3.eth.contract(abi=self.abi, bytecode=bytecode)
tx_hash = c.constructor().transact({'from': self.w3.eth.accounts[0]})
r = self.w3.eth.getTransactionReceipt(tx_hash) (tx_hash_hex, o) = c.register(self.address, self.accounts[0], self.token_address)
self.rpc.do(o)
e = unpack(bytes.fromhex(strip_0x(o['params'][0])), chain_id=self.chain_spec.chain_id())
logg.debug('e {}'.format(e))
self.address = r.contractAddress o = receipt(tx_hash_hex)
r = self.rpc.do(o)
self.assertEqual(r['status'], 1)
o = c.address_of(self.address, 'FOO', sender_address=self.accounts[0])
r = self.rpc.do(o)
address = c.parse_address_of(r)
self.assertEqual(address, self.token_address)
# create token o = c.entry(self.address, 0, sender_address=self.accounts[0])
f = open(os.path.join(testdir, '../eth_token_index/data/GiftableToken.bin'), 'r') r = self.rpc.do(o)
bytecode = f.read() address = c.parse_entry(r)
f.close() self.assertEqual(address, self.token_address)
f = open(os.path.join(testdir, '../eth_token_index/data/GiftableToken.json'), 'r') o = c.entry_count(self.address, sender_address=self.accounts[0])
self.abi_token = json.load(f) r = self.rpc.do(o)
f.close() count = c.parse_entry_count(r)
self.assertEqual(count, 1)
t = self.w3.eth.contract(abi=self.abi_token, bytecode=bytecode)
tx_hash = t.constructor('Foo Token', 'FOO', 18).transact({'from': self.w3.eth.accounts[0]})
r = self.w3.eth.getTransactionReceipt(tx_hash)
self.address_token_one = r.contractAddress
t = self.w3.eth.contract(abi=self.abi_token, bytecode=bytecode)
tx_hash = t.constructor('Bar Token', 'BAR', 18).transact({'from': self.w3.eth.accounts[0]})
r = self.w3.eth.getTransactionReceipt(tx_hash)
self.address_token_two = r.contractAddress
t = self.w3.eth.contract(abi=self.abi_token, bytecode=bytecode)
tx_hash = t.constructor('Bar Token Duplicate', 'BAR', 18).transact({'from': self.w3.eth.accounts[0]})
r = self.w3.eth.getTransactionReceipt(tx_hash)
self.address_token_three = r.contractAddress
def tearDown(self):
pass
def test_basic(self):
c = self.w3.eth.contract(abi=self.abi, address=self.address)
h = hashlib.new('sha256')
h.update('FOO'.encode('utf-8'))
z = h.digest()
# owner text
with self.assertRaises(Exception):
c.functions.register(z.hex(), self.address_token_one).transact({'from': self.w3.eth.accounts[1]})
logg.debug('using identifier {}'.format(z.hex()))
# Register FOO symbol
c.functions.register(z.hex(), self.address_token_one).transact({'from': self.w3.eth.accounts[0]})
# Raise on duplicate FOO symbol
with self.assertRaises(Exception):
c.functions.register(z.hex(), self.address_token_one).transact({'from': self.w3.eth.accounts[0]})
# Raise on mismatch between supplied symbol and token symbol reported by ERC20
with self.assertRaises(Exception):
c.functions.register(z.hex(), self.address_token_two).transact({'from': self.w3.eth.accounts[0]})
h = hashlib.new('sha256')
h.update('BAR'.encode('utf-8'))
z = h.digest()
# Register BAR symbol
c.functions.register(z.hex(), self.address_token_two).transact({'from': self.w3.eth.accounts[0]})
# Raise on duplicate BAR symbol (with different token contract address)
with self.assertRaises(Exception):
c.functions.register(z.hex(), self.address_token_three).transact({'from': self.w3.eth.accounts[0]})
if __name__ == '__main__': if __name__ == '__main__':

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"bytes32","name":"_key","type":"bytes32"}],"name":"addressOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_idx","type":"uint256"}],"name":"entry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_key","type":"bytes32"},{"internalType":"address","name":"_token","type":"address"}],"name":"register","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"registry","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"registryCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_interfaceCode","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}] [{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"bytes32","name":"_key","type":"bytes32"}],"name":"addressOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_idx","type":"uint256"}],"name":"entry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"entryCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"register","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"registry","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_interfaceCode","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]

View File

@ -42,7 +42,7 @@ contract TokenUniqueSymbolIndex {
return tokens[idx]; return tokens[idx];
} }
function register(bytes32 _key, address _token) public returns (bool) { function register(address _token) public returns (bool) {
require(msg.sender == owner); require(msg.sender == owner);
bytes memory token_symbol; bytes memory token_symbol;
@ -55,7 +55,6 @@ contract TokenUniqueSymbolIndex {
token_symbol = abi.decode(_r, (bytes)); token_symbol = abi.decode(_r, (bytes));
token_symbol_key = sha256(token_symbol); token_symbol_key = sha256(token_symbol);
require(_key == token_symbol_key);
idx = registry[token_symbol_key]; idx = registry[token_symbol_key];
require(idx == 0); require(idx == 0);
@ -64,7 +63,7 @@ contract TokenUniqueSymbolIndex {
return true; return true;
} }
function registryCount() public view returns ( uint256 ) { function entryCount() public view returns ( uint256 ) {
return tokens.length - 1; return tokens.length - 1;
} }
} }