Skip to content
This repository has been archived by the owner on Dec 4, 2024. It is now read-only.

[Feature] Adding loadbot ERC20 and ERC721 token transfer mode #425

Merged
merged 40 commits into from
Mar 30, 2022
Merged
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
f968a1c
* Added erc20 contract bytecode and abi
ZeljkoBenovic Feb 10, 2022
3da7931
Added support for erc20 token transfer
ZeljkoBenovic Feb 10, 2022
a99d99c
Pleasing linter gods
ZeljkoBenovic Feb 11, 2022
28480f6
* Added gasUsed metric
ZeljkoBenovic Feb 15, 2022
a70c821
* Added average blocks utilistaion metric
ZeljkoBenovic Feb 15, 2022
d8e6ff2
Linter fixes
ZeljkoBenovic Feb 17, 2022
12ec83c
Merge branch 'develop' into feature/loadbot_erc20
ZeljkoBenovic Feb 17, 2022
61c2e9a
added erc721 loadbot mode
ZeljkoBenovic Feb 25, 2022
e155634
move contract bytecode encoding to GetExampleTransaction()
dbrajovic Feb 25, 2022
1e834c1
refactor erc20 mode
dbrajovic Mar 1, 2022
b5265d9
add erc20 generator
dbrajovic Mar 1, 2022
c77fac6
* Fix grammar errors
ZeljkoBenovic Mar 1, 2022
1251e7a
Merge branch 'feature/loadbot_erc721' into feature/loadbot_erc20
ZeljkoBenovic Mar 1, 2022
438adc5
* Added ERC721 support
ZeljkoBenovic Mar 2, 2022
6b3aef8
* Fixed some error returns
ZeljkoBenovic Mar 2, 2022
2a95042
increase wait for receipt timeout threshold
ZeljkoBenovic Mar 2, 2022
32f5d10
upped min receipt wait time to 2 min.
ZeljkoBenovic Mar 2, 2022
397510d
* Set max-conns default to very high value
ZeljkoBenovic Mar 2, 2022
7c8e097
pleasing linter gods
ZeljkoBenovic Mar 2, 2022
7e0ee0b
Merge branch 'develop' into feature/loadbot_erc20
ZeljkoBenovic Mar 7, 2022
e70281e
* refactored to acomodate Cobra CLI
ZeljkoBenovic Mar 7, 2022
2a541d3
removed receiptTimeout unnecessary declaration
ZeljkoBenovic Mar 8, 2022
4997af2
added calculateGasMetrics method
ZeljkoBenovic Mar 8, 2022
9660f02
calculateGasMetrics method set to run async
ZeljkoBenovic Mar 8, 2022
c181cf7
created updateGasEstimate method
ZeljkoBenovic Mar 26, 2022
168f73c
* moved constructor args to const
ZeljkoBenovic Mar 26, 2022
f2881dd
moved error channel out of the for loop
ZeljkoBenovic Mar 26, 2022
912c140
set json omitempty on ContractAddress and ContractBlockData
ZeljkoBenovic Mar 27, 2022
fd63762
* moved contract deployment exectution data to dedicated method
ZeljkoBenovic Mar 27, 2022
ecf9a2e
* using errorgroup to wait for errors for goroutines in calculateGasM…
ZeljkoBenovic Mar 27, 2022
a9000c8
* added isValidMode method
ZeljkoBenovic Mar 27, 2022
6e26304
* restored calcMaxTimeout func
ZeljkoBenovic Mar 27, 2022
3c8fa5a
* increased min wait time for receipts to 60s
ZeljkoBenovic Mar 27, 2022
65207fa
* added isTokenTransferMode helper method
ZeljkoBenovic Mar 27, 2022
0d0dc34
* moved contract related data from BaseGenerator to ContractTnxsGener…
ZeljkoBenovic Mar 27, 2022
37a0fba
pleasing lint gods
ZeljkoBenovic Mar 27, 2022
601ceef
Merge branch 'develop' into feature/loadbot_erc20
ZeljkoBenovic Mar 29, 2022
7e9ba05
resolve conflicts
ZeljkoBenovic Mar 29, 2022
ac607d1
Added token supply description
ZeljkoBenovic Mar 29, 2022
f87e680
lint fix
ZeljkoBenovic Mar 29, 2022
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
637 changes: 637 additions & 0 deletions command/loadbot/abis.go

Large diffs are not rendered by default.

100 changes: 100 additions & 0 deletions command/loadbot/deploy_contract.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package loadbot

import (
"context"
"fmt"
"sync/atomic"
"time"

"github.com/0xPolygon/polygon-edge/command/loadbot/generator"
"github.com/0xPolygon/polygon-edge/helper/tests"
txpoolOp "github.com/0xPolygon/polygon-edge/txpool/proto"
"github.com/0xPolygon/polygon-edge/types"

"github.com/umbracle/go-web3/jsonrpc"
)

