Skip to content

Commit

Permalink
Merge pull request #2735 from codablock/pr_llmq_instantsend
Browse files Browse the repository at this point in the history
Implement LLMQ based InstantSend
  • Loading branch information
codablock committed Mar 8, 2019
2 parents e47af29 + 06fc655 commit 64ae912
Show file tree
Hide file tree
Showing 36 changed files with 1,597 additions and 96 deletions.
23 changes: 21 additions & 2 deletions qa/rpc-tests/autoix-mempool.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,21 @@ def fill_mempool(self):
def run_test(self):
# make sure masternodes are synced
sync_masternodes(self.nodes)

self.nodes[0].spork("SPORK_17_QUORUM_DKG_ENABLED", 0)
self.wait_for_sporks_same()
self.mine_quorum()

print("Test old InstantSend")
self.test_auto();

self.nodes[0].spork("SPORK_20_INSTANTSEND_LLMQ_BASED", 0)
self.wait_for_sporks_same()

print("Test new InstantSend")
self.test_auto(True);

def test_auto(self, new_is = False):
self.activate_autoix_bip9()
self.set_autoix_spork_state(True)

Expand All @@ -146,13 +161,17 @@ def run_test(self):

# fill mempool with transactions
self.set_autoix_spork_state(False)
self.nodes[0].spork("SPORK_2_INSTANTSEND_ENABLED", 4070908800)
self.wait_for_sporks_same()
self.fill_mempool()
self.set_autoix_spork_state(True)
self.nodes[0].spork("SPORK_2_INSTANTSEND_ENABLED", 0)
self.wait_for_sporks_same()

# autoIX is not working now
assert(not self.send_simple_tx(sender, receiver))
# regular IX is still working
assert(self.send_regular_IX(sender, receiver))
# regular IX is still working for old IS but not for new one
assert(not self.send_regular_IX(sender, receiver) if new_is else self.send_regular_IX(sender, receiver))

# generate one block to clean up mempool and retry auto and regular IX
# generate 2 more blocks to have enough confirmations for IX
Expand Down
42 changes: 29 additions & 13 deletions qa/rpc-tests/p2p-autoinstantsend.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,14 @@ def set_autoix_spork_state(self, state):
self.nodes[0].spork('SPORK_16_INSTANTSEND_AUTOLOCKS', value)

# sends regular IX with high fee and may inputs (not-simple transaction)
def send_regular_IX(self):
def send_regular_IX(self, check_fee = True):
receiver_addr = self.nodes[self.receiver_idx].getnewaddress()
txid = self.nodes[0].instantsendtoaddress(receiver_addr, 1.0)
MIN_FEE = satoshi_round(-0.0001)
fee = self.nodes[0].gettransaction(txid)['fee']
expected_fee = MIN_FEE * len(self.nodes[0].getrawtransaction(txid, True)['vin'])
assert_equal(fee, expected_fee)
if (check_fee):
MIN_FEE = satoshi_round(-0.0001)
fee = self.nodes[0].gettransaction(txid)['fee']
expected_fee = MIN_FEE * len(self.nodes[0].getrawtransaction(txid, True)['vin'])
assert_equal(fee, expected_fee)
return self.wait_for_instantlock(txid, self.nodes[0])

# sends simple trx, it should become IX if autolocks are allowed
Expand All @@ -115,6 +116,21 @@ def send_complex_tx(self):
def run_test(self):
# make sure masternodes are synced
sync_masternodes(self.nodes)

self.nodes[0].spork("SPORK_17_QUORUM_DKG_ENABLED", 0)
self.wait_for_sporks_same()
self.mine_quorum()

print("Test old InstantSend")
self.test_auto();

self.nodes[0].spork("SPORK_20_INSTANTSEND_LLMQ_BASED", 0)
self.wait_for_sporks_same()

print("Test new InstantSend")
self.test_auto(True);

def test_auto(self, new_is = False):
# feed the sender with some balance
sender_addr = self.nodes[self.sender_idx].getnewaddress()
self.nodes[0].sendtoaddress(sender_addr, 1)
Expand All @@ -126,26 +142,26 @@ def run_test(self):

assert(not self.get_autoix_spork_state())

assert(self.send_regular_IX())
assert(not self.send_simple_tx())
assert(not self.send_complex_tx())
assert(self.send_regular_IX(not new_is))
assert(self.send_simple_tx() if new_is else not self.send_simple_tx())
assert(self.send_complex_tx() if new_is else not self.send_complex_tx())

self.activate_autoix_bip9()
self.set_autoix_spork_state(True)

assert(self.get_autoix_bip9_status() == 'active')
assert(self.get_autoix_spork_state())

assert(self.send_regular_IX())
assert(self.send_regular_IX(not new_is))
assert(self.send_simple_tx())
assert(not self.send_complex_tx())
assert(self.send_complex_tx() if new_is else not self.send_complex_tx())

self.set_autoix_spork_state(False)
assert(not self.get_autoix_spork_state())

