Merge branch 'lash/declarator' into 'master'

Add declarator

See merge request grassrootseconomics/cic-contracts!2
This commit is contained in:
Louis Holbrook 2021-02-14 12:22:42 +00:00
commit 33c230dedf
19 changed files with 366 additions and 24 deletions

15
Declarator.sol Normal file
View File

@ -0,0 +1,15 @@
pragma solidity >=0.6.12;
// Author: Louis Holbrook <dev@holbrook.no> 0826EDA1702D1E87C6E2875121D2E7BB88C2A746
// SPDX-License-Identifier: GPL-3.0-or-later
// File-version: 1
// Description: The ERC20 standard interface as specified in EIP20 (sha256:9f843cbb25a737c9351b0b6a6f54b86864490d0d5284f6877b4929d481d34312)
interface AddressDeclarator {
function addDeclaration(address _address, bytes32 _proof) external returns ( bytes32 );
function declaratorCount(address _objectAddress) external view returns ( uint256 );
function declaratorAddressAt(address _targetAddress, uint256 _idx) external view returns ( address );
function declaration(address _subjectAddress, address _objectAddress) external view returns ( bytes32[] memory );
function declarationCount(address _subjectAddress) external view returns ( uint256 );
function declarationAddressAt(address _subjectAddress, uint256 _idx) external view returns ( address );
}

6
ERC20.txt Normal file
View File

@ -0,0 +1,6 @@
totalSupply()
balanceOf(address)
allowance(address,address)
transfer(address,uint)
approve(address,uint)
transferFrom(address,address,uint)

View File

@ -2,16 +2,17 @@ pragma solidity >=0.6.12;
// Author: Louis Holbrook <dev@holbrook.no> 0826EDA1702D1E87C6E2875121D2E7BB88C2A746
// SPDX-License-Identifier: GPL-3.0-or-later
// File-version: 3
// File-version: 4
interface Faucet {
event FaucetUsed(address indexed _recipient, address indexed _token, uint256 _value);
event FaucetFail(address indexed _recipient, address indexed _token, uint256 _value);
event FaucetAmountChange(address indexed _token, uint256 _value);
function amount() external view returns (uint256);
function token() external view returns (address);
function setAmount(uint256 _amount) external returns (bool);
function giveTo(address _recipient) external returns (bool);
function gimme() external returns (bool);
function cooldown(address _recipient) external returns (uint256);
}

View File

@ -8,4 +8,5 @@ pragma solidity >=0.6.12;
interface RegistryClient {
function registryCount() external view returns (uint256);
function addressOf(bytes32) external view returns (address);
function entry(uint256) external view returns (address);
}

View File

@ -9,4 +9,5 @@ interface RegistryStandard {
function registryCount() external view returns (uint256);
function addressOf(bytes32) external view returns (address);
function register(bytes32,address) external view returns (bool);
function entry(uint256) external view returns (address);
}

View File

@ -1,19 +0,0 @@
pragma solidity >=0.6.12;
// Author: Louis Holbrook <dev@holbrook.no> 0826EDA1702D1E87C6E2875121D2E7BB88C2A746
// SPDX-License-Identifier: GPL-3.0-or-later
// File-version: 2
interface TransferApproval {
event NewRequest(address indexed _sender, address indexed _recipient, address indexed _token, uint256 _value, uint256 _serial);
event NewExecution(uint256 serial);
event NewRejection(uint256 serial);
function serial() external view returns (uint256);
function requests(uint256) external view returns (address);
function approvers(address) external view returns(bool);
function request(address _recipient, address _token, uint256 _value) external returns (uint256);
function execute(uint256 _serial) external returns (bool);
function reject(uint256 _serial) external returns (bool);
}

31
TransferAuthorization.sol Normal file
View File

@ -0,0 +1,31 @@
pragma solidity >=0.6.12;
// Author: Louis Holbrook <dev@holbrook.no> 0826EDA1702D1E87C6E2875121D2E7BB88C2A746
// SPDX-License-Identifier: GPL-3.0-or-later
// File-version: 2
interface TransferAuthorization {
event NewRequest(address indexed _sender, address indexed _recipient, address indexed _token, uint256 _value, uint256 _serial);
event NewExecution(uint256 serial);
event NewRejection(uint256 serial);
event TransferFail(uint256 serial);
struct Transaction {
uint256 serial;
address sender;
address recipient;
address token;
uint256 value;
}
function approvers() external view returns (address[] memory);
function count() external view returns (uint256);
function lastSerial() external view returns (uint256);
function nextSerial() external view returns (uint256);
function getSerialAt(uint256 _idx) external view returns (uint256);
function requests(uint256 _serial) external view returns (Transaction memory);
function createRequest(address _recipient, address _token, uint256 _value) external returns (uint256);
function executeRequest(uint256 _serial) external returns (bool);
function rejectRequest(uint256 _serial) external returns (bool);
}