func (l *Loadbot) deployContract(
grpcClient txpoolOp.TxnPoolOperatorClient,
jsonClient *jsonrpc.Client,
receiptTimeout time.Duration) error {
start := time.Now()

_, ok := l.generator.(generator.ContractTxnGenerator)
if !ok {
return fmt.Errorf("invalid generator type, it needs to be a generator.ContractTxnGenerator interface")
}

// deploy SC
txHash, err := l.executeTxn(grpcClient)
if err != nil {
//nolint:forcetypeassert
l.generator.(generator.ContractTxnGenerator).MarkFailedContractTxn(&generator.FailedContractTxnInfo{
TxHash: txHash.String(),
Error: &generator.TxnError{
Error: err,
ErrorType: generator.AddErrorType,
},
})
atomic.AddUint64(&l.metrics.ContractMetrics.FailedContractTransactionsCount, 1)

return fmt.Errorf("could not execute transaction, %w", err)
}

// set timeout
ctx, cancel := context.WithTimeout(context.Background(), receiptTimeout)
defer cancel()

// and wait for receipt
receipt, err := tests.WaitForReceipt(ctx, jsonClient.Eth(), txHash)

if err != nil {
//nolint:forcetypeassert
l.generator.(generator.ContractTxnGenerator).MarkFailedContractTxn(&generator.FailedContractTxnInfo{
TxHash: txHash.String(),
Error: &generator.TxnError{
Error: err,
ErrorType: generator.ReceiptErrorType,
},
})
atomic.AddUint64(&l.metrics.ContractMetrics.FailedContractTransactionsCount, 1)

return fmt.Errorf("could not get the receipt, %w", err)
}

end := time.Now()
// initialize gas metrics map with block nuber as index
l.metrics.ContractMetrics.ContractGasMetrics.Blocks[receipt.BlockNumber] = GasMetrics{}
// fetch contract address
l.metrics.ContractMetrics.ContractAddress = receipt.ContractAddress
// set contract address in order to get new example txn and gas estimate
//nolint:forcetypeassert
l.generator.(generator.ContractTxnGenerator).SetContractAddress(types.StringToAddress(
receipt.ContractAddress.String(),
))

// we're done with SC deployment
// we defined SC address and
// now get new gas estimates for CS token transfers
if err := l.updateGasEstimate(jsonClient); err != nil {
return fmt.Errorf("unable to get gas estimate, %w", err)
}

// record contract deployment metrics
l.metrics.ContractMetrics.ContractDeploymentDuration.reportTurnAroundTime(
txHash,
&metadata{
turnAroundTime: end.Sub(start),
blockNumber: receipt.BlockNumber,
},
)
// calculate contract deployment metrics
if err := l.calculateGasMetrics(jsonClient, l.metrics.ContractMetrics.ContractGasMetrics); err != nil {
return fmt.Errorf("unable to calculate contract block gas metrics: %w", err)
}

l.metrics.ContractMetrics.ContractDeploymentDuration.calcTurnAroundMetrics()
l.metrics.ContractMetrics.ContractDeploymentDuration.TotalExecTime = end.Sub(start)

return nil
}
120 changes: 87 additions & 33 deletions command/loadbot/execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,24 @@ import (
"context"
"crypto/ecdsa"
"fmt"
"math/big"
"sync"
"sync/atomic"
"time"

"github.com/0xPolygon/polygon-edge/command/loadbot/generator"
"github.com/0xPolygon/polygon-edge/helper/tests"
txpoolOp "github.com/0xPolygon/polygon-edge/txpool/proto"
"github.com/golang/protobuf/ptypes/any"
"github.com/umbracle/go-web3/jsonrpc"
"math/big"
"sync"
"sync/atomic"
"time"

"github.com/0xPolygon/polygon-edge/types"
"github.com/umbracle/go-web3"
)

