github.com/turingchain2020/turingchain@v1.1.21/blockchain/push.go (about) 1 package blockchain 2 3 import ( 4 "bytes" 5 "compress/gzip" 6 "io/ioutil" 7 "net" 8 "net/http" 9 "sync" 10 "sync/atomic" 11 "time" 12 13 "github.com/turingchain2020/turingchain/common" 14 dbm "github.com/turingchain2020/turingchain/common/db" 15 "github.com/turingchain2020/turingchain/types" 16 ) 17 18 const ( 19 notRunning = int32(1) 20 running = int32(2) 21 pushBlockMaxSeq = 10 22 pushTxReceiptMaxSeq = 100 23 pushMaxSize = 1 * 1024 * 1024 24 maxPushSubscriber = int(100) 25 subscribeStatusActive = int32(1) 26 subscribeStatusNotActive = int32(2) 27 postFail2Sleep = int32(60) //一次发送失败,sleep的次数 28 chanBufCap = int(10) 29 ) 30 31 // Push types ID 32 const ( 33 PushBlock = int32(0) 34 PushBlockHeader = int32(1) 35 PushTxReceipt = int32(2) 36 PushTxResult = int32(3) 37 ) 38 39 // CommonStore 通用的store 接口 40 // 修改大一点,可能可以用 db.KVDB 41 // 先改动小一点, 用store, 如果接口一样可以直接换 42 type CommonStore interface { 43 SetSync(key, value []byte) error 44 Set(key, value []byte) error 45 GetKey(key []byte) ([]byte, error) 46 PrefixCount(prefix []byte) int64 47 List(prefix []byte) ([][]byte, error) 48 } 49 50 //SequenceStore ... 51 type SequenceStore interface { 52 LoadBlockLastSequence() (int64, error) 53 // seqUpdateChan -> block sequence 54 GetBlockSequence(seq int64) (*types.BlockSequence, error) 55 // hash -> block header 56 GetBlockHeaderByHash(hash []byte) (*types.Header, error) 57 // seqUpdateChan -> block, size 58 LoadBlockBySequence(seq int64) (*types.BlockDetail, int, error) 59 // get last header 60 LastHeader() *types.Header 61 // hash -> seqUpdateChan 62 GetSequenceByHash(hash []byte) (int64, error) 63 } 64 65 //PostService ... 66 type PostService interface { 67 PostData(subscribe *types.PushSubscribeReq, postdata []byte, seq int64) (err error) 68 } 69 70 //当前的实现是为每个订阅者单独启动一个协程goroute,然后单独为每个subscriber分别过滤指定类型的交易, 71 //进行归类,这种方式集成了区块推送方式的处理机制,但是对于订阅者数量大的情况,势必会浪费cpu的开销, 72 //数据库的读取开销不会额外增加明星,因为会有cach 73 //TODO:后续需要考虑将区块推送和交易执行回执推送进行重构,提高并行推送效率 74 //pushNotify push Notify 75 type pushNotify struct { 76 subscribe *types.PushSubscribeReq 77 seqUpdateChan chan int64 78 closechan chan struct{} 79 status int32 80 postFail2Sleep int32 81 } 82 83 //Push ... 84 type Push struct { 85 store CommonStore 86 sequenceStore SequenceStore 87 tasks map[string]*pushNotify 88 mu sync.Mutex 89 postService PostService 90 cfg *types.TuringchainConfig 91 postFail2Sleep int32 92 postwg *sync.WaitGroup 93 } 94 95 //PushClient ... 96 type PushClient struct { 97 client *http.Client 98 } 99 100 // PushType ... 101 type PushType int32 102 103 func (pushType PushType) string() string { 104 return []string{"PushBlock", "PushBlockHeader", "PushTxReceipt", "PushTxResult", "NotSupported"}[pushType] 105 } 106 107 //PostData ... 108 func (pushClient *PushClient) PostData(subscribe *types.PushSubscribeReq, postdata []byte, seq int64) (err error) { 109 //post data in body 110 chainlog.Info("postData begin", "seq", seq, "subscribe name", subscribe.Name) 111 var buf bytes.Buffer 112 g := gzip.NewWriter(&buf) 113 if _, err = g.Write(postdata); err != nil { 114 return err 115 } 116 if err = g.Close(); err != nil { 117 return err 118 } 119 120 req, err := http.NewRequest("POST", subscribe.URL, &buf) 121 if err != nil { 122 return err 123 } 124 125 req.Header.Set("Content-Type", "text/plain") 126 req.Header.Set("Content-Encoding", "gzip") 127 resp, err := pushClient.client.Do(req) 128 if err != nil { 129 chainlog.Info("postData", "Do err", err) 130 return err 131 } 132 body, err := ioutil.ReadAll(resp.Body) 133 if err != nil { 134 _ = resp.Body.Close() 135 return err 136 } 137 if string(body) != "ok" && string(body) != "OK" { 138 chainlog.Error("postData fail", "name:", subscribe.Name, "URL", subscribe.URL, 139 "Contract:", subscribe.Contract, "body", string(body)) 140 _ = resp.Body.Close() 141 return types.ErrPushSeqPostData 142 } 143 chainlog.Debug("postData success", "name", subscribe.Name, "URL", subscribe.URL, 144 "Contract:", subscribe.Contract, "updateSeq", seq) 145 return resp.Body.Close() 146 } 147 148 //ProcAddBlockSeqCB 添加seq callback 149 func (chain *BlockChain) procSubscribePush(subscribe *types.PushSubscribeReq) error { 150 if !chain.enablePushSubscribe { 151 chainlog.Error("Push is not enabled for subscribed") 152 return types.ErrPushNotSupport 153 } 154 155 if !chain.isRecordBlockSequence { 156 chainlog.Error("procSubscribePush not support sequence") 157 return types.ErrRecordBlockSequence 158 } 159 160 if subscribe == nil { 161 chainlog.Error("procSubscribePush para is null") 162 return types.ErrInvalidParam 163 } 164 165 if chain.client.GetConfig().IsEnable("reduceLocaldb") && subscribe.Type == PushTxReceipt { 166 chainlog.Error("Tx receipts are reduced on this node") 167 return types.ErrTxReceiptReduced 168 } 169 return chain.push.addSubscriber(subscribe) 170 } 171 172 //ProcListPush 列出所有已经设置的推送订阅 173 func (chain *BlockChain) ProcListPush() (*types.PushSubscribes, error) { 174 if !chain.isRecordBlockSequence { 175 return nil, types.ErrRecordBlockSequence 176 } 177 if !chain.enablePushSubscribe { 178 return nil, types.ErrPushNotSupport 179 } 180 181 values, err := chain.push.store.List(pushPrefix) 182 if err != nil { 183 return nil, err 184 } 185 var listSeqCBs types.PushSubscribes 186 for _, value := range values { 187 var onePush types.PushWithStatus 188 err := types.Decode(value, &onePush) 189 if err != nil { 190 return nil, err 191 } 192 listSeqCBs.Pushes = append(listSeqCBs.Pushes, onePush.Push) 193 } 194 return &listSeqCBs, nil 195 } 196 197 // ProcGetLastPushSeq Seq的合法值从0开始的,所以没有获取到或者获取失败都应该返回-1 198 func (chain *BlockChain) ProcGetLastPushSeq(name string) (int64, error) { 199 if !chain.isRecordBlockSequence { 200 return -1, types.ErrRecordBlockSequence 201 } 202 if !chain.enablePushSubscribe { 203 return -1, types.ErrPushNotSupport 204 } 205 206 lastSeqbytes, err := chain.push.store.GetKey(calcLastPushSeqNumKey(name)) 207 if lastSeqbytes == nil || err != nil { 208 if err != dbm.ErrNotFoundInDb { 209 storeLog.Error("getSeqCBLastNum", "error", err) 210 } 211 return -1, types.ErrPushNotSubscribed 212 } 213 n, err := decodeHeight(lastSeqbytes) 214 if err != nil { 215 return -1, err 216 } 217 storeLog.Error("getSeqCBLastNum", "name", name, "num", n) 218 219 return n, nil 220 } 221 222 func newpush(commonStore CommonStore, seqStore SequenceStore, cfg *types.TuringchainConfig) *Push { 223 tasks := make(map[string]*pushNotify) 224 225 pushClient := &PushClient{ 226 client: &http.Client{Transport: &http.Transport{ 227 Dial: (&net.Dialer{ 228 Timeout: 30 * time.Second, 229 KeepAlive: 30 * time.Second, 230 }).Dial, 231 TLSHandshakeTimeout: 10 * time.Second, 232 ResponseHeaderTimeout: 10 * time.Second, 233 ExpectContinueTimeout: 1 * time.Second, 234 }}, 235 } 236 service := &Push{store: commonStore, 237 sequenceStore: seqStore, 238 tasks: tasks, 239 postService: pushClient, 240 cfg: cfg, 241 postFail2Sleep: postFail2Sleep, 242 postwg: &sync.WaitGroup{}, 243 } 244 service.init() 245 246 return service 247 } 248 249 //初始化: 从数据库读出seq的数目 250 func (push *Push) init() { 251 var subscribes []*types.PushSubscribeReq 252 values, err := push.store.List(pushPrefix) 253 if err != nil && err != dbm.ErrNotFoundInDb { 254 chainlog.Error("Push init", "err", err) 255 return 256 } 257 if 0 == len(values) { 258 return 259 } 260 for _, value := range values { 261 var pushWithStatus types.PushWithStatus 262 err := types.Decode(value, &pushWithStatus) 263 if err != nil { 264 chainlog.Error("Push init", "Failed to decode subscribe due to err:", err) 265 return 266 } 267 if pushWithStatus.Status == subscribeStatusActive { 268 subscribes = append(subscribes, pushWithStatus.Push) 269 } 270 271 } 272 for _, subscribe := range subscribes { 273 push.addTask(subscribe) 274 } 275 } 276 277 // Close ... 278 func (push *Push) Close() { 279 push.mu.Lock() 280 for _, task := range push.tasks { 281 close(task.closechan) 282 } 283 push.mu.Unlock() 284 push.postwg.Wait() 285 } 286 287 func (push *Push) addSubscriber(subscribe *types.PushSubscribeReq) error { 288 if subscribe == nil { 289 chainlog.Error("addSubscriber input para is null") 290 return types.ErrInvalidParam 291 } 292 293 if subscribe.Type < PushBlock || subscribe.Type > PushTxResult { 294 chainlog.Error("addSubscriber input type is error", "type", subscribe.Type) 295 return types.ErrInvalidParam 296 } 297 298 //如果需要配置起始的块的信息,则为了保持一致性,三项缺一不可 299 if subscribe.LastBlockHash != "" || subscribe.LastSequence != 0 || subscribe.LastHeight != 0 { 300 if subscribe.LastBlockHash == "" || subscribe.LastSequence == 0 || subscribe.LastHeight == 0 { 301 chainlog.Error("addSubscriber ErrInvalidParam", "seqUpdateChan", subscribe.LastSequence, "height", subscribe.LastHeight, "hash", subscribe.LastBlockHash) 302 return types.ErrInvalidParam 303 } 304 } 305 306 //如果该用户已经注册了订阅请求,则只是确认是否需用重新启动,否则就直接返回 307 if exist, subscribeInDB := push.hasSubscriberExist(subscribe); exist { 308 if subscribeInDB.URL != subscribe.URL || subscribeInDB.Type != subscribe.Type { 309 return types.ErrNotAllowModifyPush 310 } 311 //使用保存在数据库中的push配置,而不是最新的配置信息 312 return push.check2ResumePush(subscribeInDB) 313 } 314 315 push.mu.Lock() 316 if len(push.tasks) >= maxPushSubscriber { 317 chainlog.Error("addSubscriber too many push subscriber") 318 push.mu.Unlock() 319 return types.ErrTooManySeqCB 320 } 321 push.mu.Unlock() 322 323 //处理需要从指定高度开始推送的订阅请求 324 if subscribe.LastSequence > 0 { 325 sequence, err := push.sequenceStore.GetBlockSequence(subscribe.LastSequence) 326 if err != nil { 327 chainlog.Error("addSubscriber continue-seqUpdateChan-push", "load-1", err) 328 return err 329 } 330 331 // 注册点,在节点上存在 332 // 同一高度,不一定同一个hash,有分叉的可能;但同一个hash必定同一个高度 333 reloadHash := common.ToHex(sequence.Hash) 334 if subscribe.LastBlockHash == reloadHash { 335 // 先填入last seqUpdateChan, 而不是从0开始 336 err = push.setLastPushSeq(subscribe.Name, subscribe.LastSequence) 337 if err != nil { 338 chainlog.Error("addSubscriber", "setLastPushSeq", err) 339 return err 340 } 341 return push.persisAndStart(subscribe) 342 } 343 } 344 345 return push.persisAndStart(subscribe) 346 } 347 348 func (push *Push) hasSubscriberExist(subscribe *types.PushSubscribeReq) (bool, *types.PushSubscribeReq) { 349 value, err := push.store.GetKey(calcPushKey(subscribe.Name)) 350 if err == nil { 351 var pushWithStatus types.PushWithStatus 352 err = types.Decode(value, &pushWithStatus) 353 return err == nil, pushWithStatus.Push 354 } 355 return false, nil 356 } 357 358 func (push *Push) subscriberCount() int64 { 359 return push.store.PrefixCount(pushPrefix) 360 } 361 362 //向数据库添加交易回执订阅信息 363 func (push *Push) persisAndStart(subscribe *types.PushSubscribeReq) error { 364 if len(subscribe.Name) > 128 || len(subscribe.URL) > 1024 || len(subscribe.URL) == 0 { 365 storeLog.Error("Invalid para to persisAndStart due to wrong length", "len(subscribe.Name)=", len(subscribe.Name), 366 "len(subscribe.URL)=", len(subscribe.URL), "len(subscribe.Contract)=", len(subscribe.Contract)) 367 return types.ErrInvalidParam 368 } 369 key := calcPushKey(subscribe.Name) 370 storeLog.Info("persisAndStart", "key", string(key), "subscribe", subscribe) 371 push.addTask(subscribe) 372 373 pushWithStatus := &types.PushWithStatus{ 374 Push: subscribe, 375 Status: subscribeStatusActive, 376 } 377 378 return push.store.SetSync(key, types.Encode(pushWithStatus)) 379 } 380 381 func (push *Push) check2ResumePush(subscribe *types.PushSubscribeReq) error { 382 if len(subscribe.Name) > 128 || len(subscribe.URL) > 1024 || len(subscribe.Contract) > 128 { 383 storeLog.Error("Invalid para to persisAndStart due to wrong length", "len(subscribe.Name)=", len(subscribe.Name), 384 "len(subscribe.URL)=", len(subscribe.URL), "len(subscribe.Contract)=", len(subscribe.Contract)) 385 return types.ErrInvalidParam 386 } 387 push.mu.Lock() 388 defer push.mu.Unlock() 389 390 keyStr := string(calcPushKey(subscribe.Name)) 391 storeLog.Info("check2ResumePush", "key", keyStr, "subscribe", subscribe) 392 393 notify := push.tasks[keyStr] 394 //有可能因为连续发送失败已经导致将其从推送任务中删除了 395 if nil == notify { 396 push.tasks[keyStr] = &pushNotify{ 397 subscribe: subscribe, 398 seqUpdateChan: make(chan int64, chanBufCap), 399 closechan: make(chan struct{}), 400 status: notRunning, 401 } 402 push.runTask(push.tasks[keyStr]) 403 storeLog.Info("check2ResumePush new pushNotify created") 404 return nil 405 } 406 407 if running == atomic.LoadInt32(¬ify.status) { 408 storeLog.Info("Is already in state:running", "postFail2Sleep", atomic.LoadInt32(¬ify.postFail2Sleep)) 409 atomic.StoreInt32(¬ify.postFail2Sleep, 0) 410 return nil 411 } 412 storeLog.Info("check2ResumePush to resume a push", "name", subscribe.Name) 413 414 push.runTask(push.tasks[keyStr]) 415 return nil 416 } 417 418 //每次add一个新push时,发送最新的seq 419 func (push *Push) updateLastSeq(name string) { 420 last, err := push.sequenceStore.LoadBlockLastSequence() 421 if err != nil { 422 chainlog.Error("LoadBlockLastSequence", "err", err) 423 return 424 } 425 426 notify := push.tasks[string(calcPushKey(name))] 427 notify.seqUpdateChan <- last 428 chainlog.Debug("updateLastSeq", "last", last, "notify.seqUpdateChan", len(notify.seqUpdateChan)) 429 } 430 431 // addTask 每个name 有一个task, 通知新增推送 432 func (push *Push) addTask(subscribe *types.PushSubscribeReq) { 433 push.mu.Lock() 434 defer push.mu.Unlock() 435 keyStr := string(calcPushKey(subscribe.Name)) 436 push.tasks[keyStr] = &pushNotify{ 437 subscribe: subscribe, 438 seqUpdateChan: make(chan int64, chanBufCap), 439 closechan: make(chan struct{}), 440 status: notRunning, 441 postFail2Sleep: 0, 442 } 443 444 push.runTask(push.tasks[keyStr]) 445 } 446 447 func trigeRun(run chan struct{}, sleep time.Duration, name string) { 448 chainlog.Info("trigeRun", name, "name", "sleep", sleep, "run len", len(run)) 449 if sleep > 0 { 450 time.Sleep(sleep) 451 } 452 go func() { 453 run <- struct{}{} 454 }() 455 } 456 457 func (push *Push) runTask(input *pushNotify) { 458 //触发goroutine运行 459 push.updateLastSeq(input.subscribe.Name) 460 461 go func(in *pushNotify) { 462 var lastesBlockSeq int64 463 var continueFailCount int32 464 var err error 465 466 subscribe := in.subscribe 467 lastProcessedseq := push.getLastPushSeq(subscribe) 468 469 atomic.StoreInt32(&in.status, running) 470 471 runChan := make(chan struct{}, 10) 472 pushMaxSeq := pushBlockMaxSeq 473 if subscribe.Type == PushTxReceipt { 474 pushMaxSeq = pushTxReceiptMaxSeq 475 } 476 477 chainlog.Debug("start push with info", "subscribe name", subscribe.Name, "Type", PushType(subscribe.Type).string()) 478 for { 479 select { 480 case <-runChan: 481 if atomic.LoadInt32(&input.postFail2Sleep) > 0 { 482 if postFail2SleepNew := atomic.AddInt32(&input.postFail2Sleep, -1); postFail2SleepNew > 0 { 483 chainlog.Debug("wait another ticker for post fail", "postFail2Sleep", postFail2SleepNew, "name", in.subscribe.Name) 484 trigeRun(runChan, time.Second, subscribe.Name) 485 continue 486 } 487 } 488 489 case lastestSeq := <-in.seqUpdateChan: 490 chainlog.Debug("runTask recv:", "lastestSeq", lastestSeq, "subscribe name", subscribe.Name, "Type", PushType(subscribe.Type).string()) 491 //首先判断是否存在发送失败的情况,如果存在,则进行进行sleep操作 492 if atomic.LoadInt32(&input.postFail2Sleep) > 0 { 493 if postFail2SleepNew := atomic.AddInt32(&input.postFail2Sleep, -1); postFail2SleepNew > 0 { 494 chainlog.Debug("wait another ticker for post fail", "postFail2Sleep", postFail2SleepNew, "name", in.subscribe.Name) 495 trigeRun(runChan, time.Second, subscribe.Name) 496 continue 497 } 498 } 499 //获取当前最新的sequence,这样就可以一次性发送多个区块的信息,而不需要每次从通知chan中获取最新sequence 500 if lastesBlockSeq, err = push.sequenceStore.LoadBlockLastSequence(); err != nil { 501 chainlog.Error("LoadBlockLastSequence", "err", err) 502 return 503 } 504 505 //没有更新的区块,则不进行处理,同时等待一定的时间 506 if lastProcessedseq >= lastesBlockSeq { 507 continue 508 } 509 chainlog.Debug("another new block", "subscribe name", subscribe.Name, "Type", PushType(subscribe.Type).string(), 510 "last push sequence", lastProcessedseq, "lastest sequence", lastesBlockSeq, 511 "time second", time.Now().Second()) 512 //确定一次推送的数量,如果需要更新的数量少于门限值,则一次只推送一个区块的交易数据 513 seqCount := pushMaxSeq 514 if seqCount > int(lastesBlockSeq-lastProcessedseq) { 515 seqCount = int(lastesBlockSeq - lastProcessedseq) 516 } 517 518 data, updateSeq, err := push.getPushData(subscribe, lastProcessedseq+1, seqCount, pushMaxSize) 519 if err != nil { 520 chainlog.Error("getPushData", "err", err, "seqCurrent", lastProcessedseq+1, "maxSeq", seqCount, 521 "Name", subscribe.Name, "pushType:", PushType(subscribe.Type).string()) 522 continue 523 } 524 525 if data != nil { 526 err = push.postService.PostData(subscribe, data, updateSeq) 527 if err != nil { 528 continueFailCount++ 529 chainlog.Error("postdata failed", "err", err, "lastProcessedseq", lastProcessedseq, 530 "Name", subscribe.Name, "pushType:", PushType(subscribe.Type).string(), "continueFailCount", continueFailCount) 531 if continueFailCount >= 3 { 532 atomic.StoreInt32(&in.status, notRunning) 533 chainlog.Error("postdata failed exceed 3 times", "Name", subscribe.Name, "in.status", atomic.LoadInt32(&in.status)) 534 535 pushWithStatus := &types.PushWithStatus{ 536 Push: subscribe, 537 Status: subscribeStatusNotActive, 538 } 539 540 key := calcPushKey(subscribe.Name) 541 push.mu.Lock() 542 delete(push.tasks, string(key)) 543 push.mu.Unlock() 544 _ = push.store.SetSync(key, types.Encode(pushWithStatus)) 545 push.postwg.Done() 546 return 547 } 548 //sleep 60s,每次1s,总计60次,在每次结束时,等待接收方重新进行请求推送 549 atomic.StoreInt32(&input.postFail2Sleep, push.postFail2Sleep) 550 trigeRun(runChan, time.Second, subscribe.Name) 551 continue 552 } 553 _ = push.setLastPushSeq(subscribe.Name, updateSeq) 554 } 555 continueFailCount = 0 556 lastProcessedseq = updateSeq 557 // 在联盟链情况下, 无新增交易的情况下, 不会完成从新开始同步 558 // 在公链情况下, 需要有新区块才能触发推送, 559 // 所以这里在未同步到最新区块, 需要主动触发同步 560 if lastProcessedseq < lastesBlockSeq { 561 push.mu.Lock() 562 if len(in.seqUpdateChan) == 0 { 563 in.seqUpdateChan <- lastesBlockSeq 564 } 565 push.mu.Unlock() 566 } 567 case <-in.closechan: 568 push.postwg.Done() 569 chainlog.Info("getPushData", "push task closed for subscribe", subscribe.Name) 570 return 571 } 572 } 573 574 }(input) 575 push.postwg.Add(1) 576 } 577 578 // UpdateSeq sequence 更新通知 579 func (push *Push) UpdateSeq(seq int64) { 580 push.mu.Lock() 581 defer push.mu.Unlock() 582 for _, notify := range push.tasks { 583 //再写入seq(一定不会block,因为加了lock,不存在两个同时写channel的情况) 584 if len(notify.seqUpdateChan) < chanBufCap { 585 chainlog.Info("new block Update Seq notified", "subscribe", notify.subscribe.Name, "current sequence", seq, "length", len(notify.seqUpdateChan)) 586 notify.seqUpdateChan <- seq 587 } 588 chainlog.Info("new block UpdateSeq", "subscribe", notify.subscribe.Name, "current sequence", seq, "length", len(notify.seqUpdateChan)) 589 } 590 } 591 592 func (push *Push) getPushData(subscribe *types.PushSubscribeReq, startSeq int64, seqCount, maxSize int) ([]byte, int64, error) { 593 if subscribe.Type == PushBlock { 594 return push.getBlockSeqs(subscribe.Encode, startSeq, seqCount, maxSize) 595 } else if subscribe.Type == PushBlockHeader { 596 return push.getHeaderSeqs(subscribe.Encode, startSeq, seqCount, maxSize) 597 } else if subscribe.Type == PushTxResult { 598 return push.getTxResults(subscribe.Encode, startSeq, seqCount) 599 } 600 return push.getTxReceipts(subscribe, startSeq, seqCount, maxSize) 601 } 602 603 func (push *Push) getTxReceipts(subscribe *types.PushSubscribeReq, startSeq int64, seqCount, maxSize int) ([]byte, int64, error) { 604 txReceipts := &types.TxReceipts4Subscribe{} 605 totalSize := 0 606 actualIterCount := 0 607 for i := startSeq; i < startSeq+int64(seqCount); i++ { 608 chainlog.Info("getTxReceipts", "startSeq:", i) 609 seqdata, err := push.sequenceStore.GetBlockSequence(i) 610 if err != nil { 611 return nil, -1, err 612 } 613 detail, _, err := push.sequenceStore.LoadBlockBySequence(i) 614 if err != nil { 615 return nil, -1, err 616 } 617 618 txReceiptsPerBlk := &types.TxReceipts4SubscribePerBlk{} 619 chainlog.Info("getTxReceipts", "height:", detail.Block.Height, "tx numbers:", len(detail.Block.Txs), "Receipts numbers:", len(detail.Receipts)) 620 for txIndex, tx := range detail.Block.Txs { 621 if subscribe.Contract[string(tx.Execer)] { 622 chainlog.Info("getTxReceipts", "txIndex:", txIndex) 623 txReceiptsPerBlk.Tx = append(txReceiptsPerBlk.Tx, tx) 624 txReceiptsPerBlk.ReceiptData = append(txReceiptsPerBlk.ReceiptData, detail.Receipts[txIndex]) 625 //txReceiptsPerBlk.KV = append(txReceiptsPerBlk.KV, detail.KV[txIndex]) 626 } 627 } 628 if len(txReceiptsPerBlk.Tx) > 0 { 629 txReceiptsPerBlk.Height = detail.Block.Height 630 txReceiptsPerBlk.BlockHash = detail.Block.Hash(push.cfg) 631 txReceiptsPerBlk.ParentHash = detail.Block.ParentHash 632 txReceiptsPerBlk.PreviousHash = []byte{} 633 txReceiptsPerBlk.AddDelType = int32(seqdata.Type) 634 txReceiptsPerBlk.SeqNum = i 635 } 636 size := types.Size(txReceiptsPerBlk) 637 if len(txReceiptsPerBlk.Tx) > 0 && totalSize+size < maxSize { 638 txReceipts.TxReceipts = append(txReceipts.TxReceipts, txReceiptsPerBlk) 639 totalSize += size 640 chainlog.Debug("get Tx Receipts subscribed for pushing", "Name", subscribe.Name, "contract:", subscribe.Contract, 641 "height=", txReceiptsPerBlk.Height) 642 } else if totalSize+size > maxSize { 643 break 644 } 645 actualIterCount++ 646 } 647 648 updateSeq := startSeq + int64(actualIterCount) - 1 649 if len(txReceipts.TxReceipts) == 0 { 650 return nil, updateSeq, nil 651 } 652 chainlog.Info("getTxReceipts", "updateSeq", updateSeq, "actualIterCount", actualIterCount) 653 654 var postdata []byte 655 var err error 656 if subscribe.Encode == "json" { 657 postdata, err = types.PBToJSON(txReceipts) 658 if err != nil { 659 return nil, -1, err 660 } 661 } else { 662 postdata = types.Encode(txReceipts) 663 } 664 665 return postdata, updateSeq, nil 666 } 667 668 func (push *Push) getBlockDataBySeq(seq int64) (*types.BlockSeq, int, error) { 669 seqdata, err := push.sequenceStore.GetBlockSequence(seq) 670 if err != nil { 671 return nil, 0, err 672 } 673 detail, blockSize, err := push.sequenceStore.LoadBlockBySequence(seq) 674 if err != nil { 675 return nil, 0, err 676 } 677 return &types.BlockSeq{Num: seq, Seq: seqdata, Detail: detail}, blockSize, nil 678 } 679 680 func (push *Push) getTxResults(encode string, seq int64, seqCount int) ([]byte, int64, error) { 681 var txResultSeqs types.TxResultSeqs 682 for i := seq; i < seq+int64(seqCount); i++ { 683 blockSeq, _, err := push.getBlockDataBySeq(i) 684 if err != nil { 685 return nil, -1, err 686 } 687 txResults := types.TxResultPerBlock{ 688 Items: make([]*types.TxHashWithReceiptType, len(blockSeq.Detail.Receipts)), 689 Height: blockSeq.Detail.Block.Height, 690 BlockHash: blockSeq.Detail.Block.Hash(push.cfg), 691 ParentHash: blockSeq.Detail.Block.ParentHash, 692 AddDelType: int32(blockSeq.Seq.Type), 693 SeqNum: blockSeq.Num, 694 } 695 for i := range txResults.Items { 696 txResults.Items[i] = &types.TxHashWithReceiptType{ 697 Hash: blockSeq.Detail.Block.Txs[i].Hash(), 698 Ty: blockSeq.Detail.Receipts[i].Ty, 699 } 700 } 701 txResultSeqs.Items = append(txResultSeqs.Items, &txResults) 702 } 703 704 var postdata []byte 705 var err error 706 if encode == "json" { 707 postdata, err = types.PBToJSON(&txResultSeqs) 708 if err != nil { 709 return nil, -1, err 710 } 711 } else { 712 postdata = types.Encode(&txResultSeqs) 713 } 714 return postdata, txResultSeqs.Items[0].SeqNum + int64(len(txResultSeqs.Items)) - 1, nil 715 } 716 717 func (push *Push) getBlockSeqs(encode string, seq int64, seqCount, maxSize int) ([]byte, int64, error) { 718 seqs := &types.BlockSeqs{} 719 totalSize := 0 720 for i := 0; i < seqCount; i++ { 721 seq, size, err := push.getBlockDataBySeq(seq + int64(i)) 722 if err != nil { 723 return nil, -1, err 724 } 725 if totalSize == 0 || totalSize+size < maxSize { 726 seqs.Seqs = append(seqs.Seqs, seq) 727 totalSize += size 728 } else { 729 break 730 } 731 } 732 updateSeq := seqs.Seqs[0].Num + int64(len(seqs.Seqs)) - 1 733 734 var postdata []byte 735 var err error 736 if encode == "json" { 737 postdata, err = types.PBToJSON(seqs) 738 if err != nil { 739 return nil, -1, err 740 } 741 } else { 742 postdata = types.Encode(seqs) 743 } 744 return postdata, updateSeq, nil 745 } 746 747 func (push *Push) getHeaderSeqs(encode string, seq int64, seqCount, maxSize int) ([]byte, int64, error) { 748 seqs := &types.HeaderSeqs{} 749 totalSize := 0 750 for i := 0; i < seqCount; i++ { 751 seq, size, err := push.getHeaderDataBySeq(seq + int64(i)) 752 if err != nil { 753 return nil, -1, err 754 } 755 if totalSize == 0 || totalSize+size < maxSize { 756 seqs.Seqs = append(seqs.Seqs, seq) 757 totalSize += size 758 } else { 759 break 760 } 761 } 762 updateSeq := seqs.Seqs[0].Num + int64(len(seqs.Seqs)) - 1 763 764 var postdata []byte 765 var err error 766 767 if encode == "json" { 768 postdata, err = types.PBToJSON(seqs) 769 if err != nil { 770 return nil, -1, err 771 } 772 } else { 773 postdata = types.Encode(seqs) 774 } 775 return postdata, updateSeq, nil 776 } 777 778 func (push *Push) getHeaderDataBySeq(seq int64) (*types.HeaderSeq, int, error) { 779 seqdata, err := push.sequenceStore.GetBlockSequence(seq) 780 if err != nil { 781 return nil, 0, err 782 } 783 header, err := push.sequenceStore.GetBlockHeaderByHash(seqdata.Hash) 784 if err != nil { 785 return nil, 0, err 786 } 787 return &types.HeaderSeq{Num: seq, Seq: seqdata, Header: header}, header.Size(), nil 788 } 789 790 // GetLastPushSeq Seq的合法值从0开始的,所以没有获取到或者获取失败都应该返回-1 791 func (push *Push) getLastPushSeq(subscribe *types.PushSubscribeReq) int64 { 792 seqbytes, err := push.store.GetKey(calcLastPushSeqNumKey(subscribe.Name)) 793 if seqbytes == nil || err != nil { 794 if err != dbm.ErrNotFoundInDb { 795 storeLog.Error("getLastPushSeq", "error", err) 796 } 797 return -1 798 } 799 n, err := decodeHeight(seqbytes) 800 if err != nil { 801 return -1 802 } 803 chainlog.Info("getLastPushSeq", "name", subscribe.Name, 804 "Contract:", subscribe.Contract, "num", n) 805 806 return n 807 } 808 809 func (push *Push) setLastPushSeq(name string, num int64) error { 810 return push.store.SetSync(calcLastPushSeqNumKey(name), types.Encode(&types.Int64{Data: num})) 811 }