github.com/supragya/TendermintConnector@v0.0.0-20210619045051-113e32b84fb1/chains/cosmos/handlerCOSMOS.go (about) 1 package cosmos 2 3 import ( 4 5 // "bytes" 6 7 "bufio" 8 "crypto/sha256" 9 b64 "encoding/base64" 10 "encoding/hex" 11 "encoding/json" 12 "errors" 13 "fmt" 14 "io" 15 "io/ioutil" 16 "net" 17 "net/http" 18 "os" 19 "runtime" 20 "strconv" 21 "strings" 22 "time" 23 24 "github.com/gogo/protobuf/proto" 25 lru "github.com/hashicorp/golang-lru" 26 log "github.com/sirupsen/logrus" 27 "github.com/supragya/TendermintConnector/chains" 28 "github.com/supragya/TendermintConnector/chains/cosmos/conn" 29 "github.com/supragya/TendermintConnector/chains/cosmos/crypto/ed25519" 30 "github.com/supragya/TendermintConnector/chains/cosmos/crypto/merkle" 31 "github.com/supragya/TendermintConnector/chains/cosmos/libs/bits" 32 flow "github.com/supragya/TendermintConnector/chains/cosmos/libs/flowrate" 33 tmmath "github.com/supragya/TendermintConnector/chains/cosmos/libs/math" 34 "github.com/supragya/TendermintConnector/chains/cosmos/libs/protoio" 35 "github.com/supragya/TendermintConnector/chains/cosmos/libs/timer" 36 tmcons "github.com/supragya/TendermintConnector/chains/cosmos/proto/tendermint/consensus" 37 tmp2p "github.com/supragya/TendermintConnector/chains/cosmos/proto/tendermint/p2p" 38 tmproto "github.com/supragya/TendermintConnector/chains/cosmos/proto/tendermint/types" 39 "github.com/supragya/TendermintConnector/marlin" 40 marlinTypes "github.com/supragya/TendermintConnector/types" 41 ) 42 43 // ServicedTMCore is a string associated with each TM core handler 44 // to decipher which handler is to be attached. 45 var ServicedTMCore chains.NodeType = chains.NodeType{Version: "", Network: "cosmoshub-4", ProtocolVersionApp: "0", ProtocolVersionBlock: "11", ProtocolVersionP2p: "8"} 46 47 // ---------------------- DATA CONNECT INTERFACE -------------------------------- 48 49 func RunDataConnect(peerAddr string, 50 marlinTo chan marlinTypes.MarlinMessage, 51 marlinFrom chan marlinTypes.MarlinMessage, 52 isConnectionOutgoing bool, 53 keyFile string, 54 listenPort int) { 55 log.Info("Starting cosmos Tendermint Core Handler - Vanilla Tendermint") 56 57 if keyFile != "" { 58 isKeyFileUsed = true 59 keyFileLocation = keyFile 60 } 61 62 for { 63 handler, err := createTMHandler(peerAddr, "0.0.0.0:0", marlinTo, marlinFrom, isConnectionOutgoing, listenPort, true) 64 65 if err != nil { 66 log.Error("Error encountered while creating TM Handler: ", err) 67 os.Exit(1) 68 } 69 70 if isConnectionOutgoing { 71 err = handler.dialPeer() 72 } else { 73 err = handler.acceptPeer() 74 } 75 if err != nil { 76 log.Error("Base Connection establishment with peer unsuccessful: ", err) 77 goto REATTEMPT_CONNECTION 78 } 79 80 err = handler.upgradeConnectionAndHandshake() 81 if err != nil { 82 log.Error("Error while upgrading connection and handshaking with peer: ", err) 83 goto REATTEMPT_CONNECTION 84 } 85 86 handler.beginServicing() 87 88 select { 89 case <-handler.signalConnError: 90 handler.signalShutSend <- struct{}{} 91 handler.signalShutRecv <- struct{}{} 92 handler.signalShutThroughput <- struct{}{} 93 goto REATTEMPT_CONNECTION 94 } 95 96 REATTEMPT_CONNECTION: 97 handler.baseConnection.Close() 98 handler.secretConnection.Close() 99 log.Info("Error encountered with connection to the peer. Attempting reconnect post 1 second.") 100 time.Sleep(1 * time.Second) 101 } 102 } 103 104 func (h *TendermintHandler) dialPeer() error { 105 var err error 106 h.baseConnection, err = net.DialTimeout("tcp", h.peerAddr, 2000*time.Millisecond) 107 if err != nil { 108 return err 109 } 110 111 return nil 112 } 113 114 func (h *TendermintHandler) acceptPeer() error { 115 116 listener, err := net.Listen("tcp", "0.0.0.0:"+strconv.Itoa(h.listenPort)) 117 if err != nil { 118 return err 119 } 120 121 log.Info("TMCore side listening for dials to ", 122 string(hex.EncodeToString(h.privateKey.PubKey().Address())), "@<SYSTEM-IP-ADDR>:", h.listenPort) 123 124 h.baseConnection, err = listener.Accept() 125 if err != nil { 126 return err 127 } 128 129 return nil 130 } 131 132 func (h *TendermintHandler) upgradeConnectionAndHandshake() error { 133 log.Info("Handshaking procedure") 134 var err error 135 h.secretConnection, err = conn.MakeSecretConnection(h.baseConnection, h.privateKey) 136 if err != nil { 137 return err 138 } 139 140 err = h.handshake() 141 if err != nil { 142 return err 143 } 144 145 log.Info("Established connection with TM peer [" + 146 string(hex.EncodeToString(h.secretConnection.RemotePubKey().Address())) + 147 "] a.k.a. " + h.peerNodeInfo.Moniker) 148 return nil 149 } 150 151 func (h *TendermintHandler) handshake() error { 152 var ( 153 errc = make(chan error, 2) 154 ourNodeInfo tmp2p.DefaultNodeInfo = tmp2p.DefaultNodeInfo{ 155 tmp2p.ProtocolVersion{App: 0, Block: 11, P2P: 8}, 156 string(hex.EncodeToString(h.privateKey.PubKey().Address())), 157 "tcp://127.0.0.1:20026", //TODO Correct this - v0.2 prerelease 158 "cosmoshub-4", 159 "", 160 []byte{channelBc, channelCsSt, channelCsDc, channelCsVo, 161 channelCsVs, channelMm, channelEv}, 162 "marlin-tendermint-connector", 163 tmp2p.DefaultNodeInfoOther{"on", "tcp://0.0.0.0:26667"}, // TODO: Correct this - v0.2 prerelease 164 } 165 ) 166 167 go func(errc chan<- error, c net.Conn) { 168 _, err := protoio.NewDelimitedWriter(c).WriteMsg(&ourNodeInfo) 169 if err != nil { 170 log.Error("Error encountered while sending handshake message ", err) 171 } 172 errc <- err 173 }(errc, h.secretConnection) 174 175 go func(errc chan<- error, c net.Conn) { 176 protoReader := protoio.NewDelimitedReader(c, 10240) 177 _, err := protoReader.ReadMsg(&h.peerNodeInfo) 178 if err != nil { 179 log.Error("Error encountered while recieving handshake message ", err) 180 } 181 errc <- err 182 }(errc, h.secretConnection) 183 184 for i := 0; i < cap(errc); i++ { 185 err := <-errc 186 if err != nil { 187 log.Error("Encountered error in handshake with TM core: ", err) 188 return err 189 } 190 } 191 return nil 192 } 193 194 func (h *TendermintHandler) beginServicing() error { 195 // Create a P2P Connection 196 h.p2pConnection = P2PConnection{ 197 conn: h.secretConnection, 198 bufConnReader: bufio.NewReaderSize(h.secretConnection, 65535), 199 bufConnWriter: bufio.NewWriterSize(h.secretConnection, 65535), 200 sendMonitor: flow.New(0, 0), 201 recvMonitor: flow.New(0, 0), 202 send: make(chan struct{}, 1), 203 pong: make(chan struct{}, 1), 204 doneSendRoutine: make(chan struct{}, 1), 205 quitSendRoutine: make(chan struct{}, 1), 206 quitRecvRoutine: make(chan struct{}, 1), 207 flushTimer: timer.NewThrottleTimer("flush", 100*time.Millisecond), 208 pingTimer: time.NewTicker(30 * time.Second), 209 pongTimeoutCh: make(chan bool, 1), 210 } 211 212 // Start P2P Send and recieve routines + Status messages for message throughput 213 go h.sendRoutine() 214 go h.recvRoutine() 215 go h.throughput.presentThroughput(5, h.signalShutThroughput) 216 217 // Allow cosmosnet messages from marlin Relay 218 marlin.AllowServicedChainMessages(h.servicedChainId) 219 return nil 220 } 221 222 func (h *TendermintHandler) sendRoutine() { 223 protoWriter := protoio.NewDelimitedWriter(h.p2pConnection.bufConnWriter) 224 for { 225 var _n int 226 var err error 227 select { 228 case <-h.p2pConnection.pong: 229 // log.Info("Send Pong") 230 _n, err = protoWriter.WriteMsg(mustWrapPacket(&tmp2p.PacketPong{})) 231 if err != nil { 232 log.Error("Failed to send PacketPong", "err", err) 233 h.flush() 234 h.signalConnError <- struct{}{} 235 return 236 } 237 h.p2pConnection.sendMonitor.Update(_n) 238 h.flush() 239 240 // Actual message packets from Marlin Relay (encoded in Marlin Tendermint Data Transfer Protocol v1) 241 case marlinMsg := <-h.marlinFrom: 242 switch marlinMsg.Channel { 243 case channelCsSt: 244 msgEncoded, err := h.getBytesFromMarlinMessage(&marlinMsg) 245 if err != nil { 246 log.Debug("Cannot get bytes from marlin to a valid Consensus Message: ", err) 247 } 248 msg, err := decodeMsg(msgEncoded) 249 if err != nil { 250 log.Debug("Cannot decode messages to CsSt: ", err) 251 } 252 switch msg.(type) { 253 case *NewRoundStepMessage: 254 for _, pkt := range marlinMsg.Packets { 255 _n, err := protoWriter.WriteMsg(mustWrapPacket(&tmp2p.PacketMsg{ 256 ChannelID: int32(channelCsSt), 257 EOF: pkt.EOF == 1, 258 Data: pkt.Bytes, 259 })) 260 if err != nil { 261 log.Error("Error occurred in sending data to TMCore: ", err) 262 // h.signalConnError <- struct{}{} 263 } 264 h.p2pConnection.sendMonitor.Update(int(_n)) 265 err = h.p2pConnection.bufConnWriter.Flush() 266 if err != nil { 267 log.Error("Cannot flush buffer: ", err) 268 } 269 } 270 } 271 } 272 } 273 } 274 } 275 276 func (h *TendermintHandler) getBytesFromMarlinMessage(marlinMsg *marlinTypes.MarlinMessage) ([]byte, error) { 277 var databuf []byte 278 for _, pkt := range marlinMsg.Packets { 279 databuf = append(databuf, pkt.Bytes...) 280 } 281 return databuf, nil 282 } 283 284 func (h *TendermintHandler) flush() { 285 err := h.p2pConnection.bufConnWriter.Flush() 286 if err != nil { 287 log.Error("BufConnWriter flush failed", "err", err) 288 } 289 } 290 291 func (h *TendermintHandler) recvRoutine() { 292 log.Info("TMCore -> Connector Routine Started") 293 protoReader := protoio.NewDelimitedReader(h.p2pConnection.bufConnReader, 2000) 294 295 FOR_LOOP: 296 for { 297 select { 298 case <-h.signalShutRecv: 299 log.Info("TMCore -> Connector Routine shutdown") 300 break FOR_LOOP 301 default: 302 } 303 h.p2pConnection.recvMonitor.Limit(20000, 5120000, true) 304 305 /* 306 Peek into bufConnReader for debugging 307 308 if numBytes := c.bufConnReader.Buffered(); numBytes > 0 { 309 bz, err := c.bufConnReader.Peek(cmn.MinInt(numBytes, 100)) 310 if err == nil { 311 // return 312 } else { 313 log.Debug("Error peeking connection buffer ", "err ", err) 314 // return nil 315 } 316 log.Info("Peek connection buffer ", "numBytes ", numBytes, " bz ", bz) 317 } 318 */ 319 320 // Read packet type 321 var packet tmp2p.Packet 322 _n, err := protoReader.ReadMsg(&packet) 323 324 h.p2pConnection.recvMonitor.Update(int(_n)) 325 326 // Unmarshalling test 327 if err != nil { 328 if err == io.EOF { 329 log.Error("TMCore -> Connector Connection is closed (likely by the other side) ", err) 330 } else { 331 log.Error("TMCore -> Connector Connection failed (reading byte): ", err) 332 } 333 h.signalConnError <- struct{}{} 334 break FOR_LOOP 335 } 336 337 switch pkt := packet.Sum.(type) { 338 case *tmp2p.Packet_PacketPing: 339 // TODO: prevent abuse, as they cause flush()'s. 340 // https://github.com/tendermint/tendermint/issues/1190 341 // log.Info("Receive Ping ", pkt) 342 select { 343 case h.p2pConnection.pong <- struct{}{}: 344 default: 345 // never block 346 } 347 case *tmp2p.Packet_PacketPong: 348 log.Info("Receive Pong") 349 select { 350 case h.p2pConnection.pongTimeoutCh <- false: 351 default: 352 // never block 353 } 354 case *tmp2p.Packet_PacketMsg: 355 var eof uint32 = 0 356 if pkt.PacketMsg.EOF { 357 eof = 1 358 } 359 h.channelBuffer[byte(pkt.PacketMsg.ChannelID)] = append(h.channelBuffer[byte(pkt.PacketMsg.ChannelID)], 360 marlinTypes.PacketMsg{ 361 EOF: eof, 362 Bytes: pkt.PacketMsg.Data, 363 }) 364 365 // In case of incomplete message 366 if !pkt.PacketMsg.EOF { 367 log.Debug("TMCore -> Connector partial ", byte(pkt.PacketMsg.ChannelID)) 368 break 369 } 370 371 // Reach here only in case message has EOF. At this point the previous 372 // messages are in h.channelBuffer[channel-idx] 373 switch byte(pkt.PacketMsg.ChannelID) { 374 case channelBc: 375 h.throughput.putInfo("from", "=BcMSG", 1) 376 log.Debug("TMCore -> Connector Blockhain is not serviced") 377 h.channelBuffer[channelBc] = h.channelBuffer[channelBc][:0] 378 case channelCsSt: 379 log.Debug("TMCore -> Connector CsSt servicing") 380 submessage, err := h.serviceConsensusStateMessage() 381 if err != nil { 382 log.Warning("Could not service consensus state message due to err: ", err) 383 } 384 h.throughput.putInfo("from", submessage, 1) 385 h.channelBuffer[channelCsSt] = h.channelBuffer[channelCsSt][:0] 386 case channelCsDc: 387 log.Debug("TMCore -> Connector CsDc servicing") 388 submessage, err := h.serviceConsensusDataMessage() 389 if err != nil { 390 log.Warning("Could not service consensus data message due to err: ", err) 391 } 392 h.throughput.putInfo("from", submessage, 1) 393 h.channelBuffer[channelCsDc] = h.channelBuffer[channelCsDc][:0] 394 case channelCsVo: 395 log.Debug("TMCore -> Connector CsDo servicing") 396 submessage, err := h.serviceConsensusVoteMessage() 397 if err != nil { 398 log.Warning("Could not service consensus vote message due to err: ", err) 399 } 400 h.throughput.putInfo("from", submessage, 1) 401 h.channelBuffer[channelCsVo] = h.channelBuffer[channelCsVo][:0] 402 case channelCsVs: 403 h.throughput.putInfo("from", "=CsVsVSB", 1) 404 log.Debug("TMCore -> Connector Consensus Vote Set Bits Channel is not serviced") 405 h.channelBuffer[channelCsVs] = h.channelBuffer[channelCsVs][:0] 406 case channelMm: 407 h.throughput.putInfo("from", "=MmMSG", 1) 408 log.Debug("TMCore -> Connector Mempool Channel is not serviced") 409 h.channelBuffer[channelMm] = h.channelBuffer[channelMm][:0] 410 case channelEv: 411 h.throughput.putInfo("from", "=EvMSG", 1) 412 log.Debug("TMCore -> Connector Evidence Channel is not serviced") 413 h.channelBuffer[channelEv] = h.channelBuffer[channelEv][:0] 414 default: 415 h.throughput.putInfo("from", "=UnkUNK", 1) 416 log.Warning("TMCore -> Connector Unknown ChannelID Message recieved: ", pkt.PacketMsg.ChannelID) 417 h.channelBuffer[byte(pkt.PacketMsg.ChannelID)] = h.channelBuffer[byte(pkt.PacketMsg.ChannelID)][:0] 418 } 419 } 420 } 421 422 // Cleanup 423 close(h.p2pConnection.pong) 424 for range h.p2pConnection.pong { 425 // Drain 426 } 427 } 428 429 func (h *TendermintHandler) getBytesFromChannelBuffer(chanbuf []marlinTypes.PacketMsg) []byte { 430 var databuf []byte 431 for _, pkt := range chanbuf { 432 databuf = append(databuf, pkt.Bytes...) 433 } 434 return databuf 435 } 436 437 func (h *TendermintHandler) serviceConsensusStateMessage() (string, error) { 438 ch := channelCsSt 439 msgBytes := h.getBytesFromChannelBuffer(h.channelBuffer[ch]) 440 msg, err := decodeMsg(msgBytes) 441 if err != nil { 442 return "", err 443 } 444 445 switch msg.(type) { 446 case *NewRoundStepMessage: 447 message := marlinTypes.MarlinMessage{ 448 ChainID: h.servicedChainId, 449 Channel: ch, 450 Packets: h.channelBuffer[ch], 451 } 452 // Send to marlin side 453 // select { 454 // case h.marlinTo <- message: 455 // default: 456 // log.Warning("Too many messages in channel marlinTo. Dropping oldest messages") 457 // _ = <-h.marlinTo 458 // h.marlinTo <- message 459 // } 460 // Reflect Back NRS message to get CsVoVOT messages 461 select { 462 case h.marlinFrom <- message: 463 default: 464 log.Warning("Too many messages in channel marlinFrom. Dropping oldest messages") 465 _ = <-h.marlinFrom 466 h.marlinFrom <- message 467 } 468 return "-CsStNRS", nil 469 case *Proposal: 470 return "-CsStPRO", nil 471 case *NewValidBlockMessage: 472 log.Debug("Found NewValidBlock, not servicing") 473 return "-CsStNVB", nil 474 case *HasVoteMessage: 475 log.Debug("Found HasVote, not servicing") 476 return "-CsStHVM", nil 477 case *VoteSetMaj23Message: 478 log.Debug("Found SetMaj23, not servicing") 479 return "-CsStM23", nil 480 default: 481 log.Warning("Unknown Consensus state message ", msg) 482 return "-CsStXXX", nil 483 } 484 } 485 486 func (h *TendermintHandler) serviceConsensusDataMessage() (string, error) { 487 ch := channelCsDc 488 msgBytes := h.getBytesFromChannelBuffer(h.channelBuffer[ch]) 489 msg, err := decodeMsg(msgBytes) 490 if err != nil { 491 return "", err 492 } 493 494 switch msg.(type) { 495 case *ProposalMessage: 496 h.cahcedDcProposal = msg.(*ProposalMessage) 497 h.cachedDcProposalPacket = make([]marlinTypes.PacketMsg, 0) 498 for _, pkt := range h.channelBuffer[ch] { 499 h.cachedDcProposalPacket = append(h.cachedDcProposalPacket, pkt) 500 } 501 return "@CsDcPRO", nil 502 case *ProposalPOLMessage: 503 log.Debug("Found Proposal POL, not servicing") 504 return "-CsDcPOL", nil 505 case *BlockPartMessage: 506 507 if h.cahcedDcProposal == nil || msg.(*BlockPartMessage).Height != h.cahcedDcProposal.Proposal.Height || msg.(*BlockPartMessage).Round != h.cahcedDcProposal.Proposal.Round { 508 // log.Info("Cannot find the bpm match ", msg.(*BlockPartMessage).Height, msg.(*BlockPartMessage).Round) 509 return "-CsDcBPM", nil 510 } 511 // log.Info("Found the bpm match ", msg.(*BlockPartMessage).Height, msg.(*BlockPartMessage).Round) 512 message := marlinTypes.MarlinMessage{ 513 ChainID: h.servicedChainId, 514 Channel: byte(0x90), 515 Packets: h.cachedDcProposalPacket, 516 Packets2: h.channelBuffer[ch], 517 } 518 519 // log.Info("Sending an Ox90 message ", len(message.Packets), len(message.Packets2)) 520 // Send to marlin side 521 select { 522 case h.marlinTo <- message: 523 default: 524 log.Warning("Too many messages in channel marlinTo. Dropping oldest messages") 525 _ = <-h.marlinTo 526 h.marlinTo <- message 527 } 528 log.Debug("Found BlockPart, not servicing") 529 return "+CsDcBPM", nil 530 default: 531 log.Warning("Unknown Consensus data message ", msg) 532 return "-CsDcXXX", nil 533 } 534 } 535 536 func (h *TendermintHandler) serviceConsensusVoteMessage() (string, error) { 537 ch := channelCsVo 538 msgBytes := h.getBytesFromChannelBuffer(h.channelBuffer[ch]) 539 msg, err := decodeMsg(msgBytes) 540 if err != nil { 541 return "", err 542 } 543 544 switch msg.(type) { 545 case *VoteMessage: 546 message := marlinTypes.MarlinMessage{ 547 ChainID: h.servicedChainId, 548 Channel: ch, 549 Packets: h.channelBuffer[ch], 550 } 551 // Send to marlin side 552 select { 553 case h.marlinTo <- message: 554 default: 555 log.Warning("Too many messages in channel marlinTo. Dropping oldest messages") 556 _ = <-h.marlinTo 557 h.marlinTo <- message 558 } 559 return "+CsVoVOT", nil 560 default: 561 log.Warning("Unknown Consensus vote message ", msg) 562 return "-CsVoXXX", nil 563 } 564 } 565 566 // ---------------------- KEY GENERATION INTERFACE ----------------------------- 567 568 var ServicedKeyFile string = "cosmos" 569 var isKeyFileUsed, memoized bool 570 var keyFileLocation string 571 572 var privateKey ed25519.PrivKey 573 574 func AsSha256(o interface{}) string { 575 h := sha256.New() 576 h.Write([]byte(fmt.Sprintf("%v", o))) 577 578 return fmt.Sprintf("%x", h.Sum(nil)) 579 } 580 581 func GenerateKeyFile(fileLocation string) { 582 log.Info("Generating KeyPair for cosmoshub-4-mainnet") 583 584 privateKey := ed25519.GenPrivKey() 585 publicKey := privateKey.PubKey() 586 587 key := keyData{ 588 Chain: "cosmoshub-4-mainnet", 589 IdString: string(hex.EncodeToString(publicKey.Address())), 590 PrivateKey: privateKey.Bytes(), 591 PublicKey: publicKey.Bytes(), 592 Integrity: "", 593 } 594 595 key.Integrity = AsSha256(key) 596 597 log.Info("ID for node after generating KeyPair: ", key.IdString) 598 599 encodedJson, err := json.MarshalIndent(&key, "", " ") 600 if err != nil { 601 log.Error("Error generating KeyFile: ", err) 602 } 603 err = ioutil.WriteFile(fileLocation, encodedJson, 0644) 604 if err != nil { 605 log.Error("Error generating KeyFile: ", err) 606 } 607 608 log.Info("Successfully written keyfile ", fileLocation) 609 } 610 611 func VerifyKeyFile(fileLocation string) (bool, error) { 612 log.Info("Accessing disk to extract info from KeyFile: ", fileLocation) 613 jsonFile, err := os.Open(fileLocation) 614 // if we os.Open returns an error then handle it 615 if err != nil { 616 log.Error("Error accessing file KeyFile: ", fileLocation, " error: ", err, ". exiting application.") 617 os.Exit(1) 618 } 619 defer jsonFile.Close() 620 621 byteValue, err := ioutil.ReadAll(jsonFile) 622 if err != nil { 623 log.Error("Error decoding KeyFile: ", fileLocation, " error: ", err, ". exiting application.") 624 os.Exit(1) 625 } 626 var key keyData 627 json.Unmarshal(byteValue, &key) 628 629 integrity := key.Integrity 630 key.Integrity = "" 631 632 if key.Chain == "cosmoshub-4-mainnet" && integrity == AsSha256(key) { 633 log.Info("Integrity for KeyFile: ", fileLocation, " checked. Integrity OK.") 634 return true, nil 635 } else { 636 log.Error("Integrity for KeyFile: ", fileLocation, " checked. Integrity NOT OK.") 637 return false, nil 638 } 639 } 640 641 // ---------------------- COMMON UTILITIES --------------------------------- 642 643 func createTMHandler(peerAddr string, 644 rpcAddr string, 645 marlinTo chan marlinTypes.MarlinMessage, 646 marlinFrom chan marlinTypes.MarlinMessage, 647 isConnectionOutgoing bool, 648 listenPort int, 649 isDataConnect bool) (TendermintHandler, error) { 650 chainId, ok := marlinTypes.ServicedChains["cosmoshub-4-mainnet"] 651 if !ok { 652 return TendermintHandler{}, errors.New("Cannot find cosmos in list of serviced chains by marlin connector") 653 } 654 655 privateKey := getPrivateKey() 656 657 vCache, err := lru.New2Q(500) 658 if err != nil { 659 return TendermintHandler{}, err 660 } 661 662 return TendermintHandler{ 663 servicedChainId: chainId, 664 listenPort: listenPort, 665 isConnectionOutgoing: isConnectionOutgoing, 666 peerAddr: peerAddr, 667 rpcAddr: rpcAddr, 668 privateKey: privateKey, 669 // codec: amino.NewCodec(), 670 validatorCache: vCache, 671 marlinTo: marlinTo, 672 marlinFrom: marlinFrom, 673 channelBuffer: make(map[byte][]marlinTypes.PacketMsg), 674 proposerCache: make(map[int64]Validator), 675 throughput: throughPutData{ 676 isDataConnect: isDataConnect, 677 toTMCore: make(map[string]uint32), 678 fromTMCore: make(map[string]uint32), 679 spam: make(map[string]uint32), 680 }, 681 signalConnError: make(chan struct{}, 1), 682 signalShutSend: make(chan struct{}, 1), 683 signalShutRecv: make(chan struct{}, 1), 684 signalShutThroughput: make(chan struct{}, 1), 685 }, nil 686 } 687 688 func getPrivateKey() ed25519.PrivKey { 689 if !isKeyFileUsed { 690 return ed25519.GenPrivKey() 691 } else { 692 if !memoized { 693 valid, err := VerifyKeyFile(keyFileLocation) 694 if err != nil { 695 log.Error("Error verifying keyfile integrity: ", keyFileLocation) 696 os.Exit(1) 697 } else if !valid { 698 os.Exit(1) 699 } 700 log.Info("Accessing disk to extract info from KeyFile: ", keyFileLocation) 701 jsonFile, err := os.Open(keyFileLocation) 702 // if we os.Open returns an error then handle it 703 if err != nil { 704 log.Error("Error accessing file KeyFile: ", keyFileLocation, " error: ", err, ". exiting application.") 705 os.Exit(1) 706 } 707 defer jsonFile.Close() 708 709 byteValue, err := ioutil.ReadAll(jsonFile) 710 if err != nil { 711 log.Error("Error decoding KeyFile: ", keyFileLocation, " error: ", err, ". exiting application.") 712 os.Exit(1) 713 } 714 var key keyData 715 json.Unmarshal(byteValue, &key) 716 log.Info("Connector assumes for all connections henceforth the ID: ", key.IdString) 717 privateKey = key.PrivateKey 718 memoized = true 719 } 720 return privateKey 721 } 722 } 723 724 func (t *throughPutData) putInfo(direction string, key string, count uint32) { 725 t.mu.Lock() 726 switch direction { 727 case "to": 728 t.toTMCore[key] = t.toTMCore[key] + count 729 case "from": 730 t.fromTMCore[key] = t.fromTMCore[key] + count 731 case "spam": 732 t.spam[key] = t.spam[key] + count 733 } 734 t.mu.Unlock() 735 } 736 737 func (t *throughPutData) presentThroughput(sec time.Duration, shutdownCh chan struct{}) { 738 for { 739 time.Sleep(sec * time.Second) 740 741 select { 742 case <-shutdownCh: 743 return 744 default: 745 } 746 t.mu.Lock() 747 if t.isDataConnect { 748 log.Info(fmt.Sprintf("[DataConnect stats] To TMCore %v\tFrom TMCore %v", t.toTMCore, t.fromTMCore)) 749 } else { 750 log.Info(fmt.Sprintf("[SpamFilter stats] Served %v", t.spam)) 751 } 752 t.toTMCore = make(map[string]uint32) 753 t.fromTMCore = make(map[string]uint32) 754 t.spam = make(map[string]uint32) 755 t.mu.Unlock() 756 } 757 } 758 759 //----------------------------------------------------------------------------- 760 // Messages 761 762 // Message is a message that can be sent and received on the Reactor 763 type Message interface { 764 // ValidateBasic() error // No restrictions 765 } 766 767 func decodeMsg(bz []byte) (msg Message, err error) { 768 pb := &tmcons.Message{} 769 if err = proto.Unmarshal(bz, pb); err != nil { 770 return msg, err 771 } 772 773 return MsgFromProto(pb) 774 } 775 776 // MsgFromProto takes a consensus proto message and returns the native go type 777 func MsgFromProto(msg *tmcons.Message) (Message, error) { 778 if msg == nil { 779 return nil, errors.New("consensus: nil message") 780 } 781 var pb Message 782 783 switch msg := msg.Sum.(type) { 784 case *tmcons.Message_NewRoundStep: 785 rs, err := tmmath.SafeConvertUint8(int64(msg.NewRoundStep.Step)) 786 // deny message based on possible overflow 787 if err != nil { 788 return nil, fmt.Errorf("denying message due to possible overflow: %w", err) 789 } 790 pb = &NewRoundStepMessage{ 791 Height: msg.NewRoundStep.Height, 792 Round: msg.NewRoundStep.Round, 793 Step: int8(rs), 794 SecondsSinceStartTime: msg.NewRoundStep.SecondsSinceStartTime, 795 LastCommitRound: msg.NewRoundStep.LastCommitRound, 796 } 797 case *tmcons.Message_NewValidBlock: 798 pbPartSetHeader, err := PartSetHeaderFromProto(&msg.NewValidBlock.BlockPartSetHeader) 799 if err != nil { 800 return nil, fmt.Errorf("parts to proto error: %w", err) 801 } 802 803 pbBits := new(bits.BitArray) 804 pbBits.FromProto(msg.NewValidBlock.BlockParts) 805 806 pb = &NewValidBlockMessage{ 807 Height: msg.NewValidBlock.Height, 808 Round: msg.NewValidBlock.Round, 809 BlockPartSetHeader: *pbPartSetHeader, 810 BlockParts: pbBits, 811 IsCommit: msg.NewValidBlock.IsCommit, 812 } 813 case *tmcons.Message_Proposal: 814 pbP, err := ProposalFromProto(&msg.Proposal.Proposal) 815 if err != nil { 816 return nil, fmt.Errorf("proposal msg to proto error: %w", err) 817 } 818 819 pb = &ProposalMessage{ 820 Proposal: pbP, 821 } 822 case *tmcons.Message_ProposalPol: 823 pbBits := new(bits.BitArray) 824 pbBits.FromProto(&msg.ProposalPol.ProposalPol) 825 pb = &ProposalPOLMessage{ 826 Height: msg.ProposalPol.Height, 827 ProposalPOLRound: msg.ProposalPol.ProposalPolRound, 828 ProposalPOL: pbBits, 829 } 830 case *tmcons.Message_BlockPart: 831 parts, err := PartFromProto(&msg.BlockPart.Part) 832 if err != nil { 833 return nil, fmt.Errorf("blockpart msg to proto error: %w", err) 834 } 835 pb = &BlockPartMessage{ 836 Height: msg.BlockPart.Height, 837 Round: msg.BlockPart.Round, 838 Part: parts, 839 } 840 case *tmcons.Message_Vote: 841 vote, err := VoteFromProto(msg.Vote.Vote) 842 if err != nil { 843 return nil, fmt.Errorf("vote msg to proto error: %w", err) 844 } 845 846 pb = &VoteMessage{ 847 Vote: vote, 848 } 849 case *tmcons.Message_HasVote: 850 pb = &HasVoteMessage{ 851 Height: msg.HasVote.Height, 852 Round: msg.HasVote.Round, 853 Type: msg.HasVote.Type, 854 Index: msg.HasVote.Index, 855 } 856 case *tmcons.Message_VoteSetMaj23: 857 bi, err := BlockIDFromProto(&msg.VoteSetMaj23.BlockID) 858 if err != nil { 859 return nil, fmt.Errorf("voteSetMaj23 msg to proto error: %w", err) 860 } 861 pb = &VoteSetMaj23Message{ 862 Height: msg.VoteSetMaj23.Height, 863 Round: msg.VoteSetMaj23.Round, 864 Type: msg.VoteSetMaj23.Type, 865 BlockID: *bi, 866 } 867 case *tmcons.Message_VoteSetBits: 868 bi, err := BlockIDFromProto(&msg.VoteSetBits.BlockID) 869 if err != nil { 870 return nil, fmt.Errorf("voteSetBits msg to proto error: %w", err) 871 } 872 bits := new(bits.BitArray) 873 bits.FromProto(&msg.VoteSetBits.Votes) 874 875 pb = &VoteSetBitsMessage{ 876 Height: msg.VoteSetBits.Height, 877 Round: msg.VoteSetBits.Round, 878 Type: msg.VoteSetBits.Type, 879 BlockID: *bi, 880 Votes: bits, 881 } 882 default: 883 return nil, fmt.Errorf("consensus: message not recognized: %T", msg) 884 } 885 886 // if err := pb.ValidateBasic(); err != nil { 887 // return nil, err 888 // } 889 890 return pb, nil 891 } 892 893 // FromProto sets a protobuf PartSetHeader to the given pointer 894 func PartSetHeaderFromProto(ppsh *tmproto.PartSetHeader) (*PartSetHeader, error) { 895 if ppsh == nil { 896 return nil, errors.New("nil PartSetHeader") 897 } 898 psh := new(PartSetHeader) 899 psh.Total = ppsh.Total 900 psh.Hash = ppsh.Hash 901 902 return psh, nil 903 } 904 905 // FromProto sets a protobuf BlockID to the given pointer. 906 // It returns an error if the block id is invalid. 907 func BlockIDFromProto(bID *tmproto.BlockID) (*BlockID, error) { 908 if bID == nil { 909 return nil, errors.New("nil BlockID") 910 } 911 912 blockID := new(BlockID) 913 ph, err := PartSetHeaderFromProto(&bID.PartSetHeader) 914 if err != nil { 915 return nil, err 916 } 917 918 blockID.PartSetHeader = *ph 919 blockID.Hash = bID.Hash 920 921 return blockID, nil 922 } 923 924 // FromProto sets a protobuf Proposal to the given pointer. 925 // It returns an error if the proposal is invalid. 926 func ProposalFromProto(pp *tmproto.Proposal) (*Proposal, error) { 927 if pp == nil { 928 return nil, errors.New("nil proposal") 929 } 930 931 p := new(Proposal) 932 933 blockID, err := BlockIDFromProto(&pp.BlockID) 934 if err != nil { 935 return nil, err 936 } 937 938 p.BlockID = *blockID 939 p.Type = pp.Type 940 p.Height = pp.Height 941 p.Round = pp.Round 942 p.POLRound = pp.PolRound 943 p.Timestamp = pp.Timestamp 944 p.Signature = pp.Signature 945 946 return p, nil 947 } 948 949 func PartFromProto(pb *tmproto.Part) (*Part, error) { 950 if pb == nil { 951 return nil, errors.New("nil part") 952 } 953 954 part := new(Part) 955 proof, err := merkle.ProofFromProto(&pb.Proof) 956 if err != nil { 957 return nil, err 958 } 959 part.Index = pb.Index 960 part.Bytes = pb.Bytes 961 part.Proof = *proof 962 963 return part, nil 964 } 965 966 // FromProto converts a proto generetad type to a handwritten type 967 // return type, nil if everything converts safely, otherwise nil, error 968 func VoteFromProto(pv *tmproto.Vote) (*Vote, error) { 969 if pv == nil { 970 return nil, errors.New("nil vote") 971 } 972 973 blockID, err := BlockIDFromProto(&pv.BlockID) 974 if err != nil { 975 return nil, err 976 } 977 978 vote := new(Vote) 979 vote.Type = pv.Type 980 vote.Height = pv.Height 981 vote.Round = pv.Round 982 vote.BlockID = *blockID 983 vote.Timestamp = pv.Timestamp 984 vote.ValidatorAddress = pv.ValidatorAddress 985 vote.ValidatorIndex = pv.ValidatorIndex 986 vote.Signature = pv.Signature 987 988 return vote, nil 989 } 990 991 //---------------------------------------- 992 // Packet 993 994 // mustWrapPacket takes a packet kind (oneof) and wraps it in a tmp2p.Packet message. 995 func mustWrapPacket(pb proto.Message) *tmp2p.Packet { 996 var msg tmp2p.Packet 997 998 switch pb := pb.(type) { 999 case *tmp2p.Packet: // already a packet 1000 msg = *pb 1001 case *tmp2p.PacketPing: 1002 msg = tmp2p.Packet{ 1003 Sum: &tmp2p.Packet_PacketPing{ 1004 PacketPing: pb, 1005 }, 1006 } 1007 case *tmp2p.PacketPong: 1008 msg = tmp2p.Packet{ 1009 Sum: &tmp2p.Packet_PacketPong{ 1010 PacketPong: pb, 1011 }, 1012 } 1013 case *tmp2p.PacketMsg: 1014 msg = tmp2p.Packet{ 1015 Sum: &tmp2p.Packet_PacketMsg{ 1016 PacketMsg: pb, 1017 }, 1018 } 1019 default: 1020 panic(fmt.Errorf("unknown packet type %T", pb)) 1021 } 1022 1023 return &msg 1024 } 1025 1026 // ---------------------- SPAM FILTER INTERFACE -------------------------------- 1027 1028 // RunSpamFilter serves as the entry point for a TM Core handler when serving as a spamfilter 1029 func RunSpamFilter(rpcAddr string, 1030 marlinTo chan marlinTypes.MarlinMessage, 1031 marlinFrom chan marlinTypes.MarlinMessage) { 1032 log.Info("Starting Cosmoshub-4 Tendermint SpamFilter") 1033 1034 handler, err := createTMHandler("0.0.0.0:0", rpcAddr, marlinTo, marlinFrom, false, 0, false) 1035 if err != nil { 1036 log.Error("Error encountered while creating TM Handler: ", err) 1037 os.Exit(1) 1038 } 1039 1040 marlin.AllowServicedChainMessages(handler.servicedChainId) 1041 1042 coreCount := runtime.NumCPU() 1043 multiple := 2 1044 log.Info("Runtime found number of CPUs on machine to be ", coreCount, ". Hence, running ", multiple*coreCount, " spamfilter handlers.") 1045 1046 for i := 0; i < multiple*coreCount; i++ { 1047 go handler.beginServicingSpamFilter(i) 1048 } 1049 1050 handler.throughput.presentThroughput(5, handler.signalShutThroughput) 1051 } 1052 1053 func (h *TendermintHandler) beginServicingSpamFilter(id int) { 1054 log.Info("Running TM side spam filter handler ", id) 1055 // Blocking all except CsVoVOT 1056 for marlinMsg := range h.marlinFrom { 1057 switch marlinMsg.Channel { 1058 case channelBc: 1059 h.throughput.putInfo("spam", "-CsBc", 1) 1060 log.Debug("TMCore <-> Marlin Blockhain is not serviced") 1061 h.marlinTo <- h.spamVerdictMessage(marlinMsg, false) 1062 case channelCsSt: 1063 msgBytes := h.getBytesFromChannelBuffer(marlinMsg.Packets) 1064 msg, err := decodeMsg(msgBytes) 1065 if err != nil { 1066 h.throughput.putInfo("spam", "-CsStUNK", uint32(len(marlinMsg.Packets))) 1067 h.marlinTo <- h.spamVerdictMessage(marlinMsg, false) 1068 } else { 1069 switch msg.(type) { 1070 case *NewRoundStepMessage: 1071 h.throughput.putInfo("spam", "+CsStNRS", uint32(len(marlinMsg.Packets))) 1072 h.marlinTo <- h.spamVerdictMessage(marlinMsg, false) 1073 default: 1074 h.throughput.putInfo("spam", "-CsStUNK", uint32(len(marlinMsg.Packets))) 1075 h.marlinTo <- h.spamVerdictMessage(marlinMsg, false) 1076 } 1077 } 1078 case channelCsVo: 1079 // log.Info("consensus vote") 1080 msgBytes := h.getBytesFromChannelBuffer(marlinMsg.Packets) 1081 msg, err := decodeMsg(msgBytes) 1082 if err != nil { 1083 h.throughput.putInfo("spam", "-CsVoUNK", uint32(len(marlinMsg.Packets))) 1084 h.marlinTo <- h.spamVerdictMessage(marlinMsg, false) 1085 } else { 1086 switch msg.(type) { 1087 case *VoteMessage: 1088 if h.thoroughMessageCheck(msg) { 1089 h.marlinTo <- h.spamVerdictMessage(marlinMsg, true) 1090 h.throughput.putInfo("spam", "+CsVoVOT", uint32(len(marlinMsg.Packets))) 1091 } else { 1092 h.marlinTo <- h.spamVerdictMessage(marlinMsg, false) 1093 h.throughput.putInfo("spam", "-CsVoVOT", uint32(len(marlinMsg.Packets))) 1094 } 1095 default: 1096 h.throughput.putInfo("spam", "-CsVoUNK", uint32(len(marlinMsg.Packets))) 1097 h.marlinTo <- h.spamVerdictMessage(marlinMsg, false) 1098 } 1099 } 1100 case channelCsDc: 1101 msgBytes := h.getBytesFromChannelBuffer(marlinMsg.Packets) 1102 msg, err := decodeMsg(msgBytes) 1103 if err != nil { 1104 h.throughput.putInfo("spam", "-CsDcUNK(bare)", uint32(len(marlinMsg.Packets))) 1105 h.marlinTo <- h.spamVerdictMessage(marlinMsg, false) 1106 } else { 1107 switch msg.(type) { 1108 case *ProposalMessage: 1109 h.marlinTo <- h.spamVerdictMessage(marlinMsg, false) 1110 h.throughput.putInfo("spam", "-CsDcPRO(bare)", uint32(len(marlinMsg.Packets))) 1111 case *BlockPartMessage: 1112 h.marlinTo <- h.spamVerdictMessage(marlinMsg, false) 1113 h.throughput.putInfo("spam", "-CsDcBPM(bare)", uint32(len(marlinMsg.Packets))) 1114 default: 1115 h.throughput.putInfo("spam", "-CsVoUNK", uint32(len(marlinMsg.Packets))) 1116 h.marlinTo <- h.spamVerdictMessage(marlinMsg, false) 1117 } 1118 } 1119 case byte(0x90): 1120 // Special case: here the Proposal message comes bundled with a block part message 1121 // log.Info("0x90 channel -- ", len(marlinMsg.Packets), len(marlinMsg.Packets2)) 1122 msgBytes := h.getBytesFromChannelBuffer(marlinMsg.Packets) 1123 msg, err := decodeMsg(msgBytes) 1124 if err != nil { 1125 h.throughput.putInfo("spam", "-CsDcUNK", uint32(len(marlinMsg.Packets))) 1126 h.marlinTo <- h.spamVerdictMessage(marlinMsg, false) 1127 } 1128 // log.Info("here ", msg) 1129 msgBytes2 := h.getBytesFromChannelBuffer(marlinMsg.Packets2) 1130 msg2, err := decodeMsg(msgBytes2) 1131 if err != nil { 1132 h.throughput.putInfo("spam", "-CsDcUNK", uint32(len(marlinMsg.Packets2))) 1133 h.marlinTo <- h.spamVerdictMessage(marlinMsg, false) 1134 } 1135 // log.Info("here2 ", msg2) 1136 switch msg.(type) { 1137 case *ProposalMessage: 1138 switch msg2.(type) { 1139 case *BlockPartMessage: 1140 pro := msg.(*ProposalMessage) 1141 bpm := msg2.(*BlockPartMessage) 1142 1143 if pro.Proposal.Height != bpm.Height || pro.Proposal.Round != bpm.Round { 1144 log.Info("PRO BPM height round mismatch") 1145 h.throughput.putInfo("spam", "-CsDcBPM", 1) 1146 h.marlinTo <- h.spamVerdictMessage(marlinMsg, false) 1147 break 1148 } 1149 1150 val, ok := h.getProposerAtHeight(pro.Proposal.Height) 1151 if !ok { 1152 log.Info("error getting proposer at height") 1153 h.throughput.putInfo("spam", "-CsDcBPM", 1) 1154 h.marlinTo <- h.spamVerdictMessage(marlinMsg, false) 1155 break 1156 } 1157 1158 p := pro.Proposal.ToProto() 1159 ok = val.PublicKey.VerifySignature(ProposalSignBytes("cosmoshub-4", p), pro.Proposal.Signature) 1160 if !ok { 1161 log.Info("PRO signature mismatch ", pro.Proposal.Height, pro.Proposal.Round, val.Address) 1162 h.throughput.putInfo("spam", "-CsDcBPM", 1) 1163 h.marlinTo <- h.spamVerdictMessage(marlinMsg, false) 1164 break 1165 } 1166 1167 err := bpm.Part.Proof.Verify(pro.Proposal.BlockID.PartSetHeader.Hash, bpm.Part.Bytes) 1168 if err != nil { 1169 log.Info("PRO BPM merkle verification error") 1170 h.throughput.putInfo("spam", "-CsDcBPM", 1) 1171 h.marlinTo <- h.spamVerdictMessage(marlinMsg, false) 1172 break 1173 } 1174 1175 h.throughput.putInfo("spam", "+CsDcBPM", 1) 1176 h.marlinTo <- h.spamVerdictMessage(marlinMsg, true) 1177 1178 default: 1179 h.throughput.putInfo("spam", "-CsDcBPM", 1) 1180 h.marlinTo <- h.spamVerdictMessage(marlinMsg, false) 1181 } 1182 default: 1183 h.throughput.putInfo("spam", "-CsDcBPM", 1) 1184 h.marlinTo <- h.spamVerdictMessage(marlinMsg, false) 1185 } 1186 1187 case channelCsVs: 1188 h.throughput.putInfo("spam", "-CsVs", 1) 1189 h.marlinTo <- h.spamVerdictMessage(marlinMsg, false) 1190 log.Debug("TMCore <-> Marlin Consensensus Vote Set Bits Channel is not serviced") 1191 case channelMm: 1192 h.throughput.putInfo("spam", "-CsMm", 1) 1193 h.marlinTo <- h.spamVerdictMessage(marlinMsg, false) 1194 log.Debug("TMCore <-> Marlin Mempool Channel is not serviced") 1195 case channelEv: 1196 h.throughput.putInfo("spam", "-CsEv", 1) 1197 h.marlinTo <- h.spamVerdictMessage(marlinMsg, false) 1198 log.Debug("TMCore <-> MarlinEvidence Channel is not serviced") 1199 default: 1200 h.throughput.putInfo("spam", "-UnkUNK", 1) 1201 h.marlinTo <- h.spamVerdictMessage(marlinMsg, false) 1202 } 1203 } 1204 } 1205 1206 func (h *TendermintHandler) thoroughMessageCheck(msg Message) bool { 1207 switch msg.(type) { 1208 case *VoteMessage: 1209 if validator, ok := h.getValidators(msg.(*VoteMessage).Vote.Height); ok { 1210 vidx := msg.(*VoteMessage).Vote.ValidatorIndex 1211 vaddr := msg.(*VoteMessage).Vote.ValidatorAddress.String() 1212 if vidx >= int32(len(validator)) || vaddr != validator[vidx].Address || 1213 !validator[vidx].PublicKey.VerifySignature(VoteSignBytes("cosmoshub-4", msg.(*VoteMessage).Vote.ToProto()), msg.(*VoteMessage).Vote.Signature) { 1214 return false 1215 } 1216 return true 1217 } 1218 return false 1219 case *BlockPartMessage: 1220 log.Info("Block part message: ", msg) 1221 // Cache hash verification, needs Proposal message support 1222 return false 1223 case *ProposalMessage: 1224 // log.Info("Block proposal message: ", msg) 1225 _, ok := h.getProposerAtHeight(msg.(ProposalMessage).Proposal.Height) 1226 if !ok { 1227 return false 1228 } 1229 1230 // if _, ok := h.getValidators(msg.(*ProposalMessage).Proposal.Height); ok { 1231 // // Check signature, add to map so that BPM messages can be verified 1232 // return true 1233 // } 1234 return false 1235 default: 1236 return false 1237 } 1238 } 1239 1240 func (h *TendermintHandler) getProposerAtHeight(height int64) (Validator, bool) { 1241 if val, ok := h.proposerCache[height]; ok { 1242 // Let clear some stuff from cache 1243 delete(h.proposerCache, height-10) 1244 1245 return val, true 1246 } 1247 // Find stuff from RPC 1248 1249 if validatorlist, ok := h.getValidators(height); ok { 1250 type ConsensusResponse struct { 1251 Result struct { 1252 RoundState struct { 1253 HeightRoundStep string `json:"height/round/step"` 1254 Proposer struct { 1255 Address string `json:"address"` 1256 Index int `json:"index"` 1257 } `json:"proposer"` 1258 } `json:"round_state"` 1259 } `json:"result"` 1260 } 1261 1262 response, err := http.Get("http://" + h.rpcAddr + "/consensus_state") 1263 if err != nil { 1264 log.Error("Error while sending request to get blockchain status") 1265 return Validator{}, false 1266 } 1267 1268 bodyBytes, err := ioutil.ReadAll(response.Body) 1269 var jsonResult ConsensusResponse 1270 err = json.Unmarshal(bodyBytes, &jsonResult) 1271 if err != nil { 1272 log.Error("Undecodable response from rpc for next validator search") 1273 return Validator{}, false 1274 } 1275 response.Body.Close() 1276 1277 heightString := strings.Split(jsonResult.Result.RoundState.HeightRoundStep, "/")[0] 1278 heightFromResponse, err := strconv.ParseInt(heightString, 10, 64) 1279 if heightFromResponse != height { 1280 log.Error("Cannot find height match. Not newest proposal") 1281 return Validator{}, false 1282 } 1283 1284 candidateValidator := validatorlist[jsonResult.Result.RoundState.Proposer.Index] 1285 if strings.ToUpper(candidateValidator.Address) == jsonResult.Result.RoundState.Proposer.Address { 1286 return candidateValidator, true 1287 } 1288 1289 for _, val := range validatorlist { 1290 if strings.ToUpper(val.Address) == jsonResult.Result.RoundState.Proposer.Address { 1291 return val, true 1292 } 1293 } 1294 1295 } 1296 return Validator{}, false 1297 } 1298 1299 func (h *TendermintHandler) getValidators(height int64) ([]Validator, bool) { 1300 if height+10 < h.maxValidHeight { 1301 // Don't service messages too old 1302 return []Validator{}, false 1303 } else if h.validatorCache.Contains(height) { 1304 value, ok := h.validatorCache.Get(height) 1305 return value.([]Validator), ok 1306 } else { 1307 var validatorSet []Validator 1308 // log.Info("Asked about height: ", height) 1309 for i := 1; i < 3; i++ { 1310 // log.Info(len(validatorSet), height) 1311 response, err := http.Get("http://" + h.rpcAddr + "/validators?height=" + strconv.Itoa((int)(height)) + "&per_page=100&page=" + strconv.Itoa((int)(i))) 1312 if err != nil { 1313 log.Error("Error while sending request to get validators at height: ", height, " err: ", err) 1314 return []Validator{}, false 1315 } else { 1316 bodyBytes, err := ioutil.ReadAll(response.Body) 1317 if err != nil { 1318 log.Error("Error while parsing request to get validators at height: ", height, " err: ", err) 1319 return []Validator{}, false 1320 } 1321 var jsonResult map[string]interface{} 1322 json.Unmarshal(bodyBytes, &jsonResult) 1323 // verify interface for errors 1324 if _, errorFieldFound := jsonResult["error"]; errorFieldFound { 1325 return []Validator{}, false 1326 } 1327 validatorInfo := jsonResult["result"].(map[string]interface{})["validators"].([]interface{}) 1328 1329 for _, v := range validatorInfo { 1330 if v.(map[string]interface{})["pub_key"].(map[string]interface{})["type"] != "tendermint/PubKeyEd25519" { 1331 log.Error("Not all keys of validators are tendermint/PubKeyEd25519. Cannot continue with this validator set from TMCore") 1332 return []Validator{}, false 1333 } 1334 decodedSlice, err := b64.StdEncoding.DecodeString(v.(map[string]interface{})["pub_key"].(map[string]interface{})["value"].(string)) 1335 if err != nil { 1336 log.Error("Could not decode base64 pubkey") 1337 return []Validator{}, false 1338 } 1339 decodedArray := make(ed25519.PubKey, 32) 1340 // log.Info(decodedSlice[:32]) 1341 for i := 0; i < 32; i++ { 1342 decodedArray[i] = decodedSlice[i] 1343 } 1344 validatorSet = append(validatorSet, 1345 Validator{ 1346 PublicKey: decodedArray, 1347 Address: v.(map[string]interface{})["address"].(string), 1348 }) 1349 } 1350 } 1351 response.Body.Close() 1352 } 1353 // log.Info("ht ", height, " validator set ", len(validatorSet)) 1354 h.validatorCache.Add(height, validatorSet) 1355 1356 h.maxValidHeight = height 1357 return validatorSet, true 1358 } 1359 } 1360 1361 func (h *TendermintHandler) spamVerdictMessage(msg marlinTypes.MarlinMessage, allow bool) marlinTypes.MarlinMessage { 1362 if allow { 1363 return marlinTypes.MarlinMessage{ 1364 ChainID: h.servicedChainId, 1365 Channel: byte(0x01), 1366 PacketId: msg.PacketId, 1367 } 1368 } else { 1369 return marlinTypes.MarlinMessage{ 1370 ChainID: h.servicedChainId, 1371 Channel: byte(0x00), 1372 PacketId: msg.PacketId, 1373 } 1374 } 1375 }