diff --git a/.buildkite/scripts/daily_txsource.sh b/.buildkite/scripts/daily_txsource.sh index 3bf30603371..7d3dafe87fb 100755 --- a/.buildkite/scripts/daily_txsource.sh +++ b/.buildkite/scripts/daily_txsource.sh @@ -13,4 +13,8 @@ else -X POST \ --data "{\"text\": \"Daily transaction source tests failure\"}" \ "$SLACK_WEBHOOK_URL" + + # Exit with non-zero exit code, so that the buildkite build will be + # marked as failed. + exit 1 fi diff --git a/.changelog/2744.feature.md b/.changelog/2744.feature.md new file mode 100644 index 00000000000..e6a5b6b5e21 --- /dev/null +++ b/.changelog/2744.feature.md @@ -0,0 +1 @@ +txsource: use TestEntity for funding instead of hardcoding addresses diff --git a/go/oasis-node/cmd/debug/txsource/txsource.go b/go/oasis-node/cmd/debug/txsource/txsource.go index f9aa64b124f..56394bab9c5 100644 --- a/go/oasis-node/cmd/debug/txsource/txsource.go +++ b/go/oasis-node/cmd/debug/txsource/txsource.go @@ -12,6 +12,8 @@ import ( "github.com/oasislabs/oasis-core/go/common/crypto/drbg" "github.com/oasislabs/oasis-core/go/common/crypto/mathrand" + "github.com/oasislabs/oasis-core/go/common/crypto/signature" + memorySigner "github.com/oasislabs/oasis-core/go/common/crypto/signature/signers/memory" "github.com/oasislabs/oasis-core/go/common/logging" consensus "github.com/oasislabs/oasis-core/go/consensus/api" "github.com/oasislabs/oasis-core/go/control/api" @@ -104,8 +106,22 @@ func doRun(cmd *cobra.Command, args []string) error { } logger.Debug("node synced") + // Create an account and fund it with test entity. + // NOTE: we don't use Test entity account directly in the workloads + // as using the same account in all workloads would lead to a lot of + // contention and nonce missmatches. + // Generate and fund the account that will be used for funding accounts + // during the workload. + fundingAccount, err := memorySigner.NewFactory().Generate(signature.SignerEntity, rng) + if err != nil { + return fmt.Errorf("memory signer factory generate funding account %w", err) + } + if err = workload.FundAccountFromTestEntity(ctx, logger, cnsc, fundingAccount.Public()); err != nil { + return fmt.Errorf("test entity account funding failure: %w", err) + } + logger.Debug("entering workload", "name", name) - if err = w.Run(ctx, rng, conn, cnsc, rtc); err != nil { + if err = w.Run(ctx, rng, conn, cnsc, rtc, fundingAccount); err != nil { logger.Error("workload error", "err", err) return fmt.Errorf("workload %s: %w", name, err) } diff --git a/go/oasis-node/cmd/debug/txsource/workload/oversized.go b/go/oasis-node/cmd/debug/txsource/workload/oversized.go index bfb7c615744..b64a29f8031 100644 --- a/go/oasis-node/cmd/debug/txsource/workload/oversized.go +++ b/go/oasis-node/cmd/debug/txsource/workload/oversized.go @@ -18,6 +18,7 @@ import ( ) const ( + // NameOversized is the name of the oversized workload. NameOversized = "oversized" oversizedTxGasAmount = 10000 @@ -27,7 +28,7 @@ var oversizedLogger = logging.GetLogger("cmd/txsource/workload/oversized") type oversized struct{} -func (oversized) Run(gracefulExit context.Context, rng *rand.Rand, conn *grpc.ClientConn, cnsc consensus.ClientBackend, rtc runtimeClient.RuntimeClient) error { +func (oversized) Run(gracefulExit context.Context, rng *rand.Rand, conn *grpc.ClientConn, cnsc consensus.ClientBackend, rtc runtimeClient.RuntimeClient, fundingAccount signature.Signer) error { txSignerFactory := memorySigner.NewFactory() txSigner, err := txSignerFactory.Generate(signature.SignerEntity, rng) if err != nil { @@ -47,6 +48,10 @@ func (oversized) Run(gracefulExit context.Context, rng *rand.Rand, conn *grpc.Cl fee := transaction.Fee{ Gas: oversizedTxGasAmount, } + if err = fee.Amount.FromInt64(oversizedTxGasAmount * gasPrice); err != nil { + return fmt.Errorf("Fee amount error: %w", err) + } + for { // Generate a big transfer transaction which is valid, but oversized. type customTransfer struct { @@ -63,6 +68,10 @@ func (oversized) Run(gracefulExit context.Context, rng *rand.Rand, conn *grpc.Cl return fmt.Errorf("failed to generate bogus transaction: %w", err) } + if err = transferFunds(ctx, oversizedLogger, cnsc, fundingAccount, txSigner.Public(), int64(oversizedTxGasAmount*gasPrice)); err != nil { + return fmt.Errorf("workload/oversized: account funding failure: %w", err) + } + tx := transaction.NewTransaction(nonce, &fee, staking.MethodTransfer, &xfer) signedTx, err := transaction.Sign(txSigner, tx) if err != nil { diff --git a/go/oasis-node/cmd/debug/txsource/workload/parallel.go b/go/oasis-node/cmd/debug/txsource/workload/parallel.go index fb70777d6b0..1bdd46debdd 100644 --- a/go/oasis-node/cmd/debug/txsource/workload/parallel.go +++ b/go/oasis-node/cmd/debug/txsource/workload/parallel.go @@ -19,30 +19,39 @@ import ( ) const ( + // NameParallel is the name of the parallel workload. NameParallel = "parallel" parallelSendWaitTimeoutInterval = 30 * time.Second parallelSendTimeoutInterval = 60 * time.Second parallelConcurency = 200 parallelTxGasAmount = 10 + parallelTxTransferAmount = 100 + parallelTxFundInterval = 10 ) var parallelLogger = logging.GetLogger("cmd/txsource/workload/parallel") type parallel struct{} -func (parallel) Run(gracefulExit context.Context, rng *rand.Rand, conn *grpc.ClientConn, cnsc consensus.ClientBackend, rtc runtimeClient.RuntimeClient) error { +func (parallel) Run(gracefulExit context.Context, rng *rand.Rand, conn *grpc.ClientConn, cnsc consensus.ClientBackend, rtc runtimeClient.RuntimeClient, fundingAccount signature.Signer) error { ctx := context.Background() + var err error accounts := make([]signature.Signer, parallelConcurency) - var err error fac := memorySigner.NewFactory() for i := range accounts { - // NOTE: no balances are needed for now accounts[i], err = fac.Generate(signature.SignerEntity, rng) if err != nil { return fmt.Errorf("memory signer factory Generate account %d: %w", i, err) } + + // Initial funding of accounts. + fundAmount := parallelTxTransferAmount + // self transfer amount + parallelTxFundInterval*parallelTxGasAmount*gasPrice // gas for `parallelTxFundInterval` transfers. + if err = transferFunds(ctx, parallelLogger, cnsc, fundingAccount, accounts[i].Public(), int64(fundAmount)); err != nil { + return fmt.Errorf("account funding failure: %w", err) + } } // A single global nonce is enough as we wait for all submissions to @@ -51,12 +60,17 @@ func (parallel) Run(gracefulExit context.Context, rng *rand.Rand, conn *grpc.Cli fee := transaction.Fee{ Gas: parallelTxGasAmount, } + if err = fee.Amount.FromInt64(parallelTxGasAmount); err != nil { + return fmt.Errorf("Fee amount error: %w", err) + } + var i uint64 for { + i++ + errCh := make(chan error, parallelConcurency) var wg sync.WaitGroup wg.Add(parallelConcurency) - for i := 0; i < parallelConcurency; i++ { go func(txSigner signature.Signer, nonce uint64) { defer wg.Done() @@ -65,9 +79,14 @@ func (parallel) Run(gracefulExit context.Context, rng *rand.Rand, conn *grpc.Cli transfer := staking.Transfer{ To: txSigner.Public(), } - tx := staking.NewTransferTx(nonce, &fee, &transfer) + if err = transfer.Tokens.FromInt64(parallelTxTransferAmount); err != nil { + errCh <- fmt.Errorf("transfer tokens FromInt64 %d: %w", parallelTxTransferAmount, err) + return + } - signedTx, err := transaction.Sign(txSigner, tx) + tx := staking.NewTransferTx(nonce, &fee, &transfer) + var signedTx *transaction.SignedTransaction + signedTx, err = transaction.Sign(txSigner, tx) if err != nil { parallelLogger.Error("transaction.Sign error", "err", err) errCh <- fmt.Errorf("transaction.Sign: %w", err) @@ -99,7 +118,7 @@ func (parallel) Run(gracefulExit context.Context, rng *rand.Rand, conn *grpc.Cli parallelLogger.Error("transactions not completed within timeout") return fmt.Errorf("workload parallel: transactions not completed within timeout") - case err := <-errCh: + case err = <-errCh: parallelLogger.Error("error subimit transaction", "err", err, ) @@ -111,6 +130,16 @@ func (parallel) Run(gracefulExit context.Context, rng *rand.Rand, conn *grpc.Cli ) } + if i%parallelTxFundInterval == 0 { + // Re-fund accounts for next `parallelTxFundInterval` transfers. + for i := range accounts { + fundAmount := parallelTxFundInterval * parallelTxGasAmount * gasPrice // gas for `parallelTxFundInterval` transfers. + if err = transferFunds(ctx, parallelLogger, cnsc, fundingAccount, accounts[i].Public(), int64(fundAmount)); err != nil { + return fmt.Errorf("account funding failure: %w", err) + } + } + } + select { case <-time.After(parallelSendTimeoutInterval): case <-gracefulExit.Done(): diff --git a/go/oasis-node/cmd/debug/txsource/workload/registration.go b/go/oasis-node/cmd/debug/txsource/workload/registration.go index f23e3a5048a..2365387e49a 100644 --- a/go/oasis-node/cmd/debug/txsource/workload/registration.go +++ b/go/oasis-node/cmd/debug/txsource/workload/registration.go @@ -27,6 +27,7 @@ import ( ) const ( + // NameRegistration is the name of the registration workload. NameRegistration = "registration" registryNumEntities = 10 @@ -150,7 +151,7 @@ func signNode(identity *identity.Identity, nodeDesc *node.Node) (*node.MultiSign return sigNode, nil } -func (r *registration) Run(gracefulExit context.Context, rng *rand.Rand, conn *grpc.ClientConn, cnsc consensus.ClientBackend, rtc runtimeClient.RuntimeClient) error { +func (r *registration) Run(gracefulExit context.Context, rng *rand.Rand, conn *grpc.ClientConn, cnsc consensus.ClientBackend, rtc runtimeClient.RuntimeClient, fundingAccount signature.Signer) error { // nolint: gocyclo ctx := context.Background() var err error @@ -242,6 +243,15 @@ func (r *registration) Run(gracefulExit context.Context, rng *rand.Rand, conn *g return fmt.Errorf("failed to estimate gas: %w", err) } tx.Fee.Gas = gas + feeAmount := int64(gas) * gasPrice + if err = tx.Fee.Amount.FromInt64(feeAmount); err != nil { + return fmt.Errorf("fee amount from int64: %w", err) + } + + // Fund entity account to cover registration fees. + if err = transferFunds(ctx, registryLogger, cnsc, fundingAccount, entityAccs[i].signer.Public(), feeAmount); err != nil { + return fmt.Errorf("account funding failure: %w", err) + } signedTx, err := transaction.Sign(entityAccs[i].signer, tx) if err != nil { @@ -270,8 +280,17 @@ func (r *registration) Run(gracefulExit context.Context, rng *rand.Rand, conn *g return fmt.Errorf("failed to estimate gas: %w", err) } tx.Fee.Gas = gas - entityAccs[i].reckonedNonce++ + feeAmount := int64(gas) * gasPrice + if err = tx.Fee.Amount.FromInt64(feeAmount); err != nil { + return fmt.Errorf("fee amount from uint64: %w", err) + } + // Fund entity account to cover entity registration fees. + if err = transferFunds(ctx, registryLogger, cnsc, fundingAccount, entityAccs[i].signer.Public(), feeAmount); err != nil { + return fmt.Errorf("account funding failure: %w", err) + } + + entityAccs[i].reckonedNonce++ signedTx, err := transaction.Sign(entityAccs[i].signer, tx) if err != nil { return fmt.Errorf("transaction.Sign: %w", err) @@ -312,26 +331,34 @@ func (r *registration) Run(gracefulExit context.Context, rng *rand.Rand, conn *g return fmt.Errorf("failed to estimate gas: %w", err) } tx.Fee.Gas = gas + feeAmount := gas * gasPrice + if err = tx.Fee.Amount.FromUint64(uint64(feeAmount)); err != nil { + return fmt.Errorf("fee amount from uint64: %w", err) + } + // Fund node account to cover registration fees. + if err = transferFunds(ctx, registryLogger, cnsc, fundingAccount, selectedNode.id.NodeSigner.Public(), int64(feeAmount)); err != nil { + return fmt.Errorf("account funding failure: %w", err) + } selectedNode.reckonedNonce++ signedTx, err := transaction.Sign(selectedNode.id.NodeSigner, tx) if err != nil { return fmt.Errorf("transaction.Sign: %w", err) } - transferLogger.Debug("submitting registration", + registryLogger.Debug("submitting registration", "node", selectedNode.nodeDesc, ) if err = cnsc.SubmitTx(ctx, signedTx); err != nil { return fmt.Errorf("cnsc.SubmitTx: %w", err) } - transferLogger.Debug("registered node", + registryLogger.Debug("registered node", "node", selectedNode.nodeDesc, ) select { case <-gracefulExit.Done(): - transferLogger.Debug("time's up") + registryLogger.Debug("time's up") return nil default: } diff --git a/go/oasis-node/cmd/debug/txsource/workload/runtimeplaceholder.go b/go/oasis-node/cmd/debug/txsource/workload/runtimeplaceholder.go index f3fefde54f5..655bb848a90 100644 --- a/go/oasis-node/cmd/debug/txsource/workload/runtimeplaceholder.go +++ b/go/oasis-node/cmd/debug/txsource/workload/runtimeplaceholder.go @@ -7,6 +7,7 @@ import ( "google.golang.org/grpc" + "github.com/oasislabs/oasis-core/go/common/crypto/signature" "github.com/oasislabs/oasis-core/go/common/logging" consensus "github.com/oasislabs/oasis-core/go/consensus/api" runtimeClient "github.com/oasislabs/oasis-core/go/runtime/client/api" @@ -20,7 +21,7 @@ var ( type runtimePlaceholder struct{} -func (runtimePlaceholder) Run(_ context.Context, _ *rand.Rand, _ *grpc.ClientConn, _ consensus.ClientBackend, rtc runtimeClient.RuntimeClient) error { +func (runtimePlaceholder) Run(_ context.Context, _ *rand.Rand, _ *grpc.ClientConn, _ consensus.ClientBackend, rtc runtimeClient.RuntimeClient, fundingAccount signature.Signer) error { ctx := context.Background() var tx *runtimeClient.SubmitTxRequest // Placeholder for sending a runtime transaction from a workload. diff --git a/go/oasis-node/cmd/debug/txsource/workload/transfer.go b/go/oasis-node/cmd/debug/txsource/workload/transfer.go index 41b9935fe88..ad472b56cd8 100644 --- a/go/oasis-node/cmd/debug/txsource/workload/transfer.go +++ b/go/oasis-node/cmd/debug/txsource/workload/transfer.go @@ -18,25 +18,31 @@ import ( ) const ( - NameTransfer = "transfer" - TransferNumAccounts = 10 - TransferAmount = 1 + // NameTransfer is the name of the transfer workload. + NameTransfer = "transfer" + + transferNumAccounts = 10 + transferAmount = 1 + transferFundInterval = 10 + transferGasCost = 10 ) var transferLogger = logging.GetLogger("cmd/txsource/workload/transfer") type transfer struct{} -func (transfer) Run(gracefulExit context.Context, rng *rand.Rand, conn *grpc.ClientConn, cnsc consensus.ClientBackend, rtc runtimeClient.RuntimeClient) error { +func (transfer) Run(gracefulExit context.Context, rng *rand.Rand, conn *grpc.ClientConn, cnsc consensus.ClientBackend, rtc runtimeClient.RuntimeClient, fundingAccount signature.Signer) error { + var err error + ctx := context.Background() + + fac := memorySigner.NewFactory() // Load all the keys up front. Like, how annoyed would you be if down the line one of them turned out to be // corrupted or something, ya know? accounts := make([]struct { signer signature.Signer reckonedNonce uint64 reckonedBalance quantity.Quantity - }, TransferNumAccounts) - var err error - fac := memorySigner.NewFactory() + }, transferNumAccounts) for i := range accounts { accounts[i].signer, err = fac.Generate(signature.SignerEntity, rng) if err != nil { @@ -45,9 +51,13 @@ func (transfer) Run(gracefulExit context.Context, rng *rand.Rand, conn *grpc.Cli } // Read all the account info up front. - ctx := context.Background() stakingClient := staking.NewStakingClient(conn) for i := range accounts { + fundAmount := transferAmount*transferFundInterval + // funds for `transferFundInterval` transfers + transferGasCost*gasPrice*transferFundInterval // gas costs for `transferFundInterval` transfers + if err = transferFunds(ctx, transferLogger, cnsc, fundingAccount, accounts[i].signer.Public(), int64(fundAmount)); err != nil { + return fmt.Errorf("workload/transfer: account funding failure: %w", err) + } var account *staking.Account account, err = stakingClient.AccountInfo(ctx, &staking.OwnerQuery{ Height: consensus.HeightLatest, @@ -66,35 +76,39 @@ func (transfer) Run(gracefulExit context.Context, rng *rand.Rand, conn *grpc.Cli } fee := transaction.Fee{ - Gas: 10, + Gas: transferGasCost, + } + if err = fee.Amount.FromInt64(transferGasCost * gasPrice); err != nil { + return fmt.Errorf("Fee amount error: %w", err) } + var minBalance quantity.Quantity - if err = minBalance.FromInt64(TransferAmount); err != nil { - return fmt.Errorf("min balance FromInt64 %d: %w", TransferAmount, err) + if err = minBalance.FromInt64(transferAmount); err != nil { + return fmt.Errorf("min balance FromInt64 %d: %w", transferAmount, err) } if err = minBalance.Add(&fee.Amount); err != nil { return fmt.Errorf("min balance %v Add fee amount %v: %w", minBalance, fee.Amount, err) } for { - perm := rng.Perm(TransferNumAccounts) + perm := rng.Perm(transferNumAccounts) fromPermIdx := 0 - for ; fromPermIdx < TransferNumAccounts; fromPermIdx++ { + for ; fromPermIdx < transferNumAccounts; fromPermIdx++ { if accounts[perm[fromPermIdx]].reckonedBalance.Cmp(&minBalance) >= 0 { break } } - if fromPermIdx >= TransferNumAccounts { + if fromPermIdx >= transferNumAccounts { return fmt.Errorf("all accounts %#v have gone broke", accounts) } - toPermIdx := (fromPermIdx + 1) % TransferNumAccounts + toPermIdx := (fromPermIdx + 1) % transferNumAccounts from := &accounts[perm[fromPermIdx]] to := &accounts[perm[toPermIdx]] transfer := staking.Transfer{ To: to.signer.Public(), } - if err = transfer.Tokens.FromInt64(TransferAmount); err != nil { - return fmt.Errorf("transfer tokens FromInt64 %d: %w", TransferAmount, err) + if err = transfer.Tokens.FromInt64(transferAmount); err != nil { + return fmt.Errorf("transfer tokens FromInt64 %d: %w", transferAmount, err) } tx := staking.NewTransferTx(from.reckonedNonce, &fee, &transfer) signedTx, err := transaction.Sign(from.signer, tx) @@ -119,6 +133,21 @@ func (transfer) Run(gracefulExit context.Context, rng *rand.Rand, conn *grpc.Cli return fmt.Errorf("to reckoned balance %v Add transfer tokens %v: %w", to.reckonedBalance, transfer.Tokens, err) } + if from.reckonedNonce%transferFundInterval == 0 { + // Re-fund account for next `transferFundInterval` transfers. + fundAmount := transferGasCost * gasPrice * transferFundInterval // gas costs for `transferFundInterval` transfers. + if err = transferFunds(ctx, parallelLogger, cnsc, fundingAccount, from.signer.Public(), int64(fundAmount)); err != nil { + return fmt.Errorf("account funding failure: %w", err) + } + var fundAmountQ quantity.Quantity + if err = fundAmountQ.FromInt64(int64(fundAmount)); err != nil { + return fmt.Errorf("fundAmountQ FromInt64(%d): %w", fundAmount, err) + } + if err = from.reckonedBalance.Add(&fundAmountQ); err != nil { + return fmt.Errorf("to reckoned balance %v Add fund amount %v: %w", to.reckonedBalance, fundAmountQ, err) + } + } + select { case <-gracefulExit.Done(): transferLogger.Debug("time's up") diff --git a/go/oasis-node/cmd/debug/txsource/workload/workload.go b/go/oasis-node/cmd/debug/txsource/workload/workload.go index 3a556f56c74..94cb25763a7 100644 --- a/go/oasis-node/cmd/debug/txsource/workload/workload.go +++ b/go/oasis-node/cmd/debug/txsource/workload/workload.go @@ -2,14 +2,108 @@ package workload import ( "context" + "fmt" "math/rand" + "time" + "github.com/cenkalti/backoff/v4" "google.golang.org/grpc" + "github.com/oasislabs/oasis-core/go/common/crypto/signature" + "github.com/oasislabs/oasis-core/go/common/entity" + "github.com/oasislabs/oasis-core/go/common/logging" consensus "github.com/oasislabs/oasis-core/go/consensus/api" + "github.com/oasislabs/oasis-core/go/consensus/api/transaction" runtimeClient "github.com/oasislabs/oasis-core/go/runtime/client/api" + staking "github.com/oasislabs/oasis-core/go/staking/api" ) +const ( + maxSubmissionRetryElapsedTime = 120 * time.Second + maxSubmissionRetryInterval = 10 * time.Second + + fundAccountAmount = 100000000 + // gasPrice should be at least the configured min gas prices of validators. + gasPrice = 1 +) + +// FundAccountFromTestEntity funds an account from test entity. +func FundAccountFromTestEntity(ctx context.Context, logger *logging.Logger, cnsc consensus.ClientBackend, to signature.PublicKey) error { + _, testEntitySigner, _ := entity.TestEntity() + return transferFunds(ctx, logger, cnsc, testEntitySigner, to, fundAccountAmount) +} + +// transferFunds transfer funds between accounts. +func transferFunds(ctx context.Context, logger *logging.Logger, cnsc consensus.ClientBackend, from signature.Signer, to signature.PublicKey, transferAmount int64) error { + sched := backoff.NewExponentialBackOff() + sched.MaxInterval = maxSubmissionRetryInterval + sched.MaxElapsedTime = maxSubmissionRetryElapsedTime + // Since multiple workloads run simultaneously (in separate processes) + // there is a nonce race condition, in case of invalid nonce errors + // submission should be retried. (similarly as it is done in the + // SubmissionManager). + // Maybe just expose the SignAndSubmit() method in the + // consensus.ClientBackend? + return backoff.Retry(func() error { + // Get test entity nonce. + nonce, err := cnsc.GetSignerNonce(ctx, &consensus.GetSignerNonceRequest{ + ID: from.Public(), + Height: consensus.HeightLatest, + }) + if err != nil { + return backoff.Permanent(fmt.Errorf("GetSignerNonce TestEntity error: %w", err)) + } + + transfer := staking.Transfer{ + To: to, + } + if err = transfer.Tokens.FromInt64(transferAmount); err != nil { + return backoff.Permanent(fmt.Errorf("transfer tokens FromInt64 %d: %w", transferAmount, err)) + } + logger.Debug("transfering funds", "from", from.Public(), "to", to, "amount", transferAmount, "nonce", nonce) + + fee := transaction.Fee{} + tx := staking.NewTransferTx(nonce, &fee, &transfer) + // Estimate fee. + gas, err := cnsc.EstimateGas(ctx, &consensus.EstimateGasRequest{ + Caller: from.Public(), + Transaction: tx, + }) + if err != nil { + return fmt.Errorf("failed to estimate gas: %w", err) + } + tx.Fee.Gas = gas + feeAmount := int64(gas) * gasPrice + if err = tx.Fee.Amount.FromInt64(feeAmount); err != nil { + return fmt.Errorf("fee amount from int64: %w", err) + } + + signedTx, err := transaction.Sign(from, tx) + if err != nil { + return backoff.Permanent(fmt.Errorf("transaction.Sign: %w", err)) + } + + // Wait for a maximum of 5 seconds as submission may block forever in case the client node + // is skipping all CheckTx checks. + submitCtx, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() + if err = cnsc.SubmitTx(submitCtx, signedTx); err != nil { + // Expected errors are: + // - invalid nonce + // - timeout due to transaction being stuck due to invalid nonce (as client is skipping check-tx) + // In any case no it doesn't hurt to retry on all submission errors. + logger.Debug("SubmitTX error, retrying...", + "err", err, + "from", from.Public(), + "to", to, + "nonce", tx.Nonce, + ) + return err + } + return nil + }, backoff.WithContext(sched, ctx)) +} + // Workload is a DRBG-backed schedule of transactions. type Workload interface { // Run executes the workload. @@ -22,6 +116,7 @@ type Workload interface { conn *grpc.ClientConn, cnsc consensus.ClientBackend, rtc runtimeClient.RuntimeClient, + fundingAccount signature.Signer, ) error } diff --git a/go/oasis-test-runner/scenario/e2e/txsource.go b/go/oasis-test-runner/scenario/e2e/txsource.go index 2446b770a6d..42e7be08fed 100644 --- a/go/oasis-test-runner/scenario/e2e/txsource.go +++ b/go/oasis-test-runner/scenario/e2e/txsource.go @@ -24,7 +24,7 @@ import ( ) const ( - timeLimitShort = time.Minute + timeLimitShort = 3 * time.Minute timeLimitLong = 6 * time.Hour nodeRestartIntervalLong = 2 * time.Minute @@ -79,6 +79,12 @@ func (sc *txSourceImpl) Fixture() (*oasis.NetworkFixture, error) { // Disable CheckTx on the client node so we can submit invalid transactions. f.Clients[0].ConsensusDisableCheckTx = true + // MinGasPrice + f.Validators = []oasis.ValidatorFixture{ + oasis.ValidatorFixture{Entity: 1, MinGasPrice: 1}, + oasis.ValidatorFixture{Entity: 1, MinGasPrice: 1}, + oasis.ValidatorFixture{Entity: 1, MinGasPrice: 1}, + } return f, nil } diff --git a/tests/fixture-data/txsource/README.md b/tests/fixture-data/txsource/README.md deleted file mode 100644 index 547ad38ed73..00000000000 --- a/tests/fixture-data/txsource/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# Fixture data for txsource - -This data is parameterized for the default see -`seeeeeeeeeeeeeeeeeeeeeeeeeeeeeed` (UTF-8). - -## Inventory of configurations - -- Funded accounts for the transfer workload diff --git a/tests/fixture-data/txsource/staking-genesis.json b/tests/fixture-data/txsource/staking-genesis.json index 2da8939b67b..551789a0d03 100644 --- a/tests/fixture-data/txsource/staking-genesis.json +++ b/tests/fixture-data/txsource/staking-genesis.json @@ -1,15 +1,12 @@ { - "total_supply": "1000", - "ledger": { - "R3CRgwIWQxvnxpl1qmBm2HMOapCgaGswjtleKwnpyxg=": {"general": {"balance": "100"}}, - "arDOJeLJ9wZpuWa5t7+O8UGmZOXXdKQkN6t8IKbSylE=": {"general": {"balance": "100"}}, - "THXr3BkLrlDt89pu3AyeiVGB9u4igSx1fuLIFX98BNI=": {"general": {"balance": "100"}}, - "aYzCAO60yhEH0CWIGgEy+qM4TdNB+Ak8kq9Y/mpzQLY=": {"general": {"balance": "100"}}, - "aYGCs2K/owoE+nqLbOMFA5xqSo0SaHlI5Oj1btUFohI=": {"general": {"balance": "100"}}, - "GMGUREz0YmNCLGEvoLDpfRLYBxq1HQx9jQNDK6/Oajc=": {"general": {"balance": "100"}}, - "UJBCV7QyXc3cFIUqDvUT18RxNTtIIwZ3s6lVdC1Zs/U=": {"general": {"balance": "100"}}, - "QsJ1u2WAJiSH0m+ExtL1cRrmyfdhCV8pidgowj2g9rY=": {"general": {"balance": "100"}}, - "9zPKp7jryXaToR806PyzByNy5LVTwnie3Y3dkCxh1oo=": {"general": {"balance": "100"}}, - "+mLNei3EG4jmo6RDjjU6d/FswfbHHNpR+Lz0EdSaINw=": {"general": {"balance": "100"}} + "params": { + "gas_costs": { + "transfer": 10, + "burn": 10, + "add_escrow": 10, + "reclaim_escrow": 10 + }, + "fee_split_vote": "1", + "fee_split_propose": "1" } }