forked from cerc-io/registry-sdk
Implement setRecord and add test for setAutorityBond
This commit is contained in:
+60
-2
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
declare module 'graphql.js'
|
||||
declare module 'node-yaml'
|
||||
declare module 'canonical-json'
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
declare module 'graphql.js'
|
||||
+139
-3
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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',
|
||||
}
|
||||
}
|
||||
|
||||
+35
-3
@@ -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) {
|
||||
|
||||
@@ -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<typeof dependency_2.google.protobuf.Timestamp.prototype.toObject>;
|
||||
expiry_time?: ReturnType<typeof dependency_2.google.protobuf.Timestamp.prototype.toObject>;
|
||||
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<typeof dependency_2.google.protobuf.Timestamp.prototype.toObject>;
|
||||
expiry_time?: ReturnType<typeof dependency_2.google.protobuf.Timestamp.prototype.toObject>;
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/schema#",
|
||||
"id": "/Record",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
record:
|
||||
type: watcher
|
||||
name: ERC20 Watcher
|
||||
version: 1.0.0
|
||||
protocol:
|
||||
/: QmdeazkS38aCrqG6qKwaio2fQnShE6RGpmNdqStLkkZcQN
|
||||
@@ -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);
|
||||
|
||||
+132
@@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user