github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/services/stateroot/signature.go (about)

     1  package stateroot
     2  
     3  import (
     4  	"sync"
     5  
     6  	"github.com/nspcc-dev/neo-go/pkg/config/netmode"
     7  	"github.com/nspcc-dev/neo-go/pkg/core/state"
     8  	"github.com/nspcc-dev/neo-go/pkg/core/transaction"
     9  	"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
    10  	"github.com/nspcc-dev/neo-go/pkg/io"
    11  	"github.com/nspcc-dev/neo-go/pkg/network/payload"
    12  	"github.com/nspcc-dev/neo-go/pkg/smartcontract"
    13  	"github.com/nspcc-dev/neo-go/pkg/vm/emit"
    14  )
    15  
    16  type (
    17  	incompleteRoot struct {
    18  		sync.RWMutex
    19  		// svList is a list of state validator keys for this stateroot.
    20  		svList keys.PublicKeys
    21  		// isSent is true if the state root was already broadcasted.
    22  		isSent bool
    23  		// request is an oracle request.
    24  		root *state.MPTRoot
    25  		// sigs contains a signature from every oracle node.
    26  		sigs map[string]*rootSig
    27  		// myIndex is the index of validator for this root.
    28  		myIndex int
    29  		// myVote is an extensible message containing node's vote.
    30  		myVote *payload.Extensible
    31  		// retries is a counter of send attempts.
    32  		retries int
    33  	}
    34  
    35  	rootSig struct {
    36  		// pub is a cached public key.
    37  		pub *keys.PublicKey
    38  		// ok is true if the signature was verified.
    39  		ok bool
    40  		// sig is a state root signature.
    41  		sig []byte
    42  	}
    43  )
    44  
    45  func (r *incompleteRoot) reverify(net netmode.Magic) {
    46  	for _, sig := range r.sigs {
    47  		if !sig.ok {
    48  			sig.ok = sig.pub.VerifyHashable(sig.sig, uint32(net), r.root)
    49  		}
    50  	}
    51  }
    52  
    53  func (r *incompleteRoot) addSignature(pub *keys.PublicKey, sig []byte) {
    54  	r.sigs[string(pub.Bytes())] = &rootSig{
    55  		pub: pub,
    56  		ok:  r.root != nil,
    57  		sig: sig,
    58  	}
    59  }
    60  
    61  func (r *incompleteRoot) isSenderNow() bool {
    62  	if r.root == nil || r.isSent || len(r.svList) == 0 {
    63  		return false
    64  	}
    65  	retries := r.retries
    66  	if retries < 0 {
    67  		retries = 0
    68  	}
    69  	ind := (int(r.root.Index) - retries) % len(r.svList)
    70  	if ind < 0 {
    71  		ind += len(r.svList)
    72  	}
    73  	return ind == r.myIndex
    74  }
    75  
    76  // finalize checks if either main or backup tx has sufficient number of signatures and returns
    77  // tx and bool value indicating if it is ready to be broadcasted.
    78  func (r *incompleteRoot) finalize() (*state.MPTRoot, bool) {
    79  	if r.root == nil {
    80  		return nil, false
    81  	}
    82  
    83  	m := smartcontract.GetDefaultHonestNodeCount(len(r.svList))
    84  	sigs := make([][]byte, 0, m)
    85  	for _, pub := range r.svList {
    86  		sig, ok := r.sigs[string(pub.Bytes())]
    87  		if ok && sig.ok {
    88  			sigs = append(sigs, sig.sig)
    89  			if len(sigs) == m {
    90  				break
    91  			}
    92  		}
    93  	}
    94  	if len(sigs) != m {
    95  		return nil, false
    96  	}
    97  
    98  	verif, err := smartcontract.CreateDefaultMultiSigRedeemScript(r.svList)
    99  	if err != nil {
   100  		return nil, false
   101  	}
   102  	w := io.NewBufBinWriter()
   103  	for i := range sigs {
   104  		emit.Bytes(w.BinWriter, sigs[i])
   105  	}
   106  	r.root.Witness = []transaction.Witness{{
   107  		InvocationScript:   w.Bytes(),
   108  		VerificationScript: verif,
   109  	}}
   110  	return r.root, true
   111  }