github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/smartcontract/manifest/group.go (about) 1 package manifest 2 3 import ( 4 "crypto/elliptic" 5 "encoding/hex" 6 "encoding/json" 7 "errors" 8 "sort" 9 10 "github.com/nspcc-dev/neo-go/pkg/crypto/hash" 11 "github.com/nspcc-dev/neo-go/pkg/crypto/keys" 12 "github.com/nspcc-dev/neo-go/pkg/util" 13 "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" 14 ) 15 16 // Group represents a group of smartcontracts identified by a public key. 17 // Every SC in a group must provide signature of its hash to prove 18 // it belongs to the group. 19 type Group struct { 20 PublicKey *keys.PublicKey `json:"pubkey"` 21 Signature []byte `json:"signature"` 22 } 23 24 // Groups is just an array of Group. 25 type Groups []Group 26 27 type groupAux struct { 28 PublicKey string `json:"pubkey"` 29 Signature []byte `json:"signature"` 30 } 31 32 // IsValid checks whether the group's signature corresponds to the given hash. 33 func (g *Group) IsValid(h util.Uint160) error { 34 if !g.PublicKey.Verify(g.Signature, hash.Sha256(h.BytesBE()).BytesBE()) { 35 return errors.New("incorrect group signature") 36 } 37 return nil 38 } 39 40 // AreValid checks for groups correctness and uniqueness. 41 // If the contract hash is empty, then hash-related checks are omitted. 42 func (g Groups) AreValid(h util.Uint160) error { 43 if !h.Equals(util.Uint160{}) { 44 for i := range g { 45 err := g[i].IsValid(h) 46 if err != nil { 47 return err 48 } 49 } 50 } 51 if len(g) < 2 { 52 return nil 53 } 54 pkeys := make(keys.PublicKeys, len(g)) 55 for i := range g { 56 pkeys[i] = g[i].PublicKey 57 } 58 sort.Sort(pkeys) 59 for i := range pkeys { 60 if i == 0 { 61 continue 62 } 63 if pkeys[i].Cmp(pkeys[i-1]) == 0 { 64 return errors.New("duplicate group keys") 65 } 66 } 67 return nil 68 } 69 70 func (g Groups) Contains(k *keys.PublicKey) bool { 71 for i := range g { 72 if k.Equal(g[i].PublicKey) { 73 return true 74 } 75 } 76 return false 77 } 78 79 // MarshalJSON implements the json.Marshaler interface. 80 func (g *Group) MarshalJSON() ([]byte, error) { 81 aux := &groupAux{ 82 PublicKey: g.PublicKey.StringCompressed(), 83 Signature: g.Signature, 84 } 85 return json.Marshal(aux) 86 } 87 88 // UnmarshalJSON implements the json.Unmarshaler interface. 89 func (g *Group) UnmarshalJSON(data []byte) error { 90 aux := new(groupAux) 91 if err := json.Unmarshal(data, aux); err != nil { 92 return err 93 } 94 b, err := hex.DecodeString(aux.PublicKey) 95 if err != nil { 96 return err 97 } 98 pub := new(keys.PublicKey) 99 if err := pub.DecodeBytes(b); err != nil { 100 return err 101 } 102 g.PublicKey = pub 103 if len(aux.Signature) != keys.SignatureLen { 104 return errors.New("wrong signature length") 105 } 106 g.Signature = aux.Signature 107 return nil 108 } 109 110 // ToStackItem converts Group to stackitem.Item. 111 func (g *Group) ToStackItem() stackitem.Item { 112 return stackitem.NewStruct([]stackitem.Item{ 113 stackitem.NewByteArray(g.PublicKey.Bytes()), 114 stackitem.NewByteArray(g.Signature), 115 }) 116 } 117 118 // FromStackItem converts stackitem.Item to Group. 119 func (g *Group) FromStackItem(item stackitem.Item) error { 120 if item.Type() != stackitem.StructT { 121 return errors.New("invalid Group stackitem type") 122 } 123 group := item.Value().([]stackitem.Item) 124 if len(group) != 2 { 125 return errors.New("invalid Group stackitem length") 126 } 127 pKey, err := group[0].TryBytes() 128 if err != nil { 129 return err 130 } 131 g.PublicKey, err = keys.NewPublicKeyFromBytes(pKey, elliptic.P256()) 132 if err != nil { 133 return err 134 } 135 sig, err := group[1].TryBytes() 136 if err != nil { 137 return err 138 } 139 if len(sig) != keys.SignatureLen { 140 return errors.New("wrong signature length") 141 } 142 g.Signature = sig 143 return nil 144 }