diff --git a/bchain/coins/blockchain.go b/bchain/coins/blockchain.go index 798aba86..8c908d91 100644 --- a/bchain/coins/blockchain.go +++ b/bchain/coins/blockchain.go @@ -38,6 +38,7 @@ import ( "blockbook/bchain/coins/vipstarcoin" "blockbook/bchain/coins/xzc" "blockbook/bchain/coins/zec" + "blockbook/bchain/coins/omotenashicoin" "blockbook/common" "context" "encoding/json" @@ -107,6 +108,8 @@ func init() { BlockChainFactories["Unobtanium"] = unobtanium.NewUnobtaniumRPC BlockChainFactories["DeepOnion"] = deeponion.NewDeepOnionRPC BlockChainFactories["Bitcore"] = bitcore.NewBitcoreRPC + BlockChainFactories["Omotenashicoin"] = omotenashicoin.NewOmotenashiCoinRPC + BlockChainFactories["Omotenashicoin Testnet"] = omotenashicoin.NewOmotenashiCoinRPC } // GetCoinNameFromConfig gets coin name and coin shortcut from config file diff --git a/bchain/coins/omotenashicoin/omotenashicoinparser.go b/bchain/coins/omotenashicoin/omotenashicoinparser.go new file mode 100644 index 00000000..99cd2ec7 --- /dev/null +++ b/bchain/coins/omotenashicoin/omotenashicoinparser.go @@ -0,0 +1,269 @@ +package omotenashicoin + +import ( + "blockbook/bchain" + "blockbook/bchain/coins/btc" + "blockbook/bchain/coins/utils" + "bytes" + "io" + + "encoding/hex" + "encoding/json" + + "math/big" + + "github.com/martinboehm/btcd/blockchain" + + "github.com/juju/errors" + "github.com/martinboehm/btcd/wire" + "github.com/martinboehm/btcutil/chaincfg" +) + +// magic numbers +const ( + MainnetMagic wire.BitcoinNet = 0xdda5b5d1 + TestnetMagic wire.BitcoinNet = 0x54644363 + + // Zerocoin op codes + OP_ZEROCOINMINT = 0xc1 + OP_ZEROCOINSPEND = 0xc2 +) + +// chain parameters +var ( + MainNetParams chaincfg.Params + TestNetParams chaincfg.Params +) + +func init() { + // mainnet Address encoding magics + MainNetParams = chaincfg.MainNetParams + MainNetParams.Net = MainnetMagic + MainNetParams.PubKeyHashAddrID = []byte{63} // char S + MainNetParams.ScriptHashAddrID = []byte{18} + MainNetParams.PrivateKeyID = []byte{191} + + // testnet Address encoding magics + TestNetParams = chaincfg.TestNet3Params + TestNetParams.Net = TestnetMagic + TestNetParams.PubKeyHashAddrID = []byte{83} + TestNetParams.ScriptHashAddrID = []byte{18} + TestNetParams.PrivateKeyID = []byte{193} +} + +// OmotenashiCoinParser handle +type OmotenashiCoinParser struct { + *btc.BitcoinParser + baseparser *bchain.BaseParser + BitcoinOutputScriptToAddressesFunc btc.OutputScriptToAddressesFunc +} + +// NewOmotenashiCoinParser returns new OmotenashiCoinParser instance +func NewOmotenashiCoinParser(params *chaincfg.Params, c *btc.Configuration) *OmotenashiCoinParser { + p := &OmotenashiCoinParser{ + BitcoinParser: btc.NewBitcoinParser(params, c), + baseparser: &bchain.BaseParser{}, + } + p.BitcoinOutputScriptToAddressesFunc = p.OutputScriptToAddressesFunc + p.OutputScriptToAddressesFunc = p.outputScriptToAddresses + return p +} + +// GetChainParams contains network parameters for the main OmotenashiCoin network +func GetChainParams(chain string) *chaincfg.Params { + if !chaincfg.IsRegistered(&MainNetParams) { + err := chaincfg.Register(&MainNetParams) + if err == nil { + err = chaincfg.Register(&TestNetParams) + } + if err != nil { + panic(err) + } + } + switch chain { + case "test": + return &TestNetParams + default: + return &MainNetParams + } +} + +// ParseBlock parses raw block to our Block struct +func (p *OmotenashiCoinParser) ParseBlock(b []byte) (*bchain.Block, error) { + r := bytes.NewReader(b) + w := wire.MsgBlock{} + h := wire.BlockHeader{} + err := h.Deserialize(r) + if err != nil { + return nil, errors.Annotatef(err, "Deserialize") + } + + if h.Version > 3 && h.Version < 7 { + // Skip past AccumulatorCheckpoint (block version 4, 5 and 6) + r.Seek(32, io.SeekCurrent) + } + + err = utils.DecodeTransactions(r, 0, wire.WitnessEncoding, &w) + if err != nil { + return nil, errors.Annotatef(err, "DecodeTransactions") + } + + txs := make([]bchain.Tx, len(w.Transactions)) + for ti, t := range w.Transactions { + txs[ti] = p.TxFromMsgTx(t, false) + } + + return &bchain.Block{ + BlockHeader: bchain.BlockHeader{ + Size: len(b), + Time: h.Timestamp.Unix(), + }, + Txs: txs, + }, nil +} + +// PackTx packs transaction to byte array using protobuf +func (p *OmotenashiCoinParser) 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 *OmotenashiCoinParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) { + return p.baseparser.UnpackTx(buf) +} + +// ParseTx parses byte array containing transaction and returns Tx struct +func (p *OmotenashiCoinParser) ParseTx(b []byte) (*bchain.Tx, error) { + t := wire.MsgTx{} + r := bytes.NewReader(b) + if err := t.Deserialize(r); err != nil { + return nil, err + } + tx := p.TxFromMsgTx(&t, true) + tx.Hex = hex.EncodeToString(b) + return &tx, nil +} + +// TxFromMsgTx parses tx and adds handling for OP_ZEROCOINSPEND inputs +func (p *OmotenashiCoinParser) TxFromMsgTx(t *wire.MsgTx, parseAddresses bool) bchain.Tx { + vin := make([]bchain.Vin, len(t.TxIn)) + for i, in := range t.TxIn { + + // extra check to not confuse Tx with single OP_ZEROCOINSPEND input as a coinbase Tx + if !isZeroCoinSpendScript(in.SignatureScript) && blockchain.IsCoinBaseTx(t) { + vin[i] = bchain.Vin{ + Coinbase: hex.EncodeToString(in.SignatureScript), + Sequence: in.Sequence, + } + break + } + + s := bchain.ScriptSig{ + Hex: hex.EncodeToString(in.SignatureScript), + // missing: Asm, + } + + txid := in.PreviousOutPoint.Hash.String() + + vin[i] = bchain.Vin{ + Txid: txid, + Vout: in.PreviousOutPoint.Index, + Sequence: in.Sequence, + ScriptSig: s, + } + } + vout := make([]bchain.Vout, len(t.TxOut)) + for i, out := range t.TxOut { + addrs := []string{} + if parseAddresses { + addrs, _, _ = p.OutputScriptToAddressesFunc(out.PkScript) + } + s := bchain.ScriptPubKey{ + Hex: hex.EncodeToString(out.PkScript), + Addresses: addrs, + // missing: Asm, + // missing: Type, + } + var vs big.Int + vs.SetInt64(out.Value) + vout[i] = bchain.Vout{ + ValueSat: vs, + N: uint32(i), + ScriptPubKey: s, + } + } + tx := bchain.Tx{ + Txid: t.TxHash().String(), + Version: t.Version, + LockTime: t.LockTime, + Vin: vin, + Vout: vout, + // skip: BlockHash, + // skip: Confirmations, + // skip: Time, + // skip: Blocktime, + } + return tx +} + +// ParseTxFromJson parses JSON message containing transaction and returns Tx struct +func (p *OmotenashiCoinParser) ParseTxFromJson(msg json.RawMessage) (*bchain.Tx, error) { + var tx bchain.Tx + err := json.Unmarshal(msg, &tx) + if err != nil { + return nil, err + } + + for i := range tx.Vout { + vout := &tx.Vout[i] + // convert vout.JsonValue to big.Int and clear it, it is only temporary value used for unmarshal + vout.ValueSat, err = p.AmountToBigInt(vout.JsonValue) + if err != nil { + return nil, err + } + vout.JsonValue = "" + + if vout.ScriptPubKey.Addresses == nil { + vout.ScriptPubKey.Addresses = []string{} + } + } + + return &tx, nil +} + +// outputScriptToAddresses converts ScriptPubKey to bitcoin addresses +func (p *OmotenashiCoinParser) outputScriptToAddresses(script []byte) ([]string, bool, error) { + if isZeroCoinSpendScript(script) { + return []string{"Zerocoin Spend"}, false, nil + } + if isZeroCoinMintScript(script) { + return []string{"Zerocoin Mint"}, false, nil + } + + rv, s, _ := p.BitcoinOutputScriptToAddressesFunc(script) + return rv, s, nil +} + +func (p *OmotenashiCoinParser) GetAddrDescForUnknownInput(tx *bchain.Tx, input int) bchain.AddressDescriptor { + if len(tx.Vin) > input { + scriptHex := tx.Vin[input].ScriptSig.Hex + + if scriptHex != "" { + script, _ := hex.DecodeString(scriptHex) + return script + } + } + + s := make([]byte, 10) + return s +} + +// Checks if script is OP_ZEROCOINMINT +func isZeroCoinMintScript(signatureScript []byte) bool { + return len(signatureScript) > 1 && signatureScript[0] == OP_ZEROCOINMINT +} + +// Checks if script is OP_ZEROCOINSPEND +func isZeroCoinSpendScript(signatureScript []byte) bool { + return len(signatureScript) >= 100 && signatureScript[0] == OP_ZEROCOINSPEND +} diff --git a/bchain/coins/omotenashicoin/omotenashicoinparser_test.go b/bchain/coins/omotenashicoin/omotenashicoinparser_test.go new file mode 100755 index 00000000..f246c958 --- /dev/null +++ b/bchain/coins/omotenashicoin/omotenashicoinparser_test.go @@ -0,0 +1,402 @@ +// +build unittest + +package omotenashicoin + +import ( + "blockbook/bchain" + "blockbook/bchain/coins/btc" + "bytes" + "encoding/hex" + "fmt" + "io/ioutil" + "math/big" + "os" + "path/filepath" + "reflect" + "testing" + + "github.com/martinboehm/btcutil/chaincfg" +) + +func TestMain(m *testing.M) { + c := m.Run() + chaincfg.ResetParams() + os.Exit(c) +} + +// Test getting the address details from the address hash + +func Test_GetAddrDescFromAddress_Mainnet(t *testing.T) { + type args struct { + address string + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "P2PKH1", + args: args{address: "SSpnQKkfgwKhdsp7FDm3BziRFKfX1zbmWL"}, + want: "76a9143caafb26e97bc26f27543a523f7e2ab8b841d76988ac", + wantErr: false, + }, + { + name: "P2SH1", + args: args{address: "8Hkth4xWKCemL8u88urWXwSqUV4WSX5FDm"}, + want: "a9141d521dcf4983772b3c1e6ef937103ebdfaa1ad7787", + wantErr: false, + }, + } + parser := NewOmotenashiCoinParser(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 Test_GetAddrDescFromAddress_Testnet(t *testing.T) { + type args struct { + address string + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "P2PKH1", + args: args{address: "aRimakbKh2CiG4PSTYcnFGbVzcCZFQr2kd"}, + want: "76a914124a09dbceec6cf2060d72959ba7009649a7783088ac", + wantErr: false, + }, + { + name: "P2SH1", + args: args{address: "8GkZahDbYJ5m6tKosGy4j7uN5pTF21ktHU"}, + want: "a914124a09dbceec6cf2060d72959ba7009649a7783087", + wantErr: false, + }, + } + parser := NewOmotenashiCoinParser(GetChainParams("test"), &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 Test_GetAddressesFromAddrDesc(t *testing.T) { + type args struct { + script string + } + tests := []struct { + name string + args args + want []string + want2 bool + wantErr bool + }{ + { + name: "Normal", + args: args{script: "76a914f1684a035088c20e76ece8e4dd79bdead0e1569a88ac"}, + want: []string{"SjJSpCzWBYacyj6wGmaFoPoj79QSQq8wcw"}, + want2: true, + wantErr: false, + }, + } + + parser := NewOmotenashiCoinParser(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) + } + }) + } +} + +// Test the packing and unpacking of raw transaction data + +var ( + // Block Height 600 + testTx1 bchain.Tx + testTxPacked_testnet_1 = "0a2054af08185cf5c5d312ebd9865b4b224c6120801b209343cfb9dc3332af28a2a5126401000000010000000000000000000000000000000000000000000000000000000000000000ffffffff050258020101ffffffff0100e87648170000002321024a9c0d55966c7a46d8ac15830c6c26555a2b570a3e78c51534ccc8dadc7943c8ac000000001894e38ff105200028d80432140a0a30323538303230313031180028ffffffff0f3a520a05174876e80010001a2321024a9c0d55966c7a46d8ac15830c6c26555a2b570a3e78c51534ccc8dadc7943c8ac2222616e667766545642725934795a4e54543167625a61584e664854377951544856674d4000" + + // Block Height 135001 + testTx2 bchain.Tx + testTxPacked_mainnet_1 = "0a20a2eedb3990bddcace3a5211332e86f70d0195a2a7efaad2de18698172ff9fc6d128f0102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1803590f020445b8075e088104e0b22c0000007969696d70000000000002807c814a000000001976a91487bac515ab40891b58a05c913f908194c9d73bd588ac807584df000000001976a914a1441e207bd13f80b2142026ad39a58b5f47434d88ac0000000018c4f09ef005200028d99e0832360a30303335393066303230343435623830373565303838313034653062323263303030303030373936393639366437303030180028003a470a044a817c8010001a1976a91487bac515ab40891b58a05c913f908194c9d73bd588ac2222535a66667a6a666454486f7a394675684a5444453847704378455762544c433654743a470a04df84758010001a1976a914a1441e207bd13f80b2142026ad39a58b5f47434d88ac222253627a685264475855475245556b70557a67716877615847666f4244447565366b364000" +) + +func init() { + testTx1 = bchain.Tx{ + Hex: "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff050258020101ffffffff0100e87648170000002321024a9c0d55966c7a46d8ac15830c6c26555a2b570a3e78c51534ccc8dadc7943c8ac00000000", + Txid: "54af08185cf5c5d312ebd9865b4b224c6120801b209343cfb9dc3332af28a2a5", + LockTime: 0, + Vin: []bchain.Vin{ + { + Coinbase: "0258020101", + Sequence: 4294967295, + }, + }, + Vout: []bchain.Vout{ + { + ValueSat: *big.NewInt(100000000000), + N: 0, + ScriptPubKey: bchain.ScriptPubKey{ + Hex: "21024a9c0d55966c7a46d8ac15830c6c26555a2b570a3e78c51534ccc8dadc7943c8ac", + Addresses: []string{ + "anfwfTVBrY4yZNTT1gbZaXNfHT7yQTHVgM", + }, + }, + }, + }, + Blocktime: 1579413908, + Time: 1579413908, + } + + //135001 + testTx2 = bchain.Tx{ + Hex: "02000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1803590f020445b8075e088104e0b22c0000007969696d70000000000002807c814a000000001976a91487bac515ab40891b58a05c913f908194c9d73bd588ac807584df000000001976a914a1441e207bd13f80b2142026ad39a58b5f47434d88ac00000000", + Txid: "a2eedb3990bddcace3a5211332e86f70d0195a2a7efaad2de18698172ff9fc6d", + LockTime: 0, + Vin: []bchain.Vin{ + { + Coinbase: "03590f020445b8075e088104e0b22c0000007969696d7000", + Sequence: 0, + }, + }, + Vout: []bchain.Vout{ + { + ValueSat: *big.NewInt(1250000000), + N: 0, + ScriptPubKey: bchain.ScriptPubKey{ + Hex: "76a91487bac515ab40891b58a05c913f908194c9d73bd588ac", + Addresses: []string{ + "SZffzjfdTHoz9FuhJTDE8GpCxEWbTLC6Tt", + }, + }, + }, + { + ValueSat: *big.NewInt(3750000000), + N: 0, + ScriptPubKey: bchain.ScriptPubKey{ + Hex: "76a914a1441e207bd13f80b2142026ad39a58b5f47434d88ac", + Addresses: []string{ + "SbzhRdGXUGREUkpUzgqhwaXGfoBDDue6k6", + }, + }, + }, + }, + Blocktime: 1577564228, + Time: 1577564228, + } +} + +func Test_PackTx(t *testing.T) { + type args struct { + tx bchain.Tx + height uint32 + blockTime int64 + parser *OmotenashiCoinParser + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "mtnstestnet_1", + args: args{ + tx: testTx1, + height: 600, + blockTime: 1579413908, + parser: NewOmotenashiCoinParser(GetChainParams("test"), &btc.Configuration{}), + }, + want: testTxPacked_testnet_1, + wantErr: false, + }, + { + name: "mtnsmainnet_1", + args: args{ + tx: testTx2, + height: 135001, + blockTime: 1577564228, + parser: NewOmotenashiCoinParser(GetChainParams("main"), &btc.Configuration{}), + }, + want: testTxPacked_mainnet_1, + 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 *OmotenashiCoinParser + } + tests := []struct { + name string + args args + want *bchain.Tx + want1 uint32 + wantErr bool + }{ + { + name: "mtns_1", + args: args{ + packedTx: testTxPacked_testnet_1, + parser: NewOmotenashiCoinParser(GetChainParams("test"), &btc.Configuration{}), + }, + want: &testTx1, + want1: 600, + wantErr: false, + }, + { + name: "mtns_2", + args: args{ + packedTx: testTxPacked_mainnet_1, + parser: NewOmotenashiCoinParser(GetChainParams("main"), &btc.Configuration{}), + }, + want: &testTx2, + want1: 135001, + 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) + } + }) + } +} + +// Block test - looks for size, time, and transaction hashes + +type testBlock struct { + size int + time int64 + tx []string +} + +var testParseBlockTxs = map[int]testBlock{ + 600: { + size: 181, + time: 1579413908, + tx: []string{ + "54af08185cf5c5d312ebd9865b4b224c6120801b209343cfb9dc3332af28a2a5", + }, + }, + 135001: { + size: 224, + time: 1577564228, + tx: []string{ + "a2eedb3990bddcace3a5211332e86f70d0195a2a7efaad2de18698172ff9fc6d", + }, + }, +} + +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 := NewOmotenashiCoinParser(GetChainParams("main"), &btc.Configuration{}) + + for height, tb := range testParseBlockTxs { + b := helperLoadBlock(t, height) + + blk, err := p.ParseBlock(b) + if err != nil { + t.Fatal(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.tx) { + t.Errorf("ParseBlock() number of transactions: got %d, want %d", len(blk.Txs), len(tb.tx)) + } + + for ti, tx := range tb.tx { + if blk.Txs[ti].Txid != tx { + t.Errorf("ParseBlock() transaction %d: got %s, want %s", ti, blk.Txs[ti].Txid, tx) + } + } + } +} diff --git a/bchain/coins/omotenashicoin/omotenashicoinrpc.go b/bchain/coins/omotenashicoin/omotenashicoinrpc.go new file mode 100644 index 00000000..5e80d2fa --- /dev/null +++ b/bchain/coins/omotenashicoin/omotenashicoinrpc.go @@ -0,0 +1,59 @@ +package omotenashicoin + +import ( + "blockbook/bchain" + "blockbook/bchain/coins/btc" + "encoding/json" + + "github.com/golang/glog" +) + +// OmotenashiCoinRPC is an interface to JSON-RPC bitcoind service. +type OmotenashiCoinRPC struct { + *btc.BitcoinRPC +} + +// NewOmotenashiCoinRPC returns new OmotenashiCoinRPC instance. +func NewOmotenashiCoinRPC(config json.RawMessage, pushHandler func(bchain.NotificationType)) (bchain.BlockChain, error) { + b, err := btc.NewBitcoinRPC(config, pushHandler) + if err != nil { + return nil, err + } + + s := &OmotenashiCoinRPC{ + b.(*btc.BitcoinRPC), + } + s.RPCMarshaler = btc.JSONMarshalerV1{} + s.ChainConfig.SupportsEstimateFee = true + s.ChainConfig.SupportsEstimateSmartFee = false + + return s, nil +} + +// Initialize initializes OmotenashiCoinRPC instance. +func (b *OmotenashiCoinRPC) Initialize() error { + ci, err := b.GetChainInfo() + if err != nil { + return err + } + chainName := ci.Chain + + glog.Info("Chain name ", chainName) + params := GetChainParams(chainName) + + // always create parser + b.Parser = NewOmotenashiCoinParser(params, b.ChainConfig) + + // parameters for getInfo request + if params.Net == MainnetMagic { + b.Testnet = false + b.Network = "livenet" + } else { + b.Testnet = true + b.Network = "testnet" + } + + glog.Info("rpc: block chain ", params.Name) + + return nil +} diff --git a/bchain/coins/omotenashicoin/testdata/block_dump.135001 b/bchain/coins/omotenashicoin/testdata/block_dump.135001 new file mode 100644 index 00000000..c6f92a6d --- /dev/null +++ b/bchain/coins/omotenashicoin/testdata/block_dump.135001 @@ -0,0 +1 @@ +030000005e6e9be02bcbbb6dfe8729313ac2b0e544b8f8743042694054580000000000006dfcf92f179886e12dadfa7e2a5a19d0706fe8321321a5e3acdcbd9039dbeea244b8075e6299151bf5d3bbe80102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1803590f020445b8075e088104e0b22c0000007969696d70000000000002807c814a000000001976a91487bac515ab40891b58a05c913f908194c9d73bd588ac807584df000000001976a914a1441e207bd13f80b2142026ad39a58b5f47434d88ac00000000 diff --git a/bchain/coins/omotenashicoin/testdata/block_dump.600 b/bchain/coins/omotenashicoin/testdata/block_dump.600 new file mode 100644 index 00000000..26273b67 --- /dev/null +++ b/bchain/coins/omotenashicoin/testdata/block_dump.600 @@ -0,0 +1 @@ +03000000a044af4b7d44e7c098d427de958ea3c0acd14b082d3442ab26a057581a000000a5a228af3233dcb9cf4393201b8020614c224b5b86d9eb12d3c5f55c1808af5494f1235e2fcb221dba324d000101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff050258020101ffffffff0100e87648170000002321024a9c0d55966c7a46d8ac15830c6c26555a2b570a3e78c51534ccc8dadc7943c8ac00000000 diff --git a/configs/coins/omotenashicoin.json b/configs/coins/omotenashicoin.json new file mode 100644 index 00000000..4d3c9b3c --- /dev/null +++ b/configs/coins/omotenashicoin.json @@ -0,0 +1,70 @@ +{ + "coin": { + "name": "Omotenashicoin", + "shortcut": "MTNS", + "label": "Omotenashicoin", + "alias": "omotenashicoin" + }, + "ports": { + "blockbook_internal": 9094, + "blockbook_public": 9194, + "backend_rpc": 8094, + "backend_message_queue": 38394 + }, + "ipc": { + "rpc_url_template": "http://127.0.0.1:{{.Ports.BackendRPC}}", + "rpc_user": "rpc", + "rpc_pass": "mtnsrpc", + "rpc_timeout": 25, + "message_queue_binding_template": "tcp://127.0.0.1:{{.Ports.BackendMessageQueue}}" + }, + "backend": { + "package_name": "backend-mtns", + "package_revision": "satoshilabs-1", + "system_user": "mtns", + "version": "1.7.3", + "binary_url": "https://github.com/omotenashicoin-project/OmotenashiCoin-HDwalletbinaries/raw/master/stable/omotenashicoin-x86_64-linux-gnu.tar.gz", + "verification_type": "", + "verification_source": "", + "extract_command": "tar -C backend --strip 1 -xf", + "exclude_files": [ + "bin/omotenashicoin-qt" + ], + "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/omotenashicoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/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": { + "whitelist": "127.0.0.1" + } + }, + "blockbook": { + "package_name": "blockbook-mtns", + "system_user": "blockbook-mtns", + "internal_binding_template": ":{{.Ports.BlockbookInternal}}", + "public_binding_template": ":{{.Ports.BlockbookPublic}}", + "explorer_url": "", + "additional_params": "", + "block_chain": { + "parse": true, + "mempool_workers": 8, + "mempool_sub_workers": 2, + "block_addresses_to_keep": 300, + "xpub_magic": 61052245, + "slip44": 341, + "additional_params": { + "fiat_rates": "coingecko", + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"omotenashicoin\", \"periodSeconds\": 60}" + } + } + }, + "meta": { + "package_maintainer": "omotenashicoin dev", + "package_maintainer_email": "git@omotenashicoin.site" + } +} diff --git a/configs/coins/omotenashicoin_testnet.json b/configs/coins/omotenashicoin_testnet.json new file mode 100644 index 00000000..bd14828f --- /dev/null +++ b/configs/coins/omotenashicoin_testnet.json @@ -0,0 +1,70 @@ +{ + "coin": { + "name": "Omotenashicoin Testnet", + "shortcut": "tMTNS", + "label": "Omotenashicoin Testnet", + "alias": "omotenashicoin_testnet" + }, + "ports": { + "blockbook_internal": 19089, + "blockbook_public": 19189, + "backend_rpc": 18089, + "backend_message_queue": 48389 + }, + "ipc": { + "rpc_url_template": "http://127.0.0.1:{{.Ports.BackendRPC}}", + "rpc_user": "rpc", + "rpc_pass": "mtnsrpc", + "rpc_timeout": 25, + "message_queue_binding_template": "tcp://127.0.0.1:{{.Ports.BackendMessageQueue}}" + }, + "backend": { + "package_name": "backend-mtns-testnet", + "package_revision": "satoshilabs-1", + "system_user": "mtns", + "version": "1.7.3", + "binary_url": "https://github.com/omotenashicoin-project/OmotenashiCoin-HDwalletbinaries/raw/master/stable/omotenashicoin-x86_64-linux-gnu.tar.gz", + "verification_type": "", + "verification_source": "", + "extract_command": "tar -C backend --strip 1 -xf", + "exclude_files": [ + "bin/omotenashicoin-qt" + ], + "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/omotenashicoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", + "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/testnet4/*.log", + "postinst_script_template": "", + "service_type": "forking", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": false, + "server_config_file": "bitcoin_like.conf", + "client_config_file": "bitcoin_like_client.conf", + "additional_params": { + "whitelist": "127.0.0.1" + } + }, + "blockbook": { + "package_name": "blockbook-mtns-testnet", + "system_user": "blockbook-mtns", + "internal_binding_template": ":{{.Ports.BlockbookInternal}}", + "public_binding_template": ":{{.Ports.BlockbookPublic}}", + "explorer_url": "", + "additional_params": "", + "block_chain": { + "parse": true, + "mempool_workers": 8, + "mempool_sub_workers": 2, + "block_addresses_to_keep": 300, + "xpub_magic": 70544129, + "slip44": 1, + "additional_params": { + "fiat_rates": "coingecko", + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"omotenashicoin\", \"periodSeconds\": 60}" + } + } + }, + "meta": { + "package_maintainer": "omotenashicoin dev", + "package_maintainer_email": "git@omotenashicoin.site" + } +} diff --git a/docs/ports.md b/docs/ports.md index c60b0f9a..5f153141 100644 --- a/docs/ports.md +++ b/docs/ports.md @@ -41,6 +41,7 @@ | CPUchain | 9090 | 9190 | 8090 | 38390 | | DeepOnion | 9091 | 9191 | 8091 | 38391 | | Unobtanium | 9092 | 9192 | 65535 | 38392 | +| Omotenashicoin | 9094 | 9194 | 8094 | 38394 | | Bitcoin Testnet | 19030 | 19130 | 18030 | 48330 | | Bitcoin Cash Testnet | 19031 | 19131 | 18031 | 48331 | | Zcash Testnet | 19032 | 19132 | 18032 | 48332 | @@ -55,5 +56,6 @@ | Decred Testnet | 19061 | 19161 | 18061 | 48361 | | Flo Testnet | 19066 | 19166 | 18066 | 48366 | | Qtum Testnet | 19088 | 19188 | 18088 | 48388 | +| OmotenashicoinTestnet| 19089 | 19189 | 18089 | 48389 | > NOTE: This document is generated from coin definitions in `configs/coins`. diff --git a/tests/rpc/testdata/omotenashicoin.json b/tests/rpc/testdata/omotenashicoin.json new file mode 100644 index 00000000..87e73a0b --- /dev/null +++ b/tests/rpc/testdata/omotenashicoin.json @@ -0,0 +1,56 @@ +{ + "blockHeight": 65856, + "blockHash": "00000000000ef792eee81acc4c008c6d76d26780249ca04ce06e1f75a7589cc0", + "blockTime": 1571024221, + "blockTxs": [ + "88cc29d05f954606eb674d863cc4c574a74bf85fc18a305b22a7c7877c356ce7", + "6f9006feb50be3d0c42d98b7c88308adae2839e45f52763826a368211935166d" + ], + "txDetails": { + "6f9006feb50be3d0c42d98b7c88308adae2839e45f52763826a368211935166d": { + "hex": "01000000020f704d32f71c5fadb17036403336bb6f15563d434944974753e8c43d7880c004000000004847304402203749e06f3d3ff0cce5e5c91cdb3876cf32e4dcc12feccd4ec816cc86db6260090220546d03aa361b87c087cac86273b628dd3e1bffd93ea8b9e99d9506a627a35bde01fffffffffd215820344e8d012e7e08289310318c2caed57e36db31b711f3a722a5cfa11100000000484730440220606707fc7830306a889bb5fa549bf8e26b71b793d4d22b9ae76d37b95ace3fe1022072016086b81746d1b2a0caf12158d5f437b05ffd8b0610efe07a5672c315655701ffffffff0100c2eb0b000000001976a914f61ce564ca563d708e971db2ad24234146c377fd88ac00000000", + "txid": "6f9006feb50be3d0c42d98b7c88308adae2839e45f52763826a368211935166d", + "version": 1, + "locktime": 0, + "vin": [ + { + "txid": "04c080783dc4e85347974449433d56156fbb3633403670b1ad5f1cf7324d700f", + "vout": 0, + "scriptSig": { + "asm": "304402203749e06f3d3ff0cce5e5c91cdb3876cf32e4dcc12feccd4ec816cc86db6260090220546d03aa361b87c087cac86273b628dd3e1bffd93ea8b9e99d9506a627a35bde01", + "hex": "47304402203749e06f3d3ff0cce5e5c91cdb3876cf32e4dcc12feccd4ec816cc86db6260090220546d03aa361b87c087cac86273b628dd3e1bffd93ea8b9e99d9506a627a35bde01" + }, + "sequence": 4294967295 + }, + { + "txid": "11a1cfa522a7f311b731db367ed5ae2c8c31109328087e2e018d4e34205821fd", + "vout": 0, + "scriptSig": { + "asm": "30440220606707fc7830306a889bb5fa549bf8e26b71b793d4d22b9ae76d37b95ace3fe1022072016086b81746d1b2a0caf12158d5f437b05ffd8b0610efe07a5672c315655701", + "hex": "4730440220606707fc7830306a889bb5fa549bf8e26b71b793d4d22b9ae76d37b95ace3fe1022072016086b81746d1b2a0caf12158d5f437b05ffd8b0610efe07a5672c315655701" + }, + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": 2, + "n": 0, + "scriptPubKey": { + "asm": "OP_DUP OP_HASH160 f61ce564ca563d708e971db2ad24234146c377fd OP_EQUALVERIFY OP_CHECKSIG", + "hex": "76a914f61ce564ca563d708e971db2ad24234146c377fd88ac", + "reqSigs": 1, + "type": "pubkeyhash", + "addresses": [ + "SjjKsmPj73iTWHUzKiXfdR46qjLdKwawkh" + ] + } + } + ], + "blockhash": "00000000000ef792eee81acc4c008c6d76d26780249ca04ce06e1f75a7589cc0", + "confirmations": 0, + "time": 1571024221, + "blocktime": 1571024221 + } + } +} diff --git a/tests/rpc/testdata/omotenashicoin_testnet.json b/tests/rpc/testdata/omotenashicoin_testnet.json new file mode 100644 index 00000000..8a75d333 --- /dev/null +++ b/tests/rpc/testdata/omotenashicoin_testnet.json @@ -0,0 +1,69 @@ +{ + "blockHeight": 1234, + "blockHash": "0000000b65c99d7864c506cdfaf0daedad8d651c9fa8f72479c771ae76804839", + "blockTime": 1579761404, + "blockTxs": [ + "2c1c0368fb06443c61dca97ed74107555854bdf9b5a03d89e1958d657e1e32cb", + "f072e70a187bdc321399644612ee7076f82e541a4905b458fcb1d1e18f78287b" + ], + "txDetails": { + "f072e70a187bdc321399644612ee7076f82e541a4905b458fcb1d1e18f78287b": { + "hex": "01000000025ca81ee15d6ffe0343dfe93c027c4663ef0ea939b79f56db9734840ac1adf7f40000000049483045022100b036866dcffe2a0a503f6c8f1e8f2e264f549c2980598d69cf2364ef166a0c3102203b8050fe7b1b04a4469b5012c03240fb840f894c82e653903d405b4ff746ecb801ffffffffcaa1a7123f477980412d97f350e3f20d53cd26c48c3dceefc0acbc7af0949f0100000000484730440220449315f1aafa884f4c49138386d5bc8f9b2f9641b13a66fdbfc106986014c5580220219f9a08d62d39ff8adc893249fc479e9ccd9aa2902f7b2050e62381621f076e01ffffffff020cdc7648170000001976a9143901f589f1c60a361b4d7f92f2296259ced577bb88ac00e87648170000001976a91428904c18fb5423f5c3995359b27067d92ff85af388ac00000000", + "txid": "f072e70a187bdc321399644612ee7076f82e541a4905b458fcb1d1e18f78287b", + "version": 1, + "locktime": 0, + "vin": [ + { + "txid": "f4f7adc10a843497db569fb739a90eef63467c023ce9df4303fe6f5de11ea85c", + "vout": 0, + "scriptSig": { + "asm": "3045022100b036866dcffe2a0a503f6c8f1e8f2e264f549c2980598d69cf2364ef166a0c3102203b8050fe7b1b04a4469b5012c03240fb840f894c82e653903d405b4ff746ecb801", + "hex": "483045022100b036866dcffe2a0a503f6c8f1e8f2e264f549c2980598d69cf2364ef166a0c3102203b8050fe7b1b04a4469b5012c03240fb840f894c82e653903d405b4ff746ecb801" + }, + "sequence": 4294967295 + }, + { + "txid": "019f94f07abcacc0efce3d8cc426cd530df2e350f3972d418079473f12a7a1ca", + "vout": 0, + "scriptSig": { + "asm": "30440220449315f1aafa884f4c49138386d5bc8f9b2f9641b13a66fdbfc106986014c5580220219f9a08d62d39ff8adc893249fc479e9ccd9aa2902f7b2050e62381621f076e01", + "hex": "4730440220449315f1aafa884f4c49138386d5bc8f9b2f9641b13a66fdbfc106986014c5580220219f9a08d62d39ff8adc893249fc479e9ccd9aa2902f7b2050e62381621f076e01" + }, + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": 999.9999694, + "n": 0, + "scriptPubKey": { + "asm": "OP_DUP OP_HASH160 3901f589f1c60a361b4d7f92f2296259ced577bb OP_EQUALVERIFY OP_CHECKSIG", + "hex": "76a9143901f589f1c60a361b4d7f92f2296259ced577bb88ac", + "reqSigs": 1, + "type": "pubkeyhash", + "addresses": [ + "aVFVb55LQ9rSjHC19KYPUj3eGyQkXYzcRr" + ] + } + }, + { + "value": 1000, + "n": 1, + "scriptPubKey": { + "asm": "OP_DUP OP_HASH160 28904c18fb5423f5c3995359b27067d92ff85af3 OP_EQUALVERIFY OP_CHECKSIG", + "hex": "76a91428904c18fb5423f5c3995359b27067d92ff85af388ac", + "reqSigs": 1, + "type": "pubkeyhash", + "addresses": [ + "aTkYcdqUeREo8g9ydvpDPACzzcChy3zpiw" + ] + } + } + ], + "blockhash": "0000000b65c99d7864c506cdfaf0daedad8d651c9fa8f72479c771ae76804839", + "confirmations": 0, + "time": 1579761404, + "blocktime": 1579761404 + } + } +} diff --git a/tests/sync/testdata/omotenashicoin.json b/tests/sync/testdata/omotenashicoin.json new file mode 100644 index 00000000..4fd11c3f --- /dev/null +++ b/tests/sync/testdata/omotenashicoin.json @@ -0,0 +1,43 @@ +{ + "connectBlocks": { + "syncRanges": [ + {"lower": 135001, "upper": 135001} + ], + "blocks": { + "135001": { + "height": 135001, + "hash": "000000000007cedacd4bddd119dd9c4a4e3780bf4fc4c02800983f6a87a8574f", + "noTxs": 1, + "txDetails": [ + { + "hex": "02000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1803590f020445b8075e088104e0b22c0000007969696d70000000000002807c814a000000001976a91487bac515ab40891b58a05c913f908194c9d73bd588ac807584df000000001976a914a1441e207bd13f80b2142026ad39a58b5f47434d88ac00000000", + "txid": "a2eedb3990bddcace3a5211332e86f70d0195a2a7efaad2de18698172ff9fc6d", + "version": 2, + "vin": [ + { + "coinbase": "03590f020445b8075e088104e0b22c0000007969696d7000", + "sequence": 0 + } + ], + "vout": [ + { + "value": 12.5, + "n": 0, + "scriptPubKey": { + "hex": "76a91487bac515ab40891b58a05c913f908194c9d73bd588ac" + } + }, + { + "value": 37.5, + "n": 1, + "scriptPubKey": { + "hex": "76a914a1441e207bd13f80b2142026ad39a58b5f47434d88ac" + } + } + ] + } + ] + } + } + } +} diff --git a/tests/sync/testdata/omotenashicoin_testnet.json b/tests/sync/testdata/omotenashicoin_testnet.json new file mode 100644 index 00000000..9e22cb43 --- /dev/null +++ b/tests/sync/testdata/omotenashicoin_testnet.json @@ -0,0 +1,36 @@ +{ + "connectBlocks": { + "syncRanges": [ + {"lower": 600, "upper": 600} + ], + "blocks": { + "600": { + "height": 600, + "hash": "0000001d9724d92f0db8da3ce321b13bd780ae53251afc636ad149d0a64a8b0d", + "noTxs": 1, + "txDetails": [ + { + "hex": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff050258020101ffffffff0100e87648170000002321024a9c0d55966c7a46d8ac15830c6c26555a2b570a3e78c51534ccc8dadc7943c8ac00000000", + "txid": "54af08185cf5c5d312ebd9865b4b224c6120801b209343cfb9dc3332af28a2a5", + "version": 1, + "vin": [ + { + "coinbase": "0258020101", + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": 1000, + "n": 0, + "scriptPubKey": { + "hex": "21024a9c0d55966c7a46d8ac15830c6c26555a2b570a3e78c51534ccc8dadc7943c8ac" + } + } + ] + } + ] + } + } + } +} diff --git a/tests/tests.json b/tests/tests.json index 213a1aa3..095b2d59 100644 --- a/tests/tests.json +++ b/tests/tests.json @@ -200,5 +200,15 @@ "rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", "GetBestBlockHash", "GetBestBlockHeight"], "sync": ["ConnectBlocksParallel", "ConnectBlocks"] + }, + "omotenashicoin": { + "rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", + "EstimateSmartFee", "EstimateFee", "GetBestBlockHash", "GetBestBlockHeight"], + "sync": ["ConnectBlocksParallel", "ConnectBlocks"] + }, + "omotenashicoin_testnet": { + "rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", + "EstimateSmartFee", "EstimateFee", "GetBestBlockHash", "GetBestBlockHeight"], + "sync": ["ConnectBlocksParallel", "ConnectBlocks"] } }