github.com/aergoio/aergo@v1.3.1/p2p/subproto/blockhash.go (about) 1 /* 2 * @file 3 * @copyright defined in aergo/LICENSE.txt 4 */ 5 6 package subproto 7 8 import ( 9 "bytes" 10 "github.com/aergoio/aergo-lib/log" 11 "github.com/aergoio/aergo/p2p/p2pcommon" 12 "github.com/aergoio/aergo/p2p/p2putil" 13 "github.com/aergoio/aergo/types" 14 ) 15 16 type getHashRequestHandler struct { 17 BaseMsgHandler 18 } 19 20 type getHashResponseHandler struct { 21 BaseMsgHandler 22 } 23 24 // newBlockReqHandler creates handler for GetBlockRequest 25 func NewGetHashesReqHandler(pm p2pcommon.PeerManager, peer p2pcommon.RemotePeer, logger *log.Logger, actor p2pcommon.ActorService) *getHashRequestHandler { 26 bh := &getHashRequestHandler{BaseMsgHandler: BaseMsgHandler{protocol: p2pcommon.GetHashesRequest, pm: pm, peer: peer, actor: actor, logger: logger}} 27 28 return bh 29 } 30 31 func (bh *getHashRequestHandler) ParsePayload(rawbytes []byte) (p2pcommon.MessageBody, error) { 32 return p2putil.UnmarshalAndReturn(rawbytes, &types.GetHashesRequest{}) 33 } 34 35 func (bh *getHashRequestHandler) Handle(msg p2pcommon.Message, msgBody p2pcommon.MessageBody) { 36 remotePeer := bh.peer 37 data := msgBody.(*types.GetHashesRequest) 38 p2putil.DebugLogReceive(bh.logger, bh.protocol, msg.ID().String(), remotePeer, data) 39 chainAccessor := bh.actor.GetChainAccessor() 40 41 // check if requested too many hashes 42 if data.Size > p2pcommon.MaxBlockResponseCount { 43 resp := &types.GetHashesResponse{Status: types.ResultStatus_INVALID_ARGUMENT} 44 remotePeer.SendMessage(remotePeer.MF().NewMsgResponseOrder(msg.ID(), p2pcommon.GetHashesResponse, resp)) 45 return 46 } 47 // check if remote peer has valid chain, 48 // TODO also check if found prevBlock is on main chain or side chain, assume in main chain for now. 49 prevHash, err := chainAccessor.GetHashByNo(data.PrevNumber) 50 if err != nil || !bytes.Equal(prevHash, data.PrevHash) { 51 resp := &types.GetHashesResponse{Status: types.ResultStatus_INVALID_ARGUMENT} 52 remotePeer.SendMessage(remotePeer.MF().NewMsgResponseOrder(msg.ID(), p2pcommon.GetHashesResponse, resp)) 53 return 54 } 55 // decide total fetched size 56 bestBlock, err := bh.actor.GetChainAccessor().GetBestBlock() 57 if err != nil { 58 resp := &types.GetHashesResponse{Status: types.ResultStatus_INTERNAL} 59 remotePeer.SendMessage(remotePeer.MF().NewMsgResponseOrder(msg.ID(), p2pcommon.GetHashesResponse, resp)) 60 return 61 } 62 startNumber, endNumber, fetchSize := determineFetchSize(data.PrevNumber, bestBlock.Header.BlockNo, int(data.Size)) 63 if fetchSize <= 0 { 64 resp := &types.GetHashesResponse{Status: types.ResultStatus_INTERNAL} 65 remotePeer.SendMessage(remotePeer.MF().NewMsgResponseOrder(msg.ID(), p2pcommon.GetHashesResponse, resp)) 66 return 67 } 68 hashes := make([][]byte, fetchSize) 69 cursorNo := endNumber 70 for i := fetchSize - 1; i >= 0; i-- { 71 hash, err := bh.actor.GetChainAccessor().GetHashByNo(cursorNo) 72 if err != nil { 73 resp := &types.GetHashesResponse{Status: types.ResultStatus_INTERNAL} 74 remotePeer.SendMessage(remotePeer.MF().NewMsgResponseOrder(msg.ID(), p2pcommon.GetHashesResponse, resp)) 75 return 76 } 77 hashes[i] = hash 78 cursorNo-- 79 } 80 // check again if data is changed during fetch 81 // check if reorg (or such like it) occurred and mainchain is changed during 82 endHash, err := chainAccessor.GetHashByNo(endNumber) 83 if err != nil || !bytes.Equal(endHash, hashes[fetchSize-1]) { 84 resp := &types.GetHashesResponse{Status: types.ResultStatus_INTERNAL} 85 remotePeer.SendMessage(remotePeer.MF().NewMsgResponseOrder(msg.ID(), p2pcommon.GetHashesResponse, resp)) 86 return 87 } 88 startBlock, err := chainAccessor.GetBlock(hashes[0]) 89 if err != nil || !bytes.Equal(startBlock.Header.PrevBlockHash, prevHash) || startBlock.Header.BlockNo != startNumber { 90 resp := &types.GetHashesResponse{Status: types.ResultStatus_INTERNAL} 91 remotePeer.SendMessage(remotePeer.MF().NewMsgResponseOrder(msg.ID(), p2pcommon.GetHashesResponse, resp)) 92 return 93 } 94 95 // generate response message 96 resp := &types.GetHashesResponse{ 97 Hashes: hashes, 98 Status: types.ResultStatus_OK, 99 HasNext: false, 100 } 101 remotePeer.SendMessage(remotePeer.MF().NewMsgResponseOrder(msg.ID(), p2pcommon.GetHashesResponse, resp)) 102 } 103 104 func determineFetchSize(prevNum, currentLast types.BlockNo, maxSize int) (types.BlockNo, types.BlockNo, int) { 105 if currentLast <= prevNum { 106 return 0, 0, -1 107 } 108 fetchSize := int(currentLast - prevNum) 109 if fetchSize > maxSize { 110 fetchSize = maxSize 111 } 112 113 return prevNum + 1, prevNum + types.BlockNo(fetchSize), fetchSize 114 } 115 116 // newBlockReqHandler creates handler for GetBlockRequest 117 func NewGetHashesRespHandler(pm p2pcommon.PeerManager, peer p2pcommon.RemotePeer, logger *log.Logger, actor p2pcommon.ActorService) *getHashResponseHandler { 118 bh := &getHashResponseHandler{BaseMsgHandler: BaseMsgHandler{protocol: p2pcommon.GetHashesResponse, pm: pm, peer: peer, actor: actor, logger: logger}} 119 120 return bh 121 } 122 123 func (bh *getHashResponseHandler) ParsePayload(rawbytes []byte) (p2pcommon.MessageBody, error) { 124 return p2putil.UnmarshalAndReturn(rawbytes, &types.GetHashesResponse{}) 125 } 126 127 func (bh *getHashResponseHandler) Handle(msg p2pcommon.Message, msgBody p2pcommon.MessageBody) { 128 remotePeer := bh.peer 129 data := msgBody.(*types.GetHashesResponse) 130 p2putil.DebugLogReceiveResponse(bh.logger, bh.protocol, msg.ID().String(), msg.OriginalID().String(), bh.peer, data) 131 132 // locate request data and remove it if found 133 remotePeer.GetReceiver(msg.OriginalID())(msg, data) 134 } 135 136 type getHashByNoRequestHandler struct { 137 BaseMsgHandler 138 } 139 140 type getHashByNoResponseHandler struct { 141 BaseMsgHandler 142 } 143 144 // newBlockReqHandler creates handler for GetBlockRequest 145 func NewGetHashByNoReqHandler(pm p2pcommon.PeerManager, peer p2pcommon.RemotePeer, logger *log.Logger, actor p2pcommon.ActorService) *getHashByNoRequestHandler { 146 bh := &getHashByNoRequestHandler{BaseMsgHandler: BaseMsgHandler{protocol: p2pcommon.GetHashByNoRequest, pm: pm, peer: peer, actor: actor, logger: logger}} 147 148 return bh 149 } 150 151 func (bh *getHashByNoRequestHandler) ParsePayload(rawbytes []byte) (p2pcommon.MessageBody, error) { 152 return p2putil.UnmarshalAndReturn(rawbytes, &types.GetHashByNo{}) 153 } 154 155 func (bh *getHashByNoRequestHandler) Handle(msg p2pcommon.Message, msgBody p2pcommon.MessageBody) { 156 remotePeer := bh.peer 157 data := msgBody.(*types.GetHashByNo) 158 p2putil.DebugLogReceive(bh.logger, bh.protocol, msg.ID().String(), remotePeer, data) 159 chainAccessor := bh.actor.GetChainAccessor() 160 161 // check if remote peer has valid chain, 162 // TODO also check if found prevBlock is on main chain or side chain, assume in main chain for now. 163 targetHash, err := chainAccessor.GetHashByNo(data.BlockNo) 164 if err != nil { 165 resp := &types.GetHashByNoResponse{Status: types.ResultStatus_NOT_FOUND} 166 remotePeer.SendMessage(remotePeer.MF().NewMsgResponseOrder(msg.ID(), p2pcommon.GetHashByNoResponse, resp)) 167 return 168 } 169 170 // generate response message 171 resp := &types.GetHashByNoResponse{ 172 Status: types.ResultStatus_OK, 173 BlockHash: targetHash, 174 } 175 remotePeer.SendMessage(remotePeer.MF().NewMsgResponseOrder(msg.ID(), p2pcommon.GetHashByNoResponse, resp)) 176 } 177 178 // newBlockReqHandler creates handler for GetBlockRequest 179 func NewGetHashByNoRespHandler(pm p2pcommon.PeerManager, peer p2pcommon.RemotePeer, logger *log.Logger, actor p2pcommon.ActorService) *getHashByNoResponseHandler { 180 bh := &getHashByNoResponseHandler{BaseMsgHandler: BaseMsgHandler{protocol: p2pcommon.GetHashByNoResponse, pm: pm, peer: peer, actor: actor, logger: logger}} 181 182 return bh 183 } 184 185 func (bh *getHashByNoResponseHandler) ParsePayload(rawbytes []byte) (p2pcommon.MessageBody, error) { 186 return p2putil.UnmarshalAndReturn(rawbytes, &types.GetHashByNoResponse{}) 187 } 188 189 func (bh *getHashByNoResponseHandler) Handle(msg p2pcommon.Message, msgBody p2pcommon.MessageBody) { 190 data := msgBody.(*types.GetHashByNoResponse) 191 p2putil.DebugLogReceiveResponse(bh.logger, bh.protocol, msg.ID().String(), msg.OriginalID().String(), bh.peer, data) 192 193 // locate request data and remove it if found 194 bh.peer.GetReceiver(msg.OriginalID())(msg, data) 195 }