feat: bundlr, sequencer
This commit is contained in:
committed by
Piotr Pędziwiatr
parent
fcfa93aae3
commit
a5740a0d1d
@@ -132,6 +132,23 @@ export interface Contract<State = unknown> {
|
|||||||
strict?: boolean
|
strict?: boolean
|
||||||
): Promise<string | null>;
|
): Promise<string | null>;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new "interaction" transaction using RedStone Sequencer - this, with combination with
|
||||||
|
* RedStone Gateway, gives instant transaction availability and finality guaranteed by Bundlr.
|
||||||
|
*
|
||||||
|
* @param input - new input to the contract that will be assigned with this interactions transaction
|
||||||
|
* @param tags - additional tags that can be attached to the newly created interaction transaction
|
||||||
|
* @param transfer - additional {@link ArTransfer} than can be attached to the interaction transaction
|
||||||
|
* @param strict - transaction will be posted on Arweave only if the dry-run of the input result is "ok"
|
||||||
|
*/
|
||||||
|
bundleInteraction<Input = unknown>(
|
||||||
|
input: Input,
|
||||||
|
tags?: Tags,
|
||||||
|
transfer?: ArTransfer,
|
||||||
|
strict?: boolean
|
||||||
|
): Promise<string | null>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the full call tree report the last
|
* Returns the full call tree report the last
|
||||||
* interaction with contract (eg. after reading state)
|
* interaction with contract (eg. after reading state)
|
||||||
|
|||||||
@@ -188,6 +188,60 @@ export class HandlerBasedContract<State> implements Contract<State> {
|
|||||||
}
|
}
|
||||||
const { arweave } = this.smartweave;
|
const { arweave } = this.smartweave;
|
||||||
|
|
||||||
|
const interactionTx = await this.createInteraction(input, tags, transfer, strict);
|
||||||
|
const response = await arweave.transactions.post(interactionTx);
|
||||||
|
|
||||||
|
if (response.status !== 200) {
|
||||||
|
this.logger.error('Error while posting transaction', response);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._evaluationOptions.waitForConfirmation) {
|
||||||
|
this.logger.info('Waiting for confirmation of', interactionTx.id);
|
||||||
|
const benchmark = Benchmark.measure();
|
||||||
|
await this.waitForConfirmation(interactionTx.id);
|
||||||
|
this.logger.info('Transaction confirmed after', benchmark.elapsed());
|
||||||
|
}
|
||||||
|
return interactionTx.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
async bundleInteraction<Input>(
|
||||||
|
input: Input,
|
||||||
|
tags: Tags = [],
|
||||||
|
transfer: ArTransfer = emptyTransfer,
|
||||||
|
strict = false
|
||||||
|
): Promise<any | null> {
|
||||||
|
this.logger.info('Bundle interaction input', input);
|
||||||
|
if (!this.wallet) {
|
||||||
|
throw new Error("Wallet not connected. Use 'connect' method first.");
|
||||||
|
}
|
||||||
|
const interactionTx = await this.createInteraction(input, tags, transfer, strict);
|
||||||
|
|
||||||
|
const response = await fetch(`${this._evaluationOptions.sequencerAddress}gateway/sequencer/register`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(interactionTx),
|
||||||
|
headers: {
|
||||||
|
'Accept-Encoding': 'gzip, deflate, br',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Accept: 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
this.logger.debug(res);
|
||||||
|
return res.ok ? res.json() : Promise.reject(res);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.logger.error(error);
|
||||||
|
if (error.body?.message) {
|
||||||
|
this.logger.error(error.body.message);
|
||||||
|
}
|
||||||
|
throw new Error(`Unable to bundle interaction: ${JSON.stringify(error)}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createInteraction<Input>(input: Input, tags: { name: string; value: string }[], transfer: ArTransfer, strict: boolean) {
|
||||||
if (this._evaluationOptions.internalWrites) {
|
if (this._evaluationOptions.internalWrites) {
|
||||||
// Call contract and verify if there are any internal writes:
|
// Call contract and verify if there are any internal writes:
|
||||||
// 1. Evaluate current contract state
|
// 1. Evaluate current contract state
|
||||||
@@ -230,21 +284,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
|
|||||||
transfer.target,
|
transfer.target,
|
||||||
transfer.winstonQty
|
transfer.winstonQty
|
||||||
);
|
);
|
||||||
|
return interactionTx;
|
||||||
const response = await arweave.transactions.post(interactionTx);
|
|
||||||
|
|
||||||
if (response.status !== 200) {
|
|
||||||
this.logger.error('Error while posting transaction', response);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._evaluationOptions.waitForConfirmation) {
|
|
||||||
this.logger.info('Waiting for confirmation of', interactionTx.id);
|
|
||||||
const benchmark = Benchmark.measure();
|
|
||||||
await this.waitForConfirmation(interactionTx.id);
|
|
||||||
this.logger.info('Transaction confirmed after', benchmark.elapsed());
|
|
||||||
}
|
|
||||||
return interactionTx.id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
txId(): string {
|
txId(): string {
|
||||||
|
|||||||
@@ -92,6 +92,8 @@ export class DefaultEvaluationOptions implements EvaluationOptions {
|
|||||||
stackTrace = {
|
stackTrace = {
|
||||||
saveState: false
|
saveState: false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
sequencerAddress = "https://gateway.redstone.finance/";
|
||||||
}
|
}
|
||||||
|
|
||||||
// an interface for the contract EvaluationOptions - can be used to change the behaviour of some of the features.
|
// an interface for the contract EvaluationOptions - can be used to change the behaviour of some of the features.
|
||||||
@@ -133,4 +135,6 @@ export interface EvaluationOptions {
|
|||||||
// whether output state should be saved for each interaction in the stack trace (may result in huuuuge json files!)
|
// whether output state should be saved for each interaction in the stack trace (may result in huuuuge json files!)
|
||||||
saveState: boolean;
|
saveState: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
sequencerAddress: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ export abstract class DefaultStateEvaluator implements StateEvaluator {
|
|||||||
|
|
||||||
const interactionTx: GQLNodeInterface = missingInteraction.node;
|
const interactionTx: GQLNodeInterface = missingInteraction.node;
|
||||||
|
|
||||||
this.logger.debug(
|
this.logger.error(
|
||||||
`[${contractDefinition.txId}][${missingInteraction.node.id}][${missingInteraction.node.block.height}]: ${
|
`[${contractDefinition.txId}][${missingInteraction.node.id}][${missingInteraction.node.block.height}]: ${
|
||||||
missingInteractions.indexOf(missingInteraction) + 1
|
missingInteractions.indexOf(missingInteraction) + 1
|
||||||
}/${missingInteractions.length} [of all:${sortedInteractions.length}]`
|
}/${missingInteractions.length} [of all:${sortedInteractions.length}]`
|
||||||
|
|||||||
@@ -18,6 +18,11 @@ export class LexicographicalInteractionsSorter implements InteractionsSorter {
|
|||||||
private async addSortKey(txInfo: GQLEdgeInterface) {
|
private async addSortKey(txInfo: GQLEdgeInterface) {
|
||||||
const { node } = txInfo;
|
const { node } = txInfo;
|
||||||
|
|
||||||
|
// might have been already set by the RedStone Sequencer
|
||||||
|
if (txInfo.sortKey !== undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
txInfo.sortKey = await this.createSortKey(node.block.id, node.id, node.block.height);
|
txInfo.sortKey = await this.createSortKey(node.block.id, node.id, node.block.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,17 +10,23 @@ import {
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import knex from 'knex';
|
import knex from 'knex';
|
||||||
import os from "os";
|
import os from "os";
|
||||||
|
import {readJSON} from "../../redstone-smartweave-examples/src/_utils";
|
||||||
|
import {TsLogFactory} from '../src/logging/node/TsLogFactory';
|
||||||
|
|
||||||
const logger = LoggerFactory.INST.create('Contract');
|
const logger = LoggerFactory.INST.create('Contract');
|
||||||
|
|
||||||
|
LoggerFactory.use(new TsLogFactory());
|
||||||
LoggerFactory.INST.logLevel('fatal');
|
LoggerFactory.INST.logLevel('fatal');
|
||||||
LoggerFactory.INST.logLevel('info', 'Contract');
|
LoggerFactory.INST.logLevel('info', 'Contract');
|
||||||
|
LoggerFactory.INST.logLevel('debug', 'RedstoneGatewayInteractionsLoader');
|
||||||
|
LoggerFactory.INST.logLevel('error', 'DefaultStateEvaluator');
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
printTestInfo();
|
printTestInfo();
|
||||||
|
|
||||||
const PIANITY_CONTRACT = 'SJ3l7474UHh3Dw6dWVT1bzsJ-8JvOewtGoDdOecWIZo';
|
const PIANITY_CONTRACT = 'SJ3l7474UHh3Dw6dWVT1bzsJ-8JvOewtGoDdOecWIZo';
|
||||||
const PIANITY_COMMUNITY_CONTRACT = 'n05LTiuWcAYjizXAu-ghegaWjL89anZ6VdvuHcU6dno';
|
const PIANITY_COMMUNITY_CONTRACT = 'n05LTiuWcAYjizXAu-ghegaWjL89anZ6VdvuHcU6dno';
|
||||||
|
const LOOT_CONTRACT = 'Daj-MNSnH55TDfxqC7v4eq0lKzVIwh98srUaWqyuZtY';
|
||||||
const CACHE_PATH = 'cache.sqlite.db';
|
const CACHE_PATH = 'cache.sqlite.db';
|
||||||
|
|
||||||
const heapUsedBefore = Math.round((process.memoryUsage().heapUsed / 1024 / 1024) * 100) / 100;
|
const heapUsedBefore = Math.round((process.memoryUsage().heapUsed / 1024 / 1024) * 100) / 100;
|
||||||
@@ -51,18 +57,28 @@ async function main() {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
.setInteractionsLoader(
|
.setInteractionsLoader(
|
||||||
new RedstoneGatewayInteractionsLoader('https://gateway.redstone.finance', { notCorrupted: true })
|
new RedstoneGatewayInteractionsLoader('https://gateway.redstone.finance', {notCorrupted: true})
|
||||||
)
|
)
|
||||||
.setDefinitionLoader(
|
.setDefinitionLoader(
|
||||||
new RedstoneGatewayContractDefinitionLoader('https://gateway.redstone.finance', arweave, new MemCache())
|
new RedstoneGatewayContractDefinitionLoader('https://gateway.redstone.finance', arweave, new MemCache())
|
||||||
)
|
)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
const contract = smartweave.contract(PIANITY_CONTRACT);
|
const jwk = readJSON("../redstone-node/.secrets/redstone-jwk.json");
|
||||||
await contract.readState();
|
const contract = smartweave.contract(LOOT_CONTRACT)
|
||||||
|
/*.setEvaluationOptions({
|
||||||
|
sequencerAddress: "http://localhost:5666/"
|
||||||
|
})*/
|
||||||
|
.connect(jwk);
|
||||||
|
const bundledInteraction = await contract.bundleInteraction({
|
||||||
|
function: "generate"
|
||||||
|
});
|
||||||
|
|
||||||
const contract2 = smartweave.contract(PIANITY_COMMUNITY_CONTRACT);
|
logger.info("Bundled interaction", bundledInteraction);
|
||||||
await contract2.readState();
|
|
||||||
|
// bundlr balance I-5rWUehEv-MjdK9gFw09RxfSLQX9DIHxG614Wf8qo0 -h https://node1.bundlr.network/ -c arweave
|
||||||
|
|
||||||
|
//await contract.readState();
|
||||||
|
|
||||||
const heapUsedAfter = Math.round((process.memoryUsage().heapUsed / 1024 / 1024) * 100) / 100;
|
const heapUsedAfter = Math.round((process.memoryUsage().heapUsed / 1024 / 1024) * 100) / 100;
|
||||||
const rssUsedAfter = Math.round((process.memoryUsage().rss / 1024 / 1024) * 100) / 100;
|
const rssUsedAfter = Math.round((process.memoryUsage().rss / 1024 / 1024) * 100) / 100;
|
||||||
@@ -79,6 +95,7 @@ async function main() {
|
|||||||
const result = contract.lastReadStateStats();
|
const result = contract.lastReadStateStats();
|
||||||
|
|
||||||
logger.warn('total evaluation: ', result);
|
logger.warn('total evaluation: ', result);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
function printTestInfo() {
|
function printTestInfo() {
|
||||||
|
|||||||
Reference in New Issue
Block a user