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 }