From 3acac5185f0cdf64c33b5cfa10d25e6bbd7f9392 Mon Sep 17 00:00:00 2001 From: Patricio Tourne Passarino Date: Tue, 10 Feb 2026 18:27:48 -0300 Subject: [PATCH 1/6] feat: ticker test --- contracts/contracts/examples/ticker.tolk | 67 ++++++++++++++++ contracts/wrappers/examples.Ticker.compile.ts | 8 ++ .../tracetracking/integration_test.go | 77 +++++++++++++++++++ pkg/bindings/examples/ticker/ticker.go | 21 +++++ pkg/bindings/index.go | 4 + pkg/ton/codec/debug/example/ticker/ticker.go | 48 ++++++++++++ 6 files changed, 225 insertions(+) create mode 100644 contracts/contracts/examples/ticker.tolk create mode 100644 contracts/wrappers/examples.Ticker.compile.ts create mode 100644 pkg/bindings/examples/ticker/ticker.go create mode 100644 pkg/ton/codec/debug/example/ticker/ticker.go diff --git a/contracts/contracts/examples/ticker.tolk b/contracts/contracts/examples/ticker.tolk new file mode 100644 index 000000000..099644101 --- /dev/null +++ b/contracts/contracts/examples/ticker.tolk @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: MIT +tolk 1.2 + +/// This Contract just forwards a message to itself, decreasing its value by 1 each time, until it reaches 0. It is used to mock a long chain of messages. + +import "@stdlib/common.tolk" +import "../lib/utils" +import "../lib/access/ownable_2step" + +const CONTRACT_VERSION = "1.0.0"; + +/// Message to start the ticker. +struct (0xd4834e00) Ticker_Tick { + queryID: uint64 // Standard query_id field + times: uint32 // Number of ticks to perform before stopping. +} + +struct Storage { + id: uint32 +} + +fun Storage.load(): Storage { + return Storage.fromCell(contract.getData()); +} + +fun Storage.store(self) { + return contract.setData(self.toCell()); +} + +type Msg = Ticker_Tick + +fun onInternalMessage(in: InMessage) { + val msg = lazy Msg.fromSlice(in.body); // 63 error code is thrown if the message opcode is unknown + match (msg) { + Ticker_Tick => { + if (msg.times == 0) { // We leave negative range out intentionally to allow for unbounded loops if needed + return; + } + + // send tick to itself + val reply = createMessage({ + bounce: false, + value: 0, + dest: contract.getAddress(), + body: Ticker_Tick { + queryID: msg.queryID, + times: msg.times - 1, + }, + }); + reply.send(SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); + } + + else => { + assert (in.body.isEmpty()) throw 0xFFFF; + } + } +} +/// Gets the current id of the contract. +get fun id(): int { + val s = lazy Storage.load(); + return s.id; +} + +/// Gets the current type and version of the contract. +get fun typeAndVersion(): (slice, slice) { + return ("com.chainlink.ton.examples.Ticker", CONTRACT_VERSION) +} diff --git a/contracts/wrappers/examples.Ticker.compile.ts b/contracts/wrappers/examples.Ticker.compile.ts new file mode 100644 index 000000000..169b75b70 --- /dev/null +++ b/contracts/wrappers/examples.Ticker.compile.ts @@ -0,0 +1,8 @@ +import { CompilerConfig } from '@ton/blueprint' + +export const compile: CompilerConfig = { + lang: 'tolk', + entrypoint: 'contracts/examples/ticker.tolk', + withStackComments: true, // Fift output will contain comments, if you wish to debug its output + experimentalOptions: '', // you can pass experimental compiler options here +} diff --git a/integration-tests/tracetracking/integration_test.go b/integration-tests/tracetracking/integration_test.go index b17c7dec8..21912cea0 100644 --- a/integration-tests/tracetracking/integration_test.go +++ b/integration-tests/tracetracking/integration_test.go @@ -1,10 +1,14 @@ package tracetracking import ( + "context" + "fmt" "math/big" "math/rand/v2" + "strings" "sync" "testing" + "time" chainsel "github.com/smartcontractkit/chain-selectors" @@ -12,6 +16,8 @@ import ( "github.com/stretchr/testify/require" "github.com/xssnick/tonutils-go/address" "github.com/xssnick/tonutils-go/tlb" + "github.com/xssnick/tonutils-go/ton" + "github.com/xssnick/tonutils-go/ton/wallet" "github.com/smartcontractkit/chainlink-ton/integration-tests/tracetracking/async/wrappers/requestreply" "github.com/smartcontractkit/chainlink-ton/integration-tests/tracetracking/async/wrappers/requestreplywithtwodependencies" @@ -22,6 +28,7 @@ import ( "github.com/smartcontractkit/chainlink-ton/pkg/bindings" "github.com/smartcontractkit/chainlink-ton/pkg/bindings/examples/counter" + "github.com/smartcontractkit/chainlink-ton/pkg/bindings/examples/ticker" "github.com/smartcontractkit/chainlink-ton/pkg/ccip/bindings/ownable2step" "github.com/smartcontractkit/chainlink-ton/pkg/ton/tracetracking" "github.com/smartcontractkit/chainlink-ton/pkg/ton/tvm" @@ -620,4 +627,74 @@ func TestIntegration(t *testing.T) { t.Logf("Test completed successfully\n") }) + + t.Run("Ticker Delay", func(t *testing.T) { + const startingNumberOfTicks uint32 = 0 + const maxNumberOfTicks uint32 = 200 + const step uint32 = 50 + + t.Parallel() + + deployer := getAccount() + t.Logf("\n\n\n\n\n\nTest Setup\n==========================\n") + t.Logf("Deploying Ticker contract\n") + storage, err := tlb.ToCell(ticker.Storage{ + ID: getNextID(), + }) + require.NoError(t, err, "failed to serialize storage: %w", err) + buildPath := bindings.GetBuildDir("examples.Ticker.compiled.json") + compiledContract, err := wrappers.ParseCompiledContract(buildPath) + require.NoError(t, err, "failed to parse compiled contract: %w", err) + tickerContract, _, err := wrappers.Deploy(t.Context(), &deployer, compiledContract, storage, tlb.MustFromTON("0.05"), tvm.EmptyCell) + require.NoError(t, err, "failed to deploy Ticker contract: %w", err) + t.Logf("Ticker contract deployed at %s\n", tickerContract.Address.String()) + + t.Logf("\n\n\n\n\n\nTest Started\n==========================\n") + // for different number of ticks, measure the delay + + type measurement struct { + duration time.Duration + cost *tlb.Coins + } + measurements := make(map[uint32]measurement) + for numberOfTicks := startingNumberOfTicks; numberOfTicks <= maxNumberOfTicks; numberOfTicks += step { + t.Logf("Setting number of ticks to %d\n", numberOfTicks) + statingBalance := getBalance(t.Context(), t, deployer.Client, *tickerContract.Address) + amountToSend := tlb.MustFromTON("1.0") + startTime := time.Now() + trace, err := deployer.SendAndWaitForTrace(t.Context(), *tickerContract.Address, &wallet.Message{ + Mode: wallet.PayGasSeparately, + InternalMessage: &tlb.InternalMessage{ + DstAddr: tickerContract.Address, + Amount: amountToSend, + Body: must(tlb.ToCell(ticker.Tick{ + QueryID: uint64(numberOfTicks), + Times: numberOfTicks, + })), + }, + }) + duration := time.Since(startTime) + require.NoError(t, err, "failed to send Tick message: %w", err) + ec, err := trace.TraceExitCode() + require.NoError(t, err, "failed to get exit code from trace: %w", err) + require.Equal(t, tvm.ExitCodeSuccess, ec, "expected exit code 0, got %d", ec) + endBalance := getBalance(t.Context(), t, deployer.Client, *tickerContract.Address) + cost := must(must(endBalance.Sub(&statingBalance)).Sub(&amountToSend)) + measurements[numberOfTicks] = measurement{duration, cost} + } + var measurementsCSV strings.Builder + measurementsCSV.WriteString("number_of_ticks,duration_ms,cost\n") + for numberOfTicks, measurement := range measurements { + fmt.Fprintf(&measurementsCSV, "%d,%f,%s\n", numberOfTicks, measurement.duration.Seconds(), measurement.cost.String()) + } + t.Logf("Measurements:\n%s", measurementsCSV.String()) + }) +} + +func getBalance(ctx context.Context, t *testing.T, client ton.APIClientWrapped, addr address.Address) tlb.Coins { + block, err := client.CurrentMasterchainInfo(ctx) + require.NoError(t, err, "failed to get masterchain info: %w", err) + account, err := client.GetAccount(ctx, block, &addr) + require.NoError(t, err, "failed to get account: %w", err) + return account.State.Balance } diff --git a/pkg/bindings/examples/ticker/ticker.go b/pkg/bindings/examples/ticker/ticker.go new file mode 100644 index 000000000..703b0af86 --- /dev/null +++ b/pkg/bindings/examples/ticker/ticker.go @@ -0,0 +1,21 @@ +package ticker + +import ( + "github.com/xssnick/tonutils-go/tlb" + + "github.com/smartcontractkit/chainlink-ton/pkg/ton/tvm" +) + +type Storage struct { + ID uint32 `tlb:"## 32"` +} + +type Tick struct { + _ tlb.Magic `tlb:"#d4834e00" json:"-"` //nolint:revive // Ignore opcode tag + QueryID uint64 `tlb:"## 64"` + Times uint32 `tlb:"## 32"` // Number of ticks to perform before stopping. +} + +var TLBs = tvm.MustNewTLBMap([]any{ + Tick{}, +}).MustWithStorageType(Storage{}) diff --git a/pkg/bindings/index.go b/pkg/bindings/index.go index 561a3a266..dc5be16a8 100644 --- a/pkg/bindings/index.go +++ b/pkg/bindings/index.go @@ -1,6 +1,7 @@ package bindings import ( + "github.com/smartcontractkit/chainlink-ton/pkg/bindings/examples/ticker" "github.com/smartcontractkit/chainlink-ton/pkg/bindings/jetton/minter" "github.com/smartcontractkit/chainlink-ton/pkg/bindings/jetton/wallet" "github.com/smartcontractkit/chainlink-ton/pkg/bindings/lib/access/rbac" @@ -50,4 +51,7 @@ var Registry = tvm.ContractTLBRegistry{ PkgLib + ".access.RBAC": rbac.TLBs, PkgLib + ".funding.Withdrawable": withdrawable.TLBs, PkgLib + ".versioning.Upgradeable": upgradeable.TLBs, + + // Examples + PkgLib + ".examples.Ticker": ticker.TLBs, // Not sure if this should be here } diff --git a/pkg/ton/codec/debug/example/ticker/ticker.go b/pkg/ton/codec/debug/example/ticker/ticker.go new file mode 100644 index 000000000..8771125b4 --- /dev/null +++ b/pkg/ton/codec/debug/example/ticker/ticker.go @@ -0,0 +1,48 @@ +package ticker + +import ( + "github.com/xssnick/tonutils-go/address" + "github.com/xssnick/tonutils-go/tvm/cell" + + "github.com/smartcontractkit/chainlink-ton/pkg/bindings/examples/ticker" + "github.com/smartcontractkit/chainlink-ton/pkg/ccip/bindings/router" + + "github.com/smartcontractkit/chainlink-ton/pkg/ton/codec" + "github.com/smartcontractkit/chainlink-ton/pkg/ton/codec/debug/lib" + "github.com/smartcontractkit/chainlink-ton/pkg/ton/tvm" +) + +var TLBs = ticker.TLBs + +type decoder struct { + tlbsCtx tvm.TLBMap +} + +func NewDecoder(tlbsCtx tvm.TLBMap) lib.ContractDecoder { + return &decoder{tlbsCtx} +} + +func (d *decoder) ContractType() string { + return "com.chainlink.ton.examples.Ticker" +} + +func (d *decoder) EventInfo(dstAddr *address.Address, msg *cell.Cell) (lib.MessageInfo, error) { + return nil, codec.ErrUnknownMessage +} + +func (d *decoder) ExternalMessageInfo(msg *cell.Cell) (lib.MessageInfo, error) { + return nil, codec.ErrUnknownMessage +} + +func (d *decoder) InternalMessageInfo(msg *cell.Cell) (lib.MessageInfo, error) { + return lib.NewMessageInfoFromCell(d.ContractType(), msg, TLBs, d.tlbsCtx) +} + +func (d *decoder) ExitCodeInfo(exitCode tvm.ExitCode) (string, error) { + ec, err := router.ExitCodeCodec.NewFrom(exitCode) + if err != nil { + return "", codec.ErrUnknownMessage + } + + return ec.String(), nil +} From 1bb0885dacf98ea44b88622f36a16580dacf3cb1 Mon Sep 17 00:00:00 2001 From: Patricio Tourne Passarino Date: Tue, 10 Feb 2026 19:00:27 -0300 Subject: [PATCH 2/6] feat: make it concurrent --- .../tracetracking/integration_test.go | 158 ++++++++++++++---- 1 file changed, 122 insertions(+), 36 deletions(-) diff --git a/integration-tests/tracetracking/integration_test.go b/integration-tests/tracetracking/integration_test.go index 21912cea0..74b7faee2 100644 --- a/integration-tests/tracetracking/integration_test.go +++ b/integration-tests/tracetracking/integration_test.go @@ -630,62 +630,148 @@ func TestIntegration(t *testing.T) { t.Run("Ticker Delay", func(t *testing.T) { const startingNumberOfTicks uint32 = 0 - const maxNumberOfTicks uint32 = 200 + const maxNumberOfTicks uint32 = 2000 const step uint32 = 50 + const numWorkers = 10 + const measurementCount = (maxNumberOfTicks-startingNumberOfTicks)/step + 1 t.Parallel() - deployer := getAccount() t.Logf("\n\n\n\n\n\nTest Setup\n==========================\n") - t.Logf("Deploying Ticker contract\n") - storage, err := tlb.ToCell(ticker.Storage{ - ID: getNextID(), - }) - require.NoError(t, err, "failed to serialize storage: %w", err) + + // Deploy pool of ticker contracts, each with its own deployer account buildPath := bindings.GetBuildDir("examples.Ticker.compiled.json") compiledContract, err := wrappers.ParseCompiledContract(buildPath) require.NoError(t, err, "failed to parse compiled contract: %w", err) - tickerContract, _, err := wrappers.Deploy(t.Context(), &deployer, compiledContract, storage, tlb.MustFromTON("0.05"), tvm.EmptyCell) - require.NoError(t, err, "failed to deploy Ticker contract: %w", err) - t.Logf("Ticker contract deployed at %s\n", tickerContract.Address.String()) + + type worker struct { + deployer tracetracking.SignedAPIClient + tickerContract *address.Address + } + + deployers := make([]tracetracking.SignedAPIClient, numWorkers) + for i := range numWorkers { + deployer := getAccount() + deployers[i] = deployer + } + + workersCollector := make(chan worker, numWorkers) + var initWg sync.WaitGroup + for i, deployer := range deployers { + initWg.Add(1) + go func(i int) { + defer initWg.Done() + storage, err := tlb.ToCell(ticker.Storage{ + ID: getNextID(), + }) + require.NoError(t, err, "failed to serialize storage: %w", err) + tickerContract, _, err := wrappers.Deploy(t.Context(), &deployer, compiledContract, storage, tlb.MustFromTON("0.05"), tvm.EmptyCell) + require.NoError(t, err, "failed to deploy Ticker contract: %w", err) + workersCollector <- worker{ + deployer: deployer, + tickerContract: tickerContract.Address, + } + t.Logf("Ticker contract %d deployed at %s\n", i, tickerContract.Address.String()) + }(i) + } + + initWg.Wait() + close(workersCollector) + workers := make([]worker, 0, numWorkers) + for worker := range workersCollector { + workers = append(workers, worker) + } t.Logf("\n\n\n\n\n\nTest Started\n==========================\n") - // for different number of ticks, measure the delay type measurement struct { + ticks uint32 duration time.Duration cost *tlb.Coins } - measurements := make(map[uint32]measurement) + + // Thread-safe measurements map + var measurementsLock sync.Mutex + measurements := make(map[uint32]measurement, measurementCount) + + // Error channel for worker failures + errChan := make(chan error, numWorkers) + + // Create task channel + tasks := make(chan uint32, measurementCount) for numberOfTicks := startingNumberOfTicks; numberOfTicks <= maxNumberOfTicks; numberOfTicks += step { - t.Logf("Setting number of ticks to %d\n", numberOfTicks) - statingBalance := getBalance(t.Context(), t, deployer.Client, *tickerContract.Address) - amountToSend := tlb.MustFromTON("1.0") - startTime := time.Now() - trace, err := deployer.SendAndWaitForTrace(t.Context(), *tickerContract.Address, &wallet.Message{ - Mode: wallet.PayGasSeparately, - InternalMessage: &tlb.InternalMessage{ - DstAddr: tickerContract.Address, - Amount: amountToSend, - Body: must(tlb.ToCell(ticker.Tick{ - QueryID: uint64(numberOfTicks), - Times: numberOfTicks, - })), - }, - }) - duration := time.Since(startTime) - require.NoError(t, err, "failed to send Tick message: %w", err) - ec, err := trace.TraceExitCode() - require.NoError(t, err, "failed to get exit code from trace: %w", err) - require.Equal(t, tvm.ExitCodeSuccess, ec, "expected exit code 0, got %d", ec) - endBalance := getBalance(t.Context(), t, deployer.Client, *tickerContract.Address) - cost := must(must(endBalance.Sub(&statingBalance)).Sub(&amountToSend)) - measurements[numberOfTicks] = measurement{duration, cost} + tasks <- numberOfTicks + } + close(tasks) + + // Worker pool + var wg sync.WaitGroup + for workerID, w := range workers { + wg.Add(1) + go func(workerID int, w worker) { + defer wg.Done() + innerMeasurements := make([]measurement, 0, measurementCount) + for numberOfTicks := range tasks { + t.Logf("Worker %d processing %d ticks\n", workerID, numberOfTicks) + + statingBalance := getBalance(t.Context(), t, w.deployer.Client, *w.tickerContract) + amountToSend := tlb.MustFromTON("1.0") + startTime := time.Now() + trace, err := w.deployer.SendAndWaitForTrace(t.Context(), *w.tickerContract, &wallet.Message{ + Mode: wallet.PayGasSeparately, + InternalMessage: &tlb.InternalMessage{ + DstAddr: w.tickerContract, + Amount: amountToSend, + Body: must(tlb.ToCell(ticker.Tick{ + QueryID: uint64(numberOfTicks), + Times: numberOfTicks, + })), + }, + }) + duration := time.Since(startTime) + if err != nil { + errChan <- fmt.Errorf("worker %d: failed to send Tick message: %w", workerID, err) + return + } + ec, err := trace.TraceExitCode() + if err != nil { + errChan <- fmt.Errorf("worker %d: failed to get exit code from trace: %w", workerID, err) + return + } + if ec != tvm.ExitCodeSuccess { + errChan <- fmt.Errorf("worker %d: expected exit code 0, got %d", workerID, ec) + return + } + endBalance := getBalance(t.Context(), t, w.deployer.Client, *w.tickerContract) + cost := must(must(endBalance.Sub(&statingBalance)).Sub(&amountToSend)) + + innerMeasurements = append(innerMeasurements, measurement{numberOfTicks, duration, cost}) + + t.Logf("Worker %d completed %d ticks in %v\n", workerID, numberOfTicks, duration) + } + t.Logf("Worker %d finished processing\n", workerID) + + measurementsLock.Lock() + for _, measurement := range innerMeasurements { + measurements[measurement.ticks] = measurement + } + measurementsLock.Unlock() + t.Logf("Worker %d merged measurements\n", workerID) + }(workerID, w) } + + wg.Wait() + close(errChan) + + // Check for any errors from workers + for err := range errChan { + require.NoError(t, err) + } + var measurementsCSV strings.Builder measurementsCSV.WriteString("number_of_ticks,duration_ms,cost\n") for numberOfTicks, measurement := range measurements { - fmt.Fprintf(&measurementsCSV, "%d,%f,%s\n", numberOfTicks, measurement.duration.Seconds(), measurement.cost.String()) + fmt.Fprintf(&measurementsCSV, "%d,%.3f,%s\n", numberOfTicks, measurement.duration.Seconds(), measurement.cost.String()) } t.Logf("Measurements:\n%s", measurementsCSV.String()) }) From 504c1530f65ee95906ad72129947135b9f8dc20e Mon Sep 17 00:00:00 2001 From: Patricio Tourne Passarino Date: Tue, 10 Feb 2026 19:44:20 -0300 Subject: [PATCH 3/6] wip: fix: cost calculation number_of_ticks,duration_ms,cost 50,4.125,-0.952049999 100,5.659,-1.805099998 700,37.051,-12.041699998 300,8.139,-5.217299997 800,43.707,-13.747799998 850,50.050,-14.600849999 450,12.851,-7.776449997 950,59.973,-16.306949998 150,5.877,-2.658149998 650,33.827,-11.188649998 400,10.823,-6.923399997 900,54.453,-15.453899998 0,3.859,-0.098999999 500,22.240,-8.629499998 1000,60.836,-17.159999997 550,25.460,-9.482549998 250,7.200,-4.364249997 750,41.423,-12.894749998 600,28.948,-10.335599999 200,6.426,-3.511199998 350,9.386,-6.070349997 --- .../tracetracking/integration_test.go | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/integration-tests/tracetracking/integration_test.go b/integration-tests/tracetracking/integration_test.go index 74b7faee2..66d9238f7 100644 --- a/integration-tests/tracetracking/integration_test.go +++ b/integration-tests/tracetracking/integration_test.go @@ -629,12 +629,15 @@ func TestIntegration(t *testing.T) { }) t.Run("Ticker Delay", func(t *testing.T) { - const startingNumberOfTicks uint32 = 0 - const maxNumberOfTicks uint32 = 2000 - const step uint32 = 50 + const startingNumberOfTicks uint64 = 0 + const maxNumberOfTicks uint64 = 1000 + const step uint64 = 50 const numWorkers = 10 const measurementCount = (maxNumberOfTicks-startingNumberOfTicks)/step + 1 + costPerTick := tlb.MustFromTON("0.01") + baseCost := tlb.MustFromTON("0.05") + t.Parallel() t.Logf("\n\n\n\n\n\nTest Setup\n==========================\n") @@ -685,20 +688,20 @@ func TestIntegration(t *testing.T) { t.Logf("\n\n\n\n\n\nTest Started\n==========================\n") type measurement struct { - ticks uint32 + ticks uint64 duration time.Duration cost *tlb.Coins } // Thread-safe measurements map var measurementsLock sync.Mutex - measurements := make(map[uint32]measurement, measurementCount) + measurements := make(map[uint64]measurement, measurementCount) // Error channel for worker failures errChan := make(chan error, numWorkers) // Create task channel - tasks := make(chan uint32, measurementCount) + tasks := make(chan uint64, measurementCount) for numberOfTicks := startingNumberOfTicks; numberOfTicks <= maxNumberOfTicks; numberOfTicks += step { tasks <- numberOfTicks } @@ -712,38 +715,41 @@ func TestIntegration(t *testing.T) { defer wg.Done() innerMeasurements := make([]measurement, 0, measurementCount) for numberOfTicks := range tasks { + log := func(format string, args ...any) { + errChan <- fmt.Errorf("worker %d, task %d: %s", workerID, numberOfTicks, fmt.Sprintf(format, args...)) + } t.Logf("Worker %d processing %d ticks\n", workerID, numberOfTicks) statingBalance := getBalance(t.Context(), t, w.deployer.Client, *w.tickerContract) - amountToSend := tlb.MustFromTON("1.0") + amountToSend := must(must(costPerTick.Mul(big.NewInt(int64(numberOfTicks)))).Add(&baseCost)) startTime := time.Now() trace, err := w.deployer.SendAndWaitForTrace(t.Context(), *w.tickerContract, &wallet.Message{ Mode: wallet.PayGasSeparately, InternalMessage: &tlb.InternalMessage{ DstAddr: w.tickerContract, - Amount: amountToSend, + Amount: *amountToSend, Body: must(tlb.ToCell(ticker.Tick{ - QueryID: uint64(numberOfTicks), - Times: numberOfTicks, + QueryID: numberOfTicks, + Times: uint32(numberOfTicks), })), }, }) duration := time.Since(startTime) if err != nil { - errChan <- fmt.Errorf("worker %d: failed to send Tick message: %w", workerID, err) + log("failed to send Tick message: %w", err) return } ec, err := trace.TraceExitCode() if err != nil { - errChan <- fmt.Errorf("worker %d: failed to get exit code from trace: %w", workerID, err) + log("failed to get exit code from trace: %w", err) return } if ec != tvm.ExitCodeSuccess { - errChan <- fmt.Errorf("worker %d: expected exit code 0, got %d", workerID, ec) + log("expected exit code 0, got %d", ec) return } endBalance := getBalance(t.Context(), t, w.deployer.Client, *w.tickerContract) - cost := must(must(endBalance.Sub(&statingBalance)).Sub(&amountToSend)) + cost := must(must(statingBalance.Sub(&endBalance)).Sub(amountToSend)) innerMeasurements = append(innerMeasurements, measurement{numberOfTicks, duration, cost}) From 1d0fd80830f947ba12aeaee2a46d04ec38279490 Mon Sep 17 00:00:00 2001 From: Patricio Tourne Passarino Date: Wed, 11 Feb 2026 09:37:07 -0300 Subject: [PATCH 4/6] fix: cost calculation number_of_ticks,duration_ms,cost 50,5.302,0.147950001 550,24.484,1.617450003 250,7.316,0.735750003 750,41.098,2.205250004 800,46.251,2.352200003 350,9.356,1.029650003 850,51.968,2.499150003 0,5.043,0.001000001 100,5.811,0.294900002 600,27.744,1.764400003 150,6.047,0.441850002 650,32.649,1.911350004 200,6.558,0.588800002 400,11.005,1.176600003 450,12.821,1.323550003 1000,66.474,2.940000003 300,8.312,0.882700003 900,57.372,2.646100002 950,64.006,2.793050002 500,21.438,1.470500003 700,36.276,2.058300004 --- integration-tests/tracetracking/integration_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-tests/tracetracking/integration_test.go b/integration-tests/tracetracking/integration_test.go index 66d9238f7..0ee339a52 100644 --- a/integration-tests/tracetracking/integration_test.go +++ b/integration-tests/tracetracking/integration_test.go @@ -749,7 +749,7 @@ func TestIntegration(t *testing.T) { return } endBalance := getBalance(t.Context(), t, w.deployer.Client, *w.tickerContract) - cost := must(must(statingBalance.Sub(&endBalance)).Sub(amountToSend)) + cost := must(must(statingBalance.Add(amountToSend)).Sub(&endBalance)) innerMeasurements = append(innerMeasurements, measurement{numberOfTicks, duration, cost}) From ea0f7dc3aadb26f5a1ec98ca3d76b3611df88ccb Mon Sep 17 00:00:00 2001 From: Patricio Tourne Passarino Date: Wed, 11 Feb 2026 09:43:50 -0300 Subject: [PATCH 5/6] feat: results sorted --- .../tracetracking/integration_test.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/integration-tests/tracetracking/integration_test.go b/integration-tests/tracetracking/integration_test.go index 0ee339a52..8fcd6b259 100644 --- a/integration-tests/tracetracking/integration_test.go +++ b/integration-tests/tracetracking/integration_test.go @@ -5,6 +5,7 @@ import ( "fmt" "math/big" "math/rand/v2" + "slices" "strings" "sync" "testing" @@ -775,8 +776,17 @@ func TestIntegration(t *testing.T) { } var measurementsCSV strings.Builder - measurementsCSV.WriteString("number_of_ticks,duration_ms,cost\n") - for numberOfTicks, measurement := range measurements { + measurementsCSV.WriteString("number_of_ticks,duration_s,cost\n") + + // Sort measurements by number of ticks + sortedTicks := make([]uint64, 0, len(measurements)) + for numberOfTicks := range measurements { + sortedTicks = append(sortedTicks, numberOfTicks) + } + slices.Sort(sortedTicks) + + for _, numberOfTicks := range sortedTicks { + measurement := measurements[numberOfTicks] fmt.Fprintf(&measurementsCSV, "%d,%.3f,%s\n", numberOfTicks, measurement.duration.Seconds(), measurement.cost.String()) } t.Logf("Measurements:\n%s", measurementsCSV.String()) From e0fa6c53b03101717b70e9ed512570ec5c1170e6 Mon Sep 17 00:00:00 2001 From: Patricio Tourne Passarino Date: Wed, 11 Feb 2026 11:59:53 -0300 Subject: [PATCH 6/6] wip: csv --- integration-tests/tracetracking/ticker.csv | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 integration-tests/tracetracking/ticker.csv diff --git a/integration-tests/tracetracking/ticker.csv b/integration-tests/tracetracking/ticker.csv new file mode 100644 index 000000000..5fe148198 --- /dev/null +++ b/integration-tests/tracetracking/ticker.csv @@ -0,0 +1,22 @@ +number_of_ticks,duration_ms,cost +50,5.302,0.147950001 +550,24.484,1.617450003 +250,7.316,0.735750003 +750,41.098,2.205250004 +800,46.251,2.352200003 +350,9.356,1.029650003 +850,51.968,2.499150003 +0,5.043,0.001000001 +100,5.811,0.294900002 +600,27.744,1.764400003 +150,6.047,0.441850002 +650,32.649,1.911350004 +200,6.558,0.588800002 +400,11.005,1.176600003 +450,12.821,1.323550003 +1000,66.474,2.940000003 +300,8.312,0.882700003 +900,57.372,2.646100002 +950,64.006,2.793050002 +500,21.438,1.470500003 +700,36.276,2.058300004 \ No newline at end of file