Add $PAC (#474)
* Add $PAC * Refactor and improve masternode notifications * Update DASH/$PAC RPC documentation These RPC commands were documented: masternode.announce.broadcast masternode.subscribe masternode.list masternode.info
This commit is contained in:
parent
2559751ead
commit
7baf6cf68d
@ -1055,3 +1055,106 @@ Identify the client to the server and negotiate the protocol version.
|
||||
|
||||
["ElectrumX 1.2.1", "1.2"]
|
||||
"ElectrumX 1.2.1"
|
||||
|
||||
masternode.announce.broadcast
|
||||
--------------
|
||||
|
||||
Pass through the masternode announce message to be broadcast by the daemon.
|
||||
|
||||
Whenever a masternode comes online or a client is syncing, they will send this message which describes the masternode entry and how to validate messages from it.
|
||||
|
||||
**Signature**
|
||||
|
||||
.. function:: masternode.announce.broadcast(signmnb)
|
||||
|
||||
* *signmnb*
|
||||
|
||||
Signed masternode broadcast message.
|
||||
|
||||
**Result**
|
||||
|
||||
True if the message was broadcasted succesfully otherwise False.
|
||||
|
||||
masternode.subscribe
|
||||
--------------
|
||||
|
||||
Returns the status of masternode.
|
||||
|
||||
**Signature**
|
||||
|
||||
.. function:: masternode.subscribe(collateral)
|
||||
|
||||
* *collateral*
|
||||
|
||||
A masternode collateral is a transaction with a specific amount of coins, it's also known as a masternode identifier.
|
||||
|
||||
i.e. for DASH the required amount is 1,000 DASH or for $PAC is 500,000 $PAC.
|
||||
|
||||
**Result**
|
||||
|
||||
As this is a subcription, the client will receive a notification when the masternode status changes.
|
||||
|
||||
The status depends on the server the masternode is hosted, the internet connection, the offline time and even the collateral amount, so this subscription notice these changes to the user.
|
||||
|
||||
**Example Results**::
|
||||
|
||||
{'method': 'masternode.subscribe', u'jsonrpc': u'2.0', u'result': u'ENABLED', 'params': ['8c59133e714797650cf69043d05e409bbf45670eed7c4e4a386e52c46f1b5e24-0'], u'id': 19}
|
||||
|
||||
masternode.list
|
||||
--------------
|
||||
|
||||
Returns the list of masternodes.
|
||||
|
||||
**Signature**
|
||||
|
||||
.. function:: masternode.list(payees)
|
||||
|
||||
* *payees*
|
||||
|
||||
An array of masternode payee addresses.
|
||||
|
||||
**Result**
|
||||
|
||||
An array with the masternodes information.
|
||||
|
||||
**Examples**::
|
||||
|
||||
masternode.list("['PDFHmjKLvSGdnWgDJSJX49Rrh0SJtRANcE',
|
||||
'PDFHmjKLvSGdnWgDJSJX49Rrh0SJtRANcF']")
|
||||
|
||||
**Example Results**::
|
||||
|
||||
[
|
||||
{
|
||||
"vin": "9d298c00dae8b491d6801f50cab2e0037852cb556c5619ddb07c50421x9a31ab",
|
||||
"status": "ENABLED",
|
||||
"protocol": 70213,
|
||||
"payee": "PDFHmjKLvSGdnWgDJSJX49Rrh0SJtRANcE",
|
||||
"lastseen": "2018-04-01 12:34",
|
||||
"activeseconds": 1258000,
|
||||
"lastpaidtime": "2018-03-10 12:29",
|
||||
"lastpaidblock": 1234,
|
||||
"ip": "1.0.0.1",
|
||||
"paymentposition": 184,
|
||||
"inselection": true,
|
||||
"balance": 510350
|
||||
},
|
||||
{
|
||||
"vin": "9d298c00dae8b491d6801f50cab2e0037852cb556c5619ddb07c50421x9a31ac",
|
||||
"status": "ENABLED",
|
||||
"protocol": 70213,
|
||||
"payee": "PDFHmjKLvSGdnWgDJSJX49Rrh0SJtRANcF",
|
||||
"lastseen": "2018-04-01 12:34",
|
||||
"activeseconds": 1258000,
|
||||
"lastpaidtime": "2018-03-15 05:29",
|
||||
"lastpaidblock": 1234,
|
||||
"ip": "1.0.0.2",
|
||||
"paymentposition": 3333,
|
||||
"inselection": false,
|
||||
"balance": 520700
|
||||
},
|
||||
...,
|
||||
...,
|
||||
...,
|
||||
...
|
||||
]
|
||||
45
lib/coins.py
45
lib/coins.py
@ -1722,3 +1722,48 @@ class Xuez(Coin):
|
||||
'nonce': nonce,
|
||||
'nAccumulatorCheckpoint': hash_to_str(header[80:112]),
|
||||
}
|
||||
|
||||
class Pac(Coin):
|
||||
NAME = "PAC"
|
||||
SHORTNAME = "PAC"
|
||||
NET = "mainnet"
|
||||
XPUB_VERBYTES = bytes.fromhex("0488B21E")
|
||||
XPRV_VERBYTES = bytes.fromhex("0488ADE4")
|
||||
GENESIS_HASH = ('00000354655ff039a51273fe61d3b493'
|
||||
'bd2897fe6c16f732dbc4ae19f04b789e')
|
||||
P2PKH_VERBYTE = bytes.fromhex("37")
|
||||
P2SH_VERBYTES = [bytes.fromhex("0A")]
|
||||
WIF_BYTE = bytes.fromhex("CC")
|
||||
TX_COUNT_HEIGHT = 14939
|
||||
TX_COUNT = 23708
|
||||
TX_PER_BLOCK = 2
|
||||
RPC_PORT = 7111
|
||||
PEERS = [
|
||||
'electrum.paccoin.io s t',
|
||||
'electro-pac.paccoin.io s t'
|
||||
]
|
||||
SESSIONCLS = DashElectrumX
|
||||
DAEMON = daemon.DashDaemon
|
||||
ESTIMATE_FEE = 0.00001
|
||||
RELAY_FEE = 0.00001
|
||||
|
||||
@classmethod
|
||||
def header_hash(cls, header):
|
||||
'''Given a header return the hash.'''
|
||||
import x11_hash
|
||||
return x11_hash.getPoWHash(header)
|
||||
|
||||
class PacTestnet(Pac):
|
||||
SHORTNAME = "tPAC"
|
||||
NET = "testnet"
|
||||
XPUB_VERBYTES = bytes.fromhex("043587CF")
|
||||
XPRV_VERBYTES = bytes.fromhex("04358394")
|
||||
GENESIS_HASH = ('00000da63bd9478b655ef6bf1bf76cd9'
|
||||
'af05202ab68643f9091e049b2b5280ed')
|
||||
P2PKH_VERBYTE = bytes.fromhex("78")
|
||||
P2SH_VERBYTES = [bytes.fromhex("0E")]
|
||||
WIF_BYTE = bytes.fromhex("EF")
|
||||
TX_COUNT_HEIGHT = 16275
|
||||
TX_COUNT = 16275
|
||||
TX_PER_BLOCK = 1
|
||||
RPC_PORT = 17111
|
||||
|
||||
@ -83,6 +83,8 @@ class Controller(ServerBase):
|
||||
self.history_cache = pylru.lrucache(256)
|
||||
self.header_cache = pylru.lrucache(8)
|
||||
self.cache_height = 0
|
||||
self.cache_mn_height = 0
|
||||
self.mn_cache = pylru.lrucache(256)
|
||||
env.max_send = max(350000, env.max_send)
|
||||
# Set up the RPC request handlers
|
||||
cmds = ('add_peer daemon_url disconnect getinfo groups log peers reorg '
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
import codecs
|
||||
import itertools
|
||||
import time
|
||||
import datetime
|
||||
from functools import partial
|
||||
|
||||
from aiorpcx import ServerSession, JSONRPCAutoDetect, RPCError
|
||||
@ -495,22 +496,28 @@ class DashElectrumX(ElectrumX):
|
||||
'masternode.announce.broadcast':
|
||||
self.masternode_announce_broadcast,
|
||||
'masternode.subscribe': self.masternode_subscribe,
|
||||
'masternode.list': self.masternode_list
|
||||
})
|
||||
|
||||
async def notify_masternodes_async(self):
|
||||
for masternode in self.mns:
|
||||
status = await self.daemon.masternode_list(['status', masternode])
|
||||
self.send_notification('masternode.subscribe',
|
||||
[masternode, status.get(masternode)])
|
||||
|
||||
def notify(self, height, touched):
|
||||
'''Notify the client about changes in masternode list.'''
|
||||
result = super().notify(height, touched)
|
||||
|
||||
for masternode in self.mns:
|
||||
status = self.daemon.masternode_list(['status', masternode])
|
||||
self.send_notification('masternode.subscribe',
|
||||
[masternode, status.get(masternode)])
|
||||
self.controller.create_task(self.notify_masternodes_async())
|
||||
return result
|
||||
|
||||
|
||||
# Masternode command handlers
|
||||
async def masternode_announce_broadcast(self, signmnb):
|
||||
'''Pass through the masternode announce message to be broadcast
|
||||
by the daemon.'''
|
||||
by the daemon.
|
||||
|
||||
signmnb: signed masternode broadcast message.'''
|
||||
try:
|
||||
return await self.daemon.masternode_broadcast(['relay', signmnb])
|
||||
except DaemonError as e:
|
||||
@ -520,10 +527,103 @@ class DashElectrumX(ElectrumX):
|
||||
raise RPCError(BAD_REQUEST, 'the masternode broadcast was '
|
||||
f'rejected.\n\n{message}\n[{signmnb}]')
|
||||
|
||||
async def masternode_subscribe(self, vin):
|
||||
'''Returns the status of masternode.'''
|
||||
result = await self.daemon.masternode_list(['status', vin])
|
||||
async def masternode_subscribe(self, collateral):
|
||||
'''Returns the status of masternode.
|
||||
|
||||
collateral: masternode collateral.
|
||||
'''
|
||||
result = await self.daemon.masternode_list(['status', collateral])
|
||||
if result is not None:
|
||||
self.mns.add(vin)
|
||||
return result.get(vin)
|
||||
self.mns.add(collateral)
|
||||
return result.get(collateral)
|
||||
return None
|
||||
|
||||
async def masternode_list(self, payees):
|
||||
'''
|
||||
Returns the list of masternodes.
|
||||
|
||||
payees: a list of masternode payee addresses.
|
||||
'''
|
||||
result = []
|
||||
|
||||
def get_masternode_payment_queue(mns):
|
||||
'''
|
||||
Returns the calculated position in the payment queue for all the valid
|
||||
masterernodes in the given mns list.
|
||||
|
||||
mns: a list of masternodes information.
|
||||
'''
|
||||
now = int(datetime.datetime.utcnow().strftime("%s"))
|
||||
mn_queue=[]
|
||||
|
||||
# Only ENABLED masternodes are considered for the list.
|
||||
for line in mns:
|
||||
mnstat = mns[line].split()
|
||||
if mnstat[0] == 'ENABLED':
|
||||
# if last paid time == 0
|
||||
if int(mnstat[5]) == 0:
|
||||
# use active seconds
|
||||
mnstat.append(int(mnstat[4]))
|
||||
else:
|
||||
# now minus last paid
|
||||
delta = now - int(mnstat[5])
|
||||
# if > active seconds, use active seconds
|
||||
if delta >= int(mnstat[4]):
|
||||
mnstat.append(int(mnstat[4]))
|
||||
# use active seconds
|
||||
else:
|
||||
mnstat.append(delta)
|
||||
mn_queue.append(mnstat)
|
||||
mn_queue = sorted(mn_queue, key=lambda x: x[8], reverse=True)
|
||||
return mn_queue
|
||||
|
||||
def get_payment_position(payment_queue, address):
|
||||
'''
|
||||
Returns the position of the payment list for the given address.
|
||||
|
||||
payment_queue: position in the payment queue for the masternode.
|
||||
address: masternode payee address.
|
||||
'''
|
||||
position = -1
|
||||
for pos, mn in enumerate(payment_queue, start=1):
|
||||
if mn[2] == address:
|
||||
position = pos
|
||||
break
|
||||
return position
|
||||
|
||||
# Accordingly with the masternode payment queue, a custom list with
|
||||
# the masternode information including the payment position is returned.
|
||||
if self.controller.cache_mn_height != self.height() or not self.controller.mn_cache:
|
||||
self.controller.cache_mn_height = self.height()
|
||||
self.controller.mn_cache.clear()
|
||||
full_mn_list = await self.daemon.masternode_list(['full'])
|
||||
mn_payment_queue = get_masternode_payment_queue(full_mn_list)
|
||||
mn_payment_count = len(mn_payment_queue)
|
||||
mn_list = []
|
||||
for key, value in full_mn_list.items():
|
||||
mn_data = value.split()
|
||||
mn_info = {}
|
||||
mn_info['vin'] = key
|
||||
mn_info['status'] = mn_data[0]
|
||||
mn_info['protocol'] = mn_data[1]
|
||||
mn_info['payee'] = mn_data[2]
|
||||
mn_info['lastseen'] = mn_data[3]
|
||||
mn_info['activeseconds'] = mn_data[4]
|
||||
mn_info['lastpaidtime'] = mn_data[5]
|
||||
mn_info['lastpaidblock'] = mn_data[6]
|
||||
mn_info['ip'] = mn_data[7]
|
||||
mn_info['paymentposition'] = get_payment_position(mn_payment_queue, mn_info['payee'])
|
||||
mn_info['inselection'] = mn_info['paymentposition'] < mn_payment_count // 10
|
||||
balance = await self.controller.address_get_balance(mn_info['payee'])
|
||||
mn_info['balance'] = sum(balance.values()) / self.controller.coin.VALUE_PER_COIN
|
||||
mn_list.append(mn_info)
|
||||
self.controller.mn_cache = mn_list
|
||||
|
||||
# If payees is an empty list the whole masternode list is returned
|
||||
if payees:
|
||||
result = [mn for mn in self.controller.mn_cache
|
||||
for address in payees if mn['payee'] == address]
|
||||
else:
|
||||
result = self.controller.mn_cache
|
||||
|
||||
return result
|
||||
|
||||
17
tests/blocks/pac_mainnet_27676.json
Normal file
17
tests/blocks/pac_mainnet_27676.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"hash": "00000000000001439cf3d3b09f067a600d10cbe8b532e14413c904ae17bccd49",
|
||||
"confirmations": 39839,
|
||||
"size": 456,
|
||||
"height": 27676,
|
||||
"merkleroot": "cffe89f39de2f101d6e2509b1f5121609c369c8e4ac0152252cb0cc18c613603",
|
||||
"tx": [
|
||||
"d4426bc2597439f509e685848dbc5272b2d52e882e2b6cf4910971062e130dd6",
|
||||
"7f8761bdd82d6cf4b976008c533ae56c2f6c8f49b67597067dc156871fc40a4f"
|
||||
],
|
||||
"time": 1520767916,
|
||||
"nonce": 2372120537,
|
||||
"bits": "1a03d789",
|
||||
"previousblockhash": "00000000000003b32ed577c591f6e60eb7b534f61ebdcc9f5fd28d2386e8f391",
|
||||
"nextblockhash": "000000000000017f3d0fe299f7a52cee98a46be48c853ff8197fd78340cbfcd8",
|
||||
"block": "0000002091f3e886238dd25f9fccbd1ef634b5b70ee6f691c577d52eb3030000000000000336618cc10ccb522215c04a8e9c369c6021511f9b50e2d601f1e29df389fecfac13a55a89d7031ad9af638d0201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1f021c6c04ac13a55a083ffffbbdb14c01000d2f6e6f64655374726174756d2f0000000002631e0b75700000001976a91489982fec237dfd38a42de5900c95b6deed54dad988ac7f027c96900000001976a9143b39725cff6ae6228194e15ad817976dbbc581e588ac00000000010000000178d66e9ffd71fc5edf9ac6bbffecbcf57d26c090e157b48f231fed89002dd217000000006a47304402205d1b6d24615cb924c38686e87319ec4282434368f6bc0881a94f8d22f980ca18022038b83b016e19c6fbccedef66c5eb73147119cb1cd93dacfdd3d42f647575ed30012102171a4f5763f91281a4ee4775d2e01d81f5c1ccddf2a05662b9682f84bf09ac93feffffff0260bf570b000000001976a91489dd6e460e00695b55b710d62b2e1fb815d44b6588ac1e6bb32d350000001976a91495f95b4a02171deca1046cf5c505aa3ea57216da88ac196c0000"
|
||||
}
|
||||
15
tests/blocks/pac_testnet_16275.json
Normal file
15
tests/blocks/pac_testnet_16275.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"hash": "000003527433edc9fdf28d848c5fa3279ff8a016afc67d47a080534461871aa2",
|
||||
"size": 215,
|
||||
"height": 16275,
|
||||
"merkleroot": "699bda4bad8fc13eff818ffa1a2aa1d19a992d8436ec67e86dd8ce038130e6e5",
|
||||
"tx": [
|
||||
"699bda4bad8fc13eff818ffa1a2aa1d19a992d8436ec67e86dd8ce038130e6e5"
|
||||
],
|
||||
"time": 1525276230,
|
||||
"nonce": 2816146,
|
||||
"bits": "1e042d0c",
|
||||
"previousblockhash": "000002bc2882394ca3852cda708311e500c8a9df717255a1336cc350b07550f0",
|
||||
"nextblockhash": "000003eec7b324a19edbfc0aa539549dc02bd24e3de9d31e8c85fd3da68f1962",
|
||||
"block": "00000020f05075b050c36c33a1557271dfa9c800e5118370da2c85a34c398228bc020000e5e6308103ced86de867ec36842d999ad1a12a1afa8f81ff3ec18fad4bda9b6946dee95a0c2d041e92f82a000101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0502933f0101ffffffff020072b8101000000023210336524995b3bb2c2f300b3dd45e920f27f1cfc3c4f2e5a84e65af0fddc3bab00eac00aecefaf00000001976a914c2f707ddbb9c3ca5b2c3458cabc0d24baab66b8b88ac00000000"
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user