View File

@ -0,0 +1 @@
[{"inputs":[{"internalType":"address","name":"_address","type":"address"},{"internalType":"bytes32","name":"_proof","type":"bytes32"}],"name":"addDeclaration","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_subjectAddress","type":"address"},{"internalType":"address","name":"_objectAddress","type":"address"}],"name":"declaration","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_subjectAddress","type":"address"},{"internalType":"uint256","name":"_idx","type":"uint256"}],"name":"declarationAddressAt","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_subjectAddress","type":"address"}],"name":"declarationCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_targetAddress","type":"address"},{"internalType":"uint256","name":"_idx","type":"uint256"}],"name":"declaratorAddressAt","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_objectAddress","type":"address"}],"name":"declaratorCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]

1
abis/Declarator.json Normal file
View File

@ -0,0 +1 @@
[{"inputs":[{"internalType":"address","name":"_address","type":"address"},{"internalType":"bytes32","name":"_proof","type":"bytes32"}],"name":"addDeclaration","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_subjectAddress","type":"address"},{"internalType":"address","name":"_objectAddress","type":"address"}],"name":"declaration","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_subjectAddress","type":"address"},{"internalType":"uint256","name":"_idx","type":"uint256"}],"name":"declarationAddressAt","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_subjectAddress","type":"address"}],"name":"declarationCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_targetAddress","type":"address"},{"internalType":"uint256","name":"_idx","type":"uint256"}],"name":"declaratorAddressAt","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_objectAddress","type":"address"}],"name":"declaratorCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]

View File

@ -0,0 +1 @@
[{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"addressOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"entry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"address","name":"","type":"address"}],"name":"register","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"registryCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]

View File

