Fix dash after DIP2 update to the blockchain

pull/131/head
Martin Boehm 2019-02-27 13:29:42 +01:00
parent 0d26854c35
commit ffbbbb0b44
6 changed files with 383 additions and 1 deletions

View File

@ -1,6 +1,7 @@
package dash
import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"github.com/martinboehm/btcd/wire"
@ -45,11 +46,15 @@ func init() {
// DashParser handle
type DashParser struct {
*btc.BitcoinParser
baseparser *bchain.BaseParser
}
// NewDashParser returns new DashParser instance
func NewDashParser(params *chaincfg.Params, c *btc.Configuration) *DashParser {
return &DashParser{BitcoinParser: btc.NewBitcoinParser(params, c)}
return &DashParser{
BitcoinParser: btc.NewBitcoinParser(params, c),
baseparser: &bchain.BaseParser{},
}
}
// GetChainParams contains network parameters for the main Dash network,
@ -77,3 +82,13 @@ func GetChainParams(chain string) *chaincfg.Params {
return &MainNetParams
}
}
// PackTx packs transaction to byte array using protobuf
func (p *DashParser) PackTx(tx *bchain.Tx, height uint32, blockTime int64) ([]byte, error) {
return p.baseparser.PackTx(tx, height, blockTime)
}
// UnpackTx unpacks transaction from protobuf byte array
func (p *DashParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) {
return p.baseparser.UnpackTx(buf)
}

View File

