解析BTC区块交易SRC20铭文算法
写在最开始,SRC-20的相关技术文档确实少的可怜,很多东西都没有现成可用(拿来主义是不行了),我在做这方面的时候也是搜索一番实在没找到,只能硬“肝”了。
一、介绍
SRC-20,即STAMPS/SRC-20,是比特币生态中的一种协议。它由Mike In Space于2023年3月发布,是基于STAMPS协议发展而来的,灵感部分来源于比特币的ORDINALS协议及BRC-20协议等。SRC-20协议规定了一组资产通证化的规则,这些规则必须包含在证券型资产之中,可帮助程序员开发可使用证券通证的应用程序。
二、SRC-20代币
自区块高度 796,000 起,SRC-20 规范已更改协议规范。SRC-20 交易现在直接在 BTC 上创建,不再支持作为合约交易。SRC-20 的铸造、部署和转账均免收服务费,但使用受支持的钱包进行交易时需支付 BTC 矿工费。
SRC-20 是基于 BRC-20 建模的前沿规范。SRC-20 的早期规范基于合约交易构建,并对发行交易有特定要求。截至区块 796,000 的当前规范将 SRC-20 交易直接编码到 BTC 上,并且不使用合约交易。区块 796,000 之后在合约交易上创建的任何 SRC-20 交易都将无效。我们设计了一种直接到 BTC 的方法,该方法可以优化交易规模并降低 SRC-20 交易的成本,因此我们将其用作概念验证。
三、在了解到这些背景后,我再来做交易数据的解码功能,从上面介绍来讲解码算法跟高度有一定的个关心,由于截止目前btc区块高度,我们不对之前的高度做解析,这一点还请知悉,下面直接上代码:
1.对应btc交易src-20有两种数据,我先把两种交易HASH数据贴出来:
#第一种多签的方式
{
"jsonrpc": "2.0",
"result": {
"txid": "9676d6a46bb6733442928ed27990ee17bffe62666a1ce2bfeb170390a8c9ad53",
"hash": "da0e8138cfd07143c4ee097d0394ade05ed4bc63cb59d65d0b9c6be0939ece08",
"version": 2,
"size": 450,
"vsize": 369,
"weight": 1473,
"locktime": 0,
"vin": [
{
"txid": "b646592769c53bad806d53931502b171bb7b94c36b3e42138bf36bf3cd9d99ef",
"vout": 3,
"scriptSig": {
"asm": "",
"hex": ""
},
"txinwitness": [
"304402207392fedfe647426e105c38d518b8eb68badd50032a9d05084014d2b2c200202f02206adc3f569b74e3436c16a39b1d52c1bcd131ac9ad4f1bba0ae455529eebc570601",
"02d6b8e961a9cbbe6f1f5be08f0017a4f84bb773e3298ea236ae4f90456a6fb632"
],
"sequence": 4294967293
}
],
"vout": [
{
"value": 0.0000079,
"n": 0,
"scriptPubKey": {
"asm": "0 41fab71bc5e225bb8b31af473f08f59f7c386a88",
"desc": "addr(bc1qg8atwx79ugjmhze34arn7z84na7rs65gw6k6l8)#93sr8xlc",
"hex": "001441fab71bc5e225bb8b31af473f08f59f7c386a88",
"address": "bc1qg8atwx79ugjmhze34arn7z84na7rs65gw6k6l8",
"type": "witness_v0_keyhash"
}
},
{
"value": 0.0000079,
"n": 1,
"scriptPubKey": {
"asm": "1 03fb9cb4286f2a862d14ea8a9468ddc67bb1942f484e69e0b65b3e9ec337e77d00 021acd30a69266572b280aab63a1bf26582ef90b7047c620ef4f8f3e2a3d7ded00 020202020202020202020202020202020202020202020202020202020202020202 3 OP_CHECKMULTISIG",
"desc": "multi(1,03fb9cb4286f2a862d14ea8a9468ddc67bb1942f484e69e0b65b3e9ec337e77d00,021acd30a69266572b280aab63a1bf26582ef90b7047c620ef4f8f3e2a3d7ded00,020202020202020202020202020202020202020202020202020202020202020202)#r3aa3efh",
"hex": "512103fb9cb4286f2a862d14ea8a9468ddc67bb1942f484e69e0b65b3e9ec337e77d0021021acd30a69266572b280aab63a1bf26582ef90b7047c620ef4f8f3e2a3d7ded002102020202020202020202020202020202020202020202020202020202020202020253ae",
"type": "multisig"
}
},
{
"value": 0.0000079,
"n": 2,
"scriptPubKey": {
"asm": "1 037ea1f327b994a0d915fee911f9124e3ed47ac6a49096bd4c80249a3601470900 0275f544716081676b0ad6f27371f20f60070c650c66ca8141671e48c6984db900 020202020202020202020202020202020202020202020202020202020202020202 3 OP_CHECKMULTISIG",
"desc": "multi(1,037ea1f327b994a0d915fee911f9124e3ed47ac6a49096bd4c80249a3601470900,0275f544716081676b0ad6f27371f20f60070c650c66ca8141671e48c6984db900,020202020202020202020202020202020202020202020202020202020202020202)#0vlpjvkg",
"hex": "5121037ea1f327b994a0d915fee911f9124e3ed47ac6a49096bd4c80249a3601470900210275f544716081676b0ad6f27371f20f60070c650c66ca8141671e48c6984db9002102020202020202020202020202020202020202020202020202020202020202020253ae",
"type": "multisig"
}
},
{
"value": 0.0001946,
"n": 3,
"scriptPubKey": {
"asm": "0 8776da9ce66061d2497fe025c7798102ba6ee191",
"desc": "addr(bc1qsamd488xvpsayjtluqjuw7vpq2axacv3jx2cxa)#38qcp2er",
"hex": "00148776da9ce66061d2497fe025c7798102ba6ee191",
"address": "bc1qsamd488xvpsayjtluqjuw7vpq2axacv3jx2cxa",
"type": "witness_v0_keyhash"
}
}
]
},
"id": 1
}
#第二种,普通方式
{
"jsonrpc": "2.0",
"result": {
"txid": "7d6b51f0bf20dd121388343536e2ae0b74a15503a52a53d631609827f84fc8ce",
"hash": "551728c0f7d29ad699edec1c6e311db01d9279b188b9221482c46a96aa896bdc",
"version": 2,
"size": 352,
"vsize": 270,
"weight": 1078,
"locktime": 0,
"vin": [
{
"txid": "24de58a077b51363961f717969dfeb8a5a3823a6ac2f8b71ff084854901ba6a1",
"vout": 0,
"scriptSig": {
"asm": "",
"hex": ""
},
"txinwitness": [
"304502210080fe936bcb5fab584035d6834bd7ad9308af672d0146e30fa680b000b165b31f022074e36296caa5f8ca5ae54627d9ffed75174b5534dea934cd5bfc1941746e48f081",
"0266111294c27223e9dd58e6d2de99ce3c62d2dacf4754f9c9e0febd541a9af897"
],
"sequence": 4294967293
}
],
"vout": [
{
"value": 0.00000547,
"n": 0,
"scriptPubKey": {
"asm": "1 3dcc869b59505a250348b668d0892659fb4f4001f4e00d44fece587efcd1c9e5",
"desc": "rawtr(3dcc869b59505a250348b668d0892659fb4f4001f4e00d44fece587efcd1c9e5)#n3qywyzr",
"hex": "51203dcc869b59505a250348b668d0892659fb4f4001f4e00d44fece587efcd1c9e5",
"address": "bc1p8hxgdx6e2pdz2q6gke5dpzfxt8a57sqp7nsq6387eev8alx3e8js37kw5z",
"type": "witness_v1_taproot"
}
},
{
"value": 0.0000033,
"n": 1,
"scriptPubKey": {
"asm": "0 003d7374616d703a7b2270223a227372632d3230222c226f70223a227472616e",
"desc": "addr(bc1qqq7hxarpd4cr57ezwq3r5gnnwf3j6v3sygkzymmsygazyarjv9hqylmqeg)#pgz9204k",
"hex": "0020003d7374616d703a7b2270223a227372632d3230222c226f70223a227472616e",
"address": "bc1qqq7hxarpd4cr57ezwq3r5gnnwf3j6v3sygkzymmsygazyarjv9hqylmqeg",
"type": "witness_v0_scripthash"
}
},
{
"value": 0.0000033,
"n": 2,
"scriptPubKey": {
"asm": "0 73666572222c227469636b223a224d534b45222c22616d74223a323230367d00",
"desc": "addr(bc1qwdnx2u3z9s38g6trdv3r5gjd2d952g3vyfsk6apz8geryvpk05qqze0nuw)#7ghqd9cn",
"hex": "002073666572222c227469636b223a224d534b45222c22616d74223a323230367d00",
"address": "bc1qwdnx2u3z9s38g6trdv3r5gjd2d952g3vyfsk6apz8geryvpk05qqze0nuw",
"type": "witness_v0_scripthash"
}
},
{
"value": 0.0008335,
"n": 3,
"scriptPubKey": {
"asm": "0 41fab71bc5e225bb8b31af473f08f59f7c386a88",
"desc": "addr(bc1qg8atwx79ugjmhze34arn7z84na7rs65gw6k6l8)#93sr8xlc",
"hex": "001441fab71bc5e225bb8b31af473f08f59f7c386a88",
"address": "bc1qg8atwx79ugjmhze34arn7z84na7rs65gw6k6l8",
"type": "witness_v0_keyhash"
}
},
{
"value": 0.00003486,
"n": 4,
"scriptPubKey": {
"asm": "0 b393ece604d23b32dcbfdf161a222aa125287fe2",
"desc": "addr(bc1qkwf7eesy6gan9h9lmutp5g325yjjsllzr22u59)#gzgzpwka",
"hex": "0014b393ece604d23b32dcbfdf161a222aa125287fe2",
"address": "bc1qkwf7eesy6gan9h9lmutp5g325yjjsllzr22u59",
"type": "witness_v0_keyhash"
}
}
],
"hex": "02000000000101a1a61b90544808ff718b2faca623385a8aebdf6979711f966313b577a058de240000000000fdffffff0523020000000000002251203dcc869b59505a250348b668d0892659fb4f4001f4e00d44fece587efcd1c9e54a01000000000000220020003d7374616d703a7b2270223a227372632d3230222c226f70223a227472616e4a0100000000000022002073666572222c227469636b223a224d534b45222c22616d74223a323230367d00964501000000000016001441fab71bc5e225bb8b31af473f08f59f7c386a889e0d000000000000160014b393ece604d23b32dcbfdf161a222aa125287fe20248304502210080fe936bcb5fab584035d6834bd7ad9308af672d0146e30fa680b000b165b31f022074e36296caa5f8ca5ae54627d9ffed75174b5534dea934cd5bfc1941746e48f081210266111294c27223e9dd58e6d2de99ce3c62d2dacf4754f9c9e0febd541a9af89700000000",
"blockhash": "00000000000000000001debd1260bd8c6ee130f8f3500bdd3220e345699049c2",
"confirmations": 3354,
"time": 1752220031,
"blocktime": 1752220031
},
"id": 1
}
所以这里我们需要针对这里两种交易数据方式分别做解码
第一种交易数据解码方式,多签的解码方式相对比较复杂需要。提取并反转ARC4密钥,然后从多签输出提取公钥数据来做解码算法,具体看下面代码
#第一种数据解码方式
import json
from Crypto.Cipher import ARC4
import binascii
import re
def parse_src20_transaction(tx_data):
try:
# 1. 提取并反转ARC4密钥 (vin[0].txid反转)
#vin_txid = tx_data["vin"][0]["txid"]
#raw_bytes = binascii.unhexlify(vin_txid)[::-1] # # 关键步骤:字节反转
#print("Reversed Key:", raw_bytes.hex())
#arc4_key = raw_bytes[::-1]
#研究协议需要反转,但实际这里不需要,这里比较坑人。
arc4_key = binascii.unhexlify(vin_txid)
# 2. 从多签输出提取公钥数据
pubkey_fragments = []
for vout in tx_data["vout"]:
if vout["scriptPubKey"]["type"] == "multisig":
hex_script = vout["scriptPubKey"]["hex"]
# 提取前两个公钥 (各66字符)
pubkey1 = hex_script[4:70] # 跳过OP_CODE(51=0x33)
pubkey2 = hex_script[72:138] # 跳过间隔符
# 移除首尾字节 (首字节类型标识,尾字节00填充)
pubkey_fragments.append(pubkey1[2:-2])
pubkey_fragments.append(pubkey2[2:-2])
encrypted_hex = "".join(pubkey_fragments)
print("Encrypted Data:", encrypted_hex)
# 3. ARC4解密
cipher = ARC4.new(arc4_key)
decrypted = cipher.decrypt(binascii.unhexlify(encrypted_hex))
# 4. 协议结构解析(关键修复)
if len(decrypted) < 8:
return "错误:解密数据过短"
# 读取长度标识(2字节大端序)
data_length = int.from_bytes(decrypted[0:2], byteorder="big")
print("解密原始字节:", decrypted[:20].hex())
print("拼接后的加密数据 (hex):", encrypted_hex) # 应为576字符
print("解密密钥 (hex):", arc4_key.hex()) # 应为64字符
# 验证协议前缀
if decrypted[2:8] != b"stamp:":
return "错误:无效数据前缀"
decoded_str = decrypted.decode('utf-8', errors='ignore')
stamp_match = re.search(r'stamp:\s*(\{.*\})', decoded_str, re.DOTALL)
if not stamp_match:
return "错误:未找到有效JSON数据"
# 3. 提取并清洗JSON字符串
json_str = stamp_match.group(1)
# 移除控制字符和尾随空字符
cleaned_json = re.sub(r'[\x00-\x1F]', '', json_str).strip()
return json.loads(cleaned_json)
except Exception as e:
return f"解析失败: {str(e)}"
# ===== 使用示例 =====
if __name__ == "__main__":
# 替换为你的交易数据
tx_data = {
"vin": [{"txid": "ca3b28031b279de22b398e1d1c61b0f6b692afc59a674643c96551b9ea52396a"}],
"vout": [
{
"value": 0.0000079,
"n": 0,
"scriptPubKey": {
"asm": "0 41fab71bc5e225bb8b31af473f08f59f7c386a88",
"desc": "addr(bc1qg8atwx79ugjmhze34arn7z84na7rs65gw6k6l8)#93sr8xlc",
"hex": "001441fab71bc5e225bb8b31af473f08f59f7c386a88",
"address": "bc1qg8atwx79ugjmhze34arn7z84na7rs65gw6k6l8",
"type": "witness_v0_keyhash"
}
},
{
"value": 0.0000079,
"n": 1,
"scriptPubKey": {
"asm": "1 035297b61ef0596ffa119c7bfe572ce32ecf602607c7e6e88486b99999b2b56400 02480ed2a3933d656184950bb99431b5af6f3db3af95212e9927bfa16313e45000 020202020202020202020202020202020202020202020202020202020202020202 3 OP_CHECKMULTISIG",
"desc": "multi(1,035297b61ef0596ffa119c7bfe572ce32ecf602607c7e6e88486b99999b2b56400,02480ed2a3933d656184950bb99431b5af6f3db3af95212e9927bfa16313e45000,020202020202020202020202020202020202020202020202020202020202020202)#eap59csk",
"hex": "5121035297b61ef0596ffa119c7bfe572ce32ecf602607c7e6e88486b99999b2b564002102480ed2a3933d656184950bb99431b5af6f3db3af95212e9927bfa16313e450002102020202020202020202020202020202020202020202020202020202020202020253ae",
"type": "multisig"
}
},
{
"value": 0.0000079,
"n": 2,
"scriptPubKey": {
"asm": "1 03677879b8c530f8456b31d8a06623b511283764ee20eb1c59ba25df295e4edf00 0212c125c13de7ca7f50b02fbc3a391adb7ceab1a90b0d28cbd1ec506140f5f600 020202020202020202020202020202020202020202020202020202020202020202 3 OP_CHECKMULTISIG",
"desc": "multi(1,03677879b8c530f8456b31d8a06623b511283764ee20eb1c59ba25df295e4edf00,0212c125c13de7ca7f50b02fbc3a391adb7ceab1a90b0d28cbd1ec506140f5f600,020202020202020202020202020202020202020202020202020202020202020202)#78g9qxzq",
"hex": "512103677879b8c530f8456b31d8a06623b511283764ee20eb1c59ba25df295e4edf00210212c125c13de7ca7f50b02fbc3a391adb7ceab1a90b0d28cbd1ec506140f5f6002102020202020202020202020202020202020202020202020202020202020202020253ae",
"type": "multisig"
}
},
{
"value": 0.00015425,
"n": 3,
"scriptPubKey": {
"asm": "0 d4b0878659667c9b43cca4bc1eeb61a92c33ffdb",
"desc": "addr(bc1q6jcg0pjeve7fks7v5j7pa6mp4ykr8l7mjurcst)#mh3fex56",
"hex": "0014d4b0878659667c9b43cca4bc1eeb61a92c33ffdb",
"address": "bc1q6jcg0pjeve7fks7v5j7pa6mp4ykr8l7mjurcst",
"type": "witness_v0_keyhash"
}
}
]
}
result = parse_src20_transaction(tx_data)
print("解析结果:")
decoded_str = json.dumps(result, indent=2)
print(decoded_str)
第二种交易数据解码方式就比较简单,直接拼接具体数据解码即可,具体看下面代码
import binascii
import json
import re
def parse_src20_from_vouts(vouts):
data_hex = ""
# 按索引顺序提取数据(确保顺序正确)
for vout in sorted(vouts, key=lambda x: x["n"]):
if vout["scriptPubKey"]["type"] == "witness_v0_scripthash":
hex_data = vout["scriptPubKey"]["hex"]
# 跳过脚本头(OP_0 + PUSH32: 0020)
if hex_data.startswith("0020"):
data_hex += hex_data[4:] # 移除0020前缀
# 十六进制转文本
json_data = binascii.unhexlify(data_hex).decode("utf-8")
# 剥离"stamp:"前缀
if json_data.startswith("stamp:"):
json_data = json_data[6:]
cleaned_data = re.sub(r'[\x00-\x1F]', '', json_data)
# 使用正则匹配第一个{到最后一个}之间的内容
json_match = re.search(r'\{.*\}', cleaned_data)
if not json_match:
return {"error": "No valid JSON found in: " + cleaned_data}
json_str = json_match.group(0)
try:
return json.loads(json_str)
except json.JSONDecodeError as e:
# 处理常见错误格式
fixed_str = re.sub(r',\s*}', '}', json_str) # 修复多余逗号
fixed_str = re.sub(r',\s*\]', ']', fixed_str)
try:
return json.loads(fixed_str)
except:
return {"error": f"JSON decode failed: {str(e)}", "raw_data": cleaned_data}
# 示例调用
if __name__ == "__main__":
tx_data = {
"vin": [{"txid": "ca3b28031b279de22b398e1d1c61b0f6b692afc59a674643c96551b9ea52396a"}],
"vout": [
{
"value": 0.00000547,
"n": 0,
"scriptPubKey": {
"asm": "1 3dcc869b59505a250348b668d0892659fb4f4001f4e00d44fece587efcd1c9e5",
"desc": "rawtr(3dcc869b59505a250348b668d0892659fb4f4001f4e00d44fece587efcd1c9e5)#n3qywyzr",
"hex": "51203dcc869b59505a250348b668d0892659fb4f4001f4e00d44fece587efcd1c9e5",
"address": "bc1p8hxgdx6e2pdz2q6gke5dpzfxt8a57sqp7nsq6387eev8alx3e8js37kw5z",
"type": "witness_v1_taproot"
}
},
{
"value": 0.0000033,
"n": 1,
"scriptPubKey": {
"asm": "0 003d7374616d703a7b2270223a227372632d3230222c226f70223a227472616e",
"desc": "addr(bc1qqq7hxarpd4cr57ezwq3r5gnnwf3j6v3sygkzymmsygazyarjv9hqylmqeg)#pgz9204k",
"hex": "0020003d7374616d703a7b2270223a227372632d3230222c226f70223a227472616e",
"address": "bc1qqq7hxarpd4cr57ezwq3r5gnnwf3j6v3sygkzymmsygazyarjv9hqylmqeg",
"type": "witness_v0_scripthash"
}
},
{
"value": 0.0000033,
"n": 2,
"scriptPubKey": {
"asm": "0 73666572222c227469636b223a224d534b45222c22616d74223a323230367d00",
"desc": "addr(bc1qwdnx2u3z9s38g6trdv3r5gjd2d952g3vyfsk6apz8geryvpk05qqze0nuw)#7ghqd9cn",
"hex": "002073666572222c227469636b223a224d534b45222c22616d74223a323230367d00",
"address": "bc1qwdnx2u3z9s38g6trdv3r5gjd2d952g3vyfsk6apz8geryvpk05qqze0nuw",
"type": "witness_v0_scripthash"
}
},
{
"value": 0.0008335,
"n": 3,
"scriptPubKey": {
"asm": "0 41fab71bc5e225bb8b31af473f08f59f7c386a88",
"desc": "addr(bc1qg8atwx79ugjmhze34arn7z84na7rs65gw6k6l8)#93sr8xlc",
"hex": "001441fab71bc5e225bb8b31af473f08f59f7c386a88",
"address": "bc1qg8atwx79ugjmhze34arn7z84na7rs65gw6k6l8",
"type": "witness_v0_keyhash"
}
},
{
"value": 0.00003486,
"n": 4,
"scriptPubKey": {
"asm": "0 b393ece604d23b32dcbfdf161a222aa125287fe2",
"desc": "addr(bc1qkwf7eesy6gan9h9lmutp5g325yjjsllzr22u59)#gzgzpwka",
"hex": "0014b393ece604d23b32dcbfdf161a222aa125287fe2",
"address": "bc1qkwf7eesy6gan9h9lmutp5g325yjjsllzr22u59",
"type": "witness_v0_keyhash"
}
}
]
}
vouts = tx_data["vout"] # 你的vout数组
result = parse_src20_from_vouts(vouts)
print(result)
四、以上就是解码具体过程,下面是获取区块数据
curl https://fragrant-fittest-violet.btc.quiknode.pro/1xxxxx/ \
-X POST \
-H "Content-Type: application/json" \
--data '{"id": 1, "jsonrpc": "2.0", "method": "getblockhash", "params": [892809]}'
curl https://fragrant-fittest-violet.btc.quiknode.pro/1xxxxx/ \
-X POST \
-H "Content-Type: application/json" \
--data '{"id": 1, "jsonrpc": "2.0", "method": "getblock", "params": ["00000000000000000000a2a31e36944c434c0f3b6ad04c03ccab80eeababbcb1"]}'
curl https://fragrant-fittest-violet.btc.quiknode.pro/1xxxxx/ \
-X POST \
-H "Content-Type: application/json" \
--data '{"id": 1, "jsonrpc": "2.0", "method": "getrawtransaction", "params": ["9676d6a46bb6733442928ed27990ee17bffe62666a1ce2bfeb170390a8c9ad53", true]}'
最后我第一种的 Java 实现方式也贴出来,第二种比较简单就省略了。
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.39</version>
</dependency>
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Src20TransactionParser {
// 实现ARC4解密算法
private static byte[] decryptARC4(byte[] key, byte[] ciphertext) {
// 初始化S盒
int[] s = new int[256];
for (int i = 0; i < 256; i++) {
s[i] = i;
}
// 密钥调度算法
int j = 0;
for (int i = 0; i < 256; i++) {
j = (j + s[i] + (key[i % key.length] & 0xFF)) % 256;
int temp = s[i];
s[i] = s[j];
s[j] = temp;
}
// 伪随机生成算法
int i = 0;
j = 0;
byte[] plaintext = new byte[ciphertext.length];
for (int k = 0; k < ciphertext.length; k++) {
i = (i + 1) % 256;
j = (j + s[i]) % 256;
int temp = s[i];
s[i] = s[j];
s[j] = temp;
int keyStream = s[(s[i] + s[j]) % 256];
plaintext[k] = (byte) (ciphertext[k] ^ keyStream);
}
return plaintext;
}
// 十六进制字符串转字节数组
private static byte[] hexStringToByteArray(String hex) {
int len = hex.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4)
+ Character.digit(hex.charAt(i + 1), 16));
}
return data;
}
// 解析SRC20交易
public static Object parseSrc20Transaction(JSONObject txData) {
try {
// 1. 提取并处理ARC4密钥
JSONObject firstVin = txData.getJSONArray("vin").getJSONObject(0);
String vinTxid = firstVin.getString("txid");
byte[] arc4Key = hexStringToByteArray(vinTxid); // 直接使用原始txid字节
// 2. 从多签输出提取公钥数据
List<String> pubkeyFragments = new ArrayList<>();
JSONArray vouts = txData.getJSONArray("vout");
for (int i = 0; i < vouts.size(); i++) {
JSONObject vout = vouts.getJSONObject(i);
JSONObject scriptPubKey = vout.getJSONObject("scriptPubKey");
if ("multisig".equals(scriptPubKey.getString("type"))) {
String hexScript = scriptPubKey.getString("hex");
// 提取两个公钥片段 (各66字符)
if (hexScript.length() >= 138) {
String pubkey1 = hexScript.substring(4, 70); // 51 OP_CODE后的第一个公钥
String pubkey2 = hexScript.substring(72, 138); // 第二个公钥
pubkeyFragments.add(pubkey1.substring(2, pubkey1.length() - 2));
pubkeyFragments.add(pubkey2.substring(2, pubkey2.length() - 2));
}
}
}
// 3. 拼接加密数据并解密
StringBuilder encryptedHex = new StringBuilder();
for (String fragment : pubkeyFragments) {
encryptedHex.append(fragment);
}
byte[] encryptedData = hexStringToByteArray(encryptedHex.toString());
byte[] decrypted = decryptARC4(arc4Key, encryptedData);
// 4. 验证协议前缀并提取JSON
if (decrypted.length < 8) {
return "错误:解密数据过短";
}
// 检查"stamp:"前缀
byte[] stampPrefix = "stamp:".getBytes(StandardCharsets.UTF_8);
for (int i = 0; i < stampPrefix.length; i++) {
if (decrypted[i + 2] != stampPrefix[i]) {
return "错误:无效数据前缀";
}
}
// 5. 提取并清洗JSON字符串
String decryptedStr = new String(decrypted, StandardCharsets.UTF_8);
Pattern pattern = Pattern.compile("stamp:\\s*(\\{.*\\})", Pattern.DOTALL);
Matcher matcher = pattern.matcher(decryptedStr);
if (!matcher.find()) {
return "错误:未找到有效JSON数据";
}
String jsonStr = matcher.group(1);
String cleanedJson = jsonStr.replaceAll("[\\x00-\\x1F]", "").trim();
return JSON.parseObject(cleanedJson);
} catch (Exception e) {
return "解析失败: " + e.getMessage();
}
}
// 测试用例
public static void main(String[] args) {
String jsonData = "{\"txid\":\"9676d6a46bb6733442928ed27990ee17bffe62666a1ce2bfeb170390a8c9ad53\",\"hash\":\"da0e8138cfd07143c4ee097d0394ade05ed4bc63cb59d65d0b9c6be0939ece08\",\"version\":2,\"size\":450,\"vsize\":369,\"weight\":1473,\"locktime\":0,\"vin\":[{\"txid\":\"b646592769c53bad806d53931502b171bb7b94c36b3e42138bf36bf3cd9d99ef\",\"vout\":3,\"scriptSig\":{\"asm\":\"\",\"hex\":\"\"},\"txinwitness\":[\"304402207392fedfe647426e105c38d518b8eb68badd50032a9d05084014d2b2c200202f02206adc3f569b74e3436c16a39b1d52c1bcd131ac9ad4f1bba0ae455529eebc570601\",\"02d6b8e961a9cbbe6f1f5be08f0017a4f84bb773e3298ea236ae4f90456a6fb632\"],\"sequence\":4294967293}],\"vout\":[{\"value\":0.00000790,\"n\":0,\"scriptPubKey\":{\"asm\":\"0 41fab71bc5e225bb8b31af473f08f59f7c386a88\",\"desc\":\"addr(bc1qg8atwx79ugjmhze34arn7z84na7rs65gw6k6l8)#93sr8xlc\",\"hex\":\"001441fab71bc5e225bb8b31af473f08f59f7c386a88\",\"address\":\"bc1qg8atwx79ugjmhze34arn7z84na7rs65gw6k6l8\",\"type\":\"witness_v0_keyhash\"}},{\"value\":0.00000790,\"n\":1,\"scriptPubKey\":{\"asm\":\"1 03fb9cb4286f2a862d14ea8a9468ddc67bb1942f484e69e0b65b3e9ec337e77d00 021acd30a69266572b280aab63a1bf26582ef90b7047c620ef4f8f3e2a3d7ded00 020202020202020202020202020202020202020202020202020202020202020202 3 OP_CHECKMULTISIG\",\"desc\":\"multi(1,03fb9cb4286f2a862d14ea8a9468ddc67bb1942f484e69e0b65b3e9ec337e77d00,021acd30a69266572b280aab63a1bf26582ef90b7047c620ef4f8f3e2a3d7ded00,020202020202020202020202020202020202020202020202020202020202020202)#r3aa3efh\",\"hex\":\"512103fb9cb4286f2a862d14ea8a9468ddc67bb1942f484e69e0b65b3e9ec337e77d0021021acd30a69266572b280aab63a1bf26582ef90b7047c620ef4f8f3e2a3d7ded002102020202020202020202020202020202020202020202020202020202020202020253ae\",\"type\":\"multisig\"}},{\"value\":0.00000790,\"n\":2,\"scriptPubKey\":{\"asm\":\"1 037ea1f327b994a0d915fee911f9124e3ed47ac6a49096bd4c80249a3601470900 0275f544716081676b0ad6f27371f20f60070c650c66ca8141671e48c6984db900 020202020202020202020202020202020202020202020202020202020202020202 3 OP_CHECKMULTISIG\",\"desc\":\"multi(1,037ea1f327b994a0d915fee911f9124e3ed47ac6a49096bd4c80249a3601470900,0275f544716081676b0ad6f27371f20f60070c650c66ca8141671e48c6984db900,020202020202020202020202020202020202020202020202020202020202020202)#0vlpjvkg\",\"hex\":\"5121037ea1f327b994a0d915fee911f9124e3ed47ac6a49096bd4c80249a3601470900210275f544716081676b0ad6f27371f20f60070c650c66ca8141671e48c6984db9002102020202020202020202020202020202020202020202020202020202020202020253ae\",\"type\":\"multisig\"}},{\"value\":0.00019460,\"n\":3,\"scriptPubKey\":{\"asm\":\"0 8776da9ce66061d2497fe025c7798102ba6ee191\",\"desc\":\"addr(bc1qsamd488xvpsayjtluqjuw7vpq2axacv3jx2cxa)#38qcp2er\",\"hex\":\"00148776da9ce66061d2497fe025c7798102ba6ee191\",\"address\":\"bc1qsamd488xvpsayjtluqjuw7vpq2axacv3jx2cxa\",\"type\":\"witness_v0_keyhash\"}}],\"hex\":\"02000000000101ef999dcdf36bf38b13423e6bc3947bbb71b1021593536d80ad3bc569275946b60300000000fdffffff04160300000000000016001441fab71bc5e225bb8b31af473f08f59f7c386a88160300000000000069512103fb9cb4286f2a862d14ea8a9468ddc67bb1942f484e69e0b65b3e9ec337e77d0021021acd30a69266572b280aab63a1bf26582ef90b7047c620ef4f8f3e2a3d7ded002102020202020202020202020202020202020202020202020202020202020202020253ae1603000000000000695121037ea1f327b994a0d915fee911f9124e3ed47ac6a49096bd4c80249a3601470900210275f544716081676b0ad6f27371f20f60070c650c66ca8141671e48c6984db9002102020202020202020202020202020202020202020202020202020202020202020253ae044c0000000000001600148776da9ce66061d2497fe025c7798102ba6ee1910247304402207392fedfe647426e105c38d518b8eb68badd50032a9d05084014d2b2c200202f02206adc3f569b74e3436c16a39b1d52c1bcd131ac9ad4f1bba0ae455529eebc5706012102d6b8e961a9cbbe6f1f5be08f0017a4f84bb773e3298ea236ae4f90456a6fb63200000000\",\"blockhash\":\"00000000000000000000a2a31e36944c434c0f3b6ad04c03ccab80eeababbcb1\",\"confirmations\":15432,\"time\":1744879151,\"blocktime\":1744879151}";
JSONObject txData = JSON.parseObject(jsonData);
Object result = parseSrc20Transaction(txData);
if (result instanceof JSONObject) {
System.out.println("解析结果:");
System.out.println(JSON.toJSONString((JSONObject) result, true));
} else {
System.out.println(result);
}
}
}
打完收工。

941

被折叠的 条评论
为什么被折叠?



