Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add devnet and stress test #5

Merged
merged 14 commits into from
Oct 19, 2024
12 changes: 1 addition & 11 deletions contracts/config.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,4 @@
{
"version": 1,
"deployAliases": {
"devnet": {
"networkId": "testnet",
"url": "https://api.minascan.io/node/devnet/v1/graphql",
"keyPath": "keys/devnet.json",
"feepayerKeyPath": "/Users/boraysaygilier/.cache/zkapp-cli/keys/berkkey.json",
"feepayerAlias": "berkkey",
"fee": "0.1",
"smartContract": "NameService"
}
}
"deployAliases": {}
}
13 changes: 5 additions & 8 deletions contracts/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"devDependencies": {
"@babel/preset-env": "^7.16.4",
"@babel/preset-typescript": "^7.16.0",
"@types/node": "^22.5.5",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file does not reference o1js directly! We are getting o1js via o1js-pack....

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After fixing this locally, my script continues running.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed it while debugging the wasm TypeError. After adding it back, it works fine on my end as well. I’ll add it in the next commit.

"@typescript-eslint/eslint-plugin": "^5.5.0",
"@typescript-eslint/parser": "^5.5.0",
"eslint": "^8.7.0",
Expand Down
12 changes: 6 additions & 6 deletions contracts/src/NameService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class AdminChangedEvent extends Struct({

const offchainState = OffchainState(
{
registry: OffchainState.Map(Field, NameRecord),
registry: OffchainState.Map(Name, NameRecord),
premium: OffchainState.Field(UInt64),
},
{ logTotalCapacity: 10, maxActionsPerProof: 5 }
Expand Down Expand Up @@ -110,7 +110,7 @@ class NameService extends SmartContract {
* @param record
*
*/
@method async register_name(name: Field, record: NameRecord) {
@method async register_name(name: Name, record: NameRecord) {
(await offchainState.fields.registry.get(name)).isSome.assertFalse(); // do we need this?
let premium = await this.premium_rate();
const sender = this.sender.getAndRequireSignature();
Expand All @@ -132,7 +132,7 @@ class NameService extends SmartContract {
* @param new_record
*
*/
@method async set_record(name: Field, new_record: NameRecord) {
@method async set_record(name: Name, new_record: NameRecord) {
let current_record = (
await offchainState.fields.registry.get(name)
).assertSome('this name is not owned');
Expand All @@ -154,7 +154,7 @@ class NameService extends SmartContract {
* @param new_owner
*
*/
@method async transfer_name_ownership(name: Field, new_owner: PublicKey) {
@method async transfer_name_ownership(name: Name, new_owner: PublicKey) {
let current_record = (
await offchainState.fields.registry.get(name)
).assertSome('this name is not owned');
Expand All @@ -175,7 +175,7 @@ class NameService extends SmartContract {
* @param name
* @returns owner of given name
*/
@method.returns(PublicKey) async owner_of(name: Field) {
@method.returns(PublicKey) async owner_of(name: Name) {
return (await offchainState.fields.registry.get(name)).assertSome(
'this name is not owned'
).mina_address;
Expand All @@ -185,7 +185,7 @@ class NameService extends SmartContract {
* @param name
* @returns full record associated with given name
*/
@method.returns(NameRecord) async resolve_name(name: Field) {
@method.returns(NameRecord) async resolve_name(name: Name) {
return (await offchainState.fields.registry.get(name)).assertSome(
'this name is not owned'
);
Expand Down
176 changes: 176 additions & 0 deletions contracts/src/integration-test.ts
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@45930 what do you think about moving all test files to test folder and classification of interaction scripts and tests.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is already src/test/, which you can use for this.

I prefer test/ at the top level, but for simplicity in the build, I moved it into src/. I don't mind if you move it, as long as the tests keep passing!

Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import fs from 'fs/promises';
import {
AccountUpdate,
Field,
Mina,
PrivateKey,
UInt64,
NetworkId,
} from 'o1js';
import { NameService, NameRecord, offchainState, Name } from './NameService.js';

// check command line arg
let deployAlias = process.argv[2];
if (!deployAlias)
throw Error(`Missing <deployAlias> argument.

Usage:
node build/src/interact.js <deployAlias>
`);
Error.stackTraceLimit = 1000;
const DEFAULT_NETWORK_ID = 'testnet';

// parse config and private key from file
type Config = {
deployAliases: Record<
string,
{
networkId?: string;
url: string;
keyPath: string;
fee: string;
feepayerKeyPath: string;
feepayerAlias: string;
}
>;
};
let configJson: Config = JSON.parse(await fs.readFile('config.json', 'utf8'));
let config = configJson.deployAliases[deployAlias];
let feepayerKeysBase58: { privateKey: string; publicKey: string } = JSON.parse(
await fs.readFile(config.feepayerKeyPath, 'utf8')
);

let zkAppKeysBase58: { privateKey: string; publicKey: string } = JSON.parse(
await fs.readFile(config.keyPath, 'utf8')
);

let cycleNumber = 5;
let names: string[] = [];
let nameMap = new Map();

let feepayerKey = PrivateKey.fromBase58(feepayerKeysBase58.privateKey);
let zkAppKey = PrivateKey.fromBase58(zkAppKeysBase58.privateKey);

const Network = Mina.Network({
archive: 'https://api.minascan.io/archive/devnet/v1/graphql',
networkId: (config.networkId ?? DEFAULT_NETWORK_ID) as NetworkId,
mina: config.url,
});

const fee = Number(config.fee) * 1e9; // in nanomina (1 billion = 1.0 mina)
Mina.setActiveInstance(Network);
let tx;
let feepayerAddress = feepayerKey.toPublicKey();
console.log(feepayerAddress.toBase58());
let zkAppAddress = zkAppKey.toPublicKey();
let name_service_contract = new NameService(zkAppAddress);

console.time('compile program');
await offchainState.compile();
offchainState.setContractInstance(name_service_contract);
console.timeEnd('compile program');
console.time('compile contract');
await NameService.compile();
console.timeEnd('compile contract');

console.time('deploy');
tx = await Mina.transaction({ sender: feepayerAddress, fee: fee }, async () => {
AccountUpdate.fundNewAccount(feepayerAddress);
await name_service_contract.deploy();
})
.prove()
.sign([feepayerKey, zkAppKey])
.send()
.wait();
console.timeEnd('deploy');

console.time('set premimum rate');
tx = await Mina.transaction({ sender: feepayerAddress, fee: fee }, async () => {
await name_service_contract.set_premium(UInt64.from(100));
})
.sign([feepayerKey])
.prove()
.send()
.wait();
console.timeEnd('set premimum rate');

console.time('settlement proof 1');
let proof = await offchainState.createSettlementProof();
console.timeEnd('settlement proof 1');

console.time('settle 1');
tx = await Mina.transaction({ sender: feepayerAddress, fee: fee }, async () =>
name_service_contract.settle(proof)
)
.sign([feepayerKey])
.prove()
.send()
.wait();
console.timeEnd('settle 1');

console.time('get premimum rate');
let res;
tx = await Mina.transaction({ sender: feepayerAddress, fee: fee }, async () => {
res = await name_service_contract.premium_rate();
})
.sign([feepayerKey])
.prove()
.send()
.wait();
console.log(res!.toString());
console.timeEnd('get premimum rate');

for (let i = 0; i < cycleNumber; i++) {
for (let j = 0; j < 3; j++) {
let name = Math.random().toString(36).substring(2, 12).concat('.mina');
let new_record = new NameRecord({
mina_address: PrivateKey.randomKeypair().publicKey,
avatar: Field.random(),
url: Field.random(),
});
names.push(name);
nameMap.set(name, new_record);

console.time('register a name');
tx = await Mina.transaction(
{ sender: feepayerAddress, fee: fee + 100 },
async () => {
await name_service_contract.register_name(
Name.fromString(name),
new_record
);
}
)
.sign([feepayerKey])
.prove()
.send()
.wait();
console.timeEnd('register a name');
}

wait(8); // wait for settlement

console.time('get a randomName');
let randomName = names[Math.floor(Math.random() * names.length)];
let record = nameMap.get(randomName);
tx = await Mina.transaction(
{ sender: feepayerAddress, fee: fee },
async () => {
let res = await name_service_contract.resolve_name(
Name.fromString(randomName)
);
res.mina_address.assertEquals(record.mina_address);
res.avatar.assertEquals(record.avatar);
res.url.assertEquals(record.url);
}
)
.sign([feepayerKey])
.prove()
.send()
.wait();
console.timeEnd('get a randomName');
}

function wait(m: number) {
return new Promise((resolve) => setTimeout(resolve, m * 1000));
}
Loading