@ -0,0 +1,307 @@
// +build unittest
package dash
import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"bytes"
"encoding/hex"
"fmt"
"io/ioutil"
"math/big"
"path/filepath"
"reflect"
"testing"
)
type testBlock struct {
size int
time int64
txs []string
}
var testParseBlockTxs = map[int]testBlock{
500001: {
size: 6521,
time: 1468043164,
txs: []string{
"6d45c761892eb07b7651140aa42901b03cb501a82585fb360ff8f155d46727b0",
"5f6cbefca8a48cccd40805013e5f6c602e0d35c3511ccb7a2ae25e51dd95d38f",
"0eb37faa8b2f24c68c8e4a3009a051caded005d5f13f0dc216ff6422256f6b7b",
"2565cfacb7f8cbc73dc1053b06ca527d9798bf6bf29a778cb5924a17dd167a39",
"85f93911e8a4d8d9bbf3de009a666ed594d62aa41a34a9e3763058067e64f084",
"46712f0f32a392c71df443798e120d0f40eb93063631a992b3dedd4d4afc04e4",
"1790efaa05caad7ab546ef479041c3cb1cb9bca7b9cc7992566d2e2344701167",
},
},
// last block without special transactions, valid for bitcoin parser
1028159: {
size: 8608,
time: 1551246608,
txs: []string{
"a800f5b2dde5d48bda08d9d6fc5647c41cec902ce690a5a2be0665e6acf77c35",
"981d6668e65b70fcd97ddd68319f3c5e5163e510cc0ed479be5667bf1782f036",
"b9fd19d37ec97d038da2ccad9414ef311275d5fd3762bdec3e76f535e2295f4c",
"1b4051d02c9919ef8d482cadf6ca2002442d9436b444923cd295fe56009ec52b",
"6f1ec8472624b8e7481024ee8b228086b9b32606790e94f161589d3fe2b3a826",
"b12c512803d733f3f7afce846e18c6a46c713533cdb18a13392cbda88866523c",
"69e6d67946ed660b440c8e457933ae594ce60acdbe17ded091ce0ac6f41ed186",
"755c3b7cad9b569def3f69f897da2ee7732ee2e0a165965512680b4fe9086e12",
"63de772ff400789c2c3ad9be653817bebf92551e139d80c8e735bb0610865500",
"1566bd9bb2413c63412a13d16c7017814363f668d8b3bfe66a5478734e73f010",
"d7f441b0abca7df6530a0620661c839244bb6f26e1b4a53b783fb3acc1f5f42a",
"ec7762d0e02a87e311b128662db8ef4161dcc9d9f2831250c7366eed98fc744b",
"b977669bfc0ac3b9ca9de7512fda564a69fe49dde8a286fcb7ea99147db54b5f",
},
},
// first block with special transactions, invalid for bitcoin parser
// 1028160: {
// size: 2347,
// time: 1551246710,
// txs: []string{
// "71d6975e3b79b52baf26c3269896a34f3bedfb04561c692ffa31f64dada1f9c4",
// "ed732a404cdfd4e0475a7a016200b7eef191f2c9de0ffdef8a20091c0499299c",
// "99d0613f82ea1f928bbc98318665adbdf5b40d206bd487fe77d542e86c903f55",
// "05cbf334a563468d0e378c56b43fb5254ee9c0e35ca8fab5cb242ebd825ae97b",
// "ee91fae7be36b3b81bc60992e904e1ae91e7dffdd5751ccaef557ba62ea80a4f",
// },
// },
}
func helperLoadBlock(t *testing.T, height int) []byte {
name := fmt.Sprintf("block_dump.%d", height)
path := filepath.Join("testdata", name)
d, err := ioutil.ReadFile(path)
if err != nil {
t.Fatal(err)
}
d = bytes.TrimSpace(d)
b := make([]byte, hex.DecodedLen(len(d)))
_, err = hex.Decode(b, d)
if err != nil {
t.Fatal(err)
}
return b
}
func TestParseBlock(t *testing.T) {
p := NewDashParser(GetChainParams("main"), &btc.Configuration{})
for height, tb := range testParseBlockTxs {
b := helperLoadBlock(t, height)
blk, err := p.ParseBlock(b)
if err != nil {
t.Errorf("ParseBlock() error %v", err)
}
if blk.Size != tb.size {
t.Errorf("ParseBlock() block size: got %d, want %d", blk.Size, tb.size)
}
if blk.Time != tb.time {
t.Errorf("ParseBlock() block time: got %d, want %d", blk.Time, tb.time)
}
if len(blk.Txs) != len(tb.txs) {
t.Errorf("ParseBlock() number of transactions: got %d, want %d", len(blk.Txs), len(tb.txs))
}
for ti, tx := range tb.txs {
if blk.Txs[ti].Txid != tx {
t.Errorf("ParseBlock() transaction %d: got %s, want %s", ti, blk.Txs[ti].Txid, tx)
}
}
}
}
var (
testTx1 = bchain.Tx{
Blocktime: 1551246710,
Confirmations: 0,
Hex: "0100000001f85264d11a747bdba77d411e5e4a3d35e3aeb5843b34a95234a2121ac65496bd000000006b483045022100dfa158fbd9773fab4f6f329c807e040af0c3a40967cbe01667169b914ed5ad960220061c5876364caa3e3c9c990ad2b4cc8b1a53d4f954dbda8434b0e67cc8348ff6012103093865e1e132b33a2a5ed01c79d2edba3473826a66cb26b8311bfa42749c2190ffffffff02ec3f8a2a010000001976a91470dcef2a22575d7a8f0779fb1d6cdd48135bd22788ac3116491d000000001976a91471348f7780e955a2a60eba17ecc4c826ebc23a9888ac00000000",
LockTime: 0,
Time: 1551246710,
Txid: "ed732a404cdfd4e0475a7a016200b7eef191f2c9de0ffdef8a20091c0499299c",
Version: 1,
Vin: []bchain.Vin{
{
Txid: "bd9654c61a12a23452a9343b84b5aee3353d4a5e1e417da7db7b741ad16452f8",
Vout: 0,
ScriptSig: bchain.ScriptSig{
Hex: "483045022100dfa158fbd9773fab4f6f329c807e040af0c3a40967cbe01667169b914ed5ad960220061c5876364caa3e3c9c990ad2b4cc8b1a53d4f954dbda8434b0e67cc8348ff6012103093865e1e132b33a2a5ed01c79d2edba3473826a66cb26b8311bfa42749c2190",
},
Sequence: 4294967295,
},
},
Vout: []bchain.Vout{
{
N: 0,
ScriptPubKey: bchain.ScriptPubKey{
Addresses: []string{"XkycBX1ykVXXs92pAi6ZQwZPEre9kSHHKH"},
Hex: "76a91470dcef2a22575d7a8f0779fb1d6cdd48135bd22788ac",
},
ValueSat: *big.NewInt(5008670700),
},
{
N: 1,
ScriptPubKey: bchain.ScriptPubKey{
Addresses: []string{"Xm1R9thKBm2EZKZevXsmMX4DVwQQuTohZu"},
Hex: "76a91471348f7780e955a2a60eba17ecc4c826ebc23a9888ac",
},
ValueSat: *big.NewInt(491329073),
},
},
}
testTxPacked1 = "0a20ed732a404cdfd4e0475a7a016200b7eef191f2c9de0ffdef8a20091c0499299c12e2010100000001f85264d11a747bdba77d411e5e4a3d35e3aeb5843b34a95234a2121ac65496bd000000006b483045022100dfa158fbd9773fab4f6f329c807e040af0c3a40967cbe01667169b914ed5ad960220061c5876364caa3e3c9c990ad2b4cc8b1a53d4f954dbda8434b0e67cc8348ff6012103093865e1e132b33a2a5ed01c79d2edba3473826a66cb26b8311bfa42749c2190ffffffff02ec3f8a2a010000001976a91470dcef2a22575d7a8f0779fb1d6cdd48135bd22788ac3116491d000000001976a91471348f7780e955a2a60eba17ecc4c826ebc23a9888ac0000000018f6cad8e305200028c0e03e3299010a001220bd9654c61a12a23452a9343b84b5aee3353d4a5e1e417da7db7b741ad16452f81800226b483045022100dfa158fbd9773fab4f6f329c807e040af0c3a40967cbe01667169b914ed5ad960220061c5876364caa3e3c9c990ad2b4cc8b1a53d4f954dbda8434b0e67cc8348ff6012103093865e1e132b33a2a5ed01c79d2edba3473826a66cb26b8311bfa42749c219028ffffffff0f3a480a05012a8a3fec10001a1976a91470dcef2a22575d7a8f0779fb1d6cdd48135bd22788ac2222586b7963425831796b565858733932704169365a51775a50457265396b5348484b483a470a041d49163110011a1976a91471348f7780e955a2a60eba17ecc4c826ebc23a9888ac2222586d31523974684b426d32455a4b5a657658736d4d5834445677515175546f685a754001"
)
func TestBaseParser_ParseTxFromJson(t *testing.T) {
p := NewDashParser(GetChainParams("main"), &btc.Configuration{})
tests := []struct {
name string
msg string
want *bchain.Tx
wantErr bool
}{
{
name: "normal tx",
msg: `{"hex":"0100000001f85264d11a747bdba77d411e5e4a3d35e3aeb5843b34a95234a2121ac65496bd000000006b483045022100dfa158fbd9773fab4f6f329c807e040af0c3a40967cbe01667169b914ed5ad960220061c5876364caa3e3c9c990ad2b4cc8b1a53d4f954dbda8434b0e67cc8348ff6012103093865e1e132b33a2a5ed01c79d2edba3473826a66cb26b8311bfa42749c2190ffffffff02ec3f8a2a010000001976a91470dcef2a22575d7a8f0779fb1d6cdd48135bd22788ac3116491d000000001976a91471348f7780e955a2a60eba17ecc4c826ebc23a9888ac00000000","txid":"ed732a404cdfd4e0475a7a016200b7eef191f2c9de0ffdef8a20091c0499299c","size":226,"version":1,"type":0,"locktime":0,"vin":[{"txid":"bd9654c61a12a23452a9343b84b5aee3353d4a5e1e417da7db7b741ad16452f8","vout":0,"scriptSig":{"asm":"3045022100dfa158fbd9773fab4f6f329c807e040af0c3a40967cbe01667169b914ed5ad960220061c5876364caa3e3c9c990ad2b4cc8b1a53d4f954dbda8434b0e67cc8348ff6[ALL]03093865e1e132b33a2a5ed01c79d2edba3473826a66cb26b8311bfa42749c2190","hex":"483045022100dfa158fbd9773fab4f6f329c807e040af0c3a40967cbe01667169b914ed5ad960220061c5876364caa3e3c9c990ad2b4cc8b1a53d4f954dbda8434b0e67cc8348ff6012103093865e1e132b33a2a5ed01c79d2edba3473826a66cb26b8311bfa42749c2190"},"value":55.00000000,"valueSat":5500000000,"address":"Xgcv4bKAXaWf5sjX9KR49L98jeMwNgeXWh","sequence":4294967295}],"vout":[{"value":50.08670700,"valueSat":5008670700,"n":0,"scriptPubKey":{"asm":"OP_DUPOP_HASH16070dcef2a22575d7a8f0779fb1d6cdd48135bd227OP_EQUALVERIFYOP_CHECKSIG","hex":"76a91470dcef2a22575d7a8f0779fb1d6cdd48135bd22788ac","reqSigs":1,"type":"pubkeyhash","addresses":["XkycBX1ykVXXs92pAi6ZQwZPEre9kSHHKH"]}},{"value":4.91329073,"valueSat":491329073,"n":1,"scriptPubKey":{"asm":"OP_DUPOP_HASH16071348f7780e955a2a60eba17ecc4c826ebc23a98OP_EQUALVERIFYOP_CHECKSIG","hex":"76a91471348f7780e955a2a60eba17ecc4c826ebc23a9888ac","reqSigs":1,"type":"pubkeyhash","addresses":["Xm1R9thKBm2EZKZevXsmMX4DVwQQuTohZu"]}}],"blockhash":"000000000000002099caaf1a877911d99a5980ede9b981280eecb291afedf87b","height":1028160,"confirmations":0,"time":1551246710,"blocktime":1551246710,"instantlock":false}`,
want: &testTx1,
},
{
name: "special tx - DIP2",
msg: `{"hex":"03000500010000000000000000000000000000000000000000000000000000000000000000ffffffff170340b00f1291af3c09542bc8349901000000002f4e614effffffff024181f809000000001976a9146a341485a9444b35dc9cb90d24e7483de7d37e0088ac3581f809000000001976a9140d1156f6026bf975ea3553b03fb534d0959c294c88ac0000000026010040b00f000000000000000000000000000000000000000000000000000000000000000000","txid":"71d6975e3b79b52baf26c3269896a34f3bedfb04561c692ffa31f64dada1f9c4","size":181,"version":3,"type":5,"locktime":0,"vin":[{"coinbase":"0340b00f1291af3c09542bc8349901000000002f4e614e","sequence":4294967295}],"vout":[{"value":1.67280961,"valueSat":167280961,"n":0,"scriptPubKey":{"asm":"OP_DUPOP_HASH1606a341485a9444b35dc9cb90d24e7483de7d37e00OP_EQUALVERIFYOP_CHECKSIG","hex":"76a9146a341485a9444b35dc9cb90d24e7483de7d37e0088ac","reqSigs":1,"type":"pubkeyhash","addresses":["XkNPrBSJtrHZUvUqb3JF4g5rMB3uzaJfEL"]}},{"value":1.67280949,"valueSat":167280949,"n":1,"scriptPubKey":{"asm":"OP_DUPOP_HASH1600d1156f6026bf975ea3553b03fb534d0959c294cOP_EQUALVERIFYOP_CHECKSIG","hex":"76a9140d1156f6026bf975ea3553b03fb534d0959c294c88ac","reqSigs":1,"type":"pubkeyhash","addresses":["XbswPXhcLqm5AN5gwcTTyiUGSP2YndWwk9"]}}],"extraPayloadSize":38,"extraPayload":"010040b00f000000000000000000000000000000000000000000000000000000000000000000","cbTx":{"version":1,"height":1028160,"merkleRootMNList":"0000000000000000000000000000000000000000000000000000000000000000"},"blockhash":"000000000000002099caaf1a877911d99a5980ede9b981280eecb291afedf87b","height":1028160,"confirmations":140,"time":1551246710,"blocktime":1551246710,"instantlock":false}`,
want: &bchain.Tx{
Blocktime: 1551246710,
Confirmations: 140,
Hex: "03000500010000000000000000000000000000000000000000000000000000000000000000ffffffff170340b00f1291af3c09542bc8349901000000002f4e614effffffff024181f809000000001976a9146a341485a9444b35dc9cb90d24e7483de7d37e0088ac3581f809000000001976a9140d1156f6026bf975ea3553b03fb534d0959c294c88ac0000000026010040b00f000000000000000000000000000000000000000000000000000000000000000000",
LockTime: 0,
Time: 1551246710,
Txid: "71d6975e3b79b52baf26c3269896a34f3bedfb04561c692ffa31f64dada1f9c4",
Version: 3,
Vin: []bchain.Vin{
{
Coinbase: "0340b00f1291af3c09542bc8349901000000002f4e614e",
Sequence: 4294967295,
},
},
Vout: []bchain.Vout{
{
N: 0,
ScriptPubKey: bchain.ScriptPubKey{
Addresses: []string{"XkNPrBSJtrHZUvUqb3JF4g5rMB3uzaJfEL"},
Hex: "76a9146a341485a9444b35dc9cb90d24e7483de7d37e0088ac",
},
ValueSat: *big.NewInt(167280961),
},
{
N: 1,
ScriptPubKey: bchain.ScriptPubKey{
Addresses: []string{"XbswPXhcLqm5AN5gwcTTyiUGSP2YndWwk9"},
Hex: "76a9140d1156f6026bf975ea3553b03fb534d0959c294c88ac",
},
ValueSat: *big.NewInt(167280949),
},
},
},
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := p.ParseTxFromJson([]byte(tt.msg))
if (err != nil) != tt.wantErr {
t.Errorf("DashParser.ParseTxFromJson() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("DashParser.ParseTxFromJson() = %+v, want %+v", got, tt.want)
}
})
}
}
func Test_PackTx(t *testing.T) {
type args struct {
tx bchain.Tx
height uint32
blockTime int64
parser *DashParser
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
name: "dash-1",
args: args{
tx: testTx1,
height: 1028160,
blockTime: 1551246710,
parser: NewDashParser(GetChainParams("main"), &btc.Configuration{}),
},
want: testTxPacked1,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := tt.args.parser.PackTx(&tt.args.tx, tt.args.height, tt.args.blockTime)
if (err != nil) != tt.wantErr {
t.Errorf("packTx() error = %v, wantErr %v", err, tt.wantErr)
return
}
h := hex.EncodeToString(got)
if !reflect.DeepEqual(h, tt.want) {
t.Errorf("packTx() = %v, want %v", h, tt.want)
}
})
}
}
func Test_UnpackTx(t *testing.T) {
type args struct {
packedTx string
parser *DashParser
}
tests := []struct {
name string
args args
want *bchain.Tx
want1 uint32
wantErr bool
}{
{
name: "dash-1",
args: args{
packedTx: testTxPacked1,
parser: NewDashParser(GetChainParams("main"), &btc.Configuration{}),
},
want: &testTx1,
want1: 1028160,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
b, _ := hex.DecodeString(tt.args.packedTx)
got, got1, err := tt.args.parser.UnpackTx(b)
if (err != nil) != tt.wantErr {
t.Errorf("unpackTx() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("unpackTx() got = %v, want %v", got, tt.want)
}
if got1 != tt.want1 {
t.Errorf("unpackTx() got1 = %v, want %v", got1, tt.want1)
}
})
}
}

