Skip to content

Commit 281b3a4

Browse files
committed
feature: raw parse and example
1 parent d4e6f6e commit 281b3a4

File tree

2 files changed

+108
-1
lines changed

2 files changed

+108
-1
lines changed

BTC/Psbt/encode.md

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,4 +135,36 @@ psbt.data.outputs.forEach((output, index) => {
135135

136136
1. **解码PSBT**:首先使用`bitcoinjs-lib``Psbt.fromBase64`方法将Base64编码的PSBT字符串解码成一个PSBT对象。
137137
2. **遍历输入和输出**:通过访问`psbt.data.inputs``psbt.data.outputs`数组,可以查看每个输入和输出的详细信息。这包括UTXO信息、脚本、签名等。
138-
3. **打印信息**:在控制台打印每个输入和输出的详细信息,这有助于开发者理解和调试PSBT。
138+
3. **打印信息**:在控制台打印每个输入和输出的详细信息,这有助于开发者理解和调试PSBT。
139+
140+
## 原生解析
141+
在比特币的PSBT(Partially Signed Bitcoin Transaction)处理中,原生编码和解码是指不使用任何外部库直接操作PSBT格式数据的方法。这通常涉及到直接处理PSBT的二进制表示,理解其结构,并根据规范来手动解析或构建数据。由于PSBT规范的复杂性,这一过程比较繁琐,但非常重要,因为它能提供对PSBT工作原理的深入理解。
142+
143+
### PSBT数据结构
144+
145+
PSBT是一种包含多个键值对的数据结构,每个部分都有其特定的分隔符和格式。其结构大致如下:
146+
147+
1. **全局部分**:包含交易的全局信息,如未签名的交易(除了witness)和其他全局类型的数据。
148+
2. **输入部分**:每个输入都有自己的键值对集,可能包括非见证UTXO、签名脚本、序列号等。
149+
3. **输出部分**:每个输出可能包括与之相关的脚本或其他数据。
150+
151+
### 原生编码过程
152+
153+
编码PSBT时,你需要遵循BIP 174(PSBT规范)中定义的格式,将交易的各个部分转换为键值对,并且每个键值对之后跟一个分隔符。具体步骤如下:
154+
155+
1. **开始全局部分**:使用特定的魔术字节(`0x70736274FF`,表示"psbt"加上分隔符)开始。
156+
2. **编码全局键值对**:例如,将交易版本、交易输入和输出数量等编码为键值对。
157+
3. **输入部分**:对每个输入,编码所有相关信息(如UTXO、见证数据、部分签名等)为键值对。
158+
4. **输出部分**:对每个输出,编码所有相关信息(如赎回脚本)为键值对。
159+
5. **结束**:每个部分结束时都需要一个`0x00`作为分隔符。
160+
161+
### 原生解码过程
162+
163+
解码PSBT时,需要按照编码的相反顺序来操作:
164+
165+
1. **读取并验证魔术字节**:确认PSBT的开始符合规范。
166+
2. **解析全局键值对**:读取键值对,直到遇到分隔符。
167+
3. **逐个解析输入部分**:对于每个输入,读取其键值对,直到遇到分隔符。
168+
4. **逐个解析输出部分**:对于每个输出,同样读取其键值对。
169+
5. **验证完整性**:确保所有必要的数据都已正确解析,并且数据结构符合PSBT的规范。
170+

BTC/Psbt/rawParsePSBT.js

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
const parsePSBT = (psbtBase64) => {
2+
const psbtBuffer = Buffer.from(psbtBase64, 'base64');
3+
let index = 0;
4+
5+
// 魔术字节和分隔符
6+
const magicBytes = psbtBuffer.slice(index, index + 5);
7+
index += 5;
8+
if (magicBytes.toString('hex') !== '70736274ff') {
9+
throw new Error('Not a valid PSBT');
10+
}
11+
12+
// 解析全局部分
13+
console.log("Global Data:");
14+
while (psbtBuffer[index] !== 0x00 && index < psbtBuffer.length) {
15+
16+
if (index + 2 > psbtBuffer.length) break; // Prevent reading beyond buffer
17+
const keyLen = psbtBuffer[index];
18+
const key = psbtBuffer.slice(index + 1, index + 1 + keyLen);
19+
index += 1 + keyLen;
20+
21+
if (index + 1 > psbtBuffer.length) break; // Prevent reading beyond buffer
22+
const valueLen = psbtBuffer.readIntLE(index, 1);
23+
const value = psbtBuffer.slice(index + 1, index + 1 + valueLen);
24+
index += 1 + valueLen;
25+
26+
console.log(`Key: ${key.toString('hex')}, Value: ${value.toString('hex')}`);
27+
}
28+
index++; // Skip separator
29+
30+
// 解析输入部分
31+
console.log("Inputs:");
32+
while (index < psbtBuffer.length && psbtBuffer[index] !== 0x00) {
33+
while (psbtBuffer[index] !== 0x00 && index < psbtBuffer.length) {
34+
35+
if (index + 2 > psbtBuffer.length) break;
36+
const keyLen = psbtBuffer[index];
37+
const key = psbtBuffer.slice(index + 1, index + 1 + keyLen);
38+
index += 1 + keyLen;
39+
40+
if (index + 1 > psbtBuffer.length) break;
41+
const valueLen = psbtBuffer.readUIntLE(index, 1);
42+
const value = psbtBuffer.slice(index + 1, index + 1 + valueLen);
43+
index += 1 + valueLen;
44+
console.log(`Key: ${key.toString('hex')}, Value: ${value.toString('hex')}`);
45+
}
46+
index++; // Skip separator
47+
}
48+
index++; // Skip separator
49+
50+
// 解析输出部分
51+
console.log("Outputs:");
52+
while (index < psbtBuffer.length && psbtBuffer[index] !== 0x00) {
53+
console.log(`Output at index ${index}:`);
54+
while (psbtBuffer[index] !== 0x00 && index < psbtBuffer.length) {
55+
if (index + 2 > psbtBuffer.length) break;
56+
const keyLen = psbtBuffer[index];
57+
const key = psbtBuffer.slice(index + 1, index + 1 + keyLen);
58+
index += 1 + keyLen;
59+
60+
if (index + 1 > psbtBuffer.length) break;
61+
const valueLen = psbtBuffer.readUIntLE(index, 1);
62+
const value = psbtBuffer.slice(index + 1, index + 1 + valueLen);
63+
index += 1 + valueLen;
64+
65+
console.log(`Key: ${key.toString('hex')}, Value: ${value.toString('hex')}`);
66+
}
67+
index++; // Skip separator
68+
}
69+
};
70+
71+
72+
// 用你提供的Base64字符串测试
73+
const psbtBase64 =
74+
"cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAAAA";
75+
parsePSBT(psbtBase64);

0 commit comments

Comments
 (0)