@ -0,0 +1 @@
[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"serial","type":"uint256"}],"name":"NewExecution","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"serial","type":"uint256"}],"name":"NewRejection","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_sender","type":"address"},{"indexed":true,"internalType":"address","name":"_recipient","type":"address"},{"indexed":true,"internalType":"address","name":"_token","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_serial","type":"uint256"}],"name":"NewRequest","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"serial","type":"uint256"}],"name":"TransferFail","type":"event"},{"inputs":[],"name":"approvers","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"count","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"createRequest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_serial","type":"uint256"}],"name":"executeRequest","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_idx","type":"uint256"}],"name":"getSerialAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastSerial","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextSerial","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_serial","type":"uint256"}],"name":"rejectRequest","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_serial","type":"uint256"}],"name":"requests","outputs":[{"components":[{"internalType":"uint256","name":"serial","type":"uint256"},{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct TransferAuthorization.Transaction","name":"","type":"tuple"}],"stateMutability":"view","type":"function"}]

View File

@ -1,3 +0,0 @@
#!/bin/bash
docker build -t grassrootseconomics:cic .

47
contracts_overview.txt Normal file
View File

@ -0,0 +1,47 @@
Contract interfaces overview
AccountRegistry
* interface:
* append-only list of custodial addresses
* only writers may add addresses
* only owner may add and remove writers
AddressDeclarator
* interface:
* enables addresses to link one or more 32 byte entries to another address
* used for:
- kyc proofs to user account addresses
- token metadata (eg. tokenlists.org std), endorsements, warnings
* freely writable
Faucet
* interface:
* allows transfer of an amount of one particular token to a given address
* transfer amount can be changed at any time. invocation will used the transfer amount set at the time.
* anyone can invoke the function for anyone
* implements a function to provide cooldown info, max(uint256) means cooldown disabled (one-shot only)
Token Symbol Registry
* interface:
* simple key-value store to enforce unique token symbols
* only owner can write
* will check that token symbol reported by ERC20 interface matches provided key (case sensitive)
Transfer Approvals
* interface:
* counter-signing of transfers from custodial addresses invoked by third-party (staff)
* custodial address must create an allowance for contract
* only "approvers" may reject/execute transfers
* provides lists of open requests

3
doc/texinfo/Makefile Normal file
View File

@ -0,0 +1,3 @@
all:
makeinfo --html index.texi

1
doc/texinfo/index.texi Normal file
View File

@ -0,0 +1 @@
@include overview.texi

View File

@ -0,0 +1,117 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<!-- Created by GNU Texinfo 6.7, http://www.gnu.org/software/texinfo/ -->
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Smart contract interfaces (Untitled Document)</title>
<meta name="description" content="Smart contract interfaces (Untitled Document)">
<meta name="keywords" content="Smart contract interfaces (Untitled Document)">
<meta name="resource-type" content="document">
<meta name="distribution" content="global">
<meta name="Generator" content="makeinfo">
<link href="#Smart-contract-interfaces" rel="start" title="Smart contract interfaces">
<style type="text/css">
<!--
a.summary-letter {text-decoration: none}
blockquote.indentedblock {margin-right: 0em}
div.display {margin-left: 3.2em}
div.example {margin-left: 3.2em}
div.lisp {margin-left: 3.2em}
kbd {font-style: oblique}
pre.display {font-family: inherit}
pre.format {font-family: inherit}
pre.menu-comment {font-family: serif}
pre.menu-preformatted {font-family: serif}
span.nolinebreak {white-space: nowrap}
span.roman {font-family: initial; font-weight: normal}
span.sansserif {font-family: sans-serif; font-weight: normal}
ul.no-bullet {list-style: none}
-->
</style>
</head>
<body lang="en">
<span id="Smart-contract-interfaces"></span><span id="Smart-contract-interfaces-1"></span><h2 class="chapter">1 Smart contract interfaces</h2>
<span id="Smart-Contracts-in-the-CIC-Network"></span><h3 class="section">1.1 Smart Contracts in the CIC Network</h3>
<span id="Technology"></span><h4 class="subsection">1.1.1 Technology</h4>
<p>The long-term aim of the CIC network is to be agnostic of consensus engines. However, since we have to start <em>somewhere</em>, the current state of development only deals with Smart Contracts on the (Ethereum Virtual Machine (EVM).
</p>
<span id="The-registry-contract"></span><h4 class="subsection">1.1.2 The registry contract</h4>
<p>The CICRegistry contract defines the entry-point to the entire CIC network. All other CIC network resources can be discovered through this contract.
</p>
<p>Its implementation is contained in the <samp>cic-registry</samp> repository. Details about it are documented further in that section.
</p>
<span id="Auxiliary-contracts"></span><h4 class="subsection">1.1.3 Auxiliary contracts</h4>
<p>All other Smart Contracts in the network other than CICRegistry are essentially optional, and each applies <em>at least one</em> the interfaces defined in the <code>cic-contracts</code> repository.
</p>
<span id="Contract-interfaces"></span><h4 class="subsection">1.1.4 Contract interfaces</h4>
<p>All contracts interfaces also implement the <strong>EIP165</strong> Standard Interface Detection.
</p>
<span id="Registry"></span><h4 class="subsubsection">1.1.4.1 Registry</h4>
<p>A key-value store, which resolves arbitrary 32-byte pointers to Ethereum addresses. Typically, this is used to resolve an address from a well-known identifier, either as a UTF-8 value or a 256-bit hash.
</p>
<p>It also provides numerical index access to all registered values in order of insertion.
</p>
<span id="Registry-Client"></span><h4 class="subsubsection">1.1.4.2 Registry Client</h4>
<p>A subset of the <code>Registry</code> interface, which defines only the non-transactional methods of the contract. The <code>Registry Client</code> interface is implemented by the <code>CICRegistry</code> contract.
</p>
<span id="Faucet"></span><h4 class="subsubsection">1.1.4.3 Faucet</h4>
<p>Enables disbursement of a set amount of tokens to a requesting address.
</p>
<p>Allows privileged accounts to adjust the amount of tokens to disburse.
</p>
<p>Can be implemented as a periodic or a one-time service per account.
</p>
<span id="Transfer-approval"></span><h4 class="subsubsection">1.1.4.4 Transfer approval</h4>
<p>Enables a third-party approval to spend an ERC20 token allowance.
</p>
<p>This is useful in the case of a custodial key store, where a transaction has been initiated by another entity than the owner of the key.
</p>
<p>The contract allows the third-party address to either allow or reject the transfer.
</p>
<span id="Declarator"></span><h4 class="subsubsection">1.1.4.5 Declarator</h4>
<p>Stores one or more 32-byte entries as a description of an Ethereum address, <em>signed</em> by another Ethereum address.
</p>
<p>This can be used to <em>describe</em> network resources, be it tokens, contracts or user accounts. Examples of uses are KYC documentation proofs for users, token metadata used for display, or fraud alerts.
</p>
<p>Entries are stored by the transacting address that adds address/description pairs. In other words, any address may have different &quot;opinions&quot; registered about it, depending on which signing address it is queried in the context of. This, in turn, allows the quering entity to compile its own &quot;opinion&quot; of an address by combining data from signatures it trusts.
</p>
<p>Entries typically are text strings or content hashes.
</p>
<span id="Address-Index"></span><h4 class="subsubsection">1.1.4.6 Address Index</h4>
<p>A simple append-only list of addresses. Used to check whether an address is part of a particular group.
</p>
<span id="DEX-Index"></span><h4 class="subsubsection">1.1.4.7 DEX Index</h4>
<p>Methods required to convert between tokens.
</p><hr>
</body>
</html>

76
doc/texinfo/overview.texi Normal file
View File

@ -0,0 +1,76 @@
@node Smart contract interfaces
@chapter Smart contract interfaces
@section Smart Contracts in the CIC Network
@subsection Technology
The long-term aim of the CIC network is to be agnostic of consensus engines. However, since we have to start @emph{somewhere}, the current state of development only deals with Smart Contracts on the (Ethereum Virtual Machine (EVM).
@subsection The registry contract
The CICRegistry contract defines the entry-point to the entire CIC network. All other CIC network resources can be discovered through this contract.
Its implementation is contained in the @file{cic-registry} repository. Details about it are documented further in that section.
@subsection Auxiliary contracts
All other Smart Contracts in the network other than CICRegistry are essentially optional, and each applies @emph{at least one} the interfaces defined in the @code{cic-contracts} repository.
@subsection Contract interfaces
All contracts interfaces also implement the @strong{EIP165} Standard Interface Detection.
@subsubsection Registry
A key-value store, which resolves arbitrary 32-byte pointers to Ethereum addresses. Typically, this is used to resolve an address from a well-known identifier, either as a UTF-8 value or a 256-bit hash.
It also provides numerical index access to all registered values in order of insertion.
@subsubsection Registry Client
A subset of the @code{Registry} interface, which defines only the non-transactional methods of the contract. The @code{Registry Client} interface is implemented by the @code{CICRegistry} contract.
@subsubsection Faucet
Enables disbursement of a set amount of tokens to a requesting address.
Allows privileged accounts to adjust the amount of tokens to disburse.
Can be implemented as a periodic or a one-time service per account.
@subsubsection Transfer approval
Enables a third-party approval to spend an ERC20 token allowance.
This is useful in the case of a custodial key store, where a transaction has been initiated by another entity than the owner of the key.
The contract allows the third-party address to either allow or reject the transfer.
@subsubsection Declarator
Stores one or more 32-byte entries as a description of an Ethereum address, @emph{signed} by another Ethereum address.
This can be used to @emph{describe} network resources, be it tokens, contracts or user accounts. Examples of uses are KYC documentation proofs for users, token metadata used for display, or fraud alerts.
Entries are stored by the transacting address that adds address/description pairs. In other words, any address may have different "opinions" registered about it, depending on which signing address it is queried in the context of. This, in turn, allows the quering entity to compile its own "opinion" of an address by combining data from signatures it trusts.
Entries typically are text strings or content hashes.
@subsubsection Address Index
A simple append-only list of addresses. Used to check whether an address is part of a particular group.
@subsubsection DEX Index
Methods required to convert between tokens.

44
scripts/registry_list.py Normal file
View File

@ -0,0 +1,44 @@
import sys
from chainlib.eth.constant import ZERO_ADDRESS
from chainlib.eth.gas import DefaultGasOracle
from chainlib.eth.nonce import DefaultNonceOracle
from chainlib.eth.tx import TxFactory
from chainlib.eth.connection import HTTPConnection
from chainlib.eth.hash import keccak256_string_to_hex
from chainlib.eth.rpc import jsonrpc_template
from chainlib.eth.error import EthException
import eth_abi
from hexathon import (
strip_0x,
add_0x,
)
i = 0
while True:
conn = HTTPConnection('http://localhost:63545')
gas_oracle = DefaultGasOracle(conn)
nonce_oracle = DefaultNonceOracle(ZERO_ADDRESS, conn)
# Get Token registry address
txf = TxFactory(signer=None, gas_oracle=gas_oracle, nonce_oracle=nonce_oracle, chain_id=8996)
tx = txf.template(ZERO_ADDRESS, sys.argv[1])
identifiers_method = keccak256_string_to_hex('identifiers(uint256)')[:8]
data = add_0x(identifiers_method)
data += eth_abi.encode_single('uint256', i).hex()
txf.set_code(tx, data)
o = jsonrpc_template()
o['method'] = 'eth_call'
tx = txf.normalize(tx)
o['params'].append(tx)
o['params'].append('latest')
try:
r = conn.do(o)
except EthException:
break
id_bytes = bytes.fromhex(strip_0x(r))
id_str = id_bytes[:id_bytes.find(b'\x00')]
print(id_str.decode('utf-8̈́'))
i += 1

17
util/calculate_erc165.py Normal file
View File

@ -0,0 +1,17 @@
import sys
import web3
f = open(sys.argv[1], 'r')
z = b''
for i in range(32):
z += b'\x00'
while True:
l = f.readline()
if l == '':
break
#print('line {}'.format(l))
h = web3.Web3.keccak(text=l)
z = bytes([a ^ b for a, b in zip(h, z)])
#print(h.hex(), z.hex())
f.close()
print(h[:4].hex())