assert(self.send_regular_IX())
assert(not self.send_simple_tx())
assert(not self.send_complex_tx())
assert(self.send_regular_IX(not new_is))
assert(self.send_simple_tx() if new_is else not self.send_simple_tx())
assert(self.send_complex_tx() if new_is else not self.send_complex_tx())


if __name__ == '__main__':
Expand Down
21 changes: 20 additions & 1 deletion qa/rpc-tests/p2p-instantsend.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,20 @@ def __init__(self):
self.sender_idx = self.num_nodes - 3

def run_test(self):
self.nodes[0].spork("SPORK_17_QUORUM_DKG_ENABLED", 0)
self.wait_for_sporks_same()
self.mine_quorum()

print("Test old InstantSend")
self.test_doublespend()

self.nodes[0].spork("SPORK_20_INSTANTSEND_LLMQ_BASED", 0)
self.wait_for_sporks_same()

print("Test new InstantSend")
self.test_doublespend()

def test_doublespend(self):
# feed the sender with some balance
sender_addr = self.nodes[self.sender_idx].getnewaddress()
self.nodes[0].sendtoaddress(sender_addr, 1)
Expand Down Expand Up @@ -73,7 +87,12 @@ def run_test(self):
assert (res['hash'] != wrong_block)
# wait for long time only for first node
timeout = 1

# mine more blocks
# TODO: mine these blocks on an isolated node
set_mocktime(get_mocktime() + 1)
set_node_times(self.nodes, get_mocktime())
self.nodes[0].generate(2)
self.sync_all()

if __name__ == '__main__':
InstantSendTest().main()
9 changes: 9 additions & 0 deletions qa/rpc-tests/test_framework/test_framework.py
Original file line number Diff line number Diff line change
Expand Up @@ -498,36 +498,44 @@ def mine_quorum(self, expected_valid_count=10):
set_mocktime(get_mocktime() + 1)
set_node_times(self.nodes, get_mocktime())
self.nodes[0].generate(skip_count)
sync_blocks(self.nodes)

# Make sure all reached phase 1 (init)
self.wait_for_quorum_phase(1, None, 0)
# Give nodes some time to connect to neighbors
sleep(2)
set_mocktime(get_mocktime() + 1)
set_node_times(self.nodes, get_mocktime())
self.nodes[0].generate(2)
sync_blocks(self.nodes)

# Make sure all reached phase 2 (contribute) and received all contributions
self.wait_for_quorum_phase(2, "receivedContributions", expected_valid_count)
set_mocktime(get_mocktime() + 1)
set_node_times(self.nodes, get_mocktime())
self.nodes[0].generate(2)
sync_blocks(self.nodes)

# Make sure all reached phase 3 (complain) and received all complaints
self.wait_for_quorum_phase(3, "receivedComplaints" if expected_valid_count != 10 else None, expected_valid_count)
set_mocktime(get_mocktime() + 1)
set_node_times(self.nodes, get_mocktime())
self.nodes[0].generate(2)
sync_blocks(self.nodes)

# Make sure all reached phase 4 (justify)
self.wait_for_quorum_phase(4, None, 0)
set_mocktime(get_mocktime() + 1)
set_node_times(self.nodes, get_mocktime())
self.nodes[0].generate(2)
sync_blocks(self.nodes)

# Make sure all reached phase 5 (commit)
self.wait_for_quorum_phase(5, "receivedPrematureCommitments", expected_valid_count)
set_mocktime(get_mocktime() + 1)
set_node_times(self.nodes, get_mocktime())
self.nodes[0].generate(2)
sync_blocks(self.nodes)

# Make sure all reached phase 6 (mining)
self.wait_for_quorum_phase(6, None, 0)
Expand All @@ -544,6 +552,7 @@ def mine_quorum(self, expected_valid_count=10):
set_mocktime(get_mocktime() + 1)
set_node_times(self.nodes, get_mocktime())
self.nodes[0].generate(1)
sync_blocks(self.nodes)

sync_blocks(self.nodes)

