Skip to content

Commit

Permalink
Merge pull request #131 from freespek/th/failed-tx
Browse files Browse the repository at this point in the history
Add support for fetching failed transactions
  • Loading branch information
thpani authored Sep 30, 2024
2 parents 32c6800 + 063548a commit b96c520
Show file tree
Hide file tree
Showing 9 changed files with 202 additions and 73 deletions.
7 changes: 7 additions & 0 deletions ContractExamples/contracts/setter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,13 @@ impl SetterContract {
old
}

pub fn set_bool_if_notset(env: Env) {
if env.storage().instance().has(&MY_BOOL) {
panic!("already set");
}
env.storage().instance().set(&MY_BOOL, &true);
}

pub fn set_u32(env: Env, v: u32) -> u32 {
let old: u32 = env.storage().instance().get(&MY_U32).unwrap_or(0);
env.storage().instance().set(&MY_U32, &v);
Expand Down
6 changes: 6 additions & 0 deletions ContractExamples/scripts/setter-populate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ NET=testnet
ACCOUNT=alice
soroban keys address $ACCOUNT || (echo "add the account $ACCOUNT via soroban keys generate"; exit 1)

ACCOUNT2=bob
soroban keys address $ACCOUNT2 || (echo "add the account $ACCOUNT2 via soroban keys generate"; exit 1)

set -x

Expand Down Expand Up @@ -59,3 +61,7 @@ soroban contract invoke --id $(cat .setter.id) --source $ACCOUNT --network $NET
-- set_enum --v '{ "B": "-200" }'
soroban contract invoke --id $(cat .setter.id) --source $ACCOUNT --network $NET \
-- remove_bool

# we can provoke a failed transaction by submitting 2 transactions in parallel from different accounts
soroban contract invoke --id $(cat .setter.id) --source $ACCOUNT --network $NET -- set_bool_if_notset &
soroban contract invoke --id $(cat .setter.id) --source $ACCOUNT2 --network $NET -- set_bool_if_notset
31 changes: 17 additions & 14 deletions solarkraft/src/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,27 +113,30 @@ export async function fetch(args: any) {
// initiate the streaming loop
const closeHandler = server
.operations()
.includeFailed(true)
.cursor(startCursor)
.stream({
onmessage: async (msg: any) => {
if (msg.transaction_successful) {
const callEntryMaybe = await extractContractCall(
msg,
(id) => contractId === id,
typemapJson
)
if (callEntryMaybe.isJust()) {
const entry = callEntryMaybe.value
console.log(`+ save: ${entry.height}`)
saveContractCallEntry(args.home, entry)
}
} // TODO(#61): else: shall we also store reverted transactions?
onmessage: async (op: any) => {
if (op.type !== 'invoke_host_function') {
return
}
op = op as Horizon.ServerApi.InvokeHostFunctionOperationRecord
const callEntryMaybe = await extractContractCall(
op,
(id) => contractId === id,
typemapJson
)
if (callEntryMaybe.isJust()) {
const entry = callEntryMaybe.value
console.log(`+ save: ${entry.height}`)
saveContractCallEntry(args.home, entry)
}

nEvents++
if (nEvents % HEIGHT_FETCHING_PERIOD === 0) {
// Fetch the height of the current message and persist it for the future runs.
// Note that messages may come slightly out of order, so the heights are not precise.
const tx = await msg.transaction()
const tx = await op.transaction()
lastHeight = Math.max(lastHeight, tx.ledger_attr)
console.log(`= at: ${lastHeight}`)
// Load and save the state. Other fetchers may work concurrently,
Expand Down
6 changes: 4 additions & 2 deletions solarkraft/src/fetcher/callDecoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* @license [Apache-2.0](https://github.com/freespek/solarkraft/blob/main/LICENSE)
*/

import sdk, { Address } from '@stellar/stellar-sdk'
import sdk, { Address, Horizon } from '@stellar/stellar-sdk'
import {
ContractCallEntry,
emptyContractStorage,
Expand All @@ -26,10 +26,11 @@ import { Maybe, just, none } from '@sweet-monads/maybe'
* @param matcher a quick matcher over the contractId to avoid expensive deserialization
*/
export async function extractContractCall(
op: any,
op: Horizon.ServerApi.InvokeHostFunctionOperationRecord,
matcher: (contractId: string) => boolean,
typemapJson: any = {}
): Promise<Maybe<ContractCallEntry>> {
// `op.function` can be one of HostFunctionTypeHostFunctionTypeInvokeContract, HostFunctionTypeHostFunctionTypeCreateContract, or HostFunctionTypeHostFunctionTypeUploadContractWasm.
// https://developers.stellar.org/network/horizon/api-reference/resources/operations/object/invoke-host-function
if (op.function !== 'HostFunctionTypeHostFunctionTypeInvokeContract') {
return none()
Expand Down Expand Up @@ -120,6 +121,7 @@ export async function extractContractCall(
height,
timestamp,
txHash,
txSuccess: op.transaction_successful,
contractId,
method,
methodArgs,
Expand Down
12 changes: 5 additions & 7 deletions solarkraft/src/fetcher/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ export interface ContractCallEntry {
* Transaction hash.
*/
txHash: string
/**
* Whether the transaction was successful or failed.
*/
txSuccess: boolean
/**
* The address of the contract being called.
*/
Expand Down Expand Up @@ -213,13 +217,7 @@ export function loadContractCallEntry(filename: string): ContractCallEntry {
const contents = readFileSync(filename)
const loaded = JSONbig.parse(contents)
return {
timestamp: loaded.timestamp as number,
height: loaded.height as number,
contractId: loaded.contractId as string,
txHash: loaded.txHash as string,
method: loaded.method as string,
methodArgs: loaded.methodArgs as any[],
returnValue: loaded.returnValue,
...loaded,
fields: OrderedMap<string, any>(loaded.fields),
oldFields: OrderedMap<string, any>(loaded.oldFields),
oldStorage: Immutable.fromJS(loaded.oldStorage),
Expand Down
1 change: 1 addition & 0 deletions solarkraft/test/e2e/list.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ describe('list', () => {
timestamp: 1716393856,
height: 1000,
txHash: TX_HASH,
txSuccess: true,
contractId: CONTRACT_ID,
method: 'set_i32',
methodArgs: [42],
Expand Down
Loading

0 comments on commit b96c520

Please sign in to comment.