Browse Source

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
patch-2
elmora-do 7 years ago
committed by Neil
parent
commit
7baf6cf68d
  1. 103
      docs/protocol-methods.rst
  2. 45
      lib/coins.py
  3. 2
      server/controller.py
  4. 122
      server/session.py
  5. 17
      tests/blocks/pac_mainnet_27676.json
  6. 15
      tests/blocks/pac_testnet_16275.json

103
docs/protocol-methods.rst

@ -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

@ -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

2
server/controller.py

@ -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 '

122
server/session.py

@ -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

@ -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

@ -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…
Cancel
Save