@vbyte/btc-dev 1.0.9 → 1.0.11

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vbyte/btc-dev",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "description": "Batteries-included toolset for plebian bitcoin development",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -1,5 +1,5 @@
1
1
  import { Buff, Bytes } from '@vbyte/buff'
2
- import { ECC } from '@vbyte/micro-lib'
2
+ import { Assert, ECC } from '@vbyte/micro-lib'
3
3
 
4
4
  import {
5
5
  sign_segwit_tx,
@@ -15,6 +15,8 @@ export class TxSigner {
15
15
  private readonly _seckey : string
16
16
 
17
17
  constructor (seckey : Bytes) {
18
+ Assert.ok(Buff.is_bytes(seckey), 'seckey must be a string or bytes')
19
+ Assert.size(seckey, 32, 'seckey must be 32 bytes')
18
20
  this._seckey = Buff.bytes(seckey).hex
19
21
  }
20
22
 
package/src/class/tx.ts CHANGED
@@ -5,7 +5,6 @@ import { TransactionInput } from './txin.js'
5
5
  import { TransactionOutput } from './txout.js'
6
6
 
7
7
  import {
8
- decode_tx,
9
8
  get_txid,
10
9
  is_return_script,
11
10
  parse_tx,
@@ -21,8 +20,6 @@ import type {
21
20
  TxData,
22
21
  TxTemplate,
23
22
  TxOutput,
24
- TxSize,
25
- TxValue,
26
23
  TxInputTemplate
27
24
  } from '@/types/index.js'
28
25
 
@@ -30,23 +27,8 @@ export class Transaction {
30
27
 
31
28
  private readonly _tx : TxData
32
29
 
33
- private _size : TxSize & { segwit : number }
34
- private _hash : string
35
- private _txid : string
36
- private _value : TxValue
37
- private _vin : TransactionInput[]
38
- private _vout : TransactionOutput[]
39
-
40
30
  constructor (txdata : string | TxData | TxTemplate = {}) {
41
- this._tx = (typeof txdata !== 'string')
42
- ? parse_tx(txdata)
43
- : decode_tx(txdata)
44
- this._size = this._get_size()
45
- this._hash = get_txhash(this._tx)
46
- this._txid = get_txid(this._tx)
47
- this._value = get_tx_value(this._tx)
48
- this._vin = this._tx.vin.map(txin => new TransactionInput(txin))
49
- this._vout = this._tx.vout.map(txout => new TransactionOutput(txout))
31
+ this._tx = parse_tx(txdata)
50
32
  }
51
33
 
52
34
  get data () : TxData {
@@ -54,7 +36,7 @@ export class Transaction {
54
36
  }
55
37
 
56
38
  get hash () : string {
57
- return this._hash
39
+ return get_txhash(this._tx)
58
40
  }
59
41
 
60
42
  get locktime () {
@@ -70,21 +52,21 @@ export class Transaction {
70
52
  }
71
53
 
72
54
  get size () {
73
- return this._size
55
+ return get_txsize(this._tx)
74
56
  }
75
57
 
76
- get spends () : TransactionOutput[] {
58
+ get spends () : TxOutput[] {
77
59
  return this._tx.vin
78
60
  .filter(txin => txin.prevout !== null)
79
- .map(txin => new TransactionOutput(txin.prevout!))
61
+ .map(txin => txin.prevout!)
80
62
  }
81
63
 
82
64
  get txid () : string {
83
- return this._txid
65
+ return get_txid(this._tx)
84
66
  }
85
67
 
86
68
  get value () {
87
- return this._value
69
+ return get_tx_value(this._tx)
88
70
  }
89
71
 
90
72
  get version () : number {
@@ -92,23 +74,21 @@ export class Transaction {
92
74
  }
93
75
 
94
76
  get vin () : TransactionInput[] {
95
- return this._vin
77
+ return this._tx.vin.map((_, idx) => new TransactionInput(this, idx))
96
78
  }
97
79
 
98
80
  get vout () : TransactionOutput[] {
99
- return this._vout
81
+ return this._tx.vout.map((_, idx) => new TransactionOutput(this, idx))
100
82
  }
101
83
 
102
84
  add_vin (tx_input : TxInputTemplate) {
103
85
  const txin = create_tx_input(tx_input)
104
86
  this._tx.vin.push(txin)
105
- this._update_vin()
106
87
  }
107
88
 
108
89
  add_vout (tx_output : TxOutput) {
109
90
  const txout = create_tx_output(tx_output)
110
91
  this._tx.vout.push(txout)
111
- this._update_vout()
112
92
  }
113
93
 
114
94
  insert_vin (index : number, tx_input : TxInputTemplate) {
@@ -119,7 +99,6 @@ export class Transaction {
119
99
  } else {
120
100
  this._tx.vin.splice(index, 0, txin)
121
101
  }
122
- this._update_vin()
123
102
  }
124
103
 
125
104
  insert_vout (index : number, tx_output : TxOutput) {
@@ -130,19 +109,16 @@ export class Transaction {
130
109
  } else {
131
110
  this._tx.vout.splice(index, 0, txout)
132
111
  }
133
- this._update_vout()
134
112
  }
135
113
 
136
114
  remove_vin (index : number) {
137
115
  Assert.ok(this._tx.vin.at(index) !== undefined, 'input does not exist at index')
138
116
  this._tx.vin.splice(index, 1)
139
- this._update_vin()
140
117
  }
141
118
 
142
119
  remove_vout (index : number) {
143
120
  Assert.ok(this._tx.vout.at(index) !== undefined, 'output does not exist at index')
144
121
  this._tx.vout.splice(index, 1)
145
- this._update_vout()
146
122
  }
147
123
 
148
124
  _get_size () {
@@ -155,23 +131,6 @@ export class Transaction {
155
131
  }
156
132
  }
157
133
 
158
- _update_tx () {
159
- this._size = this._get_size()
160
- this._hash = get_txhash(this._tx)
161
- this._txid = get_txid(this._tx)
162
- this._value = get_tx_value(this._tx)
163
- }
164
-
165
- _update_vin () {
166
- this._vin = this._tx.vin.map(txin => new TransactionInput(txin))
167
- this._update_tx()
168
- }
169
-
170
- _update_vout () {
171
- this._vout = this._tx.vout.map(txout => new TransactionOutput(txout))
172
- this._update_tx()
173
- }
174
-
175
134
  toJSON () { return this.data }
176
135
  toString () { return JSON.stringify(this.data) }
177
136
  }
package/src/class/txin.ts CHANGED
@@ -1,6 +1,7 @@
1
+ import { Assert } from '@vbyte/micro-lib'
2
+ import { Transaction } from './tx.js'
1
3
  import { decode_script } from '@/lib/script/index.js'
2
4
  import { SequenceUtil } from '@/lib/meta/index.js'
3
- import { TransactionOutput } from './txout.js'
4
5
  import { TransactionWitness } from './witness.js'
5
6
 
6
7
  import {
@@ -8,69 +9,78 @@ import {
8
9
  get_txin_size,
9
10
  } from '@/lib/tx/index.js'
10
11
 
11
- import type { TxInput } from '@/types/index.js'
12
+ import type { TxInput, TxOutput } from '@/types/index.js'
12
13
 
13
14
  export class TransactionInput {
14
15
 
15
- private readonly _txin : TxInput
16
+ private readonly _tx : Transaction
17
+ private readonly _index : number
16
18
 
17
- constructor (txin : TxInput) {
18
- this._txin = txin
19
+ constructor (
20
+ transaction : Transaction,
21
+ index : number
22
+ ) {
23
+ this._tx = transaction
24
+ this._index = index
19
25
  }
20
26
 
21
27
  get coinbase () : string | null {
22
- return this._txin.coinbase
28
+ return this.data.coinbase
23
29
  }
24
30
 
25
31
  get data () : TxInput {
26
- return this._txin
32
+ const txin = this._tx.data.vin.at(this.index)
33
+ Assert.exists(txin, 'txin not found')
34
+ return txin
27
35
  }
28
36
 
29
37
  get has_prevout () : boolean {
30
- return this._txin.prevout !== null
38
+ return this.data.prevout !== null
39
+ }
40
+
41
+ get index () : number {
42
+ return this._index
31
43
  }
32
44
 
33
45
  get is_coinbase () : boolean {
34
- return this._txin.coinbase !== null
46
+ return this.data.coinbase !== null
35
47
  }
36
48
 
37
- get prevout () : TransactionOutput | null {
38
- return this._txin.prevout
39
- ? new TransactionOutput(this._txin.prevout)
40
- : null
49
+ get prevout () : TxOutput | null {
50
+ return this.data.prevout
41
51
  }
42
52
 
43
53
  get script_sig () {
44
- if (this._txin.script_sig === null) return null
54
+ if (this.data.script_sig === null) return null
45
55
  return {
46
- asm : decode_script(this._txin.script_sig),
47
- hex : this._txin.script_sig
56
+ asm : decode_script(this.data.script_sig),
57
+ hex : this.data.script_sig
48
58
  }
49
59
  }
50
60
 
51
61
  get sequence () {
52
62
  return {
53
- hex : encode_txin_sequence(this._txin.sequence).hex,
54
- data : SequenceUtil.decode(this._txin.sequence),
55
- value : this._txin.sequence
63
+ hex : encode_txin_sequence(this.data.sequence).hex,
64
+ data : SequenceUtil.decode(this.data.sequence),
65
+ value : this.data.sequence
56
66
  }
57
67
  }
58
68
 
59
69
  get size () {
60
- return get_txin_size(this._txin)
70
+ return get_txin_size(this.data)
61
71
  }
62
72
 
63
73
  get txid () : string {
64
- return this._txin.txid
74
+ return this.data.txid
65
75
  }
66
76
 
67
77
  get vout () : number {
68
- return this._txin.vout
78
+ return this.data.vout
69
79
  }
70
80
 
71
81
  get witness () {
72
- return this._txin.witness.length > 0
73
- ? new TransactionWitness(this._txin.witness)
82
+ return this.data.witness.length > 0
83
+ ? new TransactionWitness(this._tx, this.index)
74
84
  : null
75
85
  }
76
86
 
@@ -1,3 +1,5 @@
1
+ import { Assert } from '@vbyte/micro-lib'
2
+ import { Transaction } from './tx.js'
1
3
  import { decode_script } from '@/lib/script/index.js'
2
4
 
3
5
  import {
@@ -10,37 +12,48 @@ import type { TxOutput } from '@/types/index.js'
10
12
 
11
13
  export class TransactionOutput {
12
14
 
13
- private readonly _txout : TxOutput
15
+ private readonly _tx : Transaction
16
+ private readonly _index : number
14
17
 
15
- constructor (txout : TxOutput) {
16
- this._txout = txout
18
+ constructor (
19
+ transaction : Transaction,
20
+ index : number
21
+ ) {
22
+ this._tx = transaction
23
+ this._index = index
17
24
  }
18
25
 
19
26
  get data () : TxOutput {
20
- return this._txout
27
+ const txout = this._tx.data.vout.at(this.index)
28
+ Assert.exists(txout, 'txout not found')
29
+ return txout
30
+ }
31
+
32
+ get index () {
33
+ return this._index
21
34
  }
22
35
 
23
36
  get script_pk () {
24
37
  return {
25
- hex : this._txout.script_pk,
26
- asm : decode_script(this._txout.script_pk)
38
+ hex : this.data.script_pk,
39
+ asm : decode_script(this.data.script_pk)
27
40
  }
28
41
  }
29
42
 
30
43
  get size () {
31
- return get_txout_size(this._txout)
44
+ return get_txout_size(this.data)
32
45
  }
33
46
 
34
47
  get type () {
35
- return get_vout_type(this._txout.script_pk)
48
+ return get_vout_type(this.data.script_pk)
36
49
  }
37
50
 
38
51
  get value () : bigint {
39
- return this._txout.value
52
+ return this.data.value
40
53
  }
41
54
 
42
55
  get version () {
43
- return get_vout_version(this._txout.script_pk)
56
+ return get_vout_version(this.data.script_pk)
44
57
  }
45
58
 
46
59
  toJSON () { return this.data }
@@ -1,10 +1,10 @@
1
- import { Buff, Bytes } from '@vbyte/buff'
1
+ import { Transaction } from './tx.js'
2
2
  import { Assert } from '@vbyte/micro-lib'
3
3
  import { decode_script } from '@/lib/script/index.js'
4
4
 
5
5
  import {
6
6
  parse_witness,
7
- get_witness_size,
7
+ get_witness_size
8
8
  } from '@/lib/witness/index.js'
9
9
 
10
10
  import type {
@@ -16,80 +16,58 @@ import type {
16
16
 
17
17
  export class TransactionWitness {
18
18
 
19
- private readonly _elems : Buff[]
19
+ private readonly _tx : Transaction
20
+ private readonly _index : number
20
21
 
21
- private _data : WitnessData
22
- private _size : WitnessSize
23
-
24
- constructor (witness : Bytes[]) {
25
- this._elems = witness.map(e => Buff.bytes(e))
26
- this._data = parse_witness(this._elems)
27
- this._size = get_witness_size(this._elems)
22
+ constructor (
23
+ transaction : Transaction,
24
+ index : number
25
+ ) {
26
+ this._tx = transaction
27
+ this._index = index
28
28
  }
29
29
 
30
30
  get annex () : string | null {
31
- return this._data.annex
31
+ return this.data.annex
32
32
  }
33
33
 
34
34
  get cblock () : string | null {
35
- return this._data.cblock
35
+ return this.data.cblock
36
36
  }
37
37
 
38
38
  get data () : WitnessData {
39
- return this._data
39
+ return parse_witness(this.stack)
40
40
  }
41
41
 
42
42
  get params () : string[] {
43
- return this._data.params
43
+ return this.data.params
44
44
  }
45
45
 
46
46
  get script () : ScriptField | null {
47
- if (this._data.script === null) return null
47
+ if (this.data.script === null) return null
48
48
  return {
49
- hex : this._data.script,
50
- asm : decode_script(this._data.script)
49
+ hex : this.data.script,
50
+ asm : decode_script(this.data.script)
51
51
  }
52
52
  }
53
53
 
54
54
  get size () : WitnessSize {
55
- return this._size
55
+ return get_witness_size(this.stack)
56
56
  }
57
57
 
58
58
  get stack () : string[] {
59
- return this._elems.map(e => e.hex)
59
+ const txin = this._tx.data.vin.at(this._index)
60
+ Assert.exists(txin, 'txin not found at index ' + this._index)
61
+ Assert.exists(txin.witness, 'witness not found at index ' + this._index)
62
+ return txin.witness
60
63
  }
61
64
 
62
65
  get type () : WitnessType {
63
- return this._data.type
66
+ return this.data.type
64
67
  }
65
68
 
66
69
  get version () : number | null {
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()
88
- }
89
-
90
- remove (index : number) {
91
- this._elems.splice(index, 1)
92
- this._update()
70
+ return this.data.version
93
71
  }
94
72
 
95
73
  toJSON () { return this.data }
@@ -29,7 +29,7 @@ export function decode_tx (
29
29
  // Merge the options with the default config.
30
30
  const config = { ...DEFAULT_CONFIG, ...options }
31
31
  // Assert the txhex is a bytes object.
32
- Assert.is_bytes(txbytes, 'txbytes must be hex or a unit array')
32
+ Assert.is_bytes(txbytes, 'transaction must be hex or a byte-array')
33
33
  // Setup a byte-stream.
34
34
  const stream = new Stream(txbytes)
35
35
  // Parse tx version.
@@ -24,7 +24,7 @@ export function get_vsize (
24
24
  ) : number {
25
25
  const weight = Buff.bytes(bytes).length
26
26
  const remain = (weight % 4 > 0) ? 1 : 0
27
- return Math.floor(weight / 4) + remain
27
+ return Math.ceil(weight / 4) + remain
28
28
  }
29
29
 
30
30
  export function get_txsize (
@@ -35,7 +35,7 @@ export function get_txsize (
35
35
  const total = encode_tx(json, true).length
36
36
  const weight = base * 3 + total
37
37
  const remain = (weight % 4 > 0) ? 1 : 0
38
- const vsize = Math.floor(weight / 4) + remain
38
+ const vsize = Math.ceil(weight / 4) + remain
39
39
  return { base, total, vsize, weight }
40
40
  }
41
41
 
@@ -1,4 +1,5 @@
1
1
  import { Buff, Bytes } from '@vbyte/buff'
2
+ import { Assert } from '@vbyte/micro-lib'
2
3
 
3
4
  import type { WitnessSize } from '@/types/index.js'
4
5
 
@@ -10,3 +11,8 @@ export function get_witness_size (witness : Bytes[]) : WitnessSize {
10
11
  const vsize = Math.ceil(WIT_LENGTH_BYTE + size / 4)
11
12
  return { total: size, vsize }
12
13
  }
14
+
15
+ export function assert_witness (witness : unknown) : asserts witness is Bytes[] {
16
+ Assert.ok(Array.isArray(witness), 'witness must be an array')
17
+ Assert.ok(witness.every(e => Buff.is_bytes(e)), 'witness must be an array of strings or bytes')
18
+ }