parent
fa4a11c3a7
commit
954619efe9
|
@ -20,6 +20,7 @@ import (
|
|||
"blockbook/bchain/coins/monacoin"
|
||||
"blockbook/bchain/coins/myriad"
|
||||
"blockbook/bchain/coins/namecoin"
|
||||
"blockbook/bchain/coins/nuls"
|
||||
"blockbook/bchain/coins/pivx"
|
||||
"blockbook/bchain/coins/qtum"
|
||||
"blockbook/bchain/coins/vertcoin"
|
||||
|
@ -79,6 +80,7 @@ func init() {
|
|||
BlockChainFactories["Bellcoin"] = bellcoin.NewBellcoinRPC
|
||||
BlockChainFactories["Qtum"] = qtum.NewQtumRPC
|
||||
BlockChainFactories["Qtum Testnet"] = qtum.NewQtumRPC
|
||||
BlockChainFactories["NULS"] = nuls.NewNulsRPC
|
||||
}
|
||||
|
||||
// GetCoinNameFromConfig gets coin name and coin shortcut from config file
|
||||
|
|
|
@ -0,0 +1,151 @@
|
|||
package nuls
|
||||
|
||||
import (
|
||||
"blockbook/bchain"
|
||||
"blockbook/bchain/coins/btc"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"github.com/bsm/go-vlq"
|
||||
"github.com/martinboehm/btcutil/base58"
|
||||
|
||||
"github.com/martinboehm/btcd/wire"
|
||||
"github.com/martinboehm/btcutil/chaincfg"
|
||||
)
|
||||
|
||||
const (
|
||||
MainnetMagic wire.BitcoinNet = 0xbd6b0cbf
|
||||
TestnetMagic wire.BitcoinNet = 0xffcae2ce
|
||||
RegtestMagic wire.BitcoinNet = 0xdcb7c1fc
|
||||
)
|
||||
|
||||
var (
|
||||
MainNetParams chaincfg.Params
|
||||
TestNetParams chaincfg.Params
|
||||
RegtestParams chaincfg.Params
|
||||
)
|
||||
|
||||
func init() {
|
||||
MainNetParams = chaincfg.MainNetParams
|
||||
MainNetParams.Net = MainnetMagic
|
||||
|
||||
// Address encoding magics
|
||||
MainNetParams.PubKeyHashAddrID = []byte{38} // base58 prefix: G
|
||||
MainNetParams.ScriptHashAddrID = []byte{10} // base58 prefix: W
|
||||
|
||||
TestNetParams = chaincfg.TestNet3Params
|
||||
TestNetParams.Net = TestnetMagic
|
||||
|
||||
// Address encoding magics
|
||||
TestNetParams.PubKeyHashAddrID = []byte{140} // base58 prefix: y
|
||||
TestNetParams.ScriptHashAddrID = []byte{19} // base58 prefix: 8 or 9
|
||||
|
||||
RegtestParams = chaincfg.RegressionNetParams
|
||||
RegtestParams.Net = RegtestMagic
|
||||
|
||||
// Address encoding magics
|
||||
RegtestParams.PubKeyHashAddrID = []byte{140} // base58 prefix: y
|
||||
RegtestParams.ScriptHashAddrID = []byte{19} // base58 prefix: 8 or 9
|
||||
}
|
||||
|
||||
// NulsParser handle
|
||||
type NulsParser struct {
|
||||
*btc.BitcoinParser
|
||||
}
|
||||
|
||||
// NewNulsParser returns new NulsParser instance
|
||||
func NewNulsParser(params *chaincfg.Params, c *btc.Configuration) *NulsParser {
|
||||
return &NulsParser{BitcoinParser: btc.NewBitcoinParser(params, c)}
|
||||
}
|
||||
|
||||
// GetChainParams contains network parameters for the main Gincoin network,
|
||||
// the regression test Gincoin network, the test Gincoin network and
|
||||
// the simulation test Gincoin network, in this order
|
||||
func GetChainParams(chain string) *chaincfg.Params {
|
||||
if !chaincfg.IsRegistered(&MainNetParams) {
|
||||
err := chaincfg.Register(&MainNetParams)
|
||||
if err == nil {
|
||||
err = chaincfg.Register(&TestNetParams)
|
||||
}
|
||||
if err == nil {
|
||||
err = chaincfg.Register(&RegtestParams)
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
switch chain {
|
||||
case "test":
|
||||
return &TestNetParams
|
||||
case "regtest":
|
||||
return &RegtestParams
|
||||
default:
|
||||
return &MainNetParams
|
||||
}
|
||||
}
|
||||
|
||||
// PackedTxidLen returns length in bytes of packed txid
|
||||
func (p *NulsParser) PackedTxidLen() int {
|
||||
return 34
|
||||
}
|
||||
|
||||
// GetAddrDescFromAddress returns internal address representation (descriptor) of given address
|
||||
func (p *NulsParser) GetAddrDescFromAddress(address string) (bchain.AddressDescriptor, error) {
|
||||
addressByte := base58.Decode(address)
|
||||
return bchain.AddressDescriptor(addressByte), nil
|
||||
}
|
||||
|
||||
// GetAddrDescFromVout returns internal address representation (descriptor) of given transaction output
|
||||
func (p *NulsParser) GetAddrDescFromVout(output *bchain.Vout) (bchain.AddressDescriptor, error) {
|
||||
addressStr := output.ScriptPubKey.Hex
|
||||
addressByte := base58.Decode(addressStr)
|
||||
return bchain.AddressDescriptor(addressByte), nil
|
||||
}
|
||||
|
||||
// GetAddressesFromAddrDesc returns addresses for given address descriptor with flag if the addresses are searchable
|
||||
func (p *NulsParser) GetAddressesFromAddrDesc(addrDesc bchain.AddressDescriptor) ([]string, bool, error) {
|
||||
var addrs []string
|
||||
|
||||
if addrDesc != nil {
|
||||
addrs = append(addrs, base58.Encode(addrDesc))
|
||||
}
|
||||
|
||||
return addrs, true, nil
|
||||
}
|
||||
|
||||
// PackTx packs transaction to byte array
|
||||
func (p *NulsParser) PackTx(tx *bchain.Tx, height uint32, blockTime int64) ([]byte, error) {
|
||||
txBytes, error := json.Marshal(tx)
|
||||
if error != nil {
|
||||
return nil, error
|
||||
}
|
||||
|
||||
buf := make([]byte, 4+vlq.MaxLen64)
|
||||
binary.BigEndian.PutUint32(buf[0:4], height)
|
||||
vlq.PutInt(buf[4:4+vlq.MaxLen64], blockTime)
|
||||
resByes := bytes.Join([][]byte{buf, txBytes}, []byte(""))
|
||||
return resByes, nil
|
||||
}
|
||||
|
||||
// UnpackTx unpacks transaction from byte array
|
||||
func (p *NulsParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) {
|
||||
height := binary.BigEndian.Uint32(buf)
|
||||
bt, _ := vlq.Int(buf[4 : 4+vlq.MaxLen64])
|
||||
tx, err := p.ParseTx(buf[4+vlq.MaxLen64:])
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
tx.Blocktime = bt
|
||||
|
||||
return tx, height, nil
|
||||
}
|
||||
|
||||
func (p *NulsParser) ParseTx(b []byte) (*bchain.Tx, error) {
|
||||
tx := bchain.Tx{}
|
||||
err := json.Unmarshal(b, &tx)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &tx, err
|
||||
}
|
|
@ -0,0 +1,343 @@
|
|||
package nuls
|
||||
|
||||
import (
|
||||
"blockbook/bchain"
|
||||
"blockbook/bchain/coins/btc"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
testTx1, testTx2 bchain.Tx
|
||||
|
||||
testTxPacked1 = "0001e240daadfbe7931e000000007b22686578223a22222c2274786964223a223030323036626231323431303861356664393865383238623138316666303162393237633063366234373764343531326266656638346366353266306663326136613161222c2276657273696f6e223a312c226c6f636b74696d65223a302c2276696e223a5b7b22636f696e62617365223a22222c2274786964223a223030323035343537616230373033623164313034363264343334373034386330626233353634653430663537616531663632353136393539343364653161633831306130222c22766f7574223a302c22736372697074536967223a7b22686578223a22227d2c2273657175656e6365223a302c22616464726573736573223a5b224e736535334d77524c424a31575555365365644d485141466643507442377734225d7d5d2c22766f7574223a5b7b2256616c7565536174223a3339393939393030303030302c2276616c7565223a302c226e223a302c227363726970745075624b6579223a7b22686578223a224e7365347a705a4873557555376835796d7632387063476277486a75336a6f56222c22616464726573736573223a5b224e7365347a705a4873557555376835796d7632387063476277486a75336a6f56225d7d7d5d2c22626c6f636b74696d65223a313535323335373834343137357d"
|
||||
testTxPacked2 = "0007c91adaadfbb89946000000007b22686578223a22222c2274786964223a223030323037386139386633383163373134613036386436303565346265316565323139353438353736313165303938616262636333663530633536383066386164326535222c2276657273696f6e223a312c226c6f636b74696d65223a302c2276696e223a5b7b22636f696e62617365223a22222c2274786964223a223030323037613430646334623661633430376434396133633333616137353462623466303565343565353763323438313162313437653762663363616630363361383233222c22766f7574223a312c22736372697074536967223a7b22686578223a22227d2c2273657175656e6365223a302c22616464726573736573223a5b224e73653131397a326f53444a596b466b786d775944695974506642654e6b7169225d7d5d2c22766f7574223a5b7b2256616c7565536174223a3430303030303030303030302c2276616c7565223a302c226e223a302c227363726970745075624b6579223a7b22686578223a224e736534696b6a45383867324267734e7773737754646b53776953724b6a6a53222c22616464726573736573223a5b224e736534696b6a45383867324267734e7773737754646b53776953724b6a6a53225d7d7d2c7b2256616c7565536174223a373238363536353537303030302c2276616c7565223a302c226e223a312c227363726970745075624b6579223a7b22686578223a224e73653131397a326f53444a596b466b786d775944695974506642654e6b7169222c22616464726573736573223a5b224e73653131397a326f53444a596b466b786d775944695974506642654e6b7169225d7d7d5d2c22626c6f636b74696d65223a313535323335373435393535357d"
|
||||
)
|
||||
|
||||
func init() {
|
||||
testTx1 = bchain.Tx{
|
||||
Hex: "",
|
||||
Blocktime: 1552357844175,
|
||||
Txid: "00206bb124108a5fd98e828b181ff01b927c0c6b477d4512bfef84cf52f0fc2a6a1a",
|
||||
LockTime: 0,
|
||||
Version: 1,
|
||||
Vin: []bchain.Vin{
|
||||
{
|
||||
Txid: "00205457ab0703b1d10462d4347048c0bb3564e40f57ae1f6251695943de1ac810a0",
|
||||
Vout: 0,
|
||||
Sequence: 0,
|
||||
Addresses: []string{
|
||||
"Nse53MwRLBJ1WUU6SedMHQAFfCPtB7w4",
|
||||
},
|
||||
},
|
||||
},
|
||||
Vout: []bchain.Vout{
|
||||
{
|
||||
ValueSat: *big.NewInt(399999000000),
|
||||
N: 0,
|
||||
JsonValue: json.Number("0"),
|
||||
ScriptPubKey: bchain.ScriptPubKey{
|
||||
Hex: "Nse4zpZHsUuU7h5ymv28pcGbwHju3joV",
|
||||
Addresses: []string{
|
||||
"Nse4zpZHsUuU7h5ymv28pcGbwHju3joV",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
//CoinSpecificData: []string{},
|
||||
}
|
||||
|
||||
testTx2 = bchain.Tx{
|
||||
Hex: "",
|
||||
Blocktime: 1552357459555,
|
||||
Txid: "002078a98f381c714a068d605e4be1ee21954857611e098abbcc3f50c5680f8ad2e5",
|
||||
LockTime: 0,
|
||||
Version: 1,
|
||||
Vin: []bchain.Vin{
|
||||
{
|
||||
Txid: "00207a40dc4b6ac407d49a3c33aa754bb4f05e45e57c24811b147e7bf3caf063a823",
|
||||
Vout: 1,
|
||||
Sequence: 0,
|
||||
Addresses: []string{
|
||||
"Nse119z2oSDJYkFkxmwYDiYtPfBeNkqi",
|
||||
},
|
||||
},
|
||||
},
|
||||
Vout: []bchain.Vout{
|
||||
{
|
||||
ValueSat: *big.NewInt(400000000000),
|
||||
N: 0,
|
||||
JsonValue: json.Number("0"),
|
||||
ScriptPubKey: bchain.ScriptPubKey{
|
||||
Hex: "Nse4ikjE88g2BgsNwsswTdkSwiSrKjjS",
|
||||
Addresses: []string{
|
||||
"Nse4ikjE88g2BgsNwsswTdkSwiSrKjjS",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ValueSat: *big.NewInt(7286565570000),
|
||||
N: 1,
|
||||
JsonValue: json.Number("0"),
|
||||
ScriptPubKey: bchain.ScriptPubKey{
|
||||
Hex: "Nse119z2oSDJYkFkxmwYDiYtPfBeNkqi",
|
||||
Addresses: []string{
|
||||
"Nse119z2oSDJYkFkxmwYDiYtPfBeNkqi",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
//CoinSpecificData: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAddrDescFromAddress(t *testing.T) {
|
||||
type args struct {
|
||||
address string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "P2PKH",
|
||||
args: args{address: "Nse4j39uEMuxx5j577h3K2MDLAQ64JZN"},
|
||||
want: "042301ac78cd3eb193287e2c59e4cbd765c5c47d432c2fa1",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "P2PKH",
|
||||
args: args{address: "Nse2e7U7nmGT8UHsvQ7JfksLtWwoLwrd"},
|
||||
want: "0423018a90e66a64318f6af6d673487a6560f5686fd26a2e",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "P2PKH",
|
||||
args: args{address: "NsdvMEP57nzxmBa5z18rx9sQsLgUfNtw"},
|
||||
want: "04230124422cfe426573e476fd45d7c2a43a75a0b6b8c478",
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
parser := NewNulsParser(GetChainParams("main"), &btc.Configuration{})
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := parser.GetAddrDescFromAddress(tt.args.address)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
h := hex.EncodeToString(got)
|
||||
if !reflect.DeepEqual(h, tt.want) {
|
||||
t.Errorf("GetAddrDescFromAddress() = %v, want %v", h, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAddrDescFromVout(t *testing.T) {
|
||||
type args struct {
|
||||
vout bchain.Vout
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "P2PK",
|
||||
args: args{vout: bchain.Vout{ScriptPubKey: bchain.ScriptPubKey{Hex: "Nse4j39uEMuxx5j577h3K2MDLAQ64JZN"}}},
|
||||
want: "042301ac78cd3eb193287e2c59e4cbd765c5c47d432c2fa1",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "P2PK",
|
||||
args: args{vout: bchain.Vout{ScriptPubKey: bchain.ScriptPubKey{Hex: "Nse2e7U7nmGT8UHsvQ7JfksLtWwoLwrd"}}},
|
||||
want: "0423018a90e66a64318f6af6d673487a6560f5686fd26a2e",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "P2PK",
|
||||
args: args{vout: bchain.Vout{ScriptPubKey: bchain.ScriptPubKey{Hex: "NsdvMEP57nzxmBa5z18rx9sQsLgUfNtw"}}},
|
||||
want: "04230124422cfe426573e476fd45d7c2a43a75a0b6b8c478",
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
parser := NewNulsParser(GetChainParams("main"), &btc.Configuration{})
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := parser.GetAddrDescFromVout(&tt.args.vout)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("GetAddrDescFromVout() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
h := hex.EncodeToString(got)
|
||||
if !reflect.DeepEqual(h, tt.want) {
|
||||
t.Errorf("GetAddrDescFromVout() = %v, want %v", h, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAddressesFromAddrDesc(t *testing.T) {
|
||||
type args struct {
|
||||
script string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want []string
|
||||
want2 bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "P2PKH",
|
||||
args: args{script: "042301ac78cd3eb193287e2c59e4cbd765c5c47d432c2fa1"},
|
||||
want: []string{"Nse4j39uEMuxx5j577h3K2MDLAQ64JZN"},
|
||||
want2: true,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "P2PKH",
|
||||
args: args{script: "0423018a90e66a64318f6af6d673487a6560f5686fd26a2e"},
|
||||
want: []string{"Nse2e7U7nmGT8UHsvQ7JfksLtWwoLwrd"},
|
||||
want2: true,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "P2PKH",
|
||||
args: args{script: "04230124422cfe426573e476fd45d7c2a43a75a0b6b8c478"},
|
||||
want: []string{"NsdvMEP57nzxmBa5z18rx9sQsLgUfNtw"},
|
||||
want2: true,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
parser := NewNulsParser(GetChainParams("main"), &btc.Configuration{})
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
b, _ := hex.DecodeString(tt.args.script)
|
||||
got, got2, err := parser.GetAddressesFromAddrDesc(b)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("GetAddressesFromAddrDesc() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("GetAddressesFromAddrDesc() = %v, want %v", got, tt.want)
|
||||
}
|
||||
if !reflect.DeepEqual(got2, tt.want2) {
|
||||
t.Errorf("GetAddressesFromAddrDesc() = %v, want %v", got2, tt.want2)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPackTx(t *testing.T) {
|
||||
type args struct {
|
||||
tx bchain.Tx
|
||||
height uint32
|
||||
blockTime int64
|
||||
parser *NulsParser
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "test-1",
|
||||
args: args{
|
||||
tx: testTx1,
|
||||
height: 123456,
|
||||
blockTime: 1552357844175,
|
||||
parser: NewNulsParser(GetChainParams("main"), &btc.Configuration{}),
|
||||
},
|
||||
want: testTxPacked1,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "test-2",
|
||||
args: args{
|
||||
tx: testTx2,
|
||||
height: 510234,
|
||||
blockTime: 1552357459555,
|
||||
parser: NewNulsParser(GetChainParams("main"), &btc.Configuration{}),
|
||||
},
|
||||
want: testTxPacked2,
|
||||
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 TestUnpackTx(t *testing.T) {
|
||||
type args struct {
|
||||
packedTx string
|
||||
parser *NulsParser
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *bchain.Tx
|
||||
want1 uint32
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "test-1",
|
||||
args: args{
|
||||
packedTx: testTxPacked1,
|
||||
parser: NewNulsParser(GetChainParams("main"), &btc.Configuration{}),
|
||||
},
|
||||
want: &testTx1,
|
||||
want1: 123456,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "test-2",
|
||||
args: args{
|
||||
packedTx: testTxPacked2,
|
||||
parser: NewNulsParser(GetChainParams("main"), &btc.Configuration{}),
|
||||
},
|
||||
want: &testTx2,
|
||||
want1: 510234,
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,635 @@
|
|||
package nuls
|
||||
|
||||
import (
|
||||
"blockbook/bchain"
|
||||
"blockbook/bchain/coins/btc"
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"github.com/gobuffalo/packr/v2/file/resolver/encoding/hex"
|
||||
"github.com/juju/errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"net"
|
||||
"net/http"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// NulsRPC is an interface to JSON-RPC bitcoind service.
|
||||
type NulsRPC struct {
|
||||
*btc.BitcoinRPC
|
||||
client http.Client
|
||||
rpcURL string
|
||||
user string
|
||||
password string
|
||||
}
|
||||
|
||||
// NewNulsRPC returns new NulsRPC instance.
|
||||
func NewNulsRPC(config json.RawMessage, pushHandler func(bchain.NotificationType)) (bchain.BlockChain, error) {
|
||||
b, err := btc.NewBitcoinRPC(config, pushHandler)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var c btc.Configuration
|
||||
err = json.Unmarshal(config, &c)
|
||||
if err != nil {
|
||||
return nil, errors.Annotatef(err, "Invalid configuration file")
|
||||
}
|
||||
|
||||
transport := &http.Transport{
|
||||
Dial: (&net.Dialer{KeepAlive: 600 * time.Second}).Dial,
|
||||
MaxIdleConns: 100,
|
||||
MaxIdleConnsPerHost: 100, // necessary to not to deplete ports
|
||||
}
|
||||
|
||||
s := &NulsRPC{
|
||||
BitcoinRPC: b.(*btc.BitcoinRPC),
|
||||
client: http.Client{Timeout: time.Duration(c.RPCTimeout) * time.Second, Transport: transport},
|
||||
rpcURL: c.RPCURL,
|
||||
user: c.RPCUser,
|
||||
password: c.RPCPass,
|
||||
}
|
||||
s.BitcoinRPC.RPCMarshaler = btc.JSONMarshalerV1{}
|
||||
s.BitcoinRPC.ChainConfig.SupportsEstimateSmartFee = false
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Initialize initializes GincoinRPC instance.
|
||||
func (n *NulsRPC) Initialize() error {
|
||||
chainName := ""
|
||||
params := GetChainParams(chainName)
|
||||
|
||||
// always create parser
|
||||
n.BitcoinRPC.Parser = NewNulsParser(params, n.BitcoinRPC.ChainConfig)
|
||||
|
||||
// parameters for getInfo request
|
||||
if params.Net == MainnetMagic {
|
||||
n.BitcoinRPC.Testnet = false
|
||||
n.BitcoinRPC.Network = "livenet"
|
||||
} else {
|
||||
n.BitcoinRPC.Testnet = true
|
||||
n.BitcoinRPC.Network = "testnet"
|
||||
}
|
||||
|
||||
glog.Info("rpc: block chain ", params.Name)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type CmdGetNetworkInfo struct {
|
||||
Success bool `json:"success"`
|
||||
Data struct {
|
||||
LocalBestHeight int64 `json:"localBestHeight"`
|
||||
NetBestHeight int `json:"netBestHeight"`
|
||||
TimeOffset string `json:"timeOffset"`
|
||||
InCount int8 `json:"inCount"`
|
||||
OutCount int8 `json:"outCount"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type CmdGetVersionInfo struct {
|
||||
Success bool `json:"success"`
|
||||
Data struct {
|
||||
MyVersion string `json:"myVersion"`
|
||||
NewestVersion string `json:"newestVersion"`
|
||||
NetworkVersion int `json:"networkVersion"`
|
||||
Infromation string `json:"infromation"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type CmdGetBestBlockHash struct {
|
||||
Success bool `json:"success"`
|
||||
Data struct {
|
||||
Value string `json:"value"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type CmdGetBestBlockHeight struct {
|
||||
Success bool `json:"success"`
|
||||
Data struct {
|
||||
Value uint32 `json:"value"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type CmdTxBroadcast struct {
|
||||
Success bool `json:"success"`
|
||||
Data struct {
|
||||
Value string `json:"value"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type CmdGetBlockHeader struct {
|
||||
Success bool `json:"success"`
|
||||
Data struct {
|
||||
Hash string `json:"hash"`
|
||||
PreHash string `json:"preHash"`
|
||||
MerkleHash string `json:"merkleHash"`
|
||||
StateRoot string `json:"stateRoot"`
|
||||
Time int64 `json:"time"`
|
||||
Height int64 `json:"height"`
|
||||
TxCount int `json:"txCount"`
|
||||
PackingAddress string `json:"packingAddress"`
|
||||
ConfirmCount int `json:"confirmCount"`
|
||||
ScriptSig string `json:"scriptSig"`
|
||||
Size int `json:"size"`
|
||||
Reward float64 `json:"reward"`
|
||||
Fee float64 `json:"fee"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type CmdGetBlock struct {
|
||||
Success bool `json:"success"`
|
||||
Data struct {
|
||||
Hash string `json:"hash"`
|
||||
PreHash string `json:"preHash"`
|
||||
MerkleHash string `json:"merkleHash"`
|
||||
StateRoot string `json:"stateRoot"`
|
||||
Time int64 `json:"time"`
|
||||
Height int64 `json:"height"`
|
||||
TxCount int `json:"txCount"`
|
||||
PackingAddress string `json:"packingAddress"`
|
||||
ConfirmCount int `json:"confirmCount"`
|
||||
ScriptSig string `json:"scriptSig"`
|
||||
Size int `json:"size"`
|
||||
Reward float64 `json:"reward"`
|
||||
Fee float64 `json:"fee"`
|
||||
|
||||
TxList []Tx `json:"txList"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type CmdGetTx struct {
|
||||
Success bool `json:"success"`
|
||||
Tx Tx `json:"data"`
|
||||
}
|
||||
|
||||
type Tx struct {
|
||||
Hash string `json:"hash"`
|
||||
Type int `json:"type"`
|
||||
Time int64 `json:"time"`
|
||||
BlockHeight int64 `json:"blockHeight"`
|
||||
Fee float64 `json:"fee"`
|
||||
Value float64 `json:"value"`
|
||||
Remark string `json:"remark"`
|
||||
ScriptSig string `json:"scriptSig"`
|
||||
Status int `json:"status"`
|
||||
ConfirmCount int `json:"confirmCount"`
|
||||
Size int `json:"size"`
|
||||
Inputs []struct {
|
||||
FromHash string `json:"fromHash"`
|
||||
FromIndex uint32 `json:"fromIndex"`
|
||||
Address string `json:"address"`
|
||||
Value float64 `json:"value"`
|
||||
} `json:"inputs"`
|
||||
Outputs []struct {
|
||||
Address string `json:"address"`
|
||||
Value int64 `json:"value"`
|
||||
LockTime int64 `json:"lockTime"`
|
||||
} `json:"outputs"`
|
||||
}
|
||||
|
||||
type CmdGetTxBytes struct {
|
||||
Success bool `json:"success"`
|
||||
Data struct {
|
||||
Value string `json:"value"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
func (n *NulsRPC) GetChainInfo() (*bchain.ChainInfo, error) {
|
||||
networkInfo := CmdGetNetworkInfo{}
|
||||
error := n.Call("/api/network/info", &networkInfo)
|
||||
if error != nil {
|
||||
return nil, error
|
||||
}
|
||||
|
||||
versionInfo := CmdGetVersionInfo{}
|
||||
error = n.Call("/api/client/version", &versionInfo)
|
||||
if error != nil {
|
||||
return nil, error
|
||||
}
|
||||
|
||||
chainInfo := &bchain.ChainInfo{
|
||||
Chain: "nuls",
|
||||
Blocks: networkInfo.Data.NetBestHeight,
|
||||
Headers: networkInfo.Data.NetBestHeight,
|
||||
Bestblockhash: "",
|
||||
Difficulty: networkInfo.Data.TimeOffset,
|
||||
SizeOnDisk: networkInfo.Data.LocalBestHeight,
|
||||
Version: versionInfo.Data.MyVersion,
|
||||
Subversion: versionInfo.Data.NewestVersion,
|
||||
ProtocolVersion: strconv.Itoa(versionInfo.Data.NetworkVersion),
|
||||
Timeoffset: 0,
|
||||
Warnings: versionInfo.Data.Infromation,
|
||||
}
|
||||
return chainInfo, nil
|
||||
}
|
||||
|
||||
func (n *NulsRPC) GetBestBlockHash() (string, error) {
|
||||
bestBlockHash := CmdGetBestBlockHash{}
|
||||
error := n.Call("/api/block/newest/hash", &bestBlockHash)
|
||||
if error != nil {
|
||||
return "", error
|
||||
}
|
||||
return bestBlockHash.Data.Value, nil
|
||||
}
|
||||
|
||||
func (n *NulsRPC) GetBestBlockHeight() (uint32, error) {
|
||||
bestBlockHeight := CmdGetBestBlockHeight{}
|
||||
error := n.Call("/api/block/newest/height", &bestBlockHeight)
|
||||
if error != nil {
|
||||
return 0, error
|
||||
}
|
||||
return bestBlockHeight.Data.Value, nil
|
||||
}
|
||||
|
||||
func (n *NulsRPC) GetBlockHash(height uint32) (string, error) {
|
||||
blockHeader := CmdGetBlockHeader{}
|
||||
error := n.Call("/api/block/header/height/"+strconv.Itoa(int(height)), &blockHeader)
|
||||
if error != nil {
|
||||
return "", error
|
||||
}
|
||||
|
||||
if !blockHeader.Success {
|
||||
return "", bchain.ErrBlockNotFound
|
||||
}
|
||||
|
||||
return blockHeader.Data.Hash, nil
|
||||
}
|
||||
|
||||
func (n *NulsRPC) GetBlockHeader(hash string) (*bchain.BlockHeader, error) {
|
||||
uri := "/api/block/header/hash/" + hash
|
||||
return n.getBlobkHeader(uri)
|
||||
}
|
||||
|
||||
func (n *NulsRPC) GetBlockHeaderByHeight(height uint32) (*bchain.BlockHeader, error) {
|
||||
uri := "/api/block/header/height/" + strconv.Itoa(int(height))
|
||||
return n.getBlobkHeader(uri)
|
||||
}
|
||||
|
||||
func (n *NulsRPC) getBlobkHeader(uri string) (*bchain.BlockHeader, error) {
|
||||
blockHeader := CmdGetBlockHeader{}
|
||||
error := n.Call(uri, &blockHeader)
|
||||
if error != nil {
|
||||
return nil, error
|
||||
}
|
||||
|
||||
if !blockHeader.Success {
|
||||
return nil, bchain.ErrBlockNotFound
|
||||
}
|
||||
|
||||
nexHash, _ := n.GetBlockHash(uint32(blockHeader.Data.Height + 1))
|
||||
|
||||
header := &bchain.BlockHeader{
|
||||
Hash: blockHeader.Data.Hash,
|
||||
Prev: blockHeader.Data.PreHash,
|
||||
Next: nexHash,
|
||||
Height: uint32(blockHeader.Data.Height),
|
||||
Confirmations: blockHeader.Data.ConfirmCount,
|
||||
Size: blockHeader.Data.Size,
|
||||
Time: blockHeader.Data.Time / 1000,
|
||||
}
|
||||
return header, nil
|
||||
}
|
||||
|
||||
func (n *NulsRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) {
|
||||
|
||||
url := "/api/block/hash/" + hash
|
||||
|
||||
if hash == "" {
|
||||
url = "/api/block/height/" + strconv.Itoa(int(height))
|
||||
}
|
||||
|
||||
getBlock := CmdGetBlock{}
|
||||
error := n.Call(url, &getBlock)
|
||||
if error != nil {
|
||||
return nil, error
|
||||
}
|
||||
|
||||
if !getBlock.Success {
|
||||
return nil, bchain.ErrBlockNotFound
|
||||
}
|
||||
|
||||
nexHash, _ := n.GetBlockHash(uint32(getBlock.Data.Height + 1))
|
||||
|
||||
header := bchain.BlockHeader{
|
||||
Hash: getBlock.Data.Hash,
|
||||
Prev: getBlock.Data.PreHash,
|
||||
Next: nexHash,
|
||||
Height: uint32(getBlock.Data.Height),
|
||||
Confirmations: getBlock.Data.ConfirmCount,
|
||||
Size: getBlock.Data.Size,
|
||||
Time: getBlock.Data.Time / 1000,
|
||||
}
|
||||
|
||||
var txs []bchain.Tx
|
||||
|
||||
for _, rawTx := range getBlock.Data.TxList {
|
||||
tx, err := converTx(rawTx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tx.Blocktime = header.Time
|
||||
txs = append(txs, *tx)
|
||||
}
|
||||
|
||||
block := &bchain.Block{
|
||||
BlockHeader: header,
|
||||
Txs: txs,
|
||||
}
|
||||
return block, nil
|
||||
}
|
||||
|
||||
func (n *NulsRPC) GetBlockInfo(hash string) (*bchain.BlockInfo, error) {
|
||||
if hash == "" {
|
||||
return nil, bchain.ErrBlockNotFound
|
||||
}
|
||||
|
||||
getBlock := CmdGetBlock{}
|
||||
error := n.Call("/api/block/hash/"+hash, &getBlock)
|
||||
if error != nil {
|
||||
return nil, error
|
||||
}
|
||||
|
||||
if !getBlock.Success {
|
||||
return nil, bchain.ErrBlockNotFound
|
||||
}
|
||||
|
||||
nexHash, _ := n.GetBlockHash(uint32(getBlock.Data.Height + 1))
|
||||
|
||||
header := bchain.BlockHeader{
|
||||
Hash: getBlock.Data.Hash,
|
||||
Prev: getBlock.Data.PreHash,
|
||||
Next: nexHash,
|
||||
Height: uint32(getBlock.Data.Height),
|
||||
Confirmations: getBlock.Data.ConfirmCount,
|
||||
Size: getBlock.Data.Size,
|
||||
Time: getBlock.Data.Time / 1000,
|
||||
}
|
||||
|
||||
var txIds []string
|
||||
|
||||
for _, rawTx := range getBlock.Data.TxList {
|
||||
txIds = append(txIds, rawTx.Hash)
|
||||
}
|
||||
|
||||
blockInfo := &bchain.BlockInfo{
|
||||
BlockHeader: header,
|
||||
MerkleRoot: getBlock.Data.MerkleHash,
|
||||
//Version: getBlock.Data.StateRoot,
|
||||
Txids: txIds,
|
||||
}
|
||||
return blockInfo, nil
|
||||
}
|
||||
|
||||
func (n *NulsRPC) GetMempool() ([]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (n *NulsRPC) GetTransaction(txid string) (*bchain.Tx, error) {
|
||||
if txid == "" {
|
||||
return nil, bchain.ErrTxidMissing
|
||||
}
|
||||
|
||||
getTx := CmdGetTx{}
|
||||
error := n.Call("/api/tx/hash/"+txid, &getTx)
|
||||
if error != nil {
|
||||
return nil, error
|
||||
}
|
||||
|
||||
if !getTx.Success {
|
||||
return nil, bchain.ErrTxNotFound
|
||||
}
|
||||
|
||||
tx, err := converTx(getTx.Tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blockHeaderHeight := getTx.Tx.BlockHeight
|
||||
blockHeader, e := n.GetBlockHeaderByHeight(uint32(blockHeaderHeight))
|
||||
if blockHeader != nil {
|
||||
tx.Blocktime = blockHeader.Time
|
||||
}
|
||||
|
||||
hexBytys, e := n.GetTransactionSpecific(tx)
|
||||
if e == nil {
|
||||
var hex string
|
||||
json.Unmarshal(hexBytys, &hex)
|
||||
tx.Hex = hex
|
||||
tx.CoinSpecificData = hex
|
||||
}
|
||||
return tx, nil
|
||||
}
|
||||
|
||||
func (n *NulsRPC) GetTransactionForMempool(txid string) (*bchain.Tx, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (n *NulsRPC) GetTransactionSpecific(tx *bchain.Tx) (json.RawMessage, error) {
|
||||
if tx == nil {
|
||||
return nil, bchain.ErrTxNotFound
|
||||
}
|
||||
|
||||
if csd, ok := tx.CoinSpecificData.(json.RawMessage); ok {
|
||||
return csd, nil
|
||||
}
|
||||
|
||||
getTxBytes := CmdGetTxBytes{}
|
||||
error := n.Call("/api/tx/bytes?hash="+tx.Txid, &getTxBytes)
|
||||
if error != nil {
|
||||
return nil, error
|
||||
}
|
||||
|
||||
if !getTxBytes.Success {
|
||||
return nil, bchain.ErrTxNotFound
|
||||
}
|
||||
|
||||
txBytes, byErr := base64.StdEncoding.DecodeString(getTxBytes.Data.Value)
|
||||
if byErr != nil {
|
||||
return nil, byErr
|
||||
}
|
||||
hexBytes := make([]byte, len(txBytes)*2)
|
||||
hex.Encode(hexBytes, txBytes)
|
||||
|
||||
m, err := json.Marshal(string(hexBytes))
|
||||
return json.RawMessage(m), err
|
||||
}
|
||||
|
||||
func (n *NulsRPC) EstimateSmartFee(blocks int, conservative bool) (big.Int, error) {
|
||||
return n.EstimateFee(blocks)
|
||||
}
|
||||
|
||||
func (n *NulsRPC) EstimateFee(blocks int) (big.Int, error) {
|
||||
return *big.NewInt(100000), nil
|
||||
}
|
||||
|
||||
func (n *NulsRPC) SendRawTransaction(tx string) (string, error) {
|
||||
broadcast := CmdTxBroadcast{}
|
||||
req := struct {
|
||||
TxHex string `json:"txHex"`
|
||||
}{
|
||||
TxHex: tx,
|
||||
}
|
||||
|
||||
error := n.Post("/api/accountledger/transaction/broadcast", req, &broadcast)
|
||||
if error != nil {
|
||||
return "", error
|
||||
}
|
||||
|
||||
if !broadcast.Success {
|
||||
return "", bchain.ErrTxidMissing
|
||||
}
|
||||
|
||||
return broadcast.Data.Value, nil
|
||||
}
|
||||
|
||||
func (n *NulsRPC) GetMempoolTransactionsForAddrDesc(addrDesc bchain.AddressDescriptor) ([]bchain.Outpoint, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (n *NulsRPC) ResyncMempool(onNewTxAddr bchain.OnNewTxAddrFunc) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// Call calls Backend RPC interface, using RPCMarshaler interface to marshall the request
|
||||
func (b *NulsRPC) Call(uri string, res interface{}) error {
|
||||
url := b.rpcURL + uri
|
||||
httpReq, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
httpReq.SetBasicAuth(b.user, b.password)
|
||||
httpRes, err := b.client.Do(httpReq)
|
||||
// in some cases the httpRes can contain data even if it returns error
|
||||
// see http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/
|
||||
if httpRes != nil {
|
||||
defer httpRes.Body.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// if server returns HTTP error code it might not return json with response
|
||||
// handle both cases
|
||||
if httpRes.StatusCode != 200 {
|
||||
err = safeDecodeResponse(httpRes.Body, &res)
|
||||
if err != nil {
|
||||
return errors.Errorf("%v %v", httpRes.Status, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return safeDecodeResponse(httpRes.Body, &res)
|
||||
}
|
||||
|
||||
func (b *NulsRPC) Post(uri string, req interface{}, res interface{}) error {
|
||||
url := b.rpcURL + uri
|
||||
httpData, err := b.RPCMarshaler.Marshal(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
httpReq, err := http.NewRequest("POST", url, bytes.NewBuffer(httpData))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
httpReq.SetBasicAuth(b.user, b.password)
|
||||
httpReq.Header.Set("Content-Type", "application/json")
|
||||
httpRes, err := b.client.Do(httpReq)
|
||||
// in some cases the httpRes can contain data even if it returns error
|
||||
// see http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/
|
||||
if httpRes != nil {
|
||||
defer httpRes.Body.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// if server returns HTTP error code it might not return json with response
|
||||
// handle both cases
|
||||
if httpRes.StatusCode != 200 {
|
||||
err = safeDecodeResponse(httpRes.Body, &res)
|
||||
if err != nil {
|
||||
return errors.Errorf("%v %v", httpRes.Status, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return safeDecodeResponse(httpRes.Body, &res)
|
||||
}
|
||||
|
||||
func safeDecodeResponse(body io.ReadCloser, res *interface{}) (err error) {
|
||||
var data []byte
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
glog.Error("unmarshal json recovered from panic: ", r, "; data: ", string(data))
|
||||
debug.PrintStack()
|
||||
if len(data) > 0 && len(data) < 2048 {
|
||||
err = errors.Errorf("Error: %v", string(data))
|
||||
} else {
|
||||
err = errors.New("Internal error")
|
||||
}
|
||||
}
|
||||
}()
|
||||
data, err = ioutil.ReadAll(body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//fmt.Println(string(data))
|
||||
error := json.Unmarshal(data, res)
|
||||
return error
|
||||
}
|
||||
|
||||
func converTx(rawTx Tx) (*bchain.Tx, error) {
|
||||
var lockTime int64 = 0
|
||||
var vins = make([]bchain.Vin, 0)
|
||||
var vouts []bchain.Vout
|
||||
|
||||
for _, input := range rawTx.Inputs {
|
||||
vin := bchain.Vin{
|
||||
Coinbase: "",
|
||||
Txid: input.FromHash,
|
||||
Vout: input.FromIndex,
|
||||
ScriptSig: bchain.ScriptSig{},
|
||||
Sequence: 0,
|
||||
Addresses: []string{input.Address},
|
||||
}
|
||||
vins = append(vins, vin)
|
||||
}
|
||||
|
||||
for index, output := range rawTx.Outputs {
|
||||
vout := bchain.Vout{
|
||||
ValueSat: *big.NewInt(output.Value),
|
||||
//JsonValue: "",
|
||||
//LockTime: output.LockTime,
|
||||
N: uint32(index),
|
||||
ScriptPubKey: bchain.ScriptPubKey{
|
||||
Hex: output.Address,
|
||||
Addresses: []string{
|
||||
output.Address,
|
||||
},
|
||||
},
|
||||
}
|
||||
vouts = append(vouts, vout)
|
||||
|
||||
if lockTime < output.LockTime {
|
||||
lockTime = output.LockTime
|
||||
}
|
||||
}
|
||||
|
||||
tx := &bchain.Tx{
|
||||
Hex: "",
|
||||
Txid: rawTx.Hash,
|
||||
Version: 0,
|
||||
LockTime: uint32(lockTime),
|
||||
Vin: vins,
|
||||
Vout: vouts,
|
||||
Confirmations: uint32(rawTx.ConfirmCount),
|
||||
Time: rawTx.Time / 1000,
|
||||
}
|
||||
|
||||
return tx, nil
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
{
|
||||
"coin": {
|
||||
"name": "NULS",
|
||||
"shortcut": "NULS",
|
||||
"label": "NULS",
|
||||
"alias": "nuls"
|
||||
},
|
||||
"ports": {
|
||||
"backend_rpc": 8053,
|
||||
"backend_message_queue": 38353,
|
||||
"blockbook_internal": 9053,
|
||||
"blockbook_public": 9153
|
||||
},
|
||||
"ipc": {
|
||||
"rpc_url_template": "http://127.0.0.1:{{.Ports.BackendRPC}}",
|
||||
"rpc_user": "rpc",
|
||||
"rpc_pass": "rpc",
|
||||
"rpc_timeout": 25,
|
||||
"message_queue_binding_template": "tcp://127.0.0.1:{{.Ports.BackendMessageQueue}}"
|
||||
},
|
||||
"backend": {
|
||||
"package_name": "backend-nuls",
|
||||
"package_revision": "1.0-1",
|
||||
"system_user": "nuls",
|
||||
"version": "1.2.1",
|
||||
"binary_url": "http://47.52.78.92/nuls-wallet/NULS-Wallet-linux64-1.2.1.tar.gz",
|
||||
"verification_type": "sha256",
|
||||
"verification_source": "8732e94415e9b6d51e9c7e1921002b4e43d78e2b12f2de636af53a562b4f4381",
|
||||
"extract_command": "tar -C backend --strip 0 -xf",
|
||||
"exclude_files": [],
|
||||
"exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/nuls.sh --port {{.Ports.BackendRPC}} --dataDir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --pid-file /run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid",
|
||||
"logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/*.log",
|
||||
"postinst_script_template": "",
|
||||
"service_type": "forking",
|
||||
"service_additional_params_template": "",
|
||||
"protect_memory": false,
|
||||
"mainnet": true,
|
||||
"server_config_file": "bitcoin_like.conf",
|
||||
"client_config_file": "bitcoin_like_client.conf",
|
||||
"additional_params": {
|
||||
"addnode": [
|
||||
"mainnet.nuls"
|
||||
]
|
||||
}
|
||||
},
|
||||
"blockbook": {
|
||||
"package_name": "blockbook-nuls",
|
||||
"system_user": "blockbook-nuls",
|
||||
"internal_binding_template": ":{{.Ports.BlockbookInternal}}",
|
||||
"public_binding_template": ":{{.Ports.BlockbookPublic}}",
|
||||
"explorer_url": "",
|
||||
"additional_params": "-resyncindexperiod=11000",
|
||||
"block_chain": {
|
||||
"parse": true,
|
||||
"mempool_workers": 4,
|
||||
"mempool_sub_workers": 8,
|
||||
"block_addresses_to_keep": 300,
|
||||
"xpub_magic": 76067358,
|
||||
"slip44": 133,
|
||||
"additional_params": {}
|
||||
}
|
||||
},
|
||||
"meta": {
|
||||
"package_maintainer": "NULS Core Team",
|
||||
"package_maintainer_email": "ln@nuls.io"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,174 @@
|
|||
{
|
||||
"blockHeight": 2092530,
|
||||
"blockHash": "002038a2301c19dec173b6776369f4985a66a4c583c7fa26ae9db7a4369643e71370",
|
||||
"blockTime": 1552392940,
|
||||
"blockSize": 290,
|
||||
"blockTxs": [
|
||||
"0020d682b3832334ed03177a7c51a0d94c6cdaee313d14e147203d55ed0f66202234",
|
||||
"0020ad9d437d9867c778c39a04c030fe941c7c07d9529cf534935471c7c8703ee7c7"
|
||||
],
|
||||
"txDetails": {
|
||||
"0020d682b3832334ed03177a7c51a0d94c6cdaee313d14e147203d55ed0f66202234": {
|
||||
"hex": "0100e069d471690100ffffffff000c170423015b98f40fb24b34d395a774aa5ea939c89999a7679e0bff0d00000000daf11f00000017042301a1bdaf958e76c84ff8a17189a6f1f4269ad4a54b7a050f0000000000daf11f00000017042301e60bf29198ed95d53013fdae781f1272a6ccdbda90d70e0000000000daf11f00000017042301751c960ae6c2f9052a53878c9e15f08ad4e79774accb130000000000daf11f00000017042301f69e4b0348458a4846f06dc4940fc1393edd3fcfea7e0e0000000000daf11f000000170423012cfbf9441677fcea3af313da99c01b485e117e9d4f5e0c0000000000daf11f00000017042301d6d33afa523fc78011aafc0c60e2e8dc45206e5676610c0000000000daf11f00000017042301e9e760b295568f955526b29e0d3a0c1dd3a1c50a4f5e0c0000000000daf11f000000170423017e8d9ff74a624f5ee2ef1a9d665385b7314c73a1750c0d0000000000daf11f000000170423012a3435e83852742f65574f297be94c48d9ab68fbb535350100000000daf11f000000170423017904ac680eb1b6a37652fe7fbc57d545d1447d48e55f0c0000000000daf11f00000017042301d2b1c26cdcb86896890bbb741e32d8057ad78cff9dbc180000000000daf11f00000000",
|
||||
"txid": "0020d682b3832334ed03177a7c51a0d94c6cdaee313d14e147203d55ed0f66202234",
|
||||
"blocktime": 1552392940,
|
||||
"time": 1552392940,
|
||||
"locktime": 2093530,
|
||||
"version": 0,
|
||||
"vin": [],
|
||||
"vout": [
|
||||
{
|
||||
"value": 2.34818462,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"hex": "NsdykbfjmZVHYNaVrKVF89UzUgPKaRa9",
|
||||
"Addresses": ["NsdykbfjmZVHYNaVrKVF89UzUgPKaRa9"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.00984442,
|
||||
"n": 1,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nse44mP6EJWbv9qPEPgi4sjtx8QmLzjU",
|
||||
"Addresses": ["Nse44mP6EJWbv9qPEPgi4sjtx8QmLzjU"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.00972688,
|
||||
"n": 2,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nse8GNwGtoBhxGmwfetqnhnMART4MVGU",
|
||||
"Addresses": ["Nse8GNwGtoBhxGmwfetqnhnMART4MVGU"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.01297324,
|
||||
"n": 3,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nse1KbUNjc8mEGAHgCRHXRWYR6zFa9dh",
|
||||
"Addresses": ["Nse1KbUNjc8mEGAHgCRHXRWYR6zFa9dh"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.00949994,
|
||||
"n": 4,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nse9HUun2HwFAypdpBkbjXbQPMkM7nQD",
|
||||
"Addresses": ["Nse9HUun2HwFAypdpBkbjXbQPMkM7nQD"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.00810575,
|
||||
"n": 5,
|
||||
"scriptPubKey": {
|
||||
"hex": "NsdvtMNdncJC3q8un2DpBWacZL6YRzJp",
|
||||
"Addresses": ["NsdvtMNdncJC3q8un2DpBWacZL6YRzJp"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.00811382,
|
||||
"n": 6,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nse7L6F3sEGc5NRghLtkRrVGaxwgXePS",
|
||||
"Addresses": ["Nse7L6F3sEGc5NRghLtkRrVGaxwgXePS"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.00810575,
|
||||
"n": 7,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nse8W8pMG1E94HQSkieUYUyASBr6oRQr",
|
||||
"Addresses": ["Nse8W8pMG1E94HQSkieUYUyASBr6oRQr"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.00855157,
|
||||
"n": 8,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nse1uGXjbCm9JJjmL88xYhnU59B6b3GR",
|
||||
"Addresses": ["Nse1uGXjbCm9JJjmL88xYhnU59B6b3GR"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.20264373,
|
||||
"n": 9,
|
||||
"scriptPubKey": {
|
||||
"hex": "NsdviSF29wA91UtGgYuN3HL6iMWLs2hc",
|
||||
"Addresses": ["NsdviSF29wA91UtGgYuN3HL6iMWLs2hc"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.00810981,
|
||||
"n": 10,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nse1ZXaeJDcuteKwbxacdBnX46bzfcuQ",
|
||||
"Addresses": ["Nse1ZXaeJDcuteKwbxacdBnX46bzfcuQ"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.01621149,
|
||||
"n": 11,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nse75MmS9stKYE11KCaLaVRaXHLprikS",
|
||||
"Addresses": ["Nse75MmS9stKYE11KCaLaVRaXHLprikS"]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"0020ad9d437d9867c778c39a04c030fe941c7c07d9529cf534935471c7c8703ee7c7": {
|
||||
"hex": "02004a53d471690100ffffffff03230020199146789cda517776c140a8638218b8fa29e3a1857573f9463df3e6fa1f56100167537e3b680000000000000000002300208fa6362e79b40b61791dd1484b79d61c9bebf4dc4986f347ba46e65feb9ee4dc00e02c504d00000000000000000000230020fe3ca565e667bd6b2b7889c1307d2006d0ce5697d13e98311c605ca802c7ef0f00603924cb2e0200000000000000000217042301405aa9eeca8af1edae196837bde4eef29bee040600664280d1000000000000000000170423016056a17c02cd53191ce2e647c5ed02d1c70e483607cdaed3c50100000000000000006b2102f23b91b10ca411dceccf873a313399f0c3a90b14e7b0b5f43642e33da882b7bc00473045022100c2521e5951d168e43f42010e8f1ba484cb445dcdb29d066caf0bce1c78476624022074df5ccdd4eda6f3adec5420efef85d7369b9b8266bbc392f5e76eef94f0b9a9",
|
||||
"txid": "0020ad9d437d9867c778c39a04c030fe941c7c07d9529cf534935471c7c8703ee7c7",
|
||||
"blocktime": 1552392940,
|
||||
"time": 1552392934,
|
||||
"locktime": 0,
|
||||
"version": 0,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "0020199146789cda517776c140a8638218b8fa29e3a1857573f9463df3e6fa1f5610",
|
||||
"vout": 1,
|
||||
"sequence": 0,
|
||||
"scriptSig": {
|
||||
"hex": ""
|
||||
},
|
||||
"Addresses": ["Nsdz3WNA1bfGSmy5oYY3KGMjZSd1C61p"]
|
||||
},
|
||||
{
|
||||
"txid": "00208fa6362e79b40b61791dd1484b79d61c9bebf4dc4986f347ba46e65feb9ee4dc",
|
||||
"vout": 0,
|
||||
"sequence": 0,
|
||||
"scriptSig": {
|
||||
"hex": ""
|
||||
},
|
||||
"Addresses": ["Nsdz3WNA1bfGSmy5oYY3KGMjZSd1C61p"]
|
||||
},
|
||||
{
|
||||
"txid": "0020fe3ca565e667bd6b2b7889c1307d2006d0ce5697d13e98311c605ca802c7ef0f",
|
||||
"vout": 0,
|
||||
"sequence": 0,
|
||||
"scriptSig": {
|
||||
"hex": ""
|
||||
},
|
||||
"Addresses": ["Nsdz3WNA1bfGSmy5oYY3KGMjZSd1C61p"]
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 8998.00000000,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nsdx5SBU9xN9SKz6dEwcz85WdFaNj2g4",
|
||||
"Addresses": ["Nsdx5SBU9xN9SKz6dEwcz85WdFaNj2g4"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 19491.71633415,
|
||||
"n": 1,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nsdz3WNA1bfGSmy5oYY3KGMjZSd1C61p",
|
||||
"Addresses": ["Nsdz3WNA1bfGSmy5oYY3KGMjZSd1C61p"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,546 @@
|
|||
{
|
||||
"connectBlocks": {
|
||||
"syncRanges": [
|
||||
{"lower": 2184003, "upper": 2184213}
|
||||
],
|
||||
"blocks": {
|
||||
"2184003": {
|
||||
"height": 2184003,
|
||||
"hash": "0020e281a2e5095904f95e3d1f3b48da808c7511e478362f22e678bf68b976e1bbb6",
|
||||
"noTxs": 1,
|
||||
"txDetails": [
|
||||
{
|
||||
"hex": "",
|
||||
"txid": "00208f0b652c397e761e0e2e1fc31f8b1b4f7fe28a1f35af382886c4d95c8dcb9715",
|
||||
"blocktime": 1553317770,
|
||||
"time": 1553317770,
|
||||
"version": 0,
|
||||
"vin": [],
|
||||
"vout": [
|
||||
{
|
||||
"value": 0.21957008,
|
||||
"n": 0,
|
||||
"lockTime": 2185003,
|
||||
"scriptPubKey": {
|
||||
"hex": "NsdteeXfqPcsmMStKeE2Es3dv7rSXjgF",
|
||||
"Addresses": ["NsdteeXfqPcsmMStKeE2Es3dv7rSXjgF"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.00836727,
|
||||
"n": 1,
|
||||
"lockTime": 2185003,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nse4H3MD4thD2rZeW178J6AxUh4B5Dq3",
|
||||
"Addresses": ["Nse4H3MD4thD2rZeW178J6AxUh4B5Dq3"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.00823145,
|
||||
"n": 2,
|
||||
"lockTime": 2185003,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nsdx6yBvJRtguGduzxkgxEPjkMebW9DC",
|
||||
"Addresses": ["Nsdx6yBvJRtguGduzxkgxEPjkMebW9DC"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.00992713,
|
||||
"n": 3,
|
||||
"lockTime": 2185003,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nse72BM97n88MFyhWDp9iDPEbkw4fqVh",
|
||||
"Addresses": ["Nse72BM97n88MFyhWDp9iDPEbkw4fqVh"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.41158111,
|
||||
"n": 4,
|
||||
"lockTime": 2185003,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nse2c2siYena4BvSn82iprugFJE4gUsJ",
|
||||
"Addresses": ["Nse2c2siYena4BvSn82iprugFJE4gUsJ"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.00823145,
|
||||
"n": 5,
|
||||
"lockTime": 2185003,
|
||||
"scriptPubKey": {
|
||||
"hex": "NsdxARm7PHjiPLgnhMMbXLR2n7R2eded",
|
||||
"Addresses": ["NsdxARm7PHjiPLgnhMMbXLR2n7R2eded"]
|
||||
}
|
||||
},{
|
||||
"value": 0.01243773,
|
||||
"n": 6,
|
||||
"lockTime": 2185003,
|
||||
"scriptPubKey": {
|
||||
"hex": "NsdycgzrFdoev6nmxxMGJvXKTYzo36vj",
|
||||
"Addresses": ["NsdycgzrFdoev6nmxxMGJvXKTYzo36vj"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.01009588,
|
||||
"n": 7,
|
||||
"lockTime": 2185003,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nse5p6NULbecLCe8mZVVzBcSMpaZy2bB",
|
||||
"Addresses": ["Nse5p6NULbecLCe8mZVVzBcSMpaZy2bB"]
|
||||
}
|
||||
},{
|
||||
"value": 0.00823145,
|
||||
"n": 8,
|
||||
"lockTime": 2185003,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nse4e7QYA3Mj9UUqcGpCbYKexwgtSCvz",
|
||||
"Addresses": ["Nse4e7QYA3Mj9UUqcGpCbYKexwgtSCvz"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.00823145,
|
||||
"n": 9,
|
||||
"lockTime": 2185003,
|
||||
"scriptPubKey": {
|
||||
"hex": "NsdyL8HuKgrQehmefzZgKo2aydfRJQSU",
|
||||
"Addresses": ["NsdyL8HuKgrQehmefzZgKo2aydfRJQSU"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.01646950,
|
||||
"n": 10,
|
||||
"lockTime": 2185003,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nse8jHzR4cS1iyiJkewGHKEWqm5B7ZbA",
|
||||
"Addresses": ["Nse8jHzR4cS1iyiJkewGHKEWqm5B7ZbA"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.00823145,
|
||||
"n": 11,
|
||||
"lockTime": 2185003,
|
||||
"scriptPubKey": {
|
||||
"hex": "NsdveDWtHKGCbxVH1AKkMA2oB2VUv134",
|
||||
"Addresses": ["NsdveDWtHKGCbxVH1AKkMA2oB2VUv134"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.01704543,
|
||||
"n": 12,
|
||||
"lockTime": 2185003,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nsdz1QdJb7xNE4bDNg13oXMfa2jtbh36",
|
||||
"Addresses": ["Nsdz1QdJb7xNE4bDNg13oXMfa2jtbh36"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.03778238,
|
||||
"n": 13,
|
||||
"lockTime": 2185003,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nse4rPNt3S3HHZaRhuXRDfVFYSKA4TzR",
|
||||
"Addresses": ["Nse4rPNt3S3HHZaRhuXRDfVFYSKA4TzR"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.41155642,
|
||||
"n": 14,
|
||||
"lockTime": 2185003,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nse84iqmZipKg7Cn6LtV5uQshWwtdRMJ",
|
||||
"Addresses": ["Nse84iqmZipKg7Cn6LtV5uQshWwtdRMJ"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"2184004": {
|
||||
"height": 2184004,
|
||||
"hash": "002099f701eb56f8ff3486d10a4e981df04791bcc47f6749d10fbec0987bf425e8d7",
|
||||
"noTxs": 1,
|
||||
"txDetails": [
|
||||
{
|
||||
"hex": "",
|
||||
"txid": "0020ecd57d05aa56098114d2284cf48ab6edceab2351aa43add3af09b3a956fb458e",
|
||||
"blocktime": 1553317780,
|
||||
"time": 1553317780,
|
||||
"version": 0,
|
||||
"vin": [],
|
||||
"vout": [
|
||||
{
|
||||
"value": 2.39921315,
|
||||
"n": 0,
|
||||
"lockTime": 2185004,
|
||||
"scriptPubKey": {
|
||||
"hex": "NsdwtSWZrG5gwUDoR7VfxxCVzxYsNm3Y",
|
||||
"Addresses": ["NsdwtSWZrG5gwUDoR7VfxxCVzxYsNm3Y"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"2184005": {
|
||||
"height": 2184005,
|
||||
"hash": "00203f9e06e59bda071f7af9fef59a53604e59a6640c52635f6e18101396b616de82",
|
||||
"noTxs": 1,
|
||||
"txDetails": [
|
||||
{
|
||||
"hex": "",
|
||||
"txid": "002011a00b4561f8bb278d8e4744550368b217d61013fcd1eebc61f44a1c6a164765",
|
||||
"blocktime": 1553317790,
|
||||
"time": 1553317790,
|
||||
"version": 0,
|
||||
"vin": [],
|
||||
"vout": [
|
||||
{
|
||||
"value": 0.57503254,
|
||||
"n": 0,
|
||||
"lockTime": 2185005,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nsduq2g457guft4bpj44BVFqVow6kyWi",
|
||||
"Addresses": ["Nsduq2g457guft4bpj44BVFqVow6kyWi"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.04152482,
|
||||
"n": 1,
|
||||
"lockTime": 2185005,
|
||||
"scriptPubKey": {
|
||||
"hex": "NsdvZbRf6pY1ggVfwfBR8DVs4KwUyd7L",
|
||||
"Addresses": ["NsdvZbRf6pY1ggVfwfBR8DVs4KwUyd7L"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.02018105,
|
||||
"n": 2,
|
||||
"lockTime": 2185005,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nse7uEeE7aRrhFyio9ZLa2wpx7aZ6oBf",
|
||||
"Addresses": ["Nse7uEeE7aRrhFyio9ZLa2wpx7aZ6oBf"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.01177228,
|
||||
"n": 3,
|
||||
"lockTime": 2185005,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nse2EYyQXm5EntkGGNU1omz4AZVVXRyP",
|
||||
"Addresses": ["Nse2EYyQXm5EntkGGNU1omz4AZVVXRyP"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.02932482,
|
||||
"n": 4,
|
||||
"lockTime": 2185005,
|
||||
"scriptPubKey": {
|
||||
"hex": "NsdyewTUzSVwyyk55oL4WA7mR3tEt7cb",
|
||||
"Addresses": ["NsdyewTUzSVwyyk55oL4WA7mR3tEt7cb"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.08304964,
|
||||
"n": 5,
|
||||
"lockTime": 2185005,
|
||||
"scriptPubKey": {
|
||||
"hex": "NsdtpVCa8nMtcZoWcvBDDKdmvvvQiqB4",
|
||||
"Addresses": ["NsdtpVCa8nMtcZoWcvBDDKdmvvvQiqB4"]
|
||||
}
|
||||
},{
|
||||
"value": 0.16040263,
|
||||
"n": 6,
|
||||
"lockTime": 2185005,
|
||||
"scriptPubKey": {
|
||||
"hex": "NsdupfMS5mSvxTquqKdAtzzqrbzGrEVX",
|
||||
"Addresses": ["NsdupfMS5mSvxTquqKdAtzzqrbzGrEVX"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.02906321,
|
||||
"n": 7,
|
||||
"lockTime": 2185005,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nsdya19a18roRTWLKNZ4ivsswRLcQq3m",
|
||||
"Addresses": ["Nsdya19a18roRTWLKNZ4ivsswRLcQq3m"]
|
||||
}
|
||||
},{
|
||||
"value": 0.02074533,
|
||||
"n": 8,
|
||||
"lockTime": 2185005,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nse3mU18MXLYTuGGvCXyk3U1Zmf3d129",
|
||||
"Addresses": ["Nse3mU18MXLYTuGGvCXyk3U1Zmf3d129"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.04303632,
|
||||
"n": 9,
|
||||
"lockTime": 2185005,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nse11hHcyLS9pidxowEB6Nupf6rp1y83",
|
||||
"Addresses": ["Nse11hHcyLS9pidxowEB6Nupf6rp1y83"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.24783259,
|
||||
"n": 10,
|
||||
"lockTime": 2185005,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nse3rftMFxSy3vhRsyyK1axLrD1f4gvF",
|
||||
"Addresses": ["Nse3rftMFxSy3vhRsyyK1axLrD1f4gvF"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.03612242,
|
||||
"n": 11,
|
||||
"lockTime": 2185005,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nse8aukpCpwW43qnogQN3WV2SHjbgUeU",
|
||||
"Addresses": ["Nse8aukpCpwW43qnogQN3WV2SHjbgUeU"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.09346822,
|
||||
"n": 12,
|
||||
"lockTime": 2185005,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nse31sLCY9iDtsU1Fb5nwMzHQx1SC2pW",
|
||||
"Addresses": ["Nse31sLCY9iDtsU1Fb5nwMzHQx1SC2pW"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.04232209,
|
||||
"n": 13,
|
||||
"lockTime": 2185005,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nse3heixrSj8Ad2iYymSqZLEASEx46ct",
|
||||
"Addresses": ["Nse3heixrSj8Ad2iYymSqZLEASEx46ct"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.22471157,
|
||||
"n": 14,
|
||||
"lockTime": 2185005,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nse16kQr7z2msh5gerMbPDCNyZJFFgui",
|
||||
"Addresses": ["Nse16kQr7z2msh5gerMbPDCNyZJFFgui"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.02124410,
|
||||
"n": 15,
|
||||
"lockTime": 2185005,
|
||||
"scriptPubKey": {
|
||||
"hex": "NsdwqY7WsekKmzmwdbgkack2VZMo1ehq",
|
||||
"Addresses": ["NsdwqY7WsekKmzmwdbgkack2VZMo1ehq"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.04152482,
|
||||
"n": 16,
|
||||
"lockTime": 2185005,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nsdzsz17JDYQbEmZReG8vqveoXhRXVqL",
|
||||
"Addresses": ["Nsdzsz17JDYQbEmZReG8vqveoXhRXVqL"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.20762411,
|
||||
"n": 17,
|
||||
"lockTime": 2185005,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nse2rtdaQLBPiyoVmJH4BQHJn2VUSHjo",
|
||||
"Addresses": ["Nse2rtdaQLBPiyoVmJH4BQHJn2VUSHjo"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.09136708,
|
||||
"n": 18,
|
||||
"lockTime": 2185005,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nse7W2dvVKFT2UYoavU4vVFeSYG9rjb1",
|
||||
"Addresses": ["Nse7W2dvVKFT2UYoavU4vVFeSYG9rjb1"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.03410019,
|
||||
"n": 19,
|
||||
"lockTime": 2185005,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nse1PbFcq9TJirXsvsjMooZ5RM1A8y7C",
|
||||
"Addresses": ["Nse1PbFcq9TJirXsvsjMooZ5RM1A8y7C"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"2184031": {
|
||||
"height": 2184031,
|
||||
"hash": "00202ef14e5a5505e8b8658af6c15110c1b9cf4f5dead3cfb363bbaa5ffb2275ee6e",
|
||||
"noTxs": 2,
|
||||
"txDetails": [
|
||||
{
|
||||
"hex": "",
|
||||
"txid": "00206d1d22475202bbb81faba6b0e9ba22fe35c7e9d1e542d3284c5002c5a2ef7ca5",
|
||||
"blocktime": 1553318050,
|
||||
"time": 1553318050,
|
||||
"version": 0,
|
||||
"vin": [],
|
||||
"vout": [
|
||||
{
|
||||
"value": 1.14055774,
|
||||
"n": 0,
|
||||
"lockTime": 2185031,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nse5cCyitYFJ7j6ncn5UCst7pc672QEL",
|
||||
"Addresses": ["Nse5cCyitYFJ7j6ncn5UCst7pc672QEL"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.02054451,
|
||||
"n": 1,
|
||||
"lockTime": 2185031,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nse24Dta8UYJtR6jZHSqWtrfdRpnfHYd",
|
||||
"Addresses": ["Nse24Dta8UYJtR6jZHSqWtrfdRpnfHYd"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.35934295,
|
||||
"n": 2,
|
||||
"lockTime": 2185031,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nse5S92swYm231iDVZNTrDurMcGCWBcM",
|
||||
"Addresses": ["Nse5S92swYm231iDVZNTrDurMcGCWBcM"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.38161210,
|
||||
"n": 3,
|
||||
"lockTime": 2185031,
|
||||
"scriptPubKey": {
|
||||
"hex": "NsdviSF29wA91UtGgYuN3HL6iMWLs2hc",
|
||||
"Addresses": ["NsdviSF29wA91UtGgYuN3HL6iMWLs2hc"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.32403928,
|
||||
"n": 4,
|
||||
"lockTime": 2185031,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nse8nchECsiWQ2eyTgzjvH4DewAWqvpR",
|
||||
"Addresses": ["Nse8nchECsiWQ2eyTgzjvH4DewAWqvpR"]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"hex": "",
|
||||
"txid": "0020643416a8b1dad09ee2c1bb2d12aa88eef77a6462a6bd16c39fd3971d67d66919",
|
||||
"blocktime": 1553318050,
|
||||
"time": 1553318045,
|
||||
"version": 0,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "00205462276c5fb51b465c3926dceb27a6a724e63410c146bb027fdd7f7f06bcb56f",
|
||||
"vout": 0,
|
||||
"sequence": 0,
|
||||
"scriptSig": {
|
||||
"hex": ""
|
||||
}
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 5444.999,
|
||||
"n": 0,
|
||||
"lockTime": 0,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nsdwnd4auFisFJKU6iDvBxTdPkeg8qkB",
|
||||
"Addresses": ["Nsdwnd4auFisFJKU6iDvBxTdPkeg8qkB"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"2184213": {
|
||||
"height": 2184213,
|
||||
"hash": "002052a452263860be5bba31a3aace06c1cb524d48cda3d04575c3881b10241cca06",
|
||||
"noTxs": 1,
|
||||
"txDetails": [
|
||||
{
|
||||
"hex": "",
|
||||
"txid": "00204882a15d826d4401aa765510243f455f7f6a05b0e77a9edd420d366cabd98818",
|
||||
"blocktime": 1553319870,
|
||||
"time": 1553319870,
|
||||
"version": 0,
|
||||
"vin": [],
|
||||
"vout": [
|
||||
{
|
||||
"value": 0.04070938,
|
||||
"n": 0,
|
||||
"lockTime": 2185213,
|
||||
"scriptPubKey": {
|
||||
"hex": "NsdwTB38bAwybyKTfUcxZoLGEHSVqpNE",
|
||||
"Addresses": ["NsdwTB38bAwybyKTfUcxZoLGEHSVqpNE"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.20863453,
|
||||
"n": 1,
|
||||
"lockTime": 2185213,
|
||||
"scriptPubKey": {
|
||||
"hex": "Nse8Q7gk2gVGDdHk7rxasedRRjgVc18Q",
|
||||
"Addresses": ["Nse8Q7gk2gVGDdHk7rxasedRRjgVc18Q"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"handleFork": {
|
||||
"syncRanges": [
|
||||
{"lower": 2184210, "upper": 2184213}
|
||||
],
|
||||
"fakeBlocks": {
|
||||
"2184210": {
|
||||
"height": 2184210,
|
||||
"hash": "00205087cb3e6964a51b2be17593660ed4fa460706742d0d97092541d604b7ef4eaa"
|
||||
},
|
||||
"2184211": {
|
||||
"height": 2184211,
|
||||
"hash": "00204ba087c1ab04a45cfef6c61c16f97b72d118dc08fc627d7216b4a38257e906c4"
|
||||
},
|
||||
"2184212": {
|
||||
"height": 2184212,
|
||||
"hash": "002029540932d67260a4dbfb7796ffe2a96bf3abbd5c4898814b8a3057c5392d0cbb"
|
||||
},
|
||||
"2184213": {
|
||||
"height": 2184213,
|
||||
"hash": "00209d3d7e5869f1f32d4bd75587691f426d21e28022e98bd7ef0ca76590c8a6eb76"
|
||||
}
|
||||
},
|
||||
"realBlocks": {
|
||||
"2184210": {
|
||||
"height": 2184210,
|
||||
"hash": "00200fc994cceb3aa75a9adbdbfa5d2b9dbfaddb8ca88f1cd90acc43e0e775abfff3"
|
||||
},
|
||||
"2184211": {
|
||||
"height": 2184211,
|
||||
"hash": "0020a3d90cf5962177a775f47d2edaed94b2519d0cde93711d5e95878c9c6d390b85"
|
||||
},
|
||||
"2184212": {
|
||||
"height": 2184212,
|
||||
"hash": "002027d151738f460437db0ce8aaa058cd5fa9230a4e59e5257cc53e059f05419c3c"
|
||||
},
|
||||
"2184213": {
|
||||
"height": 2184213,
|
||||
"hash": "002052a452263860be5bba31a3aace06c1cb524d48cda3d04575c3881b10241cca06"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -125,5 +125,9 @@
|
|||
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync",
|
||||
"EstimateSmartFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"],
|
||||
"sync": ["ConnectBlocksParallel", "ConnectBlocks", "HandleFork"]
|
||||
},
|
||||
"nuls": {
|
||||
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"],
|
||||
"sync": ["ConnectBlocksParallel", "ConnectBlocks", "HandleFork"]
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue