Skip to content
This repository has been archived by the owner on Dec 27, 2022. It is now read-only.

Commit

Permalink
Merge pull request #72 from connext/66-generic-transfers
Browse files Browse the repository at this point in the history
66 generic transfers
  • Loading branch information
LayneHaber authored Oct 7, 2020
2 parents e4f334a + ee4bb1d commit c0104fd
Show file tree
Hide file tree
Showing 63 changed files with 1,115 additions and 634 deletions.
10 changes: 5 additions & 5 deletions modules/browser-node/src/services/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export class BrowserStore implements IEngineStore, IChainServiceStore {
async saveChannelState(
channelState: FullChannelState<any>,
commitment: ChannelCommitmentData,
transfer?: FullTransferState<any>,
transfer?: FullTransferState,
): Promise<void> {
await this.db.transaction("rw", this.db.channels, this.db.transfers, async () => {
await this.db.channels.put(channelState);
Expand Down Expand Up @@ -145,23 +145,23 @@ export class BrowserStore implements IEngineStore, IChainServiceStore {
};
}

async getActiveTransfers(channelAddress: string): Promise<FullTransferState<any>[]> {
async getActiveTransfers(channelAddress: string): Promise<FullTransferState[]> {
const collection = this.db.transfers.where("resolveUpdateNonce").equals(0);
const transfers = await collection.toArray();
return transfers.map(storedTransferToTransferState);
}

async getTransferState(transferId: string): Promise<FullTransferState<any> | undefined> {
async getTransferState(transferId: string): Promise<FullTransferState | undefined> {
const transfer = await this.db.transfers.get(transferId);
return transfer ? storedTransferToTransferState(transfer) : undefined;
}

async getTransferByRoutingId(channelAddress: string, routingId: string): Promise<FullTransferState<any> | undefined> {
async getTransferByRoutingId(channelAddress: string, routingId: string): Promise<FullTransferState | undefined> {
const transfer = await this.db.transfers.get({ channelAddress, routingId });
return transfer ? storedTransferToTransferState(transfer) : undefined;
}

async getTransfersByRoutingId(routingId: string): Promise<FullTransferState<any>[]> {
async getTransfersByRoutingId(routingId: string): Promise<FullTransferState[]> {
const transfers = this.db.transfers.where({ routingId });
const ret = await transfers.toArray();
return ret.map(storedTransferToTransferState);
Expand Down
4 changes: 0 additions & 4 deletions modules/contracts/ops/entry.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ mkdir -p $data_dir /data /tmp
touch $address_book
rm -f $chain_addresses

# TODO: the gasLimit shouldn't need to be 1000x higher than mainnet but cf tests fail otherwise..

if [[ "$evm" == "buidler" ]]
then
echo "Using buidler EVM"
Expand All @@ -35,7 +33,6 @@ then
privateKey: "0xc87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3",
balance: "1000000000000000000000000000"
}],
blockGasLimit: 9000000000,
gasPrice: 100000000000,
},
},
Expand All @@ -49,7 +46,6 @@ then
launch=$cwd'/node_modules/.bin/ganache-cli \
--db='$data_dir' \
--defaultBalanceEther=2000000000 \
--gasLimit=9000000000 \
--gasPrice=100000000000 \
--mnemonic="'"$mnemonic"'" \
--networkId='$chain_id' \
Expand Down
44 changes: 44 additions & 0 deletions modules/contracts/src.sol/TransferRegistry.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.1;
pragma experimental ABIEncoderV2;

import "./interfaces/ITransferRegistry.sol";
import "./interfaces/Types.sol";
import "./lib/LibIterableMapping.sol";


contract TransferRegistry is ITransferRegistry {

using LibIterableMapping for LibIterableMapping.IterableMapping;

address immutable owner;

LibIterableMapping.IterableMapping transfers;

constructor() {
owner = msg.sender;
}

modifier onlyOwner() {
require(msg.sender == owner, "Only owner can call function");
_;
}

// Should add a transfer definition to the registry
// onlyOwner
function addTransferDefinition(RegisteredTransfer memory definition) external override onlyOwner {
transfers.addTransferDefinition(definition);
}

// Should remove a transfer definition from the registry
// onlyOwner
function removeTransferDefinition(string memory name) external override onlyOwner {
transfers.removeTransferDefinition(name);
}

// Should return all transfer defintions in registry
function getTransferDefinitions() external override view returns (RegisteredTransfer[] memory) {
return transfers.getTransferDefinitions();
}

}
28 changes: 14 additions & 14 deletions modules/contracts/src.sol/interfaces/ITransferDefinition.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@ pragma experimental "ABIEncoderV2";

import "./Types.sol";


interface ITransferDefinition {
// Validates the initial state of the transfer.
// Called by validator.ts during `create` updates.
function create(bytes calldata) external view returns (bool);

// Validates the initial state of the transfer.
// Called by validator.ts during `create` updates.
function create(bytes calldata)
external
view
returns (bool);

// Performs a state transition to resolve a transfer and returns final balances.
// Called by validator.ts during `resolve` updates.
function resolve(bytes calldata, bytes calldata)
external
view
returns (Balance memory);
// Performs a state transition to resolve a transfer and returns final balances.
// Called by validator.ts during `resolve` updates.
function resolve(bytes calldata, bytes calldata) external view returns (Balance memory);

// Should also have the following properties
// string name
// string stateEncoding
// string resolverEncoding
// These properties are included on the transfer specifically
// to make it easier for implementers to add new transfers by
// only include a `.sol` file
function getRegistryInformation() external view returns (RegisteredTransfer memory);
}
18 changes: 18 additions & 0 deletions modules/contracts/src.sol/interfaces/ITransferRegistry.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.1;
pragma experimental "ABIEncoderV2";

import "./Types.sol";

interface ITransferRegistry {
// Should add a transfer definition to the registry
// onlyOwner
function addTransferDefinition(RegisteredTransfer memory transfer) external;

// Should remove a transfer definition to the registry
// onlyOwner
function removeTransferDefinition(string memory name) external;

// Should return all transfer defintions in registry
function getTransferDefinitions() external view returns (RegisteredTransfer[] memory);
}
7 changes: 7 additions & 0 deletions modules/contracts/src.sol/interfaces/Types.sol
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,10 @@ struct TransferDispute {
bytes32 transferStateHash;
bool isDefunded;
}

struct RegisteredTransfer {
string name;
address definition;
string stateEncoding;
string resolverEncoding;
}
80 changes: 80 additions & 0 deletions modules/contracts/src.sol/lib/LibIterableMapping.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.1;
pragma experimental ABIEncoderV2;

import "../interfaces/Types.sol";

library LibIterableMapping {
struct TransferDefinitionWithIndex {
RegisteredTransfer transfer;
uint256 index;
}

struct IterableMapping {
mapping(string => TransferDefinitionWithIndex) transfers;
string[] names;
}

function stringEqual(string memory s, string memory t) internal pure returns (bool) {
return keccak256(abi.encodePacked(s)) == keccak256(abi.encodePacked(t));
}

function isEmptyString(string memory s) internal pure returns (bool) {
return stringEqual(s, "");
}

function nameExists(IterableMapping storage self, string memory name) internal view returns (bool) {
return !isEmptyString(name) && self.names.length != 0 && stringEqual(self.names[self.transfers[name].index], name);
}

function length(IterableMapping storage self) internal view returns (uint256) {
return self.names.length;
}

function getTransferDefinitionByName(IterableMapping storage self, string memory name)
internal
view
returns (RegisteredTransfer memory)
{
require(!isEmptyString(name));
require(nameExists(self, name));
return self.transfers[name].transfer;
}

function getTransferDefinitionByIndex(IterableMapping storage self, uint256 index)
internal
view
returns (RegisteredTransfer memory)
{
require(index < self.names.length);
return self.transfers[self.names[index]].transfer;
}

function getTransferDefinitions(IterableMapping storage self) internal view returns (RegisteredTransfer[] memory) {
uint256 l = self.names.length;
RegisteredTransfer[] memory transfers = new RegisteredTransfer[](l);
for (uint256 i = 0; i < l; i++) {
transfers[i] = self.transfers[self.names[i]].transfer;
}
return transfers;
}

function addTransferDefinition(IterableMapping storage self, RegisteredTransfer memory transfer) internal {
string memory name = transfer.name;
require(!isEmptyString(name));
require(!nameExists(self, name));
self.transfers[name] = TransferDefinitionWithIndex({transfer: transfer, index: self.names.length});
self.names.push(name);
}

function removeTransferDefinition(IterableMapping storage self, string memory name) internal {
require(!isEmptyString(name));
require(nameExists(self, name));
uint256 index = self.transfers[name].index;
string memory lastName = self.names[self.names.length - 1];
self.transfers[lastName].index = index;
self.names[index] = lastName;
delete self.transfers[name];
self.names.pop();
}
}
16 changes: 16 additions & 0 deletions modules/contracts/src.sol/transferDefinitions/HashlockTransfer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,22 @@ contract HashlockTransfer is ITransferDefinition {
bytes32 preImage;
}

string StateEncoding = "tuple(tuple(uint256[2] amount, address[2] to) balance, bytes32 lockHash, uint256 expiry)";

string ResolverEncoding = "tuple(bytes32 preImage)";

string Name = "HashlockTransfer";

function getRegistryInformation() external override view returns (RegisteredTransfer memory) {
RegisteredTransfer memory info = RegisteredTransfer({
name: Name,
stateEncoding: StateEncoding,
resolverEncoding: ResolverEncoding,
definition: address(this)
});
return info;
}

function create(bytes calldata encodedState) external override view returns (bool) {
TransferState memory state = abi.decode(encodedState, (TransferState));

Expand Down
16 changes: 16 additions & 0 deletions modules/contracts/src.sol/transferDefinitions/Withdraw.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,22 @@ contract Withdraw is ITransferDefinition {
bytes responderSignature;
}

string StateEncoding = "tuple(tuple(uint256[2] amount, address[2] to) balance, bytes initiatorSignature, address initiator, address responder, bytes32 data, uint256 nonce, uint256 fee)";

string ResolverEncoding = "tuple(bytes responderSignature)";

string Name = "Withdraw";

function getRegistryInformation() external override view returns (RegisteredTransfer memory) {
RegisteredTransfer memory info = RegisteredTransfer({
name: Name,
stateEncoding: StateEncoding,
resolverEncoding: ResolverEncoding,
definition: address(this)
});
return info;
}

function create(bytes calldata encodedState) external override pure returns (bool) {
TransferState memory state = abi.decode(encodedState, (TransferState));

Expand Down
1 change: 1 addition & 0 deletions modules/contracts/src.ts/actions/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { registerTransfer, registerTransferCommand } from "./registerTransfer";
export { fund, fundCommand } from "./fund";
export { migrate, migrateCommand } from "./migrate";
export { newToken, newTokenCommand } from "./newToken";
4 changes: 1 addition & 3 deletions modules/contracts/src.ts/actions/migrate.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import { migrate } from "./migrate";

describe("migrate()", () => {
it("should run without error", async () => {
await expect(
migrate(alice, addressBookPath, true),
).to.be.fulfilled;
await expect(migrate(alice, addressBookPath, false)).to.be.fulfilled;
});
});
7 changes: 7 additions & 0 deletions modules/contracts/src.ts/actions/migrate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Argv } from "yargs";
import { artifacts } from "../artifacts";
import { cliOpts, ConstructorArgs } from "../constants";
import { getAddressBook, isContractDeployed, deployContract } from "../utils";
import { registerTransfer } from "./registerTransfer";

const { formatEther } = utils;

Expand Down Expand Up @@ -45,11 +46,17 @@ export const migrate = async (wallet: Wallet, addressBookPath: string, silent =

const mastercopy = await deployHelper("ChannelMastercopy", []);
await deployHelper("ChannelFactory", [{ name: "mastercopy", value: mastercopy.address }]);
await deployHelper("TransferRegistry", []);

// Transfers
await deployHelper("HashlockTransfer", []);
await deployHelper("Withdraw", []);

// Register default transfers
log("\nRegistering Withdraw and HashlockTransfer");
await registerTransfer("Withdraw", wallet, addressBookPath, silent);
await registerTransfer("HashlockTransfer", wallet, addressBookPath, silent);

////////////////////////////////////////
// Print summary

Expand Down
16 changes: 16 additions & 0 deletions modules/contracts/src.ts/actions/registerTransfer.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { TransferNames } from "@connext/vector-types";
import { expect } from "@connext/vector-utils";

import { addressBookPath, alice } from "../tests";

import { migrate } from "./migrate";
import { registerTransfer } from "./registerTransfer";

describe("registerTransfer()", () => {
beforeEach(async () => {
await expect(migrate(alice, addressBookPath, true)).to.be.fulfilled;
});
it("should run without error", async () => {
await expect(registerTransfer(TransferNames.HashlockTransfer, alice, addressBookPath, false)).to.be.fulfilled;
});
});
Loading

0 comments on commit c0104fd

Please sign in to comment.