From 9de3c59c19ee3d5a7ff5ecb9c6a8f1378926a084 Mon Sep 17 00:00:00 2001 From: nabarun Date: Fri, 8 Apr 2022 10:59:03 +0530 Subject: [PATCH] Implement setRecord and add test for setAutorityBond --- package.json | 7 + .../nameservice/v1beta1/nameservice.proto | 10 +- src/account.ts | 62 ++++- src/bond.test.ts | 2 - src/decs.d.ts | 3 + src/graphql.d.ts | 1 - src/index.ts | 142 ++++++++++- src/messages/nameservice.ts | 238 ++++++++++++++++++ src/naming.test.ts | 38 ++- .../nameservice/v1beta1/nameservice.ts | 60 ++--- src/schema/record.json | 5 + src/testing/data/watcher.yml | 6 + src/testing/helper.ts | 10 + src/types.ts | 132 ++++++++++ src/util.ts | 13 + tsconfig.json | 2 +- yarn.lock | 223 +++++++++++++++- 17 files changed, 901 insertions(+), 53 deletions(-) create mode 100644 src/decs.d.ts delete mode 100644 src/graphql.d.ts create mode 100644 src/schema/record.json create mode 100644 src/testing/data/watcher.yml create mode 100644 src/types.ts diff --git a/package.json b/package.json index 6a50eac..2b50d28 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "devDependencies": { "@types/is-url": "^1.2.30", "@types/jest": "^27.4.1", + "@types/semver": "^7.3.9", "dotenv": "^16.0.0", "jest": "^27.5.1", "protoc-gen-ts": "^0.8.2", @@ -23,10 +24,16 @@ "axios": "^0.26.1", "bip32": "^3.0.1", "bip39": "^3.0.4", + "canonical-json": "^0.0.4", "evmosjs": "^0.2.5", "graphql.js": "^0.6.8", + "ipld-dag-cbor": "^1.0.1", "is-url": "^1.2.4", "js-sha256": "^0.9.0", + "js-yaml": "^4.1.0", + "jsonschema": "^1.4.0", + "node-yaml": "^4.0.1", + "semver": "^7.3.5", "tiny-secp256k1": "^2.2.1" }, "scripts": { diff --git a/proto/vulcanize/nameservice/v1beta1/nameservice.proto b/proto/vulcanize/nameservice/v1beta1/nameservice.proto index eaea814..cfba853 100644 --- a/proto/vulcanize/nameservice/v1beta1/nameservice.proto +++ b/proto/vulcanize/nameservice/v1beta1/nameservice.proto @@ -68,21 +68,17 @@ message Record { string bond_id = 2 [ (gogoproto.moretags) = "json:\"bondId\" yaml:\"bondId\"" ]; - google.protobuf.Timestamp create_time = 3 [ - (gogoproto.stdtime) = true, - (gogoproto.nullable) = false, + string create_time = 3 [ (gogoproto.moretags) = "json:\"createTime\" yaml:\"createTime\"" ]; - google.protobuf.Timestamp expiry_time = 4 [ - (gogoproto.stdtime) = true, - (gogoproto.nullable) = false, + string expiry_time = 4 [ (gogoproto.moretags) = "json:\"expiryTime\" yaml:\"expiryTime\"" ]; bool deleted = 5; repeated string owners = 6 [ (gogoproto.moretags) = "json:\"owners\" yaml:\"owners\"" ]; - bytes attributes = 7 [ + string attributes = 7 [ (gogoproto.moretags) = "json:\"attributes\" yaml:\"attributes\"" ]; } diff --git a/src/account.ts b/src/account.ts index 47a5957..b5bfb5b 100644 --- a/src/account.ts +++ b/src/account.ts @@ -2,11 +2,17 @@ import assert from 'assert'; import BIP32Factory from 'bip32'; import * as ecc from 'tiny-secp256k1'; import * as bip39 from 'bip39'; +import canonicalStringify from 'canonical-json'; +import secp256k1 from 'secp256k1'; import { MessageTypes, signTypedData, SignTypedDataVersion } from '@metamask/eth-sig-util'; import { Ripemd160, Secp256k1 } from "@cosmjs/crypto"; -import { toBech32 } from '@cosmjs/encoding'; +import { fromHex, toBech32, toHex } from '@cosmjs/encoding'; import { rawSecp256k1PubkeyToRawAddress } from "@cosmjs/amino"; +import { Payload, Signature } from './types'; +import { sha256 } from 'js-sha256'; + +const AMINO_PREFIX = 'EB5AE98721'; const HDPATH = "m/44'/60'/0'/0"; const bip32 = BIP32Factory(ecc); @@ -27,6 +33,8 @@ export class Account { _publicKey?: Uint8Array _cosmosAddress?: string _formattedCosmosAddress?: string + _registryPublicKey?: string + _registryAddress?: string /** * Generate bip39 mnemonic. @@ -67,6 +75,14 @@ export class Account { return this._formattedCosmosAddress; } + get registryPublicKey() { + return this._registryPublicKey; + } + + get registryAddress() { + return this._registryAddress; + } + async init () { // Generate public key. const keypair = await Secp256k1.makeKeypair(this._privateKey); @@ -75,11 +91,18 @@ export class Account { this._publicKey = compressed // 2. Generate cosmos-sdk address. - // let publicKeySha256 = sha256(this._publicKey); this._cosmosAddress = new Ripemd160().update(keypair.pubkey).digest().toString(); // 3. Generate cosmos-sdk formatted address. this._formattedCosmosAddress = toBech32('ethm', rawSecp256k1PubkeyToRawAddress(this._publicKey)); + + // 4. Generate registry formatted public key. + const publicKeyInHex = AMINO_PREFIX + toHex(this._publicKey); + this._registryPublicKey = Buffer.from(publicKeyInHex, 'hex').toString('base64'); + + // 5. Generate registry formatted address. + let publicKeySha256 = sha256(Buffer.from(publicKeyInHex, 'hex')); + this._registryAddress = new Ripemd160().update(fromHex(publicKeySha256)).digest().toString(); } /** @@ -89,6 +112,41 @@ export class Account { return this._privateKey.toString('hex'); } + /** + * Get record signature. + * @param {object} record + */ + async signRecord(record: any) { + assert(record); + + const recordAsJson = canonicalStringify(record); + // Double sha256. + const recordBytesToSign = Buffer.from(sha256(Buffer.from(sha256(Buffer.from(recordAsJson)), 'hex')), 'hex'); + + // Sign message + assert(recordBytesToSign); + + const messageToSignSha256 = sha256(recordBytesToSign); + const messageToSignSha256InBytes = Buffer.from(messageToSignSha256, 'hex'); + const sigObj = secp256k1.ecdsaSign(messageToSignSha256InBytes, this.privateKey); + + return Buffer.from(sigObj.signature); + } + + async signPayload(payload: Payload) { + assert(payload); + + const { record } = payload; + const messageToSign = record.getMessageToSign(); + + const sig = await this.signRecord(messageToSign); + assert(this.registryPublicKey) + const signature = new Signature(this.registryPublicKey, sig.toString('base64')); + payload.addSignature(signature); + + return signature; + } + /** * Sign message. */ diff --git a/src/bond.test.ts b/src/bond.test.ts index b8f1874..943bbdb 100644 --- a/src/bond.test.ts +++ b/src/bond.test.ts @@ -1,8 +1,6 @@ import { Registry } from './index'; import { getConfig } from './testing/helper'; -const TX_WAIT_TIME = 5000; // in milliseconds. - const { chainId, restEndpoint, gqlEndpoint, privateKey, accountAddress, fee } = getConfig(); jest.setTimeout(90 * 1000); diff --git a/src/decs.d.ts b/src/decs.d.ts new file mode 100644 index 0000000..41e2e88 --- /dev/null +++ b/src/decs.d.ts @@ -0,0 +1,3 @@ +declare module 'graphql.js' +declare module 'node-yaml' +declare module 'canonical-json' diff --git a/src/graphql.d.ts b/src/graphql.d.ts deleted file mode 100644 index 5d35563..0000000 --- a/src/graphql.d.ts +++ /dev/null @@ -1 +0,0 @@ -declare module 'graphql.js' diff --git a/src/index.ts b/src/index.ts index 0fbc3c1..f2f332c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,7 +14,8 @@ import { createTxMsgCancelBond, createTxMsgCreateBond, createTxMsgRefillBond, cr import { RegistryClient } from "./registry-client"; import { Account } from "./account"; import { createTransaction } from "./txbuilder"; -import { createTxMsgReserveAuthority, MessageMsgReserveAuthority } from './messages/nameservice'; +import { createTxMsgReserveAuthority, createTxMsgSetAuthorityBond, createTxMsgSetName, createTxMsgSetRecord, MessageMsgReserveAuthority, MessageMsgSetAuthorityBond, MessageMsgSetName, MessageMsgSetRecord } from './messages/nameservice'; +import { Payload, Record } from './types'; const DEFAULT_WRITE_ERROR = 'Unable to write to chiba-clonk.'; @@ -51,6 +52,7 @@ export class Registry { path: [ 'submit' ] }g */ + console.error(error) const message = JSON.parse(error.message); return message.log || DEFAULT_WRITE_ERROR; } @@ -80,6 +82,28 @@ export class Registry { return this._client.getAccount(address); } + /** + * Publish record. + * @param transactionPrivateKey - private key in HEX to sign transaction. + */ + async setRecord( + params: { privateKey: string, record: any, bondId: string }, + senderAddress: string, + transactionPrivateKey: string, + fee: Fee + ) { + let result; + + try { + result = await this._submitRecordTx(params, senderAddress, transactionPrivateKey, fee); + } catch (err: any) { + const error = err[0] || err; + throw new Error(Registry.processWriteError(error)); + } + + return parseTxResponse(result); + } + /** * Send coins. */ @@ -247,7 +271,7 @@ export class Registry { /** * Reserve authority. */ - async reserveAuthority(params: MessageMsgReserveAuthority, senderAddress: string, privateKey: string, fee: Fee) { + async reserveAuthority(params: MessageMsgReserveAuthority, senderAddress: string, privateKey: string, fee: Fee) { let result; try { @@ -270,13 +294,125 @@ export class Registry { return parseTxResponse(result); } + /** + * Set authority bond. + * @param {string} name + * @param {string} bondId + * @param {string} privateKey + * @param {object} fee + */ + async setAuthorityBond(params: MessageMsgSetAuthorityBond, senderAddress: string, privateKey: string, fee: Fee) { + let result; + + try { + const { account: { base_account: accountInfo } } = await this.getAccount(senderAddress); + + const sender = { + accountAddress: accountInfo.address, + sequence: accountInfo.sequence, + accountNumber: accountInfo.account_number, + pubkey: accountInfo.pub_key.key, + } + + const msg = createTxMsgSetAuthorityBond(this._chain, sender, fee, '', params) + result = await this._submitTx(msg, privateKey, sender); + } catch (err: any) { + const error = err[0] || err; + throw new Error(Registry.processWriteError(error)); + } + + return parseTxResponse(result); + } + /** * Lookup authorities by names. */ - async lookupAuthorities(names: string[], auction = false) { + async lookupAuthorities(names: string[], auction = false) { return this._client.lookupAuthorities(names, auction); } + /** + * Set name (WRN) to record ID (CID). + * @param {string} wrn + * @param {string} id + * @param {string} privateKey + * @param {object} fee + */ + async setName(params: MessageMsgSetName, senderAddress: string, privateKey: string, fee: Fee) { + let result; + + try { + const { account: { base_account: accountInfo } } = await this.getAccount(senderAddress); + + const sender = { + accountAddress: accountInfo.address, + sequence: accountInfo.sequence, + accountNumber: accountInfo.account_number, + pubkey: accountInfo.pub_key.key, + } + + const msg = createTxMsgSetName(this._chain, sender, fee, '', params) + result = await this._submitTx(msg, privateKey, sender); + } catch (err: any) { + const error = err[0] || err; + throw new Error(Registry.processWriteError(error)); + } + + return parseTxResponse(result); + } + + /** + * Submit record transaction. + * @param privateKey - private key in HEX to sign message. + * @param txPrivateKey - private key in HEX to sign transaction. + */ + async _submitRecordTx( + { privateKey, record, bondId }: { privateKey: string, record: any, bondId: string }, + senderAddress: string, + txPrivateKey: string, + fee: Fee + ) { + if (!isKeyValid(privateKey)) { + throw new Error('Registry privateKey should be a hex string.'); + } + + if (!isKeyValid(bondId)) { + throw new Error(`Invalid bondId: ${bondId}.`); + } + + // Sign record. + const recordSignerAccount = new Account(Buffer.from(privateKey, 'hex')); + await recordSignerAccount.init(); + const registryRecord = new Record(record); + const payload = new Payload(registryRecord); + await recordSignerAccount.signPayload(payload); + + // Send record payload Tx. + return this._submitRecordPayloadTx({ payload, bondId }, senderAddress, txPrivateKey, fee); + } + + async _submitRecordPayloadTx(params: MessageMsgSetRecord, senderAddress: string, privateKey: string, fee: Fee) { + if (!isKeyValid(privateKey)) { + throw new Error('Registry privateKey should be a hex string.'); + } + + if (!isKeyValid(params.bondId)) { + throw new Error(`Invalid bondId: ${params.bondId}.`); + } + + const { account: { base_account: accountInfo } } = await this.getAccount(senderAddress); + + const sender = { + accountAddress: accountInfo.address, + sequence: accountInfo.sequence, + accountNumber: accountInfo.account_number, + pubkey: accountInfo.pub_key.key, + } + + const msg = createTxMsgSetRecord(this._chain, sender, fee, '', params) + return this._submitTx(msg, privateKey, sender); + } + /** * Submit a generic Tx to the chain. */ diff --git a/src/messages/nameservice.ts b/src/messages/nameservice.ts index 0d1e864..54cdefd 100644 --- a/src/messages/nameservice.ts +++ b/src/messages/nameservice.ts @@ -8,7 +8,9 @@ import { } from '@tharsis/transactions' import * as nameserviceTx from '../proto/vulcanize/nameservice/v1beta1/tx' +import * as nameservice from '../proto/vulcanize/nameservice/v1beta1/nameservice' import { createTx } from './util' +import { Payload } from '../types' const MSG_RESERVE_AUTHORITY_TYPES = { MsgValue: [ @@ -18,11 +20,66 @@ const MSG_RESERVE_AUTHORITY_TYPES = { ], } +const MSG_SET_NAME_TYPES = { + MsgValue: [ + { name: 'wrn', type: 'string' }, + { name: 'cid', type: 'string' }, + { name: 'signer', type: 'string' }, + ], +} + +const MSG_SET_RECORD_TYPES = { + MsgValue: [ + { name: 'bond_id', type: 'string' }, + { name: 'signer', type: 'string' }, + { name: 'payload', type: 'TypePayload' }, + ], + TypePayload: [ + { name: 'record', type: 'TypePayloadRecord' }, + { name: 'signatures', type: 'TypePayloadSignatures[]' }, + ], + TypePayloadRecord: [ + { name: 'id', type: 'string' }, + { name: 'bond_id', type: 'string' }, + { name: 'create_time', type: 'string' }, + { name: 'expiry_time', type: 'string' }, + { name: 'deleted', type: 'bool' }, + { name: 'attributes', type: 'string' }, + ], + TypePayloadSignatures: [ + { name: 'sig', type: 'string' }, + { name: 'pub_key', type: 'string' } + ], +} + +const MSG_SET_AUTHORITY_BOND_TYPES = { + MsgValue: [ + { name: 'name', type: 'string' }, + { name: 'bond_id', type: 'string' }, + { name: 'signer', type: 'string' }, + ], +} + export interface MessageMsgReserveAuthority { name: string owner: string } +export interface MessageMsgSetName { + wrn: string + cid: string +} + +export interface MessageMsgSetRecord { + bondId: string + payload: Payload +} + +export interface MessageMsgSetAuthorityBond { + name: string + bondId: string +} + export function createTxMsgReserveAuthority( chain: Chain, sender: Sender, @@ -47,6 +104,78 @@ export function createTxMsgReserveAuthority( return createTx(chain, sender, fee, memo, types, msg, msgCosmos) } +export function createTxMsgSetName( + chain: Chain, + sender: Sender, + fee: Fee, + memo: string, + params: MessageMsgSetName, +) { + const types = generateTypes(MSG_SET_NAME_TYPES) + + const msg = createMsgSetName( + params.wrn, + params.cid, + sender.accountAddress + ) + + const msgCosmos = protoCreateMsgSetName( + params.wrn, + params.cid, + sender.accountAddress + ) + + return createTx(chain, sender, fee, memo, types, msg, msgCosmos) +} + +export function createTxMsgSetRecord( + chain: Chain, + sender: Sender, + fee: Fee, + memo: string, + params: MessageMsgSetRecord, +) { + const types = generateTypes(MSG_SET_RECORD_TYPES) + + const msg = createMsgSetRecord( + params.bondId, + params.payload, + sender.accountAddress + ) + + const msgCosmos = protoCreateMsgSetRecord( + params.bondId, + params.payload, + sender.accountAddress + ) + + return createTx(chain, sender, fee, memo, types, msg, msgCosmos) +} + +export function createTxMsgSetAuthorityBond( + chain: Chain, + sender: Sender, + fee: Fee, + memo: string, + params: MessageMsgSetAuthorityBond, +) { + const types = generateTypes(MSG_SET_AUTHORITY_BOND_TYPES) + + const msg = createMsgSetAuthorityBond( + params.name, + params.bondId, + sender.accountAddress + ) + + const msgCosmos = protoCreateMsgSetAuthorityBond( + params.name, + params.bondId, + sender.accountAddress + ) + + return createTx(chain, sender, fee, memo, types, msg, msgCosmos) +} + function createMsgReserveAuthority( name: string, signer: string, @@ -78,3 +207,112 @@ const protoCreateMsgReserveAuthority = ( path: 'vulcanize.nameservice.v1beta1.MsgReserveAuthority', } } + +function createMsgSetName( + wrn: string, + cid: string, + signer: string +) { + return { + type: 'nameservice/SetName', + value: { + wrn, + cid, + signer + }, + } +} + +const protoCreateMsgSetName = ( + wrn: string, + cid: string, + signer: string +) => { + const setNameMessage = new nameserviceTx.vulcanize.nameservice.v1beta1.MsgSetName({ + wrn, + cid, + signer, + }) + + return { + message: setNameMessage, + path: 'vulcanize.nameservice.v1beta1.MsgSetName', + } +} + +function createMsgSetRecord( + bondId: string, + payload: Payload, + signer: string +) { + return { + type: 'nameservice/SetRecord', + value: { + bond_id: bondId, + signer, + payload: payload.serialize() + }, + } +} + +const protoCreateMsgSetRecord = ( + bondId: string, + payloadData: Payload, + signer: string +) => { + const record = new nameservice.vulcanize.nameservice.v1beta1.Record(payloadData.record.serialize()) + + const signatures = payloadData.signatures.map( + signature => new nameservice.vulcanize.nameservice.v1beta1.Signature( + signature.serialize() + ) + ) + + const payload = new nameserviceTx.vulcanize.nameservice.v1beta1.Payload({ + record, + signatures + }) + + const setNameMessage = new nameserviceTx.vulcanize.nameservice.v1beta1.MsgSetRecord({ + bond_id: bondId, + signer, + payload + }) + + return { + message: setNameMessage, + path: 'vulcanize.nameservice.v1beta1.MsgSetRecord', + } +} + +function createMsgSetAuthorityBond( + name: string, + bondId: string, + signer: string +) { + return { + type: 'nameservice/SetAuthorityBond', + value: { + name, + bond_id: bondId, + signer + }, + } +} + +const protoCreateMsgSetAuthorityBond = ( + name: string, + bondId: string, + signer: string +) => { + const setAuthorityBondMessage = new nameserviceTx.vulcanize.nameservice.v1beta1.MsgSetAuthorityBond({ + name, + bond_id: bondId, + signer, + }) + + return { + message: setAuthorityBondMessage, + path: 'vulcanize.nameservice.v1beta1.MsgSetAuthorityBond', + } +} diff --git a/src/naming.test.ts b/src/naming.test.ts index 793a0fe..c619ef2 100644 --- a/src/naming.test.ts +++ b/src/naming.test.ts @@ -1,8 +1,12 @@ import assert from 'assert'; +import path from 'path'; import { Account } from './account'; import { Registry } from './index'; -import { getConfig } from './testing/helper'; +import { ensureUpdatedConfig, getConfig } from './testing/helper'; + +const WATCHER_ID = 'bafyreibmr47ksukoadck2wigevb2jp5j5oubfadeyzb6zi57ydjsvjmmby' +const WATCHER_YML_PATH = path.join(__dirname, './testing/data/watcher.yml'); jest.setTimeout(120 * 1000); @@ -10,10 +14,11 @@ const { chainId, restEndpoint, gqlEndpoint, privateKey, accountAddress, fee } = const namingTests = () => { let registry: Registry; - let bondId: string; - + let watcher: any; + let watcherId: string; let authorityName: string; + let wrn: string; beforeAll(async () => { registry = new Registry(restEndpoint, gqlEndpoint, chainId); @@ -21,6 +26,23 @@ const namingTests = () => { // Create bond. bondId = await registry.getNextBondId(accountAddress); await registry.createBond({ denom: 'aphoton', amount: '1000000000' }, accountAddress, privateKey, fee); + + // Create bot. + watcher = await ensureUpdatedConfig(WATCHER_YML_PATH); + const result = await registry.setRecord( + { + privateKey, + bondId, + record: watcher.record + }, + accountAddress, + privateKey, + fee + ) + + // TODO: Get id from setRecord response. + // watcherId = result.data; + watcherId = WATCHER_ID; }); test('Reserve authority.', async () => { @@ -84,6 +106,16 @@ const namingTests = () => { expect(record.ownerPublicKey).toBeDefined(); expect(Number(record.height)).toBeGreaterThan(0); }); + + xtest('Set name for unbonded authority', async () => { + wrn = `wrn://${authorityName}/app/test`; + assert(watcherId) + await expect(registry.setName({ wrn, cid: watcherId }, accountAddress, privateKey, fee)).rejects.toThrow('Authority bond not found.'); + }); + + test('Set authority bond', async () => { + await registry.setAuthorityBond({ name: authorityName, bondId }, accountAddress, privateKey, fee); + }); }; if (process.env.AUCTIONS_ENABLED) { diff --git a/src/proto/vulcanize/nameservice/v1beta1/nameservice.ts b/src/proto/vulcanize/nameservice/v1beta1/nameservice.ts index 5fc55df..0ccda7a 100644 --- a/src/proto/vulcanize/nameservice/v1beta1/nameservice.ts +++ b/src/proto/vulcanize/nameservice/v1beta1/nameservice.ts @@ -311,11 +311,11 @@ export namespace vulcanize.nameservice.v1beta1 { constructor(data?: any[] | { id?: string; bond_id?: string; - create_time?: dependency_2.google.protobuf.Timestamp; - expiry_time?: dependency_2.google.protobuf.Timestamp; + create_time?: string; + expiry_time?: string; deleted?: boolean; owners?: string[]; - attributes?: Uint8Array; + attributes?: string; }) { super(); pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [6], []); @@ -356,16 +356,16 @@ export namespace vulcanize.nameservice.v1beta1 { pb_1.Message.setField(this, 2, value); } get create_time() { - return pb_1.Message.getWrapperField(this, dependency_2.google.protobuf.Timestamp, 3) as dependency_2.google.protobuf.Timestamp; + return pb_1.Message.getField(this, 3) as string; } - set create_time(value: dependency_2.google.protobuf.Timestamp) { - pb_1.Message.setWrapperField(this, 3, value); + set create_time(value: string) { + pb_1.Message.setField(this, 3, value); } get expiry_time() { - return pb_1.Message.getWrapperField(this, dependency_2.google.protobuf.Timestamp, 4) as dependency_2.google.protobuf.Timestamp; + return pb_1.Message.getField(this, 4) as string; } - set expiry_time(value: dependency_2.google.protobuf.Timestamp) { - pb_1.Message.setWrapperField(this, 4, value); + set expiry_time(value: string) { + pb_1.Message.setField(this, 4, value); } get deleted() { return pb_1.Message.getField(this, 5) as boolean; @@ -380,19 +380,19 @@ export namespace vulcanize.nameservice.v1beta1 { pb_1.Message.setField(this, 6, value); } get attributes() { - return pb_1.Message.getField(this, 7) as Uint8Array; + return pb_1.Message.getField(this, 7) as string; } - set attributes(value: Uint8Array) { + set attributes(value: string) { pb_1.Message.setField(this, 7, value); } static fromObject(data: { id?: string; bond_id?: string; - create_time?: ReturnType; - expiry_time?: ReturnType; + create_time?: string; + expiry_time?: string; deleted?: boolean; owners?: string[]; - attributes?: Uint8Array; + attributes?: string; }) { const message = new Record({}); if (data.id != null) { @@ -402,10 +402,10 @@ export namespace vulcanize.nameservice.v1beta1 { message.bond_id = data.bond_id; } if (data.create_time != null) { - message.create_time = dependency_2.google.protobuf.Timestamp.fromObject(data.create_time); + message.create_time = data.create_time; } if (data.expiry_time != null) { - message.expiry_time = dependency_2.google.protobuf.Timestamp.fromObject(data.expiry_time); + message.expiry_time = data.expiry_time; } if (data.deleted != null) { message.deleted = data.deleted; @@ -422,11 +422,11 @@ export namespace vulcanize.nameservice.v1beta1 { const data: { id?: string; bond_id?: string; - create_time?: ReturnType; - expiry_time?: ReturnType; + create_time?: string; + expiry_time?: string; deleted?: boolean; owners?: string[]; - attributes?: Uint8Array; + attributes?: string; } = {}; if (this.id != null) { data.id = this.id; @@ -435,10 +435,10 @@ export namespace vulcanize.nameservice.v1beta1 { data.bond_id = this.bond_id; } if (this.create_time != null) { - data.create_time = this.create_time.toObject(); + data.create_time = this.create_time; } if (this.expiry_time != null) { - data.expiry_time = this.expiry_time.toObject(); + data.expiry_time = this.expiry_time; } if (this.deleted != null) { data.deleted = this.deleted; @@ -459,16 +459,16 @@ export namespace vulcanize.nameservice.v1beta1 { writer.writeString(1, this.id); if (typeof this.bond_id === "string" && this.bond_id.length) writer.writeString(2, this.bond_id); - if (this.create_time !== undefined) - writer.writeMessage(3, this.create_time, () => this.create_time.serialize(writer)); - if (this.expiry_time !== undefined) - writer.writeMessage(4, this.expiry_time, () => this.expiry_time.serialize(writer)); + if (typeof this.create_time === "string" && this.create_time.length) + writer.writeString(3, this.create_time); + if (typeof this.expiry_time === "string" && this.expiry_time.length) + writer.writeString(4, this.expiry_time); if (this.deleted !== undefined) writer.writeBool(5, this.deleted); if (this.owners !== undefined) writer.writeRepeatedString(6, this.owners); - if (this.attributes !== undefined) - writer.writeBytes(7, this.attributes); + if (typeof this.attributes === "string" && this.attributes.length) + writer.writeString(7, this.attributes); if (!w) return writer.getResultBuffer(); } @@ -485,10 +485,10 @@ export namespace vulcanize.nameservice.v1beta1 { message.bond_id = reader.readString(); break; case 3: - reader.readMessage(message.create_time, () => message.create_time = dependency_2.google.protobuf.Timestamp.deserialize(reader)); + message.create_time = reader.readString(); break; case 4: - reader.readMessage(message.expiry_time, () => message.expiry_time = dependency_2.google.protobuf.Timestamp.deserialize(reader)); + message.expiry_time = reader.readString(); break; case 5: message.deleted = reader.readBool(); @@ -497,7 +497,7 @@ export namespace vulcanize.nameservice.v1beta1 { pb_1.Message.addToRepeatedField(message, 6, reader.readString()); break; case 7: - message.attributes = reader.readBytes(); + message.attributes = reader.readString(); break; default: reader.skipField(); } diff --git a/src/schema/record.json b/src/schema/record.json new file mode 100644 index 0000000..2761e2d --- /dev/null +++ b/src/schema/record.json @@ -0,0 +1,5 @@ +{ + "$schema": "http://json-schema.org/schema#", + "id": "/Record", + "type": "object" +} diff --git a/src/testing/data/watcher.yml b/src/testing/data/watcher.yml new file mode 100644 index 0000000..0d4f9cf --- /dev/null +++ b/src/testing/data/watcher.yml @@ -0,0 +1,6 @@ +record: + type: watcher + name: ERC20 Watcher + version: 1.0.0 + protocol: + /: QmdeazkS38aCrqG6qKwaio2fQnShE6RGpmNdqStLkkZcQN diff --git a/src/testing/helper.ts b/src/testing/helper.ts index 63af956..92d60ef 100644 --- a/src/testing/helper.ts +++ b/src/testing/helper.ts @@ -1,4 +1,14 @@ import assert from 'assert'; +import yaml from 'node-yaml'; +import semver from 'semver'; + +export const ensureUpdatedConfig = async (path: string) => { + const conf = await yaml.read(path); + conf.record.version = semver.inc(conf.record.version, 'patch'); + await yaml.write(path, conf); + + return conf; +}; export const getConfig = () => { assert(process.env.PRIVATE_KEY); diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..8de16b4 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,132 @@ +import assert from 'assert'; +import { Validator } from 'jsonschema'; + +import RecordSchema from './schema/record.json'; +import { Util } from './util'; + +/** + * Record. + */ +export class Record { + _record: any + + /** + * New Record. + */ + constructor(record: any) { + assert(record); + + const validator = new Validator(); + const result = validator.validate(record, RecordSchema); + if (!result.valid) { + result.errors.map(console.error); + throw new Error('Invalid record input.'); + } + + this._record = record; + } + + get attributes() { + return Buffer.from(JSON.stringify(this._record), 'binary').toString('base64') + } + + /** + * Serialize record. + */ + serialize() { + // return Util.sortJSON({ + // }); + return { + 'id': '_', + 'bond_id': '_', + 'create_time': '_', + 'expiry_time': '_', + 'deleted': true, + 'attributes': this.attributes, + // 'owners': [], + } + } + + /** + * Get message to calculate record signature. + */ + getMessageToSign() { + return Util.sortJSON(this._record); + } +} + +/** + * Record Signature. + */ +export class Signature { + _pubKey: string + _sig: string + + /** + * New Signature. + */ + constructor(pubKey: string, sig: string) { + assert(pubKey); + assert(sig); + + this._pubKey = pubKey; + this._sig = sig; + } + + /** + * Serialize Signature. + */ + serialize() { + return Util.sortJSON({ + 'pub_key': this._pubKey, + 'sig': this._sig + }); + } +} + +/** + * Message Payload. + */ +export class Payload { + _record: Record + _signatures: Signature[] + + /** + * New Payload. + */ + constructor(record: Record, ...signatures: Signature[]) { + assert(record); + + this._record = record; + this._signatures = signatures; + } + + get record() { + return this._record; + } + + get signatures() { + return this._signatures; + } + + /** + * Add message signature to payload. + */ + addSignature(signature: any) { + assert(signature); + + this._signatures.push(signature); + } + + /** + * Serialize Payload. + */ + serialize() { + // return Util.sortJSON({ + // }); + return { + 'record': this._record.serialize(), + 'signatures': this._signatures.map(s => s.serialize()) + } + } +} diff --git a/src/util.ts b/src/util.ts index 5e11950..d87982f 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,3 +1,5 @@ +import dagCBOR from 'ipld-dag-cbor'; + /** * Utils */ @@ -76,4 +78,15 @@ export class Util { return res; } + + /** + * Get record content ID. + */ + static async getContentId(record: any) { + console.log(record) + const content = dagCBOR.util.serialize(record); + const cid = await dagCBOR.util.cid(content); + + return cid.toString(); + } } diff --git a/tsconfig.json b/tsconfig.json index 9c5585c..39fff8b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -33,7 +33,7 @@ // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */ // "types": [], /* Specify type package names to be included without being referenced in a source file. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "resolveJsonModule": true, /* Enable importing .json files */ + "resolveJsonModule": true, /* Enable importing .json files */ // "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */ /* JavaScript Support */ diff --git a/yarn.lock b/yarn.lock index 54fc5c9..ab56447 100644 --- a/yarn.lock +++ b/yarn.lock @@ -551,11 +551,21 @@ tweetnacl "^1.0.3" tweetnacl-util "^0.15.1" +"@multiformats/base-x@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@multiformats/base-x/-/base-x-4.0.1.tgz#95ff0fa58711789d53aefb2590a8b7a4e715d121" + integrity sha512-eMk0b9ReBbV23xXU693TAIrLyeO5iTgBZGSJfpqriG8UkYvr/hC9u9pyMlAakDNHWmbhMZCDs6KQO0jzKD8OTw== + "@noble/hashes@^1": version "1.0.0" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.0.0.tgz#d5e38bfbdaba174805a4e649f13be9a9ed3351ae" integrity sha512-DZVbtY62kc3kkBtMHqwCOfXrT/hnoORy5BJ4+HU1IR59X0KWAOqsfzQPcUl/lQLlG7qXbe/fZ3r/emxtAl+sqg== +"@octetstream/promisify@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@octetstream/promisify/-/promisify-2.0.2.tgz#29ac3bd7aefba646db670227f895d812c1a19615" + integrity sha512-7XHoRB61hxsz8lBQrjC1tq/3OEIgpvGWg6DKAdwi7WRzruwkmsdwmOoUXbU4Dtd4RSOMDwed0SkP3y8UlMt1Bg== + "@sinonjs/commons@^1.7.0": version "1.8.3" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" @@ -570,6 +580,11 @@ dependencies: "@sinonjs/commons" "^1.7.0" +"@sovpro/delimited-stream@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@sovpro/delimited-stream/-/delimited-stream-1.1.0.tgz#4334bba7ee241036e580fdd99c019377630d26b4" + integrity sha512-kQpk267uxB19X3X2T1mvNMjyvIEonpNSHrMlK5ZaBU6aZxw7wPbpgKJOjHN3+/GPVpXgAV9soVT2oyHpLkLtyw== + "@tharsis/address-converter@^0.1.7": version "0.1.7" resolved "https://registry.yarnpkg.com/@tharsis/address-converter/-/address-converter-0.1.7.tgz#c13c4d09f30a5b908e795626a5c4c92f8120d954" @@ -743,6 +758,11 @@ dependencies: "@types/node" "*" +"@types/semver@^7.3.9": + version "7.3.9" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.9.tgz#152c6c20a7688c30b967ec1841d31ace569863fc" + integrity sha512-L/TMpyURfBkf+o/526Zb6kd/tchUP3iBDEPjqjb+U2MAJhVRxxrmr2fwpe08E7QsV7YLcpq0tUaQ9O9x97ZIxQ== + "@types/stack-utils@^2.0.0": version "2.0.1" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" @@ -841,6 +861,11 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -946,6 +971,11 @@ big-integer@1.6.36: resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.36.tgz#78631076265d4ae3555c04f85e7d9d2f3a071a36" integrity sha512-t70bfa7HYEA1D9idDbmuv7YbsbVkQ+Hp+8KFSul4aE5e/i1bjCNIRYJZlA8Q8p0r9T8cF/RVvwUgRA//FydEyg== +bignumber.js@^9.0.0: + version "9.0.2" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.2.tgz#71c6c6bed38de64e24a65ebe16cfcf23ae693673" + integrity sha512-GAcQvbpsM0pUb0zw1EI0KhQEZ+lRwR5fYaAp3vPOYuP7aDvGy6cVN6XHLauvF8SOga2y0dcLcjt3iQDTSEliyw== + bip32@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/bip32/-/bip32-3.0.1.tgz#1d1121469cce6e910e0ec3a5a1990dd62687e2a3" @@ -983,6 +1013,19 @@ bn.js@^5.2.0: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== +borc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/borc/-/borc-3.0.0.tgz#49ada1be84de86f57bb1bb89789f34c186dfa4fe" + integrity sha512-ec4JmVC46kE0+layfnwM3l15O70MlFiEbmQHY/vpqIKiUtPVntv4BY4NVnz3N4vb21edV3mY97XVckFvYHWF9g== + dependencies: + bignumber.js "^9.0.0" + buffer "^6.0.3" + commander "^2.15.0" + ieee754 "^1.1.13" + iso-url "^1.1.5" + json-text-sequence "~0.3.0" + readable-stream "^3.6.0" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -1071,7 +1114,7 @@ buffer-xor@^1.0.3: resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= -buffer@6.0.3: +buffer@6.0.3, buffer@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== @@ -1099,6 +1142,11 @@ caniuse-lite@^1.0.30001317: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001319.tgz#eb4da4eb3ecdd409f7ba1907820061d56096e88f" integrity sha512-xjlIAFHucBRSMUo1kb5D4LYgcN1M45qdKP++lhqowDpwJwGkpIRTt5qQqnhxjj1vHcI7nrJxWhCC1ATrCEBTcw== +canonical-json@^0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/canonical-json/-/canonical-json-0.0.4.tgz#6579c072c3db5c477ec41dc978fbf2b8f41074a3" + integrity sha1-ZXnAcsPbXEd+xB3JePvyuPQQdKM= + chalk@^2.0.0, chalk@^2.4.1: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -1126,6 +1174,16 @@ ci-info@^3.2.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.0.tgz#b4ed1fb6818dea4803a55c623041f9165d2066b2" integrity sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw== +cids@^1.0.0, cids@^1.1.6: + version "1.1.9" + resolved "https://registry.yarnpkg.com/cids/-/cids-1.1.9.tgz#402c26db5c07059377bcd6fb82f2a24e7f2f4a4f" + integrity sha512-l11hWRfugIcbGuTZwAM5PwpjPPjyb6UZOGwlHSnOBV5o07XhQ4gNpBN67FbODvpjyHtd+0Xs6KNvUcGBiDRsdg== + dependencies: + multibase "^4.0.1" + multicodec "^3.0.1" + multihashes "^4.0.1" + uint8arrays "^3.0.0" + cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" @@ -1148,7 +1206,7 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" -co@^4.6.0: +co@4.6.0, co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= @@ -1189,6 +1247,11 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" +commander@^2.15.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -1354,6 +1417,11 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +err-code@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/err-code/-/err-code-3.0.1.tgz#a444c7b992705f2b120ee320b09972eef331c920" + integrity sha512-GiaH0KJUewYok+eeY05IIgjtAe4Yltygk9Wqp1V5yVWLdhf0hYZchRjNIT9bb0mSwRcIusT3cx7PJUf3zEIfUA== + error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" @@ -1702,7 +1770,7 @@ iconv-lite@0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -ieee754@^1.2.1: +ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -1733,16 +1801,43 @@ inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +interface-ipld-format@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/interface-ipld-format/-/interface-ipld-format-1.0.1.tgz#bee39c70c584a033e186ff057a2be89f215963e3" + integrity sha512-WV/ar+KQJVoQpqRDYdo7YPGYIUHJxCuOEhdvsRpzLqoOIVCqPKdMMYmsLL1nCRsF3yYNio+PAJbCKiv6drrEAg== + dependencies: + cids "^1.1.6" + multicodec "^3.0.1" + multihashes "^4.0.2" + interpret@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== +ipld-dag-cbor@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ipld-dag-cbor/-/ipld-dag-cbor-1.0.1.tgz#1e07cb289aec26e393508e99d2a51ff624d876a1" + integrity sha512-PZh8rHnRETX5bj60i73W2oq6BXoZnIvYCBDwIffYVJgxMr7BEVd5PycAARBiT6daORJ/4zbqEFR5CcrjeCtm/A== + dependencies: + borc "^3.0.0" + cids "^1.0.0" + interface-ipld-format "^1.0.0" + is-circular "^1.0.2" + multicodec "^3.0.1" + multihashing-async "^2.0.0" + uint8arrays "^2.1.3" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= +is-circular@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-circular/-/is-circular-1.0.2.tgz#2e0ab4e9835f4c6b0ea2b9855a84acd501b8366c" + integrity sha512-YttjnrswnUYRVJvxCvu8z+PGMUSzC2JttP0OEXezlAEdp3EXzhf7IZ3j0gRAybJBQupedIZFhY61Tga6E0qASA== + is-core-module@^2.8.1: version "2.8.1" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" @@ -1795,6 +1890,11 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= +iso-url@^1.1.5: + version "1.2.1" + resolved "https://registry.yarnpkg.com/iso-url/-/iso-url-1.2.1.tgz#db96a49d8d9a64a1c889fc07cc525d093afb1811" + integrity sha512-9JPDgCN4B7QPkLtYAAOrEuAWvP9rWvR5offAr0/SeF046wIkglqH3VXgYYP6NcsKslH80UIVgmPqNe3j7tG2ng== + istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" @@ -2247,6 +2347,11 @@ js-sha256@^0.9.0: resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966" integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA== +js-sha3@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -2260,6 +2365,13 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + jsdom@^16.6.0: version "16.7.0" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710" @@ -2303,11 +2415,28 @@ json-parse-even-better-errors@^2.3.0: resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== +json-text-sequence@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/json-text-sequence/-/json-text-sequence-0.3.0.tgz#6603e0ee45da41f949669fd18744b97fb209e6ce" + integrity sha512-7khKIYPKwXQem4lWXfpIN/FEnhztCeRPSxH4qm3fVlqulwujrRDD54xAwDDn/qVKpFtV550+QAkcWJcufzqQuA== + dependencies: + "@sovpro/delimited-stream" "^1.1.0" + json5@2.x, json5@^2.1.2: version "2.2.1" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== +jsonschema@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.4.0.tgz#1afa34c4bc22190d8e42271ec17ac8b3404f87b2" + integrity sha512-/YgW6pRMr6M7C+4o8kS+B/2myEpHCrxO4PEWnqJNBFMjn7EWXqlQ4tGwL6xTHeRplwuZmcAncdvfOad1nT2yMw== + +junk@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/junk/-/junk-3.1.0.tgz#31499098d902b7e98c5d9b9c80f43457a88abfa1" + integrity sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ== + keccak@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.2.tgz#4c2c6e8c54e04f2670ee49fa734eb9da152206e0" @@ -2468,6 +2597,52 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +multibase@^4.0.1: + version "4.0.6" + resolved "https://registry.yarnpkg.com/multibase/-/multibase-4.0.6.tgz#6e624341483d6123ca1ede956208cb821b440559" + integrity sha512-x23pDe5+svdLz/k5JPGCVdfn7Q5mZVMBETiC+ORfO+sor9Sgs0smJzAjfTbM5tckeCqnaUuMYoz+k3RXMmJClQ== + dependencies: + "@multiformats/base-x" "^4.0.1" + +multicodec@^3.0.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/multicodec/-/multicodec-3.2.1.tgz#82de3254a0fb163a107c1aab324f2a91ef51efb2" + integrity sha512-+expTPftro8VAW8kfvcuNNNBgb9gPeNYV9dn+z1kJRWF2vih+/S79f2RVeIwmrJBUJ6NT9IUPWnZDQvegEh5pw== + dependencies: + uint8arrays "^3.0.0" + varint "^6.0.0" + +multiformats@^9.4.2: + version "9.6.4" + resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.6.4.tgz#5dce1f11a407dbb69aa612cb7e5076069bb759ca" + integrity sha512-fCCB6XMrr6CqJiHNjfFNGT0v//dxOBMrOMqUIzpPc/mmITweLEyhvMpY9bF+jZ9z3vaMAau5E8B68DW77QMXkg== + +multihashes@^4.0.1, multihashes@^4.0.2: + version "4.0.3" + resolved "https://registry.yarnpkg.com/multihashes/-/multihashes-4.0.3.tgz#426610539cd2551edbf533adeac4c06b3b90fb05" + integrity sha512-0AhMH7Iu95XjDLxIeuCOOE4t9+vQZsACyKZ9Fxw2pcsRmlX4iCn1mby0hS0bb+nQOVpdQYWPpnyusw4da5RPhA== + dependencies: + multibase "^4.0.1" + uint8arrays "^3.0.0" + varint "^5.0.2" + +multihashing-async@^2.0.0: + version "2.1.4" + resolved "https://registry.yarnpkg.com/multihashing-async/-/multihashing-async-2.1.4.tgz#26dce2ec7a40f0e7f9e732fc23ca5f564d693843" + integrity sha512-sB1MiQXPSBTNRVSJc2zM157PXgDtud2nMFUEIvBrsq5Wv96sUclMRK/ecjoP1T/W61UJBqt4tCTwMkUpt2Gbzg== + dependencies: + blakejs "^1.1.0" + err-code "^3.0.0" + js-sha3 "^0.8.0" + multihashes "^4.0.1" + murmurhash3js-revisited "^3.0.0" + uint8arrays "^3.0.0" + +murmurhash3js-revisited@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/murmurhash3js-revisited/-/murmurhash3js-revisited-3.0.0.tgz#6bd36e25de8f73394222adc6e41fa3fac08a5869" + integrity sha512-/sF3ee6zvScXMb1XFJ8gDsSnY+X8PbOyjIuBhtgis10W2Jx4ZjIhikUCIF9c4gpJxVnQIsPAFrSwTCuAjicP6g== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -2493,6 +2668,15 @@ node-releases@^2.0.2: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.2.tgz#7139fe71e2f4f11b47d4d2986aaf8c48699e0c01" integrity sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg== +node-yaml@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/node-yaml/-/node-yaml-4.0.1.tgz#3675d27c275fbea9c02e2b0faa18bb1699444cb3" + integrity sha512-ZPKi3OexXdiklsRW9g4P7jAxHAhoBRZCFmIDMQ89clLhMz+MTJuCM9y5f1R7Ru75H84hWJouwpxVOq1SdRTe7A== + dependencies: + co "4.6.0" + junk "3.1.0" + promise-fs "2.1.1" + normalize-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" @@ -2637,6 +2821,13 @@ pretty-format@^27.0.0, pretty-format@^27.5.1: ansi-styles "^5.0.0" react-is "^17.0.1" +promise-fs@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/promise-fs/-/promise-fs-2.1.1.tgz#0b725a592c165ff16157d1f13640ba390637e557" + integrity sha512-43p7e4QzAQ3w6eyN0+gbBL7jXiZFWLWYITg9wIObqkBySu/a5K1EDcQ/S6UyB/bmiZWDA4NjTbcopKLTaKcGSw== + dependencies: + "@octetstream/promisify" "2.0.2" + prompts@^2.0.1: version "2.4.2" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" @@ -2787,7 +2978,7 @@ secp256k1@^4.0.1: node-addon-api "^2.0.0" node-gyp-build "^4.2.0" -semver@7.x, semver@^7.3.2: +semver@7.x, semver@^7.3.2, semver@^7.3.5: version "7.3.5" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== @@ -3115,6 +3306,20 @@ uint8array-tools@0.0.7: resolved "https://registry.yarnpkg.com/uint8array-tools/-/uint8array-tools-0.0.7.tgz#a7a2bb5d8836eae2fade68c771454e6a438b390d" integrity sha512-vrrNZJiusLWoFWBqz5Y5KMCgP9W9hnjZHzZiZRT8oNAkq3d5Z5Oe76jAvVVSRh4U8GGR90N2X1dWtrhvx6L8UQ== +uint8arrays@^2.1.3: + version "2.1.10" + resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-2.1.10.tgz#34d023c843a327c676e48576295ca373c56e286a" + integrity sha512-Q9/hhJa2836nQfEJSZTmr+pg9+cDJS9XEAp7N2Vg5MzL3bK/mkMVfjscRGYruP9jNda6MAdf4QD/y78gSzkp6A== + dependencies: + multiformats "^9.4.2" + +uint8arrays@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-3.0.0.tgz#260869efb8422418b6f04e3fac73a3908175c63b" + integrity sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA== + dependencies: + multiformats "^9.4.2" + universalify@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" @@ -3134,6 +3339,16 @@ v8-to-istanbul@^8.1.0: convert-source-map "^1.6.0" source-map "^0.7.3" +varint@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/varint/-/varint-5.0.2.tgz#5b47f8a947eb668b848e034dcfa87d0ff8a7f7a4" + integrity sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow== + +varint@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/varint/-/varint-6.0.0.tgz#9881eb0ce8feaea6512439d19ddf84bf551661d0" + integrity sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg== + w3c-hr-time@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd"