From 74a32530dfb1af63106a0b25715cd61b8269d71a Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Wed, 15 Nov 2023 16:04:10 -0600 Subject: [PATCH] simulators/ethereum/engine: Add randomness seed input (#944) * simulators/ethereum/engine: randomness seed input * simulators/ethereum/engine: Handle zero HIVE_RANDOM_SEED --- simulators/ethereum/engine/clmock/clmock.go | 13 ++++--- .../ethereum/engine/helper/customizer.go | 34 +++++++++---------- simulators/ethereum/engine/main.go | 14 +++++++- .../ethereum/engine/suites/cancun/steps.go | 7 ++-- .../ethereum/engine/suites/engine/bad_hash.go | 7 ++-- .../engine/suites/engine/forkchoice.go | 5 ++- .../engine/suites/engine/invalid_ancestor.go | 8 ++--- .../engine/suites/engine/invalid_payload.go | 6 ++-- .../suites/engine/payload_attributes.go | 3 +- .../engine/suites/engine/payload_execution.go | 13 ++++--- .../ethereum/engine/suites/engine/reorg.go | 19 +++++------ .../suites/engine/suggested_fee_recipient.go | 5 ++- .../ethereum/engine/suites/sync/tests.go | 3 +- .../engine/suites/withdrawals/tests.go | 30 ++++++++-------- simulators/ethereum/engine/test/env.go | 8 ++++- 15 files changed, 94 insertions(+), 81 deletions(-) diff --git a/simulators/ethereum/engine/clmock/clmock.go b/simulators/ethereum/engine/clmock/clmock.go index 61ae26b235..b58c5aa18a 100644 --- a/simulators/ethereum/engine/clmock/clmock.go +++ b/simulators/ethereum/engine/clmock/clmock.go @@ -125,14 +125,12 @@ type CLMocker struct { // Global context which all procedures shall stop TestContext context.Context TimeoutContext context.Context -} -func NewCLMocker(t *hivesim.T, genesis *core.Genesis, forkConfig *config.ForkConfig) *CLMocker { - // Init random seed for different purposes - seed := time.Now().Unix() - t.Logf("Randomness seed: %v\n", seed) - rand.Seed(seed) + // Randomness source used to generate prevRandao + Rand *rand.Rand +} +func NewCLMocker(t *hivesim.T, genesis *core.Genesis, forkConfig *config.ForkConfig, randSource *rand.Rand) *CLMocker { // Create the new CL mocker newCLMocker := &CLMocker{ T: t, @@ -161,6 +159,7 @@ func NewCLMocker(t *hivesim.T, genesis *core.Genesis, forkConfig *config.ForkCon ForkConfig: forkConfig, Genesis: genesis, TestContext: context.Background(), + Rand: randSource, } // Create header history @@ -420,7 +419,7 @@ func TimestampToBeaconRoot(timestamp uint64) common.Hash { func (cl *CLMocker) GeneratePayloadAttributes() { // Generate a random value for the PrevRandao field nextPrevRandao := common.Hash{} - rand.Read(nextPrevRandao[:]) + cl.Rand.Read(nextPrevRandao[:]) cl.LatestPayloadAttributes = typ.PayloadAttributes{ Random: nextPrevRandao, diff --git a/simulators/ethereum/engine/helper/customizer.go b/simulators/ethereum/engine/helper/customizer.go index ac496e11da..b3b60af8c4 100644 --- a/simulators/ethereum/engine/helper/customizer.go +++ b/simulators/ethereum/engine/helper/customizer.go @@ -209,17 +209,17 @@ func (customizer *DowngradeForkchoiceUpdatedVersion) ForkchoiceUpdatedVersion(he var _ ForkchoiceUpdatedCustomizer = (*DowngradeForkchoiceUpdatedVersion)(nil) type PayloadCustomizer interface { - CustomizePayload(basePayload *typ.ExecutableData) (*typ.ExecutableData, error) + CustomizePayload(randSource *rand.Rand, basePayload *typ.ExecutableData) (*typ.ExecutableData, error) GetTimestamp(basePayload *typ.ExecutableData) (uint64, error) } type VersionedHashesCustomizer interface { - GetVersionedHashes(baseVesionedHashes *[]common.Hash) (*[]common.Hash, error) + GetVersionedHashes(randSource *rand.Rand, baseVesionedHashes *[]common.Hash) (*[]common.Hash, error) } type IncreaseVersionVersionedHashes struct{} -func (customizer *IncreaseVersionVersionedHashes) GetVersionedHashes(baseVesionedHashes *[]common.Hash) (*[]common.Hash, error) { +func (customizer *IncreaseVersionVersionedHashes) GetVersionedHashes(_ *rand.Rand, baseVesionedHashes *[]common.Hash) (*[]common.Hash, error) { if baseVesionedHashes == nil { return nil, fmt.Errorf("no versioned hashes available for modification") } @@ -236,7 +236,7 @@ func (customizer *IncreaseVersionVersionedHashes) GetVersionedHashes(baseVesione type CorruptVersionedHashes struct{} -func (customizer *CorruptVersionedHashes) GetVersionedHashes(baseVesionedHashes *[]common.Hash) (*[]common.Hash, error) { +func (customizer *CorruptVersionedHashes) GetVersionedHashes(_ *rand.Rand, baseVesionedHashes *[]common.Hash) (*[]common.Hash, error) { if baseVesionedHashes == nil { return nil, fmt.Errorf("no versioned hashes available for modification") } @@ -253,7 +253,7 @@ func (customizer *CorruptVersionedHashes) GetVersionedHashes(baseVesionedHashes type RemoveVersionedHash struct{} -func (customizer *RemoveVersionedHash) GetVersionedHashes(baseVesionedHashes *[]common.Hash) (*[]common.Hash, error) { +func (customizer *RemoveVersionedHash) GetVersionedHashes(_ *rand.Rand, baseVesionedHashes *[]common.Hash) (*[]common.Hash, error) { if baseVesionedHashes == nil { return nil, fmt.Errorf("no versioned hashes available for modification") } @@ -272,14 +272,14 @@ func (customizer *RemoveVersionedHash) GetVersionedHashes(baseVesionedHashes *[] type ExtraVersionedHash struct{} -func (customizer *ExtraVersionedHash) GetVersionedHashes(baseVesionedHashes *[]common.Hash) (*[]common.Hash, error) { +func (customizer *ExtraVersionedHash) GetVersionedHashes(randSource *rand.Rand, baseVesionedHashes *[]common.Hash) (*[]common.Hash, error) { if baseVesionedHashes == nil { return nil, fmt.Errorf("no versioned hashes available for modification") } result := make([]common.Hash, len(*baseVesionedHashes)+1) copy(result, *baseVesionedHashes) extraHash := common.Hash{} - rand.Read(extraHash[:]) + randSource.Read(extraHash[:]) extraHash[0] = cancun.BLOB_COMMITMENT_VERSION_KZG result[len(result)-1] = extraHash @@ -330,7 +330,7 @@ func (customData *CustomPayloadData) GetTimestamp(basePayload *typ.ExecutableDat // Construct a customized payload by taking an existing payload as base and mixing it CustomPayloadData // BlockHash is calculated automatically. -func (customData *CustomPayloadData) CustomizePayload(basePayload *typ.ExecutableData) (*typ.ExecutableData, error) { +func (customData *CustomPayloadData) CustomizePayload(randSource *rand.Rand, basePayload *typ.ExecutableData) (*typ.ExecutableData, error) { txs := basePayload.Transactions if customData.Transactions != nil { txs = *customData.Transactions @@ -460,7 +460,7 @@ func (customData *CustomPayloadData) CustomizePayload(basePayload *typ.Executabl } if customData.VersionedHashesCustomizer != nil { - result.VersionedHashes, err = customData.VersionedHashesCustomizer.GetVersionedHashes(basePayload.VersionedHashes) + result.VersionedHashes, err = customData.VersionedHashesCustomizer.GetVersionedHashes(randSource, basePayload.VersionedHashes) if err != nil { return nil, err } @@ -486,11 +486,11 @@ func (customizer *BaseNewPayloadVersionCustomizer) SetEngineAPIVersionResolver(v customizer.EngineAPIVersionResolver = v } -func (customNewPayload *BaseNewPayloadVersionCustomizer) CustomizePayload(basePayload *typ.ExecutableData) (*typ.ExecutableData, error) { +func (customNewPayload *BaseNewPayloadVersionCustomizer) CustomizePayload(randSource *rand.Rand, basePayload *typ.ExecutableData) (*typ.ExecutableData, error) { if customNewPayload.PayloadCustomizer == nil { return basePayload, nil } - return customNewPayload.PayloadCustomizer.CustomizePayload(basePayload) + return customNewPayload.PayloadCustomizer.CustomizePayload(randSource, basePayload) } func (customNewPayload *BaseNewPayloadVersionCustomizer) GetExpectedError() (*int, error) { @@ -527,7 +527,7 @@ func (customNewPayload *DowngradeNewPayloadVersion) NewPayloadVersion(timestamp return version - 1 } -func CustomizePayloadTransactions(basePayload *typ.ExecutableData, customTransactions types.Transactions) (*typ.ExecutableData, error) { +func CustomizePayloadTransactions(randSource *rand.Rand, basePayload *typ.ExecutableData, customTransactions types.Transactions) (*typ.ExecutableData, error) { byteTxs := make([][]byte, 0) for _, tx := range customTransactions { bytes, err := tx.MarshalBinary() @@ -538,7 +538,7 @@ func CustomizePayloadTransactions(basePayload *typ.ExecutableData, customTransac } return (&CustomPayloadData{ Transactions: &byteTxs, - }).CustomizePayload(basePayload) + }).CustomizePayload(randSource, basePayload) } func (customData *CustomPayloadData) String() string { @@ -590,7 +590,7 @@ func (customData *CustomPayloadData) String() string { // This function generates an invalid payload by taking a base payload and modifying the specified field such that it ends up being invalid. // One small consideration is that the payload needs to contain transactions and specially transactions using the PREVRANDAO opcode for all the fields to be compatible with this function. -func GenerateInvalidPayload(basePayload *typ.ExecutableData, payloadField InvalidPayloadBlockField) (*typ.ExecutableData, error) { +func GenerateInvalidPayload(randSource *rand.Rand, basePayload *typ.ExecutableData, payloadField InvalidPayloadBlockField) (*typ.ExecutableData, error) { var customPayloadMod *CustomPayloadData switch payloadField { @@ -636,7 +636,7 @@ func GenerateInvalidPayload(basePayload *typ.ExecutableData, payloadField Invali // This option potentially requires a transaction that uses the PREVRANDAO opcode. // Otherwise the payload will still be valid. modPrevRandao := common.Hash{} - rand.Read(modPrevRandao[:]) + randSource.Read(modPrevRandao[:]) customPayloadMod = &CustomPayloadData{ PrevRandao: &modPrevRandao, } @@ -781,7 +781,7 @@ func GenerateInvalidPayload(basePayload *typ.ExecutableData, payloadField Invali return ©Payload, nil } - return customPayloadMod.CustomizePayload(basePayload) + return customPayloadMod.CustomizePayload(randSource, basePayload) } /* @@ -789,7 +789,7 @@ func GenerateInvalidPayload(basePayload *typ.ExecutableData, payloadField Invali amounts and accounts, but the order in the list is different, so stateRoot of the resulting payload should be the same. */ -func RandomizeWithdrawalsOrder(src types.Withdrawals) types.Withdrawals { +func RandomizeWithdrawalsOrder(rand *rand.Rand, src types.Withdrawals) types.Withdrawals { dest := make(types.Withdrawals, len(src)) perm := rand.Perm(len(src)) for i, v := range perm { diff --git a/simulators/ethereum/engine/main.go b/simulators/ethereum/engine/main.go index b10a279afd..115a3bc51d 100644 --- a/simulators/ethereum/engine/main.go +++ b/simulators/ethereum/engine/main.go @@ -3,6 +3,7 @@ package main import ( "fmt" "math/big" + "math/rand" "os" "strconv" "sync" @@ -105,7 +106,17 @@ func makeRunner(tests []test.Spec, nodeType string) func(t *hivesim.T) { parallelism = p } } - t.Log("parallelism:", parallelism) + t.Log("parallelism", parallelism) + + random_seed := time.Now().Unix() + if val, ok := os.LookupEnv("HIVE_RANDOM_SEED"); ok { + if p, err := strconv.Atoi(val); err != nil { + t.Logf("Warning: invalid HIVE_RANDOM_SEED value %q", val) + } else if p > 0 { + random_seed = int64(p) + } + } + t.Log("random_seed", random_seed) var wg sync.WaitGroup var testCh = make(chan hivesim.TestSpec) @@ -214,6 +225,7 @@ func makeRunner(tests []test.Spec, nodeType string) func(t *hivesim.T) { t, c, genesis, + rand.New(rand.NewSource(random_seed)), newParams, testFiles, ) diff --git a/simulators/ethereum/engine/suites/cancun/steps.go b/simulators/ethereum/engine/suites/cancun/steps.go index 6c3ab0f67f..aa2df45baf 100644 --- a/simulators/ethereum/engine/suites/cancun/steps.go +++ b/simulators/ethereum/engine/suites/cancun/steps.go @@ -5,6 +5,7 @@ import ( "context" "fmt" "math/big" + "math/rand" "sync" "time" @@ -144,7 +145,7 @@ type VersionedHashes struct { HashVersions []byte } -func (v *VersionedHashes) GetVersionedHashes(*[]common.Hash) (*[]common.Hash, error) { +func (v *VersionedHashes) GetVersionedHashes(_ *rand.Rand, _ *[]common.Hash) (*[]common.Hash, error) { if v.Blobs == nil { return nil, nil } @@ -518,7 +519,7 @@ func (step NewPayloads) Execute(t *CancunTestContext) error { step.NewPayloadCustomizer.SetEngineAPIVersionResolver(t.ForkConfig) testEngine := t.TestEngine.WithEngineAPIVersionResolver(step.NewPayloadCustomizer) - payload, err = step.NewPayloadCustomizer.CustomizePayload(payload) + payload, err = step.NewPayloadCustomizer.CustomizePayload(t.Rand, payload) if err != nil { t.Fatalf("FAIL: Error customizing payload (payload %d/%d): %v", p+1, payloadCount, err) } @@ -714,7 +715,7 @@ func (step SendModifiedLatestPayload) Execute(t *CancunTestContext) error { // Send a custom new payload step.NewPayloadCustomizer.SetEngineAPIVersionResolver(t.ForkConfig) - payload, err = step.NewPayloadCustomizer.CustomizePayload(payload) + payload, err = step.NewPayloadCustomizer.CustomizePayload(t.Rand, payload) if err != nil { t.Fatalf("FAIL: Error customizing payload: %v", err) } diff --git a/simulators/ethereum/engine/suites/engine/bad_hash.go b/simulators/ethereum/engine/suites/engine/bad_hash.go index f484843cf8..478f68097f 100644 --- a/simulators/ethereum/engine/suites/engine/bad_hash.go +++ b/simulators/ethereum/engine/suites/engine/bad_hash.go @@ -2,7 +2,6 @@ package suite_engine import ( "fmt" - "math/rand" api "github.com/ethereum/go-ethereum/beacon/engine" "github.com/ethereum/go-ethereum/common" @@ -84,7 +83,7 @@ func (b BadHashOnNewPayload) Execute(t *test.Env) { } else if b.Syncing { // We need to send an fcU to put the client in SYNCING state. randomHeadBlock := common.Hash{} - rand.Read(randomHeadBlock[:]) + t.Rand.Read(randomHeadBlock[:]) fcU := api.ForkchoiceStateV1{ HeadBlockHash: randomHeadBlock, SafeBlockHash: t.CLMock.LatestHeader.Hash(), @@ -125,7 +124,7 @@ func (b BadHashOnNewPayload) Execute(t *test.Env) { customizer := &helper.CustomPayloadData{ ParentHash: &alteredPayload.BlockHash, } - alteredPayload, err := customizer.CustomizePayload(&t.CLMock.LatestPayloadBuilt) + alteredPayload, err := customizer.CustomizePayload(t.Rand, &t.CLMock.LatestPayloadBuilt) if err != nil { t.Fatalf("FAIL (%s): Unable to modify payload: %v", t.TestName, err) } @@ -177,7 +176,7 @@ func (b ParentHashOnNewPayload) Execute(t *test.Env) { alteredPayload := t.CLMock.LatestPayloadBuilt if b.Syncing { // Parent hash is unknown but also (incorrectly) set as the block hash - rand.Read(alteredPayload.ParentHash[:]) + t.Rand.Read(alteredPayload.ParentHash[:]) } alteredPayload.BlockHash = alteredPayload.ParentHash // Execution specification:: diff --git a/simulators/ethereum/engine/suites/engine/forkchoice.go b/simulators/ethereum/engine/suites/engine/forkchoice.go index e5f40c0674..81ef794143 100644 --- a/simulators/ethereum/engine/suites/engine/forkchoice.go +++ b/simulators/ethereum/engine/suites/engine/forkchoice.go @@ -2,7 +2,6 @@ package suite_engine import ( "fmt" - "math/rand" api "github.com/ethereum/go-ethereum/beacon/engine" "github.com/ethereum/go-ethereum/common" @@ -52,7 +51,7 @@ func (tc InconsistentForkchoiceTest) Execute(t *test.Env) { if len(alternativePayloads) > 0 { customData.ParentHash = &alternativePayloads[len(alternativePayloads)-1].BlockHash } - alternativePayload, err := customData.CustomizePayload(&t.CLMock.LatestPayloadBuilt) + alternativePayload, err := customData.CustomizePayload(t.Rand, &t.CLMock.LatestPayloadBuilt) if err != nil { t.Fatalf("FAIL (%s): Unable to construct alternative payload: %v", t.TestName, err) } @@ -112,7 +111,7 @@ func (tc ForkchoiceUpdatedUnknownBlockHashTest) Execute(t *test.Env) { // Generate a random block hash randomBlockHash := common.Hash{} - rand.Read(randomBlockHash[:]) + t.Rand.Read(randomBlockHash[:]) if tc.Field == HeadBlockHash { diff --git a/simulators/ethereum/engine/suites/engine/invalid_ancestor.go b/simulators/ethereum/engine/suites/engine/invalid_ancestor.go index fb8882cfb5..be0c378e30 100644 --- a/simulators/ethereum/engine/suites/engine/invalid_ancestor.go +++ b/simulators/ethereum/engine/suites/engine/invalid_ancestor.go @@ -106,12 +106,12 @@ func (tc InvalidMissingAncestorReOrgTest) Execute(t *test.Env) { ParentHash: &altChainPayloads[len(altChainPayloads)-1].BlockHash, ExtraData: &([]byte{0x01}), } - sidePayload, err = customizer.CustomizePayload(&t.CLMock.LatestPayloadBuilt) + sidePayload, err = customizer.CustomizePayload(t.Rand, &t.CLMock.LatestPayloadBuilt) if err != nil { t.Fatalf("FAIL (%s): Unable to customize payload: %v", t.TestName, err) } if len(altChainPayloads) == tc.InvalidIndex { - sidePayload, err = helper.GenerateInvalidPayload(sidePayload, tc.InvalidField) + sidePayload, err = helper.GenerateInvalidPayload(t.Rand, sidePayload, tc.InvalidField) if err != nil { t.Fatalf("FAIL (%s): Unable to customize payload: %v", t.TestName, err) } @@ -321,12 +321,12 @@ func (tc InvalidMissingAncestorReOrgSyncTest) Execute(t *test.Env) { ParentHash: &pHash, ExtraData: &([]byte{0x01}), } - sidePayload, err = customizer.CustomizePayload(&t.CLMock.LatestPayloadBuilt) + sidePayload, err = customizer.CustomizePayload(t.Rand, &t.CLMock.LatestPayloadBuilt) if err != nil { t.Fatalf("FAIL (%s): Unable to customize payload: %v", t.TestName, err) } if len(altChainPayloads) == tc.InvalidIndex { - sidePayload, err = helper.GenerateInvalidPayload(sidePayload, tc.InvalidField) + sidePayload, err = helper.GenerateInvalidPayload(t.Rand, sidePayload, tc.InvalidField) if err != nil { t.Fatalf("FAIL (%s): Unable to customize payload: %v", t.TestName, err) } diff --git a/simulators/ethereum/engine/suites/engine/invalid_payload.go b/simulators/ethereum/engine/suites/engine/invalid_payload.go index d38856eee3..f148dbe60f 100644 --- a/simulators/ethereum/engine/suites/engine/invalid_payload.go +++ b/simulators/ethereum/engine/suites/engine/invalid_payload.go @@ -136,7 +136,7 @@ func (tc InvalidPayloadTestCase) Execute(t *test.Env) { t.Fatalf("FAIL (%s): No transactions in the base payload", t.TestName) } - alteredPayload, err = helper.GenerateInvalidPayload(&t.CLMock.LatestPayloadBuilt, tc.InvalidField) + alteredPayload, err = helper.GenerateInvalidPayload(t.Rand, &t.CLMock.LatestPayloadBuilt, tc.InvalidField) if err != nil { t.Fatalf("FAIL (%s): Unable to modify payload (%v): %v", t.TestName, tc.InvalidField, err) } @@ -283,7 +283,7 @@ func (tc InvalidPayloadTestCase) Execute(t *test.Env) { } followUpAlteredPayload, err := (&helper.CustomPayloadData{ ParentHash: &alteredPayload.BlockHash, - }).CustomizePayload(&t.CLMock.LatestPayloadBuilt) + }).CustomizePayload(t.Rand, &t.CLMock.LatestPayloadBuilt) if err != nil { t.Fatalf("FAIL (%s): Unable to modify payload: %v", t.TestName, err) } @@ -376,7 +376,7 @@ func (tc PayloadBuildAfterInvalidPayloadTest) Execute(t *test.Env) { ) s.ExpectNoError() - inv_p, err = helper.GenerateInvalidPayload(&s.Payload, helper.InvalidStateRoot) + inv_p, err = helper.GenerateInvalidPayload(t.Rand, &s.Payload, helper.InvalidStateRoot) if err != nil { t.Fatalf("FAIL (%s): Unable to invalidate payload: %v", t.TestName, err) } diff --git a/simulators/ethereum/engine/suites/engine/payload_attributes.go b/simulators/ethereum/engine/suites/engine/payload_attributes.go index 9bbdeedb35..0f246a50ce 100644 --- a/simulators/ethereum/engine/suites/engine/payload_attributes.go +++ b/simulators/ethereum/engine/suites/engine/payload_attributes.go @@ -2,7 +2,6 @@ package suite_engine import ( "fmt" - "math/rand" "github.com/ethereum/hive/simulators/ethereum/engine/clmock" "github.com/ethereum/hive/simulators/ethereum/engine/config" @@ -48,7 +47,7 @@ func (tc InvalidPayloadAttributesTest) Execute(t *test.Env) { fcu := t.CLMock.LatestForkchoice if tc.Syncing { // Setting a random hash will put the client into `SYNCING` - rand.Read(fcu.HeadBlockHash[:]) + t.Rand.Read(fcu.HeadBlockHash[:]) } else { fcu.HeadBlockHash = t.CLMock.LatestPayloadBuilt.BlockHash } diff --git a/simulators/ethereum/engine/suites/engine/payload_execution.go b/simulators/ethereum/engine/suites/engine/payload_execution.go index e01995aaea..a86c4e253b 100644 --- a/simulators/ethereum/engine/suites/engine/payload_execution.go +++ b/simulators/ethereum/engine/suites/engine/payload_execution.go @@ -2,7 +2,6 @@ package suite_engine import ( "math/big" - "math/rand" api "github.com/ethereum/go-ethereum/beacon/engine" "github.com/ethereum/go-ethereum/common" @@ -117,7 +116,7 @@ func (spec InOrderPayloadExecutionTest) Execute(t *test.Env) { // We will be also verifying that the transactions are correctly interpreted in the canonical chain, // prepare a random account to receive funds. recipient := common.Address{} - rand.Read(recipient[:]) + t.Rand.Read(recipient[:]) amountPerTx := big.NewInt(1000) txPerPayload := 20 payloadCount := 10 @@ -243,7 +242,7 @@ func (spec MultiplePayloadsExtendingCanonicalChainTest) Execute(t *test.Env) { // We send the transactions after we got the Payload ID, before the CLMocker gets the prepared Payload OnPayloadProducerSelected: func() { recipient := common.Address{} - rand.Read(recipient[:]) + t.Rand.Read(recipient[:]) _, err := t.SendNextTransaction( t.TestContext, t.CLMock.NextBlockProducer, @@ -278,11 +277,11 @@ func (spec MultiplePayloadsExtendingCanonicalChainTest) Execute(t *test.Env) { // Fabricate and send multiple new payloads by changing the PrevRandao field for i := 0; i < payloadCount; i++ { newPrevRandao := common.Hash{} - rand.Read(newPrevRandao[:]) + t.Rand.Read(newPrevRandao[:]) customizer := &helper.CustomPayloadData{ PrevRandao: &newPrevRandao, } - newPayload, err := customizer.CustomizePayload(&basePayload) + newPayload, err := customizer.CustomizePayload(t.Rand, &basePayload) if err != nil { t.Fatalf("FAIL (%s): Unable to customize payload %v: %v", t.TestName, i, err) } @@ -347,7 +346,7 @@ func (spec NewPayloadOnSyncingClientTest) Execute(t *test.Env) { // Set a random transaction recipient recipient := common.Address{} - rand.Read(recipient[:]) + t.Rand.Read(recipient[:]) // Disconnect the first engine client from the CL Mocker and produce a block t.CLMock.RemoveEngineClient(t.Engine) @@ -484,7 +483,7 @@ func (spec NewPayloadWithMissingFcUTest) Execute(t *test.Env) { t.CLMock.ProduceBlocks(5, clmock.BlockProcessCallbacks{ OnPayloadProducerSelected: func() { var recipient common.Address - rand.Read(recipient[:]) + t.Rand.Read(recipient[:]) // Send at least one transaction per payload _, err := t.SendNextTransaction( t.TestContext, diff --git a/simulators/ethereum/engine/suites/engine/reorg.go b/simulators/ethereum/engine/suites/engine/reorg.go index 19eebe7882..05a96ee58c 100644 --- a/simulators/ethereum/engine/suites/engine/reorg.go +++ b/simulators/ethereum/engine/suites/engine/reorg.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "math/big" - "math/rand" "time" api "github.com/ethereum/go-ethereum/beacon/engine" @@ -66,7 +65,7 @@ func (spec SidechainReOrgTest) Execute(t *test.Env) { // At this point the CLMocker has a payload that will result in a specific outcome, // we can produce an alternative payload, send it, fcU to it, and verify the changes alternativePrevRandao := common.Hash{} - rand.Read(alternativePrevRandao[:]) + t.Rand.Read(alternativePrevRandao[:]) timestamp := t.CLMock.LatestPayloadBuilt.Timestamp + 1 payloadAttributes, err := (&helper.BasePayloadAttributesCustomizer{ Timestamp: ×tamp, @@ -199,7 +198,7 @@ func (spec TransactionReOrgTest) Execute(t *test.Env) { if spec.Scenario == TransactionReOrgScenarioReOrgOut { // Any payload we get should not contain any payloadAttributes := t.CLMock.LatestPayloadAttributes - rand.Read(payloadAttributes.Random[:]) + t.Rand.Read(payloadAttributes.Random[:]) r := t.TestEngine.TestEngineForkchoiceUpdated(&t.CLMock.LatestForkchoice, &payloadAttributes, t.CLMock.LatestHeader.Time) r.ExpectNoError() if r.Response.PayloadID == nil { @@ -246,7 +245,7 @@ func (spec TransactionReOrgTest) Execute(t *test.Env) { customizer := &helper.CustomPayloadData{ ExtraData: &([]byte{0x01}), } - altPayload, err = customizer.CustomizePayload(&t.CLMock.LatestPayloadBuilt) + altPayload, err = customizer.CustomizePayload(t.Rand, &t.CLMock.LatestPayloadBuilt) if err != nil { t.Fatalf("Error creating reorg payload %v", err) } @@ -272,7 +271,7 @@ func (spec TransactionReOrgTest) Execute(t *test.Env) { // impede it from being included in the next payload forkchoiceUpdated := t.CLMock.LatestForkchoice payloadAttributes := t.CLMock.LatestPayloadAttributes - rand.Read(payloadAttributes.SuggestedFeeRecipient[:]) + t.Rand.Read(payloadAttributes.SuggestedFeeRecipient[:]) f := t.TestEngine.TestEngineForkchoiceUpdated( &forkchoiceUpdated, &payloadAttributes, @@ -470,7 +469,7 @@ func (spec ReOrgBackToCanonicalTest) Execute(t *test.Env) { t.CLMock.ProduceSingleBlock(clmock.BlockProcessCallbacks{ OnPayloadAttributesGenerated: func() { payloadAttributes := t.CLMock.LatestPayloadAttributes - rand.Read(payloadAttributes.Random[:]) + t.Rand.Read(payloadAttributes.Random[:]) r := t.TestEngine.TestEngineForkchoiceUpdated(&t.CLMock.LatestForkchoice, &payloadAttributes, t.CLMock.LatestHeader.Time) r.ExpectNoError() if r.Response.PayloadID == nil { @@ -606,7 +605,7 @@ func (spec ReOrgBackFromSyncingTest) Execute(t *test.Env) { ParentHash: &altParentHash, ExtraData: &([]byte{0x01}), } - altPayload, err := customizer.CustomizePayload(&t.CLMock.LatestPayloadBuilt) + altPayload, err := customizer.CustomizePayload(t.Rand, &t.CLMock.LatestPayloadBuilt) if err != nil { t.Fatalf("FAIL (%s): Unable to customize payload: %v", t.TestName, err) } @@ -701,7 +700,7 @@ func (spec ReOrgPrevValidatedPayloadOnSideChainTest) Execute(t *test.Env) { if len(sidechainPayloads) > 0 { customData.ParentHash = &sidechainPayloads[len(sidechainPayloads)-1].BlockHash } - altPayload, err := customData.CustomizePayload(&t.CLMock.LatestPayloadBuilt) + altPayload, err := customData.CustomizePayload(t.Rand, &t.CLMock.LatestPayloadBuilt) if err != nil { t.Fatalf("FAIL (%s): Unable to customize payload: %v", t.TestName, err) } @@ -721,7 +720,7 @@ func (spec ReOrgPrevValidatedPayloadOnSideChainTest) Execute(t *test.Env) { prevRandao = common.Hash{} suggestedFeeRecipient = common.Address{0x12, 0x34} ) - rand.Read(prevRandao[:]) + t.Rand.Read(prevRandao[:]) payloadAttributesCustomizer := &helper.BasePayloadAttributesCustomizer{ Random: &prevRandao, SuggestedFeeRecipient: &suggestedFeeRecipient, @@ -800,7 +799,7 @@ func (s SafeReOrgToSideChainTest) Execute(t *test.Env) { ParentHash: &altParentHash, ExtraData: &([]byte{0x01}), } - altPayload, err := customizer.CustomizePayload(&t.CLMock.LatestPayloadBuilt) + altPayload, err := customizer.CustomizePayload(t.Rand, &t.CLMock.LatestPayloadBuilt) if err != nil { t.Fatalf("FAIL (%s): Unable to customize payload: %v", t.TestName, err) } diff --git a/simulators/ethereum/engine/suites/engine/suggested_fee_recipient.go b/simulators/ethereum/engine/suites/engine/suggested_fee_recipient.go index 9b8c834b8f..0c604fd87a 100644 --- a/simulators/ethereum/engine/suites/engine/suggested_fee_recipient.go +++ b/simulators/ethereum/engine/suites/engine/suggested_fee_recipient.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "math/big" - "math/rand" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/hive/simulators/ethereum/engine/clmock" @@ -38,9 +37,9 @@ func (tc SuggestedFeeRecipientTest) Execute(t *test.Env) { // Verify that, in a block with transactions, fees are accrued by the suggestedFeeRecipient feeRecipient := common.Address{} - rand.Read(feeRecipient[:]) + t.Rand.Read(feeRecipient[:]) txRecipient := common.Address{} - rand.Read(txRecipient[:]) + t.Rand.Read(txRecipient[:]) // Send multiple transactions for i := 0; i < tc.TransactionCount; i++ { diff --git a/simulators/ethereum/engine/suites/sync/tests.go b/simulators/ethereum/engine/suites/sync/tests.go index e94de670cb..73c34d3294 100644 --- a/simulators/ethereum/engine/suites/sync/tests.go +++ b/simulators/ethereum/engine/suites/sync/tests.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "math/big" + "math/rand" "time" api "github.com/ethereum/go-ethereum/beacon/engine" @@ -99,7 +100,7 @@ func AddSyncTestsToSuite(sim *hivesim.Simulation, suite *hivesim.Suite, tests [] } // Run the test case - test.Run(¤tTest, big.NewInt(ttd), timeout, t, c, &genesis, syncClientParams, testFiles.Copy()) + test.Run(¤tTest, big.NewInt(ttd), timeout, t, c, &genesis, rand.New(rand.NewSource(0)), syncClientParams, testFiles.Copy()) }, }) } diff --git a/simulators/ethereum/engine/suites/withdrawals/tests.go b/simulators/ethereum/engine/suites/withdrawals/tests.go index 0b8b9579a7..b2a405933a 100644 --- a/simulators/ethereum/engine/suites/withdrawals/tests.go +++ b/simulators/ethereum/engine/suites/withdrawals/tests.go @@ -1221,7 +1221,7 @@ func (ws *WithdrawalsBaseSpec) Execute(t *test.Env) { emptyWithdrawalsList := make(types.Withdrawals, 0) payloadWithEmptyWithdrawalsList, err := (&helper.CustomPayloadData{ Withdrawals: emptyWithdrawalsList, - }).CustomizePayload(&t.CLMock.LatestPayloadBuilt) + }).CustomizePayload(t.Rand, &t.CLMock.LatestPayloadBuilt) if err != nil { t.Fatalf("Unable to append withdrawals: %v", err) } @@ -1317,7 +1317,7 @@ func (ws *WithdrawalsBaseSpec) Execute(t *test.Env) { // be checked first instead of responding `INVALID` nilWithdrawalsPayload, err := (&helper.CustomPayloadData{ RemoveWithdrawals: true, - }).CustomizePayload(&t.CLMock.LatestPayloadBuilt) + }).CustomizePayload(t.Rand, &t.CLMock.LatestPayloadBuilt) if err != nil { t.Fatalf("Unable to append withdrawals: %v", err) } @@ -1370,7 +1370,7 @@ func (ws *WithdrawalsBaseSpec) Execute(t *test.Env) { payload := t.CLMock.LatestExecutedPayload // Corrupt the hash - rand.Read(payload.BlockHash[:]) + t.Rand.Read(payload.BlockHash[:]) // On engine_newPayloadV2 `INVALID_BLOCK_HASH` is deprecated // in favor of reusing `INVALID` @@ -1916,7 +1916,7 @@ func (s *MaxInitcodeSizeSpec) Execute(t *test.Env) { // Customize the payload to include a tx with an invalid initcode if invTx, ok := invalidTx.(*types.Transaction); ok { - customPayload, err := helper.CustomizePayloadTransactions(&t.CLMock.LatestPayloadBuilt, types.Transactions{invTx}) + customPayload, err := helper.CustomizePayloadTransactions(t.Rand, &t.CLMock.LatestPayloadBuilt, types.Transactions{invTx}) if err != nil { t.Fatalf("FAIL: Unable to customize payload: %v", err) } @@ -1944,7 +1944,7 @@ type GetPayloadBodiesSpec struct { } type GetPayloadBodyRequest interface { - Verify(int, *test.TestEngineClient, clmock.ExecutableDataHistory) + Verify(*rand.Rand, int, *test.TestEngineClient, clmock.ExecutableDataHistory) } type GetPayloadBodyRequestByRange struct { @@ -1952,7 +1952,7 @@ type GetPayloadBodyRequestByRange struct { Count uint64 } -func (req GetPayloadBodyRequestByRange) Verify(reqIndex int, testEngine *test.TestEngineClient, payloadHistory clmock.ExecutableDataHistory) { +func (req GetPayloadBodyRequestByRange) Verify(_ *rand.Rand, reqIndex int, testEngine *test.TestEngineClient, payloadHistory clmock.ExecutableDataHistory) { testEngine.Logf("INFO: Starting GetPayloadBodyByRange request %d", reqIndex) startTime := time.Now() defer func() { @@ -1997,7 +1997,7 @@ type GetPayloadBodyRequestByHashIndex struct { End uint64 } -func (req GetPayloadBodyRequestByHashIndex) Verify(reqIndex int, testEngine *test.TestEngineClient, payloadHistory clmock.ExecutableDataHistory) { +func (req GetPayloadBodyRequestByHashIndex) Verify(randSource *rand.Rand, reqIndex int, testEngine *test.TestEngineClient, payloadHistory clmock.ExecutableDataHistory) { testEngine.Logf("INFO: Starting GetPayloadBodyByHash request %d", reqIndex) startTime := time.Now() defer func() { @@ -2013,7 +2013,7 @@ func (req GetPayloadBodyRequestByHashIndex) Verify(reqIndex int, testEngine *tes } else { // signal to request an unknown hash (random) randHash := common.Hash{} - rand.Read(randHash[:]) + randSource.Read(randHash[:]) payloads = append(payloads, nil) hashes = append(hashes, randHash) } @@ -2027,7 +2027,7 @@ func (req GetPayloadBodyRequestByHashIndex) Verify(reqIndex int, testEngine *tes } else { // signal to request an unknown hash (random) randHash := common.Hash{} - rand.Read(randHash[:]) + randSource.Read(randHash[:]) payloads = append(payloads, nil) hashes = append(hashes, randHash) } @@ -2089,17 +2089,17 @@ func (ws *GetPayloadBodiesSpec) Execute(t *test.Env) { // Now we have an extra payload that follows the canonical chain, // but we need a side chain for the test. customizer := &helper.CustomPayloadData{ - Withdrawals: helper.RandomizeWithdrawalsOrder(t.CLMock.LatestExecutedPayload.Withdrawals), + Withdrawals: helper.RandomizeWithdrawalsOrder(t.Rand, t.CLMock.LatestExecutedPayload.Withdrawals), } - sidechainCurrent, err := customizer.CustomizePayload(&t.CLMock.LatestExecutedPayload) + sidechainCurrent, err := customizer.CustomizePayload(t.Rand, &t.CLMock.LatestExecutedPayload) if err != nil { t.Fatalf("FAIL (%s): Error obtaining custom sidechain payload: %v", t.TestName, err) } customizer = &helper.CustomPayloadData{ ParentHash: &sidechainCurrent.BlockHash, - Withdrawals: helper.RandomizeWithdrawalsOrder(nextCanonicalPayload.Withdrawals), + Withdrawals: helper.RandomizeWithdrawalsOrder(t.Rand, nextCanonicalPayload.Withdrawals), } - sidechainHead, err := customizer.CustomizePayload(nextCanonicalPayload) + sidechainHead, err := customizer.CustomizePayload(t.Rand, nextCanonicalPayload) if err != nil { t.Fatalf("FAIL (%s): Error obtaining custom sidechain payload: %v", t.TestName, err) } @@ -2158,7 +2158,7 @@ func (ws *GetPayloadBodiesSpec) Execute(t *test.Env) { go func() { defer wg.Done() for req := range workChan { - req.Request.Verify(req.Index, testEngine, payloadHistory) + req.Request.Verify(t.Rand, req.Index, testEngine, payloadHistory) } }() } @@ -2179,7 +2179,7 @@ func (ws *GetPayloadBodiesSpec) Execute(t *test.Env) { wg.Wait() } else { for i, req := range ws.GetPayloadBodiesRequests { - req.Verify(i, testEngine, payloadHistory) + req.Verify(t.Rand, i, testEngine, payloadHistory) } } diff --git a/simulators/ethereum/engine/test/env.go b/simulators/ethereum/engine/test/env.go index 8c1df04892..4fb48e5e59 100644 --- a/simulators/ethereum/engine/test/env.go +++ b/simulators/ethereum/engine/test/env.go @@ -3,6 +3,7 @@ package test import ( "context" "math/big" + "math/rand" "net/http" "time" @@ -30,6 +31,9 @@ type Env struct { // Test context will be done after timeout to allow test to gracefully finish TestContext context.Context + // Randomness source + Rand *rand.Rand + // RPC Clients Engine client.EngineClient Eth client.Eth @@ -51,13 +55,14 @@ type Env struct { TestTransactionType helper.TestTransactionType } -func Run(testSpec Spec, ttd *big.Int, timeout time.Duration, t *hivesim.T, c *hivesim.Client, genesis *core.Genesis, cParams hivesim.Params, cFiles hivesim.Params) { +func Run(testSpec Spec, ttd *big.Int, timeout time.Duration, t *hivesim.T, c *hivesim.Client, genesis *core.Genesis, randSource *rand.Rand, cParams hivesim.Params, cFiles hivesim.Params) { // Setup the CL Mocker for this test forkConfig := testSpec.GetForkConfig() clMocker := clmock.NewCLMocker( t, genesis, forkConfig, + randSource, ) // Send the CLMocker for configuration by the spec, if any. @@ -94,6 +99,7 @@ func Run(testSpec Spec, ttd *big.Int, timeout time.Duration, t *hivesim.T, c *hi ClientParams: cParams, ClientFiles: cFiles, TestTransactionType: testSpec.GetTestTransactionType(), + Rand: randSource, } env.Engines = append(env.Engines, ec) env.TestEngines = append(env.TestEngines, env.TestEngine)