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 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__)
moddir = os.path.dirname(__file__)
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
__bytecode = None
__address = 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
def abi():
if TokenUniqueSymbolIndex.__abi == None:
@ -58,27 +62,90 @@ class TokenUniqueSymbolIndex:
return TokenUniqueSymbolIndex.__bytecode
def add(self, address):
c = self.w3.eth.contract(abi=TokenUniqueSymbolIndex.__erc20_abi, address=address)
s = c.functions.symbol().call()
h = to_ref(s)
return self.contract.functions.register(h, address).transact({'from':self.signer_address})
def constructor(self, sender_address):
code = TokenUniqueSymbolIndex.bytecode()
tx = self.template(sender_address, None, use_nonce=True)
tx = self.set_code(tx, code)
return self.build(tx)
def count(self):
return self.contract.functions.registryCount().call()
def register(self, contract_address, sender_address, address, tx_format=TxFormat.JSONRPC):
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):
return self.contract.functions.entry(idx).call()
def address_of(self, contract_address, token_symbol, sender_address=ZERO_ADDRESS):
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):
ref = to_ref(symbol)
return self.contract.functions.addressOf(symbol).call()
def entry(self, contract_address, idx, sender_address=ZERO_ADDRESS):
o = jsonrpc_template()
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):
h = hashlib.new('sha256')
h.update(s.encode('utf-8'))
return h.digest().hex()
def entry_count(self, contract_address, sender_address=ZERO_ADDRESS):
o = jsonrpc_template()
o['method'] = 'eth_call'
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]
name = eth-address-index
version = 0.1.0a11
version = 0.1.0a13
description = Signed metadata declarations for ethereum addresses
author = Louis Holbrook
author_email = dev@holbrook.no
@ -57,3 +57,4 @@ console_scripts =
eth-address-declarator-add = eth_address_declarator.runnable.add:main
eth-token-index-deploy = eth_token_index.runnable.deploy: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
py-evm==0.3.0a20
giftable-erc20-token==0.0.7b13

View File

@ -1,12 +1,20 @@
# standard imports
import os
import unittest
import json
import logging
import hashlib
import web3
import eth_tester
import eth_abi
# external imports
from chainlib.eth.unittest.ethtester import EthTesterCase
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)
logg = logging.getLogger()
@ -17,108 +25,60 @@ logging.getLogger('eth.vm').setLevel(logging.WARNING)
testdir = os.path.dirname(__file__)
class Test(unittest.TestCase):
contract = None
class Test(EthTesterCase):
def setUp(self):
eth_params = eth_tester.backends.pyevm.main.get_default_genesis_params({
'gas_limit': 9000000,
})
super(Test, self).setUp()
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
#f = open(os.path.join(testdir, '../eth_token_index/data/TokenUniqueSymbolIndex.bin'), 'r')
f = open(os.path.join(testdir, '../eth_token_index/data/TokenUniqueSymbolIndex.bin'), 'r')
bytecode = f.read()
f.close()
o = receipt(tx_hash_hex)
r = self.rpc.do(o)
self.assertEqual(r['status'], 1)
#f = open(os.path.join(testdir, '../eth_token_index/data/TokenUniqueSymbolIndex.json'), 'r')
f = open(os.path.join(testdir, '../eth_token_index/data/TokenUniqueSymbolIndex.json'), 'r')
self.abi = json.load(f)
f.close()
self.address = r['contract_address']
c = GiftableToken(signer=self.signer, nonce_oracle=nonce_oracle, chain_id=self.chain_spec.chain_id())
(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)
self.eth_tester = eth_tester.EthereumTester(backend)
provider = web3.Web3.EthereumTesterProvider(self.eth_tester)
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]})
def test_register(self):
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.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))
r = self.w3.eth.getTransactionReceipt(tx_hash)
self.address = r.contractAddress
# create token
f = open(os.path.join(testdir, '../eth_token_index/data/GiftableToken.bin'), 'r')
bytecode = f.read()
f.close()
f = open(os.path.join(testdir, '../eth_token_index/data/GiftableToken.json'), 'r')
self.abi_token = json.load(f)
f.close()
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]})
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)
o = c.entry(self.address, 0, sender_address=self.accounts[0])
r = self.rpc.do(o)
address = c.parse_entry(r)
self.assertEqual(address, self.token_address)
o = c.entry_count(self.address, sender_address=self.accounts[0])
r = self.rpc.do(o)
count = c.parse_entry_count(r)
self.assertEqual(count, 1)
if __name__ == '__main__':
unittest.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];
}
function register(bytes32 _key, address _token) public returns (bool) {
function register(address _token) public returns (bool) {
require(msg.sender == owner);
bytes memory token_symbol;
@ -55,7 +55,6 @@ contract TokenUniqueSymbolIndex {
token_symbol = abi.decode(_r, (bytes));
token_symbol_key = sha256(token_symbol);
require(_key == token_symbol_key);
idx = registry[token_symbol_key];
require(idx == 0);
@ -64,7 +63,7 @@ contract TokenUniqueSymbolIndex {
return true;
}
function registryCount() public view returns ( uint256 ) {
function entryCount() public view returns ( uint256 ) {
return tokens.length - 1;
}
}