Skip to content

Commit

Permalink
chore: improve verify command
Browse files Browse the repository at this point in the history
  • Loading branch information
Trinketer22 authored Feb 15, 2024
1 parent 1b2a134 commit 6ad3dcd
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 6 deletions.
97 changes: 91 additions & 6 deletions src/cli/verify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Args, Runner, RunnerContext } from './Runner';
import path from 'path';
import { argSpec, createNetworkProvider } from '../network/createNetworkProvider';
import { selectCompile } from './build';
import { sleep } from '../utils';
import arg from 'arg';

type FuncCompilerSettings = {
Expand Down Expand Up @@ -97,6 +98,66 @@ class VerifierRegistry implements Contract {
}
}

async function lookupCodeHash(hash: Buffer, ui: UIProvider, retryCount: number = 5): Promise<string | undefined> {
type QueryResponse = {
data: {
account_states: Array<{
address: string;
workchain: number;
}>;
};
};

let queryResponse: QueryResponse;
let foundAddr: string | undefined;
let done = false;
const graphqlUrl = 'https://dton.io/graphql/';
const query = `{
account_states(page:0, page_size:1, account_state_state_init_code_hash: "${hash.toString('hex').toUpperCase()}")
{
address
workchain
}
}`;

do {
try {
ui.write('Checking if such a contract is already deployed...');
const resp = await fetch(graphqlUrl, {
method: 'POST',
body: JSON.stringify({ query }),
headers: { 'Content-Type': 'application/json' },
});
if (resp.ok) {
queryResponse = await resp.json();
const states = queryResponse.data.account_states;
if (states.length > 0) {
const state = states[0];
foundAddr = Address.parseRaw(`${state.workchain}:${state.address}`).toString();
} else {
ui.write('No such contract was found!');
}
done = true;
} else {
retryCount--;
}
// Meh
} catch (e: any) {
retryCount--;
if (e.cause) {
if (e.cause.code == 'ETIMEDOUT') {
ui.write('API timed out, waiting...');
await sleep(5000);
}
} else {
ui.write(e);
}
}
} while (!done && retryCount > 0);

return foundAddr;
}

export const verify: Runner = async (args: Args, ui: UIProvider, context: RunnerContext) => {
const localArgs = arg(argSpec);

Expand All @@ -116,9 +177,28 @@ export const verify: Runner = async (args: Args, ui: UIProvider, context: Runner
throw new Error('Cannot use custom network');
}

const addr = await ui.input('Deployed contract address');

const result = await doCompile(sel.name);
const resHash = result.code.hash();

ui.write(`Compiled code hash hex:${resHash.toString('hex')}`);
ui.write('We can look up the address with such code hash in the blockchain automatically');

const passManually = await ui.prompt('Do you want to specify the address manually?');
let addr: string;

if (passManually) {
addr = (await ui.inputAddress('Deployed contract address')).toString();
} else {
const alreadyDeployed = await lookupCodeHash(resHash, ui);
if (alreadyDeployed) {
ui.write(`Contract is already deployed at: ${alreadyDeployed}\nUsing that address.`);
ui.write(`https://tonscan.org/address/${alreadyDeployed}`);
addr = alreadyDeployed;
} else {
ui.write("Please enter the contract's address manually");
addr = (await ui.inputAddress('Deployed contract address')).toString();
}
}

let src: SourcesObject;
const fd = new FormData();
Expand Down Expand Up @@ -211,28 +291,31 @@ export const verify: Runner = async (args: Args, ui: UIProvider, context: Runner
});

if (sourceResponse.status !== 200) {
throw new Error('Could not compile on backend:\n' + (await sourceResponse.text()));
throw new Error('Could not compile on backend:\n' + (await sourceResponse.json()));
}

const sourceResult = await sourceResponse.json();

if (sourceResult.compileResult.result !== 'similar') {
throw new Error('The code is not similar');
throw new Error(sourceResult.compileResult.error);
}

let msgCell = sourceResult.msgCell;
let acquiredSigs = 1;

while (acquiredSigs < verifier.quorum) {
const signResponse = await fetch(removeRandom(remainingBackends) + '/sign', {
const curBackend = removeRandom(remainingBackends);
ui.write(`Using backend: ${curBackend}`);
const signResponse = await fetch(curBackend + '/sign', {
method: 'POST',
body: JSON.stringify({
messageCell: msgCell,
}),
headers: { 'Content-Type': 'application/json' },
});

if (signResponse.status !== 200) {
throw new Error('Could not compile on backend:\n' + (await signResponse.text()));
throw new Error('Could not sign on backend:\n' + (await signResponse.text()));
}

const signResult = await signResponse.json();
Expand All @@ -248,4 +331,6 @@ export const verify: Runner = async (args: Args, ui: UIProvider, context: Runner
value: toNano('0.5'),
body: c,
});

ui.write(`Contract successfully verified at https://verifier.ton.org/${addr}`);
};
13 changes: 13 additions & 0 deletions src/ui/InquirerUIProvider.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import inquirer from 'inquirer';
import { UIProvider } from '../ui/UIProvider';
import { Address } from '@ton/core';

class DestroyableBottomBar extends inquirer.ui.BottomBar {
destroy() {
Expand Down Expand Up @@ -27,6 +28,18 @@ export class InquirerUIProvider implements UIProvider {
return prompt;
}

async inputAddress(message: string, fallback?: Address) {
const prompt = message + (fallback === undefined ? '' : ` (default: ${fallback})`);
while (true) {
const addr = (await this.input(prompt)).trim();
try {
return addr === '' && fallback !== undefined ? fallback : Address.parse(addr);
} catch (e) {
this.write(addr + ' is not valid!\n');
}
}
}

async input(message: string): Promise<string> {
const { val } = await inquirer.prompt({
name: 'val',
Expand Down
3 changes: 3 additions & 0 deletions src/ui/UIProvider.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { Address } from '@ton/core';

export interface UIProvider {
write(message: string): void;
prompt(message: string): Promise<boolean>;
inputAddress(message: string, fallback?: Address): Promise<Address>;
input(message: string): Promise<string>;
choose<T>(message: string, choices: T[], display: (v: T) => string): Promise<T>;
setActionPrompt(message: string): void;
Expand Down

0 comments on commit 6ad3dcd

Please sign in to comment.