Skip to content

Commit

Permalink
Merge pull request ethereum#3 from aaronbuchwald/random-benchmark
Browse files Browse the repository at this point in the history
Random benchmark
  • Loading branch information
aaronbuchwald authored Oct 27, 2021
2 parents 40acb44 + 4349974 commit 4b0aa75
Showing 1 changed file with 158 additions and 5 deletions.
163 changes: 158 additions & 5 deletions core/bench_concurrent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,31 @@
package core

import (
cryptorand "crypto/rand"
"fmt"
"math/big"
"math/rand"
"os"
"path/filepath"
"testing"

"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/compiler"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core/rawdb"
"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(100000000000000000)
bankFunds = new(big.Int).Mul(big.NewInt(999999999999999999), big.NewInt(1000000000))
gspec = Genesis{
Config: params.TestPreLondonConfig,
Alloc: GenesisAlloc{
Expand All @@ -50,9 +53,155 @@ 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 := 0; i < numKeys; i++ {
res[i] = keystore.NewKeyForDirectICAP(cryptorand.Reader)
}
return res
}

// 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.
// 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)
}
contracts, err := compiler.CompileSolidity("", contractSrc)
if err != nil {
b.Fatal(err)
}
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 {
contract, exists = contracts["Storage.sol:Storage"]
}
if !exists {
b.Fatal("contract doesn't exist")
}
code := common.Hex2Bytes(contract.Code[2:])
// 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 {
b.Fatal(err)
}
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.
{
diskdb := rawdb.NewMemoryDatabase()
gspec.MustCommit(diskdb)

chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{
RequireAccessList: requireAccessList,
}, nil, nil)
if err != nil {
b.Fatalf("failed to create tester chain: %v", err)
}
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")
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))]
modifiedCallData := append(callDataMissingAddress, key.Address.Bytes()...)
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(key.Address), contractAddrs[rand.Intn(len(contractAddrs))], big.NewInt(0), 50_000, big.NewInt(1), modifiedCallData), signer, key.PrivateKey)
if err != nil {
b.Error(err)
}
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()
}
}

// Benchmarks large blocks with value transfers to non-existing accounts
func benchmarkArbitraryBlockExecution(b *testing.B, numBlocks int, numTxs int, requireAccessList bool) {
// Generate the original common chain segment and the two competing forks
Expand Down Expand Up @@ -109,11 +258,11 @@ func benchmarkArbitraryBlockExecution(b *testing.B, numBlocks int, numTxs int, r
contractAddrs = append(contractAddrs, receipt.ContractAddress)
}
}

callDataMissingAddress := common.Hex2Bytes("a6f9dae1000000000000000000000000")
//Create a storage contract 0xefc81a8c
//a6f9dae1
callDataMissingAddress := common.Hex2Bytes("6057361d000000000000000000000000")
blockGenerator := func(i int, block *BlockGen) {
block.SetCoinbase(common.Address{1})

for txi := 0; txi < numTxs; txi++ {
uniq := uint64(i*numTxs + txi + numContracts)
addr := common.Address{byte(txi)}
Expand Down Expand Up @@ -151,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 4b0aa75

Please sign in to comment.