Skip to content

Configurable native stack size for Stylus with auto-retry on overflow#4538

Open
bragaigor wants to merge 26 commits intomasterfrom
nm004-config-stylus-stack-size
Open

Configurable native stack size for Stylus with auto-retry on overflow#4538
bragaigor wants to merge 26 commits intomasterfrom
nm004-config-stylus-stack-size

Conversation

@bragaigor
Copy link
Copy Markdown
Contributor

@bragaigor bragaigor commented Mar 20, 2026

  • Add --stylus-target.native-stack-size node config to set the initial Wasmer coroutine stack size for Stylus execution (default: 0 = Wasmer's 1 MB default)
  • Add NativeStackOverflow variant to UserOutcome/UserOutcomeKind to distinguish retriable native overflows from deterministic OutOfStack (DepthChecker)
  • On native stack overflow with --stylus-target.allow-fallback=true (default):
    a. Recompile the program with Cranelift and retry
    b. Persist the Cranelift-compiled ASM to the wasm store so subsequent overflows skip recompilation
    c. If Cranelift also overflows, double the stack size once (capped at 100 MB) and retry with Cranelift
    d. If still overflowing, fail gracefully as out-of-stack
  • With --stylus-target.allow-fallback=false, no retry is attempted on overflow
  • Off-chain calls (eth_call, gas estimation) do not trigger retries or Cranelift compilation
  • Add new WasmTarget variants for Cranelift ASM storage (arm64-cranelift, amd64-cranelift, host-cranelift) in go-ethereum

pulls in OffchainLabs/go-ethereum#645
pulls in OffchainLabs/wasmer#37
closes NIT-4686

Signed-off-by: Igor Braga <5835477+bragaigor@users.noreply.github.com>
Signed-off-by: Igor Braga <5835477+bragaigor@users.noreply.github.com>
@codecov
Copy link
Copy Markdown

codecov bot commented Mar 20, 2026

Codecov Report

❌ Patch coverage is 9.15179% with 407 lines in your changes missing coverage. Please review.
✅ Project coverage is 34.10%. Comparing base (620aa42) to head (c5e37cc).

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4538      +/-   ##
==========================================
- Coverage   35.08%   34.10%   -0.98%     
==========================================
  Files         498      498              
  Lines       59096    59509     +413     
==========================================
- Hits        20735    20298     -437     
- Misses      34651    35628     +977     
+ Partials     3710     3583     -127     

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 20, 2026

❌ 10 Tests Failed:

Tests completed Failed Passed Skipped
4792 10 4782 0
View the top 3 failed tests by shortest run time
TestAliasingFlaky
Stack Traces | -0.000s run time
=== RUN   TestAliasingFlaky
=== PAUSE TestAliasingFlaky
=== CONT  TestAliasingFlaky
    common_test.go:777: BuildL1 deployConfig: DeployBold=true, DeployReferenceDAContracts=false
TestBatchPosterL1SurplusMatchesBatchGasFlaky
Stack Traces | 0.550s run time
... [CONTENT TRUNCATED: Keeping last 20 lines]
panic: runtime error: invalid memory address or nil pointer dereference [recovered, repanicked]
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x207dc92]

goroutine 55 [running]:
testing.tRunner.func1.2({0x37e5960, 0x61fc9e0})
	/opt/hostedtoolcache/go/1.25.8/x64/src/testing/testing.go:1872 +0x237
testing.tRunner.func1()
	/opt/hostedtoolcache/go/1.25.8/x64/src/testing/testing.go:1875 +0x35b
panic({0x37e5960?, 0x61fc9e0?})
	/opt/hostedtoolcache/go/1.25.8/x64/src/runtime/panic.go:783 +0x132
github.com/offchainlabs/nitro/arbnode.(*InboxTracker).GetBatchCount(0x1079900?)
	/home/runner/work/nitro/nitro/arbnode/inbox_tracker.go:210 +0x12
github.com/offchainlabs/nitro/arbnode.(*InboxTracker).FindInboxBatchContainingMessage(0x0, 0x7)
	/home/runner/work/nitro/nitro/arbnode/inbox_tracker.go:225 +0x2f
