Compare commits

..

No commits in common. "master" and "v0.4.1" have entirely different histories.

9 changed files with 74 additions and 119 deletions

View File

@ -1,16 +1,3 @@
* 0.5.3
- Add unittest module to packaging
* 0.5.2
- Move test fixture to unittest module
* 0.5.1
- Simplify contract events
- Split interfaces for mutable and immutable registries
* 0.4.x, 0.5.0
- Update packaging to beta
- Change licence to AGPL
* 0.3.6
- Correctly implement writer interface in contract
- Add contract json metadata in python package data
* 0.3.5 * 0.3.5
- Remove contract compile warnings - Remove contract compile warnings
* 0.3.4 * 0.3.4

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_account","type":"address"},{"indexed":false,"internalType":"bool","name":"_active","type":"bool"}],"name":"AddressActive","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_account","type":"address"}],"name":"AddressAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_account","type":"address"}],"name":"AddressRemoved","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"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_account","type":"address"}],"name":"WriterAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_account","type":"address"}],"name":"WriterDeleted","type":"event"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"activate","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"add","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_writer","type":"address"}],"name":"addWriter","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"deactivate","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_writer","type":"address"}],"name":"deleteWriter","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_i","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":[{"internalType":"address","name":"_account","type":"address"}],"name":"have","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"isActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isWriter","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"remove","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":"_account","type":"address"}],"name":"time","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}] [{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_executor","type":"address"},{"indexed":false,"internalType":"address","name":"_account","type":"address"}],"name":"AddressActive","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_executor","type":"address"},{"indexed":false,"internalType":"address","name":"_account","type":"address"}],"name":"AddressAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_executor","type":"address"},{"indexed":false,"internalType":"address","name":"_account","type":"address"}],"name":"AddressInactive","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_executor","type":"address"},{"indexed":false,"internalType":"address","name":"_account","type":"address"}],"name":"AddressRemoved","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"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_executor","type":"address"},{"indexed":false,"internalType":"address","name":"_account","type":"address"}],"name":"WriterAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_executor","type":"address"},{"indexed":false,"internalType":"address","name":"_account","type":"address"}],"name":"WriterDeleted","type":"event"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"activate","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"add","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_writer","type":"address"}],"name":"addWriter","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"deactivate","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_writer","type":"address"}],"name":"deleteWriter","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_i","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":[{"internalType":"address","name":"","type":"address"}],"name":"entryIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"have","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"isActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isWriter","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"remove","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":"_account","type":"address"}],"name":"time","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
from .base import TestAccountsIndex

View File

@ -1,38 +0,0 @@
# standard imports
import logging
# external imports
from chainlib.eth.unittest.ethtester import EthTesterCase
from chainlib.connection import RPCConnection
from chainlib.eth.nonce import RPCNonceOracle
from chainlib.eth.address import to_checksum_address
from chainlib.eth.tx import receipt
# local imports
from eth_accounts_index.registry import AccountRegistry
logg = logging.getLogger(__name__)
class TestAccountsIndex(EthTesterCase):
def setUp(self):
super(TestAccountsIndex, self).setUp()
self.conn = RPCConnection.connect(self.chain_spec, 'default')
nonce_oracle = RPCNonceOracle(self.accounts[0], conn=self.conn)
c = AccountRegistry(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
(tx_hash, o) = c.constructor(self.accounts[0])
r = self.conn.do(o)
logg.debug(f'published with hash {r}')
o = receipt(r)
r = self.conn.do(o)
self.address = to_checksum_address(r['contract_address'])
(tx_hash, o) = c.add_writer(self.address, self.accounts[0], self.accounts[0])
r = self.conn.do(o)
o = receipt(r)
r = self.conn.do(o)
self.assertEqual(r['status'], 1)

View File

@ -1,6 +1,6 @@
[metadata] [metadata]
name = eth-accounts-index name = eth-accounts-index
version = 0.5.3 version = 0.4.1
description = Accounts index evm contract tooling with permissioned writes description = Accounts index evm contract tooling with permissioned writes
author = Louis Holbrook author = Louis Holbrook
author_email = dev@holbrook.no author_email = dev@holbrook.no
@ -11,10 +11,9 @@ classifiers =
Programming Language :: Python :: 3 Programming Language :: Python :: 3
Operating System :: OS Independent Operating System :: OS Independent
Development Status :: 4 - Beta Development Status :: 4 - Beta
Environment :: Console Environment :: No Input/Output (Daemon)
Intended Audience :: Developers Intended Audience :: Developers
License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+) License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
Topic :: Software Development :: Libraries
Topic :: Internet Topic :: Internet
#Topic :: Blockchain :: EVM #Topic :: Blockchain :: EVM
license = AGPLv3+ license = AGPLv3+
@ -27,7 +26,6 @@ python_requires = >= 3.8
packages = packages =
eth_accounts_index eth_accounts_index
eth_accounts_index.runnable eth_accounts_index.runnable
eth_accounts_index.unittest
[options.extras_require] [options.extras_require]
testing = testing =

View File

