github.com/number571/tendermint@v0.34.11-gost/test/e2e/runner/evidence.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"math/rand"
     9  	"path/filepath"
    10  	"time"
    11  
    12  	"github.com/number571/tendermint/crypto"
    13  	"github.com/number571/tendermint/crypto/tmhash"
    14  	"github.com/number571/tendermint/internal/test/factory"
    15  	tmjson "github.com/number571/tendermint/libs/json"
    16  	"github.com/number571/tendermint/privval"
    17  	tmproto "github.com/number571/tendermint/proto/tendermint/types"
    18  	e2e "github.com/number571/tendermint/test/e2e/pkg"
    19  	"github.com/number571/tendermint/types"
    20  	"github.com/number571/tendermint/version"
    21  )
    22  
    23  // 1 in 4 evidence is light client evidence, the rest is duplicate vote evidence
    24  const lightClientEvidenceRatio = 4
    25  
    26  // InjectEvidence takes a running testnet and generates an amount of valid
    27  // evidence and broadcasts it to a random node through the rpc endpoint `/broadcast_evidence`.
    28  // Evidence is random and can be a mixture of LightClientAttackEvidence and
    29  // DuplicateVoteEvidence.
    30  func InjectEvidence(testnet *e2e.Testnet, amount int) error {
    31  	// select a random node
    32  	targetNode := testnet.RandomNode()
    33  
    34  	logger.Info(fmt.Sprintf("Injecting evidence through %v (amount: %d)...", targetNode.Name, amount))
    35  
    36  	client, err := targetNode.Client()
    37  	if err != nil {
    38  		return err
    39  	}
    40  
    41  	// request the latest block and validator set from the node
    42  	blockRes, err := client.Block(context.Background(), nil)
    43  	if err != nil {
    44  		return err
    45  	}
    46  	evidenceHeight := blockRes.Block.Height
    47  	waitHeight := blockRes.Block.Height + 3
    48  
    49  	nValidators := 100
    50  	valRes, err := client.Validators(context.Background(), &evidenceHeight, nil, &nValidators)
    51  	if err != nil {
    52  		return err
    53  	}
    54  
    55  	valSet, err := types.ValidatorSetFromExistingValidators(valRes.Validators)
    56  	if err != nil {
    57  		return err
    58  	}
    59  
    60  	// get the private keys of all the validators in the network
    61  	privVals, err := getPrivateValidatorKeys(testnet)
    62  	if err != nil {
    63  		return err
    64  	}
    65  
    66  	// wait for the node to reach the height above the forged height so that
    67  	// it is able to validate the evidence
    68  	_, err = waitForNode(targetNode, waitHeight, 30*time.Second)
    69  	if err != nil {
    70  		return err
    71  	}
    72  
    73  	var ev types.Evidence
    74  	for i := 1; i <= amount; i++ {
    75  		if i%lightClientEvidenceRatio == 0 {
    76  			ev, err = generateLightClientAttackEvidence(
    77  				privVals, evidenceHeight, valSet, testnet.Name, blockRes.Block.Time,
    78  			)
    79  		} else {
    80  			ev, err = generateDuplicateVoteEvidence(
    81  				privVals, evidenceHeight, valSet, testnet.Name, blockRes.Block.Time,
    82  			)
    83  		}
    84  		if err != nil {
    85  			return err
    86  		}
    87  
    88  		_, err := client.BroadcastEvidence(context.Background(), ev)
    89  		if err != nil {
    90  			return err
    91  		}
    92  	}
    93  
    94  	// wait for the node to reach the height above the forged height so that
    95  	// it is able to validate the evidence
    96  	_, err = waitForNode(targetNode, blockRes.Block.Height+2, 10*time.Second)
    97  	if err != nil {
    98  		return err
    99  	}
   100  
   101  	logger.Info(fmt.Sprintf("Finished sending evidence (height %d)", blockRes.Block.Height+2))
   102  
   103  	return nil
   104  }
   105  
   106  func getPrivateValidatorKeys(testnet *e2e.Testnet) ([]types.MockPV, error) {
   107  	privVals := []types.MockPV{}
   108  
   109  	for _, node := range testnet.Nodes {
   110  		if node.Mode == e2e.ModeValidator {
   111  			privKeyPath := filepath.Join(testnet.Dir, node.Name, PrivvalKeyFile)
   112  			privKey, err := readPrivKey(privKeyPath)
   113  			if err != nil {
   114  				return nil, err
   115  			}
   116  			// Create mock private validators from the validators private key. MockPV is
   117  			// stateless which means we can double vote and do other funky stuff
   118  			privVals = append(privVals, types.NewMockPVWithParams(privKey, false, false))
   119  		}
   120  	}
   121  
   122  	return privVals, nil
   123  }
   124  
   125  // creates evidence of a lunatic attack. The height provided is the common height.
   126  // The forged height happens 2 blocks later.
   127  func generateLightClientAttackEvidence(
   128  	privVals []types.MockPV,
   129  	height int64,
   130  	vals *types.ValidatorSet,
   131  	chainID string,
   132  	evTime time.Time,
   133  ) (*types.LightClientAttackEvidence, error) {
   134  	// forge a random header
   135  	forgedHeight := height + 2
   136  	forgedTime := evTime.Add(1 * time.Second)
   137  	header := makeHeaderRandom(chainID, forgedHeight)
   138  	header.Time = forgedTime
   139  
   140  	// add a new bogus validator and remove an existing one to
   141  	// vary the validator set slightly
   142  	pv, conflictingVals, err := mutateValidatorSet(privVals, vals)
   143  	if err != nil {
   144  		return nil, err
   145  	}
   146  
   147  	header.ValidatorsHash = conflictingVals.Hash()
   148  
   149  	// create a commit for the forged header
   150  	blockID := makeBlockID(header.Hash(), 1000, []byte("partshash"))
   151  	voteSet := types.NewVoteSet(chainID, forgedHeight, 0, tmproto.SignedMsgType(2), conflictingVals)
   152  	commit, err := factory.MakeCommit(blockID, forgedHeight, 0, voteSet, pv, forgedTime)
   153  	if err != nil {
   154  		return nil, err
   155  	}
   156  
   157  	ev := &types.LightClientAttackEvidence{
   158  		ConflictingBlock: &types.LightBlock{
   159  			SignedHeader: &types.SignedHeader{
   160  				Header: header,
   161  				Commit: commit,
   162  			},
   163  			ValidatorSet: conflictingVals,
   164  		},
   165  		CommonHeight:     height,
   166  		TotalVotingPower: vals.TotalVotingPower(),
   167  		Timestamp:        evTime,
   168  	}
   169  	ev.ByzantineValidators = ev.GetByzantineValidators(vals, &types.SignedHeader{
   170  		Header: makeHeaderRandom(chainID, forgedHeight),
   171  	})
   172  	return ev, nil
   173  }
   174  
   175  // generateDuplicateVoteEvidence picks a random validator from the val set and
   176  // returns duplicate vote evidence against the validator
   177  func generateDuplicateVoteEvidence(
   178  	privVals []types.MockPV,
   179  	height int64,
   180  	vals *types.ValidatorSet,
   181  	chainID string,
   182  	time time.Time,
   183  ) (*types.DuplicateVoteEvidence, error) {
   184  	// nolint:gosec // G404: Use of weak random number generator
   185  	privVal := privVals[rand.Intn(len(privVals))]
   186  
   187  	valIdx, _ := vals.GetByAddress(privVal.PrivKey.PubKey().Address())
   188  	voteA, err := factory.MakeVote(privVal, chainID, valIdx, height, 0, 2, makeRandomBlockID(), time)
   189  	if err != nil {
   190  		return nil, err
   191  	}
   192  	voteB, err := factory.MakeVote(privVal, chainID, valIdx, height, 0, 2, makeRandomBlockID(), time)
   193  	if err != nil {
   194  		return nil, err
   195  	}
   196  	ev := types.NewDuplicateVoteEvidence(voteA, voteB, time, vals)
   197  	if ev == nil {
   198  		return nil, fmt.Errorf("could not generate evidence a=%v b=%v vals=%v", voteA, voteB, vals)
   199  	}
   200  
   201  	return ev, nil
   202  }
   203  
   204  func readPrivKey(keyFilePath string) (crypto.PrivKey, error) {
   205  	keyJSONBytes, err := ioutil.ReadFile(keyFilePath)
   206  	if err != nil {
   207  		return nil, err
   208  	}
   209  	pvKey := privval.FilePVKey{}
   210  	err = tmjson.Unmarshal(keyJSONBytes, &pvKey)
   211  	if err != nil {
   212  		return nil, fmt.Errorf("error reading PrivValidator key from %v: %w", keyFilePath, err)
   213  	}
   214  
   215  	return pvKey.PrivKey, nil
   216  }
   217  
   218  func makeHeaderRandom(chainID string, height int64) *types.Header {
   219  	return &types.Header{
   220  		Version:            version.Consensus{Block: version.BlockProtocol, App: 1},
   221  		ChainID:            chainID,
   222  		Height:             height,
   223  		Time:               time.Now(),
   224  		LastBlockID:        makeBlockID([]byte("headerhash"), 1000, []byte("partshash")),
   225  		LastCommitHash:     crypto.CRandBytes(tmhash.Size),
   226  		DataHash:           crypto.CRandBytes(tmhash.Size),
   227  		ValidatorsHash:     crypto.CRandBytes(tmhash.Size),
   228  		NextValidatorsHash: crypto.CRandBytes(tmhash.Size),
   229  		ConsensusHash:      crypto.CRandBytes(tmhash.Size),
   230  		AppHash:            crypto.CRandBytes(tmhash.Size),
   231  		LastResultsHash:    crypto.CRandBytes(tmhash.Size),
   232  		EvidenceHash:       crypto.CRandBytes(tmhash.Size),
   233  		ProposerAddress:    crypto.CRandBytes(crypto.AddressSize),
   234  	}
   235  }
   236  
   237  func makeRandomBlockID() types.BlockID {
   238  	return makeBlockID(crypto.CRandBytes(tmhash.Size), 100, crypto.CRandBytes(tmhash.Size))
   239  }
   240  
   241  func makeBlockID(hash []byte, partSetSize uint32, partSetHash []byte) types.BlockID {
   242  	var (
   243  		h   = make([]byte, tmhash.Size)
   244  		psH = make([]byte, tmhash.Size)
   245  	)
   246  	copy(h, hash)
   247  	copy(psH, partSetHash)
   248  	return types.BlockID{
   249  		Hash: h,
   250  		PartSetHeader: types.PartSetHeader{
   251  			Total: partSetSize,
   252  			Hash:  psH,
   253  		},
   254  	}
   255  }
   256  
   257  func mutateValidatorSet(privVals []types.MockPV, vals *types.ValidatorSet,
   258  ) ([]types.PrivValidator, *types.ValidatorSet, error) {
   259  	newVal, newPrivVal := factory.RandValidator(false, 10)
   260  
   261  	var newVals *types.ValidatorSet
   262  	if vals.Size() > 2 {
   263  		newVals = types.NewValidatorSet(append(vals.Copy().Validators[:vals.Size()-1], newVal))
   264  	} else {
   265  		newVals = types.NewValidatorSet(append(vals.Copy().Validators, newVal))
   266  	}
   267  
   268  	// we need to sort the priv validators with the same index as the validator set
   269  	pv := make([]types.PrivValidator, newVals.Size())
   270  	for idx, val := range newVals.Validators {
   271  		found := false
   272  		for _, p := range append(privVals, newPrivVal.(types.MockPV)) {
   273  			if bytes.Equal(p.PrivKey.PubKey().Address(), val.Address) {
   274  				pv[idx] = p
   275  				found = true
   276  				break
   277  			}
   278  		}
   279  		if !found {
   280  			return nil, nil, fmt.Errorf("missing priv validator for %v", val.Address)
   281  		}
   282  	}
   283  
   284  	return pv, newVals, nil
   285  }