github.com/offchainlabs/nitro/system_tests.TestBatchPosterL1SurplusMatchesBatchGasFlaky(0xc00048afc0)
	/home/runner/work/nitro/nitro/system_tests/batch_poster_test.go:839 +0x725
testing.tRunner(0xc00048afc0, 0x41b81d8)
	/opt/hostedtoolcache/go/1.25.8/x64/src/testing/testing.go:1934 +0xea
created by testing.(*T).Run in goroutine 1
	/opt/hostedtoolcache/go/1.25.8/x64/src/testing/testing.go:1997 +0x465
TestParentChainEthConfigForkTransition
Stack Traces | 13.670s run time
... [CONTENT TRUNCATED: Keeping last 20 lines]
INFO [04-02|01:31:41.608] Submitted transaction                    hash=0xbc8dd0a8495b860a963a5d48cb8a4ea47c4993f04a4acace34ee1d6225deeb19 from=0xaF24Ca6c2831f4d4F629418b50C227DF0885613A nonce=43  recipient=0xaF24Ca6c2831f4d4F629418b50C227DF0885613A value=1
WARN [04-02|01:31:41.609] error applying transaction               tx="{\"type\":\"0x2\",\"chainId\":\"0x64aba\",\"nonce\":\"0x10e\",\"to\":\"0x0c709f340f0bb2e361229e345b7e26999d0969ab\",\"gas\":\"0x7a1200\",\"gasPrice\":\"0x0\",\"maxPriorityFeePerGas\":\"0xbebc200\",\"maxFeePerGas\":\"0xbebc200\",\"value\":\"0xe8d4a51000\",\"input\":\"0x\",\"accessList\":[],\"v\":\"0x0\",\"r\":\"0xdf4963485a7283d0654d893448fa218303e41e959f161d1347a0a56d6b98c0e4\",\"s\":\"0x4f968ee6eb6659dabed2006229e9299f053ccd70551e95a1d8a231a1ec627bb9\",\"yParity\":\"0x0\",\"hash\":\"0x2712e5c45d81800feae08d549320a6c995bc9c0e3d3f7eb5b306a8455ff58d59\"}" err="Storage root hash condition not met"
INFO [04-02|01:31:41.610] Starting work on payload                 id=0x03a476df031e01fb
WARN [04-02|01:31:41.610] Served eth_sendRawTransactionConditional reqid=1917  duration=5.089081ms    err="Storage root hash condition not met"
INFO [04-02|01:31:41.611] Updated payload                          id=0x03a476df031e01fb number=76  hash=0ce84c..d12127 txs=1   withdrawals=0 gas=21000      fees=0.002099852989 root=976208..34b8ca elapsed=1.047ms
�[90mposted new batch 67�[0;0m
INFO [04-02|01:31:41.612] Stopping work on payload                 id=0x03a476df031e01fb reason=delivery
WARN [04-02|01:31:41.612] error applying transaction               tx="{\"type\":\"0x2\",\"chainId\":\"0x64aba\",\"nonce\":\"0x10e\",\"to\":\"0x0c709f340f0bb2e361229e345b7e26999d0969ab\",\"gas\":\"0x7a1200\",\"gasPrice\":\"0x0\",\"maxPriorityFeePerGas\":\"0xbebc200\",\"maxFeePerGas\":\"0xbebc200\",\"value\":\"0xe8d4a51000\",\"input\":\"0x\",\"accessList\":[],\"v\":\"0x0\",\"r\":\"0xdf4963485a7283d0654d893448fa218303e41e959f161d1347a0a56d6b98c0e4\",\"s\":\"0x4f968ee6eb6659dabed2006229e9299f053ccd70551e95a1d8a231a1ec627bb9\",\"yParity\":\"0x0\",\"hash\":\"0x2712e5c45d81800feae08d549320a6c995bc9c0e3d3f7eb5b306a8455ff58d59\"}" err="Storage root hash condition not met"
INFO [04-02|01:31:41.613] Imported new potential chain segment     number=76  hash=0ce84c..d12127 blocks=1  txs=1   mgas=0.021  elapsed=1.362ms      mgasps=15.409   triediffs=336.54KiB  triedirty=0.00B
WARN [04-02|01:31:41.613] Served eth_sendRawTransactionConditional reqid=1918  duration=2.322203ms    err="Storage root hash condition not met"
INFO [04-02|01:31:41.613] Chain head was updated                   number=76  hash=0ce84c..d12127 root=976208..34b8ca elapsed="78.667µs"
INFO [04-02|01:31:41.614] Waiting background transaction indexer to exit
INFO [04-02|01:31:41.614] Submitted transaction                    hash=0x344bdc4fe04c921bd0b9b1d3063b47ed1ef308dbb693ad6cb93d7e69f96321bd from=0xaF24Ca6c2831f4d4F629418b50C227DF0885613A nonce=958 recipient=0x0C709F340F0BB2e361229e345B7e26999d0969Ab value=1
INFO [04-02|01:31:41.614] Writing cached state to disk             block=3   hash=61420f..7126ee root=4503cc..7b6a94
WARN [04-02|01:31:41.614] Getting file info                        dir= error="stat : no such file or directory"
INFO [04-02|01:31:41.615] Persisted trie from memory database      nodes=89   flushnodes=0 size=11.22KiB  flushsize=0.00B time="560.206µs"  flushtime=0s gcnodes=0 gcsize=0.00B gctime=0s          livenodes=35   livesize=6.30KiB
INFO [04-02|01:31:41.615] Writing cached state to disk             block=2   hash=c92b2d..351ca9 root=c7dc87..011c1f
INFO [04-02|01:31:41.615] Started log indexer
INFO [04-02|01:31:41.615] Persisted trie from memory database      nodes=18   flushnodes=0 size=3.12KiB   flushsize=0.00B time="138.329µs"  flushtime=0s gcnodes=0 gcsize=0.00B gctime=0s          livenodes=17   livesize=3.19KiB
INFO [04-02|01:31:41.615] Writing cached state to disk             block=1   hash=077c43..d9306f root=c349f6..9b02e2

📣 Thoughts on this report? Let Codecov know! | Powered by Codecov

Signed-off-by: Igor Braga <5835477+bragaigor@users.noreply.github.com>
Signed-off-by: Igor Braga <5835477+bragaigor@users.noreply.github.com>
Signed-off-by: Igor Braga <5835477+bragaigor@users.noreply.github.com>
Signed-off-by: Igor Braga <5835477+bragaigor@users.noreply.github.com>
Signed-off-by: Igor Braga <5835477+bragaigor@users.noreply.github.com>
Signed-off-by: Igor Braga <5835477+bragaigor@users.noreply.github.com>
Signed-off-by: Igor Braga <5835477+bragaigor@users.noreply.github.com>
Signed-off-by: Igor Braga <5835477+bragaigor@users.noreply.github.com>
Signed-off-by: Igor Braga <5835477+bragaigor@users.noreply.github.com>
Signed-off-by: Igor Braga <5835477+bragaigor@users.noreply.github.com>
@bragaigor bragaigor marked this pull request as ready for review March 30, 2026 22:22
@bragaigor bragaigor marked this pull request as draft March 31, 2026 00:46
Signed-off-by: Igor Braga <5835477+bragaigor@users.noreply.github.com>
Signed-off-by: Igor Braga <5835477+bragaigor@users.noreply.github.com>
Signed-off-by: Igor Braga <5835477+bragaigor@users.noreply.github.com>
Signed-off-by: Igor Braga <5835477+bragaigor@users.noreply.github.com>
Signed-off-by: Igor Braga <5835477+bragaigor@users.noreply.github.com>
Signed-off-by: Igor Braga <5835477+bragaigor@users.noreply.github.com>
Signed-off-by: Igor Braga <5835477+bragaigor@users.noreply.github.com>
Comment on lines +496 to +499
if status == userNativeStackOverflow {
return nil, fmt.Errorf("%w (program=%v, module=%v, depth=%d, allowFallback=%v, onChain=%v)",
ErrNativeStackOverflow, address, moduleHash, depth, GetAllowFallback(), runCtx.IsExecutedOnChain())
}
Copy link
Copy Markdown
Contributor Author

@bragaigor bragaigor Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we want to return error on all userNativeStackOverflow or only when status == userNativeStackOverflow && (!GetAllowFallback() || !runCtx.IsExecutedOnChain())? Or do we want to panic here?

@bragaigor bragaigor marked this pull request as ready for review April 1, 2026 13:26
@bragaigor bragaigor requested a review from magicxyyz April 1, 2026 13:27
"program", address, "module", moduleHash)
return userNativeStackOverflow, nil
}
if !runCtx.IsExecutedOnChain() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IsExecutedOnChain is true for when the call is executed in:

  • messageCommitMode - new synced/sequenced block
  • messageRecordingMode - block re-executed to record preimages required to create ValidationInput
  • messageReplayMode - block/transaction is executed i.e. in trace RPC call

