commit 15cd758fae50278fc80c2fae5a009074261f0ee3
Author: nolash <dev@holbrook.no>
Date: Tue, 16 Mar 2021 13:53:22 +0100
Initial commit, ported from cic-registry
Diffstat:
17 files changed, 425 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,2 @@
+__pycache__
+*.pyc
diff --git a/python/contract_registry/data/Registry.bin b/python/contract_registry/data/Registry.bin
@@ -0,0 +1 @@
+60806040523480156200001157600080fd5b5060405162000a7138038062000a718339818101604052810190620000379190620001c5565b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060005b815181101562000104576001828281518110620000c0577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b602002602001015190806001815401808255809150506001900390600052602060002001600090919091909150558080620000fb90620002ac565b9150506200007a565b505062000383565b6000620001236200011d8462000233565b6200020a565b905080838252602082019050828560208602820111156200014357600080fd5b60005b858110156200017757816200015c8882620001ae565b84526020840193506020830192505060018101905062000146565b5050509392505050565b600082601f8301126200019357600080fd5b8151620001a58482602086016200010c565b91505092915050565b600081519050620001bf8162000369565b92915050565b600060208284031215620001d857600080fd5b600082015167ffffffffffffffff811115620001f357600080fd5b620002018482850162000181565b91505092915050565b60006200021662000229565b905062000224828262000276565b919050565b6000604051905090565b600067ffffffffffffffff82111562000251576200025062000329565b5b602082029050602081019050919050565b6000819050919050565b6000819050919050565b620002818262000358565b810181811067ffffffffffffffff82111715620002a357620002a262000329565b5b80604052505050565b6000620002b9826200026c565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415620002ef57620002ee620002fa565b5b600182019050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000601f19601f8301169050919050565b620003748162000262565b81146200038057600080fd5b50565b6106de80620003936000396000f3fe608060405234801561001057600080fd5b506004361061007f576000357c01000000000000000000000000000000000000000000000000000000009004806328f2d4da146100845780635deed44e146100b45780638da5cb5b146100e45780639648eca214610102578063bb34534c14610132578063f0f86f7114610162575b600080fd5b61009e600480360381019061009991906104f2565b610192565b6040516100ab919061057e565b60405180910390f35b6100ce60048036038101906100c99190610466565b6101b6565b6040516100db919061057e565b60405180910390f35b6100ec6101d3565b6040516100f99190610548565b60405180910390f35b61011c60048036038101906101179190610466565b6101f7565b604051610129919061057e565b60405180910390f35b61014c60048036038101906101479190610466565b610214565b6040516101599190610548565b60405180910390f35b61017c6004803603810190610177919061048f565b610251565b6040516101899190610563565b60405180910390f35b600181815481106101a257600080fd5b906000526020600020016000915090505481565b600060046000838152602001908152602001600020549050919050565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600060036000838152602001908152602001600020549050919050565b60006002600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146102ac57600080fd5b600073ffffffffffffffffffffffffffffffffffffffff166002600087815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161461031857600080fd5b6000805b60018054905081101561038d578660018281548110610364577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9060005260206000200154141561037a57600191505b8080610385906105eb565b91505061031c565b508061039857600080fd5b846002600088815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508360036000888152602001908152602001600020819055508260046000868152602001908152602001600020819055506001915050949350505050565b60008135905061043681610663565b92915050565b60008135905061044b8161067a565b92915050565b60008135905061046081610691565b92915050565b60006020828403121561047857600080fd5b60006104868482850161043c565b91505092915050565b600080600080608085870312156104a557600080fd5b60006104b38782880161043c565b94505060206104c487828801610427565b93505060406104d58782880161043c565b92505060606104e68782880161043c565b91505092959194509250565b60006020828403121561050457600080fd5b600061051284828501610451565b91505092915050565b61052481610599565b82525050565b610533816105ab565b82525050565b610542816105b7565b82525050565b600060208201905061055d600083018461051b565b92915050565b6000602082019050610578600083018461052a565b92915050565b60006020820190506105936000830184610539565b92915050565b60006105a4826105c1565b9050919050565b60008115159050919050565b6000819050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60006105f6826105e1565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82141561062957610628610634565b5b600182019050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b61066c81610599565b811461067757600080fd5b50565b610683816105b7565b811461068e57600080fd5b50565b61069a816105e1565b81146106a557600080fd5b5056fea2646970667358221220c55a8666b79abd3b9abfdd11a7f7afb300ab9783847b841595839f89b1401e6064736f6c63430008020033
+\ No newline at end of file
diff --git a/python/contract_registry/data/Registry.json b/python/contract_registry/data/Registry.json
@@ -0,0 +1 @@
+[{"inputs":[{"internalType":"bytes32[]","name":"_identifiers","type":"bytes32[]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"bytes32","name":"_identifier","type":"bytes32"}],"name":"addressOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_identifier","type":"bytes32"}],"name":"chainOf","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_chain","type":"bytes32"}],"name":"configSumOf","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"identifiers","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_identifier","type":"bytes32"},{"internalType":"address","name":"_address","type":"address"},{"internalType":"bytes32","name":"_chainDescriptor","type":"bytes32"},{"internalType":"bytes32","name":"_chainConfig","type":"bytes32"}],"name":"set","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]
diff --git a/python/contract_registry/encoding.py b/python/contract_registry/encoding.py
@@ -0,0 +1,32 @@
+# standard imports
+import logging
+
+# external imports
+from hexathon import strip_0x
+
+logg = logging.getLogger(__name__)
+
+
+def to_text(b):
+ b = b[:b.find(0)]
+ # TODO: why was this part of this method previously?
+ # if len(b) % 2 > 0:
+ # b = b'\x00' + b
+ return b.decode('utf-8')
+
+
+def from_text(txt):
+ return '0x{:0<64s}'.format(txt.encode('utf-8').hex())
+
+
+def from_identifier(b):
+ return to_text(b)
+
+
+def from_identifier_hex(hx):
+ b = bytes.fromhex(strip_0x(hx))
+ return from_identifier(b)
+
+
+def to_identifier(txt):
+ return from_text(txt)
diff --git a/python/contract_registry/pytest/__init__.py b/python/contract_registry/pytest/__init__.py
@@ -0,0 +1 @@
+from .fixtures_registry import *
diff --git a/python/contract_registry/pytest/fixtures_registry.py b/python/contract_registry/pytest/fixtures_registry.py
@@ -0,0 +1,36 @@
+# standard imports
+import logging
+
+# external imports
+import pytest
+from chainlib.eth.connection import RPCConnection
+from chainlib.eth.tx import receipt
+
+# local imports
+from contract_registry.registry import Registry
+
+logg = logging.getLogger(__name__)
+
+valid_identifiers = [
+ 'ContractRegistry',
+ ]
+
+@pytest.fixture(scope='function')
+def registry(
+ init_eth_tester,
+ init_eth_rpc,
+ eth_accounts,
+ ):
+
+ conn = RPCConnection.connect('default')
+ builder = Registry(signer=conn.signer)
+ (tx_hash_hex, o) = builder.constructor(eth_accounts[0], valid_identifiers)
+ r = conn.do(o)
+ logg.debug('r {}'.format(r))
+
+ o = receipt(r)
+ rcpt = conn.do(o)
+
+ assert rcpt['status'] == 1
+ return rcpt['contract_address']
+
diff --git a/python/contract_registry/registry.py b/python/contract_registry/registry.py
@@ -0,0 +1,140 @@
+# standard imports
+import os
+import logging
+import json
+
+# third-party imports
+from chainlib.eth.contract import (
+ ABIContractEncoder,
+ ABIContractType,
+ abi_decode_single,
+ )
+from chainlib.chain import ChainSpec
+from chainlib.eth.constant import (
+ ZERO_ADDRESS,
+ ZERO_CONTENT,
+ MAX_UINT,
+ )
+from chainlib.eth.rpc import (
+ jsonrpc_template,
+ )
+from hexathon import (
+ even,
+ add_0x,
+ )
+from chainlib.eth.tx import TxFactory
+
+# local imports
+from .encoding import to_identifier
+
+logg = logging.getLogger(__name__)
+
+moddir = os.path.dirname(__file__)
+datadir = os.path.join(moddir, 'data')
+
+
+class Registry(TxFactory):
+
+ default_chain_spec = None
+ __chains_registry = {}
+
+ __abi = None
+ __bytecode = None
+
+
+
+ @staticmethod
+ def abi():
+ if Registry.__abi == None:
+ f = open(os.path.join(datadir, 'Registry.json'), 'r')
+ Registry.__abi = json.load(f)
+ f.close()
+ return Registry.__abi
+
+
+ @staticmethod
+ def bytecode():
+ if Registry.__bytecode == None:
+ f = open(os.path.join(datadir, 'Registry.bin'))
+ Registry.__bytecode = f.read()
+ f.close()
+ return Registry.__bytecode
+
+
+ def constructor(self, sender_address, identifier_strings=[]):
+ # TODO: handle arrays in chainlib encode instead
+ enc = ABIContractEncoder()
+ enc.uint256(32)
+ enc.uint256(len(identifier_strings))
+ for s in identifier_strings:
+ enc.bytes32(to_identifier(s))
+ data = enc.get_contents()
+
+ tx = self.template(sender_address, b'', use_nonce=True)
+ tx = self.set_code(tx, Registry.bytecode() + data)
+ logg.debug('bytecode {}\ndata {}\ntx {}'.format(Registry.bytecode(), data, tx))
+ return self.build(tx)
+
+
+ @staticmethod
+ def address(address=None):
+ if address != None:
+ Registry.__address = address
+ return Registry.__address
+
+
+ @staticmethod
+ def load_for(chain_spec):
+ chain_str = str(chain_spec)
+ raise NotImplementedError()
+
+
+ def address_of(self, contract_address, identifier_string, sender_address=ZERO_ADDRESS):
+ o = jsonrpc_template()
+ o['method'] = 'eth_call'
+ enc = ABIContractEncoder()
+ enc.method('addressOf')
+ enc.typ(ABIContractType.BYTES32)
+ identifier = to_identifier(identifier_string)
+ enc.bytes32(identifier)
+ data = add_0x(enc.encode())
+ tx = self.template(sender_address, contract_address)
+ tx = self.set_code(tx, data)
+ o['params'].append(self.normalize(tx))
+ return o
+
+
+ def parse_address_of(self, v):
+ return abi_decode_single(ABIContractType.ADDRESS, v)
+
+
+ def set(self, contract_address, sender_address, identifier_string, address, chain_descriptor_hash, chain_config_hash):
+ enc = ABIContractEncoder()
+ enc.method('set')
+ enc.typ(ABIContractType.BYTES32)
+ enc.typ(ABIContractType.ADDRESS)
+ enc.typ(ABIContractType.BYTES32)
+ enc.typ(ABIContractType.BYTES32)
+ identifier = to_identifier(identifier_string)
+ enc.bytes32(identifier)
+ enc.address(address)
+ enc.bytes32(chain_descriptor_hash)
+ enc.bytes32(chain_config_hash)
+ data = enc.encode()
+ tx = self.template(sender_address, contract_address, use_nonce=True)
+ tx = self.set_code(tx, data)
+ return self.build(tx)
+
+
+ def identifier(self, contract_address, idx, sender_address=ZERO_ADDRESS):
+ o = jsonrpc_template()
+ o['method'] = 'eth_call'
+ enc = ABIContractEncoder()
+ enc.method('identifiers')
+ enc.typ(ABIContractType.UINT256)
+ enc.uint256(idx)
+ data = add_0x(enc.encode())
+ tx = self.template(sender_address, contract_address)
+ tx = self.set_code(tx, data)
+ o['params'].append(self.normalize(tx))
+ return o
diff --git a/python/contract_registry/unittest/base.py b/python/contract_registry/unittest/base.py
diff --git a/python/gmon.out b/python/gmon.out
Binary files differ.
diff --git a/python/requirements.txt b/python/requirements.txt
@@ -0,0 +1,3 @@
+confini~=0.3.6rc2
+crypto-dev-signer~=0.4.13rc6
+chainlib~=0.0.1a25
diff --git a/python/setup.cfg b/python/setup.cfg
@@ -0,0 +1,40 @@
+[metadata]
+name = eth-contract-registry
+version = 0.5.3a25
+description = Contract registry
+author = Louis Holbrook
+author_email = dev@holbrook.no
+url = https://gitlab.com/grassrootseconomics/cic-registry
+keywords =
+ cryptocurrency
+ ethereum
+ smartcontracts
+classifiers =
+ Programming Language :: Python :: 3
+ Operating System :: OS Independent
+ Development Status :: 3 - Alpha
+ Environment :: Console
+ Intended Audience :: Developers
+ License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
+ Topic :: Software Development :: Libraries
+ Topic :: Internet
+ #Topic :: Blockchain :: EVM
+license = GPL3
+licence_files =
+ LICENSE.txt
+
+[options]
+include_package_data = True
+python_requires = >= 3.6
+packages =
+ contract_registry
+ contract_registry.helper
+ contract_registry.pytest
+ contract_registry.runnable
+
+[options.entry_points]
+console_scripts =
+ contract-registry-deploy = contract_registry.runnable.deploy:main
+ contract-registry-set = conrtact_registry.runnable.set:main
+ contract-registry-seal = contract_registry.runnable.seal:main
+ contract-registry-list = contract_registry.runnable.list:main
diff --git a/python/setup.py b/python/setup.py
@@ -0,0 +1,11 @@
+from setuptools import setup
+
+setup(
+ package_data={
+ '': [
+ 'data/*.abi.json',
+ 'data/*.bin',
+ ],
+ },
+ include_package_data=True,
+ )
diff --git a/python/test_requirements.txt b/python/test_requirements.txt
@@ -0,0 +1,3 @@
+pytest==6.0.1
+eth-tester==0.5.0b3
+py-evm==0.3.0a20
diff --git a/python/tests/conftest.py b/python/tests/conftest.py
@@ -0,0 +1,4 @@
+# external imports
+from chainlib.eth.pytest import *
+from contract_registry.pytest import *
+
diff --git a/python/tests/test_basic.py b/python/tests/test_basic.py
@@ -0,0 +1,82 @@
+# standard imports
+import os
+
+# external imports
+import logging
+import pytest
+from chainlib.eth.tx import (
+ receipt,
+ transaction,
+ )
+from chainlib.eth.connection import RPCConnection
+from chainlib.eth.contract import (
+ ABIContractEncoder,
+ ABIContractType,
+ abi_decode_single,
+ )
+from chainlib.eth.nonce import NodeNonceOracle
+from chainlib.eth.address import to_checksum_address
+from hexathon import (
+ add_0x,
+ strip_0x,
+ )
+
+# local imports
+from contract_registry.registry import Registry
+from contract_registry.encoding import from_identifier_hex
+from contract_registry.pytest.fixtures_registry import valid_identifiers
+
+logg = logging.getLogger()
+
+valid_identifiers += [
+ 'FooContract',
+ ]
+
+def test_set(
+ registry,
+ eth_accounts,
+ eth_rpc,
+ eth_signer,
+ ):
+
+ addr_registry = to_checksum_address(os.urandom(20).hex())
+ addr_foo = to_checksum_address(os.urandom(20).hex())
+ bogus_hash = add_0x(os.urandom(32).hex())
+
+ #conn = RPCConnection.connect('default')
+ nonce_oracle = NodeNonceOracle(eth_accounts[0], eth_rpc)
+ builder = Registry(signer=eth_signer, nonce_oracle=nonce_oracle)
+ (tx_hash_hex, o) = builder.set(registry, eth_accounts[0], 'ContractRegistry', addr_registry, bogus_hash, bogus_hash)
+ r = eth_rpc.do(o)
+
+ o = receipt(r)
+ rcpt = eth_rpc.do(o)
+ assert rcpt['status'] == 1
+
+ o = builder.identifier(registry, 0, sender_address=eth_accounts[0])
+ r = eth_rpc.do(o)
+ r = from_identifier_hex(r)
+ assert r == 'ContractRegistry'
+
+ o = builder.address_of(registry, 'ContractRegistry', sender_address=eth_accounts[0])
+ r = eth_rpc.do(o)
+ r = abi_decode_single(ABIContractType.ADDRESS, r)
+ assert r == addr_registry
+
+ (tx_hash_hex, o) = builder.set(registry, eth_accounts[0], 'ContractRegistry', addr_registry, bogus_hash, bogus_hash)
+ r = eth_rpc.do(o)
+ o = receipt(r)
+ rcpt = eth_rpc.do(o)
+ assert rcpt['status'] == 0
+
+ (tx_hash_hex, o) = builder.set(registry, eth_accounts[0], 'FooContract', addr_foo, bogus_hash, bogus_hash)
+ r = eth_rpc.do(o)
+ o = receipt(r)
+ rcpt = eth_rpc.do(o)
+ assert rcpt['status'] == 1
+
+ o = builder.address_of(registry, 'FooContract', sender_address=eth_accounts[0])
+ r = eth_rpc.do(o)
+ r = abi_decode_single(ABIContractType.ADDRESS, r)
+ assert r == addr_foo
+
diff --git a/solidity/Makefile b/solidity/Makefile
@@ -0,0 +1,10 @@
+SOLC = /usr/bin/solc
+
+all:
+ $(SOLC) --bin Registry.sol --evm-version byzantium | awk 'NR>3' > Registry.bin
+ truncate -s -1 Registry.bin
+ $(SOLC) --abi Registry.sol --evm-version byzantium | awk 'NR>3' > Registry.json
+
+install: all
+ cp -v *{json,bin} ../python/contract_registry/data/
+
diff --git a/solidity/Registry.sol b/solidity/Registry.sol
@@ -0,0 +1,58 @@
+pragma solidity >0.6.11;
+
+// Author: Louis Holbrook <dev@holbrook.no> 0826EDA1702D1E87C6E2875121D2E7BB88C2A746
+// SPDX-License-Identifier: GPL-3.0-or-later
+// File-version: 2
+// Description: Top-level smart contract registry for the CIC network
+
+
+contract CICRegistry {
+ address public owner;
+
+ bytes32[] public identifiers;
+ mapping (bytes32 => address) entries; // contractidentifier -> address
+ mapping (bytes32 => bytes32) chainIdentifiers; // contractidentifier -> chainidentifier
+ mapping (bytes32 => bytes32) chainConfigs; // chainidentifier -> chainconfig
+
+ constructor(bytes32[] memory _identifiers) public {
+ owner = msg.sender;
+ for (uint i = 0; i < _identifiers.length; i++) {
+ identifiers.push(_identifiers[i]);
+ }
+ }
+
+ function set(bytes32 _identifier, address _address, bytes32 _chainDescriptor, bytes32 _chainConfig) public returns (bool) {
+ require(msg.sender == owner);
+ require(entries[_identifier] == address(0));
+ bool found = false;
+ for (uint i = 0; i < identifiers.length; i++) {
+ if (identifiers[i] == _identifier) {
+ found = true;
+ }
+ }
+ require(found);
+
+ entries[_identifier] = _address;
+ chainIdentifiers[_identifier] = _chainDescriptor;
+ chainConfigs[_chainDescriptor] = _chainConfig;
+ return true;
+ }
+
+// function seal() public returns (bool) {
+// require(msg.sender == owner);
+// owner = address(0);
+// return true;
+// }
+
+ function addressOf(bytes32 _identifier) public view returns (address) {
+ return entries[_identifier];
+ }
+
+ function chainOf(bytes32 _identifier) public view returns (bytes32) {
+ return chainIdentifiers[_identifier];
+ }
+
+ function configSumOf(bytes32 _chain) public view returns (bytes32) {
+ return chainConfigs[_chain];
+ }
+}