Expand Down
2 changes: 2 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ BITCOIN_CORE_H = \
llmq/quorums_dkgsessionmgr.h \
llmq/quorums_dkgsession.h \
llmq/quorums_init.h \
llmq/quorums_instantsend.h \
llmq/quorums_signing.h \
llmq/quorums_signing_shares.h \
llmq/quorums_utils.h \
Expand Down Expand Up @@ -291,6 +292,7 @@ libdash_server_a_SOURCES = \
llmq/quorums_dkgsessionmgr.cpp \
llmq/quorums_dkgsession.cpp \
llmq/quorums_init.cpp \
llmq/quorums_instantsend.cpp \
llmq/quorums_signing.cpp \
llmq/quorums_signing_shares.cpp \
llmq/quorums_utils.cpp \
Expand Down
4 changes: 4 additions & 0 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ class CMainParams : public CChainParams {
consensus.llmqs[Consensus::LLMQ_400_60] = llmq400_60;
consensus.llmqs[Consensus::LLMQ_400_85] = llmq400_85;
consensus.llmqChainLocks = Consensus::LLMQ_400_60;
consensus.llmqForInstantSend = Consensus::LLMQ_50_60;

fMiningRequiresPeers = true;
fDefaultConsistencyChecks = false;
Expand Down Expand Up @@ -476,6 +477,7 @@ class CTestNetParams : public CChainParams {
consensus.llmqs[Consensus::LLMQ_400_60] = llmq400_60;
consensus.llmqs[Consensus::LLMQ_400_85] = llmq400_85;
consensus.llmqChainLocks = Consensus::LLMQ_50_60;
consensus.llmqForInstantSend = Consensus::LLMQ_50_60;

fMiningRequiresPeers = true;
fDefaultConsistencyChecks = false;
Expand Down Expand Up @@ -623,6 +625,7 @@ class CDevNetParams : public CChainParams {
consensus.llmqs[Consensus::LLMQ_400_60] = llmq400_60;
consensus.llmqs[Consensus::LLMQ_400_85] = llmq400_85;
consensus.llmqChainLocks = Consensus::LLMQ_50_60;
consensus.llmqForInstantSend = Consensus::LLMQ_50_60;

fMiningRequiresPeers = true;
fDefaultConsistencyChecks = false;
Expand Down Expand Up @@ -787,6 +790,7 @@ class CRegTestParams : public CChainParams {
consensus.llmqs[Consensus::LLMQ_10_60] = llmq10_60;
consensus.llmqs[Consensus::LLMQ_50_60] = llmq50_60;
consensus.llmqChainLocks = Consensus::LLMQ_10_60;
consensus.llmqForInstantSend = Consensus::LLMQ_10_60;
}

void UpdateBIP9Parameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout, int64_t nWindowSize, int64_t nThreshold)
Expand Down
1 change: 1 addition & 0 deletions src/consensus/params.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ struct Params {

std::map<LLMQType, LLMQParams> llmqs;
LLMQType llmqChainLocks;
LLMQType llmqForInstantSend{LLMQ_NONE};
};
} // namespace Consensus

Expand Down
13 changes: 13 additions & 0 deletions src/dsnotificationinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#include "llmq/quorums.h"
#include "llmq/quorums_chainlocks.h"
#include "llmq/quorums_instantsend.h"
#include "llmq/quorums_dkgsessionmgr.h"

void CDSNotificationInterface::InitializeCurrentBlockTip()
Expand Down Expand Up @@ -70,8 +71,15 @@ void CDSNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindexNew, con
llmq::quorumDKGSessionManager->UpdatedBlockTip(pindexNew, pindexFork, fInitialDownload);
}

void CDSNotificationInterface::NewPoWValidBlock(const CBlockIndex* pindex, const std::shared_ptr<const CBlock>& block)
{
llmq::chainLocksHandler->NewPoWValidBlock(pindex, block);
}

void CDSNotificationInterface::SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, int posInBlock)
{
llmq::quorumInstantSendManager->SyncTransaction(tx, pindex, posInBlock);
llmq::chainLocksHandler->SyncTransaction(tx, pindex, posInBlock);
instantsend.SyncTransaction(tx, pindex, posInBlock);
CPrivateSend::SyncTransaction(tx, pindex, posInBlock);
}
Expand All @@ -82,3 +90,8 @@ void CDSNotificationInterface::NotifyMasternodeListChanged(const CDeterministicM
governance.CheckMasternodeOrphanVotes(connman);
governance.UpdateCachesAndClean();
}

void CDSNotificationInterface::NotifyChainLock(const CBlockIndex* pindex)
{
llmq::quorumInstantSendManager->NotifyChainLock(pindex);
}
2 changes: 2 additions & 0 deletions src/dsnotificationinterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ class CDSNotificationInterface : public CValidationInterface
void AcceptedBlockHeader(const CBlockIndex *pindexNew) override;
void NotifyHeaderTip(const CBlockIndex *pindexNew, bool fInitialDownload) override;
void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override;
void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& block) override;
void SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, int posInBlock) override;
void NotifyMasternodeListChanged(const CDeterministicMNList& newList) override;
void NotifyChainLock(const CBlockIndex* pindex) override;

private:
CConnman& connman;
Expand Down
4 changes: 3 additions & 1 deletion src/governance-object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
#include "util.h"
#include "validation.h"

#include "llmq/quorums_instantsend.h"

#include <string>
#include <univalue.h>

Expand Down Expand Up @@ -618,7 +620,7 @@ bool CGovernanceObject::IsCollateralValid(std::string& strError, bool& fMissingC
}

if ((nConfirmationsIn < GOVERNANCE_FEE_CONFIRMATIONS) &&
(!instantsend.IsLockedInstantSendTransaction(nCollateralHash))) {
(!instantsend.IsLockedInstantSendTransaction(nCollateralHash) || llmq::quorumInstantSendManager->IsLocked(nCollateralHash))) {
strError = strprintf("Collateral requires at least %d confirmations to be relayed throughout the network (it has only %d)", GOVERNANCE_FEE_CONFIRMATIONS, nConfirmationsIn);
if (nConfirmationsIn >= GOVERNANCE_MIN_RELAY_FEE_CONFIRMATIONS) {
fMissingConfirmations = true;
Expand Down
Loading

0 comments on commit 64ae912

Please sign in to comment.