github.com/unicornultrafoundation/go-u2u@v1.0.0-rc1.0.20240205080301-e74a83d3fadc/eventcheck/heavycheck/heavy_check.go (about)

     1  package heavycheck
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"runtime"
     7  	"sync"
     8  
     9  	"github.com/unicornultrafoundation/go-helios/hash"
    10  	"github.com/unicornultrafoundation/go-helios/native/idx"
    11  	"github.com/unicornultrafoundation/go-u2u/core/types"
    12  	"github.com/unicornultrafoundation/go-u2u/crypto"
    13  
    14  	"github.com/unicornultrafoundation/go-u2u/eventcheck/basiccheck"
    15  	"github.com/unicornultrafoundation/go-u2u/eventcheck/epochcheck"
    16  	"github.com/unicornultrafoundation/go-u2u/native"
    17  	"github.com/unicornultrafoundation/go-u2u/native/validatorpk"
    18  )
    19  
    20  var (
    21  	ErrWrongEventSig            = errors.New("event has wrong signature")
    22  	ErrMalformedTxSig           = errors.New("tx has wrong signature")
    23  	ErrWrongPayloadHash         = errors.New("event has wrong payload hash")
    24  	ErrPubkeyChanged            = errors.New("validator pubkey has changed, cannot create BVs/EV for older epochs")
    25  	ErrUnknownEpochEventLocator = errors.New("event locator has unknown epoch")
    26  	ErrImpossibleBVsEpoch       = errors.New("BVs have an impossible epoch")
    27  	ErrUnknownEpochBVs          = errors.New("BVs are unprocessable yet")
    28  	ErrUnknownEpochEV           = errors.New("EV is unprocessable yet")
    29  
    30  	errTerminated = errors.New("terminated") // internal err
    31  )
    32  
    33  const (
    34  	// MaxBlocksPerEpoch is chosen so that even if validator chooses the latest non-liable epoch for BVs,
    35  	// he still cannot vote for latest blocks (latest = from last 128 epochs), as an epoch has at least one block
    36  	// The value is larger than a maximum possible number of blocks
    37  	// in an epoch where a single validator doesn't have 2/3W+1 weight
    38  	MaxBlocksPerEpoch = idx.Block(basiccheck.MaxLiableEpochs - 128)
    39  )
    40  
    41  // Reader is accessed by the validator to get the current state.
    42  type Reader interface {
    43  	GetEpochPubKeys() (map[idx.ValidatorID]validatorpk.PubKey, idx.Epoch)
    44  	GetEpochPubKeysOf(idx.Epoch) map[idx.ValidatorID]validatorpk.PubKey
    45  	GetEpochBlockStart(idx.Epoch) idx.Block
    46  }
    47  
    48  // Checker which requires only parents list + current epoch info
    49  type Checker struct {
    50  	config   Config
    51  	txSigner types.Signer
    52  	reader   Reader
    53  
    54  	tasksQ chan *taskData
    55  	quit   chan struct{}
    56  	wg     sync.WaitGroup
    57  }
    58  
    59  type taskData struct {
    60  	event native.EventPayloadI
    61  	bvs   *native.LlrSignedBlockVotes
    62  	ev    *native.LlrSignedEpochVote
    63  
    64  	onValidated func(error)
    65  }
    66  
    67  // New validator which performs heavy checks, related to signatures validation and Merkle tree validation
    68  func New(config Config, reader Reader, txSigner types.Signer) *Checker {
    69  	if config.Threads == 0 {
    70  		config.Threads = runtime.NumCPU()
    71  		if config.Threads > 1 {
    72  			config.Threads--
    73  		}
    74  		if config.Threads < 1 {
    75  			config.Threads = 1
    76  		}
    77  	}
    78  	return &Checker{
    79  		config:   config,
    80  		txSigner: txSigner,
    81  		reader:   reader,
    82  		tasksQ:   make(chan *taskData, config.MaxQueuedTasks),
    83  		quit:     make(chan struct{}),
    84  	}
    85  }
    86  
    87  func (v *Checker) Start() {
    88  	for i := 0; i < v.config.Threads; i++ {
    89  		v.wg.Add(1)
    90  		go v.loop()
    91  	}
    92  }
    93  
    94  func (v *Checker) Stop() {
    95  	close(v.quit)
    96  	v.wg.Wait()
    97  }
    98  
    99  func (v *Checker) Overloaded() bool {
   100  	return len(v.tasksQ) > v.config.MaxQueuedTasks/2
   101  }
   102  
   103  func (v *Checker) EnqueueEvent(e native.EventPayloadI, onValidated func(error)) error {
   104  	op := &taskData{
   105  		event:       e,
   106  		onValidated: onValidated,
   107  	}
   108  	select {
   109  	case v.tasksQ <- op:
   110  		return nil
   111  	case <-v.quit:
   112  		return errTerminated
   113  	}
   114  }
   115  
   116  func (v *Checker) EnqueueBVs(bvs native.LlrSignedBlockVotes, onValidated func(error)) error {
   117  	op := &taskData{
   118  		bvs:         &bvs,
   119  		onValidated: onValidated,
   120  	}
   121  	select {
   122  	case v.tasksQ <- op:
   123  		return nil
   124  	case <-v.quit:
   125  		return errTerminated
   126  	}
   127  }
   128  
   129  func (v *Checker) EnqueueEV(ev native.LlrSignedEpochVote, onValidated func(error)) error {
   130  	op := &taskData{
   131  		ev:          &ev,
   132  		onValidated: onValidated,
   133  	}
   134  	select {
   135  	case v.tasksQ <- op:
   136  		return nil
   137  	case <-v.quit:
   138  		return errTerminated
   139  	}
   140  }
   141  
   142  // verifySignature checks the signature against e.Creator.
   143  func verifySignature(signedHash hash.Hash, sig native.Signature, pubkey validatorpk.PubKey) bool {
   144  	if pubkey.Type != validatorpk.Types.Secp256k1 {
   145  		return false
   146  	}
   147  	return crypto.VerifySignature(pubkey.Raw, signedHash.Bytes(), sig.Bytes())
   148  }
   149  
   150  func (v *Checker) ValidateEventLocator(e native.SignedEventLocator, authEpoch idx.Epoch, authErr error, checkPayload func() bool) error {
   151  	pubkeys := v.reader.GetEpochPubKeysOf(authEpoch)
   152  	if len(pubkeys) == 0 {
   153  		return authErr
   154  	}
   155  	pubkey, ok := pubkeys[e.Locator.Creator]
   156  	if !ok {
   157  		return epochcheck.ErrAuth
   158  	}
   159  	if checkPayload != nil && !checkPayload() {
   160  		return ErrWrongPayloadHash
   161  	}
   162  	if !verifySignature(e.Locator.HashToSign(), e.Sig, pubkey) {
   163  		return ErrWrongEventSig
   164  	}
   165  	return nil
   166  }
   167  
   168  func (v *Checker) matchPubkey(creator idx.ValidatorID, epoch idx.Epoch, want []byte, authErr error) error {
   169  	pubkeys := v.reader.GetEpochPubKeysOf(epoch)
   170  	if len(pubkeys) == 0 {
   171  		return authErr
   172  	}
   173  	pubkey, ok := pubkeys[creator]
   174  	if !ok {
   175  		return epochcheck.ErrAuth
   176  	}
   177  	if bytes.Compare(pubkey.Bytes(), want) != 0 {
   178  		return ErrPubkeyChanged
   179  	}
   180  	return nil
   181  }
   182  
   183  func (v *Checker) validateBVsEpoch(bvs native.LlrBlockVotes) error {
   184  	actualEpochStart := v.reader.GetEpochBlockStart(bvs.Epoch)
   185  	if actualEpochStart == 0 {
   186  		return ErrUnknownEpochBVs
   187  	}
   188  	if bvs.Start < actualEpochStart || bvs.LastBlock() >= actualEpochStart+MaxBlocksPerEpoch {
   189  		return ErrImpossibleBVsEpoch
   190  	}
   191  	return nil
   192  }
   193  
   194  func (v *Checker) ValidateBVs(bvs native.LlrSignedBlockVotes) error {
   195  	if err := v.validateBVsEpoch(bvs.Val); err != nil {
   196  		return err
   197  	}
   198  	return v.ValidateEventLocator(bvs.Signed, bvs.Val.Epoch, ErrUnknownEpochBVs, func() bool {
   199  		return bvs.CalcPayloadHash() == bvs.Signed.Locator.PayloadHash
   200  	})
   201  }
   202  
   203  func (v *Checker) ValidateEV(ev native.LlrSignedEpochVote) error {
   204  	return v.ValidateEventLocator(ev.Signed, ev.Val.Epoch-1, ErrUnknownEpochEV, func() bool {
   205  		return ev.CalcPayloadHash() == ev.Signed.Locator.PayloadHash
   206  	})
   207  }
   208  
   209  // ValidateEvent runs heavy checks for event
   210  func (v *Checker) ValidateEvent(e native.EventPayloadI) error {
   211  	pubkeys, epoch := v.reader.GetEpochPubKeys()
   212  	if e.Epoch() != epoch {
   213  		return epochcheck.ErrNotRelevant
   214  	}
   215  	// validatorID
   216  	pubkey, ok := pubkeys[e.Creator()]
   217  	if !ok {
   218  		return epochcheck.ErrAuth
   219  	}
   220  	// event sig
   221  	if !verifySignature(e.HashToSign(), e.Sig(), pubkey) {
   222  		return ErrWrongEventSig
   223  	}
   224  	// MPs
   225  	for _, mp := range e.MisbehaviourProofs() {
   226  		if proof := mp.EventsDoublesign; proof != nil {
   227  			for _, vote := range proof.Pair {
   228  				if err := v.ValidateEventLocator(vote, vote.Locator.Epoch, ErrUnknownEpochEventLocator, nil); err != nil {
   229  					return err
   230  				}
   231  			}
   232  		}
   233  		if proof := mp.BlockVoteDoublesign; proof != nil {
   234  			for _, vote := range proof.Pair {
   235  				if err := v.ValidateBVs(vote); err != nil {
   236  					return err
   237  				}
   238  			}
   239  		}
   240  		if proof := mp.WrongBlockVote; proof != nil {
   241  			for _, pal := range proof.Pals {
   242  				if err := v.ValidateBVs(pal); err != nil {
   243  					return err
   244  				}
   245  			}
   246  		}
   247  		if proof := mp.EpochVoteDoublesign; proof != nil {
   248  			for _, vote := range proof.Pair {
   249  				if err := v.ValidateEV(vote); err != nil {
   250  					return err
   251  				}
   252  			}
   253  		}
   254  		if proof := mp.WrongEpochVote; proof != nil {
   255  			for _, pal := range proof.Pals {
   256  				if err := v.ValidateEV(pal); err != nil {
   257  					return err
   258  				}
   259  			}
   260  		}
   261  	}
   262  	// pre-cache tx sig
   263  	for _, tx := range e.Txs() {
   264  		_, err := types.Sender(v.txSigner, tx)
   265  		if err != nil {
   266  			return ErrMalformedTxSig
   267  		}
   268  	}
   269  	// Payload hash
   270  	if e.PayloadHash() != native.CalcPayloadHash(e) {
   271  		return ErrWrongPayloadHash
   272  	}
   273  	// Epochs of BVs and EV
   274  	if e.EpochVote().Epoch != 0 {
   275  		// ensure that validator's pubkey is the same in both current and vote epochs
   276  		if err := v.matchPubkey(e.Creator(), e.EpochVote().Epoch-1, pubkey.Bytes(), ErrUnknownEpochEV); err != nil {
   277  			return err
   278  		}
   279  	}
   280  	if e.BlockVotes().Epoch != 0 {
   281  		// ensure that validator's BVs epoch passes the check
   282  		if err := v.validateBVsEpoch(e.BlockVotes()); err != nil {
   283  			return err
   284  		}
   285  		// ensure that validator's pubkey is the same in both current and vote epochs
   286  		if e.BlockVotes().Epoch != e.Epoch() {
   287  			if err := v.matchPubkey(e.Creator(), e.BlockVotes().Epoch, pubkey.Bytes(), ErrUnknownEpochBVs); err != nil {
   288  				return err
   289  			}
   290  		}
   291  	}
   292  
   293  	return nil
   294  }
   295  
   296  func (v *Checker) loop() {
   297  	defer v.wg.Done()
   298  	for {
   299  		select {
   300  		case op := <-v.tasksQ:
   301  			if op.event != nil {
   302  				op.onValidated(v.ValidateEvent(op.event))
   303  			} else if op.bvs != nil {
   304  				op.onValidated(v.ValidateBVs(*op.bvs))
   305  			} else {
   306  				op.onValidated(v.ValidateEV(*op.ev))
   307  			}
   308  
   309  		case <-v.quit:
   310  			return
   311  		}
   312  	}
   313  }