@ -26,7 +26,6 @@ from chainlib.eth.block import (
# local imports # local imports
from eth_accounts_index.registry import AccountRegistry from eth_accounts_index.registry import AccountRegistry
from eth_accounts_index import AccountsIndex from eth_accounts_index import AccountsIndex
from eth_accounts_index.unittest import TestAccountsIndex
logging.basicConfig(level=logging.DEBUG) logging.basicConfig(level=logging.DEBUG)
logg = logging.getLogger() logg = logging.getLogger()
@ -34,10 +33,39 @@ logg = logging.getLogger()
testdir = os.path.dirname(__file__) testdir = os.path.dirname(__file__)
class Test(TestAccountsIndex): class TestNonceOracle:
def __init__(self, address, default_value=0):
self.nonce = default_value
def next_nonce(self):
nonce = self.nonce
self.nonce += 1
return nonce
class Test(EthTesterCase):
def setUp(self): def setUp(self):
super(Test, self).setUp() super(Test, self).setUp()
nonce_oracle = TestNonceOracle(self.accounts[0])
c = AccountRegistry(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
(tx_hash, o) = c.constructor(self.accounts[0])
self.conn = RPCConnection.connect(self.chain_spec, 'default')
r = self.conn.do(o)
logg.debug(f'deployed with hash {r}')
o = receipt(r)
r = self.conn.do(o)
self.address = to_checksum_address(r['contract_address'])
(tx_hash, o) = c.add_writer(self.address, self.accounts[0], self.accounts[0])
r = self.conn.do(o)
o = receipt(r)
r = self.conn.do(o)
self.assertEqual(r['status'], 1)
def test_1_count(self): def test_1_count(self):
@ -52,25 +80,19 @@ class Test(TestAccountsIndex):
def test_2_add(self): def test_2_add(self):
b = os.urandom(20) b = os.urandom(20)
a = to_checksum_address(b.hex()) a = to_checksum_address(b.hex())
b = os.urandom(20)
b = to_checksum_address(b.hex())
nonce_oracle = RPCNonceOracle(self.accounts[0], self.conn) nonce_oracle = RPCNonceOracle(self.accounts[0], self.conn)
c = AccountsIndex(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) c = AccountsIndex(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
(tx_hash, o) = c.add(self.address, self.accounts[0], a) (tx_hash, o) = c.add(self.address, self.accounts[0], a)
self.conn.do(o)
o = receipt(tx_hash)
r = self.conn.do(o) r = self.conn.do(o)
self.assertEqual(r['status'], 1) self.assertEqual(tx_hash, r)
o = receipt(tx_hash)
rcpt = self.conn.do(o)
self.helper.mine_block() self.helper.mine_block()
o = c.have(self.address, a, sender_address=self.accounts[0]) o = c.have(self.address, a, sender_address=self.accounts[0])
r = self.conn.do(o) r = self.conn.do(o)
self.assertEqual(int(r, 16), 1)
o = c.have(self.address, b, sender_address=self.accounts[0])
r = self.conn.do(o)
self.assertEqual(int(r, 16), 0)
def test_3_add_rlpsigned(self): def test_3_add_rlpsigned(self):

View File

@ -1,55 +1,45 @@
pragma solidity >=0.8.0; pragma solidity >0.6.12;
// SPDX-License-Identifier: AGPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// File-Version: 3 // File-Version: 2
contract CustodialAccountIndex {
contract AccountsIndex {
uint256 constant blockedField = 1 << 128; uint256 constant blockedField = 1 << 128;
address[] entryList; address[] entryList;
mapping(address => uint256) entryIndex; mapping(address => uint256) public entryIndex;
// Implements Writer
mapping(address => bool) public isWriter; mapping(address => bool) public isWriter;
// Implements ERC173
address public owner; address public owner;
address newOwner;
// Implements AccountsIndex event AddressAdded(address indexed _executor, address _account); // AccountsIndex
event AddressAdded(address _account); // AccountsIndex event AddressActive(address indexed _executor, address _account);
// Implements AccountsIndexMutable event AddressInactive(address indexed _executor, address _account);
event AddressActive(address indexed _account, bool _active); event AddressRemoved(address indexed _executor, address _account);
// Implements AccountsIndexMutable
event AddressRemoved(address _account);
// Implements ERC173
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); // EIP173 event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); // EIP173
// Implements Writer event WriterAdded(address indexed _executor, address _account); // AccountsIndex
event WriterAdded(address _account); // AccountsIndex event WriterDeleted(address indexed _executor, address _account);
// Implements Writer
event WriterDeleted(address _account);
constructor() { constructor() {
owner = msg.sender; owner = msg.sender;
entryList.push(address(0)); entryList.push(address(0));
} }
// Implements AccountsIndex
function entryCount() external view returns (uint256) { function entryCount() external view returns (uint256) {
return entryList.length - 1; return entryList.length - 1;
} }
// Implements Writer
function addWriter(address _writer) public returns (bool) { function addWriter(address _writer) public returns (bool) {
require(owner == msg.sender, 'ERR_AXX'); require(owner == msg.sender);
isWriter[_writer] = true; isWriter[_writer] = true;
emit WriterAdded(_writer); emit WriterAdded(msg.sender, _writer);
return true; return true;
} }
// Implements Writer
function deleteWriter(address _writer) public returns (bool) { function deleteWriter(address _writer) public returns (bool) {
require(owner == msg.sender, 'ERR_AXX'); require(owner == msg.sender);
delete isWriter[_writer]; delete isWriter[_writer];
emit WriterDeleted(_writer); emit WriterDeleted(msg.sender, _writer);
return true; return true;
} }
@ -66,17 +56,17 @@ contract AccountsIndex {
_entry = uint64(i); _entry = uint64(i);
_entry |= block.timestamp << 64; _entry |= block.timestamp << 64;
entryIndex[_account] = _entry; entryIndex[_account] = _entry;
emit AddressAdded(_account); emit AddressAdded(msg.sender, _account);
return true; return true;
} }
// Implements AccountsIndexMutable // Implements AccountsIndex
function remove(address _account) external returns (bool) { function remove(address _account) external returns (bool) {
uint256 i; uint256 i;
uint256 l; uint256 l;
require(isWriter[msg.sender], 'ERR_AXX'); require(isWriter[msg.sender]);
require(this.have(_account), 'ERR_ACL'); require(this.have(_account));
l = entryList.length - 1; l = entryList.length - 1;
@ -86,28 +76,28 @@ contract AccountsIndex {
} }
entryList.pop(); entryList.pop();
entryIndex[_account] = 0; entryIndex[_account] = 0;
emit AddressRemoved(_account); emit AddressRemoved(msg.sender, _account);
return true; return true;
} }
// Implements AccountsIndexMutable // Implements AccountsIndex
// Activate previously deactivated account. Will not affect the entry count.
function activate(address _account) external returns (bool) { function activate(address _account) external returns (bool) {
require(isWriter[msg.sender], 'ERR_AXX'); require(isWriter[msg.sender]);
require(entryIndex[_account] > 0, 'ERR_NOT_FOUND'); require(entryIndex[_account] > 0 && entryIndex[_account] & blockedField == blockedField);
require(entryIndex[_account] & blockedField == blockedField, 'ERR_NOT_BLOCKED');
entryIndex[_account] >>= 129; entryIndex[_account] >>= 129;
emit AddressActive(_account, true); emit AddressActive(msg.sender, _account);
return true; return true;
} }
// Implements AccountsIndexMutable // Implements AccountsIndex
// Deactivate account, without removing the entry. The entry will still be part of the entry count.
function deactivate(address _account) external returns (bool) { function deactivate(address _account) external returns (bool) {
require(isWriter[msg.sender]); require(isWriter[msg.sender]);
require(entryIndex[_account] > 0, 'ERR_NOT_FOUND'); require(entryIndex[_account] > 0 && entryIndex[_account] & blockedField == 0);
require(entryIndex[_account] & blockedField == 0, 'ERR_NOT_ACTIVE');
entryIndex[_account] <<= 129; entryIndex[_account] <<= 129;
entryIndex[_account] |= blockedField; entryIndex[_account] |= blockedField;
emit AddressActive(_account, false); emit AddressInactive(msg.sender, _account);
return true; return true;
} }
@ -128,7 +118,7 @@ contract AccountsIndex {
return entryIndex[_account] > 0; return entryIndex[_account] > 0;
} }
// Implements AccountsIndexMutable // Implements AccountsIndex
function isActive(address _account) external view returns (bool) { function isActive(address _account) external view returns (bool) {
return this.have(_account) && entryIndex[_account] & blockedField != blockedField; return this.have(_account) && entryIndex[_account] & blockedField != blockedField;
} }
@ -136,7 +126,7 @@ contract AccountsIndex {
// Implements EIP173 // Implements EIP173
function transferOwnership(address _newOwner) public returns (bool) { function transferOwnership(address _newOwner) public returns (bool) {
address oldOwner; address oldOwner;
require(msg.sender == owner, 'ERR_AXX'); require(msg.sender == owner);
oldOwner = owner; oldOwner = owner;
owner = _newOwner; owner = _newOwner;
@ -147,10 +137,7 @@ contract AccountsIndex {
// Implements EIP165 // Implements EIP165
function supportsInterface(bytes4 _sum) public pure returns (bool) { function supportsInterface(bytes4 _sum) public pure returns (bool) {
if (_sum == 0xb7bca625) { // AccountsIndex if (_sum == 0xcbdb05c7) { // AccountsIndex
return true;
}
if (_sum == 0x9479f0ae) { // AccountsIndexMutable
return true; return true;
} }
if (_sum == 0x01ffc9a7) { // EIP165 if (_sum == 0x01ffc9a7) { // EIP165
@ -159,7 +146,7 @@ contract AccountsIndex {
if (_sum == 0x9493f8b2) { // EIP173 if (_sum == 0x9493f8b2) { // EIP173
return true; return true;
} }
if (_sum == 0xabe1f1f5) { // Writer if (_sum == 0x80c84bd6) { // Writer
return true; return true;
} }
return false; return false;