first commit
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
import pytest
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
os.environ['SENTINEL_ENV'] = 'test'
|
||||
os.environ['SENTINEL_CONFIG'] = os.path.normpath(os.path.join(os.path.dirname(__file__), '../test_sentinel.conf'))
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'lib'))
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..'))
|
||||
import config
|
||||
|
||||
from sibcoind import SibcoinDaemon
|
||||
from sib_config import SibcoinConfig
|
||||
|
||||
|
||||
def test_dashd():
|
||||
config_text = SibcoinConfig.slurp_config_file(config.sibcoin_conf)
|
||||
network = 'mainnet'
|
||||
is_testnet = False
|
||||
genesis_hash = u'00000c492bf73490420868bc577680bfc4c60116e7e85343bc624787c21efa4c'
|
||||
for line in config_text.split("\n"):
|
||||
if line.startswith('testnet=1'):
|
||||
network = 'testnet'
|
||||
is_testnet = True
|
||||
genesis_hash = u'00000617791d0e19f524387f67e558b2a928b670b9a3b387ae003ad7f9093017'
|
||||
|
||||
creds = SibcoinConfig.get_rpc_creds(config_text, network)
|
||||
sibcoind = SibcoinDaemon(**creds)
|
||||
assert sibcoind.rpc_command is not None
|
||||
|
||||
assert hasattr(sibcoind, 'rpc_connection')
|
||||
|
||||
# Dash testnet block 0 hash == 00000617791d0e19f524387f67e558b2a928b670b9a3b387ae003ad7f9093017
|
||||
# test commands without arguments
|
||||
info = sibcoind.rpc_command('getinfo')
|
||||
info_keys = [
|
||||
'blocks',
|
||||
'connections',
|
||||
'difficulty',
|
||||
'errors',
|
||||
'protocolversion',
|
||||
'proxy',
|
||||
'testnet',
|
||||
'timeoffset',
|
||||
'version',
|
||||
]
|
||||
for key in info_keys:
|
||||
assert key in info
|
||||
assert info['testnet'] is is_testnet
|
||||
|
||||
# test commands with args
|
||||
assert sibcoind.rpc_command('getblockhash', 0) == genesis_hash
|
||||
@@ -0,0 +1,3 @@
|
||||
network=testnet
|
||||
db_name=database/sentinel.db
|
||||
db_driver=sqlite
|
||||
@@ -0,0 +1,315 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import pytest
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
os.environ['SENTINEL_ENV'] = 'test'
|
||||
os.environ['SENTINEL_CONFIG'] = os.path.normpath(os.path.join(os.path.dirname(__file__), '../../test_sentinel.conf'))
|
||||
sys.path.append(os.path.normpath(os.path.join(os.path.dirname(__file__), '../../../lib')))
|
||||
import misc
|
||||
import config
|
||||
from models import GovernanceObject, Proposal, Vote
|
||||
|
||||
|
||||
# clear DB tables before each execution
|
||||
def setup():
|
||||
# clear tables first
|
||||
Vote.delete().execute()
|
||||
Proposal.delete().execute()
|
||||
GovernanceObject.delete().execute()
|
||||
|
||||
|
||||
def teardown():
|
||||
pass
|
||||
|
||||
|
||||
# list of proposal govobjs to import for testing
|
||||
@pytest.fixture
|
||||
def go_list_proposals():
|
||||
items = [
|
||||
{u'AbsoluteYesCount': 1000,
|
||||
u'AbstainCount': 7,
|
||||
u'CollateralHash': u'996eae8ba8dbe5152ccb302ba513cf59b79fa95a7899fe34519804e4a4e6c94e',
|
||||
u'DataHex': u'5b5b2270726f706f73616c222c7b22656e645f65706f6368223a2232313232353230343030222c226e616d65223a2274657374222c227061796d656e745f61646472657373223a22736352613467356154697a6f724c31484a7179326431796f4d6f6e456f677239675a222c227061796d656e745f616d6f756e74223a223235222c2273746172745f65706f6368223a2231343930313833313830222c2274797065223a312c2275726c223a2268747470733a2f2f736962636f696e2e6f7267227d5d5d',
|
||||
u'DataString': u'[["proposal",{"end_epoch":"2122520400","name":"test","payment_address":"scRa4g5aTizorL1HJqy2d1yoMonEogr9gZ","payment_amount":"25","start_epoch":"1490183180","type":1,"url":"https://sibcoin.org"}]]',
|
||||
u'Hash': u'7e38a64c2e5275b978e0075be2d87765b91f1bab75285de6818c00fb009465be',
|
||||
u'IsValidReason': u'',
|
||||
u'NoCount': 25,
|
||||
u'YesCount': 1025,
|
||||
u'fBlockchainValidity': True,
|
||||
u'fCachedDelete': False,
|
||||
u'fCachedEndorsed': False,
|
||||
u'fCachedFunding': True,
|
||||
u'fCachedValid': True},
|
||||
{u'AbsoluteYesCount': 11,
|
||||
u'AbstainCount': 0,
|
||||
u'CollateralHash': u'0542fe1a708ebc5857a1a86c9c394792e89302df070f604c5a90e2d6dcddf6b2',
|
||||
u'DataHex': u'5b5b2270726f706f73616c222c7b22656e645f65706f6368223a2232313232353230343030222c226e616d65223a22746573745f32222c227061796d656e745f61646472657373223a22734e74335a7a686963513277713847545334577644475745513466554e766132636f222c227061796d656e745f616d6f756e74223a2235222c2273746172745f65706f6368223a2231343930333131303237222c2274797065223a312c2275726c223a2268747470733a2f2f736962636f696e2e6f72672f7465737432227d5d5d',
|
||||
u'DataString': u'[["proposal",{"end_epoch":"2122520400","name":"test_2","payment_address":"sNt3ZzhicQ2wq8GTS4WvDGWEQ4fUNva2co","payment_amount":"5","start_epoch":"1490311027","type":1,"url":"https://sibcoin.org/test2"}]]',
|
||||
u'Hash': u'62319ca4478962bfd6601095b29bae00cab0ad4d037f6eee55d1ccfae7d637eb',
|
||||
u'IsValidReason': u'',
|
||||
u'NoCount': 0,
|
||||
u'YesCount': 11,
|
||||
u'fBlockchainValidity': True,
|
||||
u'fCachedDelete': False,
|
||||
u'fCachedEndorsed': False,
|
||||
u'fCachedFunding': True,
|
||||
u'fCachedValid': True},
|
||||
]
|
||||
|
||||
return items
|
||||
|
||||
|
||||
# Proposal
|
||||
@pytest.fixture
|
||||
def proposal():
|
||||
# NOTE: no governance_object_id is set
|
||||
pobj = Proposal(
|
||||
start_epoch=1483250400, # 2017-01-01
|
||||
end_epoch=2122520400,
|
||||
name="wine-n-cheeze-party",
|
||||
url="https://sibcoin.net/wine-n-cheeze-party",
|
||||
payment_address="sYNpoRsQDBN8qYFxeifN2XHazF58e14BbQ",
|
||||
payment_amount=13
|
||||
)
|
||||
|
||||
# NOTE: this object is (intentionally) not saved yet.
|
||||
# We want to return an built, but unsaved, object
|
||||
return pobj
|
||||
|
||||
|
||||
def test_proposal_is_valid(proposal):
|
||||
from sibcoind import SibcoinDaemon
|
||||
import dashlib
|
||||
dashd = SibcoinDaemon.from_sibcoin_conf(config.sibcoin_conf)
|
||||
|
||||
orig = Proposal(**proposal.get_dict()) # make a copy
|
||||
|
||||
# fixture as-is should be valid
|
||||
assert proposal.is_valid() is True
|
||||
|
||||
# ============================================================
|
||||
# ensure end_date not greater than start_date
|
||||
# ============================================================
|
||||
proposal.end_epoch = proposal.start_epoch
|
||||
assert proposal.is_valid() is False
|
||||
|
||||
proposal.end_epoch = proposal.start_epoch - 1
|
||||
assert proposal.is_valid() is False
|
||||
|
||||
proposal.end_epoch = proposal.start_epoch + 0
|
||||
assert proposal.is_valid() is False
|
||||
|
||||
proposal.end_epoch = proposal.start_epoch + 1
|
||||
assert proposal.is_valid() is True
|
||||
|
||||
# reset
|
||||
proposal = Proposal(**orig.get_dict())
|
||||
|
||||
# ============================================================
|
||||
# ensure valid proposal name
|
||||
# ============================================================
|
||||
|
||||
proposal.name = ' heya!@209h '
|
||||
assert proposal.is_valid() is False
|
||||
|
||||
proposal.name = "anything' OR 'x'='x"
|
||||
assert proposal.is_valid() is False
|
||||
|
||||
proposal.name = ' '
|
||||
assert proposal.is_valid() is False
|
||||
|
||||
proposal.name = ''
|
||||
assert proposal.is_valid() is False
|
||||
|
||||
proposal.name = '0'
|
||||
assert proposal.is_valid() is True
|
||||
|
||||
proposal.name = 'R66-Y'
|
||||
assert proposal.is_valid() is True
|
||||
|
||||
proposal.name = 'valid-name'
|
||||
assert proposal.is_valid() is True
|
||||
|
||||
proposal.name = ' mostly-valid-name'
|
||||
assert proposal.is_valid() is False
|
||||
|
||||
proposal.name = 'also-mostly-valid-name '
|
||||
assert proposal.is_valid() is False
|
||||
|
||||
proposal.name = ' similarly-kinda-valid-name '
|
||||
assert proposal.is_valid() is False
|
||||
|
||||
proposal.name = 'dean miller 5493'
|
||||
assert proposal.is_valid() is False
|
||||
|
||||
proposal.name = 'dean-millerà-5493'
|
||||
assert proposal.is_valid() is False
|
||||
|
||||
proposal.name = 'dean-миллер-5493'
|
||||
assert proposal.is_valid() is False
|
||||
|
||||
# binary gibberish
|
||||
proposal.name = dashlib.deserialise('22385c7530303933375c75303363375c75303232395c75303138635c75303064335c75303163345c75303264385c75303236615c75303134625c75303163335c75303063335c75303362385c75303266615c75303261355c75303266652f2b5c75303065395c75303164655c75303136655c75303338645c75303062385c75303138635c75303064625c75303064315c75303038325c75303133325c753032333222')
|
||||
assert proposal.is_valid() is False
|
||||
|
||||
# reset
|
||||
proposal = Proposal(**orig.get_dict())
|
||||
|
||||
# ============================================================
|
||||
# ensure valid payment address
|
||||
# ============================================================
|
||||
proposal.payment_address = '7'
|
||||
assert proposal.is_valid() is False
|
||||
|
||||
proposal.payment_address = 'YYE8KWYAUU5YSWSYMB3Q3RYX8XTUU9Y7UI'
|
||||
assert proposal.is_valid() is False
|
||||
|
||||
proposal.payment_address = 'seVNpH5rkau8dKhumiLF1BYspp2vf7Lkyc'
|
||||
assert proposal.is_valid() is False
|
||||
|
||||
proposal.payment_address = '221 B Baker St., London, United Kingdom'
|
||||
assert proposal.is_valid() is False
|
||||
|
||||
# this is actually the Dash foundation multisig address...
|
||||
proposal.payment_address = '7gnwGHt17heGpG9Crfeh4KGpYNFugPhJdh'
|
||||
assert proposal.is_valid() is False
|
||||
|
||||
proposal.payment_address = 'seVNpH5rkau8dKhumiLF1BYspp2vf7Lkyb'
|
||||
assert proposal.is_valid() is True
|
||||
|
||||
proposal.payment_address = ' yYe8KwyaUu5YswSYmB3q3ryx8XTUu9y7Ui'
|
||||
assert proposal.is_valid() is False
|
||||
|
||||
proposal.payment_address = 'yYe8KwyaUu5YswSYmB3q3ryx8XTUu9y7Ui '
|
||||
assert proposal.is_valid() is False
|
||||
|
||||
proposal.payment_address = ' yYe8KwyaUu5YswSYmB3q3ryx8XTUu9y7Ui '
|
||||
assert proposal.is_valid() is False
|
||||
|
||||
# reset
|
||||
proposal = Proposal(**orig.get_dict())
|
||||
|
||||
# validate URL
|
||||
proposal.url = ' '
|
||||
assert proposal.is_valid() is False
|
||||
|
||||
proposal.url = ' '
|
||||
assert proposal.is_valid() is False
|
||||
|
||||
proposal.url = 'http://bit.ly/1e1EYJv'
|
||||
assert proposal.is_valid() is True
|
||||
|
||||
proposal.url = ' http://bit.ly/1e1EYJv'
|
||||
assert proposal.is_valid() is False
|
||||
|
||||
proposal.url = 'http://bit.ly/1e1EYJv '
|
||||
assert proposal.is_valid() is False
|
||||
|
||||
proposal.url = ' http://bit.ly/1e1EYJv '
|
||||
assert proposal.is_valid() is False
|
||||
|
||||
proposal.url = 'http://::12.34.56.78]/'
|
||||
assert proposal.is_valid() is False
|
||||
|
||||
proposal.url = 'http://[::1/foo/bad]/bad'
|
||||
assert proposal.is_valid() is False
|
||||
|
||||
proposal.url = 'http://dashcentral.org/dean-miller 5493'
|
||||
assert proposal.is_valid() is False
|
||||
|
||||
proposal.url = 'http://dashcentralisé.org/dean-miller-5493'
|
||||
assert proposal.is_valid() is True
|
||||
|
||||
proposal.url = 'http://dashcentralisé.org/dean-миллер-5493'
|
||||
assert proposal.is_valid() is True
|
||||
|
||||
proposal.url = 'https://example.com/resource.ext?param=1&other=2'
|
||||
assert proposal.is_valid() is True
|
||||
|
||||
proposal.url = 'www.com'
|
||||
assert proposal.is_valid() is True
|
||||
|
||||
proposal.url = 'v.ht/'
|
||||
assert proposal.is_valid() is True
|
||||
|
||||
proposal.url = 'ipfs:///ipfs/QmPwwoytFU3gZYk5tSppumxaGbHymMUgHsSvrBdQH69XRx/'
|
||||
assert proposal.is_valid() is True
|
||||
|
||||
proposal.url = '/ipfs/QmPwwoytFU3gZYk5tSppumxaGbHymMUgHsSvrBdQH69XRx/'
|
||||
assert proposal.is_valid() is True
|
||||
|
||||
proposal.url = 's3://bucket/thing/anotherthing/file.pdf'
|
||||
assert proposal.is_valid() is True
|
||||
|
||||
proposal.url = 'http://zqktlwi4fecvo6ri.onion/wiki/index.php/Main_Page'
|
||||
assert proposal.is_valid() is True
|
||||
|
||||
proposal.url = 'ftp://ftp.funet.fi/pub/standards/RFC/rfc959.txt'
|
||||
assert proposal.is_valid() is True
|
||||
|
||||
# gibberish URL
|
||||
proposal.url = dashlib.deserialise('22687474703a2f2f5c75303330385c75303065665c75303362345c75303362315c75303266645c75303331345c625c75303134655c75303031615c75303139655c75303133365c75303264315c75303238655c75303364395c75303230665c75303363355c75303030345c75303336665c75303238355c75303165375c75303063635c75303139305c75303262615c75303239316a5c75303130375c75303362365c7530306562645c75303133335c75303335665c7530326562715c75303038655c75303332645c75303362645c75303064665c75303135654f365c75303237335c75303363645c7530333539275c75303165345c75303339615c75303365385c75303334345c75303130615c75303265662e5c75303231625c75303164356a5c75303232345c75303163645c75303336365c75303064625c75303339665c75303230305c75303337615c75303138395c75303263325c75303038345c75303066615c75303031335c75303233655c75303135345c75303165395c75303139635c75303239375c75303039355c75303038345c75303362305c7530306233435c75303135345c75303063665c75303163345c75303261335c75303362655c75303136305c75303139365c75303263665c75303131305c7530313031475c75303162645c75303338645c75303363325c75303138625c75303235625c75303266325c75303264635c75303139335c75303066665c75303066645c75303133625c75303234305c75303137615c75303062355c75303031645c75303238655c75303166315c75303232315c75303161615c75303265325c75303335625c75303333665c75303239345c75303335315c75303038345c75303339395c75303262385c75303132375c75303330357a5c75303263625c75303066305c75303062355c75303164335c75303338385c75303364385c75303130625c75303266325c75303137305c75303335315c75303030305c75303136385c75303039646d5c75303331315c75303236615c75303330375c75303332635c75303361635c665c75303363335c75303264365c75303238645c75303136395c7530323438635c75303163385c75303261355c75303164615c75303165375c75303337355c75303332645c7530333165755c75303131665c75303338375c75303135325c75303065325c75303135326c5c75303164325c75303164615c75303136645c75303061665c75303333375c75303264375c75303339375c75303139395c75303134635c75303165385c75303234315c75303336635c75303130645c75303230635c75303161615c75303339355c75303133315c75303064615c75303165615c75303336645c75303064325c75303337365c75303363315c75303132645c75303266305c75303064364f255c75303263635c75303162645c75303062385c75303238365c75303136395c75303337335c75303232335c75303336655c75303037665c75303062616b5c75303132365c75303233305c75303330645c75303362385c75303164355c75303166615c75303338395c75303062635c75303135325c75303334365c75303139645c75303135615c75303031395c75303061385c75303133615c75303338635c75303339625c75303261655c75303065395c75303362635c75303166385c75303031665c75303230615c75303263355c75303134335c75303361635c75303334355c75303236645c75303139365c75303362665c75303135615c75303137305c75303165395c75303231395c75303332665c75303232645c75303030365c75303066305c75303134665c75303337375c75303234325d5c75303164325c75303337655c75303265665c75303331395c75303261355c75303265385c75303338395c75303235645c75303334315c75303338395c7530323230585c75303062645c75303166365c75303238645c75303231375c75303066665c75303130385c75303331305c75303330335c75303031395c75303039635c75303363315c75303039615c75303334355c75303331305c75303162335c75303263315c75303132395c75303234335c75303038627c5c75303361335c75303261635c75303165655c75303030305c75303237615c75303038385c75303066355c75303232375c75303236635c75303236355c7530336336205c75303038615c7530333561787c735c75303336305c75303362655c75303235385c75303334345c75303264365c75303262355c75303361315c75303135345c75303131625c75303061625c75303038615c75303332655c75303238325c75303031393d5c75303263335c75303332655c75303163645c75303139305c75303231305c75303131365c75303334305c75303234665c75303162635c75303333645c75303135305c75303132335c75303233645c75303133345c75303062327a5c75303331635c75303136312a5c753032316522')
|
||||
assert proposal.is_valid() is False
|
||||
|
||||
# reset
|
||||
proposal = Proposal(**orig.get_dict())
|
||||
|
||||
# ============================================================
|
||||
# ensure proposal can't request negative dash
|
||||
# ============================================================
|
||||
proposal.payment_amount = -1
|
||||
assert proposal.is_valid() is False
|
||||
|
||||
|
||||
def test_proposal_is_expired(proposal):
|
||||
cycle = 24 # testnet
|
||||
now = misc.now()
|
||||
|
||||
proposal.start_epoch = now - (86400 * 2) # two days ago
|
||||
proposal.end_epoch = now - (60 * 60) # expired one hour ago
|
||||
assert proposal.is_expired(superblockcycle=cycle) is False
|
||||
|
||||
# fudge factor + a 24-block cycle == an expiry window of 9086, so...
|
||||
proposal.end_epoch = now - 9085
|
||||
assert proposal.is_expired(superblockcycle=cycle) is False
|
||||
|
||||
proposal.end_epoch = now - 9087
|
||||
assert proposal.is_expired(superblockcycle=cycle) is True
|
||||
|
||||
|
||||
# deterministic ordering
|
||||
def test_approved_and_ranked(go_list_proposals):
|
||||
from sibcoind import SibcoinDaemon
|
||||
sibcoind = SibcoinDaemon.from_sibcoin_conf(config.sibcoin_conf)
|
||||
|
||||
for item in go_list_proposals:
|
||||
(go, subobj) = GovernanceObject.import_gobject_from_dashd(sibcoind, item)
|
||||
|
||||
prop_list = Proposal.approved_and_ranked(proposal_quorum=1, next_superblock_max_budget=60)
|
||||
|
||||
assert prop_list[0].object_hash == u'7e38a64c2e5275b978e0075be2d87765b91f1bab75285de6818c00fb009465be'
|
||||
assert prop_list[1].object_hash == u'62319ca4478962bfd6601095b29bae00cab0ad4d037f6eee55d1ccfae7d637eb'
|
||||
|
||||
|
||||
def test_proposal_size(proposal):
|
||||
orig = Proposal(**proposal.get_dict()) # make a copy
|
||||
|
||||
proposal.url = 'https://testurl.com/'
|
||||
proposal_length_bytes = len(proposal.serialise()) // 2
|
||||
|
||||
# how much space is available in the Proposal
|
||||
extra_bytes = (Proposal.MAX_DATA_SIZE - proposal_length_bytes)
|
||||
|
||||
# fill URL field with max remaining space
|
||||
proposal.url = proposal.url + ('x' * extra_bytes)
|
||||
|
||||
# ensure this is the max proposal size and is valid
|
||||
assert (len(proposal.serialise()) // 2) == Proposal.MAX_DATA_SIZE
|
||||
assert proposal.is_valid() is True
|
||||
|
||||
# add one more character to URL, Proposal should now be invalid
|
||||
proposal.url = proposal.url + 'x'
|
||||
assert (len(proposal.serialise()) // 2) == (Proposal.MAX_DATA_SIZE + 1)
|
||||
assert proposal.is_valid() is False
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
import pytest
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
os.environ['SENTINEL_ENV'] = 'test'
|
||||
os.environ['SENTINEL_CONFIG'] = os.path.normpath(os.path.join(os.path.dirname(__file__), '../../test_sentinel.conf'))
|
||||
sys.path.append(os.path.normpath(os.path.join(os.path.dirname(__file__), '../../../lib')))
|
||||
import misc
|
||||
import config
|
||||
from models import GovernanceObject, Proposal, Superblock, Vote
|
||||
|
||||
|
||||
# clear DB tables before each execution
|
||||
def setup():
|
||||
# clear tables first...
|
||||
Vote.delete().execute()
|
||||
Proposal.delete().execute()
|
||||
Superblock.delete().execute()
|
||||
GovernanceObject.delete().execute()
|
||||
|
||||
|
||||
def teardown():
|
||||
pass
|
||||
|
||||
|
||||
# list of proposal govobjs to import for testing
|
||||
@pytest.fixture
|
||||
def go_list_proposals():
|
||||
items = [
|
||||
{u'AbsoluteYesCount': 1000,
|
||||
u'AbstainCount': 7,
|
||||
u'CollateralHash': u'acb67ec3f3566c9b94a26b70b36c1f74a010a37c0950c22d683cc50da324fdca',
|
||||
u'DataHex': u'5b5b2270726f706f73616c222c207b22656e645f65706f6368223a20323132323532303430302c20226e616d65223a20227465737470726f706f73616c2d35343933222c20227061796d656e745f61646472657373223a20227365564e704835726b617538644b68756d694c46314259737070327666374c6b7962222c20227061796d656e745f616d6f756e74223a2032352e37352c202273746172745f65706f6368223a20313437343236313038362c202274797065223a20312c202275726c223a2022687474703a2f2f736962636f6e74726f6c2e6f72672f70726f706f73616c732f7465737470726f706f73616c2d35343933227d5d5d',
|
||||
u'DataString': u'[["proposal", {"end_epoch": 2122520400, "name": "testproposal-5493", "payment_address": "seVNpH5rkau8dKhumiLF1BYspp2vf7Lkyb", "payment_amount": 25.75, "start_epoch": 1474261086, "type": 1, "url": "http://sibcontrol.org/proposals/testproposal-5493"}]]',
|
||||
u'Hash': u'dfd7d63979c0b62456b63d5fc5306dbec451180adee85876cbf5b28c69d1a86c',
|
||||
u'IsValidReason': u'',
|
||||
u'NoCount': 25,
|
||||
u'YesCount': 1025,
|
||||
u'fBlockchainValidity': True,
|
||||
u'fCachedDelete': False,
|
||||
u'fCachedEndorsed': False,
|
||||
u'fCachedFunding': False,
|
||||
u'fCachedValid': True},
|
||||
{u'AbsoluteYesCount': 1000,
|
||||
u'AbstainCount': 29,
|
||||
u'CollateralHash': u'3efd23283aa98c2c33f80e4d9ed6f277d195b72547b6491f43280380f6aac810',
|
||||
u'DataHex': u'5b5b2270726f706f73616c222c207b22656e645f65706f6368223a20323132323532303430302c20226e616d65223a20226665726e616e64657a2d37363235222c20227061796d656e745f61646472657373223a2022736674734a6564686d4c71594257506b627670716b7371737653397041624c614c53222c20227061796d656e745f616d6f756e74223a2033322e30312c202273746172745f65706f6368223a20313437343236313038362c202274797065223a20312c202275726c223a2022687474703a2f2f736962636f6e74726f6c2e6f72672f70726f706f73616c732f6665726e616e64657a2d37363235227d5d5d',
|
||||
u'DataString': u'[["proposal", {"end_epoch": 2122520400, "name": "fernandez-7625", "payment_address": "sftsJedhmLqYBWPkbvpqksqsvS9pAbLaLS", "payment_amount": 32.01, "start_epoch": 1474261086, "type": 1, "url": "http://sibcontrol.org/proposals/fernandez-7625"}]]',
|
||||
u'Hash': u'0523445762025b2e01a2cd34f1d10f4816cf26ee1796167e5b029901e5873630',
|
||||
u'IsValidReason': u'',
|
||||
u'NoCount': 56,
|
||||
u'YesCount': 1056,
|
||||
u'fBlockchainValidity': True,
|
||||
u'fCachedDelete': False,
|
||||
u'fCachedEndorsed': False,
|
||||
u'fCachedFunding': False,
|
||||
u'fCachedValid': True},
|
||||
]
|
||||
|
||||
return items
|
||||
|
||||
|
||||
# list of superblock govobjs to import for testing
|
||||
@pytest.fixture
|
||||
def go_list_superblocks():
|
||||
items = [
|
||||
{u'AbsoluteYesCount': 1,
|
||||
u'AbstainCount': 0,
|
||||
u'CollateralHash': u'0000000000000000000000000000000000000000000000000000000000000000',
|
||||
u'DataHex': u'5b5b2274726967676572222c207b226576656e745f626c6f636b5f686569676874223a2037323639362c20227061796d656e745f616464726573736573223a20227365564e704835726b617538644b68756d694c46314259737070327666374c6b79627c736674734a6564686d4c71594257506b627670716b7371737653397041624c614c53222c20227061796d656e745f616d6f756e7473223a202232352e37353030303030307c32352e3735303030303030222c202274797065223a20327d5d5d',
|
||||
u'DataString': u'[["trigger", {"event_block_height": 72696, "payment_addresses": "seVNpH5rkau8dKhumiLF1BYspp2vf7Lkyb|sftsJedhmLqYBWPkbvpqksqsvS9pAbLaLS", "payment_amounts": "25.75000000|25.7575000000", "type": 2}]]',
|
||||
u'Hash': u'667c4a53eb81ba14d02860fdb4779e830eb8e98306f9145f3789d347cbeb0721',
|
||||
u'IsValidReason': u'',
|
||||
u'NoCount': 0,
|
||||
u'YesCount': 1,
|
||||
u'fBlockchainValidity': True,
|
||||
u'fCachedDelete': False,
|
||||
u'fCachedEndorsed': False,
|
||||
u'fCachedFunding': False,
|
||||
u'fCachedValid': True},
|
||||
{u'AbsoluteYesCount': 1,
|
||||
u'AbstainCount': 0,
|
||||
u'CollateralHash': u'0000000000000000000000000000000000000000000000000000000000000000',
|
||||
u'DataHex': u'5b5b2274726967676572222c207b226576656e745f626c6f636b5f686569676874223a2037323639362c20227061796d656e745f616464726573736573223a20227365564e704835726b617538644b68756d694c46314259737070327666374c6b79627c736674734a6564686d4c71594257506b627670716b7371737653397041624c614c53222c20227061796d656e745f616d6f756e7473223a202232352e37353030303030307c32352e3735303030303030222c202274797065223a20327d5d5d',
|
||||
u'DataString': u'[["trigger", {"event_block_height": 72696, "payment_addresses": "seVNpH5rkau8dKhumiLF1BYspp2vf7Lkyb|sftsJedhmLqYBWPkbvpqksqsvS9pAbLaLS", "payment_amounts": "25.75000000|25.75000000", "type": 2}]]',
|
||||
u'Hash': u'8f91ffb105739ec7d5b6c0b12000210fcfcc0837d3bb8ca6333ba93ab5fc0bdf',
|
||||
u'IsValidReason': u'',
|
||||
u'NoCount': 0,
|
||||
u'YesCount': 1,
|
||||
u'fBlockchainValidity': True,
|
||||
u'fCachedDelete': False,
|
||||
u'fCachedEndorsed': False,
|
||||
u'fCachedFunding': False,
|
||||
u'fCachedValid': True},
|
||||
{u'AbsoluteYesCount': 1,
|
||||
u'AbstainCount': 0,
|
||||
u'CollateralHash': u'0000000000000000000000000000000000000000000000000000000000000000',
|
||||
u'DataHex': u'5b5b2274726967676572222c207b226576656e745f626c6f636b5f686569676874223a2037323639362c20227061796d656e745f616464726573736573223a20227365564e704835726b617538644b68756d694c46314259737070327666374c6b79627c736674734a6564686d4c71594257506b627670716b7371737653397041624c614c53222c20227061796d656e745f616d6f756e7473223a202232352e37353030303030307c32352e3735303030303030222c202274797065223a20327d5d5d',
|
||||
u'DataString': u'[["trigger", {"event_block_height": 72696, "payment_addresses": "seVNpH5rkau8dKhumiLF1BYspp2vf7Lkyb|sftsJedhmLqYBWPkbvpqksqsvS9pAbLaLS", "payment_amounts": "25.75000000|25.75000000", "type": 2}]]',
|
||||
u'Hash': u'bc2834f357da7504138566727c838e6ada74d079e63b6104701f4f8eb05dae36',
|
||||
u'IsValidReason': u'',
|
||||
u'NoCount': 0,
|
||||
u'YesCount': 1,
|
||||
u'fBlockchainValidity': True,
|
||||
u'fCachedDelete': False,
|
||||
u'fCachedEndorsed': False,
|
||||
u'fCachedFunding': False,
|
||||
u'fCachedValid': True},
|
||||
]
|
||||
|
||||
return items
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def superblock():
|
||||
sb = Superblock(
|
||||
event_block_height=62500,
|
||||
payment_addresses='seVNpH5rkau8dKhumiLF1BYspp2vf7Lkyb|sftsJedhmLqYBWPkbvpqksqsvS9pAbLaLS',
|
||||
payment_amounts='5|3',
|
||||
proposal_hashes='e8a0057914a2e1964ae8a945c4723491caae2077a90a00a2aabee22b40081a87|d1ce73527d7cd6f2218f8ca893990bc7d5c6b9334791ce7973bfa22f155f826e',
|
||||
)
|
||||
return sb
|
||||
|
||||
|
||||
def test_superblock_is_valid(superblock):
|
||||
from sibcoind import SibcoinDaemon
|
||||
sibcoind = SibcoinDaemon.from_sibcoin_conf(config.sibcoin_conf)
|
||||
|
||||
orig = Superblock(**superblock.get_dict()) # make a copy
|
||||
|
||||
# original as-is should be valid
|
||||
assert orig.is_valid() is True
|
||||
|
||||
# mess with payment amounts
|
||||
superblock.payment_amounts = '7|yyzx'
|
||||
assert superblock.is_valid() is False
|
||||
|
||||
superblock.payment_amounts = '7,|yzx'
|
||||
assert superblock.is_valid() is False
|
||||
|
||||
superblock.payment_amounts = '7|8'
|
||||
assert superblock.is_valid() is True
|
||||
|
||||
superblock.payment_amounts = ' 7|8'
|
||||
assert superblock.is_valid() is False
|
||||
|
||||
superblock.payment_amounts = '7|8 '
|
||||
assert superblock.is_valid() is False
|
||||
|
||||
superblock.payment_amounts = ' 7|8 '
|
||||
assert superblock.is_valid() is False
|
||||
|
||||
# reset
|
||||
superblock = Superblock(**orig.get_dict())
|
||||
assert superblock.is_valid() is True
|
||||
|
||||
# mess with payment addresses
|
||||
superblock.payment_addresses = 'yTC62huR4YQEPn9AJHjnQxxreHSbgAoatV|1234 Anywhere ST, Chicago, USA'
|
||||
assert superblock.is_valid() is False
|
||||
|
||||
# leading spaces in payment addresses
|
||||
superblock.payment_addresses = ' yTC62huR4YQEPn9AJHjnQxxreHSbgAoatV'
|
||||
superblock.payment_amounts = '5.00'
|
||||
assert superblock.is_valid() is False
|
||||
|
||||
# trailing spaces in payment addresses
|
||||
superblock.payment_addresses = 'yTC62huR4YQEPn9AJHjnQxxreHSbgAoatV '
|
||||
superblock.payment_amounts = '5.00'
|
||||
assert superblock.is_valid() is False
|
||||
|
||||
# leading & trailing spaces in payment addresses
|
||||
superblock.payment_addresses = ' yTC62huR4YQEPn9AJHjnQxxreHSbgAoatV '
|
||||
superblock.payment_amounts = '5.00'
|
||||
assert superblock.is_valid() is False
|
||||
|
||||
# single payment addr/amt is ok
|
||||
superblock.payment_addresses = 'sftsJedhmLqYBWPkbvpqksqsvS9pAbLaLS'
|
||||
superblock.payment_amounts = '5.00'
|
||||
assert superblock.is_valid() is True
|
||||
|
||||
# ensure number of payment addresses matches number of payments
|
||||
superblock.payment_addresses = 'sftsJedhmLqYBWPkbvpqksqsvS9pAbLaLS'
|
||||
superblock.payment_amounts = '37.00|23.24'
|
||||
assert superblock.is_valid() is False
|
||||
|
||||
superblock.payment_addresses = 'seVNpH5rkau8dKhumiLF1BYspp2vf7Lkyb|sftsJedhmLqYBWPkbvpqksqsvS9pAbLaLS'
|
||||
superblock.payment_amounts = '37.00'
|
||||
assert superblock.is_valid() is False
|
||||
|
||||
# ensure amounts greater than zero
|
||||
superblock.payment_addresses = 'sftsJedhmLqYBWPkbvpqksqsvS9pAbLaLS'
|
||||
superblock.payment_amounts = '-37.00'
|
||||
assert superblock.is_valid() is False
|
||||
|
||||
# reset
|
||||
superblock = Superblock(**orig.get_dict())
|
||||
assert superblock.is_valid() is True
|
||||
|
||||
# mess with proposal hashes
|
||||
superblock.proposal_hashes = '7|yyzx'
|
||||
assert superblock.is_valid() is False
|
||||
|
||||
superblock.proposal_hashes = '7,|yyzx'
|
||||
assert superblock.is_valid() is False
|
||||
|
||||
superblock.proposal_hashes = '0|1'
|
||||
assert superblock.is_valid() is False
|
||||
|
||||
superblock.proposal_hashes = '0000000000000000000000000000000000000000000000000000000000000000|1111111111111111111111111111111111111111111111111111111111111111'
|
||||
assert superblock.is_valid() is True
|
||||
|
||||
# reset
|
||||
superblock = Superblock(**orig.get_dict())
|
||||
assert superblock.is_valid() is True
|
||||
|
||||
|
||||
def test_serialisable_fields():
|
||||
s1 = ['event_block_height', 'payment_addresses', 'payment_amounts', 'proposal_hashes']
|
||||
s2 = Superblock.serialisable_fields()
|
||||
|
||||
s1.sort()
|
||||
s2.sort()
|
||||
|
||||
assert s2 == s1
|
||||
|
||||
|
||||
def test_deterministic_superblock_creation(go_list_proposals):
|
||||
import dashlib
|
||||
import misc
|
||||
from sibcoind import SibcoinDaemon
|
||||
sibcoind = SibcoinDaemon.from_sibcoin_conf(config.sibcoin_conf)
|
||||
for item in go_list_proposals:
|
||||
(go, subobj) = GovernanceObject.import_gobject_from_dashd(sibcoind, item)
|
||||
|
||||
max_budget = 60
|
||||
prop_list = Proposal.approved_and_ranked(proposal_quorum=1, next_superblock_max_budget=max_budget)
|
||||
|
||||
sb = dashlib.create_superblock(prop_list, 72000, max_budget, misc.now())
|
||||
|
||||
assert sb.event_block_height == 72000
|
||||
assert sb.payment_addresses == 'seVNpH5rkau8dKhumiLF1BYspp2vf7Lkyb|sftsJedhmLqYBWPkbvpqksqsvS9pAbLaLS'
|
||||
assert sb.payment_amounts == '25.75000000|32.01000000'
|
||||
assert sb.proposal_hashes == 'dfd7d63979c0b62456b63d5fc5306dbec451180adee85876cbf5b28c69d1a86c|0523445762025b2e01a2cd34f1d10f4816cf26ee1796167e5b029901e5873630'
|
||||
|
||||
assert sb.hex_hash() == 'f8cabf11ddc5479a9440868064b85bc7c726d267fc942b324e940e02949618c7'
|
||||
|
||||
|
||||
def test_deterministic_superblock_selection(go_list_superblocks):
|
||||
from sibcoind import SibcoinDaemon
|
||||
sibcoind = SibcoinDaemon.from_sibcoin_conf(config.sibcoin_conf)
|
||||
|
||||
for item in go_list_superblocks:
|
||||
(go, subobj) = GovernanceObject.import_gobject_from_dashd(sibcoind, item)
|
||||
|
||||
# highest hash wins if same -- so just order by hash
|
||||
sb = Superblock.find_highest_deterministic('366d15f2075cf8dc29301ec862d0343f79976a804ef76ef61adf50f818228413')
|
||||
assert sb.object_hash == 'bc2834f357da7504138566727c838e6ada74d079e63b6104701f4f8eb05dae36'
|
||||
@@ -0,0 +1,85 @@
|
||||
import pytest
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
os.environ['SENTINEL_CONFIG'] = os.path.normpath(os.path.join(os.path.dirname(__file__), '../test_sentinel.conf'))
|
||||
os.environ['SENTINEL_ENV'] = 'test'
|
||||
sys.path.append(os.path.normpath(os.path.join(os.path.dirname(__file__), '../../lib')))
|
||||
import config
|
||||
#from dash_config import DashConfig
|
||||
from sib_config import SibcoinConfig
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def dash_conf(**kwargs):
|
||||
defaults = {
|
||||
'rpcuser': 'dashrpc',
|
||||
'rpcpassword': 'EwJeV3fZTyTVozdECF627BkBMnNDwQaVLakG3A4wXYyk',
|
||||
'rpcport': 29241,
|
||||
}
|
||||
|
||||
# merge kwargs into defaults
|
||||
for (key, value) in kwargs.items():
|
||||
defaults[key] = value
|
||||
|
||||
conf = """# basic settings
|
||||
testnet=1 # TESTNET
|
||||
server=1
|
||||
rpcuser={rpcuser}
|
||||
rpcpassword={rpcpassword}
|
||||
rpcallowip=127.0.0.1
|
||||
rpcport={rpcport}
|
||||
""".format(**defaults)
|
||||
|
||||
return conf
|
||||
|
||||
|
||||
def test_get_rpc_creds():
|
||||
dash_config = dash_conf()
|
||||
creds = SibcoinConfig.get_rpc_creds(dash_config, 'testnet')
|
||||
|
||||
for key in ('user', 'password', 'port'):
|
||||
assert key in creds
|
||||
assert creds.get('user') == 'dashrpc'
|
||||
assert creds.get('password') == 'EwJeV3fZTyTVozdECF627BkBMnNDwQaVLakG3A4wXYyk'
|
||||
assert creds.get('port') == 29241
|
||||
|
||||
dash_config = dash_conf(rpcpassword='s00pers33kr1t', rpcport=8000)
|
||||
creds = SibcoinConfig.get_rpc_creds(dash_config, 'testnet')
|
||||
|
||||
for key in ('user', 'password', 'port'):
|
||||
assert key in creds
|
||||
assert creds.get('user') == 'dashrpc'
|
||||
assert creds.get('password') == 's00pers33kr1t'
|
||||
assert creds.get('port') == 8000
|
||||
|
||||
no_port_specified = re.sub('\nrpcport=.*?\n', '\n', dash_conf(), re.M)
|
||||
creds = SibcoinConfig.get_rpc_creds(no_port_specified, 'testnet')
|
||||
|
||||
for key in ('user', 'password', 'port'):
|
||||
assert key in creds
|
||||
assert creds.get('user') == 'dashrpc'
|
||||
assert creds.get('password') == 'EwJeV3fZTyTVozdECF627BkBMnNDwQaVLakG3A4wXYyk'
|
||||
assert creds.get('port') == 11944
|
||||
|
||||
|
||||
def test_slurp_config_file():
|
||||
import tempfile
|
||||
|
||||
dash_config = """# basic settings
|
||||
#testnet=1 # TESTNET
|
||||
server=1
|
||||
printtoconsole=1
|
||||
txindex=1 # enable transaction index
|
||||
"""
|
||||
|
||||
expected_stripped_config = """server=1
|
||||
printtoconsole=1
|
||||
txindex=1 # enable transaction index
|
||||
"""
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode='w') as temp:
|
||||
temp.write(dash_config)
|
||||
temp.flush()
|
||||
conf = SibcoinConfig.slurp_config_file(temp.name)
|
||||
assert conf == expected_stripped_config
|
||||
@@ -0,0 +1,140 @@
|
||||
import pytest
|
||||
import sys
|
||||
import os
|
||||
os.environ['SENTINEL_CONFIG'] = os.path.normpath(os.path.join(os.path.dirname(__file__), '../test_sentinel.conf'))
|
||||
sys.path.append(os.path.normpath(os.path.join(os.path.dirname(__file__), '../../lib')))
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def valid_dash_address(network='mainnet'):
|
||||
return 'seVNpH5rkau8dKhumiLF1BYspp2vf7Lkyb' if (network == 'testnet') else 'Sa9Vn2V4gtBFHovqVQh5V6dCt4ukMYUU2Z'
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def invalid_dash_address(network='mainnet'):
|
||||
return 'seVNpH5rkau8dKhumiLF1BYspp2vf7Lkyc' if (network == 'testnet') else 'Sa9Vn2V4gtBFHovqVQh5V6dCt4ukMYUU2Y'
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def current_block_hash():
|
||||
return '000001c9ba1df5a1c58a4e458fb6febfe9329b1947802cd60a4ae90dd754b534'
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mn_list():
|
||||
from masternode import Masternode
|
||||
|
||||
masternodelist_full = {
|
||||
u'701854b26809343704ab31d1c45abc08f9f83c5c2bd503a9d5716ef3c0cda857-1': u' ENABLED 70201 yjaFS6dudxUTxYPTDB9BYd1Nv4vMJXm3vK 1474157572 82842 1474152618 71111 52.90.74.124:19999',
|
||||
u'f68a2e5d64f4a9be7ff8d0fbd9059dcd3ce98ad7a19a9260d1d6709127ffac56-1': u' ENABLED 70201 yUuAsYCnG5XrjgsGvRwcDqPhgLUnzNfe8L 1474157732 1590425 1474155175 71122 [2604:a880:800:a1::9b:0]:19999',
|
||||
u'656695ed867e193490261bea74783f0a39329ff634a10a9fb6f131807eeca744-1': u' ENABLED 70201 yepN97UoBLoP2hzWnwWGRVTcWtw1niKwcB 1474157704 824622 1474152571 71110 178.62.203.249:19999',
|
||||
}
|
||||
|
||||
mnlist = [Masternode(vin, mnstring) for (vin, mnstring) in masternodelist_full.items()]
|
||||
|
||||
return mnlist
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mn_status_good():
|
||||
# valid masternode status enabled & running
|
||||
status = {
|
||||
"vin": "CTxIn(COutPoint(f68a2e5d64f4a9be7ff8d0fbd9059dcd3ce98ad7a19a9260d1d6709127ffac56, 1), scriptSig=)",
|
||||
"service": "[2604:a880:800:a1::9b:0]:19999",
|
||||
"pubkey": "yUuAsYCnG5XrjgsGvRwcDqPhgLUnzNfe8L",
|
||||
"status": "Masternode successfully started"
|
||||
}
|
||||
return status
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mn_status_bad():
|
||||
# valid masternode but not running/waiting
|
||||
status = {
|
||||
"vin": "CTxIn(COutPoint(0000000000000000000000000000000000000000000000000000000000000000, 4294967295), coinbase )",
|
||||
"service": "[::]:0",
|
||||
"status": "Node just started, not yet activated"
|
||||
}
|
||||
return status
|
||||
|
||||
|
||||
# ========================================================================
|
||||
|
||||
|
||||
def test_valid_dash_address():
|
||||
from dashlib import is_valid_address
|
||||
|
||||
main = valid_dash_address()
|
||||
test = valid_dash_address('testnet')
|
||||
|
||||
assert is_valid_address(main) is True
|
||||
assert is_valid_address(main, 'mainnet') is True
|
||||
assert is_valid_address(main, 'testnet') is False
|
||||
|
||||
assert is_valid_address(test) is False
|
||||
assert is_valid_address(test, 'mainnet') is False
|
||||
assert is_valid_address(test, 'testnet') is True
|
||||
|
||||
|
||||
def test_invalid_dash_address():
|
||||
from dashlib import is_valid_address
|
||||
|
||||
main = invalid_dash_address()
|
||||
test = invalid_dash_address('testnet')
|
||||
|
||||
assert is_valid_address(main) is False
|
||||
assert is_valid_address(main, 'mainnet') is False
|
||||
assert is_valid_address(main, 'testnet') is False
|
||||
|
||||
assert is_valid_address(test) is False
|
||||
assert is_valid_address(test, 'mainnet') is False
|
||||
assert is_valid_address(test, 'testnet') is False
|
||||
|
||||
|
||||
def test_deterministic_masternode_elections(current_block_hash, mn_list):
|
||||
winner = elect_mn(block_hash=current_block_hash, mnlist=mn_list)
|
||||
assert winner == 'f68a2e5d64f4a9be7ff8d0fbd9059dcd3ce98ad7a19a9260d1d6709127ffac56-1'
|
||||
|
||||
winner = elect_mn(block_hash='00000056bcd579fa3dc9a1ee41e8124a4891dcf2661aa3c07cc582bfb63b52b9', mnlist=mn_list)
|
||||
assert winner == '656695ed867e193490261bea74783f0a39329ff634a10a9fb6f131807eeca744-1'
|
||||
|
||||
|
||||
def test_deterministic_masternode_elections(current_block_hash, mn_list):
|
||||
from dashlib import elect_mn
|
||||
|
||||
winner = elect_mn(block_hash=current_block_hash, mnlist=mn_list)
|
||||
assert winner == 'f68a2e5d64f4a9be7ff8d0fbd9059dcd3ce98ad7a19a9260d1d6709127ffac56-1'
|
||||
|
||||
winner = elect_mn(block_hash='00000056bcd579fa3dc9a1ee41e8124a4891dcf2661aa3c07cc582bfb63b52b9', mnlist=mn_list)
|
||||
assert winner == '656695ed867e193490261bea74783f0a39329ff634a10a9fb6f131807eeca744-1'
|
||||
|
||||
|
||||
def test_parse_masternode_status_vin():
|
||||
from dashlib import parse_masternode_status_vin
|
||||
status = mn_status_good()
|
||||
vin = parse_masternode_status_vin(status['vin'])
|
||||
assert vin == 'f68a2e5d64f4a9be7ff8d0fbd9059dcd3ce98ad7a19a9260d1d6709127ffac56-1'
|
||||
|
||||
status = mn_status_bad()
|
||||
vin = parse_masternode_status_vin(status['vin'])
|
||||
assert vin is None
|
||||
|
||||
|
||||
def test_hash_function():
|
||||
import dashlib
|
||||
sb_data_hex = '7b226576656e745f626c6f636b5f686569676874223a2037323639362c20227061796d656e745f616464726573736573223a2022795965384b77796155753559737753596d42337133727978385854557539793755697c795965384b77796155753559737753596d4233713372797838585455753979375569222c20227061796d656e745f616d6f756e7473223a202232352e37353030303030307c32352e3735303030303030222c202274797065223a20327d'
|
||||
sb_hash = '7ae8b02730113382ea75cbb1eecc497c3aa1fdd9e76e875e38617e07fb2cb21a'
|
||||
|
||||
hex_hash = "%x" % dashlib.hashit(sb_data_hex)
|
||||
assert hex_hash == sb_hash
|
||||
|
||||
|
||||
def test_blocks_to_seconds():
|
||||
import dashlib
|
||||
from decimal import Decimal
|
||||
|
||||
precision = Decimal('0.001')
|
||||
assert Decimal(dashlib.blocks_to_seconds(0)) == Decimal(0.0)
|
||||
assert Decimal(dashlib.blocks_to_seconds(2)).quantize(precision) \
|
||||
== Decimal(314.4).quantize(precision)
|
||||
assert int(dashlib.blocks_to_seconds(16616)) == 2612035
|
||||
@@ -0,0 +1,110 @@
|
||||
import pytest
|
||||
import sys
|
||||
import os
|
||||
os.environ['SENTINEL_CONFIG'] = os.path.normpath(os.path.join(os.path.dirname(__file__), '../test_sentinel.conf'))
|
||||
sys.path.append(os.path.normpath(os.path.join(os.path.dirname(__file__), '../../lib')))
|
||||
import dashlib
|
||||
import gobject_json
|
||||
|
||||
|
||||
# old format proposal hex w/multi-dimensional array
|
||||
@pytest.fixture
|
||||
def proposal_hex_old():
|
||||
return "5b5b2270726f706f73616c222c207b22656e645f65706f6368223a20313534373138333939342c20226e616d65223a20226a61636b2d73706172726f772d6e65772d73686970222c20227061796d656e745f61646472657373223a2022795965384b77796155753559737753596d4233713372797838585455753979375569222c20227061796d656e745f616d6f756e74223a2034392c202273746172745f65706f6368223a20313532313432393139342c202274797065223a20312c202275726c223a202268747470733a2f2f7777772e6461736863656e7472616c2e6f72672f626c61636b2d706561726c227d5d5d"
|
||||
|
||||
|
||||
# same proposal data as old, but streamlined format
|
||||
@pytest.fixture
|
||||
def proposal_hex_new():
|
||||
return "7b22656e645f65706f6368223a20313534373138333939342c20226e616d65223a20226a61636b2d73706172726f772d6e65772d73686970222c20227061796d656e745f61646472657373223a2022795965384b77796155753559737753596d4233713372797838585455753979375569222c20227061796d656e745f616d6f756e74223a2034392c202273746172745f65706f6368223a20313532313432393139342c202274797065223a20312c202275726c223a202268747470733a2f2f7777772e6461736863656e7472616c2e6f72672f626c61636b2d706561726c227d"
|
||||
|
||||
|
||||
# old format trigger hex w/multi-dimensional array
|
||||
@pytest.fixture
|
||||
def trigger_hex_old():
|
||||
return "5b5b2274726967676572222c207b226576656e745f626c6f636b5f686569676874223a2036323530302c20227061796d656e745f616464726573736573223a2022795965384b77796155753559737753596d42337133727978385854557539793755697c795443363268755234595145506e39414a486a6e517878726548536267416f617456222c20227061796d656e745f616d6f756e7473223a2022357c33222c202274797065223a20327d5d5d"
|
||||
|
||||
|
||||
# same data as new, but simpler format
|
||||
@pytest.fixture
|
||||
def trigger_hex_new():
|
||||
return "7b226576656e745f626c6f636b5f686569676874223a2036323530302c20227061796d656e745f616464726573736573223a2022795965384b77796155753559737753596d42337133727978385854557539793755697c795443363268755234595145506e39414a486a6e517878726548536267416f617456222c20227061796d656e745f616d6f756e7473223a2022357c33222c202274797065223a20327d"
|
||||
|
||||
|
||||
def test_valid_json():
|
||||
import binascii
|
||||
|
||||
# test some valid JSON
|
||||
assert gobject_json.valid_json("{}") is True
|
||||
assert gobject_json.valid_json("null") is True
|
||||
assert gobject_json.valid_json("true") is True
|
||||
assert gobject_json.valid_json("false") is True
|
||||
assert gobject_json.valid_json("\"rubbish\"") is True
|
||||
assert gobject_json.valid_json(
|
||||
binascii.unhexlify(proposal_hex_old())
|
||||
) is True
|
||||
assert gobject_json.valid_json(
|
||||
binascii.unhexlify(proposal_hex_new())
|
||||
) is True
|
||||
assert gobject_json.valid_json(
|
||||
binascii.unhexlify(trigger_hex_new())
|
||||
) is True
|
||||
assert gobject_json.valid_json(
|
||||
binascii.unhexlify(trigger_hex_old())
|
||||
) is True
|
||||
|
||||
# test some invalid/bad/not JSON
|
||||
assert gobject_json.valid_json("False") is False
|
||||
assert gobject_json.valid_json("True") is False
|
||||
assert gobject_json.valid_json("Null") is False
|
||||
assert gobject_json.valid_json("NULL") is False
|
||||
assert gobject_json.valid_json("nil") is False
|
||||
assert gobject_json.valid_json("rubbish") is False
|
||||
assert gobject_json.valid_json("{{}") is False
|
||||
assert gobject_json.valid_json("") is False
|
||||
|
||||
poorly_formatted = trigger_hex_old() + "7d"
|
||||
assert gobject_json.valid_json(
|
||||
binascii.unhexlify(poorly_formatted)
|
||||
) is False
|
||||
|
||||
|
||||
def test_extract_object():
|
||||
from decimal import Decimal
|
||||
import binascii
|
||||
|
||||
# jack sparrow needs a new ship - same expected proposal data for both new &
|
||||
# old formats
|
||||
expected = {
|
||||
'type': 1,
|
||||
'name': 'jack-sparrow-new-ship',
|
||||
'url': 'https://www.dashcentral.org/black-pearl',
|
||||
'start_epoch': 1521429194,
|
||||
'end_epoch': 1547183994,
|
||||
'payment_address': 'yYe8KwyaUu5YswSYmB3q3ryx8XTUu9y7Ui',
|
||||
'payment_amount': Decimal('49'),
|
||||
}
|
||||
|
||||
# test proposal old format
|
||||
json_str = binascii.unhexlify(proposal_hex_old()).decode('utf-8')
|
||||
assert gobject_json.extract_object(json_str) == expected
|
||||
|
||||
# test proposal new format
|
||||
json_str = binascii.unhexlify(proposal_hex_new()).decode('utf-8')
|
||||
assert gobject_json.extract_object(json_str) == expected
|
||||
|
||||
# same expected trigger data for both new & old formats
|
||||
expected = {
|
||||
'type': 2,
|
||||
'event_block_height': 62500,
|
||||
'payment_addresses': 'yYe8KwyaUu5YswSYmB3q3ryx8XTUu9y7Ui|yTC62huR4YQEPn9AJHjnQxxreHSbgAoatV',
|
||||
'payment_amounts': '5|3',
|
||||
}
|
||||
|
||||
# test trigger old format
|
||||
json_str = binascii.unhexlify(trigger_hex_old()).decode('utf-8')
|
||||
assert gobject_json.extract_object(json_str) == expected
|
||||
|
||||
# test trigger new format
|
||||
json_str = binascii.unhexlify(trigger_hex_new()).decode('utf-8')
|
||||
assert gobject_json.extract_object(json_str) == expected
|
||||
@@ -0,0 +1,19 @@
|
||||
import pytest
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.normpath(os.path.join(os.path.dirname(__file__), '../../lib')))
|
||||
import misc
|
||||
|
||||
|
||||
def test_is_numeric():
|
||||
assert misc.is_numeric('45') is True
|
||||
assert misc.is_numeric('45.7') is True
|
||||
assert misc.is_numeric(0) is True
|
||||
assert misc.is_numeric(-1) is True
|
||||
|
||||
assert misc.is_numeric('45,7') is False
|
||||
assert misc.is_numeric('fuzzy_bunny_slippers') is False
|
||||
assert misc.is_numeric('') is False
|
||||
assert misc.is_numeric(None) is False
|
||||
assert misc.is_numeric(False) is False
|
||||
assert misc.is_numeric(True) is False
|
||||
@@ -0,0 +1,63 @@
|
||||
import pytest
|
||||
import os
|
||||
import sys
|
||||
os.environ['SENTINEL_CONFIG'] = os.path.normpath(os.path.join(os.path.dirname(__file__), '../test_sentinel.conf'))
|
||||
sys.path.append(os.path.normpath(os.path.join(os.path.dirname(__file__), '../../lib')))
|
||||
|
||||
# setup/teardown?
|
||||
|
||||
|
||||
# Proposal model
|
||||
@pytest.fixture
|
||||
def proposal():
|
||||
from models import Proposal
|
||||
return Proposal()
|
||||
|
||||
|
||||
def test_proposal(proposal):
|
||||
d = proposal.get_dict()
|
||||
assert isinstance(d, dict)
|
||||
|
||||
fields = [
|
||||
'type',
|
||||
'name',
|
||||
'url',
|
||||
'start_epoch',
|
||||
'end_epoch',
|
||||
'payment_address',
|
||||
'payment_amount',
|
||||
]
|
||||
fields.sort()
|
||||
sorted_keys = sorted(d.keys())
|
||||
assert sorted_keys == fields
|
||||
|
||||
|
||||
# GovernanceObject model
|
||||
@pytest.fixture
|
||||
def governance_object():
|
||||
from models import GovernanceObject
|
||||
return GovernanceObject()
|
||||
|
||||
|
||||
def test_governance_object(governance_object):
|
||||
d = governance_object._meta.columns
|
||||
assert isinstance(d, dict)
|
||||
|
||||
fields = [
|
||||
'id',
|
||||
'parent_id',
|
||||
'object_creation_time',
|
||||
'object_hash',
|
||||
'object_parent_hash',
|
||||
'object_type',
|
||||
'object_revision',
|
||||
'object_fee_tx',
|
||||
'yes_count',
|
||||
'no_count',
|
||||
'abstain_count',
|
||||
'absolute_yes_count',
|
||||
]
|
||||
|
||||
fields.sort()
|
||||
sorted_keys = sorted(d.keys())
|
||||
assert sorted_keys == fields
|
||||
@@ -0,0 +1,37 @@
|
||||
import pytest
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
os.environ['SENTINEL_ENV'] = 'test'
|
||||
os.environ['SENTINEL_CONFIG'] = os.path.normpath(os.path.join(os.path.dirname(__file__), '../test_sentinel.conf'))
|
||||
sys.path.append(os.path.normpath(os.path.join(os.path.dirname(__file__), '../../lib')))
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def superblock():
|
||||
from models import Superblock
|
||||
# NOTE: no governance_object_id is set
|
||||
sbobj = Superblock(
|
||||
event_block_height=62500,
|
||||
payment_addresses='yYe8KwyaUu5YswSYmB3q3ryx8XTUu9y7Ui|yTC62huR4YQEPn9AJHjnQxxreHSbgAoatV',
|
||||
payment_amounts='5|3',
|
||||
proposal_hashes='e8a0057914a2e1964ae8a945c4723491caae2077a90a00a2aabee22b40081a87|d1ce73527d7cd6f2218f8ca893990bc7d5c6b9334791ce7973bfa22f155f826e',
|
||||
)
|
||||
|
||||
return sbobj
|
||||
|
||||
|
||||
def test_submit_command(superblock):
|
||||
cmd = superblock.get_submit_command()
|
||||
|
||||
assert re.match(r'^gobject$', cmd[0]) is not None
|
||||
assert re.match(r'^submit$', cmd[1]) is not None
|
||||
assert re.match(r'^[\da-f]+$', cmd[2]) is not None
|
||||
assert re.match(r'^[\da-f]+$', cmd[3]) is not None
|
||||
assert re.match(r'^[\d]+$', cmd[4]) is not None
|
||||
assert re.match(r'^[\w-]+$', cmd[5]) is not None
|
||||
|
||||
submit_time = cmd[4]
|
||||
|
||||
gobject_command = ['gobject', 'submit', '0', '1', submit_time, '7b226576656e745f626c6f636b5f686569676874223a2036323530302c20227061796d656e745f616464726573736573223a2022795965384b77796155753559737753596d42337133727978385854557539793755697c795443363268755234595145506e39414a486a6e517878726548536267416f617456222c20227061796d656e745f616d6f756e7473223a2022357c33222c202270726f706f73616c5f686173686573223a2022653861303035373931346132653139363461653861393435633437323334393163616165323037376139306130306132616162656532326234303038316138377c64316365373335323764376364366632323138663863613839333939306263376435633662393333343739316365373937336266613232663135356638323665222c202274797065223a20327d']
|
||||
assert cmd == gobject_command
|
||||
Reference in New Issue
Block a user