Skip to content

Commit

Permalink
Change tx trace system to generate them on the fly in order to includ…
Browse files Browse the repository at this point in the history
…e notify & sub actions, bump version
  • Loading branch information
guilledk committed Dec 28, 2023
1 parent 8fa2662 commit c6d99dd
Show file tree
Hide file tree
Showing 11 changed files with 100 additions and 47 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "leap-mock",
"repository": "git+https://github.com/guilledk/leap-mock.git",
"version": "0.1.4",
"version": "0.1.5",
"description": "AntelopeIO/leap mocker for advanced indexer testing",
"main": "./build/index.js",
"type": "module",
Expand Down
51 changes: 36 additions & 15 deletions src/chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ export class MockChain {
private chainId: string;
private startTime: string;
private blockHistory: string[][];

private transactions: {[key: number]: TransactionTraceOptions[]};
private generatedTraces: {[key: number]: ActionTrace[]} = {};

private db = new MockChainbase();

Expand All @@ -56,7 +58,7 @@ export class MockChain {
block_id: ZERO_HASH,
block_num: 0
};
private actionMockers: {[key: string]: ActionMocker[]} = {};
private actionMockers: {[key: string]: [NameType, ActionMocker][]} = {};

private jumpedLastBlock: boolean = false;
private shouldProduce: boolean;
Expand Down Expand Up @@ -105,6 +107,7 @@ export class MockChain {
async initializeMockingModule(name: string) {
const currentDir = path.dirname(fileURLToPath(import.meta.url));
const module = await import(path.join(currentDir, `./mock/${name}/manifest.js`));
const moduleConstants = await import(path.join(currentDir, `./mock/${name}/constants.js`));
const manifest: MockingManifest = module.mockingManifest;
if (!manifest)
throw new Error(`mocking module ${name} doesnt have a manifest`);
Expand All @@ -123,7 +126,7 @@ export class MockChain {
if (!(actionKey in this.actionMockers))
this.actionMockers[actionKey] = [];

this.actionMockers[actionKey].push(mocker);
this.actionMockers[actionKey].push([moduleConstants.SELF, mocker]);
});
}

Expand Down Expand Up @@ -166,6 +169,7 @@ export class MockChain {
generateActionTrace(
actionOrdinal, globalSequence,
this.db.getContract(action.account).abi,
action.account,
action
)
);
Expand Down Expand Up @@ -210,6 +214,12 @@ export class MockChain {
const traces = [];
if (blockNum in this.transactions) {
for (const txDesc of this.transactions[blockNum]) {

const actionTraces = [];
if (blockNum in this.generatedTraces)
this.generatedTraces[blockNum].forEach(
trace => actionTraces.push(['action_trace_v1', trace]));

traces.push(['transaction_trace_v0', {
id: txDesc.id ? txDesc.id : randomHash(),
status: txDesc.status ? txDesc.status : 0,
Expand All @@ -218,7 +228,7 @@ export class MockChain {
elapsed: txDesc.elapsed ? txDesc.elapsed : 0,
net_usage: txDesc.net_usage ? txDesc.net_usage : 0,
scheduled: txDesc.scheduled ? txDesc.scheduled : false,
action_traces: txDesc.action_traces,
action_traces: actionTraces,
partial: txDesc.partial ? txDesc.partial : ['partial_transaction_v0', {
expiration: addSecondsToDate(this.getBlockTimestamp(blockNum), randomInt(20, 30)).toISOString().substring(0, 19),
ref_block_num: 1,
Expand Down Expand Up @@ -389,17 +399,25 @@ export class MockChain {
};
}

private getContext(contract: NameType, contractABI: ABI, params: any) {
private getContext(receiver: NameType, contract: NameType, name: NameType, contractABI: ABI, params: any) {
return new ApplyContext(
this.globalSequence,
this.actionOrdinal,
this.db,
contractABI,
Name.from(contract),
Name.from(name),
Name.from(receiver),
params
);
}

private addExtraTrace(trace: ActionTrace) {
if (!(this.headBlockNum in this.generatedTraces))
this.generatedTraces[this.headBlockNum] = [];
this.generatedTraces[this.headBlockNum].push(trace)
}

applyTrace(trace: ActionTrace) {
const contract = trace.act.account;
const contractABI = this.db.getContract(contract).abi;
Expand All @@ -416,21 +434,24 @@ export class MockChain {
const mockers = this.actionMockers[mockerName];

if (mockers.length > 0) {
const ctx = this.getContext(contract, contractABI, params);

// apply root trace
for(const mocker of mockers) {
for(const [receiver, mocker] of mockers) {
const ctx = this.getContext(
receiver, contract, actionName, contractABI, params);

mocker.handler(ctx);
this.db.commit();
}
// if (Name.from(receiver).equals(contract))
this.addExtraTrace(ctx.getTrace());

this.actionOrdinal++;
this.globalSequence++;
this.db.commit();

// recursion to process all subActions
for(const trace of ctx.subActions)
this.applyTrace(trace)
this.actionOrdinal++;
this.globalSequence++;

// recursion to process all subActions
for(const trace of ctx.subActions)
this.applyTrace(trace);
}
} else
throw new Error(`Action handler not found for ${mockerName}`);
}
Expand All @@ -451,7 +472,7 @@ export class MockChain {
this.actionOrdinal = 0;
for (const [_, actionTrace] of tx.action_traces) {
try {
this.applyTrace(actionTrace)
this.applyTrace(actionTrace);
} catch (e) {
if (e instanceof EOSVMAssertionError) {
this.db.revert();
Expand Down
48 changes: 34 additions & 14 deletions src/mock/abstract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ export class ApplyContext {
actionOrdinal: number;
db: MockChainbase;
contractABI: ABI;
contractAccount: Name;
contractAccount: Name; // contract source code
actionName: Name;
receiver: Name; // contract thats reacting to the action
params: any;
console: string;
subActions: ActionTrace[];
Expand All @@ -30,13 +32,17 @@ export class ApplyContext {
db: MockChainbase,
contractABI: ABI,
contractAccount: Name,
actionName: Name,
receiver: Name,
params: any
) {
this.globalSequence = globalSequence;
this.actionOrdinal = actionOrdinal;
this.db = db;
this.contractABI = contractABI;
this.contractAccount = contractAccount;
this.actionName = actionName;
this.receiver = receiver;
this.params = params;
this.console = '';
this.subActions = [];
Expand Down Expand Up @@ -69,6 +75,33 @@ export class ApplyContext {
findByPrimaryKey(table: NameType, scope: NameType, key: bigint): TableRow | undefined {
return this.db.findByPrimaryKey([this.contractAccount, table, scope], key);
}

getTrace() {
return generateActionTrace(
this.actionOrdinal,
this.globalSequence,
this.contractABI,
this.receiver,
{
account: this.contractAccount,
name: this.actionName,
parameters: this.params
}
)[1];
}

sendAction(action: ActionDescriptor) {
const contractABI = this.db.getContract(action.account).abi;
this.subActions.push(
generateActionTrace(
this.actionOrdinal + this.subActions.length,
this.globalSequence + this.subActions.length,
contractABI,
this.receiver,
action
)[1]
);
}
}
export class EOSVMAssertionError extends Error {}

Expand All @@ -77,19 +110,6 @@ export function eosVMCheck(condition: boolean, errorMessage: string) {
throw new EOSVMAssertionError(errorMessage);
}

export function sendAction(ctx: ApplyContext, action: ActionDescriptor) {
const contractABI = ctx.db.getContract(action.account).abi;
ctx.subActions.push(
generateActionTrace(
ctx.actionOrdinal,
ctx.globalSequence,
contractABI,
action
)[1]
);
}


export abstract class ActionMocker {
code: Name;
action: Name;
Expand Down
4 changes: 0 additions & 4 deletions src/mock/telos.evm/descriptors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ import {ActionDescriptor} from "../../types.js";
import {TEVMTransaction} from "telos-evm-custom-ds";
import {LegacyTransaction} from "@ethereumjs/tx";

export class TelosEVMDeposit extends AntelopeTransfer {
account: NameType = TOKEN_CONTRACT;
name: NameType = 'transfer';
}

export class TelosEVMCreate implements ActionDescriptor {
account: NameType = SELF;
Expand Down
2 changes: 1 addition & 1 deletion src/mock/telos.evm/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ export const mockingManifest: MockingManifest = {
tableMappings: {
account: [
AccountRow.primaryKey,
AccountRow.byAddress,
AccountRow.byAccount,
AccountRow.byAddress
]
}
},
Expand Down
5 changes: 2 additions & 3 deletions src/mock/telos.evm/mockers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {ApplyContext, eosVMCheck, sendAction} from "../abstract.js";
import {ApplyContext, eosVMCheck} from "../abstract.js";
import {Checksum160} from "@greymass/eosio";
import {Address} from "@ethereumjs/util";
import {randomByteArray} from "../../utils.js";
Expand Down Expand Up @@ -103,8 +103,7 @@ export class TEVMWithdrawMocker extends TEVMMocker {
handler(ctx: ApplyContext) {
this.subBalance(ctx, ctx.params.to, ctx.params.quantity);

sendAction(
ctx,
ctx.sendAction(
new AntelopeTransfer({
from: this.code.toString(),
to: ctx.params.to,
Expand Down
4 changes: 2 additions & 2 deletions src/mock/telos.evm/tables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ export abstract class TEVMMocker extends ActionMocker {
const key = Name.from(name).value.toString();
return ctx.db.find(
SELF, 'account', 'eosio.evm',
2, 'i64',
3, 'i64',
key, key,
1
) as AccountRow[];
Expand All @@ -126,7 +126,7 @@ export abstract class TEVMMocker extends ActionMocker {
const addr256 = addressTypeToSHA256(addr).toString();
return ctx.db.find(
SELF, 'account', 'eosio.evm',
3, 'sha256',
2, 'sha256',
addr256, addr256,
1
) as AccountRow[];
Expand Down
8 changes: 4 additions & 4 deletions src/mock/telos.evm/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export function getEVMBalanceForAccount(runtime: ChainRuntime, account: NameType
const rows = runtime.chain.getDB().getTableRowsAPI({
code: SELF, table: 'account', scope: SELF,
key_type: 'i64',
index_position: 2,
index_position: 3,
lower_bound: Name.from(account).value.toString(),
upper_bound: Name.from(account).value.toString(),
limit: 1
Expand All @@ -103,7 +103,7 @@ export async function getEVMBalanceForAccountHTTP(rpc: JsonRpc, account: NameTyp
json: true,
code: SELF.toString(), table: 'account', scope: SELF.toString(),
key_type: 'i64',
index_position: 2,
index_position: 3,
lower_bound: Name.from(account).value.toString(),
upper_bound: Name.from(account).value.toString(),
limit: 1
Expand All @@ -119,7 +119,7 @@ export function getEVMBalanceForAddress(runtime: ChainRuntime, addr: Address): A
const rows = runtime.chain.getDB().getTableRowsAPI({
code: SELF, table: 'account', scope: SELF,
key_type: 'sha256',
index_position: 3,
index_position: 2,
lower_bound: addressToSHA256(addr).toString(),
upper_bound: addressToSHA256(addr).toString(),
limit: 1
Expand All @@ -136,7 +136,7 @@ export async function getEVMBalanceForAddressHTTP(rpc: JsonRpc, addr: Address):
json: true,
code: SELF, table: 'account', scope: SELF,
key_type: 'sha256',
index_position: 3,
index_position: 2,
lower_bound: addressToSHA256(addr).toString(),
upper_bound: addressToSHA256(addr).toString(),
limit: 1
Expand Down
16 changes: 16 additions & 0 deletions src/tests/hyperion-sequence.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
import {addressToChecksum160, assetQuantityToEvm, randomByteArray} from "../utils.js";
import {Address} from "@ethereumjs/util";
import {getBalanceForAccount, getBalanceForAccountHTTP} from "../mock/eosio.token/utils.js";
import {Name, Serializer} from "@greymass/eosio";

const quantity = '420.0000 TLOS'

Expand Down Expand Up @@ -185,6 +186,21 @@ describeMockChainTests(
aliceBalance.balance.equals(quantity),
'Balance mismatch for alice!'
).to.be.true;

// assert subaction produces correct trace
const block = runtime.chain.generateHeadBlockResponse(3);
const traces = Serializer.decode({
data: block.traces,
type: 'transaction_trace[]',
abi: runtime.chain.shipAbi
})[0][1];

// console.log(JSON.stringify(traces, null, 4));

expect(traces.action_traces.length).to.be.eq(3);
expect(traces.action_traces[0][1].receiver.toString()).to.be.eq('eosio.evm');
expect(traces.action_traces[1][1].receiver.toString()).to.be.eq('eosio.token');
expect(traces.action_traces[2][1].receiver.toString()).to.be.eq('eosio.evm');
}
}
}
Expand Down
5 changes: 3 additions & 2 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ export function generateActionTrace(
actionOrdinal: number,
globalSequence: number,
contractAbi: ABI,
receiver: NameType,
actionDesc: ActionDescriptor
): ['action_trace_v1', ActionTrace] {
let stringName;
Expand All @@ -169,15 +170,15 @@ export function generateActionTrace(
action_ordinal: actionOrdinal,
creator_action_ordinal: 0,
receipt: ['action_receipt_v0', {
receiver: actionDesc.account,
receiver: receiver,
act_digest: randomHash(),
global_sequence: globalSequence,
recv_sequence: 1,
auth_sequence: [{account: actionDesc.account, sequence: 1}],
code_sequence: 0,
abi_sequence: 0
}],
receiver: actionDesc.account,
receiver: receiver,
act: {
account: actionDesc.account,
name: actionDesc.name,
Expand Down
2 changes: 1 addition & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

"@eosrio/hyperion-sequential-reader@https://github.com/eosrio/hyperion-sequential-reader.git#hist_buf_size":
version "1.2.5"
resolved "https://github.com/eosrio/hyperion-sequential-reader.git#e6903a90989350ad31a566835d12b77e603c0c44"
resolved "https://github.com/eosrio/hyperion-sequential-reader.git#8573e0db7790d66279f640ee41bdbb442cb26236"
dependencies:
"@greymass/eosio" "0.6.11"
async "^3.2.4"
Expand Down

0 comments on commit c6d99dd

Please sign in to comment.