Compare commits

...

7 Commits

Author SHA1 Message Date
lash 6abe08f2f5
remove conftest file 2023-03-26 06:46:37 +01:00
lash faa9e71bac
Reimplement tests for stdlib unittest 2023-03-26 06:45:20 +01:00
lash 5a81927fa1
Correct identifier array interface name 2023-03-25 16:48:40 +00:00
lash c1d95613e9
Revert registry interface id 2023-03-25 14:45:46 +00:00
lash 695c8afd78
Add event for address added 2023-03-25 14:38:19 +00:00
lash 428de59a21
Change solidity contract comments 2023-03-25 12:56:50 +00:00
lash c6e3c24a88
Remove unused chain id bind 2023-03-25 08:13:49 +00:00
11 changed files with 116 additions and 86 deletions

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
[{"inputs":[{"internalType":"bytes32[]","name":"_identifiers","type":"bytes32[]"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"_newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"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"},{"internalType":"bytes32","name":"_reference","type":"bytes32"}],"name":"bind","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","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"}],"name":"set","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_sum","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]
[{"inputs":[{"internalType":"bytes32[]","name":"_identifiers","type":"bytes32[]"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"_key","type":"bytes32"},{"indexed":false,"internalType":"address","name":"_address","type":"address"}],"name":"AddressKey","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"_newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[{"internalType":"bytes32","name":"_identifier","type":"bytes32"}],"name":"addressOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"identifier","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"identifierCount","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":"bytes32","name":"_identifier","type":"bytes32"},{"internalType":"address","name":"_address","type":"address"}],"name":"set","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_sum","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]

View File

@ -1 +1 @@
{"compiler":{"version":"0.8.18+commit.87f61d96"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"bytes32[]","name":"_identifiers","type":"bytes32[]"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"_newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"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"},{"internalType":"bytes32","name":"_reference","type":"bytes32"}],"name":"bind","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","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"}],"name":"set","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_sum","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"compilationTarget":{"Registry.sol":"CICRegistry"},"evmVersion":"byzantium","libraries":{},"metadata":{"bytecodeHash":"ipfs"},"optimizer":{"enabled":false,"runs":200},"remappings":[]},"sources":{"Registry.sol":{"keccak256":"0x68d1247a7776b5e3099c175b8108cc5b82950d5372888fdf38a76715d02eaa57","license":"GPL-3.0-or-later","urls":["bzz-raw://4f4dab8d265dc89ba26c3f9b90eedeb190af46575361dd9bc24fc44a80eded36","dweb:/ipfs/QmQe2NfXMfqDMacnenJaoW29iaM5nqdzYsiZVZgqMy64zb"]}},"version":1}
{"compiler":{"version":"0.8.18+commit.87f61d96"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"bytes32[]","name":"_identifiers","type":"bytes32[]"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"_key","type":"bytes32"},{"indexed":false,"internalType":"address","name":"_address","type":"address"}],"name":"AddressKey","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"_newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[{"internalType":"bytes32","name":"_identifier","type":"bytes32"}],"name":"addressOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"identifier","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"identifierCount","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":"bytes32","name":"_identifier","type":"bytes32"},{"internalType":"address","name":"_address","type":"address"}],"name":"set","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_sum","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"compilationTarget":{"Registry.sol":"ContractRegistry"},"evmVersion":"byzantium","libraries":{},"metadata":{"bytecodeHash":"ipfs"},"optimizer":{"enabled":false,"runs":200},"remappings":[]},"sources":{"Registry.sol":{"keccak256":"0x5e3faff7b6f6451b4e924784ad8732e1f4fdd4b49de85c1f64a8b185b39fa5bd","license":"AGPL-3.0-or-later","urls":["bzz-raw://c33a89bb4aa9200fac7cb354aa9499961d11f7d4d387a536f8b9910c45d7fc87","dweb:/ipfs/QmZnK2CtsRME2WW59TT1EqmrgDNz3CaL6GRDxaQoGL2h1R"]}},"version":1}

View File

@ -43,6 +43,26 @@ class Registry(TxFactory):
return o
def identifier(self, contract_address, idx, sender_address=ZERO_ADDRESS, id_generator=None):
j = JSONRPCRequest(id_generator)
o = j.template()
o['method'] = 'eth_call'
enc = ABIContractEncoder()
enc.method('identifier')
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))
o = j.finalize(o)
return o
def identifier_count(self, contract_address, sender_address=ZERO_ADDRESS, id_generator=None):
return self.call_noarg('identifierCount', contract_address, sender_address=sender_address, id_generator=id_generator)
@classmethod
def parse_address_of(self, v):
return abi_decode_single(ABIContractType.ADDRESS, v)

