github.com/0chain/gosdk@v1.17.11/zcnbridge/authorizers_query.go (about) 1 package zcnbridge 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io/ioutil" 7 "math" 8 "net/http" 9 "strings" 10 "sync" 11 12 "github.com/0chain/gosdk/core/common" 13 "github.com/0chain/gosdk/zcnbridge/errors" 14 "github.com/0chain/gosdk/zcnbridge/ethereum" 15 h "github.com/0chain/gosdk/zcnbridge/http" 16 "github.com/0chain/gosdk/zcnbridge/log" 17 "github.com/0chain/gosdk/zcnbridge/wallet" 18 "github.com/0chain/gosdk/zcnbridge/zcnsc" 19 "github.com/0chain/gosdk/zcncore" 20 "go.uber.org/zap" 21 ) 22 23 type ( 24 // authorizerResponse is HTTP client response event 25 authorizerResponse struct { 26 // AuthorizerID is authorizer where the job was performed 27 AuthorizerID string 28 // event is server job event 29 event JobResult 30 // error describes an error occurred during event processing on client side during the call to server 31 error 32 } 33 34 requestHandler struct { 35 path string 36 values map[string]string 37 bodyDecoder func([]byte) (JobResult, error) 38 } 39 40 responseChannelType chan *authorizerResponse 41 eventsChannelType chan []JobResult 42 ) 43 44 var ( 45 client *http.Client 46 ) 47 48 // QueryEthereumMintPayload gets burn ticket and creates mint payload to be minted in the Ethereum chain 49 // zchainBurnHash - Ethereum burn transaction hash 50 func (b *BridgeClient) QueryEthereumMintPayload(zchainBurnHash string) (*ethereum.MintPayload, error) { 51 client = h.CleanClient() 52 authorizers, err := getAuthorizers(true) 53 54 if err != nil || len(authorizers) == 0 { 55 return nil, errors.Wrap("get_authorizers", "failed to get authorizers", err) 56 } 57 58 var ( 59 totalWorkers = len(authorizers) 60 values = map[string]string{ 61 "hash": zchainBurnHash, 62 } 63 ) 64 65 handler := &requestHandler{ 66 path: wallet.BurnNativeTicketPath, 67 values: values, 68 bodyDecoder: func(body []byte) (JobResult, error) { 69 ev := &ProofZCNBurn{} 70 err := json.Unmarshal(body, ev) 71 return ev, err 72 }, 73 } 74 75 thresh := b.ConsensusThreshold 76 results := queryAllAuthorizers(authorizers, handler) 77 numSuccess := len(results) 78 quorum := math.Ceil((float64(numSuccess) * 100) / float64(totalWorkers)) 79 80 if numSuccess > 0 && quorum >= thresh { 81 burnTicket, ok := results[0].(*ProofZCNBurn) 82 if !ok { 83 return nil, errors.Wrap("type_cast", "failed to convert to *proofEthereumBurn", err) 84 } 85 86 var sigs []*ethereum.AuthorizerSignature 87 for _, result := range results { 88 ticket := result.(*ProofZCNBurn) 89 sig := ðereum.AuthorizerSignature{ 90 ID: ticket.GetAuthorizerID(), 91 Signature: ticket.Signature, 92 } 93 sigs = append(sigs, sig) 94 } 95 96 payload := ðereum.MintPayload{ 97 ZCNTxnID: burnTicket.TxnID, 98 Amount: burnTicket.Amount, 99 To: burnTicket.To, 100 Nonce: burnTicket.Nonce, 101 Signatures: sigs, 102 } 103 104 return payload, nil 105 } 106 107 text := fmt.Sprintf("failed to reach the quorum. #Success: %d from #Total: %d", numSuccess, totalWorkers) 108 return nil, errors.New("get_burn_ticket", text) 109 } 110 111 // QueryEthereumBurnEvents gets ethereum burn events 112 func (b *BridgeClient) QueryEthereumBurnEvents(startNonce string) ([]*ethereum.BurnEvent, error) { 113 client = h.CleanClient() 114 authorizers, err := getAuthorizers(true) 115 116 if err != nil || len(authorizers) == 0 { 117 return nil, errors.Wrap("get_authorizers", "failed to get authorizers", err) 118 } 119 120 var ( 121 totalWorkers = len(authorizers) 122 values = map[string]string{ 123 "clientid": zcncore.GetClientWalletID(), 124 "ethereumaddress": b.EthereumAddress, 125 "startnonce": startNonce, 126 } 127 ) 128 129 handler := &requestHandler{ 130 path: wallet.BurnWzcnBurnEventsPath, 131 values: values, 132 bodyDecoder: func(body []byte) (JobResult, error) { 133 ev := &EthereumBurnEvents{} 134 err := json.Unmarshal(body, ev) 135 return ev, err 136 }, 137 } 138 139 thresh := b.ConsensusThreshold 140 results := queryAllAuthorizers(authorizers, handler) 141 numSuccess := len(results) 142 quorum := math.Ceil((float64(numSuccess) * 100) / float64(totalWorkers)) 143 144 if numSuccess > 0 && quorum >= thresh { 145 burnEvents, ok := results[0].(*EthereumBurnEvents) 146 if !ok { 147 return nil, errors.Wrap("type_cast", "failed to convert to *ethereumBurnEvents", err) 148 } 149 150 result := make([]*ethereum.BurnEvent, 0) 151 152 for _, burnEvent := range burnEvents.BurnEvents { 153 result = append(result, ðereum.BurnEvent{ 154 Nonce: burnEvent.Nonce, 155 Amount: burnEvent.Amount, 156 TransactionHash: burnEvent.TransactionHash, 157 }) 158 } 159 160 return result, nil 161 } 162 163 text := fmt.Sprintf("failed to reach the quorum. #Success: %d from #Total: %d", numSuccess, totalWorkers) 164 return nil, errors.New("get_burn_events", text) 165 } 166 167 // QueryZChainMintPayload gets burn ticket and creates mint payload to be minted in the ZChain 168 // ethBurnHash - Ethereum burn transaction hash 169 func (b *BridgeClient) QueryZChainMintPayload(ethBurnHash string) (*zcnsc.MintPayload, error) { 170 client = h.CleanClient() 171 authorizers, err := getAuthorizers(true) 172 log.Logger.Info("Got authorizers", zap.Int("amount", len(authorizers))) 173 174 if err != nil || len(authorizers) == 0 { 175 return nil, errors.Wrap("get_authorizers", "failed to get authorizers", err) 176 } 177 178 var ( 179 totalWorkers = len(authorizers) 180 values = map[string]string{ 181 "hash": ethBurnHash, 182 "clientid": zcncore.GetClientWalletID(), 183 } 184 ) 185 186 handler := &requestHandler{ 187 path: wallet.BurnWzcnTicketPath, 188 values: values, 189 bodyDecoder: func(body []byte) (JobResult, error) { 190 ev := &WZCNBurnEvent{} 191 err := json.Unmarshal(body, ev) 192 return ev, err 193 }, 194 } 195 196 thresh := b.ConsensusThreshold 197 results := queryAllAuthorizers(authorizers, handler) 198 numSuccess := len(results) 199 quorum := math.Ceil((float64(numSuccess) * 100) / float64(totalWorkers)) 200 201 if numSuccess > 0 && quorum >= thresh { 202 burnTicket, ok := results[0].Data().(*ProofEthereumBurn) 203 if !ok { 204 return nil, errors.Wrap("type_cast", "failed to convert to *proofEthereumBurn", err) 205 } 206 207 var sigs []*zcnsc.AuthorizerSignature 208 for _, result := range results { 209 ticket := result.Data().(*ProofEthereumBurn) 210 sig := &zcnsc.AuthorizerSignature{ 211 ID: result.GetAuthorizerID(), 212 Signature: ticket.Signature, 213 } 214 sigs = append(sigs, sig) 215 } 216 217 payload := &zcnsc.MintPayload{ 218 EthereumTxnID: burnTicket.TxnID, 219 Amount: common.Balance(burnTicket.Amount), 220 Nonce: burnTicket.Nonce, 221 Signatures: sigs, 222 ReceivingClientID: burnTicket.ReceivingClientID, 223 } 224 225 return payload, nil 226 } 227 228 text := fmt.Sprintf("failed to reach the quorum. #Success: %d from #Total: %d", numSuccess, totalWorkers) 229 return nil, errors.New("get_burn_ticket", text) 230 } 231 232 func queryAllAuthorizers(authorizers []*AuthorizerNode, handler *requestHandler) []JobResult { 233 var ( 234 totalWorkers = len(authorizers) 235 eventsChannel = make(eventsChannelType) 236 responseChannel = make(responseChannelType, totalWorkers) 237 ) 238 defer close(eventsChannel) 239 240 var wg sync.WaitGroup 241 242 for _, authorizer := range authorizers { 243 wg.Add(1) 244 go queryAuthorizer(authorizer, handler, responseChannel) 245 } 246 247 go handleResponse(responseChannel, eventsChannel, &wg) 248 249 wg.Wait() 250 close(responseChannel) 251 results := <-eventsChannel 252 253 return results 254 } 255 256 func handleResponse(responseChannel responseChannelType, eventsChannel eventsChannelType, wg *sync.WaitGroup) { 257 var events []JobResult 258 for job := range responseChannel { 259 if job.error == nil { 260 event := job.event 261 event.SetAuthorizerID(job.AuthorizerID) 262 events = append(events, event) 263 } 264 wg.Done() 265 } 266 eventsChannel <- events 267 } 268 269 func queryAuthorizer(au *AuthorizerNode, request *requestHandler, responseChannel responseChannelType) { 270 Logger.Info("Query from authorizer", zap.String("ID", au.ID), zap.String("URL", au.URL)) 271 ticketURL := strings.TrimSuffix(au.URL, "/") + request.path 272 273 req, err := http.NewRequest("GET", ticketURL, nil) 274 if err != nil { 275 log.Logger.Error("failed to create request", zap.Error(err)) 276 return 277 } 278 279 q := req.URL.Query() 280 for k, v := range request.values { 281 q.Add(k, v) 282 } 283 req.URL.RawQuery = q.Encode() 284 Logger.Info(req.URL.String()) 285 resp, body := readResponse(client.Do(req)) 286 resp.AuthorizerID = au.ID 287 288 if resp.error != nil { 289 Logger.Error( 290 "failed to process response", 291 zap.Error(resp.error), 292 zap.String("node.id", au.ID), 293 zap.String("node.url", au.URL), 294 ) 295 } 296 297 event, errEvent := request.bodyDecoder(body) 298 event.SetAuthorizerID(au.ID) 299 300 if errEvent != nil { 301 err := errors.Wrap("decode_message_body", "failed to decode message body", errEvent) 302 log.Logger.Error( 303 "failed to decode event body", 304 zap.Error(err), 305 zap.String("node.id", au.ID), 306 zap.String("node.url", au.URL), 307 zap.String("body", string(body)), 308 ) 309 } 310 311 resp.event = event 312 313 responseChannel <- resp 314 } 315 316 func readResponse(response *http.Response, err error) (res *authorizerResponse, body []byte) { 317 res = &authorizerResponse{} 318 if err != nil { 319 err = errors.Wrap("authorizer_post_process", "failed to call the authorizer", err) 320 Logger.Error("request response error", zap.Error(err)) 321 } 322 323 if response == nil { 324 res.error = err 325 Logger.Error("response is empty", zap.Error(err)) 326 return res, nil 327 } 328 329 if response.StatusCode >= 400 { 330 err = errors.Wrap("authorizer_post_process", fmt.Sprintf("error %d", response.StatusCode), err) 331 Logger.Error("request response status", zap.Error(err)) 332 } 333 334 body, er := ioutil.ReadAll(response.Body) 335 log.Logger.Debug("response", zap.String("response", string(body))) 336 defer response.Body.Close() 337 338 if er != nil || len(body) == 0 { 339 var errstrings []string 340 er = errors.Wrap("authorizer_post_process", "failed to read body", er) 341 if err != nil { 342 errstrings = append(errstrings, err.Error()) 343 } 344 errstrings = append(errstrings, er.Error()) 345 err = fmt.Errorf(strings.Join(errstrings, ":")) 346 } 347 348 res.error = err 349 350 return res, body 351 }