Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | 57x 57x 57x 11x 11x 12x 12x 11x 11x 12x 12x 12x 12x 12x 12x 12x 12x 11x 11x 11x 11x 11x 11x 11x 13x 12x 12x 12x 12x 12x 12x 5x 12x 12x 12x 11x 12x 12x 12x 7x 7x 7x 5x 5x 12x 7x 12x 12x 7x 7x 7x 7x 11x | import { Beef } from '@bsv/sdk'
import { StorageProvider } from "../StorageProvider"
import { entity, sdk } from '../../index.client'
/**
* Attempt to post one or more `ProvenTxReq` with status 'unsent'
* to the bitcoin network.
*
* @param reqs
*/
export async function attemptToPostReqsToNetwork(storage: StorageProvider, reqs: entity.ProvenTxReq[], trx?: sdk.TrxToken): Promise<PostReqsToNetworkResult> {
const r: PostReqsToNetworkResult = {
status: 'success',
beef: new Beef(),
details: [],
log: ''
}
for (const req of reqs) {
r.details.push({
txid: req.txid,
req,
status: "unknown",
pbrft: {
txid: req.txid,
status: "error"
},
data: undefined,
error: undefined
})
}
const txids = reqs.map(r => r.txid)
let invalid: boolean = false
for (const rb of reqs) {
let badReq: boolean = false
Iif (!rb.rawTx) {
badReq = true; rb.addHistoryNote(`invalid req: rawTx must be valid`);
}
Iif (!rb.notify.transactionIds || rb.notify.transactionIds.length < 1) {
badReq = true; rb.addHistoryNote(`invalid req: must have at least one transaction to notify`);
}
Iif (rb.attempts > 10) {
badReq = true; rb.addHistoryNote(`invalid req: too many attempts ${rb.attempts}`);
}
// Accumulate batch beefs.
if (!badReq) {
try {
await storage.mergeReqToBeefToShareExternally(rb.api, r.beef, [], trx)
} catch (eu: unknown) {
const e = sdk.WalletError.fromUnknown(eu)
Iif (e.code === 'WERR_INVALID_PARAMETER' && (e as sdk.WERR_INVALID_PARAMETER).parameter === 'txid') {
badReq = true; rb.addHistoryNote(`invalid req: depends on txid which is unknown`);
}
}
}
Iif (badReq) invalid = true
}
Iif (invalid) {
for (const req of reqs) {
// batch passes or fails as a whole...prior to post to network attempt.
req.status = 'invalid'
await req.updateStorageDynamicProperties(storage)
r.log += `status set to ${req.status}\n`
}
return r;
}
// Use cwi-external-services to post the aggregate beef
// and add the new results to aggregate results.
const services = await storage.getServices()
const pbrs = await services.postBeef(r.beef, txids)
const pbrOk = pbrs.find(p => p.status === 'success')
r.pbr = pbrOk ? pbrOk : pbrs.length > 0 ? pbrs[0] : undefined
Iif (!r.pbr) {
r.status = 'error'
} else {
for (const d of r.details) {
const pbrft = r.pbr.txidResults.find(t => t.txid === d.txid)
Iif (!pbrft) throw new sdk.WERR_INTERNAL(`postBeef service failed to return result for txid ${d.txid}`);
d.pbrft = pbrft
Iif (r.pbr.data)
d.data = JSON.stringify(r.pbr.data)
Iif (r.pbr.error)
d.error = r.pbr.error.code
// Need to learn how double spend is reported by these services.
d.status = pbrft.status === 'success' ? 'success' : 'unknown'
if (d.status !== 'success')
// If any txid result fails, the aggregate result is error.
r.status = 'error';
d.req.attempts++
const note = {
what: 'postReqsToNetwork result',
name: r.pbr.name,
result: d
}
d.req.addHistoryNote(note)
}
}
for (const d of r.details) {
let newReqStatus: sdk.ProvenTxReqStatus | undefined = undefined
let newTxStatus: sdk.TransactionStatus | undefined = undefined
// For each req, three outcomes are handled:
// 1. success: req status from unprocessed(!isDelayed)/sending(isDelayed) to unmined, tx from sending to unproven
if (d.status === 'success') {
if (['nosend', 'unprocessed', 'sending', 'unsent'].indexOf(d.req.status) > -1)
newReqStatus = 'unmined';
newTxStatus = 'unproven' // but only if sending
}
// 2. doubleSpend: req status to doubleSpend, tx to failed
else Iif (d.status === 'doubleSpend') {
newReqStatus = 'doubleSpend'
newTxStatus = 'failed'
}
// 3. unknown: req status from unprocessed to sending or remains sending, tx remains sending
else if (d.status === 'unknown') {
/* no status updates */
} else E{
throw new sdk.WERR_INTERNAL(`unexpected status ${d.status}`)
}
if (newReqStatus) {
// Only advance the status of req.
d.req.status = newReqStatus
}
await d.req.updateStorageDynamicProperties(storage)
if (newTxStatus) {
const ids = d.req.notify.transactionIds
Iif (!ids || ids.length < 1) throw new sdk.WERR_INTERNAL(`req must have at least one transactionId to notify`);
for (const id of ids) {
await storage.updateTransactionStatus(newTxStatus, id)
}
}
}
// Fetch the updated history.
// log += .req.historyPretty(since, indent + 2)
return r
}
export type PostReqsToNetworkDetailsStatus = 'success' | 'doubleSpend' | 'unknown'
export interface PostReqsToNetworkDetails {
txid: string
req: entity.ProvenTxReq
status: PostReqsToNetworkDetailsStatus
pbrft: sdk.PostTxResultForTxid
data?: string
error?: string
}
export interface PostReqsToNetworkResult {
status: "success" | "error"
beef: Beef
details: PostReqsToNetworkDetails[]
pbr?: sdk.PostBeefResult
log: string
} |