Fix block download race condition

This commit is contained in:
Mononaut 2022-06-09 22:46:55 +00:00
parent 29627a5ebd
commit 1c2f9e5649
8 changed files with 29 additions and 28 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "bitfeed-client", "name": "bitfeed-client",
"version": "2.3.2", "version": "2.3.3",
"scripts": { "scripts": {
"build": "rollup -c", "build": "rollup -c",
"dev": "rollup -c -w", "dev": "rollup -c -w",

View File

@ -3,7 +3,7 @@ import Overlay from '../components/Overlay.svelte'
import Icon from './Icon.svelte' import Icon from './Icon.svelte'
import BookmarkIcon from '../assets/icon/cil-bookmark.svg' import BookmarkIcon from '../assets/icon/cil-bookmark.svg'
import { longBtcFormat, numberFormat, feeRateFormat, dateFormat } from '../utils/format.js' import { longBtcFormat, numberFormat, feeRateFormat, dateFormat } from '../utils/format.js'
import { exchangeRates, settings, sidebarToggle, newHighlightQuery, highlightingFull, detailTx, pageWidth, latestBlockHeight, highlightInOut, loading, urlPath, currentBlock, overlay, explorerBlockData } from '../stores.js' import { exchangeRates, settings, sidebarToggle, newHighlightQuery, highlightingFull, detailTx, pageWidth, latestBlockHeight, highlightInOut, loading, urlPath, currentBlock, overlay, explorerBlock } from '../stores.js'
import { formatCurrency } from '../utils/fx.js' import { formatCurrency } from '../utils/fx.js'
import { hlToHex, mixColor, teal, purple } from '../utils/color.js' import { hlToHex, mixColor, teal, purple } from '../utils/color.js'
import { SPKToAddress } from '../utils/encodings.js' import { SPKToAddress } from '../utils/encodings.js'
@ -298,9 +298,11 @@ async function goToBlock(e) {
let hash = $detailTx.block.hash || $detailTx.block.id let hash = $detailTx.block.hash || $detailTx.block.id
let height = $detailTx.block.height let height = $detailTx.block.height
if (hash === $currentBlock.id) { if (hash === $currentBlock.id) {
onClose()
$overlay = null $overlay = null
} else if (height == $latestBlockHeight) { } else if (height == $latestBlockHeight) {
$explorerBlockData = null onClose()
$explorerBlock = null
$overlay = null $overlay = null
} else if (hash) { } else if (hash) {
loading.increment() loading.increment()

View File

@ -1,4 +1,4 @@
import { urlPath, settings, loading, detailTx, highlightInOut, explorerBlockData, overlay } from '../stores.js' import { urlPath, settings, loading, detailTx, highlightInOut, explorerBlock, overlay } from '../stores.js'
import { searchTx, searchBlockHash, searchBlockHeight } from '../utils/search.js' import { searchTx, searchBlockHash, searchBlockHeight } from '../utils/search.js'
export default class Router { export default class Router {
@ -37,7 +37,7 @@ export default class Router {
detailTx.set(null) detailTx.set(null)
highlightInOut.set(null) highlightInOut.set(null)
urlPath.set("/") urlPath.set("/")
explorerBlockData.set(null) explorerBlock.set(null)
overlay.set(null) overlay.set(null)
} else { } else {
switch (parts[1]) { switch (parts[1]) {

View File

@ -6,7 +6,7 @@ import BitcoinBlock from '../models/BitcoinBlock.js'
import TxSprite from '../models/TxSprite.js' import TxSprite from '../models/TxSprite.js'
import { FastVertexArray } from '../utils/memory.js' import { FastVertexArray } from '../utils/memory.js'
import { searchTx, fetchSpends, addSpends } from '../utils/search.js' import { searchTx, fetchSpends, addSpends } from '../utils/search.js'
import { overlay, txCount, mempoolCount, mempoolScreenHeight, blockVisible, currentBlock, selectedTx, detailTx, blockAreaSize, highlight, colorMode, blocksEnabled, latestBlockHeight, explorerBlockData, blockTransitionDirection, loading, urlPath } from '../stores.js' import { overlay, txCount, mempoolCount, mempoolScreenHeight, blockVisible, currentBlock, selectedTx, detailTx, blockAreaSize, highlight, colorMode, blocksEnabled, latestBlockHeight, explorerBlock, blockTransitionDirection, loading, urlPath } from '../stores.js'
import config from "../config.js" import config from "../config.js"
import { tick } from 'svelte'; import { tick } from 'svelte';
@ -48,9 +48,9 @@ export default class TxController {
colorMode.subscribe(mode => { colorMode.subscribe(mode => {
this.setColorMode(mode) this.setColorMode(mode)
}) })
explorerBlockData.subscribe(blockData => { explorerBlock.subscribe(block => {
if (blockData) { if (block) {
this.exploreBlock(blockData) this.exploreBlock(block)
} else { } else {
this.resumeLatest() this.resumeLatest()
} }
@ -168,21 +168,18 @@ export default class TxController {
}).map(key => { }).map(key => {
return { return {
...this.txs[key], ...this.txs[key],
inputs: this.txs[key].inputs.map(input => { return {...input, script_pub_key: null, value: null }}), inputs: this.txs[key].inputs ? this.txs[key].inputs.map(input => { return {...input, script_pub_key: null, value: null }}) : [],
} }
})] })]
}) })
} }
addBlock (blockData, realtime=true) { addBlock (block, realtime=true) {
// discard duplicate blocks // discard duplicate blocks
if (!blockData || !blockData.id || this.knownBlocks[blockData.id]) { if (!block || !block.id || this.knownBlocks[block.id]) {
return return
} }
let block
block = new BitcoinBlock(blockData)
latestBlockHeight.set(block.height) latestBlockHeight.set(block.height)
// this.knownBlocks[block.id] = true // this.knownBlocks[block.id] = true
if (this.clearBlockTimeout) clearTimeout(this.clearBlockTimeout) if (this.clearBlockTimeout) clearTimeout(this.clearBlockTimeout)
@ -285,9 +282,7 @@ export default class TxController {
return block return block
} }
async exploreBlock (blockData) { async exploreBlock (block) {
const block = blockData.isBlock ? blockData : new BitcoinBlock(blockData)
if (this.block && this.block.id === block.id) { if (this.block && this.block.id === block.id) {
this.showBlock() this.showBlock()
return return

View File

@ -1,4 +1,4 @@
import { serverConnected, serverDelay, lastBlockId } from '../stores.js' import { serverConnected, serverDelay, lastBlockId, latestBlockHeight } from '../stores.js'
import config from '../config.js' import config from '../config.js'
import api from '../utils/api.js' import api from '../utils/api.js'
import { fetchBlockByHash } from '../utils/search.js' import { fetchBlockByHash } from '../utils/search.js'
@ -6,7 +6,8 @@ import { fetchBlockByHash } from '../utils/search.js'
let mempoolTimer let mempoolTimer
let lastBlockSeen let lastBlockSeen
lastBlockId.subscribe(val => { lastBlockSeen = val }) lastBlockId.subscribe(val => { lastBlockSeen = val })
let latestBlockHeightVal
latestBlockHeight.subscribe(val => { latestBlockHeightVal = val })
class TxStream { class TxStream {
constructor () { constructor () {
@ -122,8 +123,11 @@ class TxStream {
async fetchBlock (id, calledOnLoad) { async fetchBlock (id, calledOnLoad) {
if (!id) return if (!id) return
if (id !== lastBlockSeen) { if (id !== lastBlockSeen) {
const blockData = await fetchBlockByHash(id) const block = await fetchBlockByHash(id)
window.dispatchEvent(new CustomEvent('bitcoin_block', { detail: { block: blockData, realtime: !calledOnLoad} })) // ignore this block if we've downloaded a newer one while we were waiting
if (block.height >= latestBlockHeightVal) {
window.dispatchEvent(new CustomEvent('bitcoin_block', { detail: { block, realtime: !calledOnLoad} }))
}
} else { } else {
console.log('already seen block ', lastBlockSeen) console.log('already seen block ', lastBlockSeen)
} }

View File

@ -179,7 +179,7 @@ export const blocksEnabled = derived([settings], ([$settings]) => {
export const latestBlockHeight = writable(null) export const latestBlockHeight = writable(null)
export const highlightInOut = writable(null) export const highlightInOut = writable(null)
export const loading = createCounter() export const loading = createCounter()
export const explorerBlockData = writable(null) export const explorerBlock = writable(null)
export const blockTransitionDirection = writable(null) export const blockTransitionDirection = writable(null)
export const urlPath = writable(null) export const urlPath = writable(null)

View File

@ -1,7 +1,7 @@
import api from './api.js' import api from './api.js'
import BitcoinTx from '../models/BitcoinTx.js' import BitcoinTx from '../models/BitcoinTx.js'
import BitcoinBlock from '../models/BitcoinBlock.js' import BitcoinBlock from '../models/BitcoinBlock.js'
import { detailTx, selectedTx, currentBlock, explorerBlockData, overlay, highlightInOut, urlPath } from '../stores.js' import { detailTx, selectedTx, currentBlock, explorerBlock, overlay, highlightInOut, urlPath } from '../stores.js'
import { addressToSPK } from './encodings.js' import { addressToSPK } from './encodings.js'
// Quick heuristic matching to guess what kind of search a query is for // Quick heuristic matching to guess what kind of search a query is for
@ -154,7 +154,7 @@ async function fetchBlockByHash (hash) {
if (blockData) { if (blockData) {
if (blockData.id) { if (blockData.id) {
block = new BitcoinBlock(blockData) block = new BitcoinBlock(blockData)
} else block = BitcoinBlock.decompress(blockData) } else block = new BitcoinBlock(BitcoinBlock.decompress(blockData))
} }
if (block && block.id) { if (block && block.id) {
console.log('downloaded block', block.id) console.log('downloaded block', block.id)
@ -255,7 +255,7 @@ export async function searchBlockHash (hash) {
const searchResult = await fetchBlockByHash(hash) const searchResult = await fetchBlockByHash(hash)
if (searchResult) { if (searchResult) {
if (searchResult.id) { if (searchResult.id) {
explorerBlockData.set(searchResult) explorerBlock.set(searchResult)
} }
return null return null
} else { } else {
@ -274,7 +274,7 @@ export async function searchBlockHeight (height) {
const searchResult = await fetchBlockByHeight(height) const searchResult = await fetchBlockByHeight(height)
if (searchResult) { if (searchResult) {
if (searchResult.id) { if (searchResult.id) {
explorerBlockData.set(searchResult) explorerBlock.set(searchResult)
} }
return null return null
} else { } else {

View File

@ -4,7 +4,7 @@ defmodule BitcoinStream.MixProject do
def project do def project do
[ [
app: :bitcoin_stream, app: :bitcoin_stream,
version: "2.3.2", version: "2.3.3",
elixir: "~> 1.10", elixir: "~> 1.10",
start_permanent: Mix.env() == :prod, start_permanent: Mix.env() == :prod,
deps: deps(), deps: deps(),