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 }