github.com/klaytn/klaytn@v1.10.2/tests/klay_test_blockchain_test.go (about)

     1  // Copyright 2018 The klaytn Authors
     2  // This file is part of the klaytn library.
     3  //
     4  // The klaytn library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The klaytn library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the klaytn library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package tests
    18  
    19  import (
    20  	"bytes"
    21  	"crypto/ecdsa"
    22  	"encoding/json"
    23  	"errors"
    24  	"fmt"
    25  	"math/big"
    26  	"os"
    27  	"time"
    28  
    29  	"github.com/klaytn/klaytn/blockchain"
    30  	"github.com/klaytn/klaytn/blockchain/types"
    31  	"github.com/klaytn/klaytn/blockchain/vm"
    32  	"github.com/klaytn/klaytn/common"
    33  	"github.com/klaytn/klaytn/common/profile"
    34  	"github.com/klaytn/klaytn/consensus"
    35  	"github.com/klaytn/klaytn/consensus/istanbul"
    36  	istanbulBackend "github.com/klaytn/klaytn/consensus/istanbul/backend"
    37  	istanbulCore "github.com/klaytn/klaytn/consensus/istanbul/core"
    38  	"github.com/klaytn/klaytn/consensus/misc"
    39  	"github.com/klaytn/klaytn/crypto"
    40  	"github.com/klaytn/klaytn/crypto/sha3"
    41  	"github.com/klaytn/klaytn/governance"
    42  	"github.com/klaytn/klaytn/params"
    43  	"github.com/klaytn/klaytn/reward"
    44  	"github.com/klaytn/klaytn/rlp"
    45  	"github.com/klaytn/klaytn/storage/database"
    46  	"github.com/klaytn/klaytn/work"
    47  )
    48  
    49  const transactionsJournalFilename = "transactions.rlp"
    50  
    51  // If you don't want to remove 'chaindata', set removeChaindataOnExit = false
    52  const removeChaindataOnExit = true
    53  
    54  var errEmptyPending = errors.New("pending is empty")
    55  
    56  type BCData struct {
    57  	bc                 *blockchain.BlockChain
    58  	addrs              []*common.Address
    59  	privKeys           []*ecdsa.PrivateKey
    60  	db                 database.DBManager
    61  	rewardBase         *common.Address
    62  	validatorAddresses []common.Address
    63  	validatorPrivKeys  []*ecdsa.PrivateKey
    64  	engine             consensus.Istanbul
    65  	genesis            *blockchain.Genesis
    66  	governance         governance.Engine
    67  	rewardDistributor  *reward.RewardDistributor
    68  }
    69  
    70  var (
    71  	dir      = "chaindata"
    72  	nodeAddr = common.StringToAddress("nodeAddr")
    73  )
    74  
    75  func NewBCData(maxAccounts, numValidators int) (*BCData, error) {
    76  	if numValidators > maxAccounts {
    77  		return nil, errors.New("maxAccounts should be bigger numValidators!!")
    78  	}
    79  
    80  	// Remove leveldb dir if exists
    81  	if _, err := os.Stat(dir); err == nil {
    82  		os.RemoveAll(dir)
    83  	}
    84  
    85  	// Remove transactions.rlp if exists
    86  	if _, err := os.Stat(transactionsJournalFilename); err == nil {
    87  		os.RemoveAll(transactionsJournalFilename)
    88  	}
    89  
    90  	////////////////////////////////////////////////////////////////////////////////
    91  	// Create a database
    92  	chainDb := NewDatabase(dir, database.LevelDB)
    93  
    94  	////////////////////////////////////////////////////////////////////////////////
    95  	// Create a governance
    96  	gov := generateGovernaceDataForTest()
    97  	gov.SetNodeAddress(nodeAddr)
    98  	////////////////////////////////////////////////////////////////////////////////
    99  	// Create accounts as many as maxAccounts
   100  	addrs, privKeys, err := createAccounts(maxAccounts)
   101  	if err != nil {
   102  		return nil, err
   103  	}
   104  
   105  	////////////////////////////////////////////////////////////////////////////////
   106  	// Set the genesis address
   107  	genesisAddr := *addrs[0]
   108  
   109  	////////////////////////////////////////////////////////////////////////////////
   110  	// Use the first `numValidators` accounts as validators
   111  	validatorAddresses, validatorPrivKeys := getValidatorAddrsAndKeys(addrs, privKeys, numValidators)
   112  
   113  	////////////////////////////////////////////////////////////////////////////////
   114  	// Setup istanbul consensus backend
   115  	engine := istanbulBackend.New(genesisAddr, istanbul.DefaultConfig, validatorPrivKeys[0], chainDb, gov, common.CONSENSUSNODE)
   116  
   117  	////////////////////////////////////////////////////////////////////////////////
   118  	// Make a blockchain
   119  	bc, genesis, err := initBlockChain(chainDb, nil, addrs, validatorAddresses, nil, engine)
   120  	if err != nil {
   121  		return nil, err
   122  	}
   123  
   124  	engine.Start(bc, bc.CurrentBlock, bc.HasBadBlock)
   125  
   126  	governance.AddGovernanceCacheForTest(gov, 0, genesis.Config)
   127  	rewardDistributor := reward.NewRewardDistributor(gov)
   128  
   129  	return &BCData{
   130  		bc, addrs, privKeys, chainDb,
   131  		&genesisAddr, validatorAddresses,
   132  		validatorPrivKeys, engine, genesis, gov, rewardDistributor,
   133  	}, nil
   134  }
   135  
   136  func (bcdata *BCData) Shutdown() {
   137  	bcdata.bc.Stop()
   138  
   139  	bcdata.db.Close()
   140  	// Remove leveldb dir which was created for this test.
   141  	if removeChaindataOnExit {
   142  		os.RemoveAll(dir)
   143  		os.RemoveAll(transactionsJournalFilename)
   144  	}
   145  }
   146  
   147  func (bcdata *BCData) prepareHeader() (*types.Header, error) {
   148  	tstart := time.Now()
   149  	parent := bcdata.bc.CurrentBlock()
   150  
   151  	tstamp := tstart.Unix()
   152  	if parent.Time().Cmp(new(big.Int).SetInt64(tstamp)) >= 0 {
   153  		tstamp = parent.Time().Int64() + 1
   154  	}
   155  	// this will ensure we're not going off too far in the future
   156  	if now := time.Now().Unix(); tstamp > now {
   157  		wait := time.Duration(tstamp-now) * time.Second
   158  		time.Sleep(wait)
   159  	}
   160  
   161  	num := new(big.Int).Add(parent.Number(), common.Big1)
   162  	header := &types.Header{
   163  		ParentHash: parent.Hash(),
   164  		Number:     num,
   165  		Time:       big.NewInt(tstamp),
   166  		Governance: common.Hex2Bytes("b8dc7b22676f7665726e696e676e6f6465223a22307865373333636234643237396461363936663330643437306638633034646563623534666362306432222c22676f7665726e616e63656d6f6465223a2273696e676c65222c22726577617264223a7b226d696e74696e67616d6f756e74223a393630303030303030303030303030303030302c22726174696f223a2233342f33332f3333227d2c22626674223a7b2265706f6368223a33303030302c22706f6c696379223a302c22737562223a32317d2c22756e69745072696365223a32353030303030303030307d"),
   167  		Vote:       common.Hex2Bytes("e194e733cb4d279da696f30d470f8c04decb54fcb0d28565706f6368853330303030"),
   168  	}
   169  	if bcdata.bc.Config().IsMagmaForkEnabled(num) {
   170  		header.BaseFee = misc.NextMagmaBlockBaseFee(parent.Header(), bcdata.bc.Config().Governance.KIP71)
   171  	}
   172  
   173  	if err := bcdata.engine.Prepare(bcdata.bc, header); err != nil {
   174  		return nil, errors.New(fmt.Sprintf("Failed to prepare header for mining %s.\n", err))
   175  	}
   176  
   177  	return header, nil
   178  }
   179  
   180  func (bcdata *BCData) MineABlock(transactions types.Transactions, signer types.Signer, prof *profile.Profiler) (*types.Block, types.Receipts, error) {
   181  	// Set the block header
   182  	start := time.Now()
   183  	header, err := bcdata.prepareHeader()
   184  	if err != nil {
   185  		return nil, nil, err
   186  	}
   187  	prof.Profile("mine_prepareHeader", time.Now().Sub(start))
   188  
   189  	statedb, err := bcdata.bc.State()
   190  	if err != nil {
   191  		return nil, nil, err
   192  	}
   193  
   194  	// Group transactions by the sender address
   195  	start = time.Now()
   196  	txs := make(map[common.Address]types.Transactions)
   197  	for _, tx := range transactions {
   198  		acc, err := types.Sender(signer, tx)
   199  		if err != nil {
   200  			return nil, nil, err
   201  		}
   202  		txs[acc] = append(txs[acc], tx)
   203  	}
   204  	prof.Profile("mine_groupTransactions", time.Now().Sub(start))
   205  
   206  	// Create a transaction set where transactions are sorted by price and nonce
   207  	start = time.Now()
   208  	txset := types.NewTransactionsByTimeAndNonce(signer, txs)
   209  	prof.Profile("mine_NewTransactionsByPriceAndNonce", time.Now().Sub(start))
   210  
   211  	// Apply the set of transactions
   212  	start = time.Now()
   213  	task := work.NewTask(bcdata.bc.Config(), signer, statedb, header)
   214  	task.ApplyTransactions(txset, bcdata.bc, *bcdata.rewardBase)
   215  	newtxs := task.Transactions()
   216  	receipts := task.Receipts()
   217  	prof.Profile("mine_ApplyTransactions", time.Now().Sub(start))
   218  
   219  	// Finalize the block
   220  	start = time.Now()
   221  	b, err := bcdata.engine.Finalize(bcdata.bc, header, statedb, newtxs, receipts)
   222  	if err != nil {
   223  		return nil, nil, err
   224  	}
   225  	prof.Profile("mine_finalize_block", time.Now().Sub(start))
   226  
   227  	////////////////////////////////////////////////////////////////////////////////
   228  
   229  	start = time.Now()
   230  	b, err = sealBlock(b, bcdata.validatorPrivKeys)
   231  	if err != nil {
   232  		return nil, nil, err
   233  	}
   234  	prof.Profile("mine_seal_block", time.Now().Sub(start))
   235  
   236  	return b, receipts, nil
   237  }
   238  
   239  func (bcdata *BCData) GenABlock(accountMap *AccountMap, opt *testOption,
   240  	numTransactions int, prof *profile.Profiler,
   241  ) error {
   242  	// Make a set of transactions
   243  	start := time.Now()
   244  	signer := types.MakeSigner(bcdata.bc.Config(), bcdata.bc.CurrentHeader().Number)
   245  	transactions, err := opt.makeTransactions(bcdata, accountMap, signer, numTransactions, nil, opt.txdata)
   246  	if err != nil {
   247  		return err
   248  	}
   249  	prof.Profile("main_makeTransactions", time.Now().Sub(start))
   250  
   251  	return bcdata.GenABlockWithTransactions(accountMap, transactions, prof)
   252  }
   253  
   254  func (bcdata *BCData) GenABlockWithTxpool(accountMap *AccountMap, txpool *blockchain.TxPool,
   255  	prof *profile.Profiler,
   256  ) error {
   257  	signer := types.MakeSigner(bcdata.bc.Config(), bcdata.bc.CurrentHeader().Number)
   258  
   259  	pending, err := txpool.Pending()
   260  	if err != nil {
   261  		return err
   262  	}
   263  	if len(pending) == 0 {
   264  		return errEmptyPending
   265  	}
   266  	pooltxs := types.NewTransactionsByTimeAndNonce(signer, pending)
   267  
   268  	// Set the block header
   269  	start := time.Now()
   270  	header, err := bcdata.prepareHeader()
   271  	if err != nil {
   272  		return err
   273  	}
   274  	prof.Profile("mine_prepareHeader", time.Now().Sub(start))
   275  
   276  	statedb, err := bcdata.bc.State()
   277  	if err != nil {
   278  		return err
   279  	}
   280  
   281  	start = time.Now()
   282  	task := work.NewTask(bcdata.bc.Config(), signer, statedb, header)
   283  	task.ApplyTransactions(pooltxs, bcdata.bc, *bcdata.rewardBase)
   284  	newtxs := task.Transactions()
   285  	receipts := task.Receipts()
   286  	prof.Profile("mine_ApplyTransactions", time.Now().Sub(start))
   287  
   288  	// Finalize the block
   289  	start = time.Now()
   290  	b, err := bcdata.engine.Finalize(bcdata.bc, header, statedb, newtxs, receipts)
   291  	if err != nil {
   292  		return err
   293  	}
   294  	prof.Profile("mine_finalize_block", time.Now().Sub(start))
   295  
   296  	start = time.Now()
   297  	b, err = sealBlock(b, bcdata.validatorPrivKeys)
   298  	if err != nil {
   299  		return err
   300  	}
   301  	prof.Profile("mine_seal_block", time.Now().Sub(start))
   302  
   303  	// Update accountMap
   304  	start = time.Now()
   305  	if err := accountMap.Update(newtxs, signer, statedb, b.NumberU64()); err != nil {
   306  		return err
   307  	}
   308  	prof.Profile("main_update_accountMap", time.Now().Sub(start))
   309  
   310  	// Insert the block into the blockchain
   311  	start = time.Now()
   312  	if n, err := bcdata.bc.InsertChain(types.Blocks{b}); err != nil {
   313  		return fmt.Errorf("err = %s, n = %d\n", err, n)
   314  	}
   315  	prof.Profile("main_insert_blockchain", time.Now().Sub(start))
   316  
   317  	// Apply reward
   318  	config := bcdata.bc.Config()
   319  	rules := config.Rules(bcdata.bc.CurrentHeader().Number)
   320  	pset, err := params.NewGovParamSetChainConfig(config)
   321  	if err != nil {
   322  		return err
   323  	}
   324  	start = time.Now()
   325  	spec, err := reward.CalcDeferredRewardSimple(header, rules, pset)
   326  	if err != nil {
   327  		return err
   328  	}
   329  	reward.DistributeBlockReward(accountMap, spec.Rewards)
   330  	prof.Profile("main_apply_reward", time.Now().Sub(start))
   331  
   332  	// Verification with accountMap
   333  	start = time.Now()
   334  	statedbNew, err := bcdata.bc.State()
   335  	if err != nil {
   336  		return err
   337  	}
   338  	if err := accountMap.Verify(statedbNew); err != nil {
   339  		return err
   340  	}
   341  	prof.Profile("main_verification", time.Now().Sub(start))
   342  
   343  	return nil
   344  }
   345  
   346  func (bcdata *BCData) GenABlockWithTransactions(accountMap *AccountMap, transactions types.Transactions,
   347  	prof *profile.Profiler,
   348  ) error {
   349  	signer := types.MakeSigner(bcdata.bc.Config(), bcdata.bc.CurrentHeader().Number)
   350  
   351  	statedb, err := bcdata.bc.State()
   352  	if err != nil {
   353  		return err
   354  	}
   355  
   356  	// Update accountMap
   357  	start := time.Now()
   358  	if err := accountMap.Update(transactions, signer, statedb, bcdata.bc.CurrentBlock().NumberU64()); err != nil {
   359  		return err
   360  	}
   361  	prof.Profile("main_update_accountMap", time.Now().Sub(start))
   362  
   363  	// Mine a block!
   364  	start = time.Now()
   365  	b, _, err := bcdata.MineABlock(transactions, signer, prof)
   366  	if err != nil {
   367  		return err
   368  	}
   369  	prof.Profile("main_mineABlock", time.Now().Sub(start))
   370  
   371  	txs := make(types.Transactions, len(b.Transactions()))
   372  	for i, tt := range b.Transactions() {
   373  		encodedTx, err := rlp.EncodeToBytes(tt)
   374  		if err != nil {
   375  			return err
   376  		}
   377  		decodedTx := types.Transaction{}
   378  		rlp.DecodeBytes(encodedTx, &decodedTx)
   379  		txs[i] = &decodedTx
   380  	}
   381  	b = b.WithBody(txs)
   382  
   383  	// Insert the block into the blockchain
   384  	start = time.Now()
   385  	if n, err := bcdata.bc.InsertChain(types.Blocks{b}); err != nil {
   386  		return fmt.Errorf("err = %s, n = %d\n", err, n)
   387  	}
   388  	prof.Profile("main_insert_blockchain", time.Now().Sub(start))
   389  
   390  	// Apply reward
   391  	config := bcdata.bc.Config()
   392  	rules := config.Rules(bcdata.bc.CurrentHeader().Number)
   393  	pset, err := params.NewGovParamSetChainConfig(config)
   394  	if err != nil {
   395  		return err
   396  	}
   397  	start = time.Now()
   398  	spec, err := reward.CalcDeferredRewardSimple(bcdata.bc.CurrentHeader(), rules, pset)
   399  	if err != nil {
   400  		return err
   401  	}
   402  	reward.DistributeBlockReward(accountMap, spec.Rewards)
   403  	prof.Profile("main_apply_reward", time.Now().Sub(start))
   404  
   405  	// Verification with accountMap
   406  	start = time.Now()
   407  	statedb, err = bcdata.bc.State()
   408  	if err != nil {
   409  		return err
   410  	}
   411  	if err := accountMap.Verify(statedb); err != nil {
   412  		return err
   413  	}
   414  	prof.Profile("main_verification", time.Now().Sub(start))
   415  
   416  	return nil
   417  }
   418  
   419  // //////////////////////////////////////////////////////////////////////////////
   420  func NewDatabase(dir string, dbType database.DBType) database.DBManager {
   421  	if dir == "" {
   422  		return database.NewMemoryDBManager()
   423  	} else {
   424  		dbc := &database.DBConfig{
   425  			Dir: dir, DBType: dbType, LevelDBCacheSize: 768,
   426  			OpenFilesLimit: 1024, SingleDB: false, NumStateTrieShards: 4, ParallelDBWrite: true,
   427  			LevelDBCompression: database.AllNoCompression, LevelDBBufferPool: true,
   428  		}
   429  		return database.NewDBManager(dbc)
   430  	}
   431  }
   432  
   433  // Copied from consensus/istanbul/backend/engine.go
   434  func prepareIstanbulExtra(validators []common.Address) ([]byte, error) {
   435  	var buf bytes.Buffer
   436  
   437  	buf.Write(bytes.Repeat([]byte{0x0}, types.IstanbulExtraVanity))
   438  
   439  	ist := &types.IstanbulExtra{
   440  		Validators:    validators,
   441  		Seal:          []byte{},
   442  		CommittedSeal: [][]byte{},
   443  	}
   444  
   445  	payload, err := rlp.EncodeToBytes(&ist)
   446  	if err != nil {
   447  		return nil, err
   448  	}
   449  	return append(buf.Bytes(), payload...), nil
   450  }
   451  
   452  func initBlockChain(db database.DBManager, cacheConfig *blockchain.CacheConfig, coinbaseAddrs []*common.Address, validators []common.Address,
   453  	genesis *blockchain.Genesis, engine consensus.Engine,
   454  ) (*blockchain.BlockChain, *blockchain.Genesis, error) {
   455  	extraData, err := prepareIstanbulExtra(validators)
   456  
   457  	if genesis == nil {
   458  		genesis = blockchain.DefaultGenesisBlock()
   459  		genesis.Config = Forks["Byzantium"]
   460  		genesis.ExtraData = extraData
   461  		genesis.BlockScore = big.NewInt(1)
   462  		genesis.Config.Governance = params.GetDefaultGovernanceConfig()
   463  		genesis.Config.Istanbul = params.GetDefaultIstanbulConfig()
   464  		genesis.Config.UnitPrice = 25 * params.Ston
   465  	}
   466  
   467  	alloc := make(blockchain.GenesisAlloc)
   468  	for _, a := range coinbaseAddrs {
   469  		alloc[*a] = blockchain.GenesisAccount{Balance: new(big.Int).Mul(big.NewInt(1e16), big.NewInt(params.KLAY))}
   470  	}
   471  
   472  	genesis.Alloc = alloc
   473  
   474  	chainConfig, _, err := blockchain.SetupGenesisBlock(db, genesis, params.UnusedNetworkId, false, false)
   475  	if _, ok := err.(*params.ConfigCompatError); err != nil && !ok {
   476  		return nil, nil, err
   477  	}
   478  
   479  	// The chainConfig value has been modified while executing test. (ex, The test included executing applyTransaction())
   480  	// Therefore, a deep copy is required to prevent the chainConfing value from being modified.
   481  	var cfg params.ChainConfig
   482  	b, err := json.Marshal(chainConfig)
   483  	if err != nil {
   484  		return nil, nil, err
   485  	}
   486  
   487  	err = json.Unmarshal(b, &cfg)
   488  	if err != nil {
   489  		return nil, nil, err
   490  	}
   491  
   492  	genesis.Config = &cfg
   493  
   494  	chain, err := blockchain.NewBlockChain(db, cacheConfig, genesis.Config, engine, vm.Config{})
   495  	if err != nil {
   496  		return nil, nil, err
   497  	}
   498  
   499  	return chain, genesis, nil
   500  }
   501  
   502  func createAccounts(numAccounts int) ([]*common.Address, []*ecdsa.PrivateKey, error) {
   503  	accs := make([]*common.Address, numAccounts)
   504  	privKeys := make([]*ecdsa.PrivateKey, numAccounts)
   505  
   506  	for i := 0; i < numAccounts; i++ {
   507  		k, err := crypto.GenerateKey()
   508  		if err != nil {
   509  			return nil, nil, err
   510  		}
   511  		keyAddr := crypto.PubkeyToAddress(k.PublicKey)
   512  
   513  		accs[i] = &keyAddr
   514  		privKeys[i] = k
   515  	}
   516  
   517  	return accs, privKeys, nil
   518  }
   519  
   520  // Copied from consensus/istanbul/backend/engine.go
   521  func sigHash(header *types.Header) (hash common.Hash) {
   522  	hasher := sha3.NewKeccak256()
   523  
   524  	// Clean seal is required for calculating proposer seal.
   525  	rlp.Encode(hasher, types.IstanbulFilteredHeader(header, false))
   526  	hasher.Sum(hash[:0])
   527  	return hash
   528  }
   529  
   530  // writeSeal writes the extra-data field of the given header with the given seals.
   531  // Copied from consensus/istanbul/backend/engine.go
   532  func writeSeal(h *types.Header, seal []byte) error {
   533  	if len(seal)%types.IstanbulExtraSeal != 0 {
   534  		return errors.New("invalid signature")
   535  	}
   536  
   537  	istanbulExtra, err := types.ExtractIstanbulExtra(h)
   538  	if err != nil {
   539  		return err
   540  	}
   541  
   542  	istanbulExtra.Seal = seal
   543  	payload, err := rlp.EncodeToBytes(&istanbulExtra)
   544  	if err != nil {
   545  		return err
   546  	}
   547  
   548  	h.Extra = append(h.Extra[:types.IstanbulExtraVanity], payload...)
   549  	return nil
   550  }
   551  
   552  // writeCommittedSeals writes the extra-data field of a block header with given committed seals.
   553  // Copied from consensus/istanbul/backend/engine.go
   554  func writeCommittedSeals(h *types.Header, committedSeals [][]byte) error {
   555  	errInvalidCommittedSeals := errors.New("invalid committed seals")
   556  
   557  	if len(committedSeals) == 0 {
   558  		return errInvalidCommittedSeals
   559  	}
   560  
   561  	for _, seal := range committedSeals {
   562  		if len(seal) != types.IstanbulExtraSeal {
   563  			return errInvalidCommittedSeals
   564  		}
   565  	}
   566  
   567  	istanbulExtra, err := types.ExtractIstanbulExtra(h)
   568  	if err != nil {
   569  		return err
   570  	}
   571  
   572  	istanbulExtra.CommittedSeal = make([][]byte, len(committedSeals))
   573  	copy(istanbulExtra.CommittedSeal, committedSeals)
   574  
   575  	payload, err := rlp.EncodeToBytes(&istanbulExtra)
   576  	if err != nil {
   577  		return err
   578  	}
   579  
   580  	h.Extra = append(h.Extra[:types.IstanbulExtraVanity], payload...)
   581  	return nil
   582  }
   583  
   584  // sign implements istanbul.backend.Sign
   585  // Copied from consensus/istanbul/backend/backend.go
   586  func sign(data []byte, privkey *ecdsa.PrivateKey) ([]byte, error) {
   587  	hashData := crypto.Keccak256([]byte(data))
   588  	return crypto.Sign(hashData, privkey)
   589  }
   590  
   591  func makeCommittedSeal(h *types.Header, privKeys []*ecdsa.PrivateKey) ([][]byte, error) {
   592  	committedSeals := make([][]byte, 0, 3)
   593  
   594  	for i := 1; i < 4; i++ {
   595  		seal := istanbulCore.PrepareCommittedSeal(h.Hash())
   596  		committedSeal, err := sign(seal, privKeys[i])
   597  		if err != nil {
   598  			return nil, err
   599  		}
   600  		committedSeals = append(committedSeals, committedSeal)
   601  	}
   602  
   603  	return committedSeals, nil
   604  }
   605  
   606  func sealBlock(b *types.Block, privKeys []*ecdsa.PrivateKey) (*types.Block, error) {
   607  	header := b.Header()
   608  
   609  	seal, err := sign(sigHash(header).Bytes(), privKeys[0])
   610  	if err != nil {
   611  		return nil, err
   612  	}
   613  
   614  	err = writeSeal(header, seal)
   615  	if err != nil {
   616  		return nil, err
   617  	}
   618  
   619  	committedSeals, err := makeCommittedSeal(header, privKeys)
   620  	if err != nil {
   621  		return nil, err
   622  	}
   623  
   624  	err = writeCommittedSeals(header, committedSeals)
   625  	if err != nil {
   626  		return nil, err
   627  	}
   628  
   629  	return b.WithSeal(header), nil
   630  }