120 lines
3.3 KiB
JavaScript
120 lines
3.3 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
import assert from 'node:assert/strict';
|
|
import { readFile } from 'node:fs/promises';
|
|
import { dirname, resolve } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import {
|
|
ChangeOwnerPermission,
|
|
Contract,
|
|
Network,
|
|
PrivateKey,
|
|
RoleSimple,
|
|
SplitJoinPermission,
|
|
Topology,
|
|
TransactionPack,
|
|
unicryptoReady
|
|
} from 'universa-core2';
|
|
|
|
const sourceDirectory = dirname(fileURLToPath(import.meta.url));
|
|
const topologyFile = resolve(sourceDirectory, 'universa.json');
|
|
const clientKeyFile = resolve(sourceDirectory, 'white.private.unikey');
|
|
|
|
const sleep = (milliseconds) => new Promise((resolvePromise) => {
|
|
setTimeout(resolvePromise, milliseconds);
|
|
});
|
|
|
|
async function waitForFinalState(network, connection, contractId) {
|
|
const deadline = Date.now() + 60_000;
|
|
|
|
while (Date.now() < deadline) {
|
|
const { itemResult } = await network.getState(
|
|
contractId.composite3,
|
|
connection,
|
|
{ timeout: 5_000 }
|
|
);
|
|
const state = itemResult?.state;
|
|
|
|
if (state && state !== 'UNDEFINED' && !state.startsWith('PENDING') && !state.startsWith('LOCKED')) {
|
|
return itemResult;
|
|
}
|
|
|
|
await sleep(250);
|
|
}
|
|
|
|
throw new Error('contract registration timed out');
|
|
}
|
|
|
|
await unicryptoReady;
|
|
|
|
const topologyData = JSON.parse(await readFile(topologyFile, 'utf8'));
|
|
const topology = await Topology.load(topologyData);
|
|
const clientKey = await PrivateKey.unpack(await readFile(clientKeyFile));
|
|
const network = new Network(clientKey, {
|
|
topology,
|
|
directConnection: true
|
|
});
|
|
|
|
console.log(`Connecting to ${topology.size()} Universa nodes from ${topologyFile}`);
|
|
await network.connect();
|
|
|
|
const response = await network.command('sping');
|
|
assert.equal(response?.sping, 'spong', 'unexpected network state response');
|
|
|
|
console.log(`Universa network is functioning: ${network.size()} nodes, sping=spong`);
|
|
|
|
// This key owns and signs the sample token. The white key above is used only
|
|
// to authenticate the client and authorize fee-free registration.
|
|
const tokenKey = await PrivateKey.generate({ strength: 2048 });
|
|
const issuer = new RoleSimple('issuer', {
|
|
addresses: [tokenKey.publicKey.shortAddress]
|
|
});
|
|
const splitJoinPermission = SplitJoinPermission.create('owner', {
|
|
field_name: 'amount',
|
|
min_value: '0',
|
|
min_unit: '0.01',
|
|
join_match_fields: ['state.origin']
|
|
});
|
|
|
|
const token = Contract.create(issuer, {
|
|
definitionData: {
|
|
template_name: 'UNIT_CONTRACT',
|
|
unit_name: 'Sample Token',
|
|
unit_short_name: 'SAMPLE',
|
|
description: 'A token created by the universa-core2 sample'
|
|
},
|
|
stateData: {
|
|
amount: '100'
|
|
},
|
|
permissions: [
|
|
ChangeOwnerPermission.create('owner'),
|
|
splitJoinPermission
|
|
],
|
|
expiresAt: '3m',
|
|
createdAt: new Date()
|
|
});
|
|
|
|
await token.sign(tokenKey);
|
|
const transactionPack = new TransactionPack(token.pack());
|
|
const packedTransaction = await transactionPack.pack();
|
|
const contractId = await token.hashId();
|
|
|
|
console.log(`Created and packed SAMPLE token (${packedTransaction.length} bytes)`);
|
|
|
|
const connection = await network.getRandomConnection();
|
|
await network.command(
|
|
'approve',
|
|
{ packedItem: packedTransaction },
|
|
connection,
|
|
{ timeout: 10_000 }
|
|
);
|
|
|
|
const itemResult = await waitForFinalState(network, connection, contractId);
|
|
assert.equal(
|
|
itemResult.state,
|
|
'APPROVED',
|
|
`token registration failed: ${JSON.stringify(itemResult.errors ?? [])}`
|
|
);
|
|
|
|
console.log(`SAMPLE token is ${itemResult.state}`);
|