Skip to content

Commit

Permalink
Merge pull request #292 from jhkimqd/jihwan/loadtest-blob
Browse files Browse the repository at this point in the history
feat: blob mode for loadtest
  • Loading branch information
jhkimqd authored Jun 4, 2024
2 parents e1187eb + ae50b2a commit 86be30b
Show file tree
Hide file tree
Showing 8 changed files with 198 additions and 7 deletions.
2 changes: 2 additions & 0 deletions cmd/loadtest/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ type (
ContractCallFunctionArgs *[]string
ContractCallPayable *bool
InscriptionContent *string
BlobFeeCap *uint64

// Computed
CurrentGasPrice *big.Int
Expand Down Expand Up @@ -229,6 +230,7 @@ func initFlags() {
ltp.SummaryOutputMode = LoadtestCmd.PersistentFlags().String("output-mode", "text", "Format mode for summary output (json | text)")
ltp.LegacyTransactionMode = LoadtestCmd.PersistentFlags().Bool("legacy", false, "Send a legacy transaction instead of an EIP1559 transaction.")
ltp.SendOnly = LoadtestCmd.PersistentFlags().Bool("send-only", false, "Send transactions and load without waiting for it to be mined.")
ltp.BlobFeeCap = LoadtestCmd.Flags().Uint64("blob-fee-cap", 100000, "The blob fee cap, or the maximum blob fee per chunk, in Gwei.")

// Local flags.
ltp.Modes = LoadtestCmd.Flags().StringSliceP("mode", "m", []string{"t"}, `The testing mode to use. It can be multiple like: "t,c,d,f"
Expand Down
107 changes: 107 additions & 0 deletions cmd/loadtest/blob.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package loadtest

import (
cryptorand "crypto/rand"
"crypto/sha256"
_ "embed"
"fmt"
"math/rand"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/ethereum/go-ethereum/params"

"github.com/ethereum/go-ethereum/core/types"
)

type BlobCommitment struct {
Blob kzg4844.Blob
Commitment kzg4844.Commitment
Proof kzg4844.Proof
VersionedHash common.Hash
}

// generateRandomBlobData will generate random data to be used for blob encoding
func generateRandomBlobData(size int) ([]byte, error) {
data := make([]byte, size)
n, err := cryptorand.Read(data)
if err != nil {
return nil, err
}
if n != size {
return nil, fmt.Errorf("Could not create random blob data with size %d: %v", size, err)
}
return data, nil
}

// createBlob takes in randomly generated byte slice and commits it with KZG
func createBlob(data []byte) kzg4844.Blob {
blob := kzg4844.Blob{}
fieldIndex := -1
for i := 0; i < len(data); i += 31 {
fieldIndex++
if fieldIndex == params.BlobTxFieldElementsPerBlob {
break
}
max := i + 31
if max > len(data) {
max = len(data)
}
copy(blob[fieldIndex*32+1:], data[i:max])
}
return blob
}

// generateBlobCommitment will generate the values for BlobCommitment variables
func generateBlobCommitment(data []byte) (*BlobCommitment, error) {
dataLen := len(data)
if dataLen > params.BlobTxFieldElementsPerBlob*(params.BlobTxBytesPerFieldElement-1) {
return nil, fmt.Errorf("Blob data longer than allowed (length: %v, limit: %v)", dataLen, params.BlobTxFieldElementsPerBlob*(params.BlobTxBytesPerFieldElement-1))
}
blobCommitment := BlobCommitment{
Blob: createBlob(data),
}
var err error

// Generate blob commitment
blobCommitment.Commitment, err = kzg4844.BlobToCommitment(blobCommitment.Blob)
if err != nil {
return nil, fmt.Errorf("Failed generating blob commitment: %w", err)
}

// Generate blob proof
blobCommitment.Proof, err = kzg4844.ComputeBlobProof(blobCommitment.Blob, blobCommitment.Commitment)
if err != nil {
return nil, fmt.Errorf("Failed generating blob proof: %w", err)
}

// Build versioned hash
blobCommitment.VersionedHash = sha256.Sum256(blobCommitment.Commitment[:])
blobCommitment.VersionedHash[0] = 0x01
return &blobCommitment, nil
}

// appendBlobCommitment will append the generated BlobCommitment values to blob transaction specific variables
func appendBlobCommitment(tx *types.BlobTx) error {
var err error
var blobBytes []byte
var blobRefBytes []byte
blobLen := rand.Intn((params.BlobTxFieldElementsPerBlob * (params.BlobTxBytesPerFieldElement - 1)) - len(blobBytes))
blobRefBytes, _ = generateRandomBlobData(blobLen)

if blobRefBytes == nil {
return fmt.Errorf("Unknown blob ref")
}
blobBytes = append(blobBytes, blobRefBytes...)

blobCommitment, err := generateBlobCommitment(blobBytes)
if err != nil {
return fmt.Errorf("Invalid blob: %w", err)
}

tx.BlobHashes = append(tx.BlobHashes, blobCommitment.VersionedHash)
tx.Sidecar.Blobs = append(tx.Sidecar.Blobs, blobCommitment.Blob)
tx.Sidecar.Commitments = append(tx.Sidecar.Commitments, blobCommitment.Commitment)
tx.Sidecar.Proofs = append(tx.Sidecar.Proofs, blobCommitment.Proof)
return nil
}
76 changes: 76 additions & 0 deletions cmd/loadtest/loadtest.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,17 @@ import (
"io"
"math/big"
"math/rand"

"os"
"os/signal"
"strings"
"sync"
"time"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/ethereum/go-ethereum/signer/core/apitypes"
"github.com/holiman/uint256"

"github.com/maticnetwork/polygon-cli/bindings/tester"
"github.com/maticnetwork/polygon-cli/bindings/tokens"
Expand All @@ -29,6 +33,7 @@ import (
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
ethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
ethtypes "github.com/ethereum/go-ethereum/core/types"
ethcrypto "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
Expand Down Expand Up @@ -64,6 +69,7 @@ const (
loadTestModeContractCall
loadTestModeInscription
loadTestModeUniswapV3
loadTestModeBlob

codeQualitySeed = "code code code code code code code code code code code quality"
codeQualityPrivateKey = "42b6e34dc21598a807dc19d7784c71b2a7a01f6480dc6f58258f78e539f1a1fa"
Expand Down Expand Up @@ -103,6 +109,8 @@ func characterToLoadTestMode(mode string) (loadTestMode, error) {
return loadTestModeContractCall, nil
case "inscription":
return loadTestModeInscription, nil
case "blob":
return loadTestModeBlob, nil
default:
return 0, fmt.Errorf("unrecognized load test mode: %s", mode)
}
Expand Down Expand Up @@ -637,6 +645,8 @@ func mainLoop(ctx context.Context, c *ethclient.Client, rpc *ethrpc.Client) erro
startReq, endReq, tErr = loadTestContractCall(ctx, c, myNonceValue)
case loadTestModeInscription:
startReq, endReq, tErr = loadTestInscription(ctx, c, myNonceValue)
case loadTestModeBlob:
startReq, endReq, tErr = loadTestBlob(ctx, c, myNonceValue)
default:
log.Error().Str("mode", mode.String()).Msg("We've arrived at a load test mode that we don't recognize")
}
Expand Down Expand Up @@ -1477,6 +1487,72 @@ func loadTestInscription(ctx context.Context, c *ethclient.Client, nonce uint64)
return
}

func loadTestBlob(ctx context.Context, c *ethclient.Client, nonce uint64) (t1 time.Time, t2 time.Time, err error) {
ltp := inputLoadTestParams

to := ltp.ToETHAddress
if *ltp.ToRandom {
to = getRandomAddress()
}

amount := ltp.SendAmount
chainID := new(big.Int).SetUint64(*ltp.ChainID)
privateKey := ltp.ECDSAPrivateKey

gasLimit := uint64(21000)
gasPrice, gasTipCap := getSuggestedGasPrices(ctx, c)
// blobFeeCap := uint64(1000000000) // 1eth
blobFeeCap := ltp.BlobFeeCap

// Initialize blobTx with blob transaction type
blobTx := ethtypes.BlobTx{
ChainID: uint256.NewInt(chainID.Uint64()),
Nonce: nonce,
GasTipCap: uint256.NewInt(gasTipCap.Uint64()),
GasFeeCap: uint256.NewInt(gasPrice.Uint64()),
BlobFeeCap: uint256.NewInt(*blobFeeCap),
Gas: gasLimit,
To: *to,
Value: uint256.NewInt(amount.Uint64()),
Data: nil,
AccessList: nil,
BlobHashes: make([]common.Hash, 0),
Sidecar: &types.BlobTxSidecar{
Blobs: make([]kzg4844.Blob, 0),
Commitments: make([]kzg4844.Commitment, 0),
Proofs: make([]kzg4844.Proof, 0),
},
}
// appendBlobCommitment() will take in the blobTx struct and append values to blob transaction specific keys in the following steps:
// The function will take in blobTx with empty BlobHashses, and Blob Sidecar variables initially.
// Then generateRandomBlobData() is called to generate a byte slice with random values.
// createBlob() is called to commit the randomly generated byte slice with KZG.
// generateBlobCommitment() will do the same for the Commitment and Proof.
// Append all the blob related computed values to the blobTx struct.
err = appendBlobCommitment(&blobTx)
if err != nil {
log.Error().Err(err).Msg("Unable to parse blob")
return
}
tx := types.NewTx(&blobTx)

stx, err := types.SignTx(tx, types.LatestSignerForChainID(chainID), privateKey)
if err != nil {
log.Error().Err(err).Msg("Unable to sign transaction")
return
}

t1 = time.Now()
defer func() { t2 = time.Now() }()
if *ltp.CallOnly {
log.Error().Err(err).Msg("CallOnly not supported to blob transactions")
return
} else {
err = c.SendTransaction(ctx, stx)
}
return
}

func recordSample(goRoutineID, requestID int64, err error, start, end time.Time, nonce uint64) {
s := loadTestSample{}
s.GoRoutineID = goRoutineID
Expand Down
5 changes: 3 additions & 2 deletions cmd/loadtest/loadtestmode_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions cmd/loadtest/uniswapv3.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
var (
//go:embed uniswapv3Usage.md
uniswapv3Usage string
uniswapv3LoadTestParams params
uniswapv3LoadTestParams uniswap3params
)

var uniswapV3LoadTestCmd = &cobra.Command{
Expand Down Expand Up @@ -62,15 +62,15 @@ func checkUniswapV3LoadtestFlags() error {
return nil
}

type params struct {
type uniswap3params struct {
UniswapFactoryV3, UniswapMulticall, UniswapProxyAdmin, UniswapTickLens, UniswapNFTLibDescriptor, UniswapNonfungibleTokenPositionDescriptor, UniswapUpgradeableProxy, UniswapNonfungiblePositionManager, UniswapMigrator, UniswapStaker, UniswapQuoterV2, UniswapSwapRouter, WETH9, UniswapPoolToken0, UniswapPoolToken1 *string
PoolFees *float64
SwapAmountInput *uint64
}

func init() {
// Specify subcommand flags.
params := new(params)
params := new(uniswap3params)

// Pre-deployed addresses.
params.UniswapFactoryV3 = uniswapV3LoadTestCmd.Flags().String("uniswap-factory-v3-address", "", "The address of a pre-deployed UniswapFactoryV3 contract")
Expand Down
1 change: 1 addition & 0 deletions doc/polycli_loadtest.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ The codebase has a contract that used for load testing. It's written in Solidity
--adaptive-rate-limit Enable AIMD-style congestion control to automatically adjust request rate
--adaptive-rate-limit-increment uint When using adaptive rate limiting, this flag controls the size of the additive increases. (default 50)
--batch-size uint Number of batches to perform at a time for receipt fetching. Default is 999 requests at a time. (default 999)
--blob-fee-cap uint The blob fee cap, or the maximum blob fee per chunk, in Gwei. (default 100000)
-b, --byte-count uint If we're in store mode, this controls how many bytes we'll try to store in our contract (default 1024)
--call-only When using this mode, rather than sending a transaction, we'll just call. This mode is incompatible with adaptive rate limiting, summarization, and a few other features.
--call-only-latest When using call only mode with recall, should we execute on the latest block or on the original block
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -130,13 +130,13 @@ require (
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
go.opencensus.io v0.24.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/oauth2 v0.20.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.20.0 // indirect
golang.org/x/term v0.20.0 // indirect
golang.org/x/tools v0.16.0 // indirect
golang.org/x/tools v0.21.0 // indirect
google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
Expand Down Expand Up @@ -525,6 +527,8 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM=
golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw=
golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down

0 comments on commit 86be30b

Please sign in to comment.