feat: syncState with the execution network
This commit is contained in:
@@ -8,7 +8,7 @@ import {
|
||||
InteractionResult,
|
||||
Tags
|
||||
} from '@smartweave';
|
||||
import { NetworkInfoInterface } from 'arweave/node/network';
|
||||
import {NetworkInfoInterface} from 'arweave/node/network';
|
||||
|
||||
export type CurrentTx = { interactionTxId: string; contractTxId: string };
|
||||
export type BenchmarkStats = { gatewayCommunication: number; stateEvaluation: number; total: number };
|
||||
@@ -203,4 +203,11 @@ export interface Contract<State = unknown> {
|
||||
* calculates state hash using stable stringify
|
||||
*/
|
||||
stateHash(state: State): string;
|
||||
|
||||
/**
|
||||
* this method allows to sync the state of the local SDK state with the Execution Network -
|
||||
* to make features like "viewStates" possible when using EN;
|
||||
* @param nodeAddress - distributed execution network node address
|
||||
*/
|
||||
syncState(nodeAddress: string): Promise<Contract>;
|
||||
}
|
||||
|
||||
@@ -677,4 +677,28 @@ export class HandlerBasedContract<State> implements Contract<State> {
|
||||
|
||||
return hash.digest('hex');
|
||||
}
|
||||
|
||||
async syncState(nodeAddress: string): Promise<Contract> {
|
||||
const { stateEvaluator } = this.smartweave;
|
||||
const response = await fetch(`${nodeAddress}/state?id=${this._contractTxId}&validity=true&safeHeight=true`)
|
||||
.then((res) => {
|
||||
return res.ok ? res.json() : Promise.reject(res);
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.body?.message) {
|
||||
this.logger.error(error.body.message);
|
||||
}
|
||||
throw new Error(`Unable to retrieve state. ${error.status}: ${error.body?.message}`);
|
||||
});
|
||||
|
||||
await stateEvaluator.syncState(
|
||||
this._contractTxId,
|
||||
response.height,
|
||||
response.lastTransactionId,
|
||||
response.state,
|
||||
response.validity
|
||||
);
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +65,11 @@ export interface StateEvaluator {
|
||||
* allows to manually flush state cache into underneath storage.
|
||||
*/
|
||||
flushCache(): Promise<void>;
|
||||
|
||||
/**
|
||||
* allows to syncState with an external state source (like RedStone Distributed Execution Network)
|
||||
*/
|
||||
syncState(contractTxId: string, blockHeight: number, transactionId: string, state: any, validity: any): Promise<void>;
|
||||
}
|
||||
|
||||
export class EvalStateResult<State> {
|
||||
|
||||
@@ -220,4 +220,15 @@ export class CacheableStateEvaluator extends DefaultStateEvaluator {
|
||||
async flushCache(): Promise<void> {
|
||||
return await this.cache.flush();
|
||||
}
|
||||
|
||||
async syncState(
|
||||
contractTxId: string,
|
||||
blockHeight: number,
|
||||
transactionId: string,
|
||||
state: any,
|
||||
validity: any
|
||||
): Promise<void> {
|
||||
const stateToCache = new EvalStateResult(state, validity, transactionId);
|
||||
await this.cache.put(new BlockHeightKey(contractTxId, blockHeight), stateToCache);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,4 +300,12 @@ export abstract class DefaultStateEvaluator implements StateEvaluator {
|
||||
): Promise<void>;
|
||||
|
||||
abstract flushCache(): Promise<void>;
|
||||
|
||||
abstract syncState(
|
||||
contractTxId: string,
|
||||
blockHeight: number,
|
||||
transactionId: string,
|
||||
state: any,
|
||||
validity: any
|
||||
): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -42,7 +42,9 @@ export async function createTx(
|
||||
interactionTx.addTag(SmartWeaveTags.CONTRACT_TX_ID, contractId);
|
||||
interactionTx.addTag(SmartWeaveTags.INPUT, JSON.stringify(input));
|
||||
|
||||
if (wallet) {
|
||||
await arweave.transactions.sign(interactionTx, wallet);
|
||||
}
|
||||
return interactionTx;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ 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';
|
||||
import path from "path";
|
||||
import stringify from "safe-stable-stringify";
|
||||
|
||||
50
tools/sync-state.ts
Normal file
50
tools/sync-state.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
/* eslint-disable */
|
||||
import Arweave from 'arweave';
|
||||
import {LoggerFactory, SmartWeaveNodeFactory} from '../src';
|
||||
import fs from 'fs';
|
||||
import {JWKInterface} from 'arweave/node/lib/wallet';
|
||||
|
||||
async function main() {
|
||||
LoggerFactory.INST.logLevel('info');
|
||||
const logger = LoggerFactory.INST.create('deploy');
|
||||
|
||||
const arweave = Arweave.init({
|
||||
host: 'arweave.net',
|
||||
port: 443,
|
||||
protocol: 'https'
|
||||
});
|
||||
|
||||
try {
|
||||
const smartweave = SmartWeaveNodeFactory
|
||||
.memCachedBased(arweave)
|
||||
.useRedStoneGateway()
|
||||
.build();
|
||||
|
||||
const contract = await smartweave.contract("qg5BIOUraunoi6XJzbCC-TgIAypcXyXlVprgg0zRRDE")
|
||||
.syncState("http://134.209.84.136:8080");
|
||||
|
||||
const result = await contract
|
||||
.viewState({
|
||||
function: "getNodeDetails", data: {
|
||||
address: "33F0QHcb22W7LwWR1iRC8Az1ntZG09XQ03YWuw2ABqA"
|
||||
}
|
||||
});
|
||||
|
||||
logger.info("Result", result);
|
||||
|
||||
} catch (e) {
|
||||
logger.error(e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export function readJSON(path: string): JWKInterface {
|
||||
const content = fs.readFileSync(path, "utf-8");
|
||||
try {
|
||||
return JSON.parse(content);
|
||||
} catch (e) {
|
||||
throw new Error(`File "${path}" does not contain a valid JSON`);
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((e) => console.error(e));
|
||||
Reference in New Issue
Block a user