github.com/codingfuture/orig-energi3@v0.8.4/energi/service/checkpoints.go (about) 1 // Copyright 2019 The Energi Core Authors 2 // This file is part of the Energi Core library. 3 // 4 // The Energi Core library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The Energi Core library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the Energi Core library. If not, see <http://www.gnu.org/licenses/>. 16 17 package service 18 19 import ( 20 "context" 21 "math/big" 22 23 "github.com/ethereum/go-ethereum/accounts/abi/bind" 24 "github.com/ethereum/go-ethereum/common" 25 "github.com/ethereum/go-ethereum/core" 26 "github.com/ethereum/go-ethereum/eth" 27 "github.com/ethereum/go-ethereum/eth/downloader" 28 "github.com/ethereum/go-ethereum/log" 29 "github.com/ethereum/go-ethereum/node" 30 "github.com/ethereum/go-ethereum/p2p" 31 "github.com/ethereum/go-ethereum/rpc" 32 33 energi_abi "energi.world/core/gen3/energi/abi" 34 energi_common "energi.world/core/gen3/energi/common" 35 energi_params "energi.world/core/gen3/energi/params" 36 ) 37 38 const ( 39 cppChanBufferSize = 10 40 ) 41 42 type CheckpointProposalEvent struct { 43 core.Checkpoint 44 Proposal common.Address 45 } 46 47 type CheckpointService struct { 48 server *p2p.Server 49 eth *eth.Ethereum 50 51 cpRegistry *energi_abi.ICheckpointRegistry 52 callOpts *bind.CallOpts 53 } 54 55 func NewCheckpointService(ethServ *eth.Ethereum) (node.Service, error) { 56 r := &CheckpointService{ 57 eth: ethServ, 58 callOpts: &bind.CallOpts{}, 59 } 60 return r, nil 61 } 62 63 func (c *CheckpointService) Protocols() []p2p.Protocol { 64 return nil 65 } 66 67 func (c *CheckpointService) APIs() []rpc.API { 68 return nil 69 } 70 71 func (c *CheckpointService) Start(server *p2p.Server) (err error) { 72 c.cpRegistry, err = energi_abi.NewICheckpointRegistry( 73 energi_params.Energi_CheckpointRegistry, c.eth.APIBackend) 74 if err != nil { 75 return err 76 } 77 78 c.server = server 79 80 //--- 81 oldCheckpoints, err := c.cpRegistry.Checkpoints(c.callOpts) 82 if err != nil { 83 log.Error("Failed to get old checkpoints (startup)", "err", err) 84 } else if lc := len(oldCheckpoints); lc > 0 { 85 // NOTE: enable the latest checkpoint immediately for safety reasons 86 c.onCheckpoint(oldCheckpoints[lc-1], false) 87 } 88 89 //--- 90 go c.loop() 91 return nil 92 } 93 94 func (c *CheckpointService) Stop() error { 95 return nil 96 } 97 98 func (c *CheckpointService) waitDownloader() bool { 99 events := c.eth.EventMux().Subscribe( 100 downloader.StartEvent{}, 101 downloader.DoneEvent{}, 102 downloader.FailedEvent{}, 103 ) 104 defer events.Unsubscribe() 105 106 for { 107 select { 108 case ev := <-events.Chan(): 109 if ev == nil { 110 return false 111 } 112 switch ev.Data.(type) { 113 case downloader.StartEvent: 114 continue 115 case downloader.DoneEvent: 116 return true 117 case downloader.FailedEvent: 118 return c.eth.BlockChain().IsRunning() 119 } 120 } 121 } 122 } 123 124 func (c *CheckpointService) loop() { 125 if !c.waitDownloader() { 126 return 127 } 128 129 cpChan := make(chan *energi_abi.ICheckpointRegistryCheckpoint, cppChanBufferSize) 130 131 watchOpts := &bind.WatchOpts{ 132 Context: context.WithValue( 133 context.Background(), 134 energi_params.GeneralProxyCtxKey, 135 energi_common.GeneralProxyHashGen(c.eth.BlockChain()), 136 ), 137 } 138 139 subscribe, err := c.cpRegistry.WatchCheckpoint(watchOpts, cpChan, []*big.Int{}) 140 if err != nil { 141 log.Error("Failed checkpoint subscription", "err", err) 142 return 143 } 144 145 defer subscribe.Unsubscribe() 146 147 oldCheckpoints, err := c.cpRegistry.Checkpoints(c.callOpts) 148 if err != nil { 149 log.Error("Failed to get old checkpoints", "err", err) 150 } else { 151 // NOTE: we should feed for recent first 152 for i := len(oldCheckpoints) - 1; i >= 0; i-- { 153 c.onCheckpoint(oldCheckpoints[i], false) 154 } 155 } 156 157 for { 158 select { 159 case err = <-subscribe.Err(): 160 log.Debug("Checkpoint subscription error", "err", err) 161 return 162 163 case cpData := <-cpChan: 164 c.onCheckpoint(cpData.Checkpoint, true) 165 } 166 } 167 } 168 169 func (c *CheckpointService) onCheckpoint(cpAddr common.Address, live bool) { 170 backend := c.eth.APIBackend 171 cppSigner := backend.ChainConfig().Energi.CPPSigner 172 173 cp, err := energi_abi.NewICheckpointV2Caller(cpAddr, backend) 174 if err != nil { 175 log.Warn("Failed to create CP contract caller", "addr", cpAddr, "err", err) 176 return 177 } 178 179 info, err := cp.Info(c.callOpts) 180 if err != nil { 181 log.Warn("Failed to get CP info", "addr", cpAddr, "err", err) 182 return 183 } 184 185 cpp_sig, err := cp.Signature(c.callOpts, cppSigner) 186 if err != nil { 187 log.Debug("Skipping checkpoint with no CPP sig", "addr", cpAddr, "err", err) 188 return 189 } 190 191 if len(cpp_sig) >= 65 { 192 // Drop Ecrecover workaround 193 cpp_sig[64] -= 27 194 } 195 196 sigs := []core.CheckpointSignature{ 197 core.CheckpointSignature(cpp_sig), 198 } 199 200 backend.AddDynamicCheckpoint(info.Since.Uint64(), info.Number.Uint64(), info.Hash, sigs) 201 202 if live { 203 log.Warn("Found new dynamic checkpoint", "num", info.Number, "hash", common.Hash(info.Hash).Hex()) 204 205 c.eth.EventMux().Post(CheckpointProposalEvent{ 206 core.Checkpoint{ 207 Since: info.Since.Uint64(), 208 Number: info.Number.Uint64(), 209 Hash: info.Hash, 210 }, 211 cpAddr, 212 }) 213 } 214 }