Switch channels for function callback/iterators for Index
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
deb3c6db01
commit
a56a016f6c
247
cmd/api/api.go
247
cmd/api/api.go
|
@ -79,18 +79,13 @@ func main() {
|
|||
return result
|
||||
}
|
||||
|
||||
fillFoundBlockResult := func(params url.Values, foundBlocks []*index.FoundBlock) (result []*index.FoundBlock) {
|
||||
result = make([]*index.FoundBlock, 0)
|
||||
fillMiner := !params.Has("noMiner")
|
||||
for _, foundBlock := range foundBlocks {
|
||||
if fillMiner {
|
||||
miner := indexDb.GetMiner(foundBlock.Miner)
|
||||
foundBlock.MinerAddress = miner.Address()
|
||||
foundBlock.MinerAlias = miner.Alias()
|
||||
}
|
||||
result = append(result, foundBlock)
|
||||
fillFoundBlockResult := func(fillMiner bool, foundBlock *index.FoundBlock) (result *index.FoundBlock) {
|
||||
if fillMiner {
|
||||
miner := indexDb.GetMiner(foundBlock.Miner)
|
||||
foundBlock.MinerAddress = miner.Address()
|
||||
foundBlock.MinerAlias = miner.Alias()
|
||||
}
|
||||
return result
|
||||
return foundBlock
|
||||
}
|
||||
|
||||
fillSideBlockResult := func(fillUncles, fillMined, fillMiner bool, sideBlock *index.SideBlock) *index.SideBlock {
|
||||
|
@ -114,14 +109,15 @@ func main() {
|
|||
}
|
||||
}
|
||||
if fillUncles {
|
||||
for u := range indexDb.GetSideBlocksByUncleOfId(sideBlock.TemplateId) {
|
||||
index.QueryIterate(indexDb.GetSideBlocksByUncleOfId(sideBlock.TemplateId), func(_ int, u *index.SideBlock) (stop bool) {
|
||||
sideBlock.Uncles = append(sideBlock.Uncles, index.SideBlockUncleEntry{
|
||||
TemplateId: u.TemplateId,
|
||||
Miner: u.Miner,
|
||||
SideHeight: u.SideHeight,
|
||||
Difficulty: u.Difficulty,
|
||||
})
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
return sideBlock
|
||||
}
|
||||
|
@ -242,7 +238,7 @@ func main() {
|
|||
return result
|
||||
}).(*totalKnownResult)
|
||||
|
||||
lastBlocksFound := indexDb.GetFoundBlocks("", 201)
|
||||
lastBlocksFound := index.QueryIterateToSlice(indexDb.GetFoundBlocks("", 201))
|
||||
|
||||
mainTip := indexDb.GetMainBlockTip()
|
||||
networkDifficulty := types.DifficultyFrom64(mainTip.Difficulty)
|
||||
|
@ -357,7 +353,7 @@ func main() {
|
|||
}
|
||||
|
||||
if len(lastBlocksFound) > 0 {
|
||||
result.SideChain.LastFound = fillFoundBlockResult(nil, lastBlocksFound[:1])[0]
|
||||
result.SideChain.LastFound = fillFoundBlockResult(false, lastBlocksFound[0])
|
||||
}
|
||||
|
||||
lastPoolInfo.Store(result)
|
||||
|
@ -536,7 +532,6 @@ func main() {
|
|||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_ = httputils.EncodeJson(request, writer, indexDb.GetMainLikelySweepTransactionBySpendingGlobalOutputIndices(indices...))
|
||||
|
||||
})
|
||||
|
||||
serveMux.HandleFunc("/api/transaction_lookup/{txid:[0-9a-f]+}", func(writer http.ResponseWriter, request *http.Request) {
|
||||
|
@ -755,8 +750,19 @@ func main() {
|
|||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
|
||||
result := fillSideBlockChannelResult(params, indexDb.GetSideBlocksInWindow(from, window))
|
||||
_ = httputils.StreamJsonSlice(request, writer, result)
|
||||
fillUncles := !params.Has("noUncles")
|
||||
fillMined := !params.Has("noMainStatus")
|
||||
fillMiner := !params.Has("noMiner")
|
||||
|
||||
sideBlocks := indexDb.GetSideBlocksInWindow(from, window)
|
||||
defer sideBlocks.Close()
|
||||
_ = httputils.StreamJsonIterator(request, writer, func() (int, *index.SideBlock) {
|
||||
i, v := sideBlocks.Next()
|
||||
if v != nil {
|
||||
v = fillSideBlockResult(fillUncles, fillMined, fillMiner, v)
|
||||
}
|
||||
return i, v
|
||||
})
|
||||
})
|
||||
|
||||
serveMux.HandleFunc("/api/side_blocks_in_window/{miner:[0-9]+|4[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+$}", func(writer http.ResponseWriter, request *http.Request) {
|
||||
|
@ -813,8 +819,19 @@ func main() {
|
|||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
|
||||
result := fillSideBlockChannelResult(params, indexDb.GetSideBlocksByMinerIdInWindow(miner.Id(), from, window))
|
||||
_ = httputils.StreamJsonSlice(request, writer, result)
|
||||
fillUncles := !params.Has("noUncles")
|
||||
fillMined := !params.Has("noMainStatus")
|
||||
fillMiner := !params.Has("noMiner")
|
||||
|
||||
sideBlocks := indexDb.GetSideBlocksByMinerIdInWindow(miner.Id(), from, window)
|
||||
defer sideBlocks.Close()
|
||||
_ = httputils.StreamJsonIterator(request, writer, func() (int, *index.SideBlock) {
|
||||
i, v := sideBlocks.Next()
|
||||
if v != nil {
|
||||
v = fillSideBlockResult(fillUncles, fillMined, fillMiner, v)
|
||||
}
|
||||
return i, v
|
||||
})
|
||||
})
|
||||
|
||||
serveMux.HandleFunc("/api/sweeps", func(writer http.ResponseWriter, request *http.Request) {
|
||||
|
@ -837,8 +854,13 @@ func main() {
|
|||
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_ = httputils.StreamJsonSlice(request, writer, indexDb.GetMainLikelySweepTransactions(limit))
|
||||
|
||||
result, err := indexDb.GetMainLikelySweepTransactions(limit)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer result.Close()
|
||||
_ = httputils.StreamJsonIterator(request, writer, result.Next)
|
||||
})
|
||||
|
||||
serveMux.HandleFunc("/api/sweeps/{miner:[0-9]+|4[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+$}", func(writer http.ResponseWriter, request *http.Request) {
|
||||
|
@ -878,8 +900,13 @@ func main() {
|
|||
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_ = httputils.StreamJsonSlice(request, writer, indexDb.GetMainLikelySweepTransactionsByAddress(miner.Address(), limit))
|
||||
|
||||
result, err := indexDb.GetMainLikelySweepTransactionsByAddress(miner.Address(), limit)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer result.Close()
|
||||
_ = httputils.StreamJsonIterator(request, writer, result.Next)
|
||||
})
|
||||
|
||||
serveMux.HandleFunc("/api/payouts/{miner:[0-9]+|4[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+$}", func(writer http.ResponseWriter, request *http.Request) {
|
||||
|
@ -934,14 +961,23 @@ func main() {
|
|||
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
|
||||
var result index.QueryIterator[index.Payout]
|
||||
var err error
|
||||
|
||||
if timestamp > 0 {
|
||||
_ = httputils.StreamJsonSlice(request, writer, indexDb.GetPayoutsByMinerIdFromTimestamp(miner.Id(), timestamp))
|
||||
result, err = indexDb.GetPayoutsByMinerIdFromTimestamp(miner.Id(), timestamp)
|
||||
} else if height > 0 {
|
||||
_ = httputils.StreamJsonSlice(request, writer, indexDb.GetPayoutsByMinerIdFromHeight(miner.Id(), height))
|
||||
result, err = indexDb.GetPayoutsByMinerIdFromHeight(miner.Id(), height)
|
||||
} else {
|
||||
_ = httputils.StreamJsonSlice(request, writer, indexDb.GetPayoutsByMinerId(miner.Id(), limit))
|
||||
result, err = indexDb.GetPayoutsByMinerId(miner.Id(), limit)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer result.Close()
|
||||
_ = httputils.StreamJsonIterator(request, writer, result.Next)
|
||||
})
|
||||
|
||||
serveMux.HandleFunc("/api/redirect/block/{main_height:[0-9]+|.?[0-9A-Za-z]+$}", func(writer http.ResponseWriter, request *http.Request) {
|
||||
|
@ -957,8 +993,8 @@ func main() {
|
|||
http.Redirect(writer, request, fmt.Sprintf("%s/explorer/tx/%s", cmdutils.GetSiteUrl(cmdutils.SiteKeyP2PoolIo, request.Host == torHost), txId), http.StatusFound)
|
||||
})
|
||||
serveMux.HandleFunc("/api/redirect/block/{coinbase:[0-9]+|.?[0-9A-Za-z]+$}", func(writer http.ResponseWriter, request *http.Request) {
|
||||
foundTargets := indexDb.GetFoundBlocks("WHERE side_height = $1", 1, utils.DecodeBinaryNumber(mux.Vars(request)["coinbase"]))
|
||||
if len(foundTargets) == 0 {
|
||||
foundTarget := index.QueryFirstResult(indexDb.GetFoundBlocks("WHERE side_height = $1", 1, utils.DecodeBinaryNumber(mux.Vars(request)["coinbase"])))
|
||||
if foundTarget == nil {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusNotFound)
|
||||
buf, _ := utils.MarshalJSON(struct {
|
||||
|
@ -970,7 +1006,7 @@ func main() {
|
|||
return
|
||||
}
|
||||
|
||||
http.Redirect(writer, request, fmt.Sprintf("%s/explorer/tx/%s", cmdutils.GetSiteUrl(cmdutils.SiteKeyP2PoolIo, request.Host == torHost), foundTargets[0].MainBlock.Id.String()), http.StatusFound)
|
||||
http.Redirect(writer, request, fmt.Sprintf("%s/explorer/tx/%s", cmdutils.GetSiteUrl(cmdutils.SiteKeyP2PoolIo, request.Host == torHost), foundTarget.MainBlock.Id.String()), http.StatusFound)
|
||||
})
|
||||
serveMux.HandleFunc("/api/redirect/share/{height:[0-9]+|.?[0-9A-Za-z]+$}", func(writer http.ResponseWriter, request *http.Request) {
|
||||
c := utils.DecodeBinaryNumber(mux.Vars(request)["height"])
|
||||
|
@ -979,22 +1015,24 @@ func main() {
|
|||
blockIdStart := c & 0xFFFF
|
||||
|
||||
blockStart := []byte{byte((blockIdStart >> 8) & 0xFF), byte(blockIdStart & 0xFF)}
|
||||
shares := index.ChanToSlice(indexDb.GetSideBlocksByQuery("WHERE side_height = $1;", blockHeight))
|
||||
sideBlocks, err := indexDb.GetSideBlocksByQuery("WHERE side_height = $1;", blockHeight)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var b *index.SideBlock
|
||||
for _, s := range shares {
|
||||
index.QueryIterate(sideBlocks, func(_ int, s *index.SideBlock) (stop bool) {
|
||||
if bytes.Compare(s.MainId[:2], blockStart) == 0 {
|
||||
b = s
|
||||
break
|
||||
return true
|
||||
}
|
||||
}
|
||||
if b == nil {
|
||||
for _, s := range shares {
|
||||
if bytes.Compare(s.TemplateId[:2], blockStart) == 0 {
|
||||
b = s
|
||||
break
|
||||
}
|
||||
|
||||
if bytes.Compare(s.TemplateId[:2], blockStart) == 0 {
|
||||
b = s
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
if b == nil {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
@ -1018,13 +1056,13 @@ func main() {
|
|||
height := i >> n
|
||||
outputIndex := i & ((1 << n) - 1)
|
||||
|
||||
b := indexDb.GetFoundBlocks("WHERE side_height = $1", 1, height)
|
||||
b := index.QueryFirstResult(indexDb.GetFoundBlocks("WHERE side_height = $1", 1, height))
|
||||
var tx *index.MainCoinbaseOutput
|
||||
if len(b) != 0 {
|
||||
tx = indexDb.GetMainCoinbaseOutputByIndex(b[0].MainBlock.CoinbaseId, outputIndex)
|
||||
if b != nil {
|
||||
tx = indexDb.GetMainCoinbaseOutputByIndex(b.MainBlock.CoinbaseId, outputIndex)
|
||||
}
|
||||
|
||||
if len(b) == 0 || tx == nil {
|
||||
if b == nil || tx == nil {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusNotFound)
|
||||
buf, _ := utils.MarshalJSON(struct {
|
||||
|
@ -1036,13 +1074,13 @@ func main() {
|
|||
return
|
||||
}
|
||||
|
||||
http.Redirect(writer, request, fmt.Sprintf("/proof/%s/%d", b[0].MainBlock.Id.String(), tx.Index), http.StatusFound)
|
||||
http.Redirect(writer, request, fmt.Sprintf("/proof/%s/%d", b.MainBlock.Id.String(), tx.Index), http.StatusFound)
|
||||
})
|
||||
|
||||
serveMux.HandleFunc("/api/redirect/prove/{height:[0-9]+|.[0-9A-Za-z]+}/{miner:[0-9]+|.?[0-9A-Za-z]+}", func(writer http.ResponseWriter, request *http.Request) {
|
||||
b := indexDb.GetFoundBlocks("WHERE side_height = $1", 1, utils.DecodeBinaryNumber(mux.Vars(request)["height"]))
|
||||
b := index.QueryFirstResult(indexDb.GetFoundBlocks("WHERE side_height = $1", 1, utils.DecodeBinaryNumber(mux.Vars(request)["height"])))
|
||||
miner := indexDb.GetMiner(utils.DecodeBinaryNumber(mux.Vars(request)["miner"]))
|
||||
if len(b) == 0 || miner == nil {
|
||||
if b == nil || miner == nil {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusNotFound)
|
||||
buf, _ := utils.MarshalJSON(struct {
|
||||
|
@ -1054,7 +1092,7 @@ func main() {
|
|||
return
|
||||
}
|
||||
|
||||
tx := indexDb.GetMainCoinbaseOutputByMinerId(b[0].MainBlock.Id, miner.Id())
|
||||
tx := indexDb.GetMainCoinbaseOutputByMinerId(b.MainBlock.Id, miner.Id())
|
||||
|
||||
if tx == nil {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
@ -1068,7 +1106,7 @@ func main() {
|
|||
return
|
||||
}
|
||||
|
||||
http.Redirect(writer, request, fmt.Sprintf("/proof/%s/%d", b[0].MainBlock.Id.String(), tx.Index), http.StatusFound)
|
||||
http.Redirect(writer, request, fmt.Sprintf("/proof/%s/%d", b.MainBlock.Id.String(), tx.Index), http.StatusFound)
|
||||
})
|
||||
|
||||
serveMux.HandleFunc("/api/redirect/miner/{miner:[0-9]+|.?[0-9A-Za-z]+}", func(writer http.ResponseWriter, request *http.Request) {
|
||||
|
@ -1090,11 +1128,12 @@ func main() {
|
|||
|
||||
//other redirects
|
||||
serveMux.HandleFunc("/api/redirect/last_found{kind:|/raw|/info}", func(writer http.ResponseWriter, request *http.Request) {
|
||||
var lastFoundHash types.Hash
|
||||
for _, b := range indexDb.GetFoundBlocks("", 1) {
|
||||
lastFoundHash = b.MainBlock.SideTemplateId
|
||||
b := index.QueryFirstResult(indexDb.GetFoundBlocks("", 1))
|
||||
if b == nil {
|
||||
writer.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
http.Redirect(writer, request, fmt.Sprintf("/api/block_by_id/%s%s?%s", lastFoundHash.String(), mux.Vars(request)["kind"], request.URL.RawQuery), http.StatusFound)
|
||||
http.Redirect(writer, request, fmt.Sprintf("/api/block_by_id/%s%s?%s", b.MainBlock.SideTemplateId.String(), mux.Vars(request)["kind"], request.URL.RawQuery), http.StatusFound)
|
||||
})
|
||||
serveMux.HandleFunc("/api/redirect/tip{kind:|/raw|/info}", func(writer http.ResponseWriter, request *http.Request) {
|
||||
http.Redirect(writer, request, fmt.Sprintf("/api/block_by_id/%s%s?%s", indexDb.GetSideBlockTip().TemplateId.String(), mux.Vars(request)["kind"], request.URL.RawQuery), http.StatusFound)
|
||||
|
@ -1150,17 +1189,31 @@ func main() {
|
|||
limit = 50
|
||||
}
|
||||
|
||||
var result []*index.FoundBlock
|
||||
|
||||
if minerId != 0 {
|
||||
result = fillFoundBlockResult(params, indexDb.GetFoundBlocks("WHERE miner = $1", limit, minerId))
|
||||
} else {
|
||||
result = fillFoundBlockResult(params, indexDb.GetFoundBlocks("", limit))
|
||||
}
|
||||
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_ = httputils.EncodeJson(request, writer, result)
|
||||
|
||||
var result index.QueryIterator[index.FoundBlock]
|
||||
var err error
|
||||
|
||||
if minerId != 0 {
|
||||
result, err = indexDb.GetFoundBlocks("WHERE miner = $1", limit, minerId)
|
||||
} else {
|
||||
result, err = indexDb.GetFoundBlocks("", limit)
|
||||
}
|
||||
|
||||
fillMiner := !params.Has("noMiner")
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer result.Close()
|
||||
_ = httputils.StreamJsonIterator(request, writer, func() (int, *index.FoundBlock) {
|
||||
i, v := result.Next()
|
||||
if v != nil {
|
||||
v = fillFoundBlockResult(fillMiner, v)
|
||||
}
|
||||
return i, v
|
||||
})
|
||||
})
|
||||
|
||||
serveMux.HandleFunc("/api/side_blocks", func(writer http.ResponseWriter, request *http.Request) {
|
||||
|
@ -1223,8 +1276,22 @@ func main() {
|
|||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
|
||||
result := fillSideBlockChannelResult(params, indexDb.GetShares(limit, minerId, onlyBlocks, inclusion))
|
||||
_ = httputils.StreamJsonSlice(request, writer, result)
|
||||
fillUncles := !params.Has("noUncles")
|
||||
fillMined := !params.Has("noMainStatus")
|
||||
fillMiner := !params.Has("noMiner")
|
||||
|
||||
result, err := indexDb.GetShares(limit, minerId, onlyBlocks, inclusion)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer result.Close()
|
||||
_ = httputils.StreamJsonIterator(request, writer, func() (int, *index.SideBlock) {
|
||||
i, v := result.Next()
|
||||
if v != nil {
|
||||
v = fillSideBlockResult(fillUncles, fillMined, fillMiner, v)
|
||||
}
|
||||
return i, v
|
||||
})
|
||||
})
|
||||
|
||||
serveMux.HandleFunc("/api/shares", func(writer http.ResponseWriter, request *http.Request) {
|
||||
|
@ -1280,8 +1347,23 @@ func main() {
|
|||
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
result := fillSideBlockChannelResult(params, indexDb.GetShares(limit, minerId, onlyBlocks, index.InclusionInVerifiedChain))
|
||||
_ = httputils.StreamJsonSlice(request, writer, result)
|
||||
|
||||
fillUncles := !params.Has("noUncles")
|
||||
fillMined := !params.Has("noMainStatus")
|
||||
fillMiner := !params.Has("noMiner")
|
||||
|
||||
result, err := indexDb.GetShares(limit, minerId, onlyBlocks, index.InclusionInVerifiedChain)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer result.Close()
|
||||
_ = httputils.StreamJsonIterator(request, writer, func() (int, *index.SideBlock) {
|
||||
i, v := result.Next()
|
||||
if v != nil {
|
||||
v = fillSideBlockResult(fillUncles, fillMined, fillMiner, v)
|
||||
}
|
||||
return i, v
|
||||
})
|
||||
})
|
||||
|
||||
serveMux.HandleFunc("/api/main_block_by_{by:id|height}/{block:[0-9a-f]+|[0-9]+}", func(writer http.ResponseWriter, request *http.Request) {
|
||||
|
@ -1353,7 +1435,7 @@ func main() {
|
|||
if id, err := types.HashFromString(mux.Vars(request)["block"]); err == nil {
|
||||
if b := indexDb.GetTipSideBlockByTemplateId(id); b != nil {
|
||||
block = b
|
||||
} else if bs := index.ChanToSlice(indexDb.GetSideBlocksByTemplateId(id)); len(bs) != 0 {
|
||||
} else if bs := index.QueryIterateToSlice(indexDb.GetSideBlocksByTemplateId(id)); len(bs) != 0 {
|
||||
block = bs[0]
|
||||
} else if b = indexDb.GetSideBlockByMainId(id); b != nil {
|
||||
block = b
|
||||
|
@ -1448,7 +1530,13 @@ func main() {
|
|||
case "/payouts":
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_ = httputils.StreamJsonSlice(request, writer, indexDb.GetPayoutsBySideBlock(block))
|
||||
|
||||
result, err := indexDb.GetPayoutsBySideBlock(block)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer result.Close()
|
||||
_ = httputils.StreamJsonIterator(request, writer, result.Next)
|
||||
case "/coinbase":
|
||||
foundBlock := indexDb.GetMainBlockById(block.MainId)
|
||||
if foundBlock == nil {
|
||||
|
@ -1506,7 +1594,13 @@ func main() {
|
|||
} else {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_ = httputils.EncodeJson(request, writer, fillMainCoinbaseOutputs(params, indexDb.GetMainCoinbaseOutputs(foundBlock.CoinbaseId)))
|
||||
|
||||
mainOutputs, err := indexDb.GetMainCoinbaseOutputs(foundBlock.CoinbaseId)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer mainOutputs.Close()
|
||||
_ = httputils.EncodeJson(request, writer, fillMainCoinbaseOutputs(params, index.IterateToSliceWithoutPointer[index.MainCoinbaseOutput](mainOutputs)))
|
||||
}
|
||||
default:
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
@ -1688,7 +1782,13 @@ func main() {
|
|||
}
|
||||
|
||||
blocks := make([]poolBlock, 0, 200)
|
||||
for _, b := range indexDb.GetFoundBlocks("", 200) {
|
||||
|
||||
result, err := indexDb.GetFoundBlocks("", 200)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
index.QueryIterate(result, func(_ int, b *index.FoundBlock) (stop bool) {
|
||||
blocks = append(blocks, poolBlock{
|
||||
Height: b.MainBlock.Height,
|
||||
Hash: b.MainBlock.Id,
|
||||
|
@ -1696,7 +1796,8 @@ func main() {
|
|||
TotalHashes: b.CumulativeDifficulty.Lo,
|
||||
Timestamp: b.MainBlock.Timestamp,
|
||||
})
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
|
@ -1724,7 +1825,8 @@ func main() {
|
|||
poolInfo := lastPoolInfo.Load()
|
||||
|
||||
var lastBlockFound, lastBlockFoundTime uint64
|
||||
for _, b := range indexDb.GetFoundBlocks("", 1) {
|
||||
b := index.QueryFirstResult(indexDb.GetFoundBlocks("", 1))
|
||||
if b != nil {
|
||||
lastBlockFound = b.MainBlock.Height
|
||||
lastBlockFoundTime = b.MainBlock.Timestamp
|
||||
}
|
||||
|
@ -1770,7 +1872,8 @@ func main() {
|
|||
var lastBlockFound, lastBlockFoundTime uint64
|
||||
var lastBlockFoundHash types.Hash
|
||||
var lastBlockCumulativeDifficulty types.Difficulty
|
||||
for _, b := range indexDb.GetFoundBlocks("", 1) {
|
||||
b := index.QueryFirstResult(indexDb.GetFoundBlocks("", 1))
|
||||
if b != nil {
|
||||
lastBlockFound = b.MainBlock.Height
|
||||
lastBlockFoundTime = b.MainBlock.Timestamp
|
||||
lastBlockFoundHash = b.MainBlock.Id
|
||||
|
|
|
@ -35,7 +35,7 @@ func Outputs(p2api *api.P2PoolApi, indexDb *index.Index, tip *index.SideBlock, d
|
|||
|
||||
poolBlock := p2api.LightByMainId(tip.MainId)
|
||||
if poolBlock != nil {
|
||||
window := index.ChanToSlice(indexDb.GetSideBlocksInPPLNSWindow(tip))
|
||||
window := index.QueryIterateToSlice(indexDb.GetSideBlocksInPPLNSWindow(tip))
|
||||
if len(window) == 0 {
|
||||
return nil, 0
|
||||
}
|
||||
|
@ -60,26 +60,32 @@ func Outputs(p2api *api.P2PoolApi, indexDb *index.Index, tip *index.SideBlock, d
|
|||
}
|
||||
return getByTemplateIdFull(h)
|
||||
}
|
||||
getUnclesOf := func(h types.Hash) chan *index.SideBlock {
|
||||
result := make(chan *index.SideBlock)
|
||||
|
||||
getUnclesOf := func(h types.Hash) index.QueryIterator[index.SideBlock] {
|
||||
parentEffectiveHeight := window[hintIndex].EffectiveHeight
|
||||
if window[hintIndex].TemplateId != h {
|
||||
parentEffectiveHeight = 0
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer close(result)
|
||||
for _, b := range window[hintIndex:] {
|
||||
if b.UncleOf == h {
|
||||
result <- b
|
||||
startIndex := 0
|
||||
|
||||
return &index.FakeQueryResult[index.SideBlock]{
|
||||
NextFunction: func() (int, *index.SideBlock) {
|
||||
for _, b := range window[startIndex+hintIndex:] {
|
||||
if b.UncleOf == h {
|
||||
startIndex++
|
||||
return startIndex, b
|
||||
}
|
||||
startIndex++
|
||||
|
||||
if parentEffectiveHeight != 0 && b.EffectiveHeight < parentEffectiveHeight {
|
||||
//early exit
|
||||
break
|
||||
}
|
||||
}
|
||||
if parentEffectiveHeight != 0 && b.EffectiveHeight < parentEffectiveHeight {
|
||||
//early exit
|
||||
break
|
||||
}
|
||||
}
|
||||
}()
|
||||
return result
|
||||
return 0, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return index.CalculateOutputs(indexDb,
|
||||
|
@ -104,7 +110,7 @@ func PayoutHint(p2api *api.P2PoolApi, indexDb *index.Index, tip *index.SideBlock
|
|||
if tip == nil {
|
||||
return nil, 0
|
||||
}
|
||||
window := index.ChanToSlice(indexDb.GetSideBlocksInPPLNSWindow(tip))
|
||||
window := index.QueryIterateToSlice(indexDb.GetSideBlocksInPPLNSWindow(tip))
|
||||
if len(window) == 0 {
|
||||
return nil, 0
|
||||
}
|
||||
|
@ -130,26 +136,31 @@ func PayoutHint(p2api *api.P2PoolApi, indexDb *index.Index, tip *index.SideBlock
|
|||
}
|
||||
return getByTemplateIdFull(h)
|
||||
}
|
||||
getUnclesOf := func(h types.Hash) chan *index.SideBlock {
|
||||
result := make(chan *index.SideBlock)
|
||||
getUnclesOf := func(h types.Hash) index.QueryIterator[index.SideBlock] {
|
||||
parentEffectiveHeight := window[hintIndex].EffectiveHeight
|
||||
if window[hintIndex].TemplateId != h {
|
||||
parentEffectiveHeight = 0
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer close(result)
|
||||
for _, b := range window[hintIndex:] {
|
||||
if b.UncleOf == h {
|
||||
result <- b
|
||||
startIndex := 0
|
||||
|
||||
return &index.FakeQueryResult[index.SideBlock]{
|
||||
NextFunction: func() (int, *index.SideBlock) {
|
||||
for _, b := range window[startIndex+hintIndex:] {
|
||||
if b.UncleOf == h {
|
||||
startIndex++
|
||||
return startIndex, b
|
||||
}
|
||||
startIndex++
|
||||
|
||||
if parentEffectiveHeight != 0 && b.EffectiveHeight < parentEffectiveHeight {
|
||||
//early exit
|
||||
break
|
||||
}
|
||||
}
|
||||
if parentEffectiveHeight != 0 && b.EffectiveHeight < parentEffectiveHeight {
|
||||
//early exit
|
||||
break
|
||||
}
|
||||
}
|
||||
}()
|
||||
return result
|
||||
return 0, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
blockDepth, err := index.BlocksInPPLNSWindow(tip, indexDb.Consensus(), p2api.MainDifficultyByHeight, getByTemplateId, getUnclesOf, func(b *index.SideBlock, weight types.Difficulty) {
|
||||
|
|
|
@ -244,7 +244,7 @@ func main() {
|
|||
if r.startHeight-r.tipHeight > consensus.ChainWindowSize*2 &&
|
||||
indexDb.GetTipSideBlockByTemplateId(bestTip.SideTemplateId(consensus)) != nil &&
|
||||
indexDb.GetTipSideBlockByHeight(r.startHeight+consensus.ChainWindowSize+1) != nil &&
|
||||
len(index.ChanToSlice(indexDb.GetSideBlocksByHeight(r.startHeight))) != 0 {
|
||||
index.QueryHasResults(indexDb.GetSideBlocksByHeight(r.startHeight)) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -337,9 +337,11 @@ func main() {
|
|||
}
|
||||
}
|
||||
|
||||
for mb := range indexDb.GetMainBlocksByQuery("WHERE side_template_id IS NOT NULL ORDER BY height DESC;") {
|
||||
mainBlocks, _ := indexDb.GetMainBlocksByQuery("WHERE side_template_id IS NOT NULL ORDER BY height DESC;")
|
||||
index.QueryIterate(mainBlocks, func(_ int, mb *index.MainBlock) (stop bool) {
|
||||
if err := utils2.FindAndInsertMainHeaderOutputs(mb, indexDb, client.GetDefaultClient(), getDifficultyByHeight, getByTemplateIdDirect, archiveCache.LoadByMainId, archiveCache.LoadByMainChainHeight, processBlock); err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
|
|
@ -193,14 +193,19 @@ func setupEventHandler(p2api *api.P2PoolApi, indexDb *index.Index) {
|
|||
break
|
||||
}
|
||||
sideBlockBuffer.Insert(cur)
|
||||
for u := range indexDb.GetSideBlocksByUncleOfId(cur.TemplateId) {
|
||||
index.QueryIterate(indexDb.GetSideBlocksByUncleOfId(cur.TemplateId), func(_ int, u *index.SideBlock) (stop bool) {
|
||||
sideBlockBuffer.Insert(u)
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
for _, b := range indexDb.GetFoundBlocks("", 5) {
|
||||
foundBlockBuffer.Insert(b)
|
||||
}
|
||||
func() {
|
||||
foundBlocks, _ := indexDb.GetFoundBlocks("", 5)
|
||||
index.QueryIterate(foundBlocks, func(_ int, b *index.FoundBlock) (stop bool) {
|
||||
foundBlockBuffer.Insert(b)
|
||||
return false
|
||||
})
|
||||
}()
|
||||
|
||||
fillMainCoinbaseOutputs := func(outputs index.MainCoinbaseOutputs) (result index.MainCoinbaseOutputs) {
|
||||
result = make(index.MainCoinbaseOutputs, 0, len(outputs))
|
||||
|
@ -250,14 +255,15 @@ func setupEventHandler(p2api *api.P2PoolApi, indexDb *index.Index) {
|
|||
}
|
||||
}
|
||||
|
||||
for u := range indexDb.GetSideBlocksByUncleOfId(sideBlock.TemplateId) {
|
||||
index.QueryIterate(indexDb.GetSideBlocksByUncleOfId(sideBlock.TemplateId), func(_ int, u *index.SideBlock) (stop bool) {
|
||||
sideBlock.Uncles = append(sideBlock.Uncles, index.SideBlockUncleEntry{
|
||||
TemplateId: u.TemplateId,
|
||||
Miner: u.Miner,
|
||||
SideHeight: u.SideHeight,
|
||||
Difficulty: u.Difficulty,
|
||||
})
|
||||
}
|
||||
return false
|
||||
})
|
||||
return sideBlock
|
||||
|
||||
}
|
||||
|
@ -278,13 +284,15 @@ func setupEventHandler(p2api *api.P2PoolApi, indexDb *index.Index) {
|
|||
break
|
||||
}
|
||||
var pushedNew bool
|
||||
for u := range indexDb.GetSideBlocksByUncleOfId(cur.TemplateId) {
|
||||
|
||||
index.QueryIterate(indexDb.GetSideBlocksByUncleOfId(cur.TemplateId), func(_ int, u *index.SideBlock) (stop bool) {
|
||||
if sideBlockBuffer.Insert(u) {
|
||||
//first time seen
|
||||
pushedNew = true
|
||||
blocksToReport = append(blocksToReport, fillSideBlockResult(mainTip, u))
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
if sideBlockBuffer.Insert(cur) {
|
||||
//first time seen
|
||||
pushedNew = true
|
||||
|
@ -359,12 +367,17 @@ func setupEventHandler(p2api *api.P2PoolApi, indexDb *index.Index) {
|
|||
}
|
||||
}
|
||||
}
|
||||
for _, b := range indexDb.GetFoundBlocks("", 5) {
|
||||
if foundBlockBuffer.Insert(b) {
|
||||
//first time seen
|
||||
blocksToReport = append(blocksToReport, fillFoundBlockResult(b))
|
||||
}
|
||||
}
|
||||
|
||||
func() {
|
||||
foundBlocks, _ := indexDb.GetFoundBlocks("", 5)
|
||||
index.QueryIterate(foundBlocks, func(_ int, b *index.FoundBlock) (stop bool) {
|
||||
if foundBlockBuffer.Insert(b) {
|
||||
//first time seen
|
||||
blocksToReport = append(blocksToReport, fillFoundBlockResult(b))
|
||||
}
|
||||
return false
|
||||
})
|
||||
}()
|
||||
}()
|
||||
|
||||
//sort for proper order
|
||||
|
@ -431,7 +444,15 @@ func setupEventHandler(p2api *api.P2PoolApi, indexDb *index.Index) {
|
|||
}
|
||||
}
|
||||
for _, b := range blocksToReport {
|
||||
coinbaseOutputs := fillMainCoinbaseOutputs(indexDb.GetMainCoinbaseOutputs(b.MainBlock.CoinbaseId))
|
||||
coinbaseOutputs := func() index.MainCoinbaseOutputs {
|
||||
mainOutputs, err := indexDb.GetMainCoinbaseOutputs(b.MainBlock.CoinbaseId)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer mainOutputs.Close()
|
||||
return fillMainCoinbaseOutputs(index.IterateToSliceWithoutPointer[index.MainCoinbaseOutput](mainOutputs))
|
||||
}()
|
||||
|
||||
if len(coinbaseOutputs) == 0 {
|
||||
//report next time
|
||||
foundBlockBuffer.Remove(b)
|
||||
|
|
|
@ -16,7 +16,7 @@ func EncodeJson(r *http.Request, writer io.Writer, d any) error {
|
|||
}
|
||||
|
||||
// StreamJsonSlice Streams a channel of values into a JSON list via a writer.
|
||||
func StreamJsonSlice[T any](r *http.Request, writer io.Writer, stream chan T) error {
|
||||
func StreamJsonSlice[T any](r *http.Request, writer io.Writer, stream <-chan T) error {
|
||||
encoder := utils.NewJSONEncoder(writer)
|
||||
if strings.Index(strings.ToLower(r.Header.Get("user-agent")), "mozilla") != -1 {
|
||||
encoder.SetIndent("", " ")
|
||||
|
@ -46,3 +46,35 @@ func StreamJsonSlice[T any](r *http.Request, writer io.Writer, stream chan T) er
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// StreamJsonIterator Streams an iterator of values into a JSON list via a writer.
|
||||
func StreamJsonIterator[T any](r *http.Request, writer io.Writer, next func() (int, *T)) error {
|
||||
encoder := utils.NewJSONEncoder(writer)
|
||||
if strings.Index(strings.ToLower(r.Header.Get("user-agent")), "mozilla") != -1 {
|
||||
encoder.SetIndent("", " ")
|
||||
}
|
||||
// Write start of JSON list
|
||||
_, _ = writer.Write([]byte{'[', 0xa})
|
||||
var count uint64
|
||||
defer func() {
|
||||
// Write end of JSON list
|
||||
_, _ = writer.Write([]byte{0xa, ']'})
|
||||
}()
|
||||
|
||||
for {
|
||||
_, v := next()
|
||||
if v == nil {
|
||||
break
|
||||
}
|
||||
if count > 0 {
|
||||
// Write separator between list fields
|
||||
_, _ = writer.Write([]byte{',', 0xa})
|
||||
}
|
||||
if err := encoder.EncodeWithOption(v, utils.JsonEncodeOptions...); err != nil {
|
||||
return err
|
||||
}
|
||||
count++
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ import (
|
|||
"log"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime/pprof"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
|
@ -364,28 +363,18 @@ func (i *Index) SetMinerAlias(minerId uint64, alias string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
type RowScanInterface interface {
|
||||
Scan(dest ...any) error
|
||||
}
|
||||
|
||||
type Scannable interface {
|
||||
ScanFromRow(i *Index, row RowScanInterface) error
|
||||
}
|
||||
|
||||
func (i *Index) GetView(k string) string {
|
||||
return i.views[k]
|
||||
}
|
||||
|
||||
func (i *Index) Query(query string, callback func(row RowScanInterface) error, params ...any) error {
|
||||
var parentError error
|
||||
pprof.Do(context.Background(), pprof.Labels("query", query), func(ctx context.Context) {
|
||||
if stmt, err := i.handle.Prepare(query); err != nil {
|
||||
parentError = err
|
||||
} else {
|
||||
defer stmt.Close()
|
||||
parentError = i.QueryStatement(stmt, callback, params...)
|
||||
}
|
||||
})
|
||||
if stmt, err := i.handle.Prepare(query); err != nil {
|
||||
parentError = err
|
||||
} else {
|
||||
defer stmt.Close()
|
||||
parentError = i.QueryStatement(stmt, callback, params...)
|
||||
}
|
||||
return parentError
|
||||
}
|
||||
|
||||
|
@ -409,71 +398,39 @@ func (i *Index) PrepareSideBlocksByQueryStatement(where string) (stmt *sql.Stmt,
|
|||
return i.handle.Prepare(fmt.Sprintf("SELECT "+SideBlockSelectFields+" FROM side_blocks %s;", where))
|
||||
}
|
||||
|
||||
func (i *Index) GetSideBlocksByQuery(where string, params ...any) chan *SideBlock {
|
||||
func (i *Index) GetSideBlocksByQuery(where string, params ...any) (QueryIterator[SideBlock], error) {
|
||||
if stmt, err := i.PrepareSideBlocksByQueryStatement(where); err != nil {
|
||||
returnChannel := make(chan *SideBlock, 1)
|
||||
close(returnChannel)
|
||||
return returnChannel
|
||||
return nil, err
|
||||
} else {
|
||||
return i.getSideBlocksByQueryStatement(where, stmt, params...)
|
||||
return i.getSideBlocksByQueryStatement(stmt, params...)
|
||||
}
|
||||
}
|
||||
|
||||
func (i *Index) getSideBlocksByQueryStatement(sourceQuery string, stmt *sql.Stmt, params ...any) chan *SideBlock {
|
||||
returnChannel := make(chan *SideBlock, 1)
|
||||
go func() {
|
||||
func (i *Index) getSideBlocksByQueryStatement(stmt *sql.Stmt, params ...any) (QueryIterator[SideBlock], error) {
|
||||
if r, err := queryStatement[SideBlock](i, stmt, params...); err != nil {
|
||||
defer stmt.Close()
|
||||
defer close(returnChannel)
|
||||
|
||||
pprof.Do(context.Background(), pprof.Labels("sourceQuery", sourceQuery), func(ctx context.Context) {
|
||||
err := i.QueryStatement(stmt, func(row RowScanInterface) (err error) {
|
||||
b := &SideBlock{}
|
||||
if err = b.ScanFromRow(i, row); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
returnChannel <- b
|
||||
|
||||
return nil
|
||||
}, params...)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
})
|
||||
}()
|
||||
|
||||
return returnChannel
|
||||
return nil, err
|
||||
} else {
|
||||
r.closer = func() {
|
||||
stmt.Close()
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (i *Index) GetSideBlocksByQueryStatement(source string, stmt *sql.Stmt, params ...any) chan *SideBlock {
|
||||
returnChannel := make(chan *SideBlock, 1)
|
||||
go func() {
|
||||
defer close(returnChannel)
|
||||
pprof.Do(context.Background(), pprof.Labels("source", source), func(ctx context.Context) {
|
||||
err := i.QueryStatement(stmt, func(row RowScanInterface) (err error) {
|
||||
b := &SideBlock{}
|
||||
if err = b.ScanFromRow(i, row); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
returnChannel <- b
|
||||
|
||||
return nil
|
||||
}, params...)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
})
|
||||
}()
|
||||
|
||||
return returnChannel
|
||||
func (i *Index) GetSideBlocksByQueryStatement(stmt *sql.Stmt, params ...any) (QueryIterator[SideBlock], error) {
|
||||
if r, err := queryStatement[SideBlock](i, stmt, params...); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return r, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (i *Index) PrepareMainBlocksByQueryStatement(where string) (stmt *sql.Stmt, err error) {
|
||||
return i.handle.Prepare(fmt.Sprintf("SELECT "+MainBlockSelectFields+" FROM main_blocks %s;", where))
|
||||
}
|
||||
|
||||
func (i *Index) GetShares(limit, minerId uint64, onlyBlocks bool, inclusion BlockInclusion) chan *SideBlock {
|
||||
func (i *Index) GetShares(limit, minerId uint64, onlyBlocks bool, inclusion BlockInclusion) (QueryIterator[SideBlock], error) {
|
||||
if limit == 0 {
|
||||
if minerId != 0 {
|
||||
if onlyBlocks {
|
||||
|
@ -505,164 +462,222 @@ func (i *Index) GetShares(limit, minerId uint64, onlyBlocks bool, inclusion Bloc
|
|||
}
|
||||
}
|
||||
|
||||
func (i *Index) GetFoundBlocks(where string, limit uint64, params ...any) []*FoundBlock {
|
||||
result := make([]*FoundBlock, 0, limit)
|
||||
if err := i.Query(fmt.Sprintf("SELECT * FROM "+i.views["found_main_blocks"]+" %s ORDER BY main_height DESC LIMIT %d;", where, limit), func(row RowScanInterface) error {
|
||||
var d FoundBlock
|
||||
|
||||
if err := d.ScanFromRow(i, row); err != nil {
|
||||
return err
|
||||
func (i *Index) GetFoundBlocks(where string, limit uint64, params ...any) (QueryIterator[FoundBlock], error) {
|
||||
if stmt, err := i.handle.Prepare(fmt.Sprintf("SELECT * FROM "+i.views["found_main_blocks"]+" %s ORDER BY main_height DESC LIMIT %d;", where, limit)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
if r, err := queryStatement[FoundBlock](i, stmt, params...); err != nil {
|
||||
defer stmt.Close()
|
||||
return nil, err
|
||||
} else {
|
||||
r.closer = func() {
|
||||
stmt.Close()
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
result = append(result, &d)
|
||||
return nil
|
||||
}, params...); err != nil {
|
||||
return nil
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (i *Index) GetMainBlocksByQuery(where string, params ...any) chan *MainBlock {
|
||||
func (i *Index) GetMainBlocksByQuery(where string, params ...any) (QueryIterator[MainBlock], error) {
|
||||
if stmt, err := i.PrepareMainBlocksByQueryStatement(where); err != nil {
|
||||
returnChannel := make(chan *MainBlock, 1)
|
||||
close(returnChannel)
|
||||
return returnChannel
|
||||
return nil, err
|
||||
} else {
|
||||
return i.getMainBlocksByQueryStatement(stmt, params...)
|
||||
}
|
||||
}
|
||||
|
||||
func (i *Index) getMainBlocksByQueryStatement(stmt *sql.Stmt, params ...any) chan *MainBlock {
|
||||
returnChannel := make(chan *MainBlock, 1)
|
||||
go func() {
|
||||
func (i *Index) getMainBlocksByQueryStatement(stmt *sql.Stmt, params ...any) (QueryIterator[MainBlock], error) {
|
||||
if r, err := queryStatement[MainBlock](i, stmt, params...); err != nil {
|
||||
defer stmt.Close()
|
||||
defer close(returnChannel)
|
||||
err := i.QueryStatement(stmt, func(row RowScanInterface) (err error) {
|
||||
b := &MainBlock{}
|
||||
if err = b.ScanFromRow(i, row); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
returnChannel <- b
|
||||
|
||||
return nil
|
||||
}, params...)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return nil, err
|
||||
} else {
|
||||
r.closer = func() {
|
||||
stmt.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
return returnChannel
|
||||
return r, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (i *Index) GetMainBlocksByQueryStatement(stmt *sql.Stmt, params ...any) chan *MainBlock {
|
||||
returnChannel := make(chan *MainBlock, 1)
|
||||
go func() {
|
||||
defer close(returnChannel)
|
||||
err := i.QueryStatement(stmt, func(row RowScanInterface) (err error) {
|
||||
b := &MainBlock{}
|
||||
if err = b.ScanFromRow(i, row); err != nil {
|
||||
return err
|
||||
}
|
||||
func (i *Index) GetMainBlocksByQueryStatement(stmt *sql.Stmt, params ...any) (QueryIterator[MainBlock], error) {
|
||||
if r, err := queryStatement[MainBlock](i, stmt, params...); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return r, nil
|
||||
}
|
||||
}
|
||||
|
||||
returnChannel <- b
|
||||
|
||||
return nil
|
||||
}, params...)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
func (i *Index) GetMainBlockById(id types.Hash) (b *MainBlock) {
|
||||
if r, err := i.GetMainBlocksByQueryStatement(i.statements.GetMainBlockById, id[:]); err != nil {
|
||||
log.Print(err)
|
||||
} else {
|
||||
defer r.Close()
|
||||
if _, b = r.Next(); b == nil && r.Err() != nil {
|
||||
log.Print(r.Err())
|
||||
}
|
||||
}()
|
||||
|
||||
return returnChannel
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (i *Index) GetMainBlockById(id types.Hash) *MainBlock {
|
||||
r := i.GetMainBlocksByQueryStatement(i.statements.GetMainBlockById, id[:])
|
||||
defer ChanConsume(r)
|
||||
return <-r
|
||||
func (i *Index) GetMainBlockTip() (b *MainBlock) {
|
||||
if r, err := i.GetMainBlocksByQueryStatement(i.statements.TipMainBlock); err != nil {
|
||||
log.Print(err)
|
||||
} else {
|
||||
defer r.Close()
|
||||
if _, b = r.Next(); b == nil && r.Err() != nil {
|
||||
log.Print(r.Err())
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (i *Index) GetMainBlockTip() *MainBlock {
|
||||
r := i.GetMainBlocksByQueryStatement(i.statements.TipMainBlock)
|
||||
defer ChanConsume(r)
|
||||
return <-r
|
||||
func (i *Index) GetMainBlockByCoinbaseId(id types.Hash) (b *MainBlock) {
|
||||
if r, err := i.GetMainBlocksByQuery("WHERE coinbase_id = $1;", id[:]); err != nil {
|
||||
log.Print(err)
|
||||
} else {
|
||||
defer r.Close()
|
||||
if _, b = r.Next(); b == nil && r.Err() != nil {
|
||||
log.Print(r.Err())
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (i *Index) GetMainBlockByCoinbaseId(id types.Hash) *MainBlock {
|
||||
r := i.GetMainBlocksByQuery("WHERE coinbase_id = $1;", id[:])
|
||||
defer ChanConsume(r)
|
||||
return <-r
|
||||
func (i *Index) GetMainBlockByGlobalOutputIndex(globalOutputIndex uint64) (b *MainBlock) {
|
||||
if r, err := i.GetMainBlocksByQuery("WHERE coinbase_id = (SELECT id FROM main_coinbase_outputs WHERE global_output_index = $1 LIMIT 1);", globalOutputIndex); err != nil {
|
||||
log.Print(err)
|
||||
} else {
|
||||
defer r.Close()
|
||||
if _, b = r.Next(); b == nil && r.Err() != nil {
|
||||
log.Print(r.Err())
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (i *Index) GetMainBlockByGlobalOutputIndex(globalOutputIndex uint64) *MainBlock {
|
||||
r := i.GetMainBlocksByQuery("WHERE coinbase_id = (SELECT id FROM main_coinbase_outputs WHERE global_output_index = $1 LIMIT 1);", globalOutputIndex)
|
||||
defer ChanConsume(r)
|
||||
return <-r
|
||||
func (i *Index) GetMainBlockByHeight(height uint64) (b *MainBlock) {
|
||||
if r, err := i.GetMainBlocksByQueryStatement(i.statements.GetMainBlockByHeight, height); err != nil {
|
||||
log.Print(err)
|
||||
} else {
|
||||
defer r.Close()
|
||||
if _, b = r.Next(); b == nil && r.Err() != nil {
|
||||
log.Print(r.Err())
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (i *Index) GetMainBlockByHeight(height uint64) *MainBlock {
|
||||
r := i.GetMainBlocksByQueryStatement(i.statements.GetMainBlockByHeight, height)
|
||||
defer ChanConsume(r)
|
||||
return <-r
|
||||
func (i *Index) GetSideBlockByMainId(id types.Hash) (b *SideBlock) {
|
||||
if r, err := i.GetSideBlocksByQueryStatement(i.statements.GetSideBlockByMainId, id[:]); err != nil {
|
||||
log.Print(err)
|
||||
} else {
|
||||
defer r.Close()
|
||||
if _, b = r.Next(); b == nil && r.Err() != nil {
|
||||
log.Print(r.Err())
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (i *Index) GetSideBlockByMainId(id types.Hash) *SideBlock {
|
||||
r := i.GetSideBlocksByQueryStatement("GetSideBlockByMainId", i.statements.GetSideBlockByMainId, id[:])
|
||||
defer ChanConsume(r)
|
||||
return <-r
|
||||
func (i *Index) GetSideBlocksByTemplateId(id types.Hash) QueryIterator[SideBlock] {
|
||||
if r, err := i.GetSideBlocksByQuery("WHERE template_id = $1;", id[:]); err != nil {
|
||||
log.Print(err)
|
||||
} else {
|
||||
return r
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Index) GetSideBlocksByTemplateId(id types.Hash) chan *SideBlock {
|
||||
return i.GetSideBlocksByQuery("WHERE template_id = $1;", id[:])
|
||||
func (i *Index) GetSideBlocksByUncleOfId(id types.Hash) QueryIterator[SideBlock] {
|
||||
if r, err := i.GetSideBlocksByQueryStatement(i.statements.GetSideBlockByUncleId, id[:]); err != nil {
|
||||
log.Print(err)
|
||||
} else {
|
||||
return r
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Index) GetSideBlocksByUncleOfId(id types.Hash) chan *SideBlock {
|
||||
return i.GetSideBlocksByQueryStatement("GetSideBlocksByUncleOfId", i.statements.GetSideBlockByUncleId, id[:])
|
||||
func (i *Index) GetTipSideBlockByTemplateId(id types.Hash) (b *SideBlock) {
|
||||
if r, err := i.GetSideBlocksByQueryStatement(i.statements.TipSideBlocksTemplateId, id[:], InclusionInVerifiedChain); err != nil {
|
||||
log.Print(err)
|
||||
} else {
|
||||
defer r.Close()
|
||||
if _, b = r.Next(); b == nil && r.Err() != nil {
|
||||
log.Print(r.Err())
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (i *Index) GetTipSideBlockByTemplateId(id types.Hash) *SideBlock {
|
||||
r := i.GetSideBlocksByQueryStatement("GetTipSideBlockByTemplateId", i.statements.TipSideBlocksTemplateId, id[:], InclusionInVerifiedChain)
|
||||
defer ChanConsume(r)
|
||||
return <-r
|
||||
func (i *Index) GetSideBlocksByMainHeight(height uint64) QueryIterator[SideBlock] {
|
||||
if r, err := i.GetSideBlocksByQuery("WHERE main_height = $1;", height); err != nil {
|
||||
log.Print(err)
|
||||
} else {
|
||||
return r
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Index) GetSideBlocksByMainHeight(height uint64) chan *SideBlock {
|
||||
return i.GetSideBlocksByQuery("WHERE main_height = $1;", height)
|
||||
func (i *Index) GetSideBlocksByHeight(height uint64) QueryIterator[SideBlock] {
|
||||
if r, err := i.GetSideBlocksByQuery("WHERE side_height = $1;", height); err != nil {
|
||||
log.Print(err)
|
||||
} else {
|
||||
return r
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Index) GetSideBlocksByHeight(height uint64) chan *SideBlock {
|
||||
return i.GetSideBlocksByQuery("WHERE side_height = $1;", height)
|
||||
func (i *Index) GetTipSideBlockByHeight(height uint64) (b *SideBlock) {
|
||||
if r, err := i.GetSideBlocksByQuery("WHERE side_height = $1 AND effective_height = $2 AND inclusion = $3;", height, height, InclusionInVerifiedChain); err != nil {
|
||||
log.Print(err)
|
||||
} else {
|
||||
defer r.Close()
|
||||
if _, b = r.Next(); b == nil && r.Err() != nil {
|
||||
log.Print(r.Err())
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (i *Index) GetTipSideBlockByHeight(height uint64) *SideBlock {
|
||||
r := i.GetSideBlocksByQuery("WHERE side_height = $1 AND effective_height = $2 AND inclusion = $3;", height, height, InclusionInVerifiedChain)
|
||||
defer ChanConsume(r)
|
||||
return <-r
|
||||
func (i *Index) GetSideBlockTip() (b *SideBlock) {
|
||||
if r, err := i.GetSideBlocksByQueryStatement(i.statements.TipSideBlock, InclusionInVerifiedChain); err != nil {
|
||||
log.Print(err)
|
||||
} else {
|
||||
defer r.Close()
|
||||
if _, b = r.Next(); b == nil && r.Err() != nil {
|
||||
log.Print(r.Err())
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (i *Index) GetSideBlockTip() *SideBlock {
|
||||
r := i.GetSideBlocksByQueryStatement("GetSideBlockTip", i.statements.TipSideBlock, InclusionInVerifiedChain)
|
||||
defer ChanConsume(r)
|
||||
return <-r
|
||||
}
|
||||
|
||||
func (i *Index) GetSideBlocksInPPLNSWindow(tip *SideBlock) chan *SideBlock {
|
||||
func (i *Index) GetSideBlocksInPPLNSWindow(tip *SideBlock) QueryIterator[SideBlock] {
|
||||
return i.GetSideBlocksInWindow(tip.SideHeight, uint64(tip.WindowDepth))
|
||||
}
|
||||
|
||||
func (i *Index) GetSideBlocksInWindow(startHeight, windowSize uint64) chan *SideBlock {
|
||||
func (i *Index) GetSideBlocksInWindow(startHeight, windowSize uint64) QueryIterator[SideBlock] {
|
||||
if startHeight < windowSize {
|
||||
windowSize = startHeight
|
||||
}
|
||||
return i.GetSideBlocksByQuery("WHERE effective_height <= $1 AND effective_height > $2 AND inclusion = $3 ORDER BY effective_height DESC, side_height DESC;", startHeight, startHeight-windowSize, InclusionInVerifiedChain)
|
||||
|
||||
if r, err := i.GetSideBlocksByQuery("WHERE effective_height <= $1 AND effective_height > $2 AND inclusion = $3 ORDER BY effective_height DESC, side_height DESC;", startHeight, startHeight-windowSize, InclusionInVerifiedChain); err != nil {
|
||||
log.Print(err)
|
||||
} else {
|
||||
return r
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Index) GetSideBlocksByMinerIdInWindow(minerId, startHeight, windowSize uint64) chan *SideBlock {
|
||||
func (i *Index) GetSideBlocksByMinerIdInWindow(minerId, startHeight, windowSize uint64) QueryIterator[SideBlock] {
|
||||
if startHeight < windowSize {
|
||||
windowSize = startHeight
|
||||
}
|
||||
return i.GetSideBlocksByQuery("WHERE miner = $1 AND effective_height <= $2 AND effective_height > $3 AND inclusion = $4 ORDER BY effective_height DESC, side_height DESC;", minerId, startHeight, startHeight-windowSize, InclusionInVerifiedChain)
|
||||
|
||||
if r, err := i.GetSideBlocksByQuery("WHERE miner = $1 AND effective_height <= $2 AND effective_height > $3 AND inclusion = $4 ORDER BY effective_height DESC, side_height DESC;", minerId, startHeight, startHeight-windowSize, InclusionInVerifiedChain); err != nil {
|
||||
log.Print(err)
|
||||
} else {
|
||||
return r
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Index) InsertOrUpdateSideBlock(b *SideBlock) error {
|
||||
|
@ -763,207 +778,195 @@ func (i *Index) InsertOrUpdateMainBlock(b *MainBlock) error {
|
|||
}
|
||||
}
|
||||
|
||||
func (i *Index) GetPayoutsByMinerId(minerId uint64, limit uint64) chan *Payout {
|
||||
out := make(chan *Payout, 1)
|
||||
func (i *Index) GetPayoutsByMinerId(minerId uint64, limit uint64) (r QueryIterator[Payout], err error) {
|
||||
var stmt *sql.Stmt
|
||||
var params []any
|
||||
|
||||
go func() {
|
||||
defer close(out)
|
||||
|
||||
resultFunc := func(row RowScanInterface) error {
|
||||
p := &Payout{}
|
||||
if err := p.ScanFromRow(i, row); err != nil {
|
||||
return err
|
||||
}
|
||||
out <- p
|
||||
return nil
|
||||
if limit == 0 {
|
||||
if stmt, err = i.handle.Prepare("SELECT * FROM " + i.views["payouts"] + " WHERE miner = $1 ORDER BY main_height DESC;"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params = []any{minerId}
|
||||
} else {
|
||||
if stmt, err = i.handle.Prepare("SELECT * FROM " + i.views["payouts"] + " WHERE miner = $1 ORDER BY main_height DESC LIMIT $2;"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params = []any{minerId, limit}
|
||||
}
|
||||
|
||||
if limit == 0 {
|
||||
if err := i.Query("SELECT * FROM "+i.views["payouts"]+" WHERE miner = $1 ORDER BY main_height DESC;", resultFunc, minerId); err != nil {
|
||||
return
|
||||
}
|
||||
if r, err := queryStatement[Payout](i, stmt, params...); err != nil {
|
||||
defer stmt.Close()
|
||||
return nil, err
|
||||
} else {
|
||||
r.closer = func() {
|
||||
stmt.Close()
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (i *Index) GetPayoutsByMinerIdFromHeight(minerId uint64, height uint64) (QueryIterator[Payout], error) {
|
||||
if stmt, err := i.handle.Prepare("SELECT * FROM " + i.views["payouts"] + " WHERE miner = $1 AND main_height >= $2 ORDER BY main_height DESC;"); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
if r, err := queryStatement[Payout](i, stmt, minerId, height); err != nil {
|
||||
defer stmt.Close()
|
||||
return nil, err
|
||||
} else {
|
||||
if err := i.Query("SELECT * FROM "+i.views["payouts"]+" WHERE miner = $1 ORDER BY main_height DESC LIMIT $2;", resultFunc, minerId, limit); err != nil {
|
||||
return
|
||||
r.closer = func() {
|
||||
stmt.Close()
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
}()
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (i *Index) GetPayoutsByMinerIdFromHeight(minerId uint64, height uint64) chan *Payout {
|
||||
out := make(chan *Payout, 1)
|
||||
|
||||
go func() {
|
||||
defer close(out)
|
||||
|
||||
resultFunc := func(row RowScanInterface) error {
|
||||
p := &Payout{}
|
||||
if err := p.ScanFromRow(i, row); err != nil {
|
||||
return err
|
||||
}
|
||||
out <- p
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := i.Query("SELECT * FROM "+i.views["payouts"]+" WHERE miner = $1 AND main_height >= $2 ORDER BY main_height DESC;", resultFunc, minerId, height); err != nil {
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (i *Index) GetPayoutsByMinerIdFromTimestamp(minerId uint64, timestamp uint64) chan *Payout {
|
||||
out := make(chan *Payout, 1)
|
||||
|
||||
go func() {
|
||||
defer close(out)
|
||||
|
||||
resultFunc := func(row RowScanInterface) error {
|
||||
p := &Payout{}
|
||||
if err := p.ScanFromRow(i, row); err != nil {
|
||||
return err
|
||||
}
|
||||
out <- p
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := i.Query("SELECT * FROM "+i.views["payouts"]+" WHERE miner = $1 AND timestamp >= $2 ORDER BY main_height DESC;", resultFunc, minerId, timestamp); err != nil {
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (i *Index) GetPayoutsBySideBlock(b *SideBlock) chan *Payout {
|
||||
out := make(chan *Payout, 1)
|
||||
|
||||
go func() {
|
||||
defer close(out)
|
||||
|
||||
resultFunc := func(row RowScanInterface) error {
|
||||
p := &Payout{}
|
||||
if err := p.ScanFromRow(i, row); err != nil {
|
||||
return err
|
||||
}
|
||||
out <- p
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := i.Query("SELECT * FROM "+i.views["payouts"]+" WHERE miner = $1 AND ((side_height >= $2 AND including_height <= $2) OR main_id = $3) ORDER BY main_height DESC;", resultFunc, b.Miner, b.EffectiveHeight, &b.MainId); err != nil {
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (i *Index) GetMainCoinbaseOutputs(coinbaseId types.Hash) MainCoinbaseOutputs {
|
||||
var outputs MainCoinbaseOutputs
|
||||
if err := i.Query("SELECT "+MainCoinbaseOutputSelectFields+" FROM main_coinbase_outputs WHERE id = $1 ORDER BY index ASC;", func(row RowScanInterface) error {
|
||||
var output MainCoinbaseOutput
|
||||
if err := output.ScanFromRow(i, row); err != nil {
|
||||
return err
|
||||
}
|
||||
outputs = append(outputs, output)
|
||||
return nil
|
||||
}, coinbaseId[:]); err != nil {
|
||||
return nil
|
||||
}
|
||||
return outputs
|
||||
}
|
||||
|
||||
func (i *Index) GetMainCoinbaseOutputByIndex(coinbaseId types.Hash, index uint64) *MainCoinbaseOutput {
|
||||
var output MainCoinbaseOutput
|
||||
if err := i.Query("SELECT "+MainCoinbaseOutputSelectFields+" FROM main_coinbase_outputs WHERE id = $1 AND index = $2 ORDER BY index ASC;", func(row RowScanInterface) error {
|
||||
if err := output.ScanFromRow(i, row); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}, coinbaseId[:], index); err != nil {
|
||||
return nil
|
||||
}
|
||||
if output.Id == types.ZeroHash {
|
||||
return nil
|
||||
}
|
||||
return &output
|
||||
}
|
||||
|
||||
func (i *Index) GetMainCoinbaseOutputByGlobalOutputIndex(globalOutputIndex uint64) *MainCoinbaseOutput {
|
||||
var output MainCoinbaseOutput
|
||||
if err := i.Query("SELECT "+MainCoinbaseOutputSelectFields+" FROM main_coinbase_outputs WHERE global_output_index = $1 ORDER BY index ASC;", func(row RowScanInterface) error {
|
||||
if err := output.ScanFromRow(i, row); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}, globalOutputIndex); err != nil {
|
||||
return nil
|
||||
}
|
||||
if output.Id == types.ZeroHash {
|
||||
return nil
|
||||
}
|
||||
return &output
|
||||
}
|
||||
|
||||
func (i *Index) GetMainLikelySweepTransactions(limit uint64) chan *MainLikelySweepTransaction {
|
||||
out := make(chan *MainLikelySweepTransaction, 1)
|
||||
|
||||
go func() {
|
||||
defer close(out)
|
||||
scanFunc := func(row RowScanInterface) error {
|
||||
var tx MainLikelySweepTransaction
|
||||
if err := tx.ScanFromRow(i, row); err != nil {
|
||||
return err
|
||||
}
|
||||
out <- &tx
|
||||
return nil
|
||||
}
|
||||
|
||||
if limit > 0 {
|
||||
if err := i.Query("SELECT "+MainLikelySweepTransactionSelectFields+" FROM main_likely_sweep_transactions ORDER BY timestamp DESC LIMIT $1;", scanFunc, limit); err != nil {
|
||||
return
|
||||
}
|
||||
func (i *Index) GetPayoutsByMinerIdFromTimestamp(minerId uint64, timestamp uint64) (QueryIterator[Payout], error) {
|
||||
if stmt, err := i.handle.Prepare("SELECT * FROM " + i.views["payouts"] + " WHERE miner = $1 AND timestamp >= $2 ORDER BY main_height DESC;"); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
if r, err := queryStatement[Payout](i, stmt, minerId, timestamp); err != nil {
|
||||
defer stmt.Close()
|
||||
return nil, err
|
||||
} else {
|
||||
if err := i.Query("SELECT "+MainLikelySweepTransactionSelectFields+" FROM main_likely_sweep_transactions ORDER BY timestamp DESC;", scanFunc); err != nil {
|
||||
return
|
||||
r.closer = func() {
|
||||
stmt.Close()
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
}()
|
||||
|
||||
return out
|
||||
}
|
||||
}
|
||||
|
||||
func (i *Index) GetMainLikelySweepTransactionsByAddress(addr *address.Address, limit uint64) chan *MainLikelySweepTransaction {
|
||||
out := make(chan *MainLikelySweepTransaction, 1)
|
||||
|
||||
go func() {
|
||||
defer close(out)
|
||||
|
||||
spendPub, viewPub := addr.SpendPublicKey().AsSlice(), addr.ViewPublicKey().AsSlice()
|
||||
scanFunc := func(row RowScanInterface) error {
|
||||
var tx MainLikelySweepTransaction
|
||||
if err := tx.ScanFromRow(i, row); err != nil {
|
||||
return err
|
||||
}
|
||||
out <- &tx
|
||||
return nil
|
||||
}
|
||||
|
||||
if limit > 0 {
|
||||
if err := i.Query("SELECT "+MainLikelySweepTransactionSelectFields+" FROM main_likely_sweep_transactions WHERE miner_spend_public_key = $1 AND miner_view_public_key = $2 ORDER BY timestamp DESC LIMIT $3;", scanFunc, &spendPub, &viewPub, limit); err != nil {
|
||||
return
|
||||
}
|
||||
func (i *Index) GetPayoutsBySideBlock(b *SideBlock) (QueryIterator[Payout], error) {
|
||||
if stmt, err := i.handle.Prepare("SELECT * FROM " + i.views["payouts"] + " WHERE miner = $1 AND ((side_height >= $2 AND including_height <= $2) OR main_id = $3) ORDER BY main_height DESC;"); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
if r, err := queryStatement[Payout](i, stmt, b.Miner, b.EffectiveHeight, &b.MainId); err != nil {
|
||||
defer stmt.Close()
|
||||
return nil, err
|
||||
} else {
|
||||
if err := i.Query("SELECT "+MainLikelySweepTransactionSelectFields+" FROM main_likely_sweep_transactions WHERE miner_spend_public_key = $1 AND miner_view_public_key = $2 ORDER BY timestamp DESC;", scanFunc, &spendPub, &viewPub); err != nil {
|
||||
return
|
||||
r.closer = func() {
|
||||
stmt.Close()
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
func (i *Index) GetMainCoinbaseOutputs(coinbaseId types.Hash) (QueryIterator[MainCoinbaseOutput], error) {
|
||||
if stmt, err := i.handle.Prepare("SELECT " + MainCoinbaseOutputSelectFields + " FROM main_coinbase_outputs WHERE id = $1 ORDER BY index ASC;"); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
if r, err := queryStatement[MainCoinbaseOutput](i, stmt, coinbaseId[:]); err != nil {
|
||||
defer stmt.Close()
|
||||
return nil, err
|
||||
} else {
|
||||
r.closer = func() {
|
||||
stmt.Close()
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (i *Index) GetMainCoinbaseOutputByIndex(coinbaseId types.Hash, index uint64) (o *MainCoinbaseOutput) {
|
||||
if stmt, err := i.handle.Prepare("SELECT " + MainCoinbaseOutputSelectFields + " FROM main_coinbase_outputs WHERE id = $1 AND index = $2 ORDER BY index ASC;"); err != nil {
|
||||
log.Print(err)
|
||||
return nil
|
||||
} else {
|
||||
if r, err := queryStatement[MainCoinbaseOutput](i, stmt, coinbaseId[:], index); err != nil {
|
||||
log.Print(err)
|
||||
return nil
|
||||
} else {
|
||||
defer r.Close()
|
||||
r.closer = func() {
|
||||
stmt.Close()
|
||||
}
|
||||
defer r.Close()
|
||||
if _, o = r.Next(); o == nil && r.Err() != nil {
|
||||
log.Print(r.Err())
|
||||
}
|
||||
return o
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (i *Index) GetMainCoinbaseOutputByGlobalOutputIndex(globalOutputIndex uint64) (o *MainCoinbaseOutput) {
|
||||
if stmt, err := i.handle.Prepare("SELECT " + MainCoinbaseOutputSelectFields + " FROM main_coinbase_outputs WHERE global_output_index = $1 ORDER BY index ASC;"); err != nil {
|
||||
log.Print(err)
|
||||
return nil
|
||||
} else {
|
||||
if r, err := queryStatement[MainCoinbaseOutput](i, stmt, globalOutputIndex); err != nil {
|
||||
log.Print(err)
|
||||
return nil
|
||||
} else {
|
||||
defer r.Close()
|
||||
r.closer = func() {
|
||||
stmt.Close()
|
||||
}
|
||||
defer r.Close()
|
||||
if _, o = r.Next(); o == nil && r.Err() != nil {
|
||||
log.Print(r.Err())
|
||||
}
|
||||
return o
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (i *Index) GetMainLikelySweepTransactions(limit uint64) (r QueryIterator[MainLikelySweepTransaction], err error) {
|
||||
var stmt *sql.Stmt
|
||||
var params []any
|
||||
|
||||
if limit > 0 {
|
||||
if stmt, err = i.handle.Prepare("SELECT " + MainLikelySweepTransactionSelectFields + " FROM main_likely_sweep_transactions ORDER BY timestamp DESC LIMIT $1;"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params = []any{limit}
|
||||
} else {
|
||||
if stmt, err = i.handle.Prepare("SELECT " + MainLikelySweepTransactionSelectFields + " FROM main_likely_sweep_transactions ORDER BY timestamp DESC;"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params = []any{}
|
||||
}
|
||||
|
||||
if r, err := queryStatement[MainLikelySweepTransaction](i, stmt, params...); err != nil {
|
||||
defer stmt.Close()
|
||||
return nil, err
|
||||
} else {
|
||||
r.closer = func() {
|
||||
stmt.Close()
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (i *Index) GetMainLikelySweepTransactionsByAddress(addr *address.Address, limit uint64) (r QueryIterator[MainLikelySweepTransaction], err error) {
|
||||
var stmt *sql.Stmt
|
||||
var params []any
|
||||
|
||||
spendPub, viewPub := addr.SpendPublicKey().AsSlice(), addr.ViewPublicKey().AsSlice()
|
||||
|
||||
if limit > 0 {
|
||||
if stmt, err = i.handle.Prepare("SELECT " + MainLikelySweepTransactionSelectFields + " FROM main_likely_sweep_transactions WHERE miner_spend_public_key = $1 AND miner_view_public_key = $2 ORDER BY timestamp DESC LIMIT $3;"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params = []any{&spendPub, &viewPub, limit}
|
||||
} else {
|
||||
if stmt, err = i.handle.Prepare("SELECT " + MainLikelySweepTransactionSelectFields + " FROM main_likely_sweep_transactions WHERE miner_spend_public_key = $1 AND miner_view_public_key = $2 ORDER BY timestamp DESC;"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params = []any{&spendPub, &viewPub}
|
||||
}
|
||||
|
||||
if r, err := queryStatement[MainLikelySweepTransaction](i, stmt, params...); err != nil {
|
||||
defer stmt.Close()
|
||||
return nil, err
|
||||
} else {
|
||||
r.closer = func() {
|
||||
stmt.Close()
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
}
|
||||
|
||||
type TransactionInputQueryResult struct {
|
||||
|
@ -1367,17 +1370,3 @@ func (i *Index) InsertOrUpdatePoolBlock(b *sidechain.PoolBlock, inclusion BlockI
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ChanConsume[T any](s chan T) {
|
||||
for range s {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func ChanToSlice[T any](s chan T) (r []T) {
|
||||
r = make([]T, 0)
|
||||
for v := range s {
|
||||
r = append(r, v)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
|
66
cmd/index/iterator.go
Normal file
66
cmd/index/iterator.go
Normal file
|
@ -0,0 +1,66 @@
|
|||
package index
|
||||
|
||||
type IterateFunction[K, V any] func(key K, value V) (stop bool)
|
||||
|
||||
type IteratorFunction[K, V any] func(f IterateFunction[K, V]) (complete bool)
|
||||
|
||||
type Iterator[K, V any] interface {
|
||||
All(f IterateFunction[K, V]) (complete bool)
|
||||
}
|
||||
|
||||
func IteratorToIterator[K, V any](i Iterator[K, V], _ ...error) Iterator[K, V] {
|
||||
return i
|
||||
}
|
||||
|
||||
func IterateToSlice[T any](i Iterator[int, T], _ ...error) (s []T) {
|
||||
i.All(func(key int, value T) (stop bool) {
|
||||
s = append(s, value)
|
||||
return false
|
||||
})
|
||||
return s
|
||||
}
|
||||
|
||||
func IterateToSliceWithoutPointer[T any](i Iterator[int, *T], _ ...error) (s []T) {
|
||||
i.All(func(key int, value *T) (stop bool) {
|
||||
s = append(s, *value)
|
||||
return false
|
||||
})
|
||||
return s
|
||||
}
|
||||
|
||||
func SliceIterate[S ~[]T, T any](s S, f IterateFunction[int, T]) (complete bool) {
|
||||
for i, v := range s {
|
||||
if f(i, v) {
|
||||
return (len(s) - 1) == i
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func ChanIterate[T any](c <-chan T, f IterateFunction[int, T]) (complete bool) {
|
||||
var i int
|
||||
for {
|
||||
v, more := <-c
|
||||
if !more {
|
||||
return true
|
||||
}
|
||||
if f(i, v) {
|
||||
return false
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
func ChanConsume[T any](c <-chan T) {
|
||||
for range c {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func ChanToSlice[S ~[]T, T any](c <-chan T) (s S) {
|
||||
s = make(S, 0, len(c))
|
||||
for v := range c {
|
||||
s = append(s, v)
|
||||
}
|
||||
return s
|
||||
}
|
170
cmd/index/query.go
Normal file
170
cmd/index/query.go
Normal file
|
@ -0,0 +1,170 @@
|
|||
package index
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
)
|
||||
|
||||
type RowScanInterface interface {
|
||||
Scan(dest ...any) error
|
||||
}
|
||||
|
||||
type Scannable interface {
|
||||
ScanFromRow(i *Index, row RowScanInterface) error
|
||||
}
|
||||
|
||||
type QueryIterator[V any] interface {
|
||||
All(f IterateFunction[int, *V]) (complete bool)
|
||||
Next() (int, *V)
|
||||
Close()
|
||||
Err() error
|
||||
}
|
||||
|
||||
func QueryIterate[V any](i QueryIterator[V], f IterateFunction[int, *V]) {
|
||||
defer i.Close()
|
||||
i.All(f)
|
||||
|
||||
if i.Err() != nil {
|
||||
panic(i.Err())
|
||||
}
|
||||
}
|
||||
|
||||
func QueryIterateToSlice[T any](i QueryIterator[T], err ...error) (s []*T) {
|
||||
if len(err) > 0 {
|
||||
if err[0] != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
QueryIterate(i, func(key int, value *T) (stop bool) {
|
||||
s = append(s, value)
|
||||
return false
|
||||
})
|
||||
return s
|
||||
}
|
||||
|
||||
func QueryFirstResult[T any](i QueryIterator[T], err ...error) (v *T) {
|
||||
if len(err) > 0 {
|
||||
if err[0] != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
QueryIterate(i, func(_ int, value *T) (stop bool) {
|
||||
v = value
|
||||
return true
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func QueryHasResults[T any](i QueryIterator[T], err ...error) bool {
|
||||
if len(err) > 0 {
|
||||
if err[0] != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
var hasValue bool
|
||||
QueryIterate(i, func(key int, value *T) (stop bool) {
|
||||
if value != nil {
|
||||
hasValue = true
|
||||
}
|
||||
return true
|
||||
})
|
||||
return hasValue
|
||||
}
|
||||
|
||||
// queryStatement Queries a provided sql.Stmt and returns a QueryIterator
|
||||
// After results are read QueryIterator must be closed/freed
|
||||
func queryStatement[V any](index *Index, stmt *sql.Stmt, params ...any) (*QueryResult[V], error) {
|
||||
var testV *V
|
||||
if _, ok := any(testV).(Scannable); !ok {
|
||||
return nil, errors.New("unsupported type")
|
||||
}
|
||||
|
||||
if rows, err := stmt.Query(params...); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &QueryResult[V]{
|
||||
index: index,
|
||||
rows: rows,
|
||||
}, err
|
||||
}
|
||||
}
|
||||
|
||||
type FakeQueryResult[V any] struct {
|
||||
NextFunction func() (int, *V)
|
||||
}
|
||||
|
||||
func (r *FakeQueryResult[V]) All(f IterateFunction[int, *V]) (complete bool) {
|
||||
for {
|
||||
if i, v := r.Next(); v == nil {
|
||||
return true
|
||||
} else {
|
||||
if f(i, v) {
|
||||
// do not allow resuming
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *FakeQueryResult[V]) Next() (int, *V) {
|
||||
return r.NextFunction()
|
||||
}
|
||||
|
||||
func (r *FakeQueryResult[V]) Err() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *FakeQueryResult[V]) Close() {
|
||||
|
||||
}
|
||||
|
||||
type QueryResult[V any] struct {
|
||||
index *Index
|
||||
rows *sql.Rows
|
||||
closer func()
|
||||
i int
|
||||
err error
|
||||
}
|
||||
|
||||
func (r *QueryResult[V]) All(f IterateFunction[int, *V]) (complete bool) {
|
||||
for {
|
||||
if i, v := r.Next(); v == nil {
|
||||
return true
|
||||
} else {
|
||||
if f(i, v) {
|
||||
// do not allow resuming
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *QueryResult[V]) Next() (int, *V) {
|
||||
if r.rows.Next() {
|
||||
var v V
|
||||
if r.err = any(&v).(Scannable).ScanFromRow(r.index, r.rows); r.err != nil {
|
||||
return 0, nil
|
||||
}
|
||||
r.i++
|
||||
return r.i - 1, &v
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (r *QueryResult[V]) Err() error {
|
||||
return r.err
|
||||
}
|
||||
|
||||
func (r *QueryResult[V]) Close() {
|
||||
if r.closer != nil {
|
||||
r.closer()
|
||||
r.closer = nil
|
||||
}
|
||||
if r.rows != nil {
|
||||
r.rows.Close()
|
||||
r.rows = nil
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@ import (
|
|||
)
|
||||
|
||||
type GetByTemplateIdFunc func(h types.Hash) *SideBlock
|
||||
type GetUnclesByTemplateIdFunc func(h types.Hash) chan *SideBlock
|
||||
type GetUnclesByTemplateIdFunc func(h types.Hash) QueryIterator[SideBlock]
|
||||
type SideBlockWindowAddWeightFunc func(b *SideBlock, weight types.Difficulty)
|
||||
|
||||
type SideBlockWindowSlot struct {
|
||||
|
@ -59,7 +59,7 @@ func IterateSideBlocksInPPLNSWindow(tip *SideBlock, consensus *sidechain.Consens
|
|||
}
|
||||
curWeight := types.DifficultyFrom64(cur.Difficulty)
|
||||
|
||||
for uncle := range getUnclesByTemplateId(cur.TemplateId) {
|
||||
QueryIterate(getUnclesByTemplateId(cur.TemplateId), func(_ int, uncle *SideBlock) (stop bool) {
|
||||
//Needs to be added regardless - for other consumers
|
||||
if !slices.ContainsFunc(curEntry.Uncles, func(sideBlock *SideBlock) bool {
|
||||
return sideBlock.TemplateId == uncle.TemplateId
|
||||
|
@ -69,7 +69,7 @@ func IterateSideBlocksInPPLNSWindow(tip *SideBlock, consensus *sidechain.Consens
|
|||
|
||||
// Skip uncles which are already out of PPLNS window
|
||||
if (tip.SideHeight - uncle.SideHeight) >= consensus.ChainWindowSize {
|
||||
continue
|
||||
return false
|
||||
}
|
||||
|
||||
// Take some % of uncle's weight into this share
|
||||
|
@ -78,7 +78,7 @@ func IterateSideBlocksInPPLNSWindow(tip *SideBlock, consensus *sidechain.Consens
|
|||
|
||||
// Skip uncles that push PPLNS weight above the limit
|
||||
if newPplnsWeight.Cmp(maxPplnsWeight) > 0 {
|
||||
continue
|
||||
return false
|
||||
}
|
||||
curWeight = curWeight.Add(unclePenalty)
|
||||
|
||||
|
@ -87,7 +87,9 @@ func IterateSideBlocksInPPLNSWindow(tip *SideBlock, consensus *sidechain.Consens
|
|||
}
|
||||
|
||||
pplnsWeight = newPplnsWeight
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
// Always add non-uncle shares even if PPLNS weight goes above the limit
|
||||
slotFunc(curEntry)
|
||||
|
@ -161,7 +163,7 @@ func BlocksInPPLNSWindow(tip *SideBlock, consensus *sidechain.Consensus, difficu
|
|||
}
|
||||
curWeight := types.DifficultyFrom64(cur.Difficulty)
|
||||
|
||||
for uncle := range getUnclesByTemplateId(cur.TemplateId) {
|
||||
QueryIterate(getUnclesByTemplateId(cur.TemplateId), func(_ int, uncle *SideBlock) (stop bool) {
|
||||
//Needs to be added regardless - for other consumers
|
||||
if !slices.ContainsFunc(curEntry.Uncles, func(sideBlock *SideBlock) bool {
|
||||
return sideBlock.TemplateId == uncle.TemplateId
|
||||
|
@ -171,7 +173,7 @@ func BlocksInPPLNSWindow(tip *SideBlock, consensus *sidechain.Consensus, difficu
|
|||
|
||||
// Skip uncles which are already out of PPLNS window
|
||||
if (tip.SideHeight - uncle.SideHeight) >= consensus.ChainWindowSize {
|
||||
continue
|
||||
return false
|
||||
}
|
||||
|
||||
// Take some % of uncle's weight into this share
|
||||
|
@ -180,7 +182,7 @@ func BlocksInPPLNSWindow(tip *SideBlock, consensus *sidechain.Consensus, difficu
|
|||
|
||||
// Skip uncles that push PPLNS weight above the limit
|
||||
if newPplnsWeight.Cmp(maxPplnsWeight) > 0 {
|
||||
continue
|
||||
return false
|
||||
}
|
||||
curWeight = curWeight.Add(unclePenalty)
|
||||
|
||||
|
@ -189,7 +191,9 @@ func BlocksInPPLNSWindow(tip *SideBlock, consensus *sidechain.Consensus, difficu
|
|||
}
|
||||
|
||||
pplnsWeight = newPplnsWeight
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
// Always add non-uncle shares even if PPLNS weight goes above the limit
|
||||
bottomHeight = cur.SideHeight
|
||||
|
|
|
@ -138,13 +138,13 @@ func FindAndInsertMainHeader(h daemon.BlockHeader, indexDb *index.Index, storeFu
|
|||
if sideBlock == nil {
|
||||
//found deep orphan which found block, insert parents
|
||||
cur := t
|
||||
for cur != nil && len(index.ChanToSlice(indexDb.GetSideBlocksByTemplateId(cur.SideTemplateId(indexDb.Consensus())))) == 0 {
|
||||
for cur != nil && !index.QueryHasResults(indexDb.GetSideBlocksByTemplateId(cur.SideTemplateId(indexDb.Consensus()))) {
|
||||
orphanBlock, orphanUncles, err := indexDb.GetSideBlockFromPoolBlock(cur, index.InclusionOrphan)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
if len(index.ChanToSlice(indexDb.GetSideBlocksByTemplateId(orphanBlock.TemplateId))) > 0 {
|
||||
if index.QueryHasResults(indexDb.GetSideBlocksByTemplateId(orphanBlock.TemplateId)) {
|
||||
continue
|
||||
}
|
||||
if err := indexDb.InsertOrUpdateSideBlock(orphanBlock); err != nil {
|
||||
|
@ -152,7 +152,7 @@ func FindAndInsertMainHeader(h daemon.BlockHeader, indexDb *index.Index, storeFu
|
|||
}
|
||||
|
||||
for _, orphanUncle := range orphanUncles {
|
||||
if len(index.ChanToSlice(indexDb.GetSideBlocksByTemplateId(orphanUncle.TemplateId))) > 0 {
|
||||
if index.QueryHasResults(indexDb.GetSideBlocksByTemplateId(orphanUncle.TemplateId)) {
|
||||
continue
|
||||
}
|
||||
if err := indexDb.InsertOrUpdateSideBlock(orphanUncle); err != nil {
|
||||
|
@ -251,7 +251,7 @@ func FindAndInsertMainHeader(h daemon.BlockHeader, indexDb *index.Index, storeFu
|
|||
}
|
||||
|
||||
func FindAndInsertMainHeaderOutputs(mb *index.MainBlock, indexDb *index.Index, client *client.Client, difficultyByHeight block.GetDifficultyByHeightFunc, getByTemplateId sidechain.GetByTemplateIdFunc, getByMainId sidechain.GetByMainIdFunc, getByMainHeight sidechain.GetByMainHeightFunc, processBlock func(b *sidechain.PoolBlock) error) error {
|
||||
if len(indexDb.GetMainCoinbaseOutputs(mb.CoinbaseId)) == 0 {
|
||||
if !index.QueryHasResults(indexDb.GetMainCoinbaseOutputs(mb.CoinbaseId)) {
|
||||
//fill information
|
||||
log.Printf("inserting coinbase outputs for %s, template id %s, coinbase id %s", mb.Id, mb.SideTemplateId, mb.CoinbaseId)
|
||||
var t *sidechain.PoolBlock
|
||||
|
|
Loading…
Reference in a new issue