Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1c3cf56dd0 | |||
| 82d3ed2643 | |||
| 1ee77e3427 | |||
| 9e37cb585d | |||
| 2eab959e58 | |||
| 0c3c3bc002 |
@@ -85,21 +85,36 @@ jobs:
|
||||
# передачу команды контейнеру. Монтируем config.ini (SHiP на 8080) и genesis.json.
|
||||
- name: Start blockchain node
|
||||
run: |
|
||||
# Раннер self-hosted и персистентный: упавший прогон оставляет контейнер
|
||||
# с фиксированным именем blockchain, и следующий docker run падает с
|
||||
# exit 125 (Conflict, name already in use). Сносим остаток перед стартом.
|
||||
docker rm -f blockchain 2>/dev/null || true
|
||||
# nodeos на старте генерирует protocol_features/*.json В смонтированный
|
||||
# config-каталог. Если монтировать сам workspace, эти файлы остаются в нём
|
||||
# (untracked, root-owned — checkout --force их не чистит) и на следующем
|
||||
# прогоне ломают узел: 'plugin_config_exception: Incorrect plugin
|
||||
# configuration'. Монтируем pristine-копию во временном каталоге — узел
|
||||
# пишет сгенерированное туда, workspace остаётся чистым.
|
||||
CONFIG_DIR=$(mktemp -d)
|
||||
cp "${GITHUB_WORKSPACE}/packages/parser2/test/integration/node-config/"* "$CONFIG_DIR/"
|
||||
docker run -d --name blockchain \
|
||||
-p 8888:8888 -p 8080:8080 -p 9876:9876 \
|
||||
-v "${GITHUB_WORKSPACE}/packages/parser2/test/integration/node-config:/mnt/dev/config" \
|
||||
-v "$CONFIG_DIR:/mnt/dev/config" \
|
||||
dicoop/blockchain:v5.1.0-dev \
|
||||
/bin/bash -c '/usr/local/bin/nodeos -d /mnt/dev/data -p eosio --config-dir /mnt/dev/config --genesis-json /mnt/dev/config/genesis.json --disable-replay-opts'
|
||||
sleep 3
|
||||
- name: Wait for Chain API
|
||||
run: |
|
||||
for i in $(seq 1 60); do
|
||||
if curl -sf http://localhost:8888/v1/chain/get_info > /dev/null 2>&1; then
|
||||
# Бюджет 120 попыток × (≤2с curl + 2с sleep) ≈ до 8 мин: на нагруженном
|
||||
# раннере nodeos поднимается медленно (наблюдали >6 мин до get_info), а
|
||||
# curl без --max-time подвисал на ещё-не-отвечающем порту и съедал бюджет.
|
||||
for i in $(seq 1 120); do
|
||||
if curl -sf --max-time 2 http://localhost:8888/v1/chain/get_info > /dev/null 2>&1; then
|
||||
echo "Chain API ready after $i attempts"
|
||||
exit 0
|
||||
fi
|
||||
echo " attempt $i/60..."
|
||||
sleep 2
|
||||
echo " attempt $i/120..."
|
||||
sleep 4
|
||||
done
|
||||
echo "ERROR: Chain API did not become ready. Container logs:"
|
||||
docker logs blockchain | tail -100
|
||||
|
||||
@@ -15,7 +15,7 @@ name: Release
|
||||
# GITHUB_TOKEN — выдаётся автоматически GitHub Actions
|
||||
#
|
||||
# npm publish работает через trusted publishing (OIDC) — токен не нужен:
|
||||
# настраивается на стороне npm через Trusted Publisher: GitHub Actions.
|
||||
# настроен на стороне npm для @coopenomics/parser2 под этот workflow.
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -71,18 +71,15 @@ jobs:
|
||||
pnpm --filter @coopenomics/coopos-ship-reader build
|
||||
pnpm --filter @coopenomics/parser2 build
|
||||
|
||||
# Первый publish @coopenomics/parser2 идёт через классический NPM_TOKEN —
|
||||
# OIDC trusted publishing требует предварительно настроенного publisher на стороне
|
||||
# npm, а настроить его можно только для уже существующего пакета. После того как
|
||||
# @coopenomics/parser2 создастся и под него будет добавлен Trusted Publisher,
|
||||
# NODE_AUTH_TOKEN и NPM_CONFIG_PROVENANCE=false можно убрать и вернуть
|
||||
# provenance=true → снова OIDC.
|
||||
# npm trusted publishing: OIDC через id-token: write, без NPM_TOKEN.
|
||||
# Настроено на стороне npm: @coopenomics/parser2/access → Trusted Publisher
|
||||
# → GitHub Actions (repo=coopenomics/parser2, workflow=release.yml).
|
||||
# Provenance=true — sigstore подпишет каждый publish.
|
||||
- name: Publish via Lerna (from-package)
|
||||
if: steps.ver.outputs.released == 'true'
|
||||
run: pnpm exec lerna publish from-package --yes --no-private
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
NPM_CONFIG_PROVENANCE: false
|
||||
NPM_CONFIG_PROVENANCE: true
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Tag release
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Универсальный индексер блокчейнов EOSIO / Antelope: читает блоки из State History Plugin (SHiP) по WebSocket, декодирует actions и дельты таблиц с учётом исторических ABI, публикует унифицированный поток событий в Redis Stream. Потребители получают события через `ParserClient` с single-active-consumer lock'ом, recovery после сбоев и dead-letter для poison-messages.
|
||||
|
||||
[](https://github.com/coopenomics/parser/actions/workflows/ci.yml)
|
||||
[](https://github.com/coopenomics/parser2/actions/workflows/ci.yml)
|
||||
[](https://www.npmjs.com/package/@coopenomics/parser2)
|
||||
[](LICENSE)
|
||||
|
||||
@@ -240,6 +240,28 @@ docker run --rm --network host \
|
||||
- [Redis key taxonomy](docs/redis-key-taxonomy.md)
|
||||
- [Disaster recovery / fork scenarios](docs/disaster-recovery.md)
|
||||
|
||||
## Известные ограничения
|
||||
|
||||
### Схлопывание дельт create+remove внутри одного блока
|
||||
|
||||
Парсер **не увидит** жизненный цикл строки таблицы, у которой `emplace` и `erase` произошли **внутри одного блока** (даже в разных транзакциях). Это поведение унаследовано от upstream-плагина `state_history_plugin` (SHiP) ноджеса: на уровне `chainbase::undo_index::on_remove` строка, созданная в текущей undo session и тут же удалённая, физически уничтожается без записи в `_removed_values` — это by-design оптимизация для отката блока (нечего откатывать), но она же делает event невидимым для SHiP-снимка состояния. SHiP пакует только то, что есть в undo session, и не имеет шанса увидеть транзиентную пару.
|
||||
|
||||
**Симптомы:**
|
||||
- На цепочке таблица пустая (получена и тут же удалена).
|
||||
- ACTION-логи и `getclearance`/`apprvappndx`-подобные действия видны нормально — экшены идут параллельным потоком и не зависят от undo.
|
||||
- В Redis stream / БД-индексе нет ни `present:true`, ни `present:false` события для конкретного `primary_key`.
|
||||
|
||||
**Когда это всплывает на практике:**
|
||||
- Контракт делает `emplace + erase` одной сущности в одной транзакции (anti-pattern, но встречается).
|
||||
- Несколько коротких транзакций над одной сущностью пакуются производителем блоков в один блок (массовый импорт, auto-approve, bulk-операции, миграции, batch-сценарии).
|
||||
|
||||
**Workaround на стороне приложения:**
|
||||
- Разнести `emplace` и `erase` по разным блокам — в seed/bulk-сценариях добавить sleep ≥ 1 × `block_time` (≈500 мс для EOSIO) между транзакциями. В production-flow ручного approve'а это не нужно — между нажатием кнопки и второй транзакцией всегда проходит более одного блока.
|
||||
- Если транзиентные пары — часть нормального дизайна контракта, рассмотреть переписывание на `modify(status="approved")` вместо `erase`: SHiP корректно эмиттит дельту изменения статуса.
|
||||
|
||||
**Системный фикс (планируется):**
|
||||
Drop-in патч `chainbase` + `state_history_plugin` в нашем форке `~/coopos` (uENOSIO) — отдельный список `_transient_removed_values` в undo_index + паковка пар `[present:true, present:false]` в `table_delta_v0` без изменения wire-формата. Wire-совместимо со всеми существующими SHiP-клиентами; после деплоя форка парсер автоматически начнёт видеть транзиентные пары без изменений в коде. Технические детали и план работ — в задаче `f3-shipchainbase-patch-vidimost-tranzientnykh-delt-createremove-vnutri-bloka.md` проекта parser2 в `_blago`.
|
||||
|
||||
## Разработка
|
||||
|
||||
### Структура монорепы
|
||||
|
||||
@@ -14,7 +14,7 @@ Minimal example: Parser (SHiP → Redis indexer) and ParserClient (consumer) run
|
||||
**Step 1 — Clone and install**
|
||||
|
||||
```bash
|
||||
git clone https://github.com/coopenomics/parser.git
|
||||
git clone https://github.com/coopenomics/parser2.git
|
||||
cd parser
|
||||
pnpm install
|
||||
```
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@coopenomics/parser2",
|
||||
"version": "1.0.2",
|
||||
"version": "1.1.0",
|
||||
"description": "Universal EOSIO/Antelope SHiP-to-Redis blockchain indexer (parser)",
|
||||
"license": "MIT",
|
||||
"author": "Coopenomics contributors",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/coopenomics/parser"
|
||||
"url": "https://github.com/coopenomics/parser2"
|
||||
},
|
||||
"type": "module",
|
||||
"exports": {
|
||||
|
||||
@@ -136,13 +136,19 @@ export class BlockProcessor {
|
||||
block_num: blockNum,
|
||||
block_time: blockTime,
|
||||
block_id: blockId,
|
||||
transaction_id: trace.transactionId,
|
||||
account: trace.account,
|
||||
name: trace.name,
|
||||
authorization: [...trace.authorization],
|
||||
data,
|
||||
action_ordinal: trace.actionOrdinal,
|
||||
creator_action_ordinal: trace.creatorActionOrdinal,
|
||||
global_sequence: trace.globalSequence,
|
||||
receipt: trace.receipt,
|
||||
context_free: trace.contextFree,
|
||||
elapsed: trace.elapsed,
|
||||
console: trace.console,
|
||||
account_ram_deltas: [...trace.accountRamDeltas],
|
||||
}
|
||||
|
||||
actionEvents.push({ ...partial, event_id: computeEventId(partial) })
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
* повторные доставки.
|
||||
*/
|
||||
|
||||
import type { ActionAuthorization, ActionReceipt } from '@coopenomics/coopos-ship-reader'
|
||||
import type { ActionAuthorization, ActionReceipt, AccountRamDelta } from '@coopenomics/coopos-ship-reader'
|
||||
|
||||
/** Событие вызова смарт-контракта (inline action). */
|
||||
export interface ActionEvent {
|
||||
@@ -20,6 +20,8 @@ export interface ActionEvent {
|
||||
/** ISO-8601 время блока из трассировки транзакции. */
|
||||
block_time: string
|
||||
block_id: string
|
||||
/** Хеш транзакции, в которой исполнено действие. */
|
||||
transaction_id: string
|
||||
/** Аккаунт-владелец контракта (account). */
|
||||
account: string
|
||||
/** Имя действия (action name). */
|
||||
@@ -29,10 +31,20 @@ export interface ActionEvent {
|
||||
data: Record<string, unknown>
|
||||
/** Порядковый номер действия внутри транзакции (1-based). */
|
||||
action_ordinal: number
|
||||
/** Порядковый номер родительского (создавшего) действия; 0 — если действие верхнего уровня. */
|
||||
creator_action_ordinal: number
|
||||
/** Глобальная уникальная последовательность — монотонный счётчик действий в цепи. */
|
||||
global_sequence: bigint
|
||||
/** Квитанция об исполнении; null если нет трассировки. */
|
||||
receipt: ActionReceipt | null
|
||||
/** Является ли действие context-free. */
|
||||
context_free: boolean
|
||||
/** Время исполнения действия в микросекундах. */
|
||||
elapsed: number
|
||||
/** Консольный вывод действия (eosio::print и т.п.). */
|
||||
console: string
|
||||
/** Изменения RAM по аккаунтам в результате действия. */
|
||||
account_ram_deltas: AccountRamDelta[]
|
||||
}
|
||||
|
||||
/** Изменение строки в пользовательской таблице смарт-контракта (contract_row delta). */
|
||||
|
||||
@@ -57,8 +57,13 @@ function makeBlock(blockNum = 1, numTraces = 0, numDeltas = 0): ShipBlock {
|
||||
authorization: [{ actor: 'alice', permission: 'active' }],
|
||||
actRaw: new Uint8Array([1, 2, 3]),
|
||||
actionOrdinal: i + 1,
|
||||
creatorActionOrdinal: 0,
|
||||
globalSequence: BigInt(100 + i),
|
||||
receipt: null,
|
||||
contextFree: false,
|
||||
elapsed: 0,
|
||||
console: '',
|
||||
accountRamDeltas: [],
|
||||
blockNum,
|
||||
blockId: 'a'.repeat(64),
|
||||
blockTime: '2024-01-01T00:00:00.000',
|
||||
@@ -176,8 +181,13 @@ describe('BlockProcessor — ABI updates (Story 4.3)', () => {
|
||||
authorization: [],
|
||||
actRaw: new Uint8Array([1]),
|
||||
actionOrdinal: 1,
|
||||
creatorActionOrdinal: 0,
|
||||
globalSequence: BigInt(1),
|
||||
receipt: null,
|
||||
contextFree: false,
|
||||
elapsed: 0,
|
||||
console: '',
|
||||
accountRamDeltas: [],
|
||||
blockNum: 500,
|
||||
blockId: 'c'.repeat(64),
|
||||
blockTime: '2024-01-01T00:00:00.000',
|
||||
@@ -210,8 +220,13 @@ describe('BlockProcessor — ABI updates (Story 4.3)', () => {
|
||||
authorization: [],
|
||||
actRaw: new Uint8Array([1]),
|
||||
actionOrdinal: 1,
|
||||
creatorActionOrdinal: 0,
|
||||
globalSequence: BigInt(2),
|
||||
receipt: null,
|
||||
contextFree: false,
|
||||
elapsed: 0,
|
||||
console: '',
|
||||
accountRamDeltas: [],
|
||||
blockNum: 501,
|
||||
blockId: 'c'.repeat(64),
|
||||
blockTime: '2024-01-01T00:00:00.000',
|
||||
@@ -324,8 +339,8 @@ describe('BlockProcessor — native deltas (Epic 6)', () => {
|
||||
lastIrreversible: { blockNum: 20, blockId: 'b'.repeat(64) },
|
||||
prevBlock: null,
|
||||
traces: [
|
||||
{ account: 'eosio.token', name: 'transfer', authorization: [], actRaw: new Uint8Array([1]), actionOrdinal: 1, globalSequence: BigInt(1), receipt: null, blockNum: 20, blockId: 'b'.repeat(64), blockTime: '2024-01-01T00:00:00.000', transactionId: 'c'.repeat(64) },
|
||||
{ account: 'eosio.token', name: 'transfer', authorization: [], actRaw: new Uint8Array([1]), actionOrdinal: 2, globalSequence: BigInt(2), receipt: null, blockNum: 20, blockId: 'b'.repeat(64), blockTime: '2024-01-01T00:00:00.000', transactionId: 'c'.repeat(64) },
|
||||
{ account: 'eosio.token', name: 'transfer', authorization: [], actRaw: new Uint8Array([1]), actionOrdinal: 1, creatorActionOrdinal: 0, globalSequence: BigInt(1), receipt: null, contextFree: false, elapsed: 0, console: '', accountRamDeltas: [], blockNum: 20, blockId: 'b'.repeat(64), blockTime: '2024-01-01T00:00:00.000', transactionId: 'c'.repeat(64) },
|
||||
{ account: 'eosio.token', name: 'transfer', authorization: [], actRaw: new Uint8Array([1]), actionOrdinal: 2, creatorActionOrdinal: 0, globalSequence: BigInt(2), receipt: null, contextFree: false, elapsed: 0, console: '', accountRamDeltas: [], blockNum: 20, blockId: 'b'.repeat(64), blockTime: '2024-01-01T00:00:00.000', transactionId: 'c'.repeat(64) },
|
||||
],
|
||||
deltas: [
|
||||
{ name: 'contract_row', present: true, rowRaw: new Uint8Array([1]), code: 'eosio.token', scope: 'alice', table: 'accounts', primaryKey: '1' },
|
||||
@@ -355,7 +370,7 @@ describe('BlockProcessor — native deltas (Epic 6)', () => {
|
||||
head: { blockNum: 5, blockId: 'd'.repeat(64) },
|
||||
lastIrreversible: { blockNum: 5, blockId: 'd'.repeat(64) },
|
||||
prevBlock: null,
|
||||
traces: [{ account: 'eosio', name: 'newaccount', authorization: [], actRaw: new Uint8Array([1]), actionOrdinal: 1, globalSequence: BigInt(10), receipt: null, blockNum: 5, blockId: 'd'.repeat(64), blockTime: '2024-01-01T00:00:00.000', transactionId: 'e'.repeat(64) }],
|
||||
traces: [{ account: 'eosio', name: 'newaccount', authorization: [], actRaw: new Uint8Array([1]), actionOrdinal: 1, creatorActionOrdinal: 0, globalSequence: BigInt(10), receipt: null, contextFree: false, elapsed: 0, console: '', accountRamDeltas: [], blockNum: 5, blockId: 'd'.repeat(64), blockTime: '2024-01-01T00:00:00.000', transactionId: 'e'.repeat(64) }],
|
||||
deltas: [
|
||||
{ name: 'contract_row', present: true, rowRaw: new Uint8Array([1]), code: 'eosio', scope: 'eosio', table: 'global', primaryKey: '0' },
|
||||
{ name: 'permission' as never, present: true, rowRaw: new Uint8Array([1]) },
|
||||
|
||||
@@ -15,13 +15,19 @@ function makeAction(override: Partial<WithoutId<Extract<ParserEvent, { kind: 'ac
|
||||
block_num: 100,
|
||||
block_time: '2024-01-01T00:00:00.000',
|
||||
block_id: BLOCK_ID,
|
||||
transaction_id: 'tx0000000000000000000000000000000000000000000000000000000000000000',
|
||||
account: 'eosio.token',
|
||||
name: 'transfer',
|
||||
authorization: [],
|
||||
data: {},
|
||||
action_ordinal: 1,
|
||||
creator_action_ordinal: 0,
|
||||
global_sequence: 12345n,
|
||||
receipt: null,
|
||||
context_free: false,
|
||||
elapsed: 0,
|
||||
console: '',
|
||||
account_ram_deltas: [],
|
||||
...override,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,13 +10,19 @@ const actionEvent: ParserEvent = {
|
||||
kind: 'action',
|
||||
event_id: 'id1',
|
||||
...BLOCK,
|
||||
transaction_id: 'tx0000000000000000000000000000000000000000000000000000000000000000',
|
||||
account: 'eosio',
|
||||
name: 'updateauth',
|
||||
authorization: [],
|
||||
data: { from: 'alice', permission: 'active' },
|
||||
action_ordinal: 1,
|
||||
creator_action_ordinal: 0,
|
||||
global_sequence: 100n,
|
||||
receipt: null,
|
||||
context_free: false,
|
||||
elapsed: 0,
|
||||
console: '',
|
||||
account_ram_deltas: [],
|
||||
}
|
||||
|
||||
const deltaEvent: ParserEvent = {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Clean-room SHiP WebSocket client for EOSIO/Antelope blockchains. Streams blockchain blocks, deserializes actions and deltas via `@wharfkit/antelope`, with support for all 24 native system table delta types.
|
||||
|
||||
Primary consumer: [`@coopenomics/parser2`](https://github.com/coopenomics/parser).
|
||||
Primary consumer: [`@coopenomics/parser2`](https://github.com/coopenomics/parser2).
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@coopenomics/coopos-ship-reader",
|
||||
"version": "0.1.0",
|
||||
"version": "0.2.0",
|
||||
"description": "Clean-room SHiP WebSocket client for EOSIO/Antelope blockchains",
|
||||
"license": "MIT",
|
||||
"author": "Coopenomics contributors",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ABI, Serializer, Bytes } from '@wharfkit/antelope'
|
||||
import { ShipProtocolError } from './errors.js'
|
||||
import type { BlockPosition, ShipBlock, ShipTrace, ShipDelta, ActionReceipt, ActionAuthorization } from './types/ship.js'
|
||||
import type { BlockPosition, ShipBlock, ShipTrace, ShipDelta, ActionReceipt, ActionAuthorization, AuthSequence, AccountRamDelta } from './types/ship.js'
|
||||
|
||||
export type ShipAbi = ABI
|
||||
|
||||
@@ -61,7 +61,12 @@ interface RawActionTrace {
|
||||
}
|
||||
receipt: RawActionReceipt | null
|
||||
action_ordinal: number
|
||||
global_sequence: string
|
||||
creator_action_ordinal?: number
|
||||
global_sequence?: string
|
||||
context_free?: boolean
|
||||
elapsed?: string | number
|
||||
console?: string
|
||||
account_ram_deltas?: Array<{ account: string; delta: string | number }>
|
||||
}
|
||||
|
||||
interface RawActionReceipt {
|
||||
@@ -69,6 +74,9 @@ interface RawActionReceipt {
|
||||
act_digest: string
|
||||
global_sequence: string
|
||||
recv_sequence: string
|
||||
// auth_sequence — массив пар (account, sequence). wharfkit может отдать как
|
||||
// объект {account, sequence} или как кортеж [account, sequence].
|
||||
auth_sequence?: Array<{ account: string; sequence: string | number } | [string, string | number]>
|
||||
code_sequence: number
|
||||
abi_sequence: number
|
||||
}
|
||||
@@ -142,17 +150,28 @@ export function decodeBlocksResult(raw: unknown, abi: ShipAbi, blockNum: number,
|
||||
const receiptData = Array.isArray(at.receipt)
|
||||
? (at.receipt[1] as RawActionTrace['receipt'])
|
||||
: at.receipt
|
||||
const authSequence: AuthSequence[] = (receiptData?.auth_sequence ?? []).map(pair =>
|
||||
Array.isArray(pair)
|
||||
? { account: String(pair[0]), sequence: String(pair[1]) }
|
||||
: { account: String(pair.account), sequence: String(pair.sequence) },
|
||||
)
|
||||
const receipt: ActionReceipt | null = receiptData
|
||||
? ({
|
||||
receiver: String(receiptData.receiver),
|
||||
actDigest: String(receiptData.act_digest),
|
||||
globalSequence: BigInt(receiptData.global_sequence),
|
||||
recvSequence: BigInt(receiptData.recv_sequence),
|
||||
authSequence,
|
||||
codeSequence: receiptData.code_sequence,
|
||||
abiSequence: receiptData.abi_sequence,
|
||||
} as ActionReceipt)
|
||||
: null
|
||||
|
||||
const accountRamDeltas: AccountRamDelta[] = (at.account_ram_deltas ?? []).map(d => ({
|
||||
account: String(d.account),
|
||||
delta: Number(d.delta),
|
||||
}))
|
||||
|
||||
// action_trace_v1 не имеет top-level global_sequence (только в receipt).
|
||||
// Fallback: если at.global_sequence undefined — берём из receipt.
|
||||
const topGlobalSeq = at.global_sequence ?? receiptData?.global_sequence
|
||||
@@ -162,8 +181,13 @@ export function decodeBlocksResult(raw: unknown, abi: ShipAbi, blockNum: number,
|
||||
authorization,
|
||||
actRaw: at.act.data instanceof Bytes ? at.act.data.array : Uint8Array.from([]),
|
||||
actionOrdinal: at.action_ordinal,
|
||||
creatorActionOrdinal: at.creator_action_ordinal ?? 0,
|
||||
globalSequence: topGlobalSeq !== undefined ? BigInt(topGlobalSeq) : 0n,
|
||||
receipt,
|
||||
contextFree: at.context_free ?? false,
|
||||
elapsed: at.elapsed !== undefined ? Number(at.elapsed) : 0,
|
||||
console: at.console ?? '',
|
||||
accountRamDeltas,
|
||||
blockNum: thisBlock.blockNum,
|
||||
blockId: thisBlock.blockId,
|
||||
blockTime,
|
||||
|
||||
@@ -23,6 +23,8 @@ export type {
|
||||
BlockPosition,
|
||||
ActionReceipt,
|
||||
ActionAuthorization,
|
||||
AuthSequence,
|
||||
AccountRamDelta,
|
||||
} from './types/ship.js'
|
||||
|
||||
export type {
|
||||
|
||||
@@ -8,11 +8,22 @@ export interface ActionAuthorization {
|
||||
readonly permission: string
|
||||
}
|
||||
|
||||
export interface AuthSequence {
|
||||
readonly account: string
|
||||
readonly sequence: string
|
||||
}
|
||||
|
||||
export interface AccountRamDelta {
|
||||
readonly account: string
|
||||
readonly delta: number
|
||||
}
|
||||
|
||||
export interface ActionReceipt {
|
||||
readonly receiver: string
|
||||
readonly actDigest: string
|
||||
readonly globalSequence: bigint
|
||||
readonly recvSequence: bigint
|
||||
readonly authSequence: readonly AuthSequence[]
|
||||
readonly codeSequence: number
|
||||
readonly abiSequence: number
|
||||
}
|
||||
@@ -23,8 +34,13 @@ export interface ShipTrace {
|
||||
readonly authorization: readonly ActionAuthorization[]
|
||||
readonly actRaw: Uint8Array
|
||||
readonly actionOrdinal: number
|
||||
readonly creatorActionOrdinal: number
|
||||
readonly globalSequence: bigint
|
||||
readonly receipt: ActionReceipt | null
|
||||
readonly contextFree: boolean
|
||||
readonly elapsed: number
|
||||
readonly console: string
|
||||
readonly accountRamDeltas: readonly AccountRamDelta[]
|
||||
readonly blockNum: number
|
||||
readonly blockId: string
|
||||
readonly blockTime: string
|
||||
|
||||
Reference in New Issue
Block a user