View File

@ -94,6 +94,8 @@ class ContractRegistry(Registry):
def set(self, contract_address, sender_address, identifier_string, address):
if len(identifier_string) > 32:
raise ValueError('String too long')
enc = ABIContractEncoder()
enc.method('set')
enc.typ(ABIContractType.BYTES32)

View File

@ -4,7 +4,7 @@ set -e
set -x
default_pythonpath=$PYTHONPATH:.
export PYTHONPATH=${default_pythonpath:-.}
for f in `ls tests/*.py`; do
for f in `ls tests/test_*.py`; do
python $f
if [ $? -gt 0 ]; then
exit 1

View File

@ -1,6 +1,6 @@
[metadata]
name = eth-contract-registry
version = 0.10.0
version = 0.11.0
description = Ethereum Smart Contract key-value registry
author = Louis Holbrook
author_email = dev@holbrook.no

View File

@ -1,3 +1,4 @@
pytest==6.0.1
eth-tester==0.5.0b3
py-evm==0.3.0a20
eth-erc20~=0.7.2

View File

@ -1,4 +0,0 @@
# external imports
from chainlib.eth.pytest import *
from eth_contract_registry.pytest import *

View File

@ -1,74 +1,86 @@
# standard imports
import os
import unittest
import json
import logging
import hashlib
# external imports
import logging
import pytest
from chainlib.eth.tx import (
receipt,
transaction,
)
from chainlib.connection import RPCConnection
from chainlib.eth.unittest.ethtester import EthTesterCase
from chainlib.eth.contract import (
ABIContractEncoder,
ABIContractType,
abi_decode_single,
)
from chainlib.eth.nonce import RPCNonceOracle
from chainlib.eth.address import to_checksum_address
from chainlib.eth.tx import receipt
from giftable_erc20_token import GiftableToken
from hexathon import (
add_0x,
strip_0x,
)
add_0x,
strip_0x,
same as same_hex,
)
# local imports
from eth_contract_registry import Registry
from eth_contract_registry.registry import ContractRegistry
from eth_contract_registry.encoding import from_identifier_hex
from eth_contract_registry.pytest.fixtures_registry import valid_identifiers
from eth_contract_registry import Registry
logging.basicConfig(level=logging.DEBUG)
logg = logging.getLogger()
valid_identifiers += [
'FooContract',
]
def test_set(
default_chain_spec,
registry,
eth_accounts,
eth_rpc,
eth_signer,
roles,
):
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())
class TestContractRegistry(EthTesterCase):
nonce_oracle = RPCNonceOracle(roles['CONTRACT_DEPLOYER'], eth_rpc)
builder = ContractRegistry(default_chain_spec, signer=eth_signer, nonce_oracle=nonce_oracle)
def setUp(self):
super(TestContractRegistry, self).setUp()
self.registry_ids = ['FOo', 'Bar', 'baz', 'XYZZY']
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 == strip_0x(registry)
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
c = ContractRegistry(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
(tx_hash_hex, o) = c.constructor(self.accounts[0], self.registry_ids)
self.rpc.do(o)
o = receipt(tx_hash_hex)
rcpt = self.rpc.do(o)
self.assertEqual(rcpt['status'], 1)
self.address = rcpt['contract_address']
logg.info('registry published to ' + self.address)
(tx_hash_hex, o) = builder.set(registry, roles['CONTRACT_DEPLOYER'], 'ContractRegistry', addr_registry)
r = eth_rpc.do(o)
o = receipt(r)
rcpt = eth_rpc.do(o)
assert rcpt['status'] == 0
(tx_hash_hex, o) = builder.set(registry, roles['CONTRACT_DEPLOYER'], 'FooContract', addr_foo)
r = eth_rpc.do(o)
o = receipt(r)
rcpt = eth_rpc.do(o)
assert rcpt['status'] == 1
def test_retrieve(self):
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
c = ContractRegistry(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
(tx_hash_hex, o) = c.set(self.address, self.accounts[0], 'FOO', self.address)
r = self.rpc.do(o)
o = receipt(tx_hash_hex)
r = self.rpc.do(o)
self.assertEqual(r['status'], 0)
builder = Registry(default_chain_spec)
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
(tx_hash_hex, o) = c.set(self.address, self.accounts[0], 'FOo', self.address)
r = self.rpc.do(o)
o = receipt(tx_hash_hex)
r = self.rpc.do(o)
self.assertEqual(r['status'], 1)
c = ContractRegistry(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
o = c.address_of(self.address, 'FOo', sender_address=self.accounts[0])
r = self.rpc.do(o)
self.assertTrue(same_hex(strip_0x(r)[24:], self.address))
def test_identifiers(self):
c = Registry(self.chain_spec)
o = c.identifier_count(self.address, sender_address=self.accounts[0])
r = self.rpc.do(o)
self.assertEqual(int(r, 16), 4)
for i in range(4):
o = c.identifier(self.address, i, sender_address=self.accounts[0])
r = self.rpc.do(o)
r = bytes.fromhex(strip_0x(r))
r = r.strip(b'\x00')
s = r.decode('utf-8')
self.assertEqual(s, self.registry_ids[i])
if __name__ == '__main__':
unittest.main()

View File

@ -1,54 +1,51 @@
pragma solidity >0.6.11;
pragma solidity >=0.8.0;
// 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
// SPDX-License-Identifier: AGPL-3.0-or-later
// File-version: 4
// Description: Keyed smart contract registry
contract CICRegistry {
contract ContractRegistry {
mapping (bytes32 => address) entries;
// Implements ERC173
address public owner;
// Implements RegistryClient
bytes32[] public identifiers;
mapping (bytes32 => address) entries; // contractidentifier -> address
mapping (bytes32 => bytes32[]) entryBindings; // contractidentifier -> chainidentifier
// Implements Registry
bytes32[] public identifier;
// Implements ERC173
event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);
// Implements Registry
event AddressKey(bytes32 indexed _key, address _address);
constructor(bytes32[] memory _identifiers) {
owner = msg.sender;
for (uint i = 0; i < _identifiers.length; i++) {
identifiers.push(_identifiers[i]);
identifier.push(_identifiers[i]);
}
}
// Implements Registry
// Assign address to identifier
function set(bytes32 _identifier, address _address) public returns (bool) {
require(msg.sender == owner);
require(entries[_identifier] == address(0));
require(_address != address(0));
bool found = false;
for (uint i = 0; i < identifiers.length; i++) {
if (identifiers[i] == _identifier) {
for (uint i = 0; i < identifier.length; i++) {
if (identifier[i] == _identifier) {
found = true;
}
}
require(found);
entries[_identifier] = _address;
return true;
}
// Implements Registry
function bind(bytes32 _identifier, bytes32 _reference) public returns (bool) {
require(msg.sender == owner);
require(entries[_identifier] != address(0));
emit AddressKey(_identifier, _address);
entryBindings[_identifier].push(_reference);
return true;
}
@ -56,24 +53,26 @@ contract CICRegistry {
function transferOwnership(address _newOwner) public returns (bool) {
address _oldOwner;
require(msg.sender == owner);
require(msg.sender == owner, 'ERR_AXX');
_oldOwner = owner;
owner = _newOwner;
emit OwnershipTransferred(_oldOwner, _newOwner);
return true;
}
// Implements RegistryClient
// Implements Registry
function addressOf(bytes32 _identifier) public view returns (address) {
return entries[_identifier];
}
// Implements Registry
function identifierCount() public view returns(uint256) {
return identifier.length;
}
// Implements ERC165
function supportsInterface(bytes4 _sum) public pure returns (bool) {
if (_sum == 0xd719b0cc) { // Registry
return true;
}
if (_sum == 0x93c68796) { // RegistryClient
if (_sum == 0xeffbf671) { // Registry
return true;
}
if (_sum == 0x01ffc9a7) { // ERC165