github.com/palisadeinc/bor@v0.0.0-20230615125219-ab7196213d15/tests/bor/helper.go (about) 1 //go:build integration 2 3 package bor 4 5 import ( 6 "context" 7 "crypto/ecdsa" 8 "encoding/hex" 9 "encoding/json" 10 "fmt" 11 "io/ioutil" 12 "math/big" 13 "sort" 14 "testing" 15 "time" 16 17 "github.com/golang/mock/gomock" 18 19 "github.com/ethereum/go-ethereum/accounts" 20 "github.com/ethereum/go-ethereum/accounts/keystore" 21 "github.com/ethereum/go-ethereum/cmd/utils" 22 "github.com/ethereum/go-ethereum/common" 23 "github.com/ethereum/go-ethereum/consensus" 24 "github.com/ethereum/go-ethereum/consensus/bor" 25 "github.com/ethereum/go-ethereum/consensus/bor/clerk" 26 "github.com/ethereum/go-ethereum/consensus/bor/heimdall" //nolint:typecheck 27 "github.com/ethereum/go-ethereum/consensus/bor/heimdall/span" 28 "github.com/ethereum/go-ethereum/consensus/bor/valset" 29 "github.com/ethereum/go-ethereum/consensus/misc" 30 "github.com/ethereum/go-ethereum/core" 31 "github.com/ethereum/go-ethereum/core/state" 32 "github.com/ethereum/go-ethereum/core/types" 33 "github.com/ethereum/go-ethereum/core/vm" 34 "github.com/ethereum/go-ethereum/crypto" 35 "github.com/ethereum/go-ethereum/crypto/secp256k1" 36 "github.com/ethereum/go-ethereum/eth" 37 "github.com/ethereum/go-ethereum/eth/downloader" 38 "github.com/ethereum/go-ethereum/eth/ethconfig" 39 "github.com/ethereum/go-ethereum/ethdb" 40 "github.com/ethereum/go-ethereum/miner" 41 "github.com/ethereum/go-ethereum/node" 42 "github.com/ethereum/go-ethereum/p2p" 43 "github.com/ethereum/go-ethereum/p2p/enode" 44 "github.com/ethereum/go-ethereum/params" 45 "github.com/ethereum/go-ethereum/tests/bor/mocks" 46 ) 47 48 var ( 49 50 // Only this account is a validator for the 0th span 51 key, _ = crypto.HexToECDSA(privKey) 52 addr = crypto.PubkeyToAddress(key.PublicKey) // 0x71562b71999873DB5b286dF957af199Ec94617F7 53 54 // This account is one the validators for 1st span (0-indexed) 55 key2, _ = crypto.HexToECDSA(privKey2) 56 addr2 = crypto.PubkeyToAddress(key2.PublicKey) // 0x9fB29AAc15b9A4B7F17c3385939b007540f4d791 57 58 keys = []*ecdsa.PrivateKey{key, key2} 59 ) 60 61 const ( 62 privKey = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291" 63 privKey2 = "9b28f36fbd67381120752d6172ecdcf10e06ab2d9a1367aac00cdcd6ac7855d3" 64 65 // The genesis for tests was generated with following parameters 66 extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal 67 68 sprintSize uint64 = 4 69 spanSize uint64 = 8 70 71 validatorHeaderBytesLength = common.AddressLength + 20 // address + power 72 ) 73 74 type initializeData struct { 75 genesis *core.Genesis 76 ethereum *eth.Ethereum 77 } 78 79 func setupMiner(t *testing.T, n int, genesis *core.Genesis) ([]*node.Node, []*eth.Ethereum, []*enode.Node) { 80 t.Helper() 81 82 // Create an Ethash network based off of the Ropsten config 83 var ( 84 stacks []*node.Node 85 nodes []*eth.Ethereum 86 enodes []*enode.Node 87 ) 88 89 for i := 0; i < n; i++ { 90 // Start the node and wait until it's up 91 stack, ethBackend, err := InitMiner(genesis, keys[i], true) 92 if err != nil { 93 t.Fatal("Error occured while initialising miner", "error", err) 94 } 95 96 for stack.Server().NodeInfo().Ports.Listener == 0 { 97 time.Sleep(250 * time.Millisecond) 98 } 99 // Connect the node to all the previous ones 100 for _, n := range enodes { 101 stack.Server().AddPeer(n) 102 } 103 // Start tracking the node and its enode 104 stacks = append(stacks, stack) 105 nodes = append(nodes, ethBackend) 106 enodes = append(enodes, stack.Server().Self()) 107 } 108 109 return stacks, nodes, enodes 110 } 111 112 func buildEthereumInstance(t *testing.T, db ethdb.Database) *initializeData { 113 genesisData, err := ioutil.ReadFile("./testdata/genesis.json") 114 if err != nil { 115 t.Fatalf("%s", err) 116 } 117 118 gen := &core.Genesis{} 119 120 if err := json.Unmarshal(genesisData, gen); err != nil { 121 t.Fatalf("%s", err) 122 } 123 124 ethConf := ð.Config{ 125 Genesis: gen, 126 BorLogs: true, 127 } 128 129 ethConf.Genesis.MustCommit(db) 130 131 ethereum := utils.CreateBorEthereum(ethConf) 132 if err != nil { 133 t.Fatalf("failed to register Ethereum protocol: %v", err) 134 } 135 136 ethConf.Genesis.MustCommit(ethereum.ChainDb()) 137 138 ethereum.Engine().(*bor.Bor).Authorize(addr, func(account accounts.Account, s string, data []byte) ([]byte, error) { 139 return crypto.Sign(crypto.Keccak256(data), key) 140 }) 141 142 return &initializeData{ 143 genesis: gen, 144 ethereum: ethereum, 145 } 146 } 147 148 func insertNewBlock(t *testing.T, chain *core.BlockChain, block *types.Block) { 149 t.Helper() 150 151 if _, err := chain.InsertChain([]*types.Block{block}); err != nil { 152 t.Fatalf("%s", err) 153 } 154 } 155 156 type Option func(header *types.Header) 157 158 func buildNextBlock(t *testing.T, _bor consensus.Engine, chain *core.BlockChain, parentBlock *types.Block, signer []byte, borConfig *params.BorConfig, txs []*types.Transaction, currentValidators []*valset.Validator, opts ...Option) *types.Block { 159 t.Helper() 160 161 header := &types.Header{ 162 Number: big.NewInt(int64(parentBlock.Number().Uint64() + 1)), 163 Difficulty: big.NewInt(int64(parentBlock.Difficulty().Uint64())), 164 GasLimit: parentBlock.GasLimit(), 165 ParentHash: parentBlock.Hash(), 166 } 167 number := header.Number.Uint64() 168 169 if signer == nil { 170 signer = getSignerKey(header.Number.Uint64()) 171 } 172 173 header.Time = parentBlock.Time() + bor.CalcProducerDelay(header.Number.Uint64(), 0, borConfig) 174 header.Extra = make([]byte, 32+65) // vanity + extraSeal 175 176 isSpanStart := IsSpanStart(number) 177 isSprintEnd := IsSprintEnd(number) 178 179 if isSpanStart { 180 header.Difficulty = new(big.Int).SetInt64(int64(len(currentValidators))) 181 } 182 183 if isSprintEnd { 184 sort.Sort(valset.ValidatorsByAddress(currentValidators)) 185 186 validatorBytes := make([]byte, len(currentValidators)*validatorHeaderBytesLength) 187 header.Extra = make([]byte, 32+len(validatorBytes)+65) // vanity + validatorBytes + extraSeal 188 189 for i, val := range currentValidators { 190 copy(validatorBytes[i*validatorHeaderBytesLength:], val.HeaderBytes()) 191 } 192 193 copy(header.Extra[32:], validatorBytes) 194 } 195 196 if chain.Config().IsLondon(header.Number) { 197 header.BaseFee = misc.CalcBaseFee(chain.Config(), parentBlock.Header()) 198 199 if !chain.Config().IsLondon(parentBlock.Number()) { 200 parentGasLimit := parentBlock.GasLimit() * params.ElasticityMultiplier 201 header.GasLimit = core.CalcGasLimit(parentGasLimit, parentGasLimit) 202 } 203 } 204 205 for _, opt := range opts { 206 opt(header) 207 } 208 209 state, err := chain.State() 210 if err != nil { 211 t.Fatalf("%s", err) 212 } 213 214 b := &blockGen{header: header} 215 for _, tx := range txs { 216 b.addTxWithChain(chain, state, tx, addr) 217 } 218 219 ctx := context.Background() 220 221 // Finalize and seal the block 222 block, _ := _bor.FinalizeAndAssemble(ctx, chain, b.header, state, b.txs, nil, b.receipts) 223 224 // Write state changes to db 225 root, err := state.Commit(chain.Config().IsEIP158(b.header.Number)) 226 if err != nil { 227 panic(fmt.Sprintf("state write error: %v", err)) 228 } 229 230 if err := state.Database().TrieDB().Commit(root, false, nil); err != nil { 231 panic(fmt.Sprintf("trie write error: %v", err)) 232 } 233 234 res := make(chan *types.Block, 1) 235 236 err = _bor.Seal(ctx, chain, block, res, nil) 237 if err != nil { 238 // an error case - sign manually 239 sign(t, header, signer, borConfig) 240 return types.NewBlockWithHeader(header) 241 } 242 243 return <-res 244 } 245 246 type blockGen struct { 247 txs []*types.Transaction 248 receipts []*types.Receipt 249 gasPool *core.GasPool 250 header *types.Header 251 } 252 253 func (b *blockGen) addTxWithChain(bc *core.BlockChain, statedb *state.StateDB, tx *types.Transaction, coinbase common.Address) { 254 if b.gasPool == nil { 255 b.setCoinbase(coinbase) 256 } 257 258 statedb.Prepare(tx.Hash(), len(b.txs)) 259 260 receipt, err := core.ApplyTransaction(bc.Config(), bc, &b.header.Coinbase, b.gasPool, statedb, b.header, tx, &b.header.GasUsed, vm.Config{}, nil) 261 if err != nil { 262 panic(err) 263 } 264 265 b.txs = append(b.txs, tx) 266 b.receipts = append(b.receipts, receipt) 267 } 268 269 func (b *blockGen) setCoinbase(addr common.Address) { 270 if b.gasPool != nil { 271 if len(b.txs) > 0 { 272 panic("coinbase must be set before adding transactions") 273 } 274 275 panic("coinbase can only be set once") 276 } 277 278 b.header.Coinbase = addr 279 b.gasPool = new(core.GasPool).AddGas(b.header.GasLimit) 280 } 281 282 func sign(t *testing.T, header *types.Header, signer []byte, c *params.BorConfig) { 283 t.Helper() 284 285 sig, err := secp256k1.Sign(crypto.Keccak256(bor.BorRLP(header, c)), signer) 286 if err != nil { 287 t.Fatalf("%s", err) 288 } 289 290 copy(header.Extra[len(header.Extra)-extraSeal:], sig) 291 } 292 293 //nolint:unused,deadcode 294 func stateSyncEventsPayload(t *testing.T) *heimdall.StateSyncEventsResponse { 295 t.Helper() 296 297 stateData, err := ioutil.ReadFile("./testdata/states.json") 298 if err != nil { 299 t.Fatalf("%s", err) 300 } 301 302 res := &heimdall.StateSyncEventsResponse{} 303 if err := json.Unmarshal(stateData, res); err != nil { 304 t.Fatalf("%s", err) 305 } 306 307 return res 308 } 309 310 //nolint:unused,deadcode 311 func loadSpanFromFile(t *testing.T) (*heimdall.SpanResponse, *span.HeimdallSpan) { 312 t.Helper() 313 314 spanData, err := ioutil.ReadFile("./testdata/span.json") 315 if err != nil { 316 t.Fatalf("%s", err) 317 } 318 319 res := &heimdall.SpanResponse{} 320 321 if err := json.Unmarshal(spanData, res); err != nil { 322 t.Fatalf("%s", err) 323 } 324 325 return res, &res.Result 326 } 327 328 func getSignerKey(number uint64) []byte { 329 signerKey := privKey 330 331 if IsSpanStart(number) { 332 // validator set in the new span has changed 333 signerKey = privKey2 334 } 335 336 newKey, _ := hex.DecodeString(signerKey) 337 338 return newKey 339 } 340 341 func getMockedHeimdallClient(t *testing.T, heimdallSpan *span.HeimdallSpan) (*mocks.MockIHeimdallClient, *gomock.Controller) { 342 t.Helper() 343 344 ctrl := gomock.NewController(t) 345 h := mocks.NewMockIHeimdallClient(ctrl) 346 347 h.EXPECT().Span(gomock.Any(), uint64(1)).Return(heimdallSpan, nil).AnyTimes() 348 349 h.EXPECT().StateSyncEvents(gomock.Any(), gomock.Any(), gomock.Any()). 350 Return([]*clerk.EventRecordWithTime{getSampleEventRecord(t)}, nil).AnyTimes() 351 352 return h, ctrl 353 } 354 355 func getMockedSpanner(t *testing.T, validators []*valset.Validator) *bor.MockSpanner { 356 t.Helper() 357 358 spanner := bor.NewMockSpanner(gomock.NewController(t)) 359 spanner.EXPECT().GetCurrentValidatorsByHash(gomock.Any(), gomock.Any(), gomock.Any()).Return(validators, nil).AnyTimes() 360 spanner.EXPECT().GetCurrentValidatorsByBlockNrOrHash(gomock.Any(), gomock.Any(), gomock.Any()).Return(validators, nil).AnyTimes() 361 spanner.EXPECT().GetCurrentSpan(gomock.Any(), gomock.Any()).Return(&span.Span{0, 0, 0}, nil).AnyTimes() 362 spanner.EXPECT().CommitSpan(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() 363 return spanner 364 } 365 366 func generateFakeStateSyncEvents(sample *clerk.EventRecordWithTime, count int) []*clerk.EventRecordWithTime { 367 events := make([]*clerk.EventRecordWithTime, count) 368 event := *sample 369 event.ID = 1 370 events[0] = &clerk.EventRecordWithTime{} 371 *events[0] = event 372 373 for i := 1; i < count; i++ { 374 event.ID = uint64(i + 1) 375 event.Time = event.Time.Add(1 * time.Second) 376 events[i] = &clerk.EventRecordWithTime{} 377 *events[i] = event 378 } 379 380 return events 381 } 382 383 func buildStateEvent(sample *clerk.EventRecordWithTime, id uint64, timeStamp int64) *clerk.EventRecordWithTime { 384 event := *sample 385 event.ID = id 386 event.Time = time.Unix(timeStamp, 0) 387 388 return &event 389 } 390 391 func getSampleEventRecord(t *testing.T) *clerk.EventRecordWithTime { 392 t.Helper() 393 394 eventRecords := stateSyncEventsPayload(t) 395 eventRecords.Result[0].Time = time.Unix(1, 0) 396 397 return eventRecords.Result[0] 398 } 399 400 func newGwei(n int64) *big.Int { 401 return new(big.Int).Mul(big.NewInt(n), big.NewInt(params.GWei)) 402 } 403 404 func IsSpanEnd(number uint64) bool { 405 return (number+1)%spanSize == 0 406 } 407 408 func IsSpanStart(number uint64) bool { 409 return number%spanSize == 0 410 } 411 412 func IsSprintStart(number uint64) bool { 413 return number%sprintSize == 0 414 } 415 416 func IsSprintEnd(number uint64) bool { 417 return (number+1)%sprintSize == 0 418 } 419 420 func InitGenesis(t *testing.T, faucets []*ecdsa.PrivateKey, fileLocation string, sprintSize uint64) *core.Genesis { 421 t.Helper() 422 423 // sprint size = 8 in genesis 424 genesisData, err := ioutil.ReadFile(fileLocation) 425 if err != nil { 426 t.Fatalf("%s", err) 427 } 428 429 genesis := &core.Genesis{} 430 431 if err := json.Unmarshal(genesisData, genesis); err != nil { 432 t.Fatalf("%s", err) 433 } 434 435 genesis.Config.ChainID = big.NewInt(15001) 436 genesis.Config.EIP150Hash = common.Hash{} 437 genesis.Config.Bor.Sprint["0"] = sprintSize 438 439 return genesis 440 } 441 442 func InitMiner(genesis *core.Genesis, privKey *ecdsa.PrivateKey, withoutHeimdall bool) (*node.Node, *eth.Ethereum, error) { 443 // Define the basic configurations for the Ethereum node 444 datadir, _ := ioutil.TempDir("", "") 445 446 config := &node.Config{ 447 Name: "geth", 448 Version: params.Version, 449 DataDir: datadir, 450 P2P: p2p.Config{ 451 ListenAddr: "0.0.0.0:0", 452 NoDiscovery: true, 453 MaxPeers: 25, 454 }, 455 UseLightweightKDF: true, 456 } 457 // Create the node and configure a full Ethereum node on it 458 stack, err := node.New(config) 459 if err != nil { 460 return nil, nil, err 461 } 462 463 ethBackend, err := eth.New(stack, ðconfig.Config{ 464 Genesis: genesis, 465 NetworkId: genesis.Config.ChainID.Uint64(), 466 SyncMode: downloader.FullSync, 467 DatabaseCache: 256, 468 DatabaseHandles: 256, 469 TxPool: core.DefaultTxPoolConfig, 470 GPO: ethconfig.Defaults.GPO, 471 Ethash: ethconfig.Defaults.Ethash, 472 Miner: miner.Config{ 473 Etherbase: crypto.PubkeyToAddress(privKey.PublicKey), 474 GasCeil: genesis.GasLimit * 11 / 10, 475 GasPrice: big.NewInt(1), 476 Recommit: time.Second, 477 }, 478 WithoutHeimdall: withoutHeimdall, 479 }) 480 481 if err != nil { 482 return nil, nil, err 483 } 484 485 // register backend to account manager with keystore for signing 486 keydir := stack.KeyStoreDir() 487 488 n, p := keystore.StandardScryptN, keystore.StandardScryptP 489 kStore := keystore.NewKeyStore(keydir, n, p) 490 491 _, err = kStore.ImportECDSA(privKey, "") 492 493 if err != nil { 494 return nil, nil, err 495 } 496 497 acc := kStore.Accounts()[0] 498 err = kStore.Unlock(acc, "") 499 500 if err != nil { 501 return nil, nil, err 502 } 503 504 // proceed to authorize the local account manager in any case 505 ethBackend.AccountManager().AddBackend(kStore) 506 507 err = stack.Start() 508 509 return stack, ethBackend, err 510 }