IsExecutedOnChain is means:

these message modes are executed onchain so cannot make any gas shortcuts

https://github.com/OffchainLabs/go-ethereum/blob/461e5177cdbb8d237702dbb889ed2db19515f8c7/core/state_transition.go#L296-L299

I am not sure if we want to use fallback in any other mode then messageCommitMode. Do we need to support fallback in messageRecordingMode @tsahee ?

// doubling path. No corruption or crash results.
baseStackSize := configuredNativeStackSize.Load()
defer func() {
SetNativeStackSize(baseStackSize)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure yet, but I think that there might be an issue with setting stack size globally.

Because IsExecutedOnChain is true at this point the concurrent execution of trace calls can possibly mess with the head block execution.

If we decide that we want to allow fallback only when IsCommitMode is true, then I think we should have some guarantee that only one thread will set the stack size. That still will mess with stack size used by block recording thread and RPC calls - not sure how serious is that.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me give some more context.

SetNativeStackSize is process-wide — it modifies a global AtomicUsize (DEFAULT_STACK_SIZE) in Wasmer shared across all threads.
DrainStackPool is best-effort (lock-free queue). So yes, when retryOnStackOverflow doubles the stack, every concurrent Stylus execution in the process is affected.

Concurrent execution paths that share this global:

  ┌──────────────────────────────────┬───────────────────┬──────────────┬───────────────────────────────────────┐
  │             Context              │ IsExecutedOnChain │ IsCommitMode │              Concurrent?              │
  ├──────────────────────────────────┼───────────────────┼──────────────┼───────────────────────────────────────┤
  │ Sequencer (block production)     │ true              │ true         │ Yes, with all below                   │
  ├──────────────────────────────────┼───────────────────┼──────────────┼───────────────────────────────────────┤
  │ Block validation (non-sequencer) │ true              │ true         │ Yes                                   │
  ├──────────────────────────────────┼───────────────────┼──────────────┼───────────────────────────────────────┤
  │ Debug trace (debug_traceBlock)   │ true              │ false        │ Highly — runtime.NumCPU() goroutines  │
  ├──────────────────────────────────┼───────────────────┼──────────────┼───────────────────────────────────────┤
  │ Block recording (proofs)         │ true              │ false        │ Yes                                   │
  ├──────────────────────────────────┼───────────────────┼──────────────┼───────────────────────────────────────┤
  │ eth_call / gas estimation        │ false             │ false        │ Yes, but fallback is already disabled │
  └──────────────────────────────────┴───────────────────┴──────────────┴───────────────────────────────────────┘

The key issue: debug trace calls run with IsExecutedOnChain=true and spawn multiple goroutines sharing one runCtx. If the sequencer doubles the stack during block production, concurrent trace goroutines may see the doubled size (benign — they just get more stack than needed). If the sequencer's defer restores the base size while a trace goroutine is about to allocate a coroutine, that goroutine gets the smaller stack and may overflow again — but that's equivalent to the trace never seeing the doubled value. No corruption, just a duplicate overflow.

Lets take 2 threads example in a happy path:

  ┌──────┬──────────────────────────────────┬─────────────────────────────────────┬──────────────────────┐    
  │ Step │                G1                │                 G2                  │  DEFAULT_STACK_SIZE  │    
  ├──────┼──────────────────────────────────┼─────────────────────────────────────┼──────────────────────┤    
  │ 1    │ SetNativeStackSize(2MB)          │                                     │ 2MB                  │    
  ├──────┼──────────────────────────────────┼─────────────────────────────────────┼──────────────────────┤    
  │ 2    │ DrainStackPool()                 │                                     │ 2MB                  │    
  ├──────┼──────────────────────────────────┼─────────────────────────────────────┼──────────────────────┤
  │ 3    │ doStylusCall() → enters Rust     │                                     │ 2MB                  │    
  ├──────┼──────────────────────────────────┼─────────────────────────────────────┼──────────────────────┤    
  │ 4    │ catch_traps: reads 2MB, allocs   │                                     │ 2MB                  │
  │      │ 2MB stack                        │                                     │                      │    
  ├──────┼──────────────────────────────────┼─────────────────────────────────────┼──────────────────────┤
  │ 5    │ executing wasm...                │ SetNativeStackSize(2MB)             │ 2MB (no-op, same     │    
  │      │                                  │                                     │ value)               │
  ├──────┼──────────────────────────────────┼─────────────────────────────────────┼──────────────────────┤    
  │ 6    │ executing wasm...                │ DrainStackPool()                    │ 2MB                  │
  ├──────┼──────────────────────────────────┼─────────────────────────────────────┼──────────────────────┤    
  │ 7    │ executing wasm...                │ doStylusCall() → enters Rust        │ 2MB                  │
  ├──────┼──────────────────────────────────┼─────────────────────────────────────┼──────────────────────┤
  │ 8    │ executing wasm...                │ catch_traps: reads 2MB,             │ 2MB                  │
  │      │                                  │ DefaultStack::new(2MB)              │                      │
  ├──────┼──────────────────────────────────┼─────────────────────────────────────┼──────────────────────┤    
  │ 9    │ returns from Rust                │ executing wasm with 2MB stack...    │ 2MB                  │
  ├──────┼──────────────────────────────────┼─────────────────────────────────────┼──────────────────────┤    
  │ 10   │ defer: SetNativeStackSize(1MB)   │ executing wasm with 2MB stack...    │ 1MB                  │
  ├──────┼──────────────────────────────────┼─────────────────────────────────────┼──────────────────────┤    
  │ 11   │ defer: DrainStackPool()          │ executing wasm with 2MB stack...    │ 1MB                  │
  ├──────┼──────────────────────────────────┼─────────────────────────────────────┼──────────────────────┤    
  │ 12   │ done                             │ returns successfully                │ 1MB                  │
  ├──────┼──────────────────────────────────┼─────────────────────────────────────┼──────────────────────┤    
  │ 13   │                                  │ defer: SetNativeStackSize(1MB)      │ 1MB                  │
  ├──────┼──────────────────────────────────┼─────────────────────────────────────┼──────────────────────┤    
  │ 14   │                                  │ defer: DrainStackPool()             │ 1MB                  │
  └──────┴──────────────────────────────────┴─────────────────────────────────────┴──────────────────────┘

And now the same threads in an unhappy path:

  ┌──────┬─────────────────────────────────┬─────────────────────────────────────────┬────────────────────┐
  │ Step │               G1                │                   G2                    │ DEFAULT_STACK_SIZE │   
  ├──────┼─────────────────────────────────┼─────────────────────────────────────────┼────────────────────┤
  │ 1    │ SetNativeStackSize(2MB)         │                                         │ 2MB                │   
  ├──────┼─────────────────────────────────┼─────────────────────────────────────────┼────────────────────┤
  │ 2    │ DrainStackPool()                │                                         │ 2MB                │
  ├──────┼─────────────────────────────────┼─────────────────────────────────────────┼────────────────────┤   
  │ 3    │ doStylusCall() → enters Rust    │                                         │ 2MB                │
  ├──────┼─────────────────────────────────┼─────────────────────────────────────────┼────────────────────┤   
  │ 4    │ catch_traps: reads 2MB, allocs  │                                         │ 2MB                │
  │      │ 2MB                             │                                         │                    │   
  ├──────┼─────────────────────────────────┼─────────────────────────────────────────┼────────────────────┤
  │ 5    │ executing wasm...               │ SetNativeStackSize(2MB)                 │ 2MB                │   
  ├──────┼─────────────────────────────────┼─────────────────────────────────────────┼────────────────────┤
  │ 6    │ executing wasm...               │ DrainStackPool()                        │ 2MB                │
  ├──────┼─────────────────────────────────┼─────────────────────────────────────────┼────────────────────┤
  │ 7    │ returns from Rust               │                                         │ 2MB                │   
  ├──────┼─────────────────────────────────┼─────────────────────────────────────────┼────────────────────┤
  │ 8    │ defer: SetNativeStackSize(1MB)  │                                         │ 1MB                │   
  ├──────┼─────────────────────────────────┼─────────────────────────────────────────┼────────────────────┤
  │ 9    │ defer: DrainStackPool()         │                                         │ 1MB                │   
  ├──────┼─────────────────────────────────┼─────────────────────────────────────────┼────────────────────┤
  │ 10   │ done                            │ doStylusCall() → enters Rust            │ 1MB                │   
  ├──────┼─────────────────────────────────┼─────────────────────────────────────────┼────────────────────┤
  │ 11   │                                 │ catch_traps: reads 1MB,                 │ 1MB                │   
  │      │                                 │ DefaultStack::new(1MB)                  │                    │
  ├──────┼─────────────────────────────────┼─────────────────────────────────────────┼────────────────────┤   
  │ 12   │                                 │ overflows again on 1MB stack            │ 1MB                │
  ├──────┼─────────────────────────────────┼─────────────────────────────────────────┼────────────────────┤
  │ 13   │                                 │ returns NativeStackOverflow → tx        │ 1MB                │
  │      │                                 │ reverts                                 │                    │   
  ├──────┼─────────────────────────────────┼─────────────────────────────────────────┼────────────────────┤
  │ 14   │                                 │ defer: SetNativeStackSize(1MB)          │ 1MB                │   
  └──────┴─────────────────────────────────┴─────────────────────────────────────────┴────────────────────┘

the worst case in any race scenario is that a concurrent thread overflows again (same outcome as if the doubling never happened) which is the last scenario above. Does that help?


evmApi := newApi(evm, tracingInfo, scope, memoryModel)
defer evmApi.drop()
savedGas := scope.Contract.Gas
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this only field that we need to save for possible later revert?
I think that at least Contract.UseMultiGas and Contract.RetainedMultiGas might need to also be reverted.

Also what about the rest of vm.ScopeContext?

I haven't read into that deep enough, but I think that we might need to save whole vm.ScopeContext as it includes Stack and Memory context of the contract and we will need to revert those also.

Copy link
Copy Markdown
Contributor Author

@bragaigor bragaigor Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's a quick analysis by Claude and was able to confirm by looking further at the code:

  • scope.Stack — This is the EVM operand stack (used by the interpreter for PUSH, POP, ADD, etc.). Stylus programs run inside Wasmer with their own WASM stack. The CGo stylus_call never touches scope.Stack. In fact, when Stylus is invoked, the EVM interpreter isn't running — callProgram is called directly from the precompile path, so the EVM stack is dormant.
  • scope.Memory — Same story. This is EVM memory (used by MLOAD, MSTORE, etc.). Stylus programs have their own WASM linear memory managed by Wasmer. The stylus_call FFI doesn't read or write scope.Memory.
  • scope.Contract (other fields) — The immutable fields (caller, address, code, codehash, value) don't change during execution. RetainedMultiGas is only modified by EVM instructions, never by Stylus host I/O.

I'll save UsedMultiGas as well. Does the above make sense, or did I miss something and we need to save other fields?

Signed-off-by: Igor Braga <5835477+bragaigor@users.noreply.github.com>
Signed-off-by: Igor Braga <5835477+bragaigor@users.noreply.github.com>
Signed-off-by: Igor Braga <5835477+bragaigor@users.noreply.github.com>
Signed-off-by: Igor Braga <5835477+bragaigor@users.noreply.github.com>
Signed-off-by: Igor Braga <5835477+bragaigor@users.noreply.github.com>
Signed-off-by: Igor Braga <5835477+bragaigor@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants