feat: bundlr, sequencer

This commit is contained in:
ppedziwiatr
2022-01-24 22:32:15 +01:00
committed by Piotr Pędziwiatr
parent fcfa93aae3
commit a5740a0d1d
6 changed files with 105 additions and 22 deletions

View File

@@ -132,6 +132,23 @@ export interface Contract<State = unknown> {
strict?: boolean
): 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
* interaction with contract (eg. after reading state)

View File

@@ -188,6 +188,60 @@ export class HandlerBasedContract<State> implements Contract<State> {
}
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) {
// Call contract and verify if there are any internal writes:
// 1. Evaluate current contract state
@@ -230,21 +284,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
transfer.target,
transfer.winstonQty
);
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;
return interactionTx;
}
txId(): string {

View File

@@ -92,6 +92,8 @@ export class DefaultEvaluationOptions implements EvaluationOptions {
stackTrace = {
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.
@@ -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!)
saveState: boolean;
};
sequencerAddress: string;
}

View File

@@ -77,7 +77,7 @@ export abstract class DefaultStateEvaluator implements StateEvaluator {
const interactionTx: GQLNodeInterface = missingInteraction.node;
this.logger.debug(
this.logger.error(
`[${contractDefinition.txId}][${missingInteraction.node.id}][${missingInteraction.node.block.height}]: ${
missingInteractions.indexOf(missingInteraction) + 1
}/${missingInteractions.length} [of all:${sortedInteractions.length}]`

View File

@@ -18,6 +18,11 @@ export class LexicographicalInteractionsSorter implements InteractionsSorter {
private async addSortKey(txInfo: GQLEdgeInterface) {
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);
}

View File

@@ -10,17 +10,23 @@ import {
import * as fs from 'fs';
import knex from 'knex';
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');
LoggerFactory.use(new TsLogFactory());
LoggerFactory.INST.logLevel('fatal');
LoggerFactory.INST.logLevel('info', 'Contract');
LoggerFactory.INST.logLevel('debug', 'RedstoneGatewayInteractionsLoader');
LoggerFactory.INST.logLevel('error', 'DefaultStateEvaluator');
async function main() {
printTestInfo();
const PIANITY_CONTRACT = 'SJ3l7474UHh3Dw6dWVT1bzsJ-8JvOewtGoDdOecWIZo';
const PIANITY_COMMUNITY_CONTRACT = 'n05LTiuWcAYjizXAu-ghegaWjL89anZ6VdvuHcU6dno';
const LOOT_CONTRACT = 'Daj-MNSnH55TDfxqC7v4eq0lKzVIwh98srUaWqyuZtY';
const CACHE_PATH = 'cache.sqlite.db';
const heapUsedBefore = Math.round((process.memoryUsage().heapUsed / 1024 / 1024) * 100) / 100;
@@ -51,18 +57,28 @@ async function main() {
)
)
.setInteractionsLoader(
new RedstoneGatewayInteractionsLoader('https://gateway.redstone.finance', { notCorrupted: true })
new RedstoneGatewayInteractionsLoader('https://gateway.redstone.finance', {notCorrupted: true})
)
.setDefinitionLoader(
new RedstoneGatewayContractDefinitionLoader('https://gateway.redstone.finance', arweave, new MemCache())
)
.build();
const contract = smartweave.contract(PIANITY_CONTRACT);
await contract.readState();
const jwk = readJSON("../redstone-node/.secrets/redstone-jwk.json");
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);
await contract2.readState();
logger.info("Bundled interaction", bundledInteraction);
// 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 rssUsedAfter = Math.round((process.memoryUsage().rss / 1024 / 1024) * 100) / 100;
@@ -79,6 +95,7 @@ async function main() {
const result = contract.lastReadStateStats();
logger.warn('total evaluation: ', result);
return;
}
function printTestInfo() {