code.vegaprotocol.io/vega@v0.79.0/core/staking/checkpoint.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 staking 17 18 import ( 19 "context" 20 "sort" 21 22 "code.vegaprotocol.io/vega/core/events" 23 "code.vegaprotocol.io/vega/core/types" 24 "code.vegaprotocol.io/vega/libs/proto" 25 "code.vegaprotocol.io/vega/logging" 26 checkpoint "code.vegaprotocol.io/vega/protos/vega/checkpoint/v1" 27 pbevents "code.vegaprotocol.io/vega/protos/vega/events/v1" 28 ) 29 30 type Checkpoint struct { 31 log *logging.Logger 32 accounting *Accounting 33 stakeVerifier *StakeVerifier 34 ethEventSource EthereumEventSource 35 } 36 37 func NewCheckpoint( 38 log *logging.Logger, 39 accounting *Accounting, 40 stakeVerifier *StakeVerifier, 41 ethEventSource EthereumEventSource, 42 ) *Checkpoint { 43 return &Checkpoint{ 44 log: log, 45 accounting: accounting, 46 stakeVerifier: stakeVerifier, 47 ethEventSource: ethEventSource, 48 } 49 } 50 51 func (c *Checkpoint) Name() types.CheckpointName { 52 return types.StakingCheckpoint 53 } 54 55 func (c *Checkpoint) Checkpoint() ([]byte, error) { 56 msg := &checkpoint.Staking{ 57 Accepted: c.getAcceptedEvents(), 58 LastBlockSeen: c.getLastBlockSeen(), 59 } 60 ret, err := proto.Marshal(msg) 61 if err != nil { 62 return nil, err 63 } 64 return ret, nil 65 } 66 67 func (c *Checkpoint) Load(ctx context.Context, data []byte) error { 68 b := checkpoint.Staking{} 69 if err := proto.Unmarshal(data, &b); err != nil { 70 return err 71 } 72 73 // first we deduplicates those events, this is a fix for v0.50.4 74 dedup := dedupEvents(b.Accepted) 75 76 for _, evt := range dedup { 77 stakeLinking := types.StakeLinkingFromProto(evt) 78 79 // this will send all necessary events as well 80 c.accounting.AddEvent(ctx, stakeLinking) 81 // now add event to the hash mapping 82 if !c.stakeVerifier.ensureNotDuplicate(stakeLinking.ID, stakeLinking.Hash()) { 83 c.log.Panic("invalid checkpoint, duplicate event stored", 84 logging.String("event-id", stakeLinking.ID), 85 ) 86 } 87 } 88 89 stakeLinkingEvents := make([]events.Event, 0, len(b.Accepted)) 90 for _, acc := range c.accounting.hashableAccounts { 91 for _, e := range acc.Events { 92 stakeLinkingEvents = append(stakeLinkingEvents, events.NewStakeLinking(ctx, *e)) 93 } 94 } 95 96 c.accounting.broker.SendBatch(stakeLinkingEvents) 97 98 // 0 is default value, we assume that it was then not set 99 if b.LastBlockSeen != 0 { 100 for _, addr := range c.stakeVerifier.ocv.GetStakingBridgeAddresses() { 101 c.ethEventSource.UpdateContractBlock(addr, c.accounting.chainID, b.LastBlockSeen) 102 } 103 } 104 105 return nil 106 } 107 108 func (c *Checkpoint) getAcceptedEvents() []*pbevents.StakeLinking { 109 out := make([]*pbevents.StakeLinking, 0, len(c.accounting.hashableAccounts)) 110 111 for _, acc := range c.accounting.hashableAccounts { 112 for _, evt := range acc.Events { 113 out = append(out, evt.IntoProto()) 114 } 115 } 116 return out 117 } 118 119 // getLastBlockSeen will return the oldest pending transaction block 120 // from the stake verifier. By doing so we can restart listening to ethereum 121 // from the block of the oldest non accepted / verified stake linking event 122 // which should ensure that we haven't missed any. 123 func (c *Checkpoint) getLastBlockSeen() uint64 { 124 if block := c.stakeVerifier.getLastBlockSeen(); block != 0 { 125 return block 126 } 127 128 // now if block is still 0, we use the accounting stuff to find 129 // the newest block verified then instead ... 130 return c.accounting.getLastBlockSeen() 131 } 132 133 type key struct { 134 txHash string 135 logIndex, blockHeight uint64 136 } 137 138 func dedupEvents(evts []*pbevents.StakeLinking) []*pbevents.StakeLinking { 139 evtsM := map[key]*pbevents.StakeLinking{} 140 for _, v := range evts { 141 k := key{v.TxHash, v.LogIndex, v.BlockHeight} 142 evt, ok := evtsM[k] 143 if !ok { 144 // we haven't seen this event, just add it and move on 145 evtsM[k] = v 146 continue 147 } 148 // we have seen this one already, let's save to earliest one only 149 if evt.FinalizedAt > v.FinalizedAt { 150 evtsM[k] = v 151 } 152 } 153 154 // now we sort and return 155 out := make([]*pbevents.StakeLinking, 0, len(evtsM)) 156 for _, v := range evtsM { 157 out = append(out, v) 158 } 159 160 sort.Slice(out, func(i, j int) bool { return out[i].Id < out[j].Id }) 161 return out 162 }