Skip to content

Commit

Permalink
Add simple working random block execution bencmark
Browse files Browse the repository at this point in the history
  • Loading branch information
aaronbuchwald committed Oct 26, 2021
1 parent 3445c77 commit 4349974
Showing 1 changed file with 78 additions and 11 deletions.
89 changes: 78 additions & 11 deletions core/bench_concurrent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,15 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
)

var (
signer = types.HomesteadSigner{}
testBankKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey)
bankFunds = big.NewInt(999999999999999999)
bankFunds = new(big.Int).Mul(big.NewInt(999999999999999999), big.NewInt(1000000000))
gspec = Genesis{
Config: params.TestPreLondonConfig,
Alloc: GenesisAlloc{
Expand All @@ -52,13 +53,12 @@ var (
},
GasLimit: 100e6, // 100 M
}
code []byte
)

// genRandomAddrs generates [numKeys] randomly generated private keys
func genRandomAddrs(numKeys int) []*keystore.Key {
res := make([]*keystore.Key, numKeys)
for i := 1; i < numKeys; i++ {
for i := 0; i < numKeys; i++ {
res[i] = keystore.NewKeyForDirectICAP(cryptorand.Reader)
}
return res
Expand All @@ -67,11 +67,19 @@ func genRandomAddrs(numKeys int) []*keystore.Key {
// generateRanodomExecution returns the list of blocks necessary to deploy [numContracts] and send dummy transactions from [numKeys] randomized
// over which key is sending the transaction as well as which contract is being called. This will be done for [numTxs] per block across [numBlocks].
// [requireAccessList] indicates whether the vmConfig will require a complete access list.
func generateRandomExecution(b *testing.B, numContracts int, numBlocks int, numTxs int, numKeys int, requireAccessList bool) []*types.Block {
// Also returns an int specifying the index of the first random block (as opposed to the setup blocks for deploying contracts and distributing funds).
func generateRandomExecution(b *testing.B, numContracts int, numBlocks int, numTxs int, numKeys int, requireAccessList bool) ([]*types.Block, int) {
engine := ethash.NewFaker()
db := rawdb.NewMemoryDatabase()
genesis := gspec.MustCommit(db)
// Create a an empty slice of blocks with enough space pre-allocated
// for [numBlocks], plus additional space for the block deploying contracts,
// the block sending funds to the randomly generated keys.
// Note: blocks excludes the genesis block.
blocks := make([]*types.Block, 0, numBlocks+2)
gopath := os.Getenv("GOPATH")

// generate contract from source code within the repo
contractSrc, err := filepath.Abs(gopath + "/src/github.com/ethereum/go-ethereum/core/Storage.sol")
if err != nil {
b.Fatal(err)
Expand All @@ -83,6 +91,8 @@ func generateRandomExecution(b *testing.B, numContracts int, numBlocks int, numT
if err != nil {
b.Fatal(err)
}

// Deploy [numContracts] instances of the Storage contract
contractAddrs := make([]common.Address, 0, numContracts)
contract, exists := contracts[fmt.Sprintf("%s:%s", contractSrc, "Storage")]
if !exists {
Expand All @@ -92,7 +102,8 @@ func generateRandomExecution(b *testing.B, numContracts int, numBlocks int, numT
b.Fatal("contract doesn't exist")
}
code := common.Hex2Bytes(contract.Code[2:])
generateContractChain, _ := GenerateChain(gspec.Config, genesis, engine, db, 1, func(i int, block *BlockGen) {
// Generate a single block that will be responsible for deploying the contracts at the start of the execution
generateContractsBlock, _ := GenerateChain(gspec.Config, genesis, engine, db, 1, func(i int, block *BlockGen) {
for i := 0; i < numContracts; i++ {
tx, err := types.SignTx(types.NewContractCreation(uint64(i), common.Big0, 1_000_000, common.Big1, code), signer, testBankKey)
if err != nil {
Expand All @@ -101,8 +112,13 @@ func generateRandomExecution(b *testing.B, numContracts int, numBlocks int, numT
block.AddTx(tx)
}
})
// Add the single block generated to deploy [numContracts] Storage contracts.
blocks = append(blocks, generateContractsBlock...)

// Create a closure here, so that we can simply throw away the database and blockchain
// that is created temporarily in order to get the receipts from the transactions. The
// receipts are used to get the addresses of the deployed contracts.
{
// Import the genertate contract chain to get the receipts
diskdb := rawdb.NewMemoryDatabase()
gspec.MustCommit(diskdb)

Expand All @@ -112,17 +128,35 @@ func generateRandomExecution(b *testing.B, numContracts int, numBlocks int, numT
if err != nil {
b.Fatalf("failed to create tester chain: %v", err)
}
if _, err := chain.InsertChain(generateContractChain); err != nil {
if _, err := chain.InsertChain(generateContractsBlock); err != nil {
b.Fatalf("failed to insert shared chain: %v", err)
}
receipts := chain.GetReceiptsByHash(chain.CurrentBlock().Hash())
for _, receipt := range receipts {
contractAddrs = append(contractAddrs, receipt.ContractAddress)
}
}

// Generate the private keys and create a slice of a single block that will
// be responsible for sending funds to each of the addresses.
keys := genRandomAddrs(numKeys)
fundPrivateKeysBlock, _ := GenerateChain(gspec.Config, blocks[len(blocks)-1], engine, db, numBlocks, func(i int, block *BlockGen) {
block.SetCoinbase(common.Address{1})

for _, key := range keys {
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), key.Address, new(big.Int).Mul(big.NewInt(10), big.NewInt(int64(params.Ether))), params.TxGas, nil, nil), signer, testBankKey)
if err != nil {
b.Fatal(err)
}
block.AddTx(tx)
}
})
blocks = append(blocks, fundPrivateKeysBlock...)

// Generate chain of [numBlocks], containing [numTxs] each, which will create random transactions calling a random
// storage contract from one of the randomly generated private keys.
callDataMissingAddress := common.Hex2Bytes("6057361d000000000000000000000000")
blockGenerator := func(i int, block *BlockGen) {
randomBlocks, _ := GenerateChain(gspec.Config, blocks[len(blocks)-1], engine, db, numBlocks, func(i int, block *BlockGen) {
block.SetCoinbase(common.Address{1})
for txi := 0; txi < numTxs; txi++ {
key := keys[rand.Intn(len(keys))]
Expand All @@ -133,10 +167,39 @@ func generateRandomExecution(b *testing.B, numContracts int, numBlocks int, numT
}
block.AddTx(tx)
}
})
index := len(blocks)
blocks = append(blocks, randomBlocks...)
return blocks, index
}

func benchmarkRandomBlockExecution(b *testing.B, numBlocks int, numTxs int, numContracts int, numKeys int, requireAccessList bool, memdb bool) {
// Generate the slice of blocks whose execution we wish to benchmark.
blocks, startIndex := generateRandomExecution(b, numContracts, numBlocks, numTxs, numKeys, requireAccessList)

for i := 0; i < b.N; i++ {
var diskdb ethdb.Database
if memdb {
diskdb = rawdb.NewMemoryDatabase()
} else {
panic("not implemented to use actual disk databaase TODO: use leveldb")
}
gspec.MustCommit(diskdb)
chain, err := NewBlockChain(diskdb, nil, gspec.Config, ethash.NewFaker(), vm.Config{
RequireAccessList: requireAccessList,
}, nil, nil)
if err != nil {
b.Fatalf("failed to create tester chain: %v", err)
}
if _, err := chain.InsertChain(blocks[:startIndex]); err != nil {
b.Fatalf("failed to insert setup blocks for random execution: %v", err)
}
b.StartTimer()
if _, err := chain.InsertChain(blocks[startIndex:]); err != nil {
b.Fatalf("failed to insert shared chain: %v", err)
}
b.StopTimer()
}
shared, _ := GenerateChain(gspec.Config, generateContractChain[0], engine, db, numBlocks, blockGenerator)
blocks := append(generateContractChain, shared...)
return blocks
}

// Benchmarks large blocks with value transfers to non-existing accounts
Expand Down Expand Up @@ -237,3 +300,7 @@ func benchmarkArbitraryBlockExecution(b *testing.B, numBlocks int, numTxs int, r
func BenchmarkSimpleBlockTransactionExecution(b *testing.B) {
benchmarkArbitraryBlockExecution(b, 10, 10, false)
}

func BenchmarkRandomParallelExecution(b *testing.B) {
benchmarkRandomBlockExecution(b, 10, 10, 10, 10, true, true)
}

0 comments on commit 4349974

Please sign in to comment.