Go 实现比特币多重签名服务
使用 Go 写了个比特币多重签名的服务,这篇文章总结服务的结构及实现思路。
源码中主要涉及到的 Go package :
- dep Go package 管理工具
- cobra 实现 cli
- LevelDB 保存加密处理后的私钥
- gin 实现接口接受外部服务请求
- gRPC & protocol buffer 内部服务调用
- btcd 连接比特币节点的 JSON-RPC 比特币区块、交易数据结构工具
采用 3-3 多重签名(在这里硬编码了,最好的做法是让用户动态选择 x-y 签名),也就是三个普通地址生成多签地址,消费多签地址中的比特币需要三个地址对应的私钥成功签名才可构建交易的数据结构,签名服务通过 gRPC 获取普通地址私钥进行签名,最后把签名的数据通过节点广播到比特币网络。
// serveCmd represents the api command
var serveCmd = &cobra.Command{
Use: "api",
Short: "External API",
Long: `Provide WEB API for external`,
Run: func(cmd *cobra.Command, args []string) {
ormDB = ormBbAlias{dbConn()}
ormDB.DBMigrate()
defer ormDB.Close()
switch api {
case "normal_address":
normalAddress()
case "multisig_address":
multisigAddress()
case "sign_tx":
signTx()
case "grpc":
addressServer()
default:
log.Fatalf("You must specify -a flag")
}
},
}
启动服务命令:
➜ bitcoin_multisig_with_node git:(master) ✗ bitcoin_multisig_with_node api -h
Using Configure file: /Users/hww/bitcoin.yml
Provide WEB API for external
Usage:
bitcoin_multisig_with_node api [flags]
Flags:
-a, --api string External API(value: normal_address, multisig_address sign_tx grpc)
-h, --help help for api
生成普通地址
在三台独立的内网服务器部署生成普通地址的服务,确保私钥不集中存储在一台服务器。当然在本地开发时,我们可以开启不同的端口启动服务给外部请求生成普通地址:
var (
client bitcoinClientAlias
g errgroup.Group
)
func mormalRoute() http.Handler {
r := ginEngine()
r.Use(location.Default())
r.GET("/address", normolAddressHandler)
return r
}
func normalAddress() {
server00 := &http.Server{
Addr: ":3000",
Handler: mormalRoute(),
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
server01 := &http.Server{
Addr: ":3001",
Handler: mormalRoute(),
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
server02 := &http.Server{
Addr: ":3002",
Handler: mormalRoute(),
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
g.Go(func() error {
return server00.ListenAndServe()
})
g.Go(func() error {
return server01.ListenAndServe()
})
g.Go(func() error {
return server02.ListenAndServe()
})
if err := g.Wait(); err != nil {
log.Fatal(err)
}
}
func normolAddressHandler(c *gin.Context) {
address, pubkey, err := generateNormalAddress("regtest")
if err != nil {
ginResponseException(c, 400, err)
return
}
ormDB.Create(&AddressPub{Address: *address, PublicKeyBytes: *pubkey, URL: location.Get(c).Hostname()})
c.JSON(200, gin.H{
"status": 200,
"address": address,
})
}
生成多签地址
生成多签地址服务当初部署,你也可以通过实现比特币多签地址生成协议脱离节点生成多钱地址,在这里我们提供一个对外的接口,用户把三个普通地址传给 web 服务,服务端处理请求并返回多签地址。
func multisigAddress() {
c := config.bitcoinClient()
client = bitcoinClientAlias{c}
r := ginEngine()
r.GET("/address", multisigAddressHandle)
if err := r.Run(":3003"); err != nil {
log.Fatalf(err.Error())
}
}
构建交易并首次签名
签名服务单独部署,通过 gRPC 去部署生成普通地址的服务获取私钥进行签名。
func signTx() {
c := config.bitcoinClient()
client = bitcoinClientAlias{c}
r := ginEngine()
r.GET("/issue", issueTx)
r.GET("/confirm", confirmHandle)
if err := r.Run(":3004"); err != nil {
log.Fatalf(err.Error())
}
}
开启 gRPC 服务端服务,接收签名服务发来的获取私钥请求
func addressServer() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
RegisterNormalAddressServer(s, &gRPCServer{})
reflection.Register(s)
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to gRPC serve: %v", err)
}
}
构建交易并首次签名
func issueTx(c *gin.Context) {
var (
params *txParams
dbMultisig MultiSig
dbAddress AddressPub
inputs []btcjson.RawTxInput
)
// 参数校验
params = new(txParams) // should initialize pointer before use
txidsHash, err := params.signTxParams(c)
if err != nil {
ginResponseException(c, 400, err)
return
}
ormDB.Where("address = ?", params.From).First(&dbAddress)
ormDB.Model(&dbAddress).Related(&dbMultisig)
// 交易输入
txVins, err := client.constructVins(txidsHash)
if err != nil {
ginResponseException(c, 400, err)
return
}
// 构建多签交易
rawTx, err := client.NewMultiSigTx(dbMultisig, *params, txVins)
if err != nil {
ginResponseException(c, 400, err)
return
}
// 确认签名输入参数
for _, txRawResult := range txVins {
input := signInput(txRawResult, dbMultisig)
inputs = append(inputs, *input)
}
// gRPC 获取私钥
wif, err := dbAddress.grpcGetNormalAddressWif()
if err != nil {
ginResponseException(c, 400, err)
return
}
// 签名交易
msgTx, result, err := client.SignRawTransaction3(rawTx, inputs, []string{*wif})
if err != nil {
if err != nil {
ginResponseException(c, 400, err)
return
}
}
txHex := ""
if msgTx != nil {
// Serialize the transaction and convert to hex string.
buf := bytes.NewBuffer(make([]byte, 0, msgTx.SerializeSize()))
if err := msgTx.Serialize(buf); err != nil {
// return newFutureError(err)
}
txHex = hex.EncodeToString(buf.Bytes())
}
c.JSON(200, gin.H{
"status": 200,
"sign_tx": txHex,
"result": result,
})
}
最后一轮签名成功后使用 SendRawTransaction
广播交易到比特币网络
比特币 regtest 环境多重签名测试流程
首先具备比特币网络测试节点环境,详情可以 Bitcoin-core daemon and regtest install
生成地址
// 请求不同机器生成普通地址
curl "172.17.1.19:3000/address" // {"address":"1PnBXZzZTr7TLnr56ndBPLQANAGRAq9h4w","status":200}
curl "172.17.1.19:3001/address" // {"address":"1KEPw2vC3NvGEcvguCt85uuKNmAGtYiuYC","status":200}
curl "172.17.1.19:3002/address" // {"address":"1GKhpdfXHUC1mBM6WrD63tbKvMUyj9irdb","status":200}
// 请求生成多签地址服务生成多签地址
curl "172.17.1.9:3003/address?addresses=1PnBXZzZTr7TLnr56ndBPLQANAGRAq9h4w,1KEPw2vC3NvGEcvguCt85uuKNmAGtYiuYC,1GKhpdfXHUC1mBM6WrD63tbKvMUyj9irdb" //{"address":"2NBvbEGvFYiz3EAFyTsyUA8QKTmgBcJnbsb","status":200}
交易并首次签名
// 在 regtest 环境中给多签地址转入三笔交易生成三个 txid, 每笔 10 个币
bitcoin-cli -regtest sendtoaddress 2NBvbEGvFYiz3EAFyTsyUA8QKTmgBcJnbsb 10 // txid: 97adab60d7a0f71fca8f22b0007ef9b0b3f8eb1ce10341b4a5ee998d0bfbec72
bitcoin-cli -regtest sendtoaddress 2NBvbEGvFYiz3EAFyTsyUA8QKTmgBcJnbsb 10 // txid: 9244f24ec81630742591a15007e84d6e113db259340e5ac319e6d38440baf89f
bitcoin-cli -regtest sendtoaddress 2NBvbEGvFYiz3EAFyTsyUA8QKTmgBcJnbsb 10 // txid: 23a6466147c3d82fa2967dd3871b649793ee8b53f0463378ce7c0b8bcdf1278f
// 开启挖矿确认以上三笔转账,以此我们的多签地址就有了 30 个比特币
bitcoin-cli -regtest generatetoaddress 10 2N8i6PbrW5nNtGbrHcPf5FJ1tNSyBou6t2b
/*
[
"3647fc9392ae2694b2ae6cc6336fad55f21fef4317e7b5bd13912a400d6ae84f",
"17acb72d5172d4d025bc3c7e953c7995d2b37fd6a50dac9dee705f3be2b83eb8",
"70a3c40665a20920bd25718ea80488e9dd8f9880d393a1b309b16be3cd84ddad",
"77abc300678788290a6ac054893c69877bbca6bb1c0010dae0afd578491dd67f",
"64d1087a723910a485989ffb15df7032617c06ae752f853af52868e08bcd4fe5",
"272f1761f1fcb6f8095775f200edf5e039f7dce36c166caa48f53fc821aafbf2",
"2740af50876e3b940b575455aecd014820b2515f15f126d6ef84ed384704c51f",
"050689882db8a2f55a3afb27a5083ddcc7951b93b379db165d8500785d0ccf2a",
"3c8b335bbcc7dc83fec5aa12e4ec2a881bd3067387b192806979bcd9c27ba18a",
"6cc259b0200d77c4a86255d3e19c02c47048c86933c2712fdbfae37cfd019f04"
]
*/
// 在 regtest 环境中生成普通地址接收,用以接多签地址即将转出的币
bitcoin-cli -regtest getnewaddress receiveaddress // 2Mu43Qw5pngjAmtu7n7evdkXE2E9ocXCft1
// 生成多签地址的其中某个普通地址发起交易
curl "192.168.7.44:3004/issue?txid=97adab60d7a0f71fca8f22b0007ef9b0b3f8eb1ce10341b4a5ee998d0bfbec72,9244f24ec81630742591a15007e84d6e113db259340e5ac319e6d38440baf89f,23a6466147c3d82fa2967dd3871b649793ee8b53f0463378ce7c0b8bcdf1278f&from=1KEPw2vC3NvGEcvguCt85uuKNmAGtYiuYC&to=2Mu43Qw5pngjAmtu7n7evdkXE2E9ocXCft1&amount=21"
/*
生成交易并返回首次签名结果
{
"result":false,
"sign_tx":"020000000372ecfb0b8d99eea5b44103e11cebf8b3b0f97e00b0228fca1ff7a0d760abad9701000000fd150100483045022100b277e07d37f98f1c97dccf92f7c6bb7093622301d33bee48c57bb6fda687c068022066f1cafe7296fb57053577c1c3705b39dcfaa9f010c9699b20ad1cd98f0c5496014cc9534104362119e29a90c958c89efc186a23bb859c2d5de57cb0a6a9480ea47087f508d9fe0eceeaa42aec7d0757049c5395aaa1aeebc6b2babd3d1a9ca09122b8890f4c4104cc1f24bb27e7897c483e73d39542f578ec458f33ceea96cfed59ec5ae1b8474cbb3f96badd66caa134cdbc3c8d67ee4c424ea779414ad74383974761a8386a3541045b64a0d78f107a2525beaf4ede77b4bbabba671a86bfc37bc425382c85abb06c5cc4251cef05fd793491c4184db4601b69697eec12a4cb5199dc11f5a772ac6953aeffffffff9ff8ba4084d3e619c35a0e3459b23d116e4de80750a19125743016c84ef2449200000000fd14010047304402203062597d764932cd85ec9e5f82a6d471984d0d03ab1268ea4651343624387679022026203223c9a2968530f2af4a16bb079fee2905cc5cbba258f1f823603e57c895014cc9534104362119e29a90c958c89efc186a23bb859c2d5de57cb0a6a9480ea47087f508d9fe0eceeaa42aec7d0757049c5395aaa1aeebc6b2babd3d1a9ca09122b8890f4c4104cc1f24bb27e7897c483e73d39542f578ec458f33ceea96cfed59ec5ae1b8474cbb3f96badd66caa134cdbc3c8d67ee4c424ea779414ad74383974761a8386a3541045b64a0d78f107a2525beaf4ede77b4bbabba671a86bfc37bc425382c85abb06c5cc4251cef05fd793491c4184db4601b69697eec12a4cb5199dc11f5a772ac6953aeffffffff8f27f1cd8b0b7cce783346f0538bee9397641b87d37d96a22fd8c3476146a62301000000fd14010047304402206390095bade6a4159ad036d9cfe7e7f0004024da09d460784d8bb45806664e62022059d21c421e1ddcdb17d70998617af6fc85589b35f6bd7c423d5443da4ec48642014cc9534104362119e29a90c958c89efc186a23bb859c2d5de57cb0a6a9480ea47087f508d9fe0eceeaa42aec7d0757049c5395aaa1aeebc6b2babd3d1a9ca09122b8890f4c4104cc1f24bb27e7897c483e73d39542f578ec458f33ceea96cfed59ec5ae1b8474cbb3f96badd66caa134cdbc3c8d67ee4c424ea779414ad74383974761a8386a3541045b64a0d78f107a2525beaf4ede77b4bbabba671a86bfc37bc425382c85abb06c5cc4251cef05fd793491c4184db4601b69697eec12a4cb5199dc11f5a772ac6953aeffffffff0200752b7d0000000017a91413d2d93195352467f94a12a3def1e4c09d15ac278778d5a4350000000017a914cce437c88256ca0a9fa1eed83e5b192f4dcc39f18700000000",
"status":200
}
*/
// 第二个普通地址进行第二次签名
curl "192.168.7.44:3004/confirm?txid=97adab60d7a0f71fca8f22b0007ef9b0b3f8eb1ce10341b4a5ee998d0bfbec72,9244f24ec81630742591a15007e84d6e113db259340e5ac319e6d38440baf89f,23a6466147c3d82fa2967dd3871b649793ee8b53f0463378ce7c0b8bcdf1278f&from=1PnBXZzZTr7TLnr56ndBPLQANAGRAq9h4w&signtx=020000000372ecfb0b8d99eea5b44103e11cebf8b3b0f97e00b0228fca1ff7a0d760abad9701000000fd150100483045022100b277e07d37f98f1c97dccf92f7c6bb7093622301d33bee48c57bb6fda687c068022066f1cafe7296fb57053577c1c3705b39dcfaa9f010c9699b20ad1cd98f0c5496014cc9534104362119e29a90c958c89efc186a23bb859c2d5de57cb0a6a9480ea47087f508d9fe0eceeaa42aec7d0757049c5395aaa1aeebc6b2babd3d1a9ca09122b8890f4c4104cc1f24bb27e7897c483e73d39542f578ec458f33ceea96cfed59ec5ae1b8474cbb3f96badd66caa134cdbc3c8d67ee4c424ea779414ad74383974761a8386a3541045b64a0d78f107a2525beaf4ede77b4bbabba671a86bfc37bc425382c85abb06c5cc4251cef05fd793491c4184db4601b69697eec12a4cb5199dc11f5a772ac6953aeffffffff9ff8ba4084d3e619c35a0e3459b23d116e4de80750a19125743016c84ef2449200000000fd14010047304402203062597d764932cd85ec9e5f82a6d471984d0d03ab1268ea4651343624387679022026203223c9a2968530f2af4a16bb079fee2905cc5cbba258f1f823603e57c895014cc9534104362119e29a90c958c89efc186a23bb859c2d5de57cb0a6a9480ea47087f508d9fe0eceeaa42aec7d0757049c5395aaa1aeebc6b2babd3d1a9ca09122b8890f4c4104cc1f24bb27e7897c483e73d39542f578ec458f33ceea96cfed59ec5ae1b8474cbb3f96badd66caa134cdbc3c8d67ee4c424ea779414ad74383974761a8386a3541045b64a0d78f107a2525beaf4ede77b4bbabba671a86bfc37bc425382c85abb06c5cc4251cef05fd793491c4184db4601b69697eec12a4cb5199dc11f5a772ac6953aeffffffff8f27f1cd8b0b7cce783346f0538bee9397641b87d37d96a22fd8c3476146a62301000000fd14010047304402206390095bade6a4159ad036d9cfe7e7f0004024da09d460784d8bb45806664e62022059d21c421e1ddcdb17d70998617af6fc85589b35f6bd7c423d5443da4ec48642014cc9534104362119e29a90c958c89efc186a23bb859c2d5de57cb0a6a9480ea47087f508d9fe0eceeaa42aec7d0757049c5395aaa1aeebc6b2babd3d1a9ca09122b8890f4c4104cc1f24bb27e7897c483e73d39542f578ec458f33ceea96cfed59ec5ae1b8474cbb3f96badd66caa134cdbc3c8d67ee4c424ea779414ad74383974761a8386a3541045b64a0d78f107a2525beaf4ede77b4bbabba671a86bfc37bc425382c85abb06c5cc4251cef05fd793491c4184db4601b69697eec12a4cb5199dc11f5a772ac6953aeffffffff0200752b7d0000000017a91413d2d93195352467f94a12a3def1e4c09d15ac278778d5a4350000000017a914cce437c88256ca0a9fa1eed83e5b192f4dcc39f18700000000"
/*
{
"result":false,
"sign_tx":"020000000372ecfb0b8d99eea5b44103e11cebf8b3b0f97e00b0228fca1ff7a0d760abad9701000000fd5f01004830450221009c1518665cdb11e4e0d55e23fcbb19030c22ceaec081f646be0e8d062622df4c022037364e73584aee4fa9b8d26311039408f1d272b6e4955f0aaa377dc1090b87bb01483045022100b277e07d37f98f1c97dccf92f7c6bb7093622301d33bee48c57bb6fda687c068022066f1cafe7296fb57053577c1c3705b39dcfaa9f010c9699b20ad1cd98f0c549601004cc9534104362119e29a90c958c89efc186a23bb859c2d5de57cb0a6a9480ea47087f508d9fe0eceeaa42aec7d0757049c5395aaa1aeebc6b2babd3d1a9ca09122b8890f4c4104cc1f24bb27e7897c483e73d39542f578ec458f33ceea96cfed59ec5ae1b8474cbb3f96badd66caa134cdbc3c8d67ee4c424ea779414ad74383974761a8386a3541045b64a0d78f107a2525beaf4ede77b4bbabba671a86bfc37bc425382c85abb06c5cc4251cef05fd793491c4184db4601b69697eec12a4cb5199dc11f5a772ac6953aeffffffff9ff8ba4084d3e619c35a0e3459b23d116e4de80750a19125743016c84ef2449200000000fd5d0100473044022010ad85ee4f66b63c7765ccbdee57c66c9c37dee0965be4c5934e3e7f9b453e5102205089c146899a6228ba9acb12e714fb15f50619fccb99fe279d7aaa90489f39380147304402203062597d764932cd85ec9e5f82a6d471984d0d03ab1268ea4651343624387679022026203223c9a2968530f2af4a16bb079fee2905cc5cbba258f1f823603e57c89501004cc9534104362119e29a90c958c89efc186a23bb859c2d5de57cb0a6a9480ea47087f508d9fe0eceeaa42aec7d0757049c5395aaa1aeebc6b2babd3d1a9ca09122b8890f4c4104cc1f24bb27e7897c483e73d39542f578ec458f33ceea96cfed59ec5ae1b8474cbb3f96badd66caa134cdbc3c8d67ee4c424ea779414ad74383974761a8386a3541045b64a0d78f107a2525beaf4ede77b4bbabba671a86bfc37bc425382c85abb06c5cc4251cef05fd793491c4184db4601b69697eec12a4cb5199dc11f5a772ac6953aeffffffff8f27f1cd8b0b7cce783346f0538bee9397641b87d37d96a22fd8c3476146a62301000000fd5e0100483045022100b8e4ebf749ab4bba66e55e857a07b834558a16aeecd93d6e007811456d78d0b302205a8cbcc8f63f118536f938894f3a27236c353a177c43c0ef9987b29bac3227ef0147304402206390095bade6a4159ad036d9cfe7e7f0004024da09d460784d8bb45806664e62022059d21c421e1ddcdb17d70998617af6fc85589b35f6bd7c423d5443da4ec4864201004cc9534104362119e29a90c958c89efc186a23bb859c2d5de57cb0a6a9480ea47087f508d9fe0eceeaa42aec7d0757049c5395aaa1aeebc6b2babd3d1a9ca09122b8890f4c4104cc1f24bb27e7897c483e73d39542f578ec458f33ceea96cfed59ec5ae1b8474cbb3f96badd66caa134cdbc3c8d67ee4c424ea779414ad74383974761a8386a3541045b64a0d78f107a2525beaf4ede77b4bbabba671a86bfc37bc425382c85abb06c5cc4251cef05fd793491c4184db4601b69697eec12a4cb5199dc11f5a772ac6953aeffffffff0200752b7d0000000017a91413d2d93195352467f94a12a3def1e4c09d15ac278778d5a4350000000017a914cce437c88256ca0a9fa1eed83e5b192f4dcc39f18700000000",
"status":200
}
*/
// 第三个普通地址进行第三次签名并广播交易
curl "192.168.7.44:3004/confirm?txid=97adab60d7a0f71fca8f22b0007ef9b0b3f8eb1ce10341b4a5ee998d0bfbec72,9244f24ec81630742591a15007e84d6e113db259340e5ac319e6d38440baf89f,23a6466147c3d82fa2967dd3871b649793ee8b53f0463378ce7c0b8bcdf1278f&from=1GKhpdfXHUC1mBM6WrD63tbKvMUyj9irdb&signtx=020000000372ecfb0b8d99eea5b44103e11cebf8b3b0f97e00b0228fca1ff7a0d760abad9701000000fd5f01004830450221009c1518665cdb11e4e0d55e23fcbb19030c22ceaec081f646be0e8d062622df4c022037364e73584aee4fa9b8d26311039408f1d272b6e4955f0aaa377dc1090b87bb01483045022100b277e07d37f98f1c97dccf92f7c6bb7093622301d33bee48c57bb6fda687c068022066f1cafe7296fb57053577c1c3705b39dcfaa9f010c9699b20ad1cd98f0c549601004cc9534104362119e29a90c958c89efc186a23bb859c2d5de57cb0a6a9480ea47087f508d9fe0eceeaa42aec7d0757049c5395aaa1aeebc6b2babd3d1a9ca09122b8890f4c4104cc1f24bb27e7897c483e73d39542f578ec458f33ceea96cfed59ec5ae1b8474cbb3f96badd66caa134cdbc3c8d67ee4c424ea779414ad74383974761a8386a3541045b64a0d78f107a2525beaf4ede77b4bbabba671a86bfc37bc425382c85abb06c5cc4251cef05fd793491c4184db4601b69697eec12a4cb5199dc11f5a772ac6953aeffffffff9ff8ba4084d3e619c35a0e3459b23d116e4de80750a19125743016c84ef2449200000000fd5d0100473044022010ad85ee4f66b63c7765ccbdee57c66c9c37dee0965be4c5934e3e7f9b453e5102205089c146899a6228ba9acb12e714fb15f50619fccb99fe279d7aaa90489f39380147304402203062597d764932cd85ec9e5f82a6d471984d0d03ab1268ea4651343624387679022026203223c9a2968530f2af4a16bb079fee2905cc5cbba258f1f823603e57c89501004cc9534104362119e29a90c958c89efc186a23bb859c2d5de57cb0a6a9480ea47087f508d9fe0eceeaa42aec7d0757049c5395aaa1aeebc6b2babd3d1a9ca09122b8890f4c4104cc1f24bb27e7897c483e73d39542f578ec458f33ceea96cfed59ec5ae1b8474cbb3f96badd66caa134cdbc3c8d67ee4c424ea779414ad74383974761a8386a3541045b64a0d78f107a2525beaf4ede77b4bbabba671a86bfc37bc425382c85abb06c5cc4251cef05fd793491c4184db4601b69697eec12a4cb5199dc11f5a772ac6953aeffffffff8f27f1cd8b0b7cce783346f0538bee9397641b87d37d96a22fd8c3476146a62301000000fd5e0100483045022100b8e4ebf749ab4bba66e55e857a07b834558a16aeecd93d6e007811456d78d0b302205a8cbcc8f63f118536f938894f3a27236c353a177c43c0ef9987b29bac3227ef0147304402206390095bade6a4159ad036d9cfe7e7f0004024da09d460784d8bb45806664e62022059d21c421e1ddcdb17d70998617af6fc85589b35f6bd7c423d5443da4ec4864201004cc9534104362119e29a90c958c89efc186a23bb859c2d5de57cb0a6a9480ea47087f508d9fe0eceeaa42aec7d0757049c5395aaa1aeebc6b2babd3d1a9ca09122b8890f4c4104cc1f24bb27e7897c483e73d39542f578ec458f33ceea96cfed59ec5ae1b8474cbb3f96badd66caa134cdbc3c8d67ee4c424ea779414ad74383974761a8386a3541045b64a0d78f107a2525beaf4ede77b4bbabba671a86bfc37bc425382c85abb06c5cc4251cef05fd793491c4184db4601b69697eec12a4cb5199dc11f5a772ac6953aeffffffff0200752b7d0000000017a91413d2d93195352467f94a12a3def1e4c09d15ac278778d5a4350000000017a914cce437c88256ca0a9fa1eed83e5b192f4dcc39f18700000000"
/*
{
"result":true,
"sign_tx":"21ba06597c14339d7819511e5b4aa07834b503f1bce1d2141964a00c9a16d26e",
"status":200
}
*/
// regtest 环境开启挖矿,确认多签交易
bitcoin-cli -regtest generatetoaddress 10 2N8i6PbrW5nNtGbrHcPf5FJ1tNSyBou6t2b
// regtest 查询 receiveaddress 地址余额
bitcoin-cli -regtest getbalance receiveaddress // 21.00000000