@vbyte/btc-dev 1.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/LICENSE +121 -0
- package/README.md +5 -0
- package/dist/class/index.d.ts +5 -0
- package/dist/class/index.js +5 -0
- package/dist/class/signer.d.ts +18 -0
- package/dist/class/signer.js +32 -0
- package/dist/class/tx.d.ts +38 -0
- package/dist/class/tx.js +73 -0
- package/dist/class/txin.d.ts +29 -0
- package/dist/class/txin.js +68 -0
- package/dist/class/txout.d.ts +18 -0
- package/dist/class/txout.js +38 -0
- package/dist/class/witness.d.ts +20 -0
- package/dist/class/witness.js +57 -0
- package/dist/const.d.ts +22 -0
- package/dist/const.js +36 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +10 -0
- package/dist/lib/address/encode.d.ts +3 -0
- package/dist/lib/address/encode.js +79 -0
- package/dist/lib/address/index.d.ts +20 -0
- package/dist/lib/address/index.js +21 -0
- package/dist/lib/address/p2pkh.d.ts +10 -0
- package/dist/lib/address/p2pkh.js +33 -0
- package/dist/lib/address/p2sh.d.ts +10 -0
- package/dist/lib/address/p2sh.js +33 -0
- package/dist/lib/address/p2tr.d.ts +8 -0
- package/dist/lib/address/p2tr.js +26 -0
- package/dist/lib/address/p2wpkh.d.ts +10 -0
- package/dist/lib/address/p2wpkh.js +34 -0
- package/dist/lib/address/p2wsh.d.ts +10 -0
- package/dist/lib/address/p2wsh.js +33 -0
- package/dist/lib/address/script.d.ts +5 -0
- package/dist/lib/address/script.js +46 -0
- package/dist/lib/address/util.d.ts +4 -0
- package/dist/lib/address/util.js +57 -0
- package/dist/lib/meta/index.d.ts +2 -0
- package/dist/lib/meta/index.js +2 -0
- package/dist/lib/meta/pointer.d.ts +42 -0
- package/dist/lib/meta/pointer.js +69 -0
- package/dist/lib/meta/scribe.d.ts +7 -0
- package/dist/lib/meta/scribe.js +192 -0
- package/dist/lib/psbt/encoder.d.ts +5 -0
- package/dist/lib/psbt/encoder.js +21 -0
- package/dist/lib/psbt/index.d.ts +4 -0
- package/dist/lib/psbt/index.js +4 -0
- package/dist/lib/psbt/meta.d.ts +3 -0
- package/dist/lib/psbt/meta.js +11 -0
- package/dist/lib/psbt/util.d.ts +5 -0
- package/dist/lib/psbt/util.js +44 -0
- package/dist/lib/psbt/validate.d.ts +2 -0
- package/dist/lib/psbt/validate.js +11 -0
- package/dist/lib/script/decode.d.ts +2 -0
- package/dist/lib/script/decode.js +55 -0
- package/dist/lib/script/encode.d.ts +6 -0
- package/dist/lib/script/encode.js +80 -0
- package/dist/lib/script/index.d.ts +126 -0
- package/dist/lib/script/index.js +17 -0
- package/dist/lib/script/util.d.ts +2 -0
- package/dist/lib/script/util.js +10 -0
- package/dist/lib/script/words.d.ts +116 -0
- package/dist/lib/script/words.js +164 -0
- package/dist/lib/sighash/index.d.ts +5 -0
- package/dist/lib/sighash/index.js +5 -0
- package/dist/lib/sighash/segwit.d.ts +3 -0
- package/dist/lib/sighash/segwit.js +89 -0
- package/dist/lib/sighash/sign.d.ts +3 -0
- package/dist/lib/sighash/sign.js +20 -0
- package/dist/lib/sighash/taproot.d.ts +9 -0
- package/dist/lib/sighash/taproot.js +124 -0
- package/dist/lib/sighash/util.d.ts +3 -0
- package/dist/lib/sighash/util.js +16 -0
- package/dist/lib/sighash/verify.d.ts +1 -0
- package/dist/lib/sighash/verify.js +1 -0
- package/dist/lib/taproot/cblock.d.ts +3 -0
- package/dist/lib/taproot/cblock.js +55 -0
- package/dist/lib/taproot/encode.d.ts +6 -0
- package/dist/lib/taproot/encode.js +26 -0
- package/dist/lib/taproot/index.d.ts +4 -0
- package/dist/lib/taproot/index.js +4 -0
- package/dist/lib/taproot/parse.d.ts +11 -0
- package/dist/lib/taproot/parse.js +47 -0
- package/dist/lib/taproot/tree.d.ts +3 -0
- package/dist/lib/taproot/tree.js +49 -0
- package/dist/lib/tx/create.d.ts +6 -0
- package/dist/lib/tx/create.js +54 -0
- package/dist/lib/tx/decode.d.ts +9 -0
- package/dist/lib/tx/decode.js +109 -0
- package/dist/lib/tx/encode.d.ts +15 -0
- package/dist/lib/tx/encode.js +94 -0
- package/dist/lib/tx/index.d.ts +10 -0
- package/dist/lib/tx/index.js +10 -0
- package/dist/lib/tx/locktime.d.ts +7 -0
- package/dist/lib/tx/locktime.js +37 -0
- package/dist/lib/tx/meta.d.ts +8 -0
- package/dist/lib/tx/meta.js +48 -0
- package/dist/lib/tx/parse.d.ts +2 -0
- package/dist/lib/tx/parse.js +12 -0
- package/dist/lib/tx/sequence.d.ts +7 -0
- package/dist/lib/tx/sequence.js +65 -0
- package/dist/lib/tx/size.d.ts +10 -0
- package/dist/lib/tx/size.js +48 -0
- package/dist/lib/tx/validate.d.ts +7 -0
- package/dist/lib/tx/validate.js +21 -0
- package/dist/lib/tx/witness.d.ts +3 -0
- package/dist/lib/tx/witness.js +85 -0
- package/dist/main.cjs +14625 -0
- package/dist/main.cjs.map +1 -0
- package/dist/module.mjs +14610 -0
- package/dist/module.mjs.map +1 -0
- package/dist/package.json +106 -0
- package/dist/schema/index.d.ts +2 -0
- package/dist/schema/index.js +2 -0
- package/dist/schema/taproot.d.ts +18 -0
- package/dist/schema/taproot.js +9 -0
- package/dist/schema/tx.d.ts +278 -0
- package/dist/schema/tx.js +35 -0
- package/dist/script.js +15 -0
- package/dist/script.js.map +1 -0
- package/dist/types/address.d.ts +34 -0
- package/dist/types/address.js +1 -0
- package/dist/types/index.d.ts +9 -0
- package/dist/types/index.js +9 -0
- package/dist/types/meta.d.ts +10 -0
- package/dist/types/meta.js +1 -0
- package/dist/types/psbt.d.ts +9 -0
- package/dist/types/psbt.js +1 -0
- package/dist/types/sighash.d.ts +14 -0
- package/dist/types/sighash.js +1 -0
- package/dist/types/taproot.d.ts +35 -0
- package/dist/types/taproot.js +1 -0
- package/dist/types/transaction.d.ts +81 -0
- package/dist/types/transaction.js +1 -0
- package/dist/types/txdata.d.ts +45 -0
- package/dist/types/txdata.js +1 -0
- package/dist/types/txmeta.d.ts +19 -0
- package/dist/types/txmeta.js +1 -0
- package/dist/types/witness.d.ts +30 -0
- package/dist/types/witness.js +1 -0
- package/package.json +106 -0
- package/src/class/index.ts +5 -0
- package/src/class/signer.ts +47 -0
- package/src/class/tx.ts +118 -0
- package/src/class/txin.ts +95 -0
- package/src/class/txout.ts +57 -0
- package/src/class/witness.ts +85 -0
- package/src/const.ts +43 -0
- package/src/index.ts +14 -0
- package/src/lib/address/encode.ts +183 -0
- package/src/lib/address/index.ts +24 -0
- package/src/lib/address/p2pkh.ts +65 -0
- package/src/lib/address/p2sh.ts +65 -0
- package/src/lib/address/p2tr.ts +51 -0
- package/src/lib/address/p2wpkh.ts +67 -0
- package/src/lib/address/p2wsh.ts +65 -0
- package/src/lib/address/script.ts +63 -0
- package/src/lib/address/util.ts +102 -0
- package/src/lib/meta/index.ts +2 -0
- package/src/lib/meta/pointer.ts +107 -0
- package/src/lib/meta/scribe.ts +251 -0
- package/src/lib/psbt/encoder.ts +24 -0
- package/src/lib/psbt/index.ts +4 -0
- package/src/lib/psbt/meta.ts +15 -0
- package/src/lib/psbt/util.ts +62 -0
- package/src/lib/psbt/validate.ts +18 -0
- package/src/lib/script/decode.ts +75 -0
- package/src/lib/script/encode.ts +130 -0
- package/src/lib/script/index.ts +26 -0
- package/src/lib/script/util.ts +78 -0
- package/src/lib/script/words.ts +182 -0
- package/src/lib/sighash/index.ts +5 -0
- package/src/lib/sighash/segwit.ts +152 -0
- package/src/lib/sighash/sign.ts +35 -0
- package/src/lib/sighash/taproot.ts +236 -0
- package/src/lib/sighash/util.ts +29 -0
- package/src/lib/sighash/verify.ts +83 -0
- package/src/lib/taproot/cblock.ts +95 -0
- package/src/lib/taproot/encode.ts +49 -0
- package/src/lib/taproot/index.ts +4 -0
- package/src/lib/taproot/parse.ts +65 -0
- package/src/lib/taproot/tree.ts +94 -0
- package/src/lib/tx/create.ts +82 -0
- package/src/lib/tx/decode.ts +145 -0
- package/src/lib/tx/encode.ts +154 -0
- package/src/lib/tx/index.ts +10 -0
- package/src/lib/tx/locktime.ts +57 -0
- package/src/lib/tx/meta.ts +73 -0
- package/src/lib/tx/parse.ts +16 -0
- package/src/lib/tx/sequence.ts +146 -0
- package/src/lib/tx/size.ts +77 -0
- package/src/lib/tx/validate.ts +36 -0
- package/src/lib/tx/witness.ts +122 -0
- package/src/schema/index.ts +2 -0
- package/src/schema/taproot.ts +12 -0
- package/src/schema/tx.ts +42 -0
- package/src/types/address.ts +39 -0
- package/src/types/index.ts +9 -0
- package/src/types/meta.ts +10 -0
- package/src/types/psbt.ts +15 -0
- package/src/types/sighash.ts +16 -0
- package/src/types/taproot.ts +40 -0
- package/src/types/transaction.ts +98 -0
- package/src/types/txdata.ts +53 -0
- package/src/types/txmeta.ts +25 -0
- package/src/types/witness.ts +36 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Buff } from '@vbyte/buff'
|
|
2
|
+
import { Assert } from '@vbyte/micro-lib'
|
|
3
|
+
import { hash160 } from '@vbyte/micro-lib/hash'
|
|
4
|
+
import { encode_address } from './encode.js'
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
get_address_config,
|
|
8
|
+
parse_address
|
|
9
|
+
} from './util.js'
|
|
10
|
+
|
|
11
|
+
import type {
|
|
12
|
+
AddressData,
|
|
13
|
+
ChainNetwork
|
|
14
|
+
} from '@/types/index.js'
|
|
15
|
+
|
|
16
|
+
const ADDR_TYPE = 'p2w-pkh'
|
|
17
|
+
|
|
18
|
+
export namespace P2WPKH {
|
|
19
|
+
export const create = create_p2wpkh_address
|
|
20
|
+
export const encode = encode_p2wpkh_address
|
|
21
|
+
export const decode = decode_p2wpkh_address
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function create_p2wpkh_address (
|
|
25
|
+
pubkey : string | Uint8Array,
|
|
26
|
+
network : ChainNetwork = 'main',
|
|
27
|
+
) : string {
|
|
28
|
+
// Convert the public key into bytes.
|
|
29
|
+
const bytes = Buff.bytes(pubkey)
|
|
30
|
+
// Assert the payload size is correct.
|
|
31
|
+
Assert.size(bytes, 33, `invalid payload size: ${bytes.length} !== 33` )
|
|
32
|
+
// Convert the bytes into a hash.
|
|
33
|
+
const hash = hash160(bytes)
|
|
34
|
+
// Encode the address.
|
|
35
|
+
return encode_p2wpkh_address(hash, network)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function encode_p2wpkh_address (
|
|
39
|
+
pk_hash : string | Uint8Array,
|
|
40
|
+
network : ChainNetwork = 'main',
|
|
41
|
+
) : string {
|
|
42
|
+
// Convert the public key hash into bytes.
|
|
43
|
+
const bytes = Buff.bytes(pk_hash)
|
|
44
|
+
// Get the address configuration.
|
|
45
|
+
const config = get_address_config(network, ADDR_TYPE)
|
|
46
|
+
// Assert the configuration exists.
|
|
47
|
+
Assert.exists(config, `unrecognized address config: ${ADDR_TYPE} on ${network}` )
|
|
48
|
+
// Assert the payload size is correct.
|
|
49
|
+
Assert.size(bytes, config.size, `invalid payload size: ${bytes.length} !== ${config.size}` )
|
|
50
|
+
// Encode the address.
|
|
51
|
+
return encode_address({
|
|
52
|
+
data : bytes,
|
|
53
|
+
format : 'bech32',
|
|
54
|
+
prefix : config.prefix
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function decode_p2wpkh_address (
|
|
59
|
+
address : string
|
|
60
|
+
) : AddressData {
|
|
61
|
+
// Parse the address.
|
|
62
|
+
const parsed = parse_address(address)
|
|
63
|
+
// Assert the address type is correct.
|
|
64
|
+
Assert.ok(parsed.type === 'p2w-pkh', `address type mismatch: ${parsed.type} !== ${ADDR_TYPE}`)
|
|
65
|
+
// Return the parsed address.
|
|
66
|
+
return parsed
|
|
67
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Buff } from '@vbyte/buff'
|
|
2
|
+
import { Assert } from '@vbyte/micro-lib'
|
|
3
|
+
import { sha256 } from '@vbyte/micro-lib/hash'
|
|
4
|
+
import { encode_address } from './encode.js'
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
get_address_config,
|
|
8
|
+
parse_address
|
|
9
|
+
} from './util.js'
|
|
10
|
+
|
|
11
|
+
import type {
|
|
12
|
+
ChainNetwork,
|
|
13
|
+
AddressData
|
|
14
|
+
} from '@/types/index.js'
|
|
15
|
+
|
|
16
|
+
const ADDR_TYPE = 'p2w-sh'
|
|
17
|
+
|
|
18
|
+
export namespace P2WSH {
|
|
19
|
+
export const create = create_p2wsh_address
|
|
20
|
+
export const encode = encode_p2wsh_address
|
|
21
|
+
export const decode = decode_p2wsh_address
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function create_p2wsh_address (
|
|
25
|
+
script : string | Uint8Array,
|
|
26
|
+
network : ChainNetwork = 'main',
|
|
27
|
+
) : string {
|
|
28
|
+
// Convert the script into bytes.
|
|
29
|
+
const bytes = Buff.bytes(script)
|
|
30
|
+
// Convert the bytes into a hash.
|
|
31
|
+
const hash = sha256(bytes)
|
|
32
|
+
// Encode the address.
|
|
33
|
+
return encode_p2wsh_address(hash, network)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function encode_p2wsh_address (
|
|
37
|
+
script_hash : string | Uint8Array,
|
|
38
|
+
network : ChainNetwork = 'main',
|
|
39
|
+
) : string {
|
|
40
|
+
// Convert the script hash into bytes.
|
|
41
|
+
const bytes = Buff.bytes(script_hash)
|
|
42
|
+
// Get the address configuration.
|
|
43
|
+
const config = get_address_config(network, ADDR_TYPE)
|
|
44
|
+
// Assert the configuration exists.
|
|
45
|
+
Assert.exists(config, `unrecognized address config: ${ADDR_TYPE} on ${network}` )
|
|
46
|
+
// Assert the payload size is correct.
|
|
47
|
+
Assert.size(bytes, config.size, `invalid payload size: ${bytes.length} !== ${config.size}` )
|
|
48
|
+
// Encode the address.
|
|
49
|
+
return encode_address({
|
|
50
|
+
data : bytes,
|
|
51
|
+
format : 'bech32',
|
|
52
|
+
prefix : config.prefix
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function decode_p2wsh_address (
|
|
57
|
+
address : string
|
|
58
|
+
) : AddressData {
|
|
59
|
+
// Parse the address.
|
|
60
|
+
const parsed = parse_address(address)
|
|
61
|
+
// Assert the address type is correct.
|
|
62
|
+
Assert.ok(parsed.type === 'p2w-sh', `address type mismatch: ${parsed.type} !== ${ADDR_TYPE}`)
|
|
63
|
+
// Return the parsed address.
|
|
64
|
+
return parsed
|
|
65
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { AddressType } from '@/types/index.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Get the address script.
|
|
5
|
+
*
|
|
6
|
+
* @param script_key - The script key.
|
|
7
|
+
* @param script_type - The script type.
|
|
8
|
+
* @returns The address script.
|
|
9
|
+
*/
|
|
10
|
+
export function get_address_script (
|
|
11
|
+
script_key : string,
|
|
12
|
+
script_type : AddressType
|
|
13
|
+
) {
|
|
14
|
+
switch (script_type) {
|
|
15
|
+
case 'p2pkh':
|
|
16
|
+
return get_p2pkh_script(script_key)
|
|
17
|
+
case 'p2sh':
|
|
18
|
+
return get_p2sh_script(script_key)
|
|
19
|
+
case 'p2w-pkh':
|
|
20
|
+
return get_p2w_pkh_script(script_key)
|
|
21
|
+
case 'p2w-sh':
|
|
22
|
+
return get_p2w_sh_script(script_key)
|
|
23
|
+
case 'p2tr':
|
|
24
|
+
return get_p2tr_script(script_key)
|
|
25
|
+
default:
|
|
26
|
+
throw new Error('unrecognized script type: ' + script_type)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function get_p2pkh_script (script_key : string) {
|
|
31
|
+
return {
|
|
32
|
+
script_hex : '76a914' + script_key + '88ac',
|
|
33
|
+
script_asm : [ 'OP_DUP', 'OP_HASH160', script_key, 'OP_EQUALVERIFY', 'OP_CHECKSIG' ]
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function get_p2sh_script (script_key : string) {
|
|
38
|
+
return {
|
|
39
|
+
script_hex : 'a914' + script_key + '87',
|
|
40
|
+
script_asm : [ 'OP_HASH160', script_key, 'OP_EQUAL' ]
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function get_p2w_pkh_script (script_key : string) {
|
|
45
|
+
return {
|
|
46
|
+
script_hex : '0014' + script_key,
|
|
47
|
+
script_asm : [ 'OP_0', script_key ]
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function get_p2w_sh_script (script_key : string) {
|
|
52
|
+
return {
|
|
53
|
+
script_hex : '0020' + script_key,
|
|
54
|
+
script_asm : [ 'OP_0', script_key ]
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function get_p2tr_script (script_key : string) {
|
|
59
|
+
return {
|
|
60
|
+
script_hex : '5120' + script_key,
|
|
61
|
+
script_asm : [ 'OP_1', script_key ]
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { Buff } from '@vbyte/buff'
|
|
2
|
+
import { decode_address } from './encode.js'
|
|
3
|
+
import { get_address_script } from './script.js'
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
AddressConfig,
|
|
7
|
+
AddressConfigEntry,
|
|
8
|
+
AddressType,
|
|
9
|
+
ChainNetwork,
|
|
10
|
+
AddressContext,
|
|
11
|
+
AddressData
|
|
12
|
+
} from '@/types/index.js'
|
|
13
|
+
|
|
14
|
+
const CONFIG_TABLE : AddressConfigEntry[] = [
|
|
15
|
+
[ '1', 'p2pkh', 'main', 20, 'base58', 0x00 ],
|
|
16
|
+
[ '3', 'p2sh', 'main', 20, 'base58', 0x05 ],
|
|
17
|
+
[ 'm', 'p2pkh', 'testnet', 20, 'base58', 0x6F ],
|
|
18
|
+
[ 'n', 'p2pkh', 'testnet', 20, 'base58', 0x6F ],
|
|
19
|
+
[ '2', 'p2sh', 'testnet', 20, 'base58', 0xC4 ],
|
|
20
|
+
[ 'm', 'p2pkh', 'regtest', 20, 'base58', 0x6F ],
|
|
21
|
+
[ 'n', 'p2pkh', 'regtest', 20, 'base58', 0x6F ],
|
|
22
|
+
[ '2', 'p2sh', 'regtest', 20, 'base58', 0xC4 ],
|
|
23
|
+
[ 'bc', 'p2w-pkh', 'main', 20, 'bech32', 0 ],
|
|
24
|
+
[ 'tb', 'p2w-pkh', 'testnet', 20, 'bech32', 0 ],
|
|
25
|
+
[ 'bcrt', 'p2w-pkh', 'regtest', 20, 'bech32', 0 ],
|
|
26
|
+
[ 'bc', 'p2w-sh', 'main', 32, 'bech32', 0 ],
|
|
27
|
+
[ 'tb', 'p2w-sh', 'testnet', 32, 'bech32', 0 ],
|
|
28
|
+
[ 'bcrt', 'p2w-sh', 'regtest', 32, 'bech32', 0 ],
|
|
29
|
+
[ 'bc', 'p2tr', 'main', 32, 'bech32m', 1 ],
|
|
30
|
+
[ 'tb', 'p2tr', 'testnet', 32, 'bech32m', 1 ],
|
|
31
|
+
[ 'bcrt', 'p2tr', 'regtest', 32, 'bech32m', 1 ]
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Lookup an address configuration by its type and network.
|
|
36
|
+
*
|
|
37
|
+
* @param address_network - The network of the address.
|
|
38
|
+
* @param address_type - The type of the address.
|
|
39
|
+
* @returns The address information, or null if the address is not recognized.
|
|
40
|
+
*/
|
|
41
|
+
export function get_address_config (
|
|
42
|
+
address_network : ChainNetwork,
|
|
43
|
+
address_type : AddressType
|
|
44
|
+
) : AddressConfig | null {
|
|
45
|
+
// For each configuration in the table,
|
|
46
|
+
for (const [ prefix, type, network, size, format, version ] of CONFIG_TABLE) {
|
|
47
|
+
// Check if the address matches the configuration
|
|
48
|
+
if (type === address_type && network === address_network) {
|
|
49
|
+
// Return the address configuration.
|
|
50
|
+
return { type, prefix, network, size, format, version }
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// If no configuration matches the address, return null.
|
|
54
|
+
return null
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Get the address context.
|
|
59
|
+
*
|
|
60
|
+
* @param address - The address to get the context for.
|
|
61
|
+
* @returns The address context.
|
|
62
|
+
*/
|
|
63
|
+
export function get_address_ctx (address : string) : AddressContext {
|
|
64
|
+
// Decode the address.
|
|
65
|
+
const dec = decode_address(address)
|
|
66
|
+
// For each configuration in the table,
|
|
67
|
+
for (const [ prefix, type, network, size, format, version ] of CONFIG_TABLE) {
|
|
68
|
+
|
|
69
|
+
// Check if the address matches the configuration
|
|
70
|
+
if (format !== dec.format) continue
|
|
71
|
+
if (size !== dec.data.length) continue
|
|
72
|
+
if (version !== dec.version) continue
|
|
73
|
+
|
|
74
|
+
if (dec.prefix) {
|
|
75
|
+
if (prefix !== dec.prefix) continue
|
|
76
|
+
} else {
|
|
77
|
+
if (!address.startsWith(prefix)) continue
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Convert the decoded data into a hex string.
|
|
81
|
+
const hex = Buff.uint(dec.data).hex
|
|
82
|
+
// Return the address configuration and data.
|
|
83
|
+
return { data: dec.data, hex, type, prefix, network, size, format, version }
|
|
84
|
+
}
|
|
85
|
+
// Otherwise, throw an error
|
|
86
|
+
throw new Error('address configuration is invalid')
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Parse an address into its data and script.
|
|
91
|
+
*
|
|
92
|
+
* @param address - The address to parse.
|
|
93
|
+
* @returns The address data and script.
|
|
94
|
+
*/
|
|
95
|
+
export function parse_address (address : string) : AddressData {
|
|
96
|
+
// Get the address context.
|
|
97
|
+
const ctx = get_address_ctx(address)
|
|
98
|
+
// Get the address script.
|
|
99
|
+
const script = get_address_script(ctx.hex, ctx.type)
|
|
100
|
+
// Return the address data.
|
|
101
|
+
return { ...ctx, ...script }
|
|
102
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
export namespace TxPointer {
|
|
2
|
+
export const outpoint = {
|
|
3
|
+
encode : encode_outpoint,
|
|
4
|
+
decode : decode_outpoint,
|
|
5
|
+
verify : verify_outpoint,
|
|
6
|
+
assert : assert_outpoint,
|
|
7
|
+
}
|
|
8
|
+
export const record_id = {
|
|
9
|
+
encode : encode_inscription_id,
|
|
10
|
+
decode : decode_inscription_id,
|
|
11
|
+
verify : verify_inscription_id,
|
|
12
|
+
assert : assert_inscription_id,
|
|
13
|
+
}
|
|
14
|
+
export const rune_id = {
|
|
15
|
+
encode : encode_rune_id,
|
|
16
|
+
decode : decode_rune_id,
|
|
17
|
+
verify : verify_rune_id,
|
|
18
|
+
assert : assert_rune_id,
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function encode_inscription_id (
|
|
23
|
+
txid : string,
|
|
24
|
+
order : number = 0
|
|
25
|
+
) : string {
|
|
26
|
+
return `${txid}i${order}`
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function decode_inscription_id (
|
|
30
|
+
inscription_id : string
|
|
31
|
+
) : { txid : string, order : number } {
|
|
32
|
+
assert_inscription_id(inscription_id)
|
|
33
|
+
const [ txid, order ] = inscription_id.split('i')
|
|
34
|
+
return { txid, order : parseInt(order) }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function verify_inscription_id (
|
|
38
|
+
inscription_id : string
|
|
39
|
+
) : boolean {
|
|
40
|
+
return inscription_id.match(/^[a-f0-9]{64}i\d+$/) !== null
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function assert_inscription_id (
|
|
44
|
+
inscription_id : string
|
|
45
|
+
) : void {
|
|
46
|
+
if (!verify_inscription_id(inscription_id)) {
|
|
47
|
+
throw new Error(`invalid inscription id: ${inscription_id}`)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function encode_rune_id (
|
|
52
|
+
block_height : number,
|
|
53
|
+
block_index : number
|
|
54
|
+
) : string {
|
|
55
|
+
return `${block_height}:${block_index}`
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function decode_rune_id (
|
|
59
|
+
rune_id : string
|
|
60
|
+
) : { block_height : number, block_index : number } {
|
|
61
|
+
assert_rune_id(rune_id)
|
|
62
|
+
const [ block_height, block_index ] = rune_id.split(':')
|
|
63
|
+
return { block_height : parseInt(block_height), block_index : parseInt(block_index) }
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function verify_rune_id (
|
|
67
|
+
rune_id : string
|
|
68
|
+
) : boolean {
|
|
69
|
+
return rune_id.match(/^\d+:\d+$/) !== null
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function assert_rune_id (
|
|
73
|
+
rune_id : string
|
|
74
|
+
) : void {
|
|
75
|
+
if (!verify_rune_id(rune_id)) {
|
|
76
|
+
throw new Error(`invalid rune id: ${rune_id}`)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function encode_outpoint (
|
|
81
|
+
txid : string,
|
|
82
|
+
vout : number
|
|
83
|
+
) : string {
|
|
84
|
+
return `${txid}:${vout}`
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function decode_outpoint (
|
|
88
|
+
outpoint : string
|
|
89
|
+
) : { txid : string, vout : number } {
|
|
90
|
+
assert_outpoint(outpoint)
|
|
91
|
+
const [ txid, vout ] = outpoint.split(':')
|
|
92
|
+
return { txid, vout : parseInt(vout) }
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function verify_outpoint (
|
|
96
|
+
outpoint : string
|
|
97
|
+
) : boolean {
|
|
98
|
+
return outpoint.match(/^[a-f0-9]{64}:[0-9]+$/) !== null
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function assert_outpoint (
|
|
102
|
+
outpoint : string
|
|
103
|
+
) : void {
|
|
104
|
+
if (!verify_outpoint(outpoint)) {
|
|
105
|
+
throw new Error(`invalid outpoint: ${outpoint}`)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { Buff, Stream } from '@vbyte/buff'
|
|
2
|
+
import { Assert } from '@vbyte/micro-lib'
|
|
3
|
+
import { encode_script } from '@/lib/script/encode.js'
|
|
4
|
+
import { decode_script } from '@/lib/script/decode.js'
|
|
5
|
+
|
|
6
|
+
import type { InscriptionData } from '@/types/index.js'
|
|
7
|
+
|
|
8
|
+
const _0n = BigInt(0)
|
|
9
|
+
const _1n = BigInt(1)
|
|
10
|
+
const _26n = BigInt(26)
|
|
11
|
+
|
|
12
|
+
export namespace Inscription {
|
|
13
|
+
export const encode = encode_inscription
|
|
14
|
+
export const decode = decode_inscription
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function decode_inscription (
|
|
18
|
+
script : string
|
|
19
|
+
) : InscriptionData[] {
|
|
20
|
+
const envelopes = parse_envelopes(script)
|
|
21
|
+
return envelopes.map(parse_record)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function encode_inscription (data : InscriptionData[]) : string {
|
|
25
|
+
return data.map(create_envelope).join('')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function create_envelope (data : InscriptionData) : string {
|
|
29
|
+
let asm : string[] = [ 'OP_0', 'OP_IF', '6f7264' ]
|
|
30
|
+
|
|
31
|
+
if (typeof data.delegate === 'string') {
|
|
32
|
+
const id = encode_id(data.delegate)
|
|
33
|
+
asm.push('OP_11', id)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (typeof data.ref === 'string') {
|
|
37
|
+
asm.push('OP_WITHIN', data.ref)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (typeof data.parent === 'string') {
|
|
41
|
+
const id = encode_id(data.parent)
|
|
42
|
+
asm.push('OP_3', id)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (typeof data.opcode === 'number') {
|
|
46
|
+
const code = encode_pointer(data.opcode)
|
|
47
|
+
asm.push('OP_NOP', code)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (typeof data.pointer === 'number') {
|
|
51
|
+
const ptr = encode_pointer(data.pointer)
|
|
52
|
+
asm.push('OP_2', ptr)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (typeof data.rune === 'string') {
|
|
56
|
+
const label = encode_rune_label(data.rune)
|
|
57
|
+
asm.push('OP_13', label)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (typeof data.mimetype === 'string') {
|
|
61
|
+
const label = encode_label(data.mimetype)
|
|
62
|
+
asm.push('OP_1', label)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (typeof data.content === 'string') {
|
|
66
|
+
const chunks = encode_content(data.content)
|
|
67
|
+
asm.push('OP_0', ...chunks)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
asm.push('OP_ENDIF')
|
|
71
|
+
|
|
72
|
+
return encode_script(asm)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function parse_envelopes (
|
|
76
|
+
script : string
|
|
77
|
+
) : string[][] {
|
|
78
|
+
|
|
79
|
+
const words = decode_script(script)
|
|
80
|
+
const start_idx = words.findIndex(e => e === 'OP_0')
|
|
81
|
+
|
|
82
|
+
Assert.ok(start_idx !== -1, 'inscription envelope not found')
|
|
83
|
+
|
|
84
|
+
const envelopes = []
|
|
85
|
+
|
|
86
|
+
for (let idx = start_idx; idx < words.length; idx++) {
|
|
87
|
+
Assert.ok(words[idx + 1] === 'OP_IF', 'OP_IF missing from envelope')
|
|
88
|
+
Assert.ok(words[idx + 2] === '6f7264', 'magic bytes missing from envelope')
|
|
89
|
+
|
|
90
|
+
const stop_idx = words.findIndex(e => e === 'OP_ENDIF')
|
|
91
|
+
Assert.ok(stop_idx !== -1, 'inscription envelope missing END_IF statement')
|
|
92
|
+
|
|
93
|
+
const env = words.slice(idx + 3, stop_idx)
|
|
94
|
+
envelopes.push(env)
|
|
95
|
+
idx += stop_idx
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return envelopes
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function parse_record (envelope : string[]) {
|
|
102
|
+
const record : InscriptionData = {}
|
|
103
|
+
|
|
104
|
+
for (let i = 0; i < envelope.length; i++) {
|
|
105
|
+
switch (envelope[i]) {
|
|
106
|
+
case 'OP_1':
|
|
107
|
+
record.mimetype = decode_label(envelope[i+1])
|
|
108
|
+
i += 1
|
|
109
|
+
break
|
|
110
|
+
case 'OP_2':
|
|
111
|
+
record.pointer = decode_pointer(envelope[i+1])
|
|
112
|
+
i += 1
|
|
113
|
+
break
|
|
114
|
+
case 'OP_3':
|
|
115
|
+
record.parent = decode_id(envelope[i+1])
|
|
116
|
+
i += 1
|
|
117
|
+
break
|
|
118
|
+
case 'OP_11':
|
|
119
|
+
record.delegate = decode_id(envelope[i+1])
|
|
120
|
+
i += 1
|
|
121
|
+
break
|
|
122
|
+
case 'OP_13':
|
|
123
|
+
record.rune = decode_rune_label(envelope[i+1])
|
|
124
|
+
i += 1
|
|
125
|
+
break
|
|
126
|
+
case 'OP_WITHIN':
|
|
127
|
+
record.ref = envelope[i+1]
|
|
128
|
+
i += 1
|
|
129
|
+
break;
|
|
130
|
+
case 'OP_NOP':
|
|
131
|
+
record.opcode = decode_pointer(envelope[i+1])
|
|
132
|
+
i += 1
|
|
133
|
+
break;
|
|
134
|
+
case 'OP_0':
|
|
135
|
+
record.content = decode_content(envelope.slice(i+1))
|
|
136
|
+
return record
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return record
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function encode_id (
|
|
143
|
+
identifier : string
|
|
144
|
+
) : string {
|
|
145
|
+
Assert.ok(identifier.includes('i'), 'identifier must include an index')
|
|
146
|
+
const parts = identifier.split('i')
|
|
147
|
+
const bytes = Buff.hex(parts[0])
|
|
148
|
+
const idx = Number(parts[1])
|
|
149
|
+
const txid = bytes.reverse().hex
|
|
150
|
+
return (idx !== 0) ? txid + Buff.num(idx).hex : txid
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function decode_id (
|
|
154
|
+
hexstr : string
|
|
155
|
+
) : string {
|
|
156
|
+
const bytes = Buff.hex(hexstr)
|
|
157
|
+
const idx = bytes.at(-1) ?? 0
|
|
158
|
+
const txid = bytes.slice(0, -1).reverse().hex
|
|
159
|
+
return txid + 'i' + String(idx)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function encode_pointer (
|
|
163
|
+
pointer : number
|
|
164
|
+
) : string {
|
|
165
|
+
return Buff.num(pointer).reverse().hex
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function decode_pointer (
|
|
169
|
+
hexstr : string
|
|
170
|
+
) : number {
|
|
171
|
+
return Buff.hex(hexstr).reverse().num
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function encode_label (
|
|
175
|
+
label : string
|
|
176
|
+
) : string {
|
|
177
|
+
return Buff.str(label).hex
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function decode_label (
|
|
181
|
+
hexstr : string
|
|
182
|
+
) : string {
|
|
183
|
+
return Buff.hex(hexstr).str
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function encode_content (
|
|
187
|
+
content : string
|
|
188
|
+
) : string[] {
|
|
189
|
+
const bytes = Buff.is_hex(content)
|
|
190
|
+
? Buff.hex(content)
|
|
191
|
+
: Buff.str(content)
|
|
192
|
+
const stream = new Stream(bytes)
|
|
193
|
+
const chunks : string[]= []
|
|
194
|
+
while (stream.size > 0) {
|
|
195
|
+
if (stream.size > 520) {
|
|
196
|
+
const chunk = stream.read(520)
|
|
197
|
+
chunks.push(chunk.hex)
|
|
198
|
+
} else {
|
|
199
|
+
const chunk = stream.read(stream.size)
|
|
200
|
+
chunks.push(chunk.hex)
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return chunks
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function decode_content (
|
|
207
|
+
hexstrs : string[],
|
|
208
|
+
type : 'hex' | 'utf8' = 'hex'
|
|
209
|
+
) : string {
|
|
210
|
+
const data = Buff.join(hexstrs)
|
|
211
|
+
return (type === 'hex')
|
|
212
|
+
? data.hex
|
|
213
|
+
: data.str
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function encode_rune_label (label : string) : string {
|
|
217
|
+
const str = label.toUpperCase()
|
|
218
|
+
let big = _0n
|
|
219
|
+
for (const char of str) {
|
|
220
|
+
if (char >= 'A' && char <= 'Z') {
|
|
221
|
+
big = big * _26n + BigInt(char.charCodeAt(0) - ('A'.charCodeAt(0) - 1))
|
|
222
|
+
} else { continue }
|
|
223
|
+
}
|
|
224
|
+
big = big - _1n
|
|
225
|
+
return Buff.big(big).reverse().hex
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function decode_rune_label (hex: string): string {
|
|
229
|
+
// Convert hex to BigInt, with byte order reversed
|
|
230
|
+
let big = Buff.hex(hex).reverse().big
|
|
231
|
+
// Add 1 as per the encoding algorithm
|
|
232
|
+
big = big + _1n
|
|
233
|
+
// Initialize result string
|
|
234
|
+
let result = ''
|
|
235
|
+
// Convert the BigInt back to a string of alphabet characters
|
|
236
|
+
while (big > _0n) {
|
|
237
|
+
// Get remainder after division by 26
|
|
238
|
+
const mod = big % _26n
|
|
239
|
+
// Convert remainder to character (0 maps to 'Z', 1 to 'A', 2 to 'B', etc.)
|
|
240
|
+
if (mod === _0n) {
|
|
241
|
+
result = 'Z' + result
|
|
242
|
+
big = big / _26n - _1n // Adjust for special case of 'Z'
|
|
243
|
+
} else {
|
|
244
|
+
// Map 1 to 'A', 2 to 'B', etc.
|
|
245
|
+
const charCode = Number(mod) + 'A'.charCodeAt(0) - 1
|
|
246
|
+
result = String.fromCharCode(charCode) + result
|
|
247
|
+
big = big / _26n
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return result
|
|
251
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Base64 } from '@vbyte/micro-lib'
|
|
2
|
+
import { Transaction } from '@scure/btc-signer'
|
|
3
|
+
|
|
4
|
+
import type { PSBTData } from '@/types/index.js'
|
|
5
|
+
|
|
6
|
+
export function decode_psbt (b64str : string) : Transaction {
|
|
7
|
+
const psbt = Base64.decode(b64str)
|
|
8
|
+
return Transaction.fromPSBT(psbt, { allowUnknownOutputs: true })
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function encode_psbt (psbt : PSBTData) : string {
|
|
12
|
+
const psbt_bytes = psbt.toPSBT(0)
|
|
13
|
+
return Base64.encode(psbt_bytes)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function parse_psbt (psbt : string | PSBTData) : Transaction {
|
|
17
|
+
if (psbt instanceof Transaction) {
|
|
18
|
+
return psbt
|
|
19
|
+
} else if (typeof psbt === 'string') {
|
|
20
|
+
return decode_psbt(psbt)
|
|
21
|
+
} else {
|
|
22
|
+
throw new Error('invalid psbt input: ' + psbt)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { parse_psbt } from './encoder.js'
|
|
2
|
+
import { finalize_legacy_inputs } from './util.js'
|
|
3
|
+
|
|
4
|
+
import { PSBTData } from '@/types/index.js'
|
|
5
|
+
|
|
6
|
+
export function get_vsize (psbt : string | PSBTData) : number {
|
|
7
|
+
const pdata = parse_psbt(psbt)
|
|
8
|
+
return pdata.vsize
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function get_txhex (psbt : PSBTData) : string {
|
|
12
|
+
let pdata = parse_psbt(psbt)
|
|
13
|
+
pdata = finalize_legacy_inputs(pdata)
|
|
14
|
+
return pdata.hex
|
|
15
|
+
}
|