Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New token structure #360

Merged
merged 28 commits into from
Jan 20, 2020
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
d1a6807
chore(issuer): bootstrap
kosecki123 Dec 11, 2019
aede3a6
chore(issuer): claim / request flow
kosecki123 Dec 13, 2019
587db56
chore(issuer): test for inputhash correctness
kosecki123 Dec 16, 2019
2f03caa
chore(issuer): expose volume methods
kosecki123 Dec 17, 2019
3c99367
chore(issuer): split issuer and registry
kosecki123 Dec 20, 2019
f661c69
chore(issuer): private<->public volume transfers
kosecki123 Dec 23, 2019
9566ce3
chore(issuer): private transfers
kosecki123 Dec 23, 2019
d1f6a73
chore(issuer): diagrams
kosecki123 Dec 27, 2019
e6cf472
chore(issuer): public issuer
kosecki123 Dec 27, 2019
4357785
chore(issuer): added high level recipes
kosecki123 Dec 27, 2019
589c98d
chore(issuer): rendered diagrams version
kosecki123 Dec 30, 2019
b0c9247
chore(issuer): disable linting
kosecki123 Dec 30, 2019
9770ff2
Merge branch 'master' into feat/new-token-structure
kosecki123 Dec 30, 2019
8177302
fix(issuer): rm unused dependecies
kosecki123 Dec 30, 2019
d645366
chore(issuer): set package as private
kosecki123 Dec 30, 2019
f7428b7
Merge branch 'master' into feat/new-token-structure
josipbagaric Jan 8, 2020
6511677
feat(issuer): add JS wrappers for PublicIssuer and Certificate
josipbagaric Jan 14, 2020
800f102
feat(issuer): implement validity data checks
josipbagaric Jan 14, 2020
bf56cfc
Merge branch 'master' into feat/new-token-structure
josipbagaric Jan 17, 2020
e2fe33b
chore: fix build
josipbagaric Jan 17, 2020
7bb908a
chore: remove unnecessary functions
josipbagaric Jan 17, 2020
e63388e
adr: Migrate certificate structure to conform to ERC-1888
josipbagaric Jan 17, 2020
192ea5c
chore: review comments
josipbagaric Jan 17, 2020
acf4152
Merge branch 'master' into feat/new-token-structure
josipbagaric Jan 18, 2020
7c672eb
Merge branch 'master' into feat/new-token-structure
josipbagaric Jan 19, 2020
37098f7
feat(issuer): initial skeleton for private issuance
josipbagaric Jan 19, 2020
8d95f76
chore(issuer): review cleanup
josipbagaric Jan 20, 2020
f5caf3a
chore(issuer): fix build
josipbagaric Jan 20, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# 8. Migrate certificate structure to conform to ERC-1888

Date: 2020-01-17

## Status

Accepted

## Context

Our current certificate structure is based on ERC-721 non-fungible tokens. This presents an issue when a part of a certificate's volume has to be transferred to another owner.
In cases like these, we currently "split" the certificate into 2 smaller certificates, and then transfer one of the certificates to the new owner, and leave the original certificate to the original owner - deprecating the old certificate.
This approach is not ideal, so we started looking into better ways of changing owners for smaller parts of the certificates.

## Decision

We decided to use the [ERC-1888](https://github.com/ethereum/EIPs/issues/1888) Certificate structure so that we can comply and work on standardizing Certificates.

## Consequences

We would no longer need to split certificates and lose the certificate history whenever we want to transfer a part of the certificate to a new owner.
3 changes: 3 additions & 0 deletions packages/issuer/.env.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
WEB3="http://localhost:8560"
DEPLOY_KEY="d9066ff9f753a1898709b568119055660a77d9aae4d7a4ad677b8fb3d2a571e5"
BACKEND_URL="http://localhost:3040"
7 changes: 7 additions & 0 deletions packages/issuer/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.DS_Store
node_modules
dist
.vscode
build
db.sqlite
.openzeppelin/.session
206 changes: 206 additions & 0 deletions packages/issuer/README mermaid.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
# Issuer

## Registry

`Registry.sol` is ERC 1888 compatible registry for certificates issued by various issuers over various topics.

## Issuers

1) `PrivateIssuer.sol` is an implementation of privacy focused issuer which hides the volume for newly created certificates until the `ERC1888` claiming event.

