Skip to content

Commit

Permalink
Merge pull request #157 from perun-network/fixes-2024
Browse files Browse the repository at this point in the history
Completed multichain support and the Ethereum backend.
  • Loading branch information
RmbRT authored Oct 14, 2024
2 parents 6b04f04 + 688f126 commit 39aa91b
Show file tree
Hide file tree
Showing 26 changed files with 321 additions and 91 deletions.
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,17 @@
"./api/util": "./api/util/index.js",
"./ledger": "./ledger/index.js",
"./ledger/assets": "./ledger/assets/index.js",
"./ledger/backend/contracts": "./ledger/backend/contracts/index.js",
"./ledger/backend/ethereum": "./ledger/backend/ethereum/index.js",
"./test": "./test/index.js",
"./test/assets": "./test/assets/index.js",
"./test/ledger": "./test/ledger/index.js",
"./utils": "./utils/index.js",
"./utils/arrays": "./utils/arrays/index.js",
"./export/typedjson": "./export/typedjson.js"
"./utils/hexbytes": "./utils/hexbytes.js",
"./export/typedjson": "./export/typedjson.js",
"./crypto": "./crypto/index.js",
"./crypto/ethereum": "./crypto/ethereum/index.js",
"./crypto/substrate": "./crypto/substrate/index.js"
},
"imports": {
"#erdstall/*": "./*/index.js"
Expand Down
40 changes: 28 additions & 12 deletions src/api/responses/clientconfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
import { ErdstallObject, registerErdstallType } from "#erdstall/api";
import { jsonObject } from "#erdstall/export/typedjson";
import { Backend, BackendChainConfig } from "#erdstall/ledger/backend";
import { EthereumChainConfig } from "#erdstall/ledger/backend/ethereum/chainconfig";
import { SubstrateChainConfig } from "#erdstall/ledger/backend/substrate/chainconfig";
import { Address, Crypto } from "#erdstall/crypto";
import { EthereumAddress } from "#erdstall/crypto/ethereum";
import { Chain } from "#erdstall/ledger";
import { customJSON } from "../util";

Expand All @@ -19,21 +22,25 @@ export class ChainConfig<B extends Backend> {
this.data = data;
}

clone(): ChainConfig<B> {
return new ChainConfig<B>(this.id, this.type, this.data!.clone());
}

static fromJSON(data: any): ChainConfig<Backend> {
const d = JSON.parse(data);
switch (d.type) {
switch (data.type) {
case "substrate":
return new ChainConfig(data.id, "substrate", {
blockStreamLAddr: d.data.blockStreamLAddr,
});
return new ChainConfig(data.id, "substrate",
new SubstrateChainConfig(data.data.blockStreamLAddr));
case "ethereum":
return new ChainConfig(d.id, "ethereum", {
contract: d.data.contract,
networkID: d.data.networkID,
powDepth: d.data.powDepth,
});
return new ChainConfig(data.id, "ethereum",
new EthereumChainConfig({
contract: EthereumAddress.fromJSON(data.data.contract),
nodeRPC: data.data.nodeRPC,
networkID: data.data.networkID,
powDepth: data.data.powDepth,
}));
default:
throw new Error(`unknown backend type: ${d.type}`);
throw new Error(`unknown backend type: ${data.type}`);
}
}

Expand Down Expand Up @@ -87,7 +94,7 @@ export class ClientConfig extends ErdstallObject {
static fromJSON(data: any): ClientConfig {
let chains: ChainConfig<Backend>[] = [];
for (const conf of data.chains as ChainConfig<Backend>[]) {
chains.push(ChainConfig.fromJSON(JSON.stringify(conf)));
chains.push(ChainConfig.fromJSON(conf));
}
let enc: Map<Chain, Address<Crypto>> = new Map();
for(const key in data.enclave ?? {}) {
Expand All @@ -113,6 +120,15 @@ export class ClientConfig extends ErdstallObject {
protected objectTypeName(): string {
return clientConfigTypeName;
}

clone(): ClientConfig {
return new ClientConfig(
this.chains.map(c => c.clone()),
new Map<Chain, Address<Crypto>>(
Array.from(this.enclave.entries()).map(([c,addr]) => [c, addr.clone()])),
this.enclaveNativeSigner.clone()
);
}
}

registerErdstallType(clientConfigTypeName, ClientConfig);
Expand Down
5 changes: 3 additions & 2 deletions src/api/transactions/exitrequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@ const fullExitTypeName = "full";

@jsonObject
export class FullExit extends ExitMode {
chain: number;
@jsonMember(Number)
chain?: number;

constructor(chain: number, immediate: boolean) {
constructor(chain: number | undefined, immediate: boolean) {
super(immediate);
this.chain = chain;
}
Expand Down
9 changes: 3 additions & 6 deletions src/api/transactions/transaction.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,8 @@ const testTrade =
const testError = '{"id":"an-id","error":"could not get proof"}';

const testSubscribeTXs =
'{"id":"an-id","data":{"type":"SubscribeTXs","data":{"who":{"type":"ethereum","data":"0x165803a9e8ae54dd4e8da44a8d19897c19cb91aeaea6afc67a7e8c6f35323716"}, "cancel": false}}}';
'{"id":"an-id","data":{"type":"SubscribeTXs","data":{"who":{"type":"ethereum","data":"0x8d19897c19cb91aeaea6afc67a7e8c6f35323716"}, "cancel": false}}}';
const testSubscribeBalanceProofs =
'{"id":"an-id","data":{"type":"SubscribeBalanceProofs","data":{"who":{"type":"ethereum","data":"0x165803a9e8ae54dd4e8da44a8d19897c19cb91aeaea6afc67a7e8c6f35323716"}}}}';
'{"id":"an-id","data":{"type":"SubscribeBalanceProofs","data":{"who":{"type":"ethereum","data":"0x8d19897c19cb91aeaea6afc67a7e8c6f35323716"}}}}';
const testSubscribePhaseShifts =
'{"id":"an-id","data":{"type":"SubscribePhaseShifts","data":{"cancel": false}}}';

const testHashing =
'{"sig":{"type":"test","data":{"address":{"type":"test","data":"0x11342d7d8ae3c7ead6845ade6838864c64197ec6a3f9ea8b6e91d6e95cbd1b80"},"msg":"eyJ2YWx1ZSI6eyJkYXRhIjp7Im5vbmNlIjoiMjEyNTY5MzA0NDkxNjI0OTIyOSIsInJlY2lwaWVudCI6eyJkYXRhIjoiOWZkNGJlYjE5MzYzYWE4NTg2MzIzNTQ0NTA2MmJjMzc5NTU1NjFjNDMyOGIxMzhmYTc0ZDQ2NTljYzgxODdkMCIsInR5cGUiOiJ0ZXN0In0sInNlbmRlciI6eyJkYXRhIjoiMTEzNDJkN2Q4YWUzYzdlYWQ2ODQ1YWRlNjgzODg2NGM2NDE5N2VjNmEzZjllYThiNmU5MWQ2ZTk1Y2JkMWI4MCIsInR5cGUiOiJ0ZXN0In0sInZhbHVlcyI6eyIzMDgxMCI6eyJuZnRzIjp7IjljMzk0ZDE3YjJiNmI5ZGM1YjU3MTg5ODkxNmY0MGQ5Y2ViOTY0NmU1MWNmNDFhY2VjZTU0ODQzOTk1MDFhYTYiOlsiMHg2YTg5YmY0YzdhZGEzMjQ4YTUwNzYyMGU4NWJjMzBmMDQ3M2IyNmQ3MDdiZGUxZGViZDEwOWNlM2NhMWJkOTRkIiwiMHhmMTY2NWQ4MWNhYWI4MTNhMjlkOTFmNTIxMjhjOGQ4YjcwOWQ2MGI0MmUwNzhhOWFiODYxYjlhZDRkMzEyODA1IiwiMHhmZTc3MzRjM2RiMTlkM2Y5Y2Y2NWEyYjY4ZTVmMmIyYjljZGI5ODFlMjdkYmM1NGRkZWQ0YjRmNmU3ODA1NzE1Il19fSwiMzc2NzYiOnsiZnVuZ2libGVzIjp7IjdjOWRlMDM0ZmFiNmViOTU3ZDNmNzNmOWU5ZTE2OGE4OWE3ZTFkZGZiMzFjNTQyOWYxNDQxZDRkZDBmNGI5NDAiOiIweDk0OWMxODY3YzdmZGUwYTZmODFlMmY2MmNhYWNkOWExMWE1MjMyZmJmYWM5YmQzZmE5MGU5ZWIxOGY0Mzk2MyJ9fSwiNDY4MzIiOnsiZnVuZ2libGVzIjp7IjQzMTllOGYxOTZjZTNjNWE3NzNlZTNlMzc3ZDNmZDhmMjE4ODEwYTNmYjAzZWZhZDdjNzUxMzE2MDE4ODgzYzkiOiIweGYwMmRmZjJkZWIwYmQ5ODA3NmFmYjNmOTFiOGI1ZjYzZGI2N2UzYWQ2OGFkMjEwNGZiOGU3MGRmYjYwM2NjMjcifX19fSwidHlwZSI6IlRyYW5zZmVyIn19"}},"sender":{"type":"test","data":"0x11342d7d8ae3c7ead6845ade6838864c64197ec6a3f9ea8b6e91d6e95cbd1b80"},"nonce":"2125693044916249229","recipient":{"type":"test","data":"0x9fd4beb19363aa85863235445062bc37955561c4328b138fa74d4659cc8187d0"},"values":{"30810":{"nfts":{"9c394d17b2b6b9dc5b571898916f40d9ceb9646e51cf41acece5484399501aa6":["0x6a89bf4c7ada3248a507620e85bc30f0473b26d707bde1debd109ce3ca1bd94d","0xf1665d81caab813a29d91f52128c8d8b709d60b42e078a9ab861b9ad4d312805","0xfe7734c3db19d3f9cf65a2b68e5f2b2b9cdb981e27dbc54dded4b4f6e7805715"]}},"37676":{"fungibles":{"7c9de034fab6eb957d3f73f9e9e168a89a7e1ddfb31c5429f1441d4dd0f4b940":"0x949c1867c7fde0a6f81e2f62caacd9a11a5232fbfac9bd3fa90e9eb18f43963"}},"46832":{"fungibles":{"4319e8f196ce3c5a773ee3e377d3fd8f218810a3fb03efad7c751316018883c9":"0xf02dff2deb0bd98076afb3f91b8b5f63db67e3ad68ad2104fb8e70dfb603cc27"}}}}';
'{"id":"an-id","data":{"type":"SubscribePhaseShifts","data":{"cancel": false}}}';
1 change: 1 addition & 0 deletions src/api/util/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export * from "./bigint";
export { ABIEncoder, ABIPacked } from "./abiencoder";
export type { ABIEncodable, ABIValue } from "./abiencoder";
export * from "./customjson";
export * from "./pending_transaction";
13 changes: 12 additions & 1 deletion src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
import * as crypto from "#erdstall/crypto";
import { AttestationResult, ClientConfig } from "#erdstall/api/responses";
import { Enclave, isEnclaveEvent, EnclaveReader } from "#erdstall/enclave";
import { Account, isLedgerEvent, LedgerEvent } from "#erdstall/ledger";
import { Chain, Account, isLedgerEvent, LedgerEvent } from "#erdstall/ledger";
import { LocalAsset } from "#erdstall/ledger/assets";
import { Backend } from "#erdstall/ledger/backend";
import { BackendChainConfig } from "#erdstall/ledger/backend/backends";
Expand Down Expand Up @@ -50,6 +50,15 @@ export class Client<Bs extends Backend[]> implements ErdstallClient<Bs> {

private blockchainReadCtors: BackendClientConstructors;

#config?: ClientConfig;

get config(): ClientConfig | undefined { return this.#config?.clone(); }
get chainTypes() {
return new Map<Chain, string>(
this.#config!.chains.map(cfg => [cfg.id, cfg.type]));
}


constructor(
enclaveConn: (EnclaveReader & InternalEnclaveWatcher) | URL,
blockchainReadCtors: BackendClientConstructors,
Expand Down Expand Up @@ -256,6 +265,8 @@ export class Client<Bs extends Backend[]> implements ErdstallClient<Bs> {

this.enclaveConn.once("error", reject);
this.enclaveConn.once("config", (config: ClientConfig) => {
this.#config = config;

onConfigHandler(config);

this.erdstallOneShotHandlerCache.clear();
Expand Down
4 changes: 3 additions & 1 deletion src/crypto/address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ export function registerAddressType(

export abstract class Address<_C extends Crypto> {
abstract type(): _C;
get key(): string { return Address.toJSON(this); }
get key(): string { return JSON.stringify(Address.toJSON(this)); }
abstract equals(other: Address<_C>): boolean;
abstract toString(): string;
abstract toJSON(): string;
abstract get keyBytes(): Uint8Array;

clone(): Address<_C> { return Address.fromJSON(Address.toJSON(this)) as Address<_C>; }

static ensure(addr: string | Address<Crypto>): Address<Crypto> {
if (addr === undefined) return addr;
if (addr instanceof Address) return addr;
Expand Down
29 changes: 26 additions & 3 deletions src/crypto/asset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,23 @@
import { Chain } from "#erdstall/ledger/chain";
import { Address, Crypto } from "#erdstall/crypto";
import { ethers } from "ethers";
import { toHex } from "#erdstall/utils/hexbytes";


export enum AssetType {
Fungible,
NFT
}

export function AssetTypeName(t: AssetType): string {
switch(t)
{
case AssetType.Fungible: return "FUN";
case AssetType.NFT: return "NFT";
}
throw new Error(`Unknown AssetType: ${t}`);
}

export class AssetID {
// [Origin Chain][AssetType][ID LocalAsset] packed into fixed-size array.
bytes: Uint8Array;
Expand All @@ -25,7 +40,7 @@ export class AssetID {

static fromMetadata(
chain: Chain,
type: number,
type: AssetType,
localID: Uint8Array,
): AssetID {
const bytes = new Uint8Array(3 + localID.length);
Expand All @@ -41,8 +56,8 @@ export class AssetID {
return origin as Chain;
}

type(): number {
return this.bytes[2];
type(): AssetType {
return this.bytes[2] as AssetType;
}

localID(): Uint8Array {
Expand All @@ -59,4 +74,12 @@ export class AssetID {
}
return 0;
}

toString(): string {
return `${
this.origin()
}/${
AssetTypeName(this.type())
}/${toHex(this.localID(), "")}`;
}
}
2 changes: 2 additions & 0 deletions src/crypto/ethereum/address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export class EthereumAddress extends Address<"ethereum"> implements ABIValue {
private value: Uint8Array;
constructor(value: Uint8Array) {
super();
if(value.length !== 20)
throw new Error(`Invalid length (${value.length}/20)`);
this.value = value;
}

Expand Down
12 changes: 9 additions & 3 deletions src/crypto/ethereum/signer.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
// SPDX-License-Identifier: Apache-2.0
"use strict";

import { Signer as EthersSigner, ethers } from "ethers";
import { Signer as EthersSigner, Provider, ethers } from "ethers";
import { Signer, Address, Signature } from "#erdstall/crypto";
import { EthereumSignature } from "./signature";
import { EthereumAddress } from "./address";

// Compile-time check that the EthersSigner implements the Signer interface.
export class EthereumSigner implements Signer<"ethereum"> {
readonly ethersSigner: EthersSigner;
#ethersSigner: EthersSigner;

get ethersSigner(): EthersSigner { return this.#ethersSigner; }

connect(p: Provider) { this.#ethersSigner = this.#ethersSigner.connect(p); }

constructor(ethersSigner: EthersSigner) {
this.ethersSigner = ethersSigner;
this.#ethersSigner = ethersSigner;
}

type(): "ethereum" { return "ethereum"; }

async sign(message: Uint8Array): Promise<Signature<"ethereum">> {
const sig = await this.ethersSigner.signMessage(
ethers.getBytes(ethers.keccak256(message)));
Expand Down
1 change: 1 addition & 0 deletions src/crypto/signer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Crypto, Address, Signature } from "#erdstall/crypto";
const signatureImpls = new Map<string, Serializable<Signature<Crypto>>>();

export abstract class Signer<B extends Crypto> {
abstract type(): B;
abstract address(): Promise<Address<B>>;
abstract sign(msg: Uint8Array): Promise<Signature<B>>;
}
Expand Down
3 changes: 3 additions & 0 deletions src/crypto/substrate/signer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ export class SubstrateSigner implements Signer<"substrate"> {
this.keyPair = keyPair;
}

type(): "substrate" { return "substrate"; }


async sign(message: Uint8Array): Promise<Signature<"substrate">> {
await cryptoWaitReady();
const sig = sr25519Sign(message, this.keyPair);
Expand Down
23 changes: 14 additions & 9 deletions src/enclave/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ export class Enclave implements EnclaveWriter {
private calls: Map<string, [Function, Function]>;
private id: number;

private opened: boolean = false;
private globallySubscribed: boolean;
private individuallySubscribed: Set<Address<Crypto>>;
private phaseShiftSubscribed: boolean;
Expand Down Expand Up @@ -317,12 +318,11 @@ export class Enclave implements EnclaveWriter {
const [resolve, reject] = this.calls.get(msg.id)!;
this.calls.delete(msg.id);
if (msg.error) {
reject(msg.error);
this.callEvent("error", msg.error);
reject(new Error(msg.error));
return this.callEvent("error", msg.error);
} else {
resolve(msg.data);
return resolve(msg.data);
}
return
} else if(msg.error) {
console.error("unexpected error:", msg.error);
this.callEvent("error", msg.error);
Expand Down Expand Up @@ -380,16 +380,20 @@ export class Enclave implements EnclaveWriter {

this.callEvent("error", new Error("connection error"));

if(this.opened) {
setTimeout(() => {
try {
this.connect();
} catch {}
}, 1000);
}

this.provider.close();
setTimeout(() => {
try {
this.connect();
} catch {}
}, 1000);
}

private onOpen(_: Event) {
const calls = [];
this.opened = true;
if (this.globallySubscribed)
calls.push(new SubscribeTXs(), new SubscribeBalanceProofs());

Expand All @@ -408,6 +412,7 @@ export class Enclave implements EnclaveWriter {
}

private onClose(_: Event) {
this.opened = false;
this.callEvent("close", {} as any);
}
}
3 changes: 2 additions & 1 deletion src/erdstall.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ export interface Exiter {
* to use `Leaver.leave` over the individual `Exiter.exit` call, because the
* latter has to be manually followed by a call to `Withdrawer.withdraw`.
*/
exit(): Promise<BalanceProofs>;
exit(chain?: number): Promise<BalanceProofs>;
}

/**
Expand All @@ -261,6 +261,7 @@ export interface Leaver<Bs extends Backend[]> extends Exiter {
* more information about theh return type.
*/
leave(
chain?: number,
notify?: (message: string, stage: number, maxStages: number) => void,
): Promise< Map<number, TransactionGenerator<Bs[number]>> >;
}
Expand Down
5 changes: 5 additions & 0 deletions src/ledger/assets/amount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
assertUint256,
registerAssetType,
} from "./asset";
import { AssetType } from "#erdstall/crypto";
import { bigTo0xEven } from "#erdstall/export/typedjson";

/** Amount represents a currency amount in its smallest unit. */
Expand All @@ -23,6 +24,8 @@ export class Amount extends Asset {
this.value = v;
}

assetType(): AssetType.Fungible { return AssetType.Fungible; }

toJSON() {
return bigTo0xEven(this.value);
}
Expand All @@ -31,6 +34,8 @@ export class Amount extends Asset {
return new Amount(BigInt(hexString));
}

toString() { return this.value.toString(); }

typeTag(): TypeTagName {
return TypeTags.Amount;
}
Expand Down
Loading

0 comments on commit 39aa91b

Please sign in to comment.