@did-btcr2/method 0.19.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +223 -1
- package/dist/browser.js +3496 -4202
- package/dist/browser.mjs +3496 -4202
- package/dist/cjs/core/beacon/beacon.js +25 -0
- package/dist/cjs/core/beacon/beacon.js.map +1 -0
- package/dist/cjs/core/beacon/cas-beacon.js +20 -36
- package/dist/cjs/core/beacon/cas-beacon.js.map +1 -1
- package/dist/cjs/core/beacon/error.js +4 -4
- package/dist/cjs/core/beacon/error.js.map +1 -1
- package/dist/cjs/core/beacon/factory.js +5 -7
- package/dist/cjs/core/beacon/factory.js.map +1 -1
- package/dist/cjs/core/beacon/interfaces.js +1 -31
- package/dist/cjs/core/beacon/interfaces.js.map +1 -1
- package/dist/cjs/core/beacon/signal-discovery.js +183 -0
- package/dist/cjs/core/beacon/signal-discovery.js.map +1 -0
- package/dist/cjs/core/beacon/singleton.js +56 -86
- package/dist/cjs/core/beacon/singleton.js.map +1 -1
- package/dist/cjs/core/beacon/smt-beacon.js +22 -39
- package/dist/cjs/core/beacon/smt-beacon.js.map +1 -1
- package/dist/cjs/core/beacon/utils.js +4 -9
- package/dist/cjs/core/beacon/utils.js.map +1 -1
- package/dist/cjs/core/resolve.js +121 -307
- package/dist/cjs/core/resolve.js.map +1 -1
- package/dist/cjs/core/update.js +62 -154
- package/dist/cjs/core/update.js.map +1 -1
- package/dist/cjs/did-btcr2.js +100 -91
- package/dist/cjs/did-btcr2.js.map +1 -1
- package/dist/cjs/index.js +3 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/utils/appendix.js +6 -15
- package/dist/cjs/utils/appendix.js.map +1 -1
- package/dist/cjs/utils/did-document-builder.js +5 -6
- package/dist/cjs/utils/did-document-builder.js.map +1 -1
- package/dist/cjs/utils/did-document.js +42 -38
- package/dist/cjs/utils/did-document.js.map +1 -1
- package/dist/esm/core/beacon/beacon.js +25 -0
- package/dist/esm/core/beacon/beacon.js.map +1 -0
- package/dist/esm/core/beacon/cas-beacon.js +20 -36
- package/dist/esm/core/beacon/cas-beacon.js.map +1 -1
- package/dist/esm/core/beacon/error.js +4 -4
- package/dist/esm/core/beacon/error.js.map +1 -1
- package/dist/esm/core/beacon/factory.js +5 -7
- package/dist/esm/core/beacon/factory.js.map +1 -1
- package/dist/esm/core/beacon/interfaces.js +1 -31
- package/dist/esm/core/beacon/interfaces.js.map +1 -1
- package/dist/esm/core/beacon/signal-discovery.js +183 -0
- package/dist/esm/core/beacon/signal-discovery.js.map +1 -0
- package/dist/esm/core/beacon/singleton.js +56 -86
- package/dist/esm/core/beacon/singleton.js.map +1 -1
- package/dist/esm/core/beacon/smt-beacon.js +22 -39
- package/dist/esm/core/beacon/smt-beacon.js.map +1 -1
- package/dist/esm/core/beacon/utils.js +4 -9
- package/dist/esm/core/beacon/utils.js.map +1 -1
- package/dist/esm/core/resolve.js +121 -307
- package/dist/esm/core/resolve.js.map +1 -1
- package/dist/esm/core/update.js +62 -154
- package/dist/esm/core/update.js.map +1 -1
- package/dist/esm/did-btcr2.js +100 -91
- package/dist/esm/did-btcr2.js.map +1 -1
- package/dist/esm/index.js +3 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/utils/appendix.js +6 -15
- package/dist/esm/utils/appendix.js.map +1 -1
- package/dist/esm/utils/did-document-builder.js +5 -6
- package/dist/esm/utils/did-document-builder.js.map +1 -1
- package/dist/esm/utils/did-document.js +42 -38
- package/dist/esm/utils/did-document.js.map +1 -1
- package/dist/types/core/beacon/beacon.d.ts +44 -0
- package/dist/types/core/beacon/beacon.d.ts.map +1 -0
- package/dist/types/core/beacon/cas-beacon.d.ts +19 -30
- package/dist/types/core/beacon/cas-beacon.d.ts.map +1 -1
- package/dist/types/core/beacon/error.d.ts +2 -2
- package/dist/types/core/beacon/error.d.ts.map +1 -1
- package/dist/types/core/beacon/factory.d.ts +4 -6
- package/dist/types/core/beacon/factory.d.ts.map +1 -1
- package/dist/types/core/beacon/interfaces.d.ts +7 -46
- package/dist/types/core/beacon/interfaces.d.ts.map +1 -1
- package/dist/types/core/beacon/signal-discovery.d.ts +25 -0
- package/dist/types/core/beacon/signal-discovery.d.ts.map +1 -0
- package/dist/types/core/beacon/singleton.d.ts +17 -30
- package/dist/types/core/beacon/singleton.d.ts.map +1 -1
- package/dist/types/core/beacon/smt-beacon.d.ts +21 -33
- package/dist/types/core/beacon/smt-beacon.d.ts.map +1 -1
- package/dist/types/core/beacon/utils.d.ts.map +1 -1
- package/dist/types/core/interfaces.d.ts +1 -8
- package/dist/types/core/interfaces.d.ts.map +1 -1
- package/dist/types/core/resolve.d.ts +34 -47
- package/dist/types/core/resolve.d.ts.map +1 -1
- package/dist/types/core/types.d.ts +21 -8
- package/dist/types/core/types.d.ts.map +1 -1
- package/dist/types/core/update.d.ts +30 -73
- package/dist/types/core/update.d.ts.map +1 -1
- package/dist/types/did-btcr2.d.ts +44 -47
- package/dist/types/did-btcr2.d.ts.map +1 -1
- package/dist/types/index.d.ts +3 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/utils/appendix.d.ts.map +1 -1
- package/dist/types/utils/did-document-builder.d.ts +5 -1
- package/dist/types/utils/did-document-builder.d.ts.map +1 -1
- package/dist/types/utils/did-document.d.ts +26 -21
- package/dist/types/utils/did-document.d.ts.map +1 -1
- package/package.json +8 -7
- package/src/core/beacon/beacon.ts +58 -0
- package/src/core/beacon/cas-beacon.ts +30 -44
- package/src/core/beacon/error.ts +5 -6
- package/src/core/beacon/factory.ts +7 -9
- package/src/core/beacon/interfaces.ts +7 -64
- package/src/core/beacon/signal-discovery.ts +237 -0
- package/src/core/beacon/singleton.ts +78 -100
- package/src/core/beacon/smt-beacon.ts +32 -49
- package/src/core/beacon/utils.ts +16 -13
- package/src/core/interfaces.ts +1 -9
- package/src/core/resolve.ts +163 -395
- package/src/core/types.ts +25 -8
- package/src/core/update.ts +91 -236
- package/src/did-btcr2.ts +154 -116
- package/src/index.ts +8 -1
- package/src/utils/appendix.ts +8 -22
- package/src/utils/did-document-builder.ts +5 -7
- package/src/utils/did-document.ts +80 -73
|
@@ -1,67 +1,53 @@
|
|
|
1
1
|
import { BitcoinNetworkConnection } from '@did-btcr2/bitcoin';
|
|
2
|
-
import {
|
|
2
|
+
import { KeyBytes } from '@did-btcr2/common';
|
|
3
|
+
import { SignedBTCR2Update } from '@did-btcr2/cryptosuite';
|
|
3
4
|
import { SidecarData } from '../types.js';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
5
|
+
import { Beacon } from './beacon.js';
|
|
6
|
+
import { CASBeaconError } from './error.js';
|
|
7
|
+
import { BeaconService, BeaconSignal, BlockMetadata } from './interfaces.js';
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* Implements {@link https://dcdpr.github.io/did-btcr2/terminology.html#cas-beacon | CAS Beacon}.
|
|
9
|
-
*
|
|
10
11
|
* @class CASBeacon
|
|
11
12
|
* @type {CASBeacon}
|
|
12
|
-
* @extends {
|
|
13
|
+
* @extends {Beacon}
|
|
13
14
|
*/
|
|
14
|
-
export class CASBeacon extends
|
|
15
|
+
export class CASBeacon extends Beacon {
|
|
15
16
|
/**
|
|
16
17
|
* Creates an instance of CASBeacon.
|
|
17
18
|
* @param {BeaconService} service The service of the Beacon.
|
|
18
|
-
* @param {?BeaconSidecarData} [sidecar] The sidecar data of the Beacon.
|
|
19
19
|
*/
|
|
20
|
-
constructor(
|
|
21
|
-
service:
|
|
22
|
-
signals: Array<BeaconSignal>,
|
|
23
|
-
sidecar: SidecarData,
|
|
24
|
-
bitcoin?: BitcoinNetworkConnection
|
|
25
|
-
) {
|
|
26
|
-
super({ ...service, type: 'CASBeacon' }, signals, sidecar, bitcoin);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Establish a CASBeacon instance based on the provided service and sidecar data.
|
|
31
|
-
* @param {BeaconService} service - The beacon service configuration.
|
|
32
|
-
* @param {SidecarData} sidecar - The sidecar data.
|
|
33
|
-
* @returns {CASBeacon} The established CASBeacon instance.
|
|
34
|
-
*/
|
|
35
|
-
static establish(service: BeaconService, signals: Array<BeaconSignal>, sidecar: SidecarData): CASBeacon {
|
|
36
|
-
return new CASBeacon(service, signals, sidecar);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* TODO: Figure out if this is necessary or not.
|
|
41
|
-
* @param {HexString} updateHash The hash of the update to generate the signal for.
|
|
42
|
-
* @returns {BeaconSignal} The generated signal.
|
|
43
|
-
* @throws {MethodError} if the signal is invalid.
|
|
44
|
-
*/
|
|
45
|
-
generateSignal(updateHash: HexString): BeaconSignal {
|
|
46
|
-
throw new Error('Method not implemented.' + updateHash);
|
|
20
|
+
constructor(service: BeaconService) {
|
|
21
|
+
super({ ...service, type: 'CASBeacon' });
|
|
47
22
|
}
|
|
48
23
|
|
|
49
24
|
/**
|
|
50
25
|
* Implements {@link https://dcdpr.github.io/did-btcr2/operations/resolve.html#process-cas-beacon | 7.2.e.1 Process CAS Beacon}.
|
|
51
|
-
* @
|
|
52
|
-
* @
|
|
26
|
+
* @param {Array<BeaconSignal>} signals The array of Beacon Signals to process.
|
|
27
|
+
* @param {SidecarData} sidecar The sidecar data associated with the CAS Beacon.
|
|
28
|
+
* @returns {Promise<Array<[SignedBTCR2Update, BlockMetadata]>>} The processed signals.
|
|
29
|
+
* @throws {CASBeaconError} if processing fails.
|
|
53
30
|
*/
|
|
54
|
-
processSignals(
|
|
55
|
-
|
|
31
|
+
processSignals(
|
|
32
|
+
signals: Array<BeaconSignal>,
|
|
33
|
+
sidecar: SidecarData
|
|
34
|
+
): Promise<Array<[SignedBTCR2Update, BlockMetadata]>> {
|
|
35
|
+
throw new CASBeaconError('Method not implemented.', `METHOD_NOT_IMPLEMENTED`, { signals, sidecar });
|
|
56
36
|
}
|
|
57
37
|
|
|
58
38
|
/**
|
|
59
|
-
*
|
|
60
|
-
* @param {
|
|
61
|
-
* @
|
|
62
|
-
* @
|
|
39
|
+
* Broadcast CAS Beacon signal to the Bitcoin network.
|
|
40
|
+
* @param {SignedBTCR2Update} signedUpdate The signed BTCR2 update to broadcast.
|
|
41
|
+
* @param {KeyBytes} secretKey The secret key for signing the Bitcoin transaction.
|
|
42
|
+
* @param {BitcoinNetworkConnection} bitcoin The Bitcoin network connection.
|
|
43
|
+
* @return {Promise<SignedBTCR2Update>} The signed update that was broadcasted.
|
|
44
|
+
* @throws {CASBeaconError} if broadcasting fails.
|
|
63
45
|
*/
|
|
64
|
-
async broadcastSignal(
|
|
65
|
-
|
|
46
|
+
async broadcastSignal(
|
|
47
|
+
signedUpdate: SignedBTCR2Update,
|
|
48
|
+
secretKey: KeyBytes,
|
|
49
|
+
bitcoin: BitcoinNetworkConnection
|
|
50
|
+
): Promise<SignedBTCR2Update> {
|
|
51
|
+
throw new CASBeaconError('Method not implemented.', `METHOD_NOT_IMPLEMENTED`, {signedUpdate, secretKey, bitcoin});
|
|
66
52
|
}
|
|
67
53
|
}
|
package/src/core/beacon/error.ts
CHANGED
|
@@ -18,7 +18,6 @@ export class BeaconParticipantError extends MethodError {
|
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
|
|
22
21
|
export class SingletonBeaconError extends MethodError {
|
|
23
22
|
constructor(message: string, type: string = 'SingletonBeaconError', data?: Record<string, any>) {
|
|
24
23
|
super(message, type, data);
|
|
@@ -31,14 +30,14 @@ export class AggregateBeaconError extends MethodError {
|
|
|
31
30
|
}
|
|
32
31
|
}
|
|
33
32
|
|
|
34
|
-
export class
|
|
35
|
-
constructor(message: string, type: string = '
|
|
33
|
+
export class CASBeaconError extends MethodError {
|
|
34
|
+
constructor(message: string, type: string = 'CASBeaconError', data?: Record<string, any>) {
|
|
36
35
|
super(message, type, data);
|
|
37
36
|
}
|
|
38
37
|
}
|
|
39
38
|
|
|
40
|
-
export class
|
|
41
|
-
constructor(message: string, type: string = '
|
|
39
|
+
export class SMTBeaconError extends MethodError {
|
|
40
|
+
constructor(message: string, type: string = 'SMTBeaconError', data?: Record<string, any>) {
|
|
42
41
|
super(message, type, data);
|
|
43
42
|
}
|
|
44
|
-
}
|
|
43
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { MethodError } from '@did-btcr2/common';
|
|
2
|
-
import {
|
|
2
|
+
import { Beacon } from './beacon.js';
|
|
3
3
|
import { CASBeacon } from './cas-beacon.js';
|
|
4
|
-
import {
|
|
4
|
+
import { BeaconService } from './interfaces.js';
|
|
5
5
|
import { SingletonBeacon } from './singleton.js';
|
|
6
6
|
import { SMTBeacon } from './smt-beacon.js';
|
|
7
7
|
|
|
@@ -13,19 +13,17 @@ import { SMTBeacon } from './smt-beacon.js';
|
|
|
13
13
|
export class BeaconFactory {
|
|
14
14
|
/**
|
|
15
15
|
* Establish a Beacon instance based on the provided service and optional sidecar data.
|
|
16
|
-
* @param {BeaconService} service
|
|
17
|
-
* @param {Array<BeaconSignal>} signals - The array of beacon signals.
|
|
18
|
-
* @param {SidecarData} sidecar - The sidecar data associated with the beacon.
|
|
16
|
+
* @param {BeaconService} service The beacon service configuration.
|
|
19
17
|
* @returns {Beacon} The established Beacon instance.
|
|
20
18
|
*/
|
|
21
|
-
static establish(service: BeaconService
|
|
19
|
+
static establish(service: BeaconService): Beacon {
|
|
22
20
|
switch (service.type) {
|
|
23
21
|
case 'SingletonBeacon':
|
|
24
|
-
return new SingletonBeacon(service
|
|
22
|
+
return new SingletonBeacon(service);
|
|
25
23
|
case 'CASBeacon':
|
|
26
|
-
return new CASBeacon(service
|
|
24
|
+
return new CASBeacon(service);
|
|
27
25
|
case 'SMTBeacon':
|
|
28
|
-
return new SMTBeacon(service
|
|
26
|
+
return new SMTBeacon(service);
|
|
29
27
|
default:
|
|
30
28
|
throw new MethodError('Invalid Beacon Type', 'INVALID_BEACON_ERROR', service);
|
|
31
29
|
}
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { DidServiceEndpoint, DidService
|
|
4
|
-
import { SidecarData } from '../types.js';
|
|
5
|
-
import { BTCR2SignedUpdate } from '@did-btcr2/cryptosuite';
|
|
1
|
+
import { RawTransactionRest, RawTransactionV2 } from '@did-btcr2/bitcoin';
|
|
2
|
+
import { UnixTimestamp } from '@did-btcr2/common';
|
|
3
|
+
import { DidServiceEndpoint, DidService } from '@web5/dids';
|
|
6
4
|
|
|
7
5
|
/**
|
|
8
|
-
* Represents a Beacon Service, which extends
|
|
6
|
+
* Represents a Beacon Service, which extends a W3C DID Service by setting serviceEndpoint
|
|
7
|
+
* as a single DidServiceEndpoint.
|
|
9
8
|
* @interface BeaconService
|
|
10
|
-
* @extends
|
|
9
|
+
* @extends DidService
|
|
11
10
|
*/
|
|
12
|
-
export interface BeaconService extends
|
|
11
|
+
export interface BeaconService extends DidService {
|
|
13
12
|
serviceEndpoint: DidServiceEndpoint;
|
|
14
13
|
}
|
|
15
14
|
|
|
@@ -65,60 +64,4 @@ export interface BeaconSignal {
|
|
|
65
64
|
* Metadata about the block containing the Beacon Signal.
|
|
66
65
|
*/
|
|
67
66
|
blockMetadata: BlockMetadata;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Abstract class representing an AggregateBeacon.
|
|
72
|
-
* @abstract
|
|
73
|
-
* @class AggregateBeacon
|
|
74
|
-
* @type {AggregateBeacon}
|
|
75
|
-
*/
|
|
76
|
-
export abstract class AggregateBeacon {
|
|
77
|
-
/**
|
|
78
|
-
* The Beacon service object parsed from the DID Document.
|
|
79
|
-
*/
|
|
80
|
-
service: BeaconService;
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* The array of Beacon Signals associated with this Beacon service.
|
|
84
|
-
*/
|
|
85
|
-
signals: Array<BeaconSignal>;
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* The sidecar data associated with this Beacon service.
|
|
89
|
-
* TODO: Make this more specific to Beacon type.
|
|
90
|
-
*/
|
|
91
|
-
sidecar: SidecarData;
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* The Bitcoin network connection associated with this Beacon service.
|
|
95
|
-
*/
|
|
96
|
-
bitcoin: BitcoinNetworkConnection;
|
|
97
|
-
|
|
98
|
-
constructor(
|
|
99
|
-
service: BeaconService,
|
|
100
|
-
signals: Array<BeaconSignal>,
|
|
101
|
-
sidecar: SidecarData,
|
|
102
|
-
bitcoin?: BitcoinNetworkConnection
|
|
103
|
-
) {
|
|
104
|
-
this.service = service;
|
|
105
|
-
this.signals = signals;
|
|
106
|
-
this.sidecar = sidecar;
|
|
107
|
-
this.bitcoin = bitcoin!;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Generates an unsigned update in a Beacon Signal (implemented by subclasses).
|
|
112
|
-
*/
|
|
113
|
-
abstract generateSignal(updateHash: HexString): BeaconSignal;
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Processes a Beacon Signal (implemented by subclasses).
|
|
117
|
-
*/
|
|
118
|
-
abstract processSignals(): Promise<Array<[BTCR2SignedUpdate, BlockMetadata]>>;
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Broadcasts a signed update in a Beacon Signal (implemented by subclasses).
|
|
122
|
-
*/
|
|
123
|
-
abstract broadcastSignal(updateHash: HexString): Promise<HexString>;
|
|
124
67
|
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BitcoinNetworkConnection,
|
|
3
|
+
BlockV3,
|
|
4
|
+
GENESIS_TX_ID,
|
|
5
|
+
RawTransactionV2,
|
|
6
|
+
TXIN_WITNESS_COINBASE
|
|
7
|
+
} from '@did-btcr2/bitcoin';
|
|
8
|
+
import { ResolveError } from '@did-btcr2/common';
|
|
9
|
+
import { BeaconService, BeaconSignal } from './interfaces.js';
|
|
10
|
+
import { BeaconUtils } from './utils.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Static utility class for discovering Beacon Signals on the Bitcoin blockchain.
|
|
14
|
+
* Extracted from {@link Resolve} for single-responsibility and independent testability.
|
|
15
|
+
*
|
|
16
|
+
* @class BeaconSignalDiscovery
|
|
17
|
+
*/
|
|
18
|
+
export class BeaconSignalDiscovery {
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Retrieves the beacon signals for the given array of BeaconService objects
|
|
22
|
+
* using an esplora/electrs REST API connection via a bitcoin I/O driver.
|
|
23
|
+
* @param {Array<BeaconService>} beaconServices Array of BeaconService objects to retrieve signals for
|
|
24
|
+
* @param {BitcoinNetworkConnection} bitcoin Bitcoin network connection to use for REST calls
|
|
25
|
+
* @returns {Promise<Map<BeaconService, Array<BeaconSignal>>>} Map of beacon service to its discovered signals
|
|
26
|
+
*/
|
|
27
|
+
static async indexer(
|
|
28
|
+
beaconServices: Array<BeaconService>,
|
|
29
|
+
bitcoin: BitcoinNetworkConnection
|
|
30
|
+
): Promise<Map<BeaconService, Array<BeaconSignal>>> {
|
|
31
|
+
const beaconServiceSignals = new Map<BeaconService, Array<BeaconSignal>>();
|
|
32
|
+
|
|
33
|
+
// Fetch the current block count once before the loop
|
|
34
|
+
const currentBlockCount = await bitcoin.network.rest.block.count();
|
|
35
|
+
|
|
36
|
+
// Iterate over each beacon
|
|
37
|
+
for (const beaconService of beaconServices) {
|
|
38
|
+
beaconServiceSignals.set(beaconService, []);
|
|
39
|
+
// Get the transactions for the beacon address via REST
|
|
40
|
+
const beaconSignals = await bitcoin.network.rest.address.getTxs(
|
|
41
|
+
beaconService.serviceEndpoint as string
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
// If no signals are found, continue
|
|
45
|
+
if (!beaconSignals || !beaconSignals.length) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Iterate over each signal
|
|
50
|
+
for (const beaconSignal of beaconSignals) {
|
|
51
|
+
// Get the last vout in the transaction
|
|
52
|
+
const signalVout = beaconSignal.vout.slice(-1)[0];
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Look for OP_RETURN in last vout scriptpubkey_asm
|
|
56
|
+
* Vout (rest) format:
|
|
57
|
+
* {
|
|
58
|
+
* scriptpubkey: '6a20570f177c65e64fb5cf61180b664cdddf09ab76153c2b192e22006e5b22a3917a',
|
|
59
|
+
* scriptpubkey_asm: 'OP_RETURN OP_PUSHBYTES_32 570f177c65e64fb5cf61180b664cdddf09ab76153c2b192e22006e5b22a3917a',
|
|
60
|
+
* scriptpubkey_type: 'op_return',
|
|
61
|
+
* value: 0
|
|
62
|
+
* }
|
|
63
|
+
*/
|
|
64
|
+
if(!signalVout || !signalVout.scriptpubkey_asm.includes('OP_RETURN')) {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Construct output map for easier access
|
|
69
|
+
const outputMap = new Map<string, string | number>(Object.entries(signalVout));
|
|
70
|
+
|
|
71
|
+
// Grab the signal vout scriptpubkey
|
|
72
|
+
const signalVoutScriptPubkey = outputMap.get('scriptpubkey_asm') as string;
|
|
73
|
+
|
|
74
|
+
// If the signal vout scriptpubkey does not exist, continue to next signal
|
|
75
|
+
if(!signalVoutScriptPubkey){
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Extract hex string hash of the signal bytes from the scriptpubkey
|
|
80
|
+
const updateHash = signalVoutScriptPubkey.split(' ').slice(-1)[0];
|
|
81
|
+
if(!updateHash) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Use the pre-fetched block count instead of calling per-signal
|
|
86
|
+
const confirmations = currentBlockCount - beaconSignal.status.block_height + 1;
|
|
87
|
+
|
|
88
|
+
// Push the beacon signal object to the signals array for the beacon service
|
|
89
|
+
beaconServiceSignals.get(beaconService)?.push({
|
|
90
|
+
tx : beaconSignal,
|
|
91
|
+
signalBytes : updateHash,
|
|
92
|
+
blockMetadata : {
|
|
93
|
+
confirmations,
|
|
94
|
+
height : beaconSignal.status.block_height,
|
|
95
|
+
time : beaconSignal.status.block_time,
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return beaconServiceSignals;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Traverse the full blockchain from genesis to chain top looking for beacon signals.
|
|
106
|
+
* @param {Array<BeaconService>} beaconServices Array of BeaconService objects to search for signals.
|
|
107
|
+
* @param {BitcoinNetworkConnection} bitcoin Bitcoin network connection to use for RPC calls.
|
|
108
|
+
* @returns {Promise<Map<BeaconService, Array<BeaconSignal>>>} Map of beacon service to its discovered signals.
|
|
109
|
+
*/
|
|
110
|
+
static async fullnode(
|
|
111
|
+
beaconServices: Array<BeaconService>,
|
|
112
|
+
bitcoin: BitcoinNetworkConnection
|
|
113
|
+
): Promise<Map<BeaconService, Array<BeaconSignal>>> {
|
|
114
|
+
const beaconServiceSignals = new Map<BeaconService, Array<BeaconSignal>>();
|
|
115
|
+
|
|
116
|
+
for(const beaconService of beaconServices) {
|
|
117
|
+
beaconServiceSignals.set(beaconService, []);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Get the RPC connection from the bitcoin network
|
|
121
|
+
const rpc = bitcoin.network.rpc;
|
|
122
|
+
|
|
123
|
+
// Ensure that the RPC connection is available
|
|
124
|
+
if(!rpc) {
|
|
125
|
+
throw new ResolveError('RPC connection is not available', 'RPC_CONNECTION_ERROR', bitcoin);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Get the current block height once before the loop
|
|
129
|
+
const targetHeight = await rpc.getBlockCount();
|
|
130
|
+
|
|
131
|
+
// Hoist the beacon services map before the loop
|
|
132
|
+
const beaconServicesMap = BeaconUtils.getBeaconServicesMap(beaconServices);
|
|
133
|
+
|
|
134
|
+
// Set genesis height
|
|
135
|
+
let height = 0;
|
|
136
|
+
|
|
137
|
+
// Opt into rpc connection to get the block data at the blockhash
|
|
138
|
+
let block = await bitcoin.network.rpc!.getBlock({ height }) as BlockV3;
|
|
139
|
+
|
|
140
|
+
console.info(`Searching for beacon signals, please wait ...`);
|
|
141
|
+
while (block.height <= targetHeight) {
|
|
142
|
+
// Iterate over each transaction in the block
|
|
143
|
+
for (const tx of block.tx) {
|
|
144
|
+
// If the txid is a coinbase, continue ...
|
|
145
|
+
if (tx.txid === GENESIS_TX_ID) {
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Iterate over each input in the transaction
|
|
150
|
+
for (const vin of tx.vin) {
|
|
151
|
+
|
|
152
|
+
// If the vin is a coinbase transaction, continue ...
|
|
153
|
+
if (vin.coinbase) {
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// If the vin txinwitness contains a coinbase did, continue ...
|
|
158
|
+
if (vin.txinwitness && vin.txinwitness.length === 1 && vin.txinwitness[0] === TXIN_WITNESS_COINBASE) {
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// If the txid from the vin is undefined, continue ...
|
|
163
|
+
if (!vin.txid) {
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// If the vout from the vin is undefined, continue ...
|
|
168
|
+
if (vin.vout === undefined) {
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Get the previous output transaction data
|
|
173
|
+
const prevout = await rpc.getRawTransaction(vin.txid, 2) as RawTransactionV2;
|
|
174
|
+
|
|
175
|
+
// If the previous output vout at the vin.vout index is undefined, continue ...
|
|
176
|
+
if (!prevout.vout[vin.vout]) {
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Get the address from the scriptPubKey from the prevvout
|
|
181
|
+
const scriptPubKey = prevout.vout[vin.vout].scriptPubKey;
|
|
182
|
+
|
|
183
|
+
// If the scriptPubKey.address is undefined, continue ...
|
|
184
|
+
if (!scriptPubKey.address) {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Use the hoisted beaconServicesMap instead of rebuilding per-vin
|
|
189
|
+
const beaconService = beaconServicesMap.get(scriptPubKey.address);
|
|
190
|
+
if (!beaconService) {
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Look for 'OP_RETURN' in the scriptPubKey asm
|
|
195
|
+
const txVoutScriptPubkeyAsm = prevout.vout[vin.vout].scriptPubKey.asm;
|
|
196
|
+
if(!txVoutScriptPubkeyAsm.includes('OP_RETURN')) {
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Log the found txid and beacon
|
|
201
|
+
console.info(`Tx ${tx.txid} contains beacon service address ${scriptPubKey.address} and OP_RETURN!`, tx);
|
|
202
|
+
|
|
203
|
+
// Extract hex string hash of the signal bytes from the scriptpubkey
|
|
204
|
+
const updateHash = txVoutScriptPubkeyAsm.split(' ').slice(-1)[0];
|
|
205
|
+
if(!updateHash) {
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Push the beacon signal object to the beacon signals array for that beacon service
|
|
210
|
+
beaconServiceSignals.get(beaconService)?.push({
|
|
211
|
+
tx,
|
|
212
|
+
signalBytes : updateHash,
|
|
213
|
+
blockMetadata : {
|
|
214
|
+
height : block.height,
|
|
215
|
+
time : block.time,
|
|
216
|
+
confirmations : block.confirmations
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Increment the height
|
|
223
|
+
height += 1;
|
|
224
|
+
|
|
225
|
+
// Use pre-fetched targetHeight instead of calling rpc.getBlockCount() every iteration
|
|
226
|
+
if(height > targetHeight) {
|
|
227
|
+
console.info(`Chain tip reached ${height}, breaking ...`);
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Reset the block var to the next block data
|
|
232
|
+
block = await rpc.getBlock({ height }) as BlockV3;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return beaconServiceSignals;
|
|
236
|
+
}
|
|
237
|
+
}
|