Migration process equals to a swap from private certificate to public certificate and is (currently) a one-way process. For certificate owner

Issuer uses specific topic to issue:
- private certificates
- public certificates (used when migrating from private to public certificate)

2) `PublicIssuer.sol` is an implementation of a public I-REC compliant issuer

### Recipes

1) Private issuance and private trading
- issue using PrivateIssuer.requestIssue / PrivateIssuer.approveIssue
- transfer using PrivateIssuer.privateTransfer

2) Private issuance and public trading
- issue using PrivateIssuer.requestIssue / PrivateIssuer.approveIssue
- migrate to public using PrivateIssuer.migrateToPublic
- transfer / trade public volumes

3) Public issuance and private trading
- issue using PublicIssuer
- deposit volume to PrivateIssuer
- transfer using PrivateIssuer.privateTransfer

### Technical documentation

1) Private requesting and issuance

```mermaid
graph TD;
A(Device Owner) -->|1. Request Issue| P(PrivateIssuer.sol)
I(I-REC) -->|2. Approve Issue| P
P-->|3. Issue with private topic| R(Registry.sol)
```

```mermaid
sequenceDiagram
participant U as Device Owner
participant IS as Issuer
participant I as Issuer Contract
participant R as Registry Contract
participant A as I-REC API
participant DB as Database

U->>+I: encodeIssue(from, to, deviceId)
I-->>-U: (bytes)data

U->>+I: requestIssue(data)
I-->-U: emit IssueRequest(msg.sender, id)

IS->>+I: getRequest(id)
I-->-IS: RequestIssue

IS->>+A: validate(relevant request data)
A-->-IS: (boolean)result
IS->>+IS: createProof(to, balance)
IS->>+DB: storeProof()
DB-->-IS: ok
IS->>+I: approveIssue(to, requestId, commitment, validityData)
Note over IS,I: commitment = rootHash of the proof
I->>+R: issue(to, validityData, privateTopic,0, data)
R-->-I: (uint)id
I-->-IS: emit IssueSingle, emit CommitmentUpdated
```

2) Migrating certificate to public certificate

```mermaid
graph TD;
A(Certificate Owner) -->|1. Request Migration| P(PrivateIssuer.sol)
I(I-REC) -->|2. Approve Migration| P
P-->|3. Was already migrated?|P
P-->|4. NO: Request issuance| PUB(PublicIssuer.sol)
P-->|5. YES: Request minting| PUB
PUB-->|6. Issue or mint|R(Registry)
```

```mermaid
sequenceDiagram
participant U as Device/Certificate Owner
participant IS as Issuer
participant PUB as Public Issuer Contract
participant I as Private Issuer Contract
participant R as Registry Contract
participant A as I-REC API
participant DB as Database

U->>+IS: requestProofForBalance()
IS-->>-U: (address, value, salt, proof)
U->>U: hash = sha3(address, value, salt)

U->>+I: requestMigrateToPublic(id, hash)
I-->-U: emit MigrateToPublicRequest(msg.sender, id)

IS->>+I: getRequestMigrateToPublic(id)
I-->-IS: RequestStateChange

IS->>+I: migrateToPublic(id, value, salt, proof, newCommitment)
Note over IS,I: newCommitment = private balance update
I->>+PUB: requestIssueFor(request.owner, data)
PUB->>-I: returns requestId
I->>+PUB: approveIssue()
PUB->>-I: returns id
I-->-IS: emit PublicCertificateCreated(privateId, id)
```

Note: alternatively we define a request / approve migration from private to public. So instead of private issuer calling `mint or issue` we call public issuer "issue" function, this also means that public issuer becomes a issuer address for public certificates.

