From e4b5e2c9f3e23376b33d38c8db93e74292dc2281 Mon Sep 17 00:00:00 2001 From: ValarDragon Date: Sun, 28 Oct 2018 21:37:19 -0700 Subject: [PATCH 1/4] Make simulation use a transition matrix for block size This enables simulating periods of high load, and periods of low to no load. (low load because future ops will still terminate in that time frame) --- PENDING.md | 1 + client/lcd/certificates.go | 2 +- x/mock/simulation/constants.go | 6 +++ x/mock/simulation/random_simulate_blocks.go | 41 ++++++++++++--------- 4 files changed, 32 insertions(+), 18 deletions(-) diff --git a/PENDING.md b/PENDING.md index aa7cca2c4a..a43485bebd 100644 --- a/PENDING.md +++ b/PENDING.md @@ -37,6 +37,7 @@ IMPROVEMENTS * SDK - #2573 [x/distribution] add accum invariance + - \#1924 [simulation] Use a transition matrix for block size * Tendermint diff --git a/client/lcd/certificates.go b/client/lcd/certificates.go index 1516ed35af..f47f2397c7 100644 --- a/client/lcd/certificates.go +++ b/client/lcd/certificates.go @@ -43,7 +43,7 @@ func generateSelfSignedCert(host string) (certBytes []byte, priv *ecdsa.PrivateK KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, BasicConstraintsValid: true, - IsCA: true, + IsCA: true, } hosts := strings.Split(host, ",") for _, h := range hosts { diff --git a/x/mock/simulation/constants.go b/x/mock/simulation/constants.go index a96d4541f1..2c4543b72d 100644 --- a/x/mock/simulation/constants.go +++ b/x/mock/simulation/constants.go @@ -28,4 +28,10 @@ var ( {10, 50, 5}, {0, 10, 1000}, }) + // 3 states: rand in range [0, 4*provided blocksize], rand in range [0, 2 * provided blocksize], 0 + blockSizeTransitionMatrix, _ = CreateTransitionMatrix([][]int{ + {85, 5, 0}, + {15, 92, 1}, + {0, 3, 99}, + }) ) diff --git a/x/mock/simulation/random_simulate_blocks.go b/x/mock/simulation/random_simulate_blocks.go index 2506af377b..78939698b6 100644 --- a/x/mock/simulation/random_simulate_blocks.go +++ b/x/mock/simulation/random_simulate_blocks.go @@ -108,7 +108,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, blockLogBuilders = make([]*strings.Builder, numBlocks) } displayLogs := logPrinter(testingMode, blockLogBuilders) - blockSimulator := createBlockSimulator(testingMode, tb, t, event, invariants, ops, operationQueue, timeOperationQueue, numBlocks, displayLogs) + blockSimulator := createBlockSimulator(testingMode, tb, t, event, invariants, ops, operationQueue, timeOperationQueue, numBlocks, blockSize, displayLogs) if !testingMode { b.ResetTimer() } else { @@ -142,7 +142,6 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, } ctx := app.NewContext(false, header) - thisBlockSize := getBlockSize(r, blockSize) // Run queued operations. Ignores blocksize if blocksize is too small logWriter("Queued operations") @@ -153,9 +152,8 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, assertAllInvariants(t, app, header, invariants, "QueuedOperations", displayLogs) } - thisBlockSize = thisBlockSize - numQueuedOpsRan - numQueuedTimeOpsRan logWriter("Standard operations") - operations := blockSimulator(thisBlockSize, r, app, ctx, accs, header, logWriter) + operations := blockSimulator(r, app, ctx, accs, header, logWriter) opCount += operations + numQueuedOpsRan + numQueuedTimeOpsRan if testingMode { // Make sure invariants hold at end of block @@ -200,9 +198,14 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, // Returns a function to simulate blocks. Written like this to avoid constant parameters being passed everytime, to minimize // memory overhead -func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, event func(string), invariants []Invariant, ops []WeightedOperation, operationQueue map[int][]Operation, timeOperationQueue []FutureOperation, totalNumBlocks int, displayLogs func()) func( - blocksize int, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accounts []Account, header abci.Header, logWriter func(string)) (opCount int) { - totalOpWeight := 0 +func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, event func(string), invariants []Invariant, ops []WeightedOperation, operationQueue map[int][]Operation, timeOperationQueue []FutureOperation, totalNumBlocks int, avgBlockSize int, displayLogs func()) func( + r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accounts []Account, header abci.Header, logWriter func(string)) (opCount int) { + var ( + lastBlocksizeState = 0 // state for [4 * uniform distribution] + totalOpWeight = 0 + blocksize int + ) + for i := 0; i < len(ops); i++ { totalOpWeight += ops[i].Weight } @@ -217,8 +220,10 @@ func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, event f // shouldn't happen return ops[0].Op } - return func(blocksize int, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, + + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accounts []Account, header abci.Header, logWriter func(string)) (opCount int) { + lastBlocksizeState, blocksize = getBlockSize(r, lastBlocksizeState, avgBlockSize) for j := 0; j < blocksize; j++ { logUpdate, futureOps, err := selectOp(r)(r, app, ctx, accounts, event) if err != nil { @@ -253,16 +258,18 @@ func getTestingMode(tb testing.TB) (testingMode bool, t *testing.T, b *testing.B return } -func getBlockSize(r *rand.Rand, blockSize int) int { - load := r.Float64() - switch { - case load < 0.33: - return 0 - case load < 0.66: - return r.Intn(blockSize * 2) - default: - return r.Intn(blockSize * 4) +func getBlockSize(r *rand.Rand, lastBlockSizeState, avgBlockSize int) (state, blocksize int) { + // TODO: Make blockSizeTransitionMatrix non-global + // TODO: Make default blocksize transitition matrix actually make the average blocksize equal to avgBlockSize + state = blockSizeTransitionMatrix.NextState(r, lastBlockSizeState) + if state == 0 { + blocksize = r.Intn(avgBlockSize * 4) + } else if state == 1 { + blocksize = r.Intn(avgBlockSize * 2) + } else { + blocksize = 0 } + return } // adds all future operations into the operation queue. From 100f88800b02f79e04d1d6e4a47471077a6357eb Mon Sep 17 00:00:00 2001 From: ValarDragon Date: Mon, 29 Oct 2018 09:37:42 -0700 Subject: [PATCH 2/4] address bez's comments --- x/mock/simulation/random_simulate_blocks.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/x/mock/simulation/random_simulate_blocks.go b/x/mock/simulation/random_simulate_blocks.go index 78939698b6..7c3c38240d 100644 --- a/x/mock/simulation/random_simulate_blocks.go +++ b/x/mock/simulation/random_simulate_blocks.go @@ -198,8 +198,13 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, // Returns a function to simulate blocks. Written like this to avoid constant parameters being passed everytime, to minimize // memory overhead -func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, event func(string), invariants []Invariant, ops []WeightedOperation, operationQueue map[int][]Operation, timeOperationQueue []FutureOperation, totalNumBlocks int, avgBlockSize int, displayLogs func()) func( - r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accounts []Account, header abci.Header, logWriter func(string)) (opCount int) { +func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, + event func(string), invariants []Invariant, + ops []WeightedOperation, operationQueue map[int][]Operation, timeOperationQueue []FutureOperation, + totalNumBlocks int, avgBlockSize int, displayLogs func()) func( + r *rand.Rand, + app *baseapp.BaseApp, ctx sdk.Context, + accounts []Account, header abci.Header, logWriter func(string)) (opCount int) { var ( lastBlocksizeState = 0 // state for [4 * uniform distribution] totalOpWeight = 0 @@ -258,6 +263,12 @@ func getTestingMode(tb testing.TB) (testingMode bool, t *testing.T, b *testing.B return } +/** getBlockSize returns a block size as determined from the transition matrix. +It targets making average block size the provided parameter. +The three states it moves between are: +* "over stuffed" blocks with average size of 2 * avgblocksize, +* normal sized blocks, hitting avgBlocksize on average, +* and empty blocks, with no txs / only txs scheduled from the past. */ func getBlockSize(r *rand.Rand, lastBlockSizeState, avgBlockSize int) (state, blocksize int) { // TODO: Make blockSizeTransitionMatrix non-global // TODO: Make default blocksize transitition matrix actually make the average blocksize equal to avgBlockSize From 5e2efd999aae9ed12352ee4e64ce3f498ec6a811 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 31 Oct 2018 17:37:50 +0100 Subject: [PATCH 3/4] perform minor doc and function cleanup --- x/mock/simulation/random_simulate_blocks.go | 27 ++++++++++++--------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/x/mock/simulation/random_simulate_blocks.go b/x/mock/simulation/random_simulate_blocks.go index 7c3c38240d..ba477864f2 100644 --- a/x/mock/simulation/random_simulate_blocks.go +++ b/x/mock/simulation/random_simulate_blocks.go @@ -22,6 +22,12 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) +type blockSimFn func( + r *rand.Rand, + app *baseapp.BaseApp, ctx sdk.Context, + accounts []Account, header abci.Header, logWriter func(string), +) (opCount int) + // Simulate tests application by sending random messages. func Simulate(t *testing.T, app *baseapp.BaseApp, appStateFn func(r *rand.Rand, accs []Account) json.RawMessage, @@ -201,10 +207,8 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, event func(string), invariants []Invariant, ops []WeightedOperation, operationQueue map[int][]Operation, timeOperationQueue []FutureOperation, - totalNumBlocks int, avgBlockSize int, displayLogs func()) func( - r *rand.Rand, - app *baseapp.BaseApp, ctx sdk.Context, - accounts []Account, header abci.Header, logWriter func(string)) (opCount int) { + totalNumBlocks int, avgBlockSize int, displayLogs func()) blockSimFn { + var ( lastBlocksizeState = 0 // state for [4 * uniform distribution] totalOpWeight = 0 @@ -263,15 +267,16 @@ func getTestingMode(tb testing.TB) (testingMode bool, t *testing.T, b *testing.B return } -/** getBlockSize returns a block size as determined from the transition matrix. -It targets making average block size the provided parameter. -The three states it moves between are: -* "over stuffed" blocks with average size of 2 * avgblocksize, -* normal sized blocks, hitting avgBlocksize on average, -* and empty blocks, with no txs / only txs scheduled from the past. */ +// getBlockSize returns a block size as determined from the transition matrix. +// It targets making average block size the provided parameter. The three +// states it moves between are: +// "over stuffed" blocks with average size of 2 * avgblocksize, +// normal sized blocks, hitting avgBlocksize on average, +// and empty blocks, with no txs / only txs scheduled from the past. func getBlockSize(r *rand.Rand, lastBlockSizeState, avgBlockSize int) (state, blocksize int) { // TODO: Make blockSizeTransitionMatrix non-global - // TODO: Make default blocksize transitition matrix actually make the average blocksize equal to avgBlockSize + // TODO: Make default blocksize transition matrix actually make the average + // blocksize equal to avgBlockSize. state = blockSizeTransitionMatrix.NextState(r, lastBlockSizeState) if state == 0 { blocksize = r.Intn(avgBlockSize * 4) From 835315dcaa6745bc28a258131205adfadbf7a691 Mon Sep 17 00:00:00 2001 From: ValarDragon Date: Wed, 31 Oct 2018 10:11:41 -0700 Subject: [PATCH 4/4] move typedef --- x/mock/simulation/random_simulate_blocks.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/x/mock/simulation/random_simulate_blocks.go b/x/mock/simulation/random_simulate_blocks.go index ba477864f2..098c1647ab 100644 --- a/x/mock/simulation/random_simulate_blocks.go +++ b/x/mock/simulation/random_simulate_blocks.go @@ -22,12 +22,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -type blockSimFn func( - r *rand.Rand, - app *baseapp.BaseApp, ctx sdk.Context, - accounts []Account, header abci.Header, logWriter func(string), -) (opCount int) - // Simulate tests application by sending random messages. func Simulate(t *testing.T, app *baseapp.BaseApp, appStateFn func(r *rand.Rand, accs []Account) json.RawMessage, @@ -202,6 +196,11 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, return nil } +type blockSimFn func( + r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, + accounts []Account, header abci.Header, logWriter func(string), +) (opCount int) + // Returns a function to simulate blocks. Written like this to avoid constant parameters being passed everytime, to minimize // memory overhead func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T,