@vbyte/btc-dev 1.0.6 → 1.0.8

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.
@@ -1,6 +1,6 @@
1
1
  import type { LocktimeData, SequenceData } from './meta.js';
2
2
  import type { TxOutput, TxOutputType, TxSize, TxValue } from './txdata.js';
3
- import type { WitnessType, WitnessVersion } from './witness.js';
3
+ import type { WitnessData, WitnessVersion } from './witness.js';
4
4
  export interface LocktimeField {
5
5
  hex: string;
6
6
  data: LocktimeData | null;
@@ -27,17 +27,6 @@ export interface TransactionData {
27
27
  vin: TransactionInputData[];
28
28
  vout: TransactionOutputData[];
29
29
  }
30
- export interface WitnessField {
31
- annex: string | null;
32
- cblock: string | null;
33
- params: string[];
34
- script: ScriptField | null;
35
- size: number;
36
- stack: string[];
37
- type: WitnessType;
38
- version: WitnessVersion;
39
- vsize: number;
40
- }
41
30
  export interface TransactionInputData {
42
31
  coinbase?: string | null;
43
32
  prevout?: TransactionOutputData | null;
@@ -46,7 +35,7 @@ export interface TransactionInputData {
46
35
  size: number;
47
36
  txid: string;
48
37
  vout: number;
49
- witness?: WitnessField | null;
38
+ witness?: WitnessData | null;
50
39
  }
51
40
  export interface TransactionOutputData {
52
41
  script_pk: ScriptField;
@@ -55,7 +55,7 @@ export interface TxData {
55
55
  }
56
56
  export interface TxSize {
57
57
  base: number;
58
- real: number;
58
+ total: number;
59
59
  weight: number;
60
60
  vsize: number;
61
61
  }
@@ -1,34 +1,35 @@
1
- export type WitnessContext = WitnessInfo | TaprootScript | SegwitScript | TaprootSpend | SegwitSpend;
1
+ export type WitnessContext = WitnessData | TaprootScript | SegwitScript | TaprootSpend | SegwitSpend;
2
2
  export type WitnessVersion = number | null;
3
3
  export type WitnessType = 'p2w-pkh' | 'p2w-sh' | 'p2tr-pk' | 'p2tr-ts' | 'unknown';
4
4
  export interface WitnessSize {
5
- size: number;
5
+ total: number;
6
6
  vsize: number;
7
7
  }
8
- export interface WitnessInfo {
8
+ export interface WitnessData {
9
9
  annex: string | null;
10
10
  cblock: string | null;
11
11
  params: string[];
12
12
  script: string | null;
13
+ stack: string[];
13
14
  type: WitnessType;
14
15
  version: WitnessVersion;
15
16
  }
16
- export interface TaprootScript extends WitnessInfo {
17
+ export interface TaprootScript extends WitnessData {
17
18
  cblock: string;
18
19
  script: string;
19
20
  type: 'p2tr-ts';
20
21
  version: 1;
21
22
  }
22
- export interface SegwitScript extends WitnessInfo {
23
+ export interface SegwitScript extends WitnessData {
23
24
  script: string;
24
25
  type: 'p2w-sh';
25
26
  version: 0;
26
27
  }
27
- export interface TaprootSpend extends WitnessInfo {
28
+ export interface TaprootSpend extends WitnessData {
28
29
  type: 'p2tr-pk';
29
30
  version: 1;
30
31
  }
31
- export interface SegwitSpend extends WitnessInfo {
32
+ export interface SegwitSpend extends WitnessData {
32
33
  type: 'p2w-pkh';
33
34
  version: 0;
34
35
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vbyte/btc-dev",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "Batteries-included toolset for plebian bitcoin development",
5
5
  "type": "module",
6
6
  "keywords": [
package/src/class/tx.ts CHANGED
@@ -1,3 +1,5 @@
1
+
2
+ import { Assert } from '@vbyte/micro-lib'
1
3
  import { LocktimeUtil } from '@/lib/meta/index.js'
2
4
  import { TransactionInput } from './txin.js'
3
5
  import { TransactionOutput } from './txout.js'
@@ -11,36 +13,41 @@ import {
11
13
  get_tx_value,
12
14
  get_txhash,
13
15
  encode_tx_locktime,
16
+ create_tx_input,
17
+ create_tx_output,
14
18
  } from '@/lib/tx/index.js'
15
19
 
16
20
  import type {
17
21
  TxData,
18
22
  TxTemplate,
23
+ TransactionData,
24
+ TxOutput,
19
25
  TxSize,
20
26
  TxValue,
21
- TransactionData
27
+ TxInputTemplate
22
28
  } from '@/types/index.js'
23
29
 
24
30
  export class Transaction {
25
31
 
26
- private readonly _size : TxSize
27
- private readonly _tx : TxData
28
- private readonly _hash : string
29
- private readonly _value : TxValue
30
- private readonly _vin : TransactionInput[]
31
- private readonly _vout : TransactionOutput[]
32
+ private readonly _tx : TxData
33
+
34
+ private _size : TxSize & { segwit : number }
35
+ private _hash : string
36
+ private _txid : string
37
+ private _value : TxValue
38
+ private _vin : TransactionInput[]
39
+ private _vout : TransactionOutput[]
32
40
 
33
- constructor (txdata : string | TxData | TxTemplate) {
41
+ constructor (txdata : string | TxData | TxTemplate = {}) {
34
42
  this._tx = (typeof txdata !== 'string')
35
43
  ? parse_tx(txdata)
36
44
  : decode_tx(txdata)
37
-
38
- this._vin = this._tx.vin.map(txin => new TransactionInput(txin))
39
- this._vout = this._tx.vout.map(txout => new TransactionOutput(txout))
40
-
41
- this._size = get_txsize(this._tx)
45
+ this._size = this._get_size()
42
46
  this._hash = get_txhash(this._tx)
47
+ this._txid = get_txid(this._tx)
43
48
  this._value = get_tx_value(this._tx)
49
+ this._vin = this._tx.vin.map(txin => new TransactionInput(txin))
50
+ this._vout = this._tx.vout.map(txout => new TransactionOutput(txout))
44
51
  }
45
52
 
46
53
  get data () : TransactionData {
@@ -75,13 +82,7 @@ export class Transaction {
75
82
  }
76
83
 
77
84
  get size () {
78
- return {
79
- ...this._size,
80
- segwit : this._vin.reduce((acc, txin) => acc + (txin.witness?.vsize ?? 0), 0),
81
- vin : this._vin.reduce((acc, txin) => acc + txin.size, 0),
82
- vout : this._vout.reduce((acc, txout) => acc + txout.size, 0),
83
- witness : this._vin.reduce((acc, txin) => acc + (txin.witness?.size ?? 0), 0)
84
- }
85
+ return this._size
85
86
  }
86
87
 
87
88
  get spends () : TransactionOutput[] {
@@ -91,7 +92,7 @@ export class Transaction {
91
92
  }
92
93
 
93
94
  get txid () : string {
94
- return get_txid(this._tx)
95
+ return this._txid
95
96
  }
96
97
 
97
98
  get value () {
@@ -110,9 +111,79 @@ export class Transaction {
110
111
  return this._vout
111
112
  }
112
113
 
113
- toJSON () { return this.data }
114
- toString () { return JSON.stringify(this.data) }
115
- }
114
+ add_vin (tx_input : TxInputTemplate) {
115
+ const txin = create_tx_input(tx_input)
116
+ this._tx.vin.push(txin)
117
+ this._update_vin()
118
+ }
119
+
120
+ add_vout (tx_output : TxOutput) {
121
+ const txout = create_tx_output(tx_output)
122
+ this._tx.vout.push(txout)
123
+ this._update_vout()
124
+ }
116
125
 
126
+ insert_vin (index : number, tx_input : TxInputTemplate) {
127
+ Assert.ok(index >= 0 && index <= this._tx.vin.length, 'input goes out of bounds')
128
+ const txin = create_tx_input(tx_input)
129
+ if (index === this._tx.vin.length) {
130
+ this._tx.vin.push(txin)
131
+ } else {
132
+ this._tx.vin.splice(index, 0, txin)
133
+ }
134
+ this._update_vin()
135
+ }
117
136
 
137
+ insert_vout (index : number, tx_output : TxOutput) {
138
+ Assert.ok(index >= 0 && index <= this._tx.vout.length, 'output goes out of bounds')
139
+ const txout = create_tx_output(tx_output)
140
+ if (index === this._tx.vout.length) {
141
+ this._tx.vout.push(txout)
142
+ } else {
143
+ this._tx.vout.splice(index, 0, txout)
144
+ }
145
+ this._update_vout()
146
+ }
147
+
148
+ remove_vin (index : number) {
149
+ Assert.ok(this._tx.vin.at(index) !== undefined, 'input does not exist at index')
150
+ this._tx.vin.splice(index, 1)
151
+ this._update_vin()
152
+ }
118
153
 
154
+ remove_vout (index : number) {
155
+ Assert.ok(this._tx.vout.at(index) !== undefined, 'output does not exist at index')
156
+ this._tx.vout.splice(index, 1)
157
+ this._update_vout()
158
+ }
159
+
160
+ _get_size () {
161
+ return {
162
+ ...get_txsize(this._tx),
163
+ segwit : this.vin.reduce((acc, txin) => acc + (txin.witness?.size.vsize ?? 0), 0),
164
+ vin : this.vin.reduce((acc, txin) => acc + txin.size, 0),
165
+ vout : this.vout.reduce((acc, txout) => acc + txout.size, 0),
166
+ witness : this.vin.reduce((acc, txin) => acc + (txin.witness?.size.total ?? 0), 0)
167
+ }
168
+ }
169
+
170
+ _update_tx () {
171
+ this._size = this._get_size()
172
+ this._hash = get_txhash(this._tx)
173
+ this._txid = get_txid(this._tx)
174
+ this._value = get_tx_value(this._tx)
175
+ }
176
+
177
+ _update_vin () {
178
+ this._vin = this._tx.vin.map(txin => new TransactionInput(txin))
179
+ this._update_tx()
180
+ }
181
+
182
+ _update_vout () {
183
+ this._vout = this._tx.vout.map(txout => new TransactionOutput(txout))
184
+ this._update_tx()
185
+ }
186
+
187
+ toJSON () { return this.data }
188
+ toString () { return JSON.stringify(this.data) }
189
+ }
package/src/class/txin.ts CHANGED
@@ -15,16 +15,10 @@ import type {
15
15
 
16
16
  export class TransactionInput {
17
17
 
18
- private readonly _size : number
19
- private readonly _txin : TxInput
20
- private readonly _witness : TransactionWitness | null
18
+ private readonly _txin : TxInput
21
19
 
22
20
  constructor (txin : TxInput) {
23
- this._size = get_txin_size(txin)
24
- this._txin = txin
25
- this._witness = txin.witness.length > 0
26
- ? new TransactionWitness(txin.witness)
27
- : null
21
+ this._txin = txin
28
22
  }
29
23
 
30
24
  get coinbase () : string | null {
@@ -75,7 +69,7 @@ export class TransactionInput {
75
69
  }
76
70
 
77
71
  get size () {
78
- return this._size
72
+ return get_txin_size(this._txin)
79
73
  }
80
74
 
81
75
  get txid () : string {
@@ -87,7 +81,9 @@ export class TransactionInput {
87
81
  }
88
82
 
89
83
  get witness () {
90
- return this._witness
84
+ return this._txin.witness.length > 0
85
+ ? new TransactionWitness(this._txin.witness)
86
+ : null
91
87
  }
92
88
 
93
89
  toJSON () { return this.data }
@@ -2,20 +2,17 @@ import { decode_script } from '@/lib/script/index.js'
2
2
 
3
3
  import {
4
4
  get_txout_size,
5
- get_vout_info
5
+ get_vout_type,
6
+ get_vout_version
6
7
  } from '@/lib/tx/index.js'
7
8
 
8
- import type { TxOutput, TransactionOutputData, TxOutputInfo } from '@/types/index.js'
9
+ import type { TxOutput, TransactionOutputData } from '@/types/index.js'
9
10
 
10
11
  export class TransactionOutput {
11
12
 
12
- private readonly _info : TxOutputInfo
13
- private readonly _size : number
14
13
  private readonly _txout : TxOutput
15
14
 
16
15
  constructor (txout : TxOutput) {
17
- this._info = get_vout_info(txout)
18
- this._size = get_txout_size(txout)
19
16
  this._txout = txout
20
17
  }
21
18
 
@@ -37,11 +34,11 @@ export class TransactionOutput {
37
34
  }
38
35
 
39
36
  get size () {
40
- return this._size
37
+ return get_txout_size(this._txout)
41
38
  }
42
39
 
43
40
  get type () {
44
- return this._info.type
41
+ return get_vout_type(this._txout.script_pk)
45
42
  }
46
43
 
47
44
  get value () : bigint {
@@ -49,7 +46,7 @@ export class TransactionOutput {
49
46
  }
50
47
 
51
48
  get version () {
52
- return this._info.version
49
+ return get_vout_version(this._txout.script_pk)
53
50
  }
54
51
 
55
52
  toJSON () { return this.data }
@@ -1,83 +1,95 @@
1
1
  import { Buff, Bytes } from '@vbyte/buff'
2
+ import { Assert } from '@vbyte/micro-lib'
2
3
  import { decode_script } from '@/lib/script/index.js'
3
4
 
4
5
  import {
5
- parse_witness_data,
6
+ parse_witness,
6
7
  get_witness_size,
7
8
  } from '@/lib/witness/index.js'
8
9
 
9
10
  import type {
10
11
  ScriptField,
11
- WitnessField,
12
- WitnessInfo,
12
+ WitnessData,
13
13
  WitnessSize,
14
14
  WitnessType
15
15
  } from '@/types/index.js'
16
16
 
17
17
  export class TransactionWitness {
18
18
 
19
- private readonly _data : Buff[]
20
- private readonly _meta : WitnessInfo
21
- private readonly _size : WitnessSize
19
+ private readonly _elems : Buff[]
20
+
21
+ private _data : WitnessData
22
+ private _size : WitnessSize
22
23
 
23
24
  constructor (witness : Bytes[]) {
24
- this._data = witness.map(e => Buff.bytes(e))
25
- this._meta = parse_witness_data(witness)
26
- this._size = get_witness_size(witness)
25
+ this._elems = witness.map(e => Buff.bytes(e))
26
+ this._data = parse_witness(this._elems)
27
+ this._size = get_witness_size(this._elems)
27
28
  }
28
29
 
29
30
  get annex () : string | null {
30
- return this._meta.annex
31
+ return this._data.annex
31
32
  }
32
33
 
33
34
  get cblock () : string | null {
34
- return this._meta.cblock
35
+ return this._data.cblock
35
36
  }
36
37
 
37
- get data () : WitnessField {
38
- return {
39
- annex : this.annex,
40
- cblock : this.cblock,
41
- params : this.params,
42
- script : this.script,
43
- size : this.size,
44
- stack : this.stack,
45
- type : this.type,
46
- version : this.version,
47
- vsize : this.vsize
48
- }
38
+ get data () : WitnessData {
39
+ return this._data
49
40
  }
50
41
 
51
42
  get params () : string[] {
52
- return this._meta.params
43
+ return this._data.params
53
44
  }
54
45
 
55
46
  get script () : ScriptField | null {
56
- if (this._meta.script === null) return null
47
+ if (this._data.script === null) return null
57
48
  return {
58
- hex : this._meta.script,
59
- asm : decode_script(this._meta.script)
49
+ hex : this._data.script,
50
+ asm : decode_script(this._data.script)
60
51
  }
61
52
  }
62
53
 
63
- get size () : number {
64
- return this._size.size
54
+ get size () : WitnessSize {
55
+ return this._size
65
56
  }
66
57
 
67
58
  get stack () : string[] {
68
- return this._data.map(e => e.hex)
59
+ return this._elems.map(e => e.hex)
69
60
  }
70
61
 
71
62
  get type () : WitnessType {
72
- return this._meta.type
63
+ return this._data.type
73
64
  }
74
65
 
75
66
  get version () : number | null {
76
- return this._meta.version
67
+ return this._data.version
68
+ }
69
+
70
+ _update () {
71
+ this._data = parse_witness(this._elems)
72
+ this._size = get_witness_size(this._elems)
73
+ }
74
+
75
+ add (elem : Bytes) {
76
+ this._elems.push(Buff.bytes(elem))
77
+ this._update()
78
+ }
79
+
80
+ insert (index : number, elem : Bytes) {
81
+ Assert.ok(index >= 0 && index <= this._elems.length, 'index out of bounds')
82
+ if (index === this._elems.length) {
83
+ this._elems.push(Buff.bytes(elem))
84
+ } else {
85
+ this._elems.splice(index, 0, Buff.bytes(elem))
86
+ }
87
+ this._update()
77
88
  }
78
89
 
79
- get vsize () : number {
80
- return this._size.vsize
90
+ remove (index : number) {
91
+ this._elems.splice(index, 1)
92
+ this._update()
81
93
  }
82
94
 
83
95
  toJSON () { return this.data }
@@ -1,6 +1,6 @@
1
1
  import { Buff, Stream } from '@vbyte/buff'
2
2
  import { Assert, ECC } from '@vbyte/micro-lib'
3
- import { parse_witness_data } from '@/lib/witness/parse.js'
3
+ import { parse_witness } from '@/lib/witness/parse.js'
4
4
 
5
5
  import {
6
6
  encode_tapbranch,
@@ -11,7 +11,7 @@ import {
11
11
  import type { ControlBlock } from '@/types/index.js'
12
12
 
13
13
  export function parse_taproot_witness (witness : string[]) {
14
- const { cblock, params, script } = parse_witness_data(witness)
14
+ const { cblock, params, script } = parse_witness(witness)
15
15
 
16
16
  Assert.exists(cblock, 'cblock is null')
17
17
  Assert.exists(script, 'script is null')
@@ -32,11 +32,11 @@ export function get_txsize (
32
32
  ) : TxSize {
33
33
  const json = parse_tx(txdata)
34
34
  const base = encode_tx(json, false).length
35
- const size = encode_tx(json, true).length
36
- const weight = base * 3 + size
35
+ const total = encode_tx(json, true).length
36
+ const weight = base * 3 + total
37
37
  const remain = (weight % 4 > 0) ? 1 : 0
38
38
  const vsize = Math.floor(weight / 4) + remain
39
- return { base, real: size, vsize, weight }
39
+ return { base, total, vsize, weight }
40
40
  }
41
41
 
42
42
  export function get_vin_size (vin : TxInput[]) : number {
@@ -3,15 +3,16 @@ import { is_valid_script } from '@/lib/script/decode.js'
3
3
  import { TAPLEAF_VERSIONS } from '@/const.js'
4
4
 
5
5
  import type {
6
- WitnessInfo,
6
+ WitnessData,
7
7
  WitnessType
8
8
  } from '@/types/index.js'
9
9
 
10
- export function parse_witness_data (
10
+ export function parse_witness (
11
11
  witness : Bytes[]
12
- ) : WitnessInfo {
12
+ ) : WitnessData {
13
13
  // Parse the witness data.
14
14
  const elems = witness.map(e => Buff.bytes(e))
15
+ const stack = witness.map(e => Buff.bytes(e).hex)
15
16
  const annex = parse_annex_data(elems)
16
17
  if (annex !== null) elems.pop()
17
18
  const cblock = parse_cblock_data(elems)
@@ -21,7 +22,7 @@ export function parse_witness_data (
21
22
  const script = parse_witness_script(elems, type)
22
23
  if (script !== null) elems.pop()
23
24
  const params = elems.map(e => e.hex)
24
- return { annex, cblock, params, script, type, version }
25
+ return { annex, cblock, params, script, stack, type, version }
25
26
  }
26
27
 
27
28
  function parse_annex_data (
@@ -8,5 +8,5 @@ export function get_witness_size (witness : Bytes[]) : WitnessSize {
8
8
  const stack = witness.map(e => Buff.bytes(e))
9
9
  const size = stack.reduce((prev, next) => prev + next.length, 0)
10
10
  const vsize = Math.ceil(WIT_LENGTH_BYTE + size / 4)
11
- return { size, vsize }
11
+ return { total: size, vsize }
12
12
  }
@@ -1,6 +1,6 @@
1
- import type { LocktimeData, SequenceData } from './meta.js'
2
- import type { TxOutput, TxOutputType, TxSize, TxValue } from './txdata.js'
3
- import type { WitnessType, WitnessVersion } from './witness.js'
1
+ import type { LocktimeData, SequenceData } from './meta.js'
2
+ import type { TxOutput, TxOutputType, TxSize, TxValue } from './txdata.js'
3
+ import type { WitnessData, WitnessVersion } from './witness.js'
4
4
 
5
5
  export interface LocktimeField {
6
6
  hex : string
@@ -32,18 +32,6 @@ export interface TransactionData {
32
32
  vout : TransactionOutputData[]
33
33
  }
34
34
 
35
- export interface WitnessField {
36
- annex : string | null
37
- cblock : string | null
38
- params : string[]
39
- script : ScriptField | null
40
- size : number
41
- stack : string[]
42
- type : WitnessType
43
- version : WitnessVersion
44
- vsize : number
45
- }
46
-
47
35
  export interface TransactionInputData {
48
36
  coinbase? : string | null
49
37
  prevout? : TransactionOutputData | null
@@ -52,7 +40,7 @@ export interface TransactionInputData {
52
40
  size : number
53
41
  txid : string
54
42
  vout : number
55
- witness? : WitnessField | null
43
+ witness? : WitnessData | null
56
44
  }
57
45
 
58
46
  export interface TransactionOutputData {
@@ -67,7 +67,7 @@ export interface TxData {
67
67
 
68
68
  export interface TxSize {
69
69
  base : number
70
- real : number
70
+ total : number
71
71
  weight : number
72
72
  vsize : number
73
73
  }
@@ -1,40 +1,41 @@
1
- export type WitnessContext = WitnessInfo | TaprootScript | SegwitScript | TaprootSpend | SegwitSpend
1
+ export type WitnessContext = WitnessData | TaprootScript | SegwitScript | TaprootSpend | SegwitSpend
2
2
  export type WitnessVersion = number | null
3
3
  export type WitnessType = 'p2w-pkh' | 'p2w-sh' | 'p2tr-pk' | 'p2tr-ts' | 'unknown'
4
4
 
5
5
  export interface WitnessSize {
6
- size : number
6
+ total : number
7
7
  vsize : number
8
8
  }
9
9
 
10
- export interface WitnessInfo {
10
+ export interface WitnessData {
11
11
  annex : string | null
12
12
  cblock : string | null
13
13
  params : string[]
14
14
  script : string | null
15
+ stack : string[]
15
16
  type : WitnessType
16
17
  version : WitnessVersion
17
18
  }
18
19
 
19
- export interface TaprootScript extends WitnessInfo {
20
+ export interface TaprootScript extends WitnessData {
20
21
  cblock : string
21
22
  script : string
22
23
  type : 'p2tr-ts'
23
24
  version : 1
24
25
  }
25
26
 
26
- export interface SegwitScript extends WitnessInfo {
27
+ export interface SegwitScript extends WitnessData {
27
28
  script : string
28
29
  type : 'p2w-sh'
29
30
  version : 0
30
31
  }
31
32
 
32
- export interface TaprootSpend extends WitnessInfo {
33
+ export interface TaprootSpend extends WitnessData {
33
34
  type : 'p2tr-pk'
34
35
  version : 1
35
36
  }
36
37
 
37
- export interface SegwitSpend extends WitnessInfo {
38
+ export interface SegwitSpend extends WitnessData {
38
39
  type : 'p2w-pkh'
39
40
  version : 0
40
41
  }