Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ jobs:
uses: golangci/golangci-lint-action@v6
with:
version: v1.64
args: --timeout=5m
45 changes: 36 additions & 9 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,40 +1,66 @@
module github.com/gnolang/supernova

go 1.23
go 1.23.0

toolchain go1.24.1

require (
github.com/gnolang/gno v0.0.0-20250129165357-b392287f0d2c
github.com/gnolang/gno v0.0.0-20250826105341-fba926958b6b
github.com/peterbourgon/ff/v3 v3.4.0
github.com/schollz/progressbar/v3 v3.18.0
github.com/stretchr/testify v1.10.0
)

require (
dario.cat/mergo v1.0.1 // indirect
github.com/DataDog/zstd v1.4.5 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect
github.com/btcsuite/btcd/btcutil v1.1.6 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cockroachdb/apd/v3 v3.2.1 // indirect
github.com/cockroachdb/errors v1.11.3 // indirect
github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
github.com/cockroachdb/pebble v1.1.5 // indirect
github.com/cockroachdb/redact v1.1.5 // indirect
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/getsentry/sentry-go v0.27.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/gofrs/flock v0.12.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect
github.com/klauspost/compress v1.16.0 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/lib/pq v1.10.9 // indirect
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/onsi/gomega v1.31.1 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.15.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.42.0 // indirect
github.com/prometheus/procfs v0.9.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/rs/cors v1.11.1 // indirect
github.com/rs/xid v1.6.0 // indirect
github.com/sig-0/insertion-queue v0.0.0-20241004125609-6b3ca841346b // indirect
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
go.etcd.io/bbolt v1.3.11 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/otel v1.34.0 // indirect
Expand All @@ -46,14 +72,15 @@ require (
go.opentelemetry.io/otel/trace v1.34.0 // indirect
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/crypto v0.40.0 // indirect
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect
golang.org/x/mod v0.22.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/term v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/mod v0.26.0 // indirect
golang.org/x/net v0.42.0 // indirect
golang.org/x/sync v0.16.0 // indirect
golang.org/x/sys v0.34.0 // indirect
golang.org/x/term v0.33.0 // indirect
golang.org/x/text v0.28.0 // indirect
golang.org/x/tools v0.35.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
google.golang.org/grpc v1.69.4 // indirect
Expand Down
102 changes: 86 additions & 16 deletions go.sum

Large diffs are not rendered by default.

22 changes: 21 additions & 1 deletion internal/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ import (
"github.com/gnolang/supernova/internal/common"
)

const simulatePath = ".app/simulate"
const (
simulatePath = ".app/simulate"
gaspricePath = "auth/gasprice"
)

type Client struct {
conn *client.RPCClient
Expand Down Expand Up @@ -170,3 +173,20 @@ func (h *Client) EstimateGas(tx *std.Tx) (int64, error) {
// for executing the transaction
return deliverTx.GasUsed, nil
}

func (h *Client) FetchGasPrice() (std.GasPrice, error) {
// Perform auth/gasprice
gp := std.GasPrice{}

qres, err := h.conn.ABCIQuery(gaspricePath, []byte{})
if err != nil {
return gp, err
}

err = amino.UnmarshalJSON(qres.Response.Data, &gp)
if err != nil {
return gp, err
}

return gp, nil
}
35 changes: 8 additions & 27 deletions internal/distributor/distributor.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type Client interface {
GetAccount(address string) (*gnoland.GnoAccount, error)
BroadcastTransaction(tx *std.Tx) error
EstimateGas(tx *std.Tx) (int64, error)
FetchGasPrice() (std.GasPrice, error)
}

// Distributor is the process
Expand All @@ -42,41 +43,20 @@ func NewDistributor(
func (d *Distributor) Distribute(
distributor crypto.PrivKey,
accounts []crypto.Address,
transactions uint64,
chainID string,
gasPrice std.GasPrice,
calculatedRuntimeCost std.Coin,
) ([]std.Account, error) {
fmt.Printf("\n💸 Starting Fund Distribution 💸\n\n")

// Calculate the base fees
subAccountCost := calculateRuntimeCosts(int64(transactions))
fmt.Printf(
"Calculated sub-account cost as %d %s\n",
subAccountCost.Amount,
subAccountCost.Denom,
calculatedRuntimeCost.Amount,
calculatedRuntimeCost.Denom,
)

// Fund the accounts
return d.fundAccounts(distributor, accounts, subAccountCost, chainID)
}

// calculateRuntimeCosts calculates the amount of funds
// each account needs to have in order to participate in the
// stress test run
func calculateRuntimeCosts(totalTx int64) std.Coin {
// Cost of a single run transaction for the sub-account
// NOTE: Since there is no gas estimation support yet, this value
// is fixed, but it will change in the future once pricing estimations
// are added
baseTxCost := common.CalculateFeeInRatio(1_000_000, common.DefaultGasPrice)

// Each account should have enough funds
// to execute the entire run
subAccountCost := std.Coin{
Denom: common.Denomination,
Amount: totalTx * baseTxCost.GasFee.Amount,
}

return subAccountCost
return d.fundAccounts(distributor, accounts, calculatedRuntimeCost, chainID, gasPrice)
}

// fundAccounts attempts to fund accounts that have missing funds,
Expand All @@ -86,6 +66,7 @@ func (d *Distributor) fundAccounts(
accounts []crypto.Address,
singleRunCost std.Coin,
chainID string,
gasPrice std.GasPrice,
) ([]std.Account, error) {
type shortAccount struct {
missingFunds std.Coin
Expand Down Expand Up @@ -150,7 +131,7 @@ func (d *Distributor) fundAccounts(
var (
distributorBalance = distributor.Coins
fundableIndex = 0
defaultFee = common.CalculateFeeInRatio(100_000, common.DefaultGasPrice)
defaultFee = common.CalculateFeeInRatio(100_000, gasPrice)
)

for _, account := range shortAccounts {
Expand Down
11 changes: 7 additions & 4 deletions internal/distributor/distributor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ func TestDistributor_Distribute(t *testing.T) {

var (
numTx = uint64(1000)
singleCost = calculateRuntimeCosts(int64(numTx))
singleCost = std.Coin{
Denom: common.Denomination,
Amount: int64(numTx) * 100_00,
}
)

getAccount := func(address string, accounts []crypto.PrivKey) crypto.PrivKey {
Expand Down Expand Up @@ -66,7 +69,7 @@ func TestDistributor_Distribute(t *testing.T) {
addresses = append(addresses, account.PubKey().Address())
}

readyAccounts, err := d.Distribute(accounts[0], addresses, numTx, "dummy")
readyAccounts, err := d.Distribute(accounts[0], addresses, "dummy", common.DefaultGasPrice, singleCost)
if err != nil {
t.Fatalf("unable to distribute funds, %v", err)
}
Expand Down Expand Up @@ -122,7 +125,7 @@ func TestDistributor_Distribute(t *testing.T) {
addresses = append(addresses, account.PubKey().Address())
}

readyAccounts, err := d.Distribute(accounts[0], addresses, numTx, "dummy")
readyAccounts, err := d.Distribute(accounts[0], addresses, "dummy", common.DefaultGasPrice, singleCost)

assert.Nil(t, readyAccounts)
assert.ErrorIs(t, err, errInsufficientFunds)
Expand Down Expand Up @@ -192,7 +195,7 @@ func TestDistributor_Distribute(t *testing.T) {
addresses = append(addresses, account.PubKey().Address())
}

readyAccounts, err := d.Distribute(accounts[0], addresses, numTx, "dummy")
readyAccounts, err := d.Distribute(accounts[0], addresses, "dummy", common.DefaultGasPrice, singleCost)
if err != nil {
t.Fatalf("unable to distribute funds, %v", err)
}
Expand Down
10 changes: 10 additions & 0 deletions internal/distributor/mock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ type (
broadcastTransactionDelegate func(*std.Tx) error
getAccountDelegate func(string) (*gnoland.GnoAccount, error)
estimateGasDelegate func(*std.Tx) (int64, error)
fetchGasPriceDelegate func() (std.GasPrice, error)
)

type mockClient struct {
broadcastTransactionFn broadcastTransactionDelegate
getAccountFn getAccountDelegate
estimateGasFn estimateGasDelegate
fetchGasPriceFn fetchGasPriceDelegate
}

func (m *mockClient) BroadcastTransaction(tx *std.Tx) error {
Expand All @@ -40,3 +42,11 @@ func (m *mockClient) EstimateGas(tx *std.Tx) (int64, error) {

return 0, nil
}

func (m *mockClient) FetchGasPrice() (std.GasPrice, error) {
if m.fetchGasPriceFn != nil {
return m.fetchGasPriceFn()
}

return std.GasPrice{}, nil
}
60 changes: 44 additions & 16 deletions internal/pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/gnolang/gno/tm2/pkg/crypto"
"github.com/gnolang/gno/tm2/pkg/crypto/bip39"
"github.com/gnolang/gno/tm2/pkg/std"
"github.com/gnolang/supernova/internal/batcher"
"github.com/gnolang/supernova/internal/client"
"github.com/gnolang/supernova/internal/collector"
Expand Down Expand Up @@ -65,14 +66,32 @@ func (p *Pipeline) Execute() error {
// Initialize the accounts for the runtime
accounts := p.initializeAccounts()

gasPrice, err := p.cli.FetchGasPrice()
if err != nil {
return err
}

lastBlock, err := p.cli.GetLatestBlockHeight()
if err != nil {
return fmt.Errorf("unable to get last block, %w", err)
}

maxGas, err := p.cli.GetBlockGasLimit(lastBlock)
if err != nil {
return fmt.Errorf("unable to get block gas limit, %w", err)
}
// Predeploy any pending transactions
if err := prepareRuntime(
estimatedGas, err := prepareRuntime(
mode,
accounts[0],
p.cfg.ChainID,
p.cli,
txRuntime,
); err != nil {
maxGas,
gasPrice,
p.cfg.Transactions,
)
if err != nil {
return err
}

Expand All @@ -86,8 +105,9 @@ func (p *Pipeline) Execute() error {
runAccounts, err := distributor.NewDistributor(p.cli).Distribute(
accounts[0],
addresses,
p.cfg.Transactions,
p.cfg.ChainID,
gasPrice,
estimatedGas,
)
if err != nil {
return fmt.Errorf("unable to distribute funds, %w", err)
Expand All @@ -109,6 +129,8 @@ func (p *Pipeline) Execute() error {
runKeys,
runAccounts,
p.cfg.Transactions,
maxGas,
gasPrice,
p.cfg.ChainID,
p.cli.EstimateGas,
)
Expand Down Expand Up @@ -192,42 +214,48 @@ func prepareRuntime(
chainID string,
cli pipelineClient,
txRuntime runtime.Runtime,
) error {
if mode != runtime.RealmCall {
return nil
}

fmt.Printf("\n✨ Starting Predeployment Procedure ✨\n\n")

currentMaxGas int64,
gasPrice std.GasPrice,
transactions uint64,
) (std.Coin, error) {
// Get the deployer account
deployer, err := cli.GetAccount(deployerKey.PubKey().Address().String())
if err != nil {
return fmt.Errorf("unable to fetch deployer account, %w", err)
return std.Coin{}, fmt.Errorf("unable to fetch deployer account, %w", err)
}

signCB := runtime.SignTransactionsCb(chainID, deployer, deployerKey)

if mode != runtime.RealmCall {
return txRuntime.CalculateRuntimeCosts(deployer, cli.EstimateGas, signCB, currentMaxGas, gasPrice, transactions)
}

fmt.Printf("\n✨ Starting Predeployment Procedure ✨\n\n")

// Get the predeploy transactions
predeployTxs, err := txRuntime.Initialize(
deployer,
deployerKey,
chainID,
signCB,
cli.EstimateGas,
currentMaxGas,
gasPrice,
)
if err != nil {
return fmt.Errorf("unable to initialize runtime, %w", err)
return std.Coin{}, fmt.Errorf("unable to initialize runtime, %w", err)
}

bar := progressbar.Default(int64(len(predeployTxs)), "predeployed txs")

// Execute the predeploy transactions
for _, tx := range predeployTxs {
if err := cli.BroadcastTransaction(tx); err != nil {
return fmt.Errorf("unable to broadcast predeploy tx, %w", err)
return std.Coin{}, fmt.Errorf("unable to broadcast predeploy tx, %w", err)
}

_ = bar.Add(1) //nolint:errcheck // No need to check
}

fmt.Printf("✅ Successfully predeployed %d transactions\n", len(predeployTxs))

return nil
return txRuntime.CalculateRuntimeCosts(deployer, cli.EstimateGas, signCB, currentMaxGas, gasPrice, transactions)
}
Loading
Loading