const (
maxReceiptWait = 5 * time.Minute
minReceiptWait = 30 * time.Second
minReceiptWait = 1 * time.Minute

defaultFastestTurnAround = time.Hour * 24
defaultSlowestTurnAround = time.Duration(0)
Expand All @@ -33,6 +34,8 @@ type Mode string
const (
transfer Mode = "transfer"
deploy Mode = "deploy"
erc20 Mode = "erc20"
erc721 Mode = "erc721"
)

type Account struct {
Expand All @@ -54,6 +57,8 @@ type Configuration struct {
GasPrice *big.Int
GasLimit *big.Int
ContractArtifact *generator.ContractArtifact
ConstructorArgs []byte // smart contract constructor args
MaxWait uint64 // max wait time for receipts in minutes
}

type metadata struct {
Expand All @@ -64,10 +69,30 @@ type metadata struct {
blockNumber uint64
}

type GasMetrics struct {
GasUsed uint64
GasLimit uint64
Utilization float64
}

type BlockGasMetrics struct {
Blocks map[uint64]GasMetrics
BlockGasMutex *sync.Mutex
}

type ContractMetricsData struct {
FailedContractTransactionsCount uint64
ContractDeploymentDuration ExecDuration
ContractAddress web3.Address
ContractGasMetrics *BlockGasMetrics
}

type Metrics struct {
TotalTransactionsSentCount uint64
FailedTransactionsCount uint64
TransactionDuration ExecDuration
ContractMetrics ContractMetricsData
GasMetrics *BlockGasMetrics
}

type Loadbot struct {
Expand All @@ -85,6 +110,19 @@ func NewLoadbot(cfg *Configuration) *Loadbot {
TransactionDuration: ExecDuration{
blockTransactions: make(map[uint64]uint64),
},
ContractMetrics: ContractMetricsData{
ContractDeploymentDuration: ExecDuration{
blockTransactions: make(map[uint64]uint64),
},
ContractGasMetrics: &BlockGasMetrics{
Blocks: make(map[uint64]GasMetrics),
BlockGasMutex: &sync.Mutex{},
},
},
GasMetrics: &BlockGasMetrics{
Blocks: make(map[uint64]GasMetrics),
BlockGasMutex: &sync.Mutex{},
},
},
}
}
Expand Down Expand Up @@ -135,60 +173,70 @@ func (l *Loadbot) Run() error {

// Set up the transaction generator
generatorParams := &generator.GeneratorParams{
Nonce: nonce,
ChainID: l.cfg.ChainID,
SenderAddress: sender.Address,
SenderKey: sender.PrivateKey,
GasPrice: gasPrice,
Value: l.cfg.Value,
Nonce: nonce,
ChainID: l.cfg.ChainID,
SenderAddress: sender.Address,
RecieverAddress: l.cfg.Receiver,
SenderKey: sender.PrivateKey,
GasPrice: gasPrice,
Value: l.cfg.Value,
ContractArtifact: l.cfg.ContractArtifact,
ConstructorArgs: l.cfg.ConstructorArgs,
}

var (
txnGenerator generator.TransactionGenerator
genErr error = nil
txnGenerator generator.TransactionGenerator
tokenTxnGenerator generator.ContractTxnGenerator
genErr error
)

switch l.cfg.GeneratorMode {
case transfer:
txnGenerator, genErr = generator.NewTransferGenerator(generatorParams)
case deploy:
txnGenerator, genErr = generator.NewDeployGenerator(generatorParams)
case erc20:
tokenTxnGenerator, genErr = generator.NewERC20Generator(generatorParams)
case erc721:
tokenTxnGenerator, genErr = generator.NewERC721Generator(generatorParams)
}

if genErr != nil {
return fmt.Errorf("unable to start generator, %w", genErr)
}

l.generator = txnGenerator

// Get the gas estimate
exampleTxn, err := l.generator.GetExampleTransaction()
if err != nil {
return fmt.Errorf("unable to get example transaction, %w", err)
switch l.cfg.GeneratorMode {
case erc20, erc721:
l.generator = tokenTxnGenerator
default:
l.generator = txnGenerator
}

gasLimit := l.cfg.GasLimit
if gasLimit == nil {
// No gas limit specified, query the network for an estimation
gasEstimate, estimateErr := estimateGas(jsonClient, exampleTxn)
if estimateErr != nil {
return fmt.Errorf("unable to get gas estimate, %w", err)
}

gasLimit = new(big.Int).SetUint64(gasEstimate)
if err := l.updateGasEstimate(jsonClient); err != nil {
return fmt.Errorf("could not update gas estimate, %w", err)
}

l.generator.SetGasEstimate(gasLimit.Uint64())

ticker := time.NewTicker(1 * time.Second / time.Duration(l.cfg.TPS))
defer ticker.Stop()

var wg sync.WaitGroup

receiptTimeout := calcMaxTimeout(l.cfg.Count, l.cfg.TPS)
var receiptTimeout time.Duration
// if max-wait flag is not set it will be calculated dynamically
if l.cfg.MaxWait == 0 {
receiptTimeout = calcMaxTimeout(l.cfg.Count, l.cfg.TPS)
} else {
receiptTimeout = time.Duration(l.cfg.MaxWait) * time.Minute
}

startTime := time.Now()

if l.isTokenTransferMode() {
if err := l.deployContract(grpcClient, jsonClient, receiptTimeout); err != nil {
return fmt.Errorf("unable to deploy smart contract, %w", err)
}
}

var wg sync.WaitGroup

for i := uint64(0); i < l.cfg.Count; i++ {
<-ticker.C

Expand Down Expand Up @@ -236,6 +284,8 @@ func (l *Loadbot) Run() error {
return
}

l.initGasMetricsBlocksMap(receipt.BlockNumber)

// Stop the performance timer
end := time.Now()

Expand All @@ -253,6 +303,10 @@ func (l *Loadbot) Run() error {

endTime := time.Now()

if err := l.calculateGasMetrics(jsonClient, l.metrics.GasMetrics); err != nil {
return fmt.Errorf("unable to calculate block gas metrics: %w", err)
}

// Calculate the turn around metrics now that the loadbot is done
l.metrics.TransactionDuration.calcTurnAroundMetrics()
l.metrics.TransactionDuration.TotalExecTime = endTime.Sub(startTime)
Expand Down
3 changes: 2 additions & 1 deletion command/loadbot/generator/base.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package generator

import (
"github.com/0xPolygon/polygon-edge/crypto"
"sync"

"github.com/0xPolygon/polygon-edge/crypto"
)

type BaseGenerator struct {
Expand Down
Loading