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", "1.2"]
|
||||||
"ElectrumX 1.2.1"
|
"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,
|
'nonce': nonce,
|
||||||
'nAccumulatorCheckpoint': hash_to_str(header[80:112]),
|
'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.history_cache = pylru.lrucache(256)
|
||||||
self.header_cache = pylru.lrucache(8)
|
self.header_cache = pylru.lrucache(8)
|
||||||
self.cache_height = 0
|
self.cache_height = 0
|
||||||
|
self.cache_mn_height = 0
|
||||||
|
self.mn_cache = pylru.lrucache(256)
|
||||||
env.max_send = max(350000, env.max_send)
|
env.max_send = max(350000, env.max_send)
|
||||||
# Set up the RPC request handlers
|
# Set up the RPC request handlers
|
||||||
cmds = ('add_peer daemon_url disconnect getinfo groups log peers reorg '
|
cmds = ('add_peer daemon_url disconnect getinfo groups log peers reorg '
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
import codecs
|
import codecs
|
||||||
import itertools
|
import itertools
|
||||||
import time
|
import time
|
||||||
|
import datetime
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from aiorpcx import ServerSession, JSONRPCAutoDetect, RPCError
|
from aiorpcx import ServerSession, JSONRPCAutoDetect, RPCError
|
||||||
@ -495,22 +496,28 @@ class DashElectrumX(ElectrumX):
|
|||||||
'masternode.announce.broadcast':
|
'masternode.announce.broadcast':
|
||||||
self.masternode_announce_broadcast,
|
self.masternode_announce_broadcast,
|
||||||
'masternode.subscribe': self.masternode_subscribe,
|
'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):
|
def notify(self, height, touched):
|
||||||
'''Notify the client about changes in masternode list.'''
|
'''Notify the client about changes in masternode list.'''
|
||||||
result = super().notify(height, touched)
|
result = super().notify(height, touched)
|
||||||
|
self.controller.create_task(self.notify_masternodes_async())
|
||||||
for masternode in self.mns:
|
|
||||||
status = self.daemon.masternode_list(['status', masternode])
|
|
||||||
self.send_notification('masternode.subscribe',
|
|
||||||
[masternode, status.get(masternode)])
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
# Masternode command handlers
|
# Masternode command handlers
|
||||||
async def masternode_announce_broadcast(self, signmnb):
|
async def masternode_announce_broadcast(self, signmnb):
|
||||||
'''Pass through the masternode announce message to be broadcast
|
'''Pass through the masternode announce message to be broadcast
|
||||||
by the daemon.'''
|
by the daemon.
|
||||||
|
|
||||||
|
signmnb: signed masternode broadcast message.'''
|
||||||
try:
|
try:
|
||||||
return await self.daemon.masternode_broadcast(['relay', signmnb])
|
return await self.daemon.masternode_broadcast(['relay', signmnb])
|
||||||
except DaemonError as e:
|
except DaemonError as e:
|
||||||
@ -520,10 +527,103 @@ class DashElectrumX(ElectrumX):
|
|||||||
raise RPCError(BAD_REQUEST, 'the masternode broadcast was '
|
raise RPCError(BAD_REQUEST, 'the masternode broadcast was '
|
||||||
f'rejected.\n\n{message}\n[{signmnb}]')
|
f'rejected.\n\n{message}\n[{signmnb}]')
|
||||||
|
|
||||||
async def masternode_subscribe(self, vin):
|
async def masternode_subscribe(self, collateral):
|
||||||
'''Returns the status of masternode.'''
|
'''Returns the status of masternode.
|
||||||
result = await self.daemon.masternode_list(['status', vin])
|
|
||||||
|
collateral: masternode collateral.
|
||||||
|
'''
|
||||||
|
result = await self.daemon.masternode_list(['status', collateral])
|
||||||
if result is not None:
|
if result is not None:
|
||||||
self.mns.add(vin)
|
self.mns.add(collateral)
|
||||||
return result.get(vin)
|
return result.get(collateral)
|
||||||
return None
|
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