View File

@ -6,8 +6,11 @@ import (
"encoding/json"
"github.com/golang/glog"
"github.com/juju/errors"
)
const firstBlockWithSpecialTransactions = 1028160
// DashRPC is an interface to JSON-RPC bitcoind service.
type DashRPC struct {
*btc.BitcoinRPC
@ -54,3 +57,57 @@ func (b *DashRPC) Initialize() error {
return nil
}
// GetBlock returns block with given hash.
func (b *DashRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) {
if hash == "" && height < firstBlockWithSpecialTransactions {
return b.BitcoinRPC.GetBlock(hash, height)
}
var err error
if hash == "" && height > 0 {
hash, err = b.GetBlockHash(height)
if err != nil {
return nil, err
}
}
glog.V(1).Info("rpc: getblock (verbosity=1) ", hash)
res := btc.ResGetBlockThin{}
req := btc.CmdGetBlock{Method: "getblock"}
req.Params.BlockHash = hash
req.Params.Verbosity = 1
err = b.Call(&req, &res)
if err != nil {
return nil, errors.Annotatef(err, "hash %v", hash)
}
if res.Error != nil {
return nil, errors.Annotatef(res.Error, "hash %v", hash)
}
txs := make([]bchain.Tx, 0, len(res.Result.Txids))
for _, txid := range res.Result.Txids {
tx, err := b.GetTransaction(txid)
if err != nil {
if err == bchain.ErrTxNotFound {
glog.Errorf("rpc: getblock: skipping transanction in block %s due error: %s", hash, err)
continue
}
return nil, err
}
txs = append(txs, *tx)
}
block := &bchain.Block{
BlockHeader: res.Result.BlockHeader,
Txs: txs,
}
return block, nil
}
// GetTransactionForMempool returns a transaction by the transaction ID.
// It could be optimized for mempool, i.e. without block time and confirmations
func (z *DashRPC) GetTransactionForMempool(txid string) (*bchain.Tx, error) {
return z.GetTransaction(txid)
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
00000020b652661d4cc8a262f9561a313b8cb15fcb4f69e6f88e908b3e0000000000000048a11a95e5a224637dc3639bf6c20c0ee0ca16b66a5dba5a9823ad9515a772c07625765cb8f34119a74e94e10503000500010000000000000000000000000000000000000000000000000000000000000000ffffffff170340b00f1291af3c09542bc8349901000000002f4e614effffffff024181f809000000001976a9146a341485a9444b35dc9cb90d24e7483de7d37e0088ac3581f809000000001976a9140d1156f6026bf975ea3553b03fb534d0959c294c88ac0000000026010040b00f0000000000000000000000000000000000000000000000000000000000000000000100000001f85264d11a747bdba77d411e5e4a3d35e3aeb5843b34a95234a2121ac65496bd000000006b483045022100dfa158fbd9773fab4f6f329c807e040af0c3a40967cbe01667169b914ed5ad960220061c5876364caa3e3c9c990ad2b4cc8b1a53d4f954dbda8434b0e67cc8348ff6012103093865e1e132b33a2a5ed01c79d2edba3473826a66cb26b8311bfa42749c2190ffffffff02ec3f8a2a010000001976a91470dcef2a22575d7a8f0779fb1d6cdd48135bd22788ac3116491d000000001976a91471348f7780e955a2a60eba17ecc4c826ebc23a9888ac000000000200000002ce167f4b13fdd25facf0a92aba23f5220f8366ac5ed29b83a3a6f2c9f76f5726000000006b483045022100c39871831ee92830da00fe1ba69925f8476cf39bcd1629857bf59b908b53649402201d168fb69ea995ed49059b7d532475891d3b482d19778f339bb95f3a6be3df6701210387bfcd2d4f7e7e68549ecb21a14e6c56eb7c5c3c5c5fddf975106fa4891268b5feffffff01fc4e7dd7446a800cf382e1f668fb155faa6c0f31f9871cdc54d801b64edbcc000000006b4830450221008b55df7a57529004a0cdf6b4ff43be097f9451c86a8457f073dd5cbab3a60f3002205c5b68ed70901e235402c850bec7ca3fac7448faf5a6989de78c2215734a7de60121034e448205a98486cb2fcc0294fa88579b6a6ba0213cbcf7e68cb9527a63a5c826feffffff028cb45e00000000001976a914cf50f0c1cecf279dac4207cd5e77caf2e1b4193f88ac2989f809000000001976a914c646b9ce369333f0b174bf70ffe169464c81776a88ac3eb00f000200000001ff6f6384099ff092fcc1257ebd2ba723fc33a1b3c858c8e4390180a071ed15aa010000006b483045022100d6e867d1c5d815552d1efa4ec06b0111d8b180d708f3497bcd53e8d945aebdbb02205fa734c5a3bb18da893da3dcd9540a2ea7535418323e8c0c1b0a88cc932a1fe5012102e03200f8de25214eaf675554a2f28d2b60c7c4c545691729dec1c2fe0d3b630bfeffffff023d346901000000001976a914953ba93fae9ab552793533769a7de6f7630781e688ac56870603000000001976a914887933e453428b99b3b2dfdd8359f6d8016873e688ac39b00f0002000000082e51598a94eea6059479fb3b31605bbc44ce50c25f014e91bd8bdd5b14891442000000006b483045022100dd04fefab5ad17929d1ed8561335a7c493761e0cbf6a23b8445d21da7b09bd4b0220398f2df4d47bc0b924ce71754d452268485b85ab3f1a8e21529559b4302003510121036b56e0f8996c289842752bc71eb60f2f9964b28653d3857725efe1bdc3193386feffffffacd131246ada5f7ad177bdc6d236766ff78559506a5da90e81fcb26366ac377b010000006a47304402202932864432b51a9894128b74fc109951d182fc91bac3a67a61b3b7a83e3bea71022021dd28448b54bf560dca69c198cdd9a0d1184adb231c72141db19d5114559ffd012102102efafa2ca31499090aaea1bf1dfd91f16bf29c96202faf48cdf14a791f757cfeffffffecba1b7c9071c42ff79e5db479369d5adb8995ec1ea28ba75676d58c09c10089000000006b48304502210096b33a9a65cbeca58cdd0ca01d4792dd075ac832aecb2d113c522bc635f535bd0220736d726b6126c4845335ad61af1e7e193d7d8661ca69c749ca257191bd24447f01210357386ba93caf20c1fee3bccf00cd4ad96535e42bda22d22c605cf3cf7532dde4feffffff0e7e7a7cfffa2c6917dc5578eac7153b334d7f813d256ccea9e60d1eb9f62da3000000006a4730440220362935ea1a705d4c275d733e350f53798f05e8a39e6cdd03f3ba5434c6d78c370220031729d52d9f8d1a301ce59335af7a5b7fa70f0bcae42dd95254e22a725bd9d4012102f7164234b08f8a6f47d92fa01eac375d0c1ebd4300ce40412302a5ea0218c19afeffffffa0f6f00c4b99b7b25ef20e64ce4a4bf5e2ca13e3cdbe8e1d7ec7dfab1e0292ab000000006a473044022077a9137654cafd4a583ae5b04faf1d549ab5d1d0655e6b48b4e1242a596efca4022008372fc1bec3490c4376408d4c9eed32b4182563ba43943b4bb959c29c91058a0121022bd6d141f0c455c7f54cb690db6b1349223ddd27fb65d64bb9ac35735ef48c5bfeffffffab6cf7419933410d25538a971b13d099e155c0339c2257f350ab2ef24944c1b9000000006b483045022100dd7b537e334e3dac76c57eb0683ea4cd74fd9a59f55f928f8c7f94a19064d8ea022045f4452f3742bb6ee2a7080507433904f5fcb94ef9ae8aa6bac0778a9889f4ef012102d00a705fcd458bc3e6dc0eab9f449162f73fff0773a4e09e2a3164b39bc26983feffffffc817f12e090dfe54e5c0bb1546fb01a6eac23f49aafa367bbbb0dc857bc98dc9000000006b483045022100959e19c75158f235abd196643dff70762f6263a058ed4b7d19c00f20ba18fef00220402189f2c8dc56139812d65c76b2b1a90b5148d7ecda6326053afc26471f9261012103330db7800ba95f92006909aba0e270985cccb34c7787dbf4776b6762f97132abfeffffff540f639ab8f986bc0832181c9efd315a1af81d07e61b367cdb10cab58f9518ff000000006b48304502210083db93b6724fe929e984f3e5df3ece7c43277a926abc841cd4cd75d15d787de0022027a299e82e420d418da973f82afcd80cf986c022042d9c758f51af64aa08fb2e01210369eccadf5758ca5eac2f3ce63001294819c119d1411a294135280cfd423ac59dfeffffff028f650f00000000001976a914535960e621cc458918703e33a7644501299a9d2188ace5a2ea01000000001976a914dcdc4137f2e7a347fcb14c85a6d1730d3e212c4b88ac00000000

File diff suppressed because one or more lines are too long