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  }