3) Claiming

Claiming is supported only by public issued certificates. Private certificates has to be migrated to public before being claimed.

```mermaid
graph TD;
A(Device Owner) -->|1. Claim| R(Registry)
```

4) Transferring from public to private

```mermaid
graph TD;
C(Certificate Owner)-->|1. 1155.Transfer| R(PrivateIssuer)
R --> |2. Update commitment| R
```

5) Migrating volume to public certificate

This is a case where private certificate was migrated to public, then part of the volume was again transferred to a private certificate.

An example is where buyer buys a public certificate but wishes to perform private trading activities before final migration to public and claiming

Note: Since `PrivateIssuer` is an owner of the token (from step 4) ) it can send publicly the request part to the requesting user. Total amount of tokens locked to smart contract won't change.

```mermaid
graph TD;
A(Certificate Owner) -->|1. Request Migration| P(PrivateIssuer)
I(I-REC) -->|2. Approve Migration| P
P-->|3. Transfer from P to requesting address| R(Registry)
P-->|4. Update commitment|P
```

Note: alternatively we can implement it as burn/mint flow. Tokens deposited to PrivateIssuer contract will be burnt immediately. Then transfer from private to public will use `Migrating certificate to public certificate` flow.

6) Private transfers

This is a case where volume can be transferred privately inside the private registry.

As an example, this can be used to transfer given volume to exchange or other account.

```mermaid
graph TD;
A(Certificate Owner) -->|1. Request private transfer| X(API)
A(Certificate Owner) -->|2. Request Private transfer| P(PrivateIssuer)
I(I-REC) -->|2. Approve| P
P-->|3. Validate new commitment| P
P-->|4. Update commitment|P
```

```mermaid
sequenceDiagram
participant U as Device/Certificate Owner
participant IS as Issuer
participant I as Issuer Contract
participant R as Registry Contract
participant A as I-REC API
participant DB as Database

U->>+IS: requestPrivateTransfer(id, value, newOwner)
IS->>DB: store updated proof tree, linked to requested
IS-->>-U: (address, value, salt, proof)
Note over IS,U: address = sender address, value = remaining balance
U->>U: hash = sha3(address, value, salt)

U->>+I: requestPrivateTransfer(id, hash)
I-->-U: emit PrivateTransferRequest(msg.sender, id)

IS->>+I: getRequestPrivateTransfer(id)
I-->-IS: RequestStateChange

IS->>+I: privateTransfer(requestId, proof, prevCommitment, newCommitment)
Note over IS,I: newCommitment = private balance update
I->>I: updateCommitment(id, prevCommitment, newCommitment)
I-->-IS: emit CommitmentUpdated
```

Notes:
`prevCommitment` is required to prevent state corruption, transition to new commitment based on other state that's currently on-chain will result in error.

Implementation:
- Certificate owner A has 1000kWh of energy on certificate id = 1 (C1)
- A requesting private transfer of 500kWh from C1 to B
- A calls API with (id, value, newOwner) in our case (1, 500000, B)
- if API approves the transfer (enough balance, maybe other API checks)
- API returns (updatedBalanceOfA, salt) in our case (500000, 'randomsalt')
- A creates onChain request where hash = hash(address, updatedBalanceOfA, salt) in our case hash(A, 5000000, salt)
- Issuer - approves by sending new commitment that is verified against the request.hash
88 changes: 88 additions & 0 deletions packages/issuer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Issuer

## Registry

`Registry.sol` is ERC 1888 compatible registry for certificates issued by various issuers over various topics.

## Issuers

1) `PrivateIssuer.sol` is an implementation of privacy focused issuer which hides the volume for newly created certificates until the `ERC1888` claiming event.

Migration process equals to a swap from private certificate to public certificate and is (currently) a one-way process. For certificate owner

Issuer uses specific topic to issue:
- private certificates
- public certificates (used when migrating from private to public certificate)

2) `PublicIssuer.sol` is an implementation of public I-REC compliant issuer

