code.vegaprotocol.io/vega@v0.79.0/core/notary/snapshot.go (about)

     1  // Copyright (C) 2023 Gobalsky Labs Limited
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15  
    16  package notary
    17  
    18  import (
    19  	"context"
    20  	"encoding/hex"
    21  	"sort"
    22  	"strings"
    23  
    24  	"code.vegaprotocol.io/vega/core/types"
    25  	"code.vegaprotocol.io/vega/libs/proto"
    26  	"code.vegaprotocol.io/vega/logging"
    27  	v1 "code.vegaprotocol.io/vega/protos/vega/commands/v1"
    28  )
    29  
    30  var (
    31  	allKey = (&types.PayloadNotary{}).Key()
    32  
    33  	hashKeys = []string{
    34  		allKey,
    35  	}
    36  )
    37  
    38  // NewWithSnapshot returns an "extended" Notary type which contains the ability to take engine snapshots.
    39  func NewWithSnapshot(
    40  	log *logging.Logger,
    41  	cfg Config,
    42  	top ValidatorTopology,
    43  	broker Broker,
    44  	cmd Commander,
    45  ) *SnapshotNotary {
    46  	log = log.Named(namedLogger)
    47  	return &SnapshotNotary{
    48  		Notary: New(log, cfg, top, broker, cmd),
    49  	}
    50  }
    51  
    52  type SnapshotNotary struct {
    53  	*Notary
    54  
    55  	// snapshot bits
    56  	serialised []byte
    57  }
    58  
    59  // StartAggregate is a wrapper to Notary's StartAggregate which also manages the snapshot state.
    60  func (n *SnapshotNotary) StartAggregate(
    61  	resource string,
    62  	kind v1.NodeSignatureKind,
    63  	signature []byte,
    64  ) {
    65  	n.Notary.StartAggregate(resource, kind, signature)
    66  }
    67  
    68  // RegisterSignature is a wrapper to Notary's RegisterSignature which also manages the snapshot state.
    69  func (n *SnapshotNotary) RegisterSignature(
    70  	ctx context.Context,
    71  	pubKey string,
    72  	ns v1.NodeSignature,
    73  ) error {
    74  	return n.Notary.RegisterSignature(ctx, pubKey, ns)
    75  }
    76  
    77  // get the serialised form of the given key.
    78  func (n *SnapshotNotary) serialise(k string) ([]byte, error) {
    79  	if k != allKey {
    80  		return nil, types.ErrSnapshotKeyDoesNotExist
    81  	}
    82  
    83  	data, err := n.serialiseNotary()
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  
    88  	n.serialised = data
    89  	return data, nil
    90  }
    91  
    92  func (n *SnapshotNotary) Namespace() types.SnapshotNamespace {
    93  	return types.NotarySnapshot
    94  }
    95  
    96  func (n *SnapshotNotary) Keys() []string {
    97  	return hashKeys
    98  }
    99  
   100  func (n *SnapshotNotary) Stopped() bool {
   101  	return false
   102  }
   103  
   104  func (n *SnapshotNotary) GetState(k string) ([]byte, []types.StateProvider, error) {
   105  	data, err := n.serialise(k)
   106  	return data, nil, err
   107  }
   108  
   109  func (n *SnapshotNotary) LoadState(ctx context.Context, payload *types.Payload) ([]types.StateProvider, error) {
   110  	if n.Namespace() != payload.Data.Namespace() {
   111  		return nil, types.ErrInvalidSnapshotNamespace
   112  	}
   113  
   114  	switch pl := payload.Data.(type) {
   115  	case *types.PayloadNotary:
   116  		return nil, n.restoreNotary(pl.Notary, payload)
   117  	default:
   118  		return nil, types.ErrUnknownSnapshotType
   119  	}
   120  }
   121  
   122  func (n *SnapshotNotary) OfferSignatures(
   123  	kind types.NodeSignatureKind,
   124  	// a callback taking a list of resource that a signature is required
   125  	// for, returning a map of signature for given resources
   126  	f func(resource string) []byte,
   127  ) {
   128  	for k, v := range n.retries.txs {
   129  		if k.kind != kind {
   130  			continue
   131  		}
   132  		if v.signature != nil {
   133  			continue
   134  		}
   135  
   136  		if signature := f(k.id); signature != nil {
   137  			v.signature = signature
   138  		}
   139  	}
   140  }
   141  
   142  // serialiseLimits returns the engine's limit data as marshalled bytes.
   143  func (n *SnapshotNotary) serialiseNotary() ([]byte, error) {
   144  	sigs := make([]*types.NotarySigs, 0, len(n.sigs)) // it will likely be longer than this but we don't know yet
   145  	for ik, ns := range n.sigs {
   146  		_, pending := n.pendingSignatures[ik]
   147  		for _n := range ns {
   148  			sigs = append(sigs,
   149  				&types.NotarySigs{
   150  					ID:      ik.id,
   151  					Kind:    int32(ik.kind),
   152  					Node:    _n.node,
   153  					Sig:     hex.EncodeToString([]byte(_n.sig)),
   154  					Pending: pending,
   155  				},
   156  			)
   157  		}
   158  
   159  		// the case where aggregate has started but we have no node sigs
   160  		if len(ns) == 0 {
   161  			sigs = append(sigs, &types.NotarySigs{ID: ik.id, Kind: int32(ik.kind), Pending: pending})
   162  		}
   163  	}
   164  
   165  	sort.SliceStable(sigs, func(i, j int) bool {
   166  		// sigs could be "" so we need to sort on ID as well
   167  		switch strings.Compare(sigs[i].ID, sigs[j].ID) {
   168  		case -1:
   169  			return true
   170  		case 1:
   171  			return false
   172  		}
   173  
   174  		return sigs[i].Sig < sigs[j].Sig
   175  	})
   176  
   177  	pl := types.Payload{
   178  		Data: &types.PayloadNotary{
   179  			Notary: &types.Notary{
   180  				Sigs: sigs,
   181  			},
   182  		},
   183  	}
   184  	return proto.Marshal(pl.IntoProto())
   185  }
   186  
   187  func (n *SnapshotNotary) restoreNotary(notary *types.Notary, p *types.Payload) error {
   188  	var (
   189  		sigs    = map[idKind]map[nodeSig]struct{}{}
   190  		retries = &txTracker{
   191  			txs: map[idKind]*signatureTime{},
   192  		}
   193  		isValidator = n.top.IsValidator()
   194  		selfSigned  = map[idKind]bool{}
   195  		self        = n.top.SelfVegaPubKey()
   196  	)
   197  
   198  	for _, s := range notary.Sigs {
   199  		idK := idKind{id: s.ID, kind: v1.NodeSignatureKind(s.Kind)}
   200  
   201  		sig, err := hex.DecodeString(s.Sig)
   202  		if err != nil {
   203  			n.log.Panic("invalid signature from snapshot", logging.Error(err))
   204  		}
   205  		ns := nodeSig{node: s.Node, sig: string(sig)}
   206  		if isValidator && s.Pending {
   207  			signed := selfSigned[idK]
   208  			if !signed {
   209  				selfSigned[idK] = strings.EqualFold(s.Node, self)
   210  			}
   211  		}
   212  
   213  		if _, ok := sigs[idK]; !ok {
   214  			sigs[idK] = map[nodeSig]struct{}{}
   215  		}
   216  
   217  		if len(ns.node) != 0 && len(ns.sig) != 0 {
   218  			sigs[idK][ns] = struct{}{}
   219  		}
   220  
   221  		if s.Pending {
   222  			n.pendingSignatures[idK] = struct{}{}
   223  		}
   224  	}
   225  
   226  	for resource, ok := range selfSigned {
   227  		if !ok {
   228  			// this is not signed, just add it to the retries list
   229  			n.log.Info("self-signature missing, adding to retry list", logging.String("id", resource.id))
   230  			retries.Add(resource, nil)
   231  		}
   232  	}
   233  
   234  	n.sigs = sigs
   235  	n.retries = retries
   236  	var err error
   237  	n.serialised, err = proto.Marshal(p.IntoProto())
   238  	return err
   239  }