commit ffb4fdee129aca53f42bd24192fa85e567a95004
Author: nolash <dev@holbrook.no>
Date: Mon, 21 Dec 2020 23:00:45 +0100
Initial commit
Diffstat:
11 files changed, 256 insertions(+), 0 deletions(-)
diff --git a/python/MANIFEST.in b/python/MANIFEST.in
@@ -0,0 +1 @@
+include **/data/TokenEndorser.json **/data/TokenEndorser.bin
diff --git a/python/eth_token_endorser/data/TokenEndorser.bin b/python/eth_token_endorser/data/TokenEndorser.bin
@@ -0,0 +1 @@
+608060405234801561001057600080fd5b506001600081905550600460009080600181540180825580915050600190039060005260206000200160009091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610bc48061008c6000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c80632d580ef61461005c578063427f91a61461008c5780634f64b2be146100bc578063c82d6828146100ec578063e6088e831461011c575b600080fd5b6100766004803603810190610071919061079a565b61014c565b604051610083919061095e565b60405180910390f35b6100a660048036038101906100a19190610771565b610561565b6040516100b39190610994565b60405180910390f35b6100d660048036038101906100d19190610864565b610579565b6040516100e39190610943565b60405180910390f35b610106600480360381019061010191906107d6565b6105b8565b6040516101139190610994565b60405180910390f35b61013660048036038101906101319190610812565b6105e9565b6040516101439190610979565b60405180910390f35b600061015783610601565b50600080602867ffffffffffffffff81111561019c577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040519080825280601f01601f1916602001820160405280156101ce5781602001600182028036833780820191505090505b5090506000856040516020016101e49190610911565b604051602081830303815290604052905060005b60148110156102c05781818151811061023a577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b602001015160f81c60f81b83828151811061027e577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535080806102b890610aa0565b9150506101f8565b50336040516020016102d29190610911565b604051602081830303815290604052905060005b60148110156103ba57818181518110610328577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b602001015160f81c60f81b8360148361034191906109c5565b81518110610378577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535080806103b290610aa0565b9150506102e6565b506002826040516103cb919061092c565b602060405180830381855afa1580156103e8573d6000803e3d6000fd5b5050506040513d601f19601f8201168201806040525081019061040b919061083b565b92506000801b60016000858152602001908152602001600020541461042f57600080fd5b846001600085815260200190815260200160002081905550600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600260008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490806001815401808255809150506001900390600052602060002001600090919091909150556000543373ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167fb8a4220b7c147fc6e04861db14024976c551727749a09806414670fac57e53408860405161054c9190610979565b60405180910390a46001935050505092915050565b60026020528060005260406000206000915090505481565b6004818154811061058957600080fd5b906000526020600020016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600360205281600052604060002081815481106105d457600080fd5b90600052602060002001600091509150505481565b60016020528060005260406000206000915090505481565b600080600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205411156106535760009050610718565b6004829080600181540180825580915050600190039060005260206000200160009091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600054600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555060008081548092919061070e90610aa0565b9190505550600190505b919050565b60008135905061072c81610b49565b92915050565b60008135905061074181610b60565b92915050565b60008151905061075681610b60565b92915050565b60008135905061076b81610b77565b92915050565b60006020828403121561078357600080fd5b60006107918482850161071d565b91505092915050565b600080604083850312156107ad57600080fd5b60006107bb8582860161071d565b92505060206107cc85828601610732565b9150509250929050565b600080604083850312156107e957600080fd5b60006107f78582860161071d565b92505060206108088582860161075c565b9150509250929050565b60006020828403121561082457600080fd5b600061083284828501610732565b91505092915050565b60006020828403121561084d57600080fd5b600061085b84828501610747565b91505092915050565b60006020828403121561087657600080fd5b60006108848482850161075c565b91505092915050565b61089681610a1b565b82525050565b6108ad6108a882610a1b565b610ae9565b82525050565b6108bc81610a2d565b82525050565b6108cb81610a39565b82525050565b60006108dc826109af565b6108e681856109ba565b93506108f6818560208601610a6d565b80840191505092915050565b61090b81610a63565b82525050565b600061091d828461089c565b60148201915081905092915050565b600061093882846108d1565b915081905092915050565b6000602082019050610958600083018461088d565b92915050565b600060208201905061097360008301846108b3565b92915050565b600060208201905061098e60008301846108c2565b92915050565b60006020820190506109a96000830184610902565b92915050565b600081519050919050565b600081905092915050565b60006109d082610a63565b91506109db83610a63565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03821115610a1057610a0f610b0d565b5b828201905092915050565b6000610a2682610a43565b9050919050565b60008115159050919050565b6000819050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60005b83811015610a8b578082015181840152602081019050610a70565b83811115610a9a576000848401525b50505050565b6000610aab82610a63565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415610ade57610add610b0d565b5b600182019050919050565b6000610af482610afb565b9050919050565b6000610b0682610b3c565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008160601b9050919050565b610b5281610a1b565b8114610b5d57600080fd5b50565b610b6981610a39565b8114610b7457600080fd5b50565b610b8081610a63565b8114610b8b57600080fd5b5056fea2646970667358221220bdfe2b23cf9bc8fac35e8b06e83bc20d08b1c9b009c2e1d7c2ccf4932a53601e64736f6c63430008000033
+\ No newline at end of file
diff --git a/python/eth_token_endorser/data/TokenEndorser.json b/python/eth_token_endorser/data/TokenEndorser.json
@@ -0,0 +1 @@
+[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_token","type":"address"},{"indexed":true,"internalType":"address","name":"_adder","type":"address"},{"indexed":true,"internalType":"uint256","name":"_index","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"_data","type":"bytes32"}],"name":"EndorsementAdded","type":"event"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"bytes32","name":"_data","type":"bytes32"}],"name":"add","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"endorsement","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"endorsers","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tokenIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"tokens","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]
diff --git a/python/eth_token_endorser/runnable/deploy.py b/python/eth_token_endorser/runnable/deploy.py
@@ -0,0 +1,62 @@
+"""Deploys token endorsement contract
+
+.. moduleauthor:: Louis Holbrook <dev@holbrook.no>
+.. pgp:: 0826EDA1702D1E87C6E2875121D2E7BB88C2A746
+
+"""
+
+# standard imports
+import os
+import json
+import argparse
+import logging
+
+# third-party imports
+import web3
+
+
+logging.basicConfig(level=logging.WARNING)
+logg = logging.getLogger()
+
+logging.getLogger('web3').setLevel(logging.WARNING)
+logging.getLogger('urllib3').setLevel(logging.WARNING)
+
+script_dir = os.path.dirname(__file__)
+data_dir = os.path.join(script_dir, '..', 'data')
+
+argparser = argparse.ArgumentParser()
+argparser.add_argument('-p', '--provider', dest='p', default='http://localhost:8545', type=str, help='Web3 provider url (http only)')
+argparser.add_argument('-o', '--owner', dest='o', type=str, help='Accounts index owner')
+argparser.add_argument('--abi-dir', dest='abi_dir', type=str, default=data_dir, help='Directory containing bytecode and abi (default: {})'.format(data_dir))
+argparser.add_argument('-v', action='store_true', help='Be verbose')
+args = argparser.parse_args()
+
+if args.v:
+ logg.setLevel(logging.DEBUG)
+
+def main():
+ w3 = web3.Web3(web3.Web3.HTTPProvider(args.p))
+
+ f = open(os.path.join(args.abi_dir, 'TokenEndorser.json'), 'r')
+ abi = json.load(f)
+ f.close()
+
+ f = open(os.path.join(args.abi_dir, 'TokenEndorser.bin'), 'r')
+ bytecode = f.read()
+ f.close()
+
+ w3.eth.defaultAccount = w3.eth.accounts[0]
+ if args.o != None:
+ w3.eth.defaultAccount = args.o
+ logg.debug('owner address {}'.format(w3.eth.defaultAccount))
+
+ c = w3.eth.contract(abi=abi, bytecode=bytecode)
+ tx_hash = c.constructor().transact()
+
+ rcpt = w3.eth.getTransactionReceipt(tx_hash)
+
+ print(rcpt.contractAddress)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/python/setup.cfg b/python/setup.cfg
@@ -0,0 +1,47 @@
+[metadata]
+name = eth-token-endorser
+version = 0.0.1
+description = Ethereum token address to multihash metadata mapper
+author = Louis Holbrook
+author_email = dev@holbrook.no
+url = https://gitlab.com/nolash/simple-multisig
+keywords =
+ ethereum
+classifiers =
+ Programming Language :: Python :: 3
+ Operating System :: OS Independent
+ Development Status :: 3 - Alpha
+ Environment :: No Input/Output (Daemon)
+ Intended Audience :: Developers
+ License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
+ Topic :: Internet
+ #Topic :: Blockchain :: EVM
+license = GPL3
+licence_files =
+ LICENSE
+
+[options]
+include_package_data = True
+python_requires = >= 3.6
+packages =
+ eth_token_endorser
+ eth_token_endorser.runnable
+install_requires =
+ web3==5.12.2
+tests_require =
+ eth-tester==0.5.0b2
+ py-evm==0.3.0a20
+
+[options.extras_require]
+testing =
+ eth-tester==0.5.0b2
+ py-evm==0.3.0a20
+
+[options.package_data]
+* =
+ data/TokenEndorser.json
+ data/TokenEndorser.bin
+
+[options.entry_points]
+console_scripts =
+ eth-token-endorser = eth_token_endorser.runnable.deploy:main
diff --git a/python/setup.py b/python/setup.py
@@ -0,0 +1,4 @@
+from setuptools import setup
+
+setup(
+ )
diff --git a/solidity/Makefile b/solidity/Makefile
@@ -0,0 +1,9 @@
+all:
+ solc TokenEndorser.sol --abi | awk 'NR>3' > TokenEndorser.json
+ solc TokenEndorser.sol --bin | awk 'NR>3' > TokenEndorser.bin
+ truncate -s -1 TokenEndorser.bin
+
+test: all
+ python test.py
+
+.PHONY: test
diff --git a/solidity/TokenEndorser.bin b/solidity/TokenEndorser.bin
@@ -0,0 +1 @@
+608060405234801561001057600080fd5b506001600081905550600460009080600181540180825580915050600190039060005260206000200160009091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610bc48061008c6000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c80632d580ef61461005c578063427f91a61461008c5780634f64b2be146100bc578063c82d6828146100ec578063e6088e831461011c575b600080fd5b6100766004803603810190610071919061079a565b61014c565b604051610083919061095e565b60405180910390f35b6100a660048036038101906100a19190610771565b610561565b6040516100b39190610994565b60405180910390f35b6100d660048036038101906100d19190610864565b610579565b6040516100e39190610943565b60405180910390f35b610106600480360381019061010191906107d6565b6105b8565b6040516101139190610994565b60405180910390f35b61013660048036038101906101319190610812565b6105e9565b6040516101439190610979565b60405180910390f35b600061015783610601565b50600080602867ffffffffffffffff81111561019c577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040519080825280601f01601f1916602001820160405280156101ce5781602001600182028036833780820191505090505b5090506000856040516020016101e49190610911565b604051602081830303815290604052905060005b60148110156102c05781818151811061023a577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b602001015160f81c60f81b83828151811061027e577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535080806102b890610aa0565b9150506101f8565b50336040516020016102d29190610911565b604051602081830303815290604052905060005b60148110156103ba57818181518110610328577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b602001015160f81c60f81b8360148361034191906109c5565b81518110610378577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535080806103b290610aa0565b9150506102e6565b506002826040516103cb919061092c565b602060405180830381855afa1580156103e8573d6000803e3d6000fd5b5050506040513d601f19601f8201168201806040525081019061040b919061083b565b92506000801b60016000858152602001908152602001600020541461042f57600080fd5b846001600085815260200190815260200160002081905550600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600260008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490806001815401808255809150506001900390600052602060002001600090919091909150556000543373ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167fb8a4220b7c147fc6e04861db14024976c551727749a09806414670fac57e53408860405161054c9190610979565b60405180910390a46001935050505092915050565b60026020528060005260406000206000915090505481565b6004818154811061058957600080fd5b906000526020600020016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600360205281600052604060002081815481106105d457600080fd5b90600052602060002001600091509150505481565b60016020528060005260406000206000915090505481565b600080600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205411156106535760009050610718565b6004829080600181540180825580915050600190039060005260206000200160009091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600054600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555060008081548092919061070e90610aa0565b9190505550600190505b919050565b60008135905061072c81610b49565b92915050565b60008135905061074181610b60565b92915050565b60008151905061075681610b60565b92915050565b60008135905061076b81610b77565b92915050565b60006020828403121561078357600080fd5b60006107918482850161071d565b91505092915050565b600080604083850312156107ad57600080fd5b60006107bb8582860161071d565b92505060206107cc85828601610732565b9150509250929050565b600080604083850312156107e957600080fd5b60006107f78582860161071d565b92505060206108088582860161075c565b9150509250929050565b60006020828403121561082457600080fd5b600061083284828501610732565b91505092915050565b60006020828403121561084d57600080fd5b600061085b84828501610747565b91505092915050565b60006020828403121561087657600080fd5b60006108848482850161075c565b91505092915050565b61089681610a1b565b82525050565b6108ad6108a882610a1b565b610ae9565b82525050565b6108bc81610a2d565b82525050565b6108cb81610a39565b82525050565b60006108dc826109af565b6108e681856109ba565b93506108f6818560208601610a6d565b80840191505092915050565b61090b81610a63565b82525050565b600061091d828461089c565b60148201915081905092915050565b600061093882846108d1565b915081905092915050565b6000602082019050610958600083018461088d565b92915050565b600060208201905061097360008301846108b3565b92915050565b600060208201905061098e60008301846108c2565b92915050565b60006020820190506109a96000830184610902565b92915050565b600081519050919050565b600081905092915050565b60006109d082610a63565b91506109db83610a63565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03821115610a1057610a0f610b0d565b5b828201905092915050565b6000610a2682610a43565b9050919050565b60008115159050919050565b6000819050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60005b83811015610a8b578082015181840152602081019050610a70565b83811115610a9a576000848401525b50505050565b6000610aab82610a63565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415610ade57610add610b0d565b5b600182019050919050565b6000610af482610afb565b9050919050565b6000610b0682610b3c565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008160601b9050919050565b610b5281610a1b565b8114610b5d57600080fd5b50565b610b6981610a39565b8114610b7457600080fd5b50565b610b8081610a63565b8114610b8b57600080fd5b5056fea2646970667358221220bdfe2b23cf9bc8fac35e8b06e83bc20d08b1c9b009c2e1d7c2ccf4932a53601e64736f6c63430008000033
+\ No newline at end of file
diff --git a/solidity/TokenEndorser.json b/solidity/TokenEndorser.json
@@ -0,0 +1 @@
+[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_token","type":"address"},{"indexed":true,"internalType":"address","name":"_adder","type":"address"},{"indexed":true,"internalType":"uint256","name":"_index","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"_data","type":"bytes32"}],"name":"EndorsementAdded","type":"event"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"bytes32","name":"_data","type":"bytes32"}],"name":"add","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"endorsement","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"endorsers","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tokenIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"tokens","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]
diff --git a/solidity/TokenEndorser.sol b/solidity/TokenEndorser.sol
@@ -0,0 +1,49 @@
+pragma solidity >=0.6.12;
+
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+contract TokenEndorsement {
+
+ uint256 count;
+ mapping ( bytes32 => bytes32 ) public endorsement;
+ mapping ( address => uint256 ) public tokenIndex;
+ mapping ( address => uint256[] ) public endorsers;
+ address[] public tokens;
+
+ event EndorsementAdded(address indexed _token, address indexed _adder, uint256 indexed _index, bytes32 _data);
+
+ constructor() {
+ count = 1;
+ tokens.push(address(0x0));
+ }
+
+ function register(address _token) private returns (bool) {
+ if (tokenIndex[_token] > 0) {
+ return false;
+ }
+ tokens.push(_token);
+ tokenIndex[_token] = count;
+ count++;
+ return true;
+ }
+
+ function add(address _token, bytes32 _data) public returns (bool) {
+ register(_token);
+ bytes32 k;
+ bytes memory signMaterial = new bytes(40);
+ bytes memory addrBytes = abi.encodePacked(_token);
+ for (uint256 i = 0; i < 20; i++) {
+ signMaterial[i] = addrBytes[i];
+ }
+ addrBytes = abi.encodePacked(msg.sender);
+ for (uint256 i = 0; i < 20; i++) {
+ signMaterial[i+20] = addrBytes[i];
+ }
+ k = sha256(signMaterial);
+ require(endorsement[k] == bytes32(0x00));
+ endorsement[k] = _data;
+ endorsers[msg.sender].push(tokenIndex[_token]);
+ emit EndorsementAdded(_token, msg.sender, count, _data);
+ return true;
+ }
+}
diff --git a/solidity/test.py b/solidity/test.py
@@ -0,0 +1,78 @@
+import logging
+import os
+import json
+import hashlib
+
+import web3
+import eth_tester
+
+logging.basicConfig(level=logging.DEBUG)
+logg = logging.getLogger()
+
+
+eth_params = eth_tester.backends.pyevm.main.get_default_genesis_params({
+ 'gas_limit': 9000000,
+ })
+backend = eth_tester.PyEVMBackend(eth_params)
+instance = eth_tester.EthereumTester(backend)
+provider = web3.Web3.EthereumTesterProvider(instance)
+w3 = web3.Web3(provider)
+
+
+f = open('TokenEndorser.bin', 'r')
+bytecode = f.read()
+f.close()
+
+f = open('TokenEndorser.json', 'r')
+abi = json.load(f)
+f.close()
+
+token_address = web3.Web3.toChecksumAddress('0x' + os.urandom(20).hex())
+
+c = w3.eth.contract(abi=abi, bytecode=bytecode)
+tx_hash = c.constructor().transact({'from': w3.eth.accounts[0]})
+r = w3.eth.getTransactionReceipt(tx_hash)
+logg.debug('contract {} initial token {}'.format(r.contractAddress, token_address))
+
+c = w3.eth.contract(abi=abi, address=r.contractAddress)
+d = '0x' + os.urandom(32).hex()
+
+# Initial token will fail in any case
+c.functions.add(token_address, d).transact({'from':w3.eth.accounts[0]})
+
+fail = False
+try:
+ c.functions.add(token_address, d).transact({'from':w3.eth.accounts[0]})
+except:
+ fail = True
+ pass
+
+if not fail:
+ raise RuntimeError('expected fail on register same token to same address')
+
+
+c.functions.add(token_address, d).transact({'from':w3.eth.accounts[1]})
+
+
+h = hashlib.new('sha256')
+h.update(bytes.fromhex(token_address[2:]))
+h.update(bytes.fromhex(w3.eth.accounts[0][2:]))
+z = h.digest()
+
+assert d[2:] == c.functions.endorsement(z.hex()).call().hex()
+
+
+another_token_address = web3.Web3.toChecksumAddress('0x' + os.urandom(20).hex())
+c.functions.add(another_token_address, d).transact({'from':w3.eth.accounts[0]})
+
+assert c.functions.endorsers(w3.eth.accounts[0], 0).call() == 1
+assert c.functions.endorsers(w3.eth.accounts[1], 0).call() == 1
+assert c.functions.endorsers(w3.eth.accounts[0], 1).call() == 2
+
+assert c.functions.tokens(1).call() == token_address
+assert c.functions.tokens(2).call() == another_token_address
+
+assert c.functions.tokenIndex(token_address).call() == 1
+assert c.functions.tokenIndex(another_token_address).call() == 2
+
+