### Recipes

1) Private issuance and private trading
- issue using PrivateIssuer.requestIssue / PrivateIssuer.approveIssue
- transfer using PrivateIssuer.privateTransfer

2) Private issuance and public trading
- issue using PrivateIssuer.requestIssue / PrivateIssuer.approveIssue
- migrate to public using PrivateIssuer.migrateToPublic
- transfer / trade public volumes

3) Public issuance and private trading
- issue using PublicIssuer
- deposit volume to PrivateIssuer
- transfer using PrivateIssuer.privateTransfer

### Technical documentation

1) Private requesting and issuance

![Private request flow](docs/private_issuance_flow.png)
![Private request activity](docs/private_issuance_activity.png)

2) Migrating certificate to public certificate

![Migrate to public flow](docs/migrate_to_public_flow.png)
![Migrate to public activity](docs/migrate_to_public_activity.png)

3) Claiming

Claiming is supported only by public issued certificates. Private certificates has to be migrated to public before being claimed.

![Claim](docs/claim.png)

4) Transferring from public to private

![Public to private](docs/public_to_private.png)

5) Migrating volume to public certificate

This is a case where private certificate was migrated to public, then part of the volume was again transferred to a private certificate.

An example is where buyer buys a public certificate but wishes to perform private trading activities before final migration to public and claiming

Note: Since `PrivateIssuer` is an owner of the token (from step 4) ) it can send publicly the request part to the requesting user. Total amount of tokens locked to smart contract won't change.

![Transfer public to private](docs/transfer_to_public.png)

Note: alternatively we can implement it as burn/mint flow. Tokens deposited to PrivateIssuer contract will be burnt immediately. Then transfer from private to public will use `Migrating certificate to public certificate` flow.

6) Private transfers

This is a case where volume can be transferred privately inside the private registry.

As an example, this can be used to transfer given volume to exchange or other account.

![Private transfer flow](docs/private_transfer_flow.png)
![Private transfer activity](docs/private_transfer_activity.png)

Notes:
`prevCommitment` is required to prevent state corruption, transition to new commitment based on other state that's currently on-chain will result in error.

Implementation:
- Certificate owner A has 1000kWh of energy on certificate id = 1 (C1)
- A requesting private transfer of 500kWh from C1 to B
- A calls API with (id, value, newOwner) in our case (1, 500000, B)
- if API approves the transfer (enough balance, maybe other API checks)
- API returns (updatedBalanceOfA, salt) in our case (500000, 'randomsalt')
- A creates onChain request where hash = hash(address, updatedBalanceOfA, salt) in our case hash(A, 5000000, salt)
- Issuer - approves by sending new commitment that is verified against the request.hash
29 changes: 29 additions & 0 deletions packages/issuer/contracts/ERC1155/Address.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
pragma solidity ^0.5.0;


/**
* Utility library of inline functions on addresses
*/
library Address {

/**
* Returns whether the target address is a contract
* @dev This function will return false if invoked during the constructor of a contract,
* as the code is not actually created until after the constructor finishes.
* @param account address of the account to check
* @return whether the target address is a contract
*/
function isContract(address account) internal view returns (bool) {
uint256 size;
// XXX Currently there is no better way to check if there is a contract in an address
// than to check the size of the code at that address.
// See https://ethereum.stackexchange.com/a/14016/36603
// for more details about how this works.
// TODO Check this again before the Serenity release, because all addresses will be
// contracts then.
// solium-disable-next-line security/no-inline-assembly
assembly { size := extcodesize(account) }
return size > 0;
}

}
10 changes: 10 additions & 0 deletions packages/issuer/contracts/ERC1155/Common.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
pragma solidity ^0.5.0;

/**
Note: Simple contract to use as base for const vals
*/
contract CommonConstants {

bytes4 constant internal ERC1155_ACCEPTED = 0xf23a6e61; // bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))
bytes4 constant internal ERC1155_BATCH_ACCEPTED = 0xbc197c81; // bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))
}
Loading