@bsv/sdk 1.10.4 → 2.0.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/dist/cjs/mod.js +1 -0
- package/dist/cjs/mod.js.map +1 -1
- package/dist/cjs/package.json +2 -3
- package/dist/cjs/src/auth/Peer.js +18 -20
- package/dist/cjs/src/auth/Peer.js.map +1 -1
- package/dist/cjs/src/identity/IdentityClient.js +20 -124
- package/dist/cjs/src/identity/IdentityClient.js.map +1 -1
- package/dist/cjs/src/primitives/TransactionSignature.js +115 -10
- package/dist/cjs/src/primitives/TransactionSignature.js.map +1 -1
- package/dist/cjs/src/primitives/utils.js +13 -112
- package/dist/cjs/src/primitives/utils.js.map +1 -1
- package/dist/cjs/src/remittance/CommsLayer.js +3 -0
- package/dist/cjs/src/remittance/CommsLayer.js.map +1 -0
- package/dist/cjs/src/remittance/IdentityLayer.js +3 -0
- package/dist/cjs/src/remittance/IdentityLayer.js.map +1 -0
- package/dist/cjs/src/remittance/RemittanceManager.js +1245 -0
- package/dist/cjs/src/remittance/RemittanceManager.js.map +1 -0
- package/dist/cjs/src/remittance/RemittanceModule.js +3 -0
- package/dist/cjs/src/remittance/RemittanceModule.js.map +1 -0
- package/dist/cjs/src/remittance/index.js +23 -0
- package/dist/cjs/src/remittance/index.js.map +1 -0
- package/dist/cjs/src/remittance/modules/BasicBRC29.js +225 -0
- package/dist/cjs/src/remittance/modules/BasicBRC29.js.map +1 -0
- package/dist/cjs/src/remittance/modules/index.js +18 -0
- package/dist/cjs/src/remittance/modules/index.js.map +1 -0
- package/dist/cjs/src/remittance/types.js +22 -0
- package/dist/cjs/src/remittance/types.js.map +1 -0
- package/dist/cjs/src/script/OP.js +15 -13
- package/dist/cjs/src/script/OP.js.map +1 -1
- package/dist/cjs/src/script/Script.js +4 -1
- package/dist/cjs/src/script/Script.js.map +1 -1
- package/dist/cjs/src/script/Spend.js +128 -46
- package/dist/cjs/src/script/Spend.js.map +1 -1
- package/dist/cjs/src/transaction/BeefTx.js +2 -2
- package/dist/cjs/src/transaction/Transaction.js +160 -0
- package/dist/cjs/src/transaction/Transaction.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/mod.js +1 -0
- package/dist/esm/mod.js.map +1 -1
- package/dist/esm/src/auth/Peer.js +18 -20
- package/dist/esm/src/auth/Peer.js.map +1 -1
- package/dist/esm/src/identity/IdentityClient.js +20 -124
- package/dist/esm/src/identity/IdentityClient.js.map +1 -1
- package/dist/esm/src/primitives/TransactionSignature.js +115 -10
- package/dist/esm/src/primitives/TransactionSignature.js.map +1 -1
- package/dist/esm/src/primitives/utils.js +13 -112
- package/dist/esm/src/primitives/utils.js.map +1 -1
- package/dist/esm/src/remittance/CommsLayer.js +2 -0
- package/dist/esm/src/remittance/CommsLayer.js.map +1 -0
- package/dist/esm/src/remittance/IdentityLayer.js +2 -0
- package/dist/esm/src/remittance/IdentityLayer.js.map +1 -0
- package/dist/esm/src/remittance/RemittanceManager.js +1254 -0
- package/dist/esm/src/remittance/RemittanceManager.js.map +1 -0
- package/dist/esm/src/remittance/RemittanceModule.js +2 -0
- package/dist/esm/src/remittance/RemittanceModule.js.map +1 -0
- package/dist/esm/src/remittance/index.js +7 -0
- package/dist/esm/src/remittance/index.js.map +1 -0
- package/dist/esm/src/remittance/modules/BasicBRC29.js +227 -0
- package/dist/esm/src/remittance/modules/BasicBRC29.js.map +1 -0
- package/dist/esm/src/remittance/modules/index.js +2 -0
- package/dist/esm/src/remittance/modules/index.js.map +1 -0
- package/dist/esm/src/remittance/types.js +19 -0
- package/dist/esm/src/remittance/types.js.map +1 -0
- package/dist/esm/src/script/OP.js +15 -13
- package/dist/esm/src/script/OP.js.map +1 -1
- package/dist/esm/src/script/Script.js +4 -1
- package/dist/esm/src/script/Script.js.map +1 -1
- package/dist/esm/src/script/Spend.js +129 -46
- package/dist/esm/src/script/Spend.js.map +1 -1
- package/dist/esm/src/transaction/BeefTx.js +3 -3
- package/dist/esm/src/transaction/BeefTx.js.map +1 -1
- package/dist/esm/src/transaction/Transaction.js +160 -0
- package/dist/esm/src/transaction/Transaction.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/mod.d.ts +1 -0
- package/dist/types/mod.d.ts.map +1 -1
- package/dist/types/src/auth/Peer.d.ts +3 -7
- package/dist/types/src/auth/Peer.d.ts.map +1 -1
- package/dist/types/src/identity/IdentityClient.d.ts +0 -8
- package/dist/types/src/identity/IdentityClient.d.ts.map +1 -1
- package/dist/types/src/primitives/TransactionSignature.d.ts +16 -4
- package/dist/types/src/primitives/TransactionSignature.d.ts.map +1 -1
- package/dist/types/src/primitives/utils.d.ts +1 -0
- package/dist/types/src/primitives/utils.d.ts.map +1 -1
- package/dist/types/src/remittance/CommsLayer.d.ts +50 -0
- package/dist/types/src/remittance/CommsLayer.d.ts.map +1 -0
- package/dist/types/src/remittance/IdentityLayer.d.ts +35 -0
- package/dist/types/src/remittance/IdentityLayer.d.ts.map +1 -0
- package/dist/types/src/remittance/RemittanceManager.d.ts +452 -0
- package/dist/types/src/remittance/RemittanceManager.d.ts.map +1 -0
- package/dist/types/src/remittance/RemittanceModule.d.ts +106 -0
- package/dist/types/src/remittance/RemittanceModule.d.ts.map +1 -0
- package/dist/types/src/remittance/index.d.ts +7 -0
- package/dist/types/src/remittance/index.d.ts.map +1 -0
- package/dist/types/src/remittance/modules/BasicBRC29.d.ts +133 -0
- package/dist/types/src/remittance/modules/BasicBRC29.d.ts.map +1 -0
- package/dist/types/src/remittance/modules/index.d.ts +2 -0
- package/dist/types/src/remittance/modules/index.d.ts.map +1 -0
- package/dist/types/src/remittance/types.d.ts +238 -0
- package/dist/types/src/remittance/types.d.ts.map +1 -0
- package/dist/types/src/script/OP.d.ts +5 -3
- package/dist/types/src/script/OP.d.ts.map +1 -1
- package/dist/types/src/script/Script.d.ts.map +1 -1
- package/dist/types/src/script/Spend.d.ts +7 -0
- package/dist/types/src/script/Spend.d.ts.map +1 -1
- package/dist/types/src/transaction/BeefTx.d.ts +2 -2
- package/dist/types/src/transaction/Transaction.d.ts +14 -0
- package/dist/types/src/transaction/Transaction.d.ts.map +1 -1
- package/dist/types/src/wallet/Wallet.interfaces.d.ts +5 -5
- package/dist/types/src/wallet/Wallet.interfaces.d.ts.map +1 -1
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/dist/umd/bundle.js +13 -13
- package/dist/umd/bundle.js.map +1 -1
- package/docs/index.md +2 -14
- package/docs/reference/auth.md +6 -12
- package/docs/reference/primitives.md +20 -78
- package/docs/reference/remittance.md +2166 -0
- package/docs/reference/script.md +11 -3
- package/docs/reference/transaction.md +27 -1
- package/docs/reference/wallet.md +6 -5
- package/docs/remittance-getting-started.md +138 -0
- package/mod.ts +1 -0
- package/package.json +12 -3
- package/src/auth/Peer.ts +18 -29
- package/src/auth/__tests/Peer.test.ts +253 -1
- package/src/identity/IdentityClient.ts +29 -153
- package/src/identity/__tests/IdentityClient.test.ts +1 -289
- package/src/overlay-tools/__tests/SHIPBroadcaster.test.ts +7 -9
- package/src/primitives/TransactionSignature.ts +129 -10
- package/src/primitives/__tests/utils.test.ts +30 -7
- package/src/primitives/utils.ts +13 -129
- package/src/remittance/CommsLayer.ts +41 -0
- package/src/remittance/IdentityLayer.ts +32 -0
- package/src/remittance/RemittanceManager.ts +1672 -0
- package/src/remittance/RemittanceModule.ts +92 -0
- package/src/remittance/__tests/BasicBRC29.test.ts +188 -0
- package/src/remittance/__tests/RemittanceManager.test.ts +493 -0
- package/src/remittance/__tests/examples.ts +130 -0
- package/src/remittance/index.ts +6 -0
- package/src/remittance/modules/BasicBRC29.ts +361 -0
- package/src/remittance/modules/index.ts +1 -0
- package/src/remittance/types.ts +284 -0
- package/src/script/OP.ts +15 -13
- package/src/script/Script.ts +3 -1
- package/src/script/Spend.ts +128 -52
- package/src/script/__tests/Chronicle.test.ts +186 -0
- package/src/script/__tests/Spend.test.ts +1 -1
- package/src/script/__tests/SpendValildVectors.test.ts +63 -0
- package/src/script/__tests/lrshiftnum.test.ts +185 -0
- package/src/script/__tests/sighashTestData.ts +1031 -0
- package/src/script/__tests/spend.valid.vectors.ts +9 -16
- package/src/transaction/BeefTx.ts +3 -3
- package/src/transaction/Transaction.ts +186 -0
- package/src/transaction/__tests/Beef.test.ts +2 -0
- package/src/transaction/__tests/Transaction.test.ts +641 -3
- package/src/wallet/Wallet.interfaces.ts +5 -5
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Invoice,
|
|
3
|
+
RemittanceOptionId,
|
|
4
|
+
Termination
|
|
5
|
+
} from '../types.js'
|
|
6
|
+
import type { ModuleContext } from '../types.js'
|
|
7
|
+
import type { RemittanceModule } from '../RemittanceModule.js'
|
|
8
|
+
import type {
|
|
9
|
+
WalletInterface,
|
|
10
|
+
WalletCounterparty,
|
|
11
|
+
PubKeyHex,
|
|
12
|
+
OriginatorDomainNameStringUnder250Bytes,
|
|
13
|
+
WalletProtocol
|
|
14
|
+
} from '../../wallet/Wallet.interfaces.js'
|
|
15
|
+
import { createNonce } from '../../auth/utils/createNonce.js'
|
|
16
|
+
import P2PKH from '../../script/templates/P2PKH.js'
|
|
17
|
+
import PublicKey from '../../primitives/PublicKey.js'
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* BRC-29-like payment option terms.
|
|
21
|
+
*
|
|
22
|
+
* This module intentionally keeps option terms minimal:
|
|
23
|
+
* - Amount is taken from the invoice total (and validated as satoshis)
|
|
24
|
+
* - The payer derives the payee's per-payment public key using wallet.getPublicKey with a stable protocolID
|
|
25
|
+
*/
|
|
26
|
+
export interface Brc29OptionTerms {
|
|
27
|
+
/** Payment amount in satoshis. */
|
|
28
|
+
amountSatoshis: number
|
|
29
|
+
/** The recipient of the payment */
|
|
30
|
+
payee: PubKeyHex
|
|
31
|
+
/** Which output index to internalize, default 0. */
|
|
32
|
+
outputIndex?: number
|
|
33
|
+
/** Optionally override the protocolID used in getPublicKey. */
|
|
34
|
+
protocolID?: WalletProtocol
|
|
35
|
+
/** Optional labels for createAction. */
|
|
36
|
+
labels?: string[]
|
|
37
|
+
/** Optional description for createAction. */
|
|
38
|
+
description?: string
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Settlement artifact carried in the settlement message.
|
|
43
|
+
*/
|
|
44
|
+
export interface Brc29SettlementArtifact {
|
|
45
|
+
customInstructions: {
|
|
46
|
+
derivationPrefix: string
|
|
47
|
+
derivationSuffix: string
|
|
48
|
+
}
|
|
49
|
+
transaction: number[]
|
|
50
|
+
amountSatoshis: number
|
|
51
|
+
outputIndex?: number
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Receipt data for BRC-29 settlements.
|
|
56
|
+
*/
|
|
57
|
+
export interface Brc29ReceiptData {
|
|
58
|
+
/** Result returned from wallet.internalizeAction, if accepted. */
|
|
59
|
+
internalizeResult?: unknown
|
|
60
|
+
/** Human-readable rejection reason, if rejected. */
|
|
61
|
+
rejectedReason?: string
|
|
62
|
+
/** If rejected with refund, contains the refund payment token. */
|
|
63
|
+
refund?: {
|
|
64
|
+
token: Brc29SettlementArtifact
|
|
65
|
+
feeSatoshis: number
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface NonceProvider {
|
|
70
|
+
createNonce: (wallet: WalletInterface, scope: WalletCounterparty, originator?: unknown) => Promise<string>
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface LockingScriptProvider {
|
|
74
|
+
/** Converts a public key string to a P2PKH locking script hex. */
|
|
75
|
+
pubKeyToP2PKHLockingScript: (publicKey: string) => Promise<string> | string
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Default nonce provider using SDK createNonce.
|
|
80
|
+
*/
|
|
81
|
+
export const DefaultNonceProvider: NonceProvider = {
|
|
82
|
+
async createNonce(wallet, scope, originator) {
|
|
83
|
+
const origin = originator as OriginatorDomainNameStringUnder250Bytes | undefined
|
|
84
|
+
return await createNonce(wallet, scope, origin)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Default locking script provider using SDK P2PKH template.
|
|
90
|
+
*/
|
|
91
|
+
export const DefaultLockingScriptProvider: LockingScriptProvider = {
|
|
92
|
+
async pubKeyToP2PKHLockingScript(publicKey: string) {
|
|
93
|
+
const address = PublicKey.fromString(publicKey).toAddress()
|
|
94
|
+
return new P2PKH().lock(address).toHex()
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface Brc29RemittanceModuleConfig {
|
|
99
|
+
/** Default protocolID to use with wallet.getPublicKey. */
|
|
100
|
+
protocolID?: WalletProtocol
|
|
101
|
+
/** Labels applied to created actions. */
|
|
102
|
+
labels?: string[]
|
|
103
|
+
/** Description applied to created actions. */
|
|
104
|
+
description?: string
|
|
105
|
+
/** Output description for created actions. */
|
|
106
|
+
outputDescription?: string
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Fee charged on refunds, in satoshis.
|
|
110
|
+
*/
|
|
111
|
+
refundFeeSatoshis?: number
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Minimum refund to issue. If refund would be smaller, module will reject without refund.
|
|
115
|
+
*/
|
|
116
|
+
minRefundSatoshis?: number
|
|
117
|
+
|
|
118
|
+
/** How wallet internalizes the payment. */
|
|
119
|
+
internalizeProtocol?: 'wallet payment' | 'basket insertion'
|
|
120
|
+
|
|
121
|
+
nonceProvider?: NonceProvider
|
|
122
|
+
lockingScriptProvider?: LockingScriptProvider
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* BRC-29-based remittance module.
|
|
127
|
+
* - payer creates a payment action to a derived P2PKH output
|
|
128
|
+
* - payer sends { tx, derivationPrefix, derivationSuffix } as settlement artifact
|
|
129
|
+
* - payee internalizes the tx output using wallet.internalizeAction
|
|
130
|
+
* - optional rejection can include a refund token embedded in the termination details
|
|
131
|
+
*/
|
|
132
|
+
export class Brc29RemittanceModule
|
|
133
|
+
implements RemittanceModule<Brc29OptionTerms, Brc29SettlementArtifact, Brc29ReceiptData> {
|
|
134
|
+
readonly id: RemittanceOptionId = 'brc29.p2pkh'
|
|
135
|
+
readonly name = 'BSV (BRC-29 derived P2PKH)'
|
|
136
|
+
readonly allowUnsolicitedSettlements = true
|
|
137
|
+
|
|
138
|
+
private readonly protocolID: WalletProtocol
|
|
139
|
+
private readonly labels: string[]
|
|
140
|
+
private readonly description: string
|
|
141
|
+
private readonly outputDescription: string
|
|
142
|
+
private readonly refundFeeSatoshis: number
|
|
143
|
+
private readonly minRefundSatoshis: number
|
|
144
|
+
private readonly internalizeProtocol: 'wallet payment' | 'basket insertion'
|
|
145
|
+
private readonly nonceProvider: NonceProvider
|
|
146
|
+
private readonly lockingScriptProvider: LockingScriptProvider
|
|
147
|
+
|
|
148
|
+
constructor(cfg: Brc29RemittanceModuleConfig = {}) {
|
|
149
|
+
// BRC-29 Protocol.
|
|
150
|
+
this.protocolID = cfg.protocolID ?? [2, '3241645161d8']
|
|
151
|
+
this.labels = cfg.labels ?? ['brc29']
|
|
152
|
+
this.description = cfg.description ?? 'BRC-29 payment'
|
|
153
|
+
this.outputDescription = cfg.outputDescription ?? 'Payment for remittance invoice'
|
|
154
|
+
this.refundFeeSatoshis = cfg.refundFeeSatoshis ?? 1000
|
|
155
|
+
this.minRefundSatoshis = cfg.minRefundSatoshis ?? 1000
|
|
156
|
+
this.internalizeProtocol = cfg.internalizeProtocol ?? 'wallet payment'
|
|
157
|
+
this.nonceProvider = cfg.nonceProvider ?? DefaultNonceProvider
|
|
158
|
+
this.lockingScriptProvider = cfg.lockingScriptProvider ?? DefaultLockingScriptProvider
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async buildSettlement(
|
|
162
|
+
args: { threadId: string; option: Brc29OptionTerms; note?: string },
|
|
163
|
+
ctx: ModuleContext
|
|
164
|
+
): Promise<{ action: 'settle'; artifact: Brc29SettlementArtifact } | { action: 'terminate'; termination: Termination }> {
|
|
165
|
+
const { wallet, originator } = ctx
|
|
166
|
+
|
|
167
|
+
let option: Brc29OptionTerms
|
|
168
|
+
try {
|
|
169
|
+
option = ensureValidOption(args.option)
|
|
170
|
+
} catch (error) {
|
|
171
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
172
|
+
return terminate('brc29.invalid_option', message)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const amountSatoshis = option.amountSatoshis
|
|
176
|
+
const origin = originator as OriginatorDomainNameStringUnder250Bytes | undefined
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
// Create per-payment derivation values.
|
|
180
|
+
const derivationPrefix = await this.nonceProvider.createNonce(wallet, 'self', origin)
|
|
181
|
+
const derivationSuffix = await this.nonceProvider.createNonce(wallet, 'self', origin)
|
|
182
|
+
|
|
183
|
+
// Derive payee public key.
|
|
184
|
+
const protocolID = option.protocolID ?? this.protocolID
|
|
185
|
+
const keyID = `${derivationPrefix} ${derivationSuffix}`
|
|
186
|
+
|
|
187
|
+
const { publicKey } = await wallet.getPublicKey(
|
|
188
|
+
{
|
|
189
|
+
protocolID,
|
|
190
|
+
keyID,
|
|
191
|
+
counterparty: option.payee
|
|
192
|
+
},
|
|
193
|
+
origin
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
if (typeof publicKey !== 'string' || publicKey.trim() === '') {
|
|
197
|
+
return terminate('brc29.public_key_missing', 'Failed to derive payee public key for BRC-29 settlement.')
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const lockingScript = await this.lockingScriptProvider.pubKeyToP2PKHLockingScript(publicKey)
|
|
201
|
+
if (typeof lockingScript !== 'string' || lockingScript.trim() === '') {
|
|
202
|
+
return terminate('brc29.locking_script_missing', 'Failed to produce P2PKH locking script.')
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const action = await wallet.createAction(
|
|
206
|
+
{
|
|
207
|
+
description: option.description ?? this.description,
|
|
208
|
+
labels: option.labels ?? this.labels,
|
|
209
|
+
outputs: [
|
|
210
|
+
{
|
|
211
|
+
satoshis: amountSatoshis,
|
|
212
|
+
lockingScript,
|
|
213
|
+
customInstructions: JSON.stringify({
|
|
214
|
+
derivationPrefix,
|
|
215
|
+
derivationSuffix,
|
|
216
|
+
payee: option.payee,
|
|
217
|
+
threadId: args.threadId,
|
|
218
|
+
note: args.note
|
|
219
|
+
}),
|
|
220
|
+
outputDescription: this.outputDescription
|
|
221
|
+
}
|
|
222
|
+
],
|
|
223
|
+
options: {
|
|
224
|
+
randomizeOutputs: false
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
origin
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
const tx = action.tx ?? action.signableTransaction?.tx
|
|
231
|
+
if (tx == null) {
|
|
232
|
+
return terminate('brc29.missing_tx', 'wallet.createAction did not return a transaction.')
|
|
233
|
+
}
|
|
234
|
+
if (!isAtomicBeef(tx)) {
|
|
235
|
+
return terminate('brc29.invalid_tx', 'wallet.createAction returned an invalid transaction payload.')
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
action: 'settle',
|
|
240
|
+
artifact: {
|
|
241
|
+
customInstructions: { derivationPrefix, derivationSuffix },
|
|
242
|
+
transaction: tx,
|
|
243
|
+
amountSatoshis: option.amountSatoshis,
|
|
244
|
+
outputIndex: option.outputIndex ?? 0
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
} catch (error) {
|
|
248
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
249
|
+
return terminate('brc29.build_failed', `BRC-29 settlement failed: ${message}`)
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
async acceptSettlement(
|
|
254
|
+
args: { threadId: string; settlement: Brc29SettlementArtifact; sender: PubKeyHex },
|
|
255
|
+
ctx: ModuleContext
|
|
256
|
+
): Promise<{ action: 'accept'; receiptData?: Brc29ReceiptData } | { action: 'terminate'; termination: Termination }> {
|
|
257
|
+
const { wallet, originator } = ctx
|
|
258
|
+
const origin = originator as OriginatorDomainNameStringUnder250Bytes | undefined
|
|
259
|
+
console.log('acceptSettlement', args)
|
|
260
|
+
try {
|
|
261
|
+
const settlement = ensureValidSettlement(args.settlement)
|
|
262
|
+
const outputIndex = settlement.outputIndex ?? 0
|
|
263
|
+
debugger
|
|
264
|
+
const internalizeResult = await wallet.internalizeAction(
|
|
265
|
+
{
|
|
266
|
+
tx: settlement.transaction,
|
|
267
|
+
outputs: [
|
|
268
|
+
{
|
|
269
|
+
paymentRemittance: {
|
|
270
|
+
derivationPrefix: settlement.customInstructions.derivationPrefix,
|
|
271
|
+
derivationSuffix: settlement.customInstructions.derivationSuffix,
|
|
272
|
+
senderIdentityKey: args.sender
|
|
273
|
+
},
|
|
274
|
+
outputIndex,
|
|
275
|
+
protocol: this.internalizeProtocol
|
|
276
|
+
}
|
|
277
|
+
],
|
|
278
|
+
labels: this.labels,
|
|
279
|
+
description: 'BRC-29 payment received'
|
|
280
|
+
},
|
|
281
|
+
origin
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
return { action: 'accept', receiptData: { internalizeResult } }
|
|
285
|
+
} catch (error) {
|
|
286
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
287
|
+
return terminate('brc29.internalize_failed', `Failed to internalize BRC-29 settlement: ${message}`)
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function terminate(code: string, message: string, details?: unknown): { action: 'terminate'; termination: Termination } {
|
|
293
|
+
return { action: 'terminate', termination: { code, message, details } }
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function ensureValidOption(option: Brc29OptionTerms): Brc29OptionTerms {
|
|
297
|
+
if (option == null || typeof option !== 'object') {
|
|
298
|
+
throw new Error('BRC-29 option terms are required')
|
|
299
|
+
}
|
|
300
|
+
const amountSatoshis = (option as Brc29OptionTerms).amountSatoshis
|
|
301
|
+
if (!Number.isInteger(amountSatoshis) || amountSatoshis <= 0) {
|
|
302
|
+
throw new Error('BRC-29 option amount must be a positive integer')
|
|
303
|
+
}
|
|
304
|
+
const outputIndex = (option as Brc29OptionTerms).outputIndex
|
|
305
|
+
if (outputIndex != null && (!Number.isInteger(outputIndex) || outputIndex < 0)) {
|
|
306
|
+
throw new Error('BRC-29 option outputIndex must be a non-negative integer')
|
|
307
|
+
}
|
|
308
|
+
const protocolID = (option as Brc29OptionTerms).protocolID
|
|
309
|
+
if (protocolID != null) {
|
|
310
|
+
if (!Array.isArray(protocolID) || protocolID.length !== 2) {
|
|
311
|
+
throw new Error('BRC-29 option protocolID must be a tuple [number, string]')
|
|
312
|
+
}
|
|
313
|
+
const [protocolNumber, protocolString] = protocolID
|
|
314
|
+
if (!Number.isInteger(protocolNumber) || protocolNumber < 0 || !isNonEmptyString(protocolString)) {
|
|
315
|
+
throw new Error('BRC-29 option protocolID must be a tuple [number, string]')
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
const labels = (option as Brc29OptionTerms).labels
|
|
319
|
+
if (labels != null && (!Array.isArray(labels) || labels.some((label) => !isNonEmptyString(label)))) {
|
|
320
|
+
throw new Error('BRC-29 option labels must be a list of non-empty strings')
|
|
321
|
+
}
|
|
322
|
+
const description = (option as Brc29OptionTerms).description
|
|
323
|
+
if (description != null && !isNonEmptyString(description)) {
|
|
324
|
+
throw new Error('BRC-29 option description must be a non-empty string')
|
|
325
|
+
}
|
|
326
|
+
return option
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function ensureValidSettlement(settlement: Brc29SettlementArtifact): Brc29SettlementArtifact {
|
|
330
|
+
if (settlement == null || typeof settlement !== 'object') {
|
|
331
|
+
throw new Error('BRC-29 settlement artifact is required')
|
|
332
|
+
}
|
|
333
|
+
const instructions = settlement.customInstructions
|
|
334
|
+
if (instructions == null || typeof instructions !== 'object') {
|
|
335
|
+
throw new Error('BRC-29 settlement requires customInstructions')
|
|
336
|
+
}
|
|
337
|
+
if (!isNonEmptyString(instructions.derivationPrefix) || !isNonEmptyString(instructions.derivationSuffix)) {
|
|
338
|
+
throw new Error('BRC-29 settlement derivation values are required')
|
|
339
|
+
}
|
|
340
|
+
const amountSatoshis = settlement.amountSatoshis
|
|
341
|
+
if (!Number.isInteger(amountSatoshis) || amountSatoshis <= 0) {
|
|
342
|
+
throw new Error('BRC-29 settlement amount must be a positive integer')
|
|
343
|
+
}
|
|
344
|
+
const outputIndex = settlement.outputIndex
|
|
345
|
+
if (outputIndex != null && (!Number.isInteger(outputIndex) || outputIndex < 0)) {
|
|
346
|
+
throw new Error('BRC-29 settlement outputIndex must be a non-negative integer')
|
|
347
|
+
}
|
|
348
|
+
if (!isAtomicBeef(settlement.transaction)) {
|
|
349
|
+
throw new Error('BRC-29 settlement transaction must be a non-empty byte array')
|
|
350
|
+
}
|
|
351
|
+
return settlement
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function isAtomicBeef(tx: unknown): tx is number[] {
|
|
355
|
+
if (!Array.isArray(tx) || tx.length === 0) return false
|
|
356
|
+
return tx.every((byte) => Number.isInteger(byte) && byte >= 0 && byte <= 255)
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function isNonEmptyString(value: unknown): value is string {
|
|
360
|
+
return typeof value === 'string' && value.trim().length > 0
|
|
361
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './BasicBRC29.js'
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { HexString, OutpointString, PubKeyHex, Base64String, WalletInterface } from '../wallet/index.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Types for core Remittance protocol.
|
|
5
|
+
*
|
|
6
|
+
* The goal is to keep the core protocol:
|
|
7
|
+
* - UTXO-friendly (transactions and partial transactions can be carried as artifacts)
|
|
8
|
+
* - Denomination-agnostic (amounts are typed, not forced to satoshis)
|
|
9
|
+
* - Module-oriented (remittance option payloads are opaque to the core)
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export type ThreadId = Base64String
|
|
13
|
+
export type RemittanceOptionId = Base64String
|
|
14
|
+
export type UnixMillis = number
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Remittance thread state machine.
|
|
18
|
+
*
|
|
19
|
+
* States:
|
|
20
|
+
* - new: thread exists but no identity/invoice/settlement activity yet.
|
|
21
|
+
* - identityRequested: identity request sent or received.
|
|
22
|
+
* - identityResponded: identity response sent or received.
|
|
23
|
+
* - identityAcknowledged: identity response acknowledged (required before proceeding).
|
|
24
|
+
* - invoiced: invoice sent or received.
|
|
25
|
+
* - settled: settlement sent or received.
|
|
26
|
+
* - receipted: receipt issued or received.
|
|
27
|
+
* - terminated: thread terminated with a reason.
|
|
28
|
+
* - errored: unexpected error occurred while processing the thread.
|
|
29
|
+
*/
|
|
30
|
+
export type RemittanceThreadState =
|
|
31
|
+
| 'new'
|
|
32
|
+
| 'identityRequested'
|
|
33
|
+
| 'identityResponded'
|
|
34
|
+
| 'identityAcknowledged'
|
|
35
|
+
| 'invoiced'
|
|
36
|
+
| 'settled'
|
|
37
|
+
| 'receipted'
|
|
38
|
+
| 'terminated'
|
|
39
|
+
| 'errored'
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Allowed remittance state transitions.
|
|
43
|
+
*
|
|
44
|
+
* This is the canonical state machine for remittance threads.
|
|
45
|
+
* Use it to validate transitions and to build audits/visualizations.
|
|
46
|
+
*/
|
|
47
|
+
export const REMITTANCE_STATE_TRANSITIONS: Record<RemittanceThreadState, RemittanceThreadState[]> = {
|
|
48
|
+
new: ['identityRequested', 'invoiced', 'settled', 'terminated', 'errored'],
|
|
49
|
+
identityRequested: ['identityResponded', 'identityAcknowledged', 'invoiced', 'settled', 'terminated', 'errored'],
|
|
50
|
+
identityResponded: ['identityAcknowledged', 'invoiced', 'settled', 'terminated', 'errored'],
|
|
51
|
+
identityAcknowledged: ['invoiced', 'settled', 'terminated', 'errored'],
|
|
52
|
+
invoiced: ['identityRequested', 'identityResponded', 'identityAcknowledged', 'settled', 'terminated', 'errored'],
|
|
53
|
+
settled: ['receipted', 'terminated', 'errored'],
|
|
54
|
+
receipted: ['terminated', 'errored'],
|
|
55
|
+
terminated: ['errored'],
|
|
56
|
+
errored: []
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface Unit {
|
|
60
|
+
/** Namespace for disambiguation, e.g. 'bsv', 'iso4217', 'token'. */
|
|
61
|
+
namespace: string
|
|
62
|
+
/** Unit code within the namespace, e.g. 'sat', 'USD', 'mnee'. */
|
|
63
|
+
code: string
|
|
64
|
+
/** Optional decimal places for display/normalization. */
|
|
65
|
+
decimals?: number
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface Amount {
|
|
69
|
+
/** Decimal string. Avoid floats at the protocol layer. */
|
|
70
|
+
value: string
|
|
71
|
+
unit: Unit
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export const SAT_UNIT: Unit = { namespace: 'bsv', code: 'sat', decimals: 0 }
|
|
75
|
+
|
|
76
|
+
export interface LineItem {
|
|
77
|
+
id?: string
|
|
78
|
+
description: string
|
|
79
|
+
/** Decimal string, e.g. '1', '2', '0.5'. */
|
|
80
|
+
quantity?: string
|
|
81
|
+
unitPrice?: Amount
|
|
82
|
+
/** Total amount for the line (optional if derivable). */
|
|
83
|
+
amount?: Amount
|
|
84
|
+
metadata?: Record<string, unknown>
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Shared commercial/metadata fields for invoice and receipt-like instruments.
|
|
89
|
+
*
|
|
90
|
+
* NOTE: "payee" and "payer" are identity keys, not addresses.
|
|
91
|
+
* Payment addresses / scripts are settlement-module concerns.
|
|
92
|
+
*/
|
|
93
|
+
export interface InstrumentBase {
|
|
94
|
+
threadId: ThreadId
|
|
95
|
+
payee: PubKeyHex
|
|
96
|
+
payer: PubKeyHex
|
|
97
|
+
note?: string
|
|
98
|
+
lineItems: LineItem[]
|
|
99
|
+
total: Amount
|
|
100
|
+
invoiceNumber: string
|
|
101
|
+
createdAt: UnixMillis
|
|
102
|
+
arbitrary?: Record<string, unknown>
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Invoice (solicitation) that contains N remittance options.
|
|
107
|
+
*
|
|
108
|
+
* Each remittance option is keyed by a module id.
|
|
109
|
+
* The payload for each option is module-defined and opaque to the core.
|
|
110
|
+
*
|
|
111
|
+
* This is where “UTXO offers” live: a module option payload can include a partial tx template,
|
|
112
|
+
* UTXO references, scripts, overlay anchors, SPV, etc. The manager does not interpret them.
|
|
113
|
+
*/
|
|
114
|
+
export interface Invoice extends InstrumentBase {
|
|
115
|
+
kind: 'invoice'
|
|
116
|
+
expiresAt?: UnixMillis
|
|
117
|
+
options: Record<RemittanceOptionId, unknown>
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* An identity certificate request.
|
|
122
|
+
*
|
|
123
|
+
* Contains a list of requested certificate types, fields from each, plus acceptable certifiers.
|
|
124
|
+
*/
|
|
125
|
+
export interface IdentityVerificationRequest {
|
|
126
|
+
kind: 'identityVerificationRequest'
|
|
127
|
+
threadId: ThreadId
|
|
128
|
+
/** Details of the requested certificates. */
|
|
129
|
+
request: {
|
|
130
|
+
/** Map of certificate types to requested fields from each. */
|
|
131
|
+
types: Record<string, string[]>
|
|
132
|
+
/** List of acceptable certifier identity keys. */
|
|
133
|
+
certifiers: PubKeyHex[]
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* An identity certificate response.
|
|
139
|
+
*
|
|
140
|
+
* Contains certificates issued by the certifiers named in the corresponding request, with fields revealed to the counterparty.
|
|
141
|
+
*/
|
|
142
|
+
export interface IdentityVerificationResponse {
|
|
143
|
+
kind: 'identityVerificationResponse'
|
|
144
|
+
threadId: ThreadId
|
|
145
|
+
/** List of certificates issued by the certifiers named in the corresponding request, with fields revealed to the counterparty. */
|
|
146
|
+
certificates: Array<{
|
|
147
|
+
/** Certificate type, e.g. base64 Type ID corresponding to 'personalId', 'businessId', etc. */
|
|
148
|
+
type: Base64String
|
|
149
|
+
/** Certifier identity key. */
|
|
150
|
+
certifier: PubKeyHex
|
|
151
|
+
/** Subject identity key. */
|
|
152
|
+
subject: PubKeyHex
|
|
153
|
+
/** The certificate's encrypted fields that have been signed. */
|
|
154
|
+
fields: Record<string, Base64String>
|
|
155
|
+
/** Signature over the cert. */
|
|
156
|
+
signature: HexString
|
|
157
|
+
/** Certificate serial number. */
|
|
158
|
+
serialNumber: Base64String
|
|
159
|
+
/** Revocation outpoint. If spent on-chain, the certificate is invalid. */
|
|
160
|
+
revocationOutpoint: OutpointString
|
|
161
|
+
/** Field revelation keys for the counterparty. */
|
|
162
|
+
keyringForVerifier: Record<string, Base64String>
|
|
163
|
+
}>
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* An identity verification acknowledgment.
|
|
168
|
+
*
|
|
169
|
+
* A simple ack message indicating that a requested identity verification has been completed successfully.
|
|
170
|
+
*/
|
|
171
|
+
export interface IdentityVerificationAcknowledgment {
|
|
172
|
+
kind: 'identityVerificationAcknowledgment'
|
|
173
|
+
threadId: ThreadId
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* A settlement attempt.
|
|
178
|
+
*
|
|
179
|
+
* This is module-agnostic: "artifact" can be a transaction, a partial transaction,
|
|
180
|
+
* a stablecoin transfer result, even a fiat card-payment approval code, etc.
|
|
181
|
+
*/
|
|
182
|
+
export interface Settlement {
|
|
183
|
+
kind: 'settlement'
|
|
184
|
+
threadId: ThreadId
|
|
185
|
+
moduleId: RemittanceOptionId
|
|
186
|
+
optionId: RemittanceOptionId
|
|
187
|
+
sender: PubKeyHex
|
|
188
|
+
createdAt: UnixMillis
|
|
189
|
+
artifact: unknown
|
|
190
|
+
note?: string
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Receipt issued by the payee (or service provider).
|
|
195
|
+
*
|
|
196
|
+
* A receipt could be a PDF, a photo/oroof-of-delivery, a copy of the payment transaction, etc.
|
|
197
|
+
*
|
|
198
|
+
* A receipt should NOT be issued when a settlement is rejected/failed. Use a Termination instead.
|
|
199
|
+
*/
|
|
200
|
+
export interface Receipt {
|
|
201
|
+
kind: 'receipt'
|
|
202
|
+
threadId: ThreadId
|
|
203
|
+
moduleId: RemittanceOptionId
|
|
204
|
+
optionId: RemittanceOptionId
|
|
205
|
+
payee: PubKeyHex
|
|
206
|
+
payer: PubKeyHex
|
|
207
|
+
createdAt: UnixMillis
|
|
208
|
+
receiptData: unknown
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Termination details for failed operations.
|
|
213
|
+
*/
|
|
214
|
+
export interface Termination {
|
|
215
|
+
/** Reason code (module-specific). */
|
|
216
|
+
code: string
|
|
217
|
+
/** Human-readable message. */
|
|
218
|
+
message: string
|
|
219
|
+
/** Optional module-specific details or refund information. */
|
|
220
|
+
details?: unknown
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Transport message format expected from the CommsLayer.
|
|
225
|
+
*
|
|
226
|
+
* It closely matches the message-box-client shapes:
|
|
227
|
+
* messageId, sender, body, etc.
|
|
228
|
+
*/
|
|
229
|
+
export interface PeerMessage {
|
|
230
|
+
messageId: string
|
|
231
|
+
sender: PubKeyHex
|
|
232
|
+
recipient: PubKeyHex
|
|
233
|
+
messageBox: string
|
|
234
|
+
body: string
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Protocol envelope kinds.
|
|
239
|
+
* Everything runs in “threads” and carries a threadId.
|
|
240
|
+
*/
|
|
241
|
+
export type RemittanceKind =
|
|
242
|
+
| 'invoice'
|
|
243
|
+
| 'identityVerificationRequest'
|
|
244
|
+
| 'identityVerificationResponse'
|
|
245
|
+
| 'identityVerificationAcknowledgment'
|
|
246
|
+
| 'settlement'
|
|
247
|
+
| 'receipt'
|
|
248
|
+
| 'termination'
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Protocol envelope.
|
|
252
|
+
*
|
|
253
|
+
* This is what RemittanceManager serializes into the CommsLayer message body.
|
|
254
|
+
*/
|
|
255
|
+
export interface RemittanceEnvelope<K extends RemittanceKind = RemittanceKind, P = unknown> {
|
|
256
|
+
/** Protocol version. */
|
|
257
|
+
v: 1
|
|
258
|
+
/** Envelope id (idempotency key). Not the transport messageId. */
|
|
259
|
+
id: string
|
|
260
|
+
kind: K
|
|
261
|
+
threadId: ThreadId
|
|
262
|
+
createdAt: UnixMillis
|
|
263
|
+
payload: P
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Simple logger interface.
|
|
268
|
+
*/
|
|
269
|
+
export interface LoggerLike {
|
|
270
|
+
log: (...args: any[]) => void
|
|
271
|
+
warn: (...args: any[]) => void
|
|
272
|
+
error: (...args: any[]) => void
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Context object passed to module methods.
|
|
277
|
+
*/
|
|
278
|
+
export interface ModuleContext {
|
|
279
|
+
wallet: WalletInterface
|
|
280
|
+
/** Optional originator domain forwarded to wallet methods. */
|
|
281
|
+
originator?: unknown
|
|
282
|
+
now: () => number
|
|
283
|
+
logger?: LoggerLike
|
|
284
|
+
}
|
package/src/script/OP.ts
CHANGED
|
@@ -63,12 +63,9 @@ const OP = {
|
|
|
63
63
|
|
|
64
64
|
// data manipulation ops
|
|
65
65
|
OP_CAT: 0x7e,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
OP_NUM2BIN: 0x80,
|
|
70
|
-
OP_RIGHT: 0x81, // Replaced in BSV
|
|
71
|
-
OP_BIN2NUM: 0x81,
|
|
66
|
+
OP_SPLIT: 0x7f, // after monolith upgrade (May 2018)
|
|
67
|
+
OP_NUM2BIN: 0x80, // after monolith upgrade (May 2018)
|
|
68
|
+
OP_BIN2NUM: 0x81, // after monolith upgrade (May 2018)
|
|
72
69
|
OP_SIZE: 0x82,
|
|
73
70
|
|
|
74
71
|
// bit logic
|
|
@@ -127,13 +124,18 @@ const OP = {
|
|
|
127
124
|
|
|
128
125
|
// expansion
|
|
129
126
|
OP_NOP1: 0xb0,
|
|
130
|
-
OP_NOP2: 0xb1,
|
|
131
|
-
OP_NOP3: 0xb2,
|
|
132
|
-
OP_NOP4: 0xb3,
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
127
|
+
OP_NOP2: 0xb1, // Used on BTC for OP_CHECKLOCKTIMEVERIFY
|
|
128
|
+
OP_NOP3: 0xb2, // Used on BTC for OP_CHECKSEQUENCEVERIFY
|
|
129
|
+
OP_NOP4: 0xb3, // OP_NOP4 allocated to restore OP_SUBSTR in 2026 CHRONICLE upgrade
|
|
130
|
+
OP_SUBSTR: 0xb3, // OP_NOP4 allocated to restore OP_SUBSTR in 2026 CHRONICLE upgrade
|
|
131
|
+
OP_NOP5: 0xb4, // OP_NOP5 allocated to restore OP_LEFT in 2026 CHRONICLE upgrade
|
|
132
|
+
OP_LEFT: 0xb4, // OP_NOP5 allocated to restore OP_LEFT in 2026 CHRONICLE upgrade
|
|
133
|
+
OP_NOP6: 0xb5, // OP_NOP6 allocated to restore OP_RIGHT in 2026 CHRONICLE upgrade
|
|
134
|
+
OP_RIGHT: 0xb5, // OP_NOP6 allocated to restore OP_RIGHT in 2026 CHRONICLE upgrade
|
|
135
|
+
OP_NOP7: 0xb6, // OP_NOP7 allocated to restore OP_LSHIFTNUM in 2026 CHRONICLE upgrade
|
|
136
|
+
OP_LSHIFTNUM: 0xb6, // OP_NOP7 allocated to restore OP_LSHIFTNUM in 2026 CHRONICLE upgrade
|
|
137
|
+
OP_NOP8: 0xb7, // OP_NOP7 allocated to restore OP_RSHIFTNUM in 2026 CHRONICLE upgrade
|
|
138
|
+
OP_RSHIFTNUM: 0xb7, // OP_NOP7 allocated to restore OP_RSHIFTNUM in 2026 CHRONICLE upgrade
|
|
137
139
|
OP_NOP9: 0xb8,
|
|
138
140
|
OP_NOP10: 0xb9,
|
|
139
141
|
OP_NOP11: 0xba,
|