Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 12 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ contract Swap {
---
#### Example: Using the `re-initisalisable` decorator

In the contract below, the owner of `tokenOwners[tokenId]` is the address stored as its mapping value. When `withdraw` is called, the commitment is nullified and the new value is `address(0)`, so no new commitment is created. This creates a problem if deposit is called again: how can a new owner provide a nullifier if the token was previously owned? The `re-initialisable` decorator solves this by allowing the variable to be re-initialised in `deposit`.
In the contract below, the owner of `tokenOwners[tokenId]` is the address stored as its mapping value. When `withdraw` is called, the commitment is nullified and the new value is `address(0)`, so no new commitment is created. This creates a problem if deposit is called again: how can a new owner provide a nullifier if the token was previously owned? The `re-initialisable` decorator solves this by allowing the variable to be re-initialised in `deposit`. Note that in the below contract the check `require(tokenOwners[tokenId] == msg.sender, "You're not the owner of this token.");` is essential to ensure that `tokenOwners[tokenId]` cannot be re-initialised maliciously in either `transfer` or `withdraw`. For a non re-initialisable secret variable this check would not be necessary because only the owner could modify it.

```solidity
contract NFT_Escrow {
Expand All @@ -272,12 +272,12 @@ contract NFT_Escrow {
}

function transfer(secret address recipient, secret uint256 tokenId) public {
require(tokenOwners[tokenId] == msg.sender, "Youre not the owner of this token.");
require(tokenOwners[tokenId] == msg.sender, "You're not the owner of this token.");
tokenOwners[tokenId] = recipient;
}

function withdraw(uint256 tokenId) public {
require(tokenOwners[tokenId] == msg.sender, "Youre not the owner of this token.");
require(tokenOwners[tokenId] == msg.sender, "You're not the owner of this token.");
tokenOwners[tokenId] = address(0);
bool success = erc721.transferFrom(address(this), msg.sender, tokenId);
require(success == true);
Expand Down Expand Up @@ -518,7 +518,7 @@ $ sh bin/startup-double

This starts two orchestration servers running the Swap contract that can be reached on ports 3000 and 3001 respectively. Let's say user A runs its server on port 3001 and user B on 3000.

For user A to initiate a swap with B, it needs a shared secret key derived from its public key (`recipientPubKey`) and the private key from user B. This is how B would compute the shared secret using the public key from A, send:
For user A to initiate a swap with B, it needs a shared secret key derived from the public key of user B (`recipientPubKey`) and its own private key. This is how A would compute the shared secret using the public key from B, send:

```
{
Expand All @@ -538,6 +538,8 @@ Response:

Use this shared key as the `sharedAddress` in swap interactions.

User B should do the same before the swap is initiated. Otherwise, their commitment DB will not be automatically updated via the event listener after User A runs `startSwap`. However, if they forget, they can always use the `backupDataRetriever` API to force the commitment DB to update. In their case `recipientPubKey` is set to the public key of user A.


##### Deposit Tokens
Each party deposits tokens they intend to trade.
Expand All @@ -563,28 +565,30 @@ as a POST request to `http://localhost:3000/deposit`.


##### Initiate Swap
User A proposes a swap to the shared address, they send:
User A proposes a swap to the shared address. First, they must obtain the public key of user B for the encryption, so that user B will receive commitment preimages via the event listener. This public key is saved in `zapps/Swap/orchestration/common/db/key1.json`, lets say it is `18506935782509777864732454467672391704006521439730278073755619393220081058435`. This should be used as the `tokenOwners_tokenIdSent_newOwnerPublicKey`. They send:
```
{
"sharedAddress": "0x3e574c310f7bc1657f7e0e127690a8f885e4bcd42c15489a332ed9a6658bfef6",
"amountSent": 30,
"tokenIdSent": 1,
"tokenIdRecieved": 2
"tokenIdRecieved": 2,
"tokenOwners_tokenIdSent_newOwnerPublicKey": "18506935782509777864732454467672391704006521439730278073755619393220081058435"
}
```
as a POST request to `http://localhost:3001/startSwap`.

This deducts 30 tokens from User A and locks token 1 for the proposed swap.

##### Complete Swap
User B accepts the swap with a matching offer, they send:
User B accepts the swap with a matching offer. User B must similarly obtain the public key of user A. This public key is saved in `zapps/Swap/orchestration/common/db/key2.json`, lets say it is `19113029868759597499687530931590653617019453270233891831264939544742234896197`. Again, this should be used as the `tokenOwners_tokenIdSent_newOwnerPublicKey`. They send:
```
{
"sharedAddress": "0x3e574c310f7bc1657f7e0e127690a8f885e4bcd42c15489a332ed9a6658bfef6",
"counterParty": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
"tokenIdSent": 2,
"tokenIdRecieved": 1,
"amountRecieved": 30
"amountRecieved": 30,
"tokenOwners_tokenIdSent_newOwnerPublicKey": "19113029868759597499687530931590653617019453270233891831264939544742234896197"
}
```
as a POST request to `http://localhost:3000/completeSwap`.
Expand Down
29 changes: 20 additions & 9 deletions src/boilerplate/common/backup-encrypted-data-listener.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,22 @@ export default class BackupEncryptedDataEventListener {
this.lastProcessedBlock = 0; // Track last block we processed
}

loadKeys() {
const keys = JSON.parse(fs.readFileSync(keyDb, 'utf-8'));
this.secretKey = generalise(keys.secretKey);
this.publicKey = generalise(keys.publicKey);
this.sharedPublicKey = keys.sharedPublicKey
? generalise(keys.sharedPublicKey)
: null;
this.sharedSecretKey = keys.sharedSecretKey
? generalise(keys.sharedSecretKey)
: null;

if (!keys.secretKey || !keys.publicKey) {
throw new Error('Invalid key file: missing required keys');
}
}

async init() {
try {
this.instance = await getContractInstance('CONTRACT_NAME');
Expand All @@ -64,15 +80,7 @@ export default class BackupEncryptedDataEventListener {
if (!fs.existsSync(keyDb))
await registerKey(utils.randomHex(31), 'CONTRACT_NAME', true);

const keys = JSON.parse(fs.readFileSync(keyDb, 'utf-8'));
this.secretKey = generalise(keys.secretKey);
this.publicKey = generalise(keys.publicKey);
this.sharedPublicKey = generalise(keys.sharedPublicKey);
this.sharedSecretKey = generalise(keys.sharedSecretKey);

if (!keys.secretKey || !keys.publicKey) {
throw new Error('Invalid key file: missing required keys');
}
this.loadKeys();
} catch (error) {
console.error(
'encrypted-data-listener',
Expand Down Expand Up @@ -240,6 +248,9 @@ export default class BackupEncryptedDataEventListener {
async processBackupEventData(eventData) {
activeBackupProcesses += 1;
try {
// Shared keys are written to key.json at runtime, so reload them here
// rather than relying on the values captured when the listener started.
this.loadKeys();
const keyPairs = [
{ secretKey: this.secretKey, publicKey: this.publicKey },
{ secretKey: this.sharedSecretKey, publicKey: this.sharedPublicKey },
Expand Down
22 changes: 0 additions & 22 deletions test/contracts/action-tests/Escrow-refs.zol
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,9 @@ contract Escrow {

secret mapping(address => uint256) public balances;
IERC20 public erc20;
secret bytes20[] public b;

struct TypeTest {
uint256 amount;
bytes20 amount2;
address amount3;
bool amount4;
}

sharedSecret mapping(address => bytes20) references;

secret mapping(uint256 => TypeTest) private d;
secret bytes20 recentReference;
secret TypeTest refStruct;

constructor(address erc20Address) {
erc20 = IERC20(erc20Address);
}
Expand All @@ -38,16 +26,6 @@ contract Escrow {
balances[msg.sender] -= amount;
unknown balances[recipient] += amount;
references[sharedAddress] = transferRef;
recentReference = transferRef;
b[0] = transferRef;
refStruct.amount = 5;
refStruct.amount4 = true;
refStruct.amount3 = recipient;
refStruct.amount2 = transferRef;
d[0].amount = 5;
d[0].amount4 = true;
d[0].amount3 = recipient;
d[0].amount2 = transferRef;

}

Expand Down
1 change: 1 addition & 0 deletions test/contracts/user-friendly-tests/NFT_Escrow.zol
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ contract NFT_Escrow {
require(approvals[msg.sender] == sender);
require(recipient != address(0), "NFT_Escrow: transfer to the zero address");
require(sender != address(0), "NFT_Escrow: transfer from the zero address");
require(tokenOwners[tokenId] == sender);
tokenOwners[tokenId] = recipient;
}

Expand Down
2 changes: 1 addition & 1 deletion test/contracts/user-friendly-tests/Swap.zol
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ contract Swap {

function deposit(secret uint256 amount, secret uint256 tokenId) public {
balances[msg.sender] += amount;
reinitialisable tokenOwners[tokenId] = msg.sender;
tokenOwners[tokenId] = msg.sender;
}

function startSwap(secret address sharedAddress, secret uint256 amountSent, secret uint256 tokenIdSent, secret uint256 tokenIdRecieved) public {
Expand Down
Loading