diff --git a/src/amount.h b/src/amount.h index 40c47e978..4405e9aab 100644 --- a/src/amount.h +++ b/src/amount.h @@ -1,170 +1,165 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Copyright (c) 2017-2019 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_AMOUNT_H #define BITCOIN_AMOUNT_H #include #include #include #include #include struct Amount { private: int64_t amount; explicit constexpr Amount(int64_t _amount) : amount(_amount) {} public: constexpr Amount() : amount(0) {} constexpr Amount(const Amount &other) : amount(other.amount) {} /** * Assignement operator. */ constexpr Amount &operator=(const Amount &other) { amount = other.amount; return *this; } static constexpr Amount zero() { return Amount(0); } static constexpr Amount satoshi() { return Amount(1); } /** * Implement standard operators */ Amount &operator+=(const Amount a) { amount += a.amount; return *this; } Amount &operator-=(const Amount a) { amount -= a.amount; return *this; } /** * Equality */ friend constexpr bool operator==(const Amount a, const Amount b) { return a.amount == b.amount; } friend constexpr bool operator!=(const Amount a, const Amount b) { return !(a == b); } /** * Comparison */ friend constexpr bool operator<(const Amount a, const Amount b) { return a.amount < b.amount; } friend constexpr bool operator>(const Amount a, const Amount b) { return b < a; } friend constexpr bool operator<=(const Amount a, const Amount b) { return !(a > b); } friend constexpr bool operator>=(const Amount a, const Amount b) { return !(a < b); } /** * Unary minus */ constexpr Amount operator-() const { return Amount(-amount); } /** * Addition and subtraction. */ friend constexpr Amount operator+(const Amount a, const Amount b) { return Amount(a.amount + b.amount); } friend constexpr Amount operator-(const Amount a, const Amount b) { return a + -b; } /** * Multiplication */ friend constexpr Amount operator*(const int64_t a, const Amount b) { return Amount(a * b.amount); } friend constexpr Amount operator*(const int a, const Amount b) { return Amount(a * b.amount); } /** * Division */ constexpr int64_t operator/(const Amount b) const { return amount / b.amount; } constexpr Amount operator/(const int64_t b) const { return Amount(amount / b); } constexpr Amount operator/(const int b) const { return Amount(amount / b); } Amount &operator/=(const int64_t n) { amount /= n; return *this; } /** * Modulus */ constexpr Amount operator%(const Amount b) const { return Amount(amount % b.amount); } constexpr Amount operator%(const int64_t b) const { return Amount(amount % b); } constexpr Amount operator%(const int b) const { return Amount(amount % b); } /** * Do not implement double ops to get an error with double and ensure * casting to integer is explicit. */ friend constexpr Amount operator*(const double a, const Amount b) = delete; constexpr Amount operator/(const double b) const = delete; constexpr Amount operator%(const double b) const = delete; // ostream support friend std::ostream &operator<<(std::ostream &stream, const Amount &ca) { return stream << ca.amount; } std::string ToString() const; // serialization support - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream &s, Operation ser_action) { - READWRITE(amount); - } + SERIALIZE_METHODS(Amount, obj) { READWRITE(obj.amount); } }; static constexpr Amount SATOSHI = Amount::satoshi(); static constexpr Amount CASH = 100 * SATOSHI; static constexpr Amount COIN = 100000000 * SATOSHI; /** * No amount larger than this (in satoshi) is valid. * * Note that this constant is *not* the total money supply, which in Bitcoin * currently happens to be less than 21,000,000 BCH for various reasons, but * rather a sanity check. As this sanity check is used by consensus-critical * validation code, the exact value of the MAX_MONEY constant is consensus * critical; in unusual circumstances like a(nother) overflow bug that allowed * for the creation of coins out of thin air modification could lead to a fork. */ static const Amount MAX_MONEY = 21000000 * COIN; inline bool MoneyRange(const Amount nValue) { return nValue >= Amount::zero() && nValue <= MAX_MONEY; } #endif // BITCOIN_AMOUNT_H diff --git a/src/avalanche/delegation.h b/src/avalanche/delegation.h index 41656a12c..e193c4e68 100644 --- a/src/avalanche/delegation.h +++ b/src/avalanche/delegation.h @@ -1,70 +1,57 @@ // Copyright (c) 2020 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_AVALANCHE_DELEGATION_H #define BITCOIN_AVALANCHE_DELEGATION_H #include #include #include #include #include namespace avalanche { class DelegationState; class Proof; class Delegation { ProofId proofid; DelegationId dgid; DelegationId computeDelegationId() const; struct Level { CPubKey pubkey; std::array sig; - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream &s, Operation ser_action) { - READWRITE(pubkey); - READWRITE(sig); - } + SERIALIZE_METHODS(Level, obj) { READWRITE(obj.pubkey, obj.sig); } }; std::vector levels; friend class DelegationBuilder; Delegation(const ProofId &proofid_, const DelegationId &dgid_, std::vector levels_) : proofid(proofid_), dgid(dgid_), levels(std::move(levels_)) {} public: explicit Delegation() {} const DelegationId &getId() const { return dgid; } const ProofId &getProofId() const { return proofid; } - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream &s, Operation ser_action) { - READWRITE(proofid); - READWRITE(levels); - - if (ser_action.ForRead()) { - dgid = computeDelegationId(); - } + SERIALIZE_METHODS(Delegation, obj) { + READWRITE(obj.proofid, obj.levels); + SER_READ(obj, obj.dgid = obj.computeDelegationId()); } bool verify(DelegationState &state, const Proof &proof, CPubKey &auth) const; }; } // namespace avalanche #endif // BITCOIN_AVALANCHE_DELEGATION_H diff --git a/src/avalanche/processor.cpp b/src/avalanche/processor.cpp index 969a9ff9d..2a4cf4be4 100644 --- a/src/avalanche/processor.cpp +++ b/src/avalanche/processor.cpp @@ -1,594 +1,590 @@ // Copyright (c) 2018-2019 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include // For DecodeSecret #include // For ::PeerManager #include #include #include #include #include #include #include /** * Run the avalanche event loop every 10ms. */ static constexpr std::chrono::milliseconds AVALANCHE_TIME_STEP{10}; // Unfortunately, the bitcoind codebase is full of global and we are kinda // forced into it here. std::unique_ptr g_avalanche; namespace avalanche { bool VoteRecord::registerVote(NodeId nodeid, uint32_t error) { // We just got a new vote, so there is one less inflight request. clearInflightRequest(); // We want to avoid having the same node voting twice in a quorum. if (!addNodeToQuorum(nodeid)) { return false; } /** * The result of the vote is determined from the error code. If the error * code is 0, there is no error and therefore the vote is yes. If there is * an error, we check the most significant bit to decide if the vote is a no * (for instance, the block is invalid) or is the vote inconclusive (for * instance, the queried node does not have the block yet). */ votes = (votes << 1) | (error == 0); consider = (consider << 1) | (int32_t(error) >= 0); /** * We compute the number of yes and/or no votes as follow: * * votes: 1010 * consider: 1100 * * yes votes: 1000 using votes & consider * no votes: 0100 using ~votes & consider */ bool yes = countBits(votes & consider & 0xff) > 6; if (!yes) { bool no = countBits(~votes & consider & 0xff) > 6; if (!no) { // The round is inconclusive. return false; } } // If the round is in agreement with previous rounds, increase confidence. if (isAccepted() == yes) { confidence += 2; return getConfidence() == AVALANCHE_FINALIZATION_SCORE; } // The round changed our state. We reset the confidence. confidence = yes; return true; } bool VoteRecord::addNodeToQuorum(NodeId nodeid) { if (nodeid == NO_NODE) { // Helpful for testing. return true; } // MMIX Linear Congruent Generator. const uint64_t r1 = 6364136223846793005 * uint64_t(nodeid) + 1442695040888963407; // Fibonacci hashing. const uint64_t r2 = 11400714819323198485ull * (nodeid ^ seed); // Combine and extract hash. const uint16_t h = (r1 + r2) >> 48; /** * Check if the node is in the filter. */ for (size_t i = 1; i < nodeFilter.size(); i++) { if (nodeFilter[(successfulVotes + i) % nodeFilter.size()] == h) { return false; } } /** * Add the node which just voted to the filter. */ nodeFilter[successfulVotes % nodeFilter.size()] = h; successfulVotes++; return true; } bool VoteRecord::registerPoll() const { uint8_t count = inflight.load(); while (count < AVALANCHE_MAX_INFLIGHT_POLL) { if (inflight.compare_exchange_weak(count, count + 1)) { return true; } } return false; } static bool IsWorthPolling(const CBlockIndex *pindex) { AssertLockHeld(cs_main); if (pindex->nStatus.isInvalid()) { // No point polling invalid blocks. return false; } if (::ChainstateActive().IsBlockFinalized(pindex)) { // There is no point polling finalized block. return false; } return true; } struct Processor::PeerData { Proof proof; Delegation delegation; }; class Processor::NotificationsHandler : public interfaces::Chain::Notifications { Processor *m_processor; public: NotificationsHandler(Processor *p) : m_processor(p) {} void updatedBlockTip() override { LOCK(m_processor->cs_peerManager); m_processor->peerManager->updatedBlockTip(); } }; Processor::Processor(interfaces::Chain &chain, CConnman *connmanIn, NodePeerManager *nodePeerManagerIn) : connman(connmanIn), nodePeerManager(nodePeerManagerIn), queryTimeoutDuration(AVALANCHE_DEFAULT_QUERY_TIMEOUT), round(0), peerManager(std::make_unique()) { if (gArgs.IsArgSet("-avasessionkey")) { sessionKey = DecodeSecret(gArgs.GetArg("-avasessionkey", "")); } else { // Pick a random key for the session. sessionKey.MakeNewKey(true); } if (gArgs.IsArgSet("-avaproof")) { peerData = std::make_unique(); { // The proof. CDataStream stream(ParseHex(gArgs.GetArg("-avaproof", "")), SER_NETWORK, 0); stream >> peerData->proof; // Ensure the peer manager knows about it. // FIXME: There is no way to register the proof at this time because // we might not have the proper chainstate at the moment. We need to // find a way to delay the registration of the proof until after IBD // has finished and the chain state is settled. // LOCK(cs_peerManager); // peerManager->getPeerId(peerData->proof); } // Generate the delegation to the session key. DelegationBuilder dgb(peerData->proof); if (sessionKey.GetPubKey() != peerData->proof.getMaster()) { dgb.addLevel(DecodeSecret(gArgs.GetArg("-avamasterkey", "")), sessionKey.GetPubKey()); } peerData->delegation = dgb.build(); } // Make sure we get notified of chain state changes. chainNotificationsHandler = chain.handleNotifications(std::make_shared(this)); } Processor::~Processor() { chainNotificationsHandler.reset(); stopEventLoop(); } bool Processor::addBlockToReconcile(const CBlockIndex *pindex) { bool isAccepted; { LOCK(cs_main); if (!IsWorthPolling(pindex)) { // There is no point polling this block. return false; } isAccepted = ::ChainActive().Contains(pindex); } return vote_records.getWriteView() ->insert(std::make_pair(pindex, VoteRecord(isAccepted))) .second; } bool Processor::isAccepted(const CBlockIndex *pindex) const { auto r = vote_records.getReadView(); auto it = r->find(pindex); if (it == r.end()) { return false; } return it->second.isAccepted(); } int Processor::getConfidence(const CBlockIndex *pindex) const { auto r = vote_records.getReadView(); auto it = r->find(pindex); if (it == r.end()) { return -1; } return it->second.getConfidence(); } namespace { /** * When using TCP, we need to sign all messages as the transport layer is * not secure. */ class TCPResponse { Response response; std::array sig; public: TCPResponse(Response responseIn, const CKey &key) : response(std::move(responseIn)) { CHashWriter hasher(SER_GETHASH, 0); hasher << response; const uint256 hash = hasher.GetHash(); // Now let's sign! if (!key.SignSchnorr(hash, sig)) { sig.fill(0); } } // serialization support - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream &s, Operation ser_action) { - READWRITE(response); - READWRITE(sig); + SERIALIZE_METHODS(TCPResponse, obj) { + READWRITE(obj.response, obj.sig); } }; } // namespace void Processor::sendResponse(CNode *pfrom, Response response) const { connman->PushMessage( pfrom, CNetMsgMaker(pfrom->GetCommonVersion()) .Make(NetMsgType::AVARESPONSE, TCPResponse(std::move(response), sessionKey))); } bool Processor::registerVotes(NodeId nodeid, const Response &response, std::vector &updates) { { // Save the time at which we can query again. LOCK(cs_peerManager); // FIXME: This will override the time even when we received an old stale // message. This should check that the message is indeed the most up to // date one before updating the time. peerManager->updateNextRequestTime( nodeid, std::chrono::steady_clock::now() + std::chrono::milliseconds(response.getCooldown())); } std::vector invs; { // Check that the query exists. auto w = queries.getWriteView(); auto it = w->find(std::make_tuple(nodeid, response.getRound())); if (it == w.end()) { nodePeerManager->Misbehaving(nodeid, 2, "unexpcted-ava-response"); return false; } invs = std::move(it->invs); w->erase(it); } // Verify that the request and the vote are consistent. const std::vector &votes = response.GetVotes(); size_t size = invs.size(); if (votes.size() != size) { nodePeerManager->Misbehaving(nodeid, 100, "invalid-ava-response-size"); return false; } for (size_t i = 0; i < size; i++) { if (invs[i].hash != votes[i].GetHash()) { nodePeerManager->Misbehaving(nodeid, 100, "invalid-ava-response-content"); return false; } } std::map responseIndex; { LOCK(cs_main); for (const auto &v : votes) { auto pindex = LookupBlockIndex(BlockHash(v.GetHash())); if (!pindex) { // This should not happen, but just in case... continue; } if (!IsWorthPolling(pindex)) { // There is no point polling this block. continue; } responseIndex.insert(std::make_pair(pindex, v)); } } { // Register votes. auto w = vote_records.getWriteView(); for (const auto &p : responseIndex) { CBlockIndex *pindex = p.first; const Vote &v = p.second; auto it = w->find(pindex); if (it == w.end()) { // We are not voting on that item anymore. continue; } auto &vr = it->second; if (!vr.registerVote(nodeid, v.GetError())) { // This vote did not provide any extra information, move on. continue; } if (!vr.hasFinalized()) { // This item has note been finalized, so we have nothing more to // do. updates.emplace_back( pindex, vr.isAccepted() ? BlockUpdate::Status::Accepted : BlockUpdate::Status::Rejected); continue; } // We just finalized a vote. If it is valid, then let the caller // know. Either way, remove the item from the map. updates.emplace_back(pindex, vr.isAccepted() ? BlockUpdate::Status::Finalized : BlockUpdate::Status::Invalid); w->erase(it); } } return true; } bool Processor::addNode(NodeId nodeid, const Proof &proof, const Delegation &delegation) { LOCK(cs_peerManager); return peerManager->addNode(nodeid, proof, delegation); } bool Processor::forNode(NodeId nodeid, std::function func) const { LOCK(cs_peerManager); return peerManager->forNode(nodeid, std::move(func)); } CPubKey Processor::getSessionPubKey() const { return sessionKey.GetPubKey(); } bool Processor::sendHello(CNode *pfrom) const { if (!peerData) { // We do not have a delegation to advertise. return false; } // Now let's sign! std::array sig; { CHashWriter hasher(SER_GETHASH, 0); hasher << peerData->delegation.getId(); hasher << pfrom->GetLocalNonce(); hasher << pfrom->nRemoteHostNonce; hasher << pfrom->GetLocalExtraEntropy(); hasher << pfrom->nRemoteExtraEntropy; const uint256 hash = hasher.GetHash(); if (!sessionKey.SignSchnorr(hash, sig)) { return false; } } connman->PushMessage(pfrom, CNetMsgMaker(pfrom->GetCommonVersion()) .Make(NetMsgType::AVAHELLO, Hello(peerData->delegation, sig))); return true; } bool Processor::startEventLoop(CScheduler &scheduler) { return eventLoop.startEventLoop( scheduler, [this]() { this->runEventLoop(); }, AVALANCHE_TIME_STEP); } bool Processor::stopEventLoop() { return eventLoop.stopEventLoop(); } std::vector Processor::getInvsForNextPoll(bool forPoll) { std::vector invs; // First remove all blocks that are not worth polling. { LOCK(cs_main); auto w = vote_records.getWriteView(); for (auto it = w->begin(); it != w->end();) { const CBlockIndex *pindex = it->first; if (!IsWorthPolling(pindex)) { w->erase(it++); } else { ++it; } } } auto r = vote_records.getReadView(); for (const std::pair &p : reverse_iterate(r)) { // Check if we can run poll. const bool shouldPoll = forPoll ? p.second.registerPoll() : p.second.shouldPoll(); if (!shouldPoll) { continue; } // We don't have a decision, we need more votes. invs.emplace_back(MSG_BLOCK, p.first->GetBlockHash()); if (invs.size() >= AVALANCHE_MAX_ELEMENT_POLL) { // Make sure we do not produce more invs than specified by the // protocol. return invs; } } return invs; } NodeId Processor::getSuitableNodeToQuery() { LOCK(cs_peerManager); return peerManager->selectNode(); } void Processor::clearTimedoutRequests() { auto now = std::chrono::steady_clock::now(); std::map timedout_items{}; { // Clear expired requests. auto w = queries.getWriteView(); auto it = w->get().begin(); while (it != w->get().end() && it->timeout < now) { for (const auto &i : it->invs) { timedout_items[i]++; } w->get().erase(it++); } } if (timedout_items.empty()) { return; } // In flight request accounting. for (const auto &p : timedout_items) { const CInv &inv = p.first; assert(inv.type == MSG_BLOCK); CBlockIndex *pindex; { LOCK(cs_main); pindex = LookupBlockIndex(BlockHash(inv.hash)); if (!pindex) { continue; } } auto w = vote_records.getWriteView(); auto it = w->find(pindex); if (it == w.end()) { continue; } it->second.clearInflightRequest(p.second); } } void Processor::runEventLoop() { // First things first, check if we have requests that timed out and clear // them. clearTimedoutRequests(); // Make sure there is at least one suitable node to query before gathering // invs. NodeId nodeid = getSuitableNodeToQuery(); if (nodeid == NO_NODE) { return; } std::vector invs = getInvsForNextPoll(); if (invs.empty()) { return; } do { /** * If we lost contact to that node, then we remove it from nodeids, but * never add the request to queries, which ensures bad nodes get cleaned * up over time. */ bool hasSent = connman->ForNode(nodeid, [this, &invs](CNode *pnode) { uint64_t current_round = round++; { // Compute the time at which this requests times out. auto timeout = std::chrono::steady_clock::now() + queryTimeoutDuration; // Register the query. queries.getWriteView()->insert( {pnode->GetId(), current_round, timeout, invs}); // Set the timeout. LOCK(cs_peerManager); peerManager->updateNextRequestTime(pnode->GetId(), timeout); } // Send the query to the node. connman->PushMessage( pnode, CNetMsgMaker(pnode->GetCommonVersion()) .Make(NetMsgType::AVAPOLL, Poll(current_round, std::move(invs)))); return true; }); // Success! if (hasSent) { return; } { // This node is obsolete, delete it. LOCK(cs_peerManager); peerManager->removeNode(nodeid); } // Get next suitable node to try again nodeid = getSuitableNodeToQuery(); } while (nodeid != NO_NODE); } } // namespace avalanche diff --git a/src/avalanche/proof.h b/src/avalanche/proof.h index 8ae2aadd5..5eae73bf5 100644 --- a/src/avalanche/proof.h +++ b/src/avalanche/proof.h @@ -1,125 +1,104 @@ // Copyright (c) 2020 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_AVALANCHE_PROOF_H #define BITCOIN_AVALANCHE_PROOF_H #include #include #include #include #include #include #include #include class CCoinsView; namespace avalanche { class ProofValidationState; class Stake { COutPoint utxo; Amount amount; uint32_t height; CPubKey pubkey; public: explicit Stake() = default; Stake(COutPoint utxo_, Amount amount_, uint32_t height_, bool is_coinbase, CPubKey pubkey_) : utxo(utxo_), amount(amount_), height(height_ << 1 | is_coinbase), pubkey(std::move(pubkey_)) {} - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream &s, Operation ser_action) { - READWRITE(utxo); - READWRITE(amount); - READWRITE(height); - READWRITE(pubkey); + SERIALIZE_METHODS(Stake, obj) { + READWRITE(obj.utxo, obj.amount, obj.height, obj.pubkey); } const COutPoint &getUTXO() const { return utxo; } Amount getAmount() const { return amount; } uint32_t getHeight() const { return height >> 1; } bool isCoinbase() const { return height & 1; } const CPubKey &getPubkey() const { return pubkey; } uint256 getHash(const ProofId &proofid) const; }; class SignedStake { Stake stake; std::array sig; public: explicit SignedStake() = default; SignedStake(Stake stake_, std::array sig_) : stake(std::move(stake_)), sig(std::move(sig_)) {} - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream &s, Operation ser_action) { - READWRITE(stake); - READWRITE(sig); - } + SERIALIZE_METHODS(SignedStake, obj) { READWRITE(obj.stake, obj.sig); } const Stake &getStake() const { return stake; } const std::array &getSignature() const { return sig; } bool verify(const ProofId &proofid) const; }; class Proof { uint64_t sequence; int64_t expirationTime; CPubKey master; std::vector stakes; ProofId proofid; ProofId computeProofId() const; public: Proof() : sequence(0), expirationTime(0), master(), stakes(), proofid() {} Proof(uint64_t sequence_, int64_t expirationTime_, CPubKey master_, std::vector stakes_) : sequence(sequence_), expirationTime(expirationTime_), master(std::move(master_)), stakes(std::move(stakes_)), proofid(computeProofId()) {} - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream &s, Operation ser_action) { - READWRITE(sequence); - READWRITE(expirationTime); - READWRITE(master); - READWRITE(stakes); - - if (ser_action.ForRead()) { - proofid = computeProofId(); - } + SERIALIZE_METHODS(Proof, obj) { + READWRITE(obj.sequence, obj.expirationTime, obj.master, obj.stakes); + SER_READ(obj, obj.proofid = obj.computeProofId()); } uint64_t getSequence() const { return sequence; } int64_t getExpirationTime() const { return expirationTime; } const CPubKey &getMaster() const { return master; } const std::vector &getStakes() const { return stakes; } const ProofId &getId() const { return proofid; } uint32_t getScore() const; bool verify(ProofValidationState &state) const; bool verify(ProofValidationState &state, const CCoinsView &view) const; }; } // namespace avalanche #endif // BITCOIN_AVALANCHE_PROOF_H diff --git a/src/avalanche/protocol.h b/src/avalanche/protocol.h index d66acbb98..29770a954 100644 --- a/src/avalanche/protocol.h +++ b/src/avalanche/protocol.h @@ -1,104 +1,84 @@ // Copyright (c) 2020 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_AVALANCHE_PROTOCOL_H #define BITCOIN_AVALANCHE_PROTOCOL_H #include #include // for CInv #include #include #include #include namespace avalanche { class Vote { uint32_t error; uint256 hash; public: Vote() : error(-1), hash() {} Vote(uint32_t errorIn, uint256 hashIn) : error(errorIn), hash(hashIn) {} const uint256 &GetHash() const { return hash; } uint32_t GetError() const { return error; } // serialization support - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream &s, Operation ser_action) { - READWRITE(error); - READWRITE(hash); - } + SERIALIZE_METHODS(Vote, obj) { READWRITE(obj.error, obj.hash); } }; class Response { uint64_t round; uint32_t cooldown; std::vector votes; public: Response() : round(-1), cooldown(-1) {} Response(uint64_t roundIn, uint32_t cooldownIn, std::vector votesIn) : round(roundIn), cooldown(cooldownIn), votes(votesIn) {} uint64_t getRound() const { return round; } uint32_t getCooldown() const { return cooldown; } const std::vector &GetVotes() const { return votes; } // serialization support - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream &s, Operation ser_action) { - READWRITE(round); - READWRITE(cooldown); - READWRITE(votes); + SERIALIZE_METHODS(Response, obj) { + READWRITE(obj.round, obj.cooldown, obj.votes); } }; class Poll { uint64_t round; std::vector invs; public: Poll(uint64_t roundIn, std::vector invsIn) : round(roundIn), invs(invsIn) {} + uint64_t GetRound() { return round; } const std::vector &GetInvs() const { return invs; } // serialization support - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream &s, Operation ser_action) { - READWRITE(round); - READWRITE(invs); - } + SERIALIZE_METHODS(Poll, obj) { READWRITE(obj.round, obj.invs); } }; class Hello { Delegation delegation; std::array sig; public: Hello(Delegation delegationIn, std::array sigIn) : delegation(std::move(delegationIn)), sig(sigIn) {} - // serialization support - ADD_SERIALIZE_METHODS; + std::array GetSig() { return sig; } - template - inline void SerializationOp(Stream &s, Operation ser_action) { - READWRITE(delegation); - READWRITE(sig); - } + // serialization support + SERIALIZE_METHODS(Hello, obj) { READWRITE(obj.delegation, obj.sig); } }; } // namespace avalanche #endif // BITCOIN_AVALANCHE_PROTOCOL_H diff --git a/src/blockstatus.h b/src/blockstatus.h index 8cd1a2a24..b7d5be088 100644 --- a/src/blockstatus.h +++ b/src/blockstatus.h @@ -1,128 +1,123 @@ // Copyright (c) 2018-2019 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_BLOCKSTATUS_H #define BITCOIN_BLOCKSTATUS_H #include #include #include struct BlockStatus { private: uint32_t status; explicit constexpr BlockStatus(uint32_t nStatusIn) : status(nStatusIn) {} static const uint32_t VALIDITY_MASK = 0x07; // Full block available in blk*.dat static const uint32_t HAS_DATA_FLAG = 0x08; // Undo data available in rev*.dat static const uint32_t HAS_UNDO_FLAG = 0x10; // The block is invalid. static const uint32_t FAILED_FLAG = 0x20; // The block has an invalid parent. static const uint32_t FAILED_PARENT_FLAG = 0x40; // Mask used to check if the block failed. static const uint32_t INVALID_MASK = FAILED_FLAG | FAILED_PARENT_FLAG; // The block is being parked for some reason. It will be reconsidered if its // chains grows. static const uint32_t PARKED_FLAG = 0x80; // One of the block's parent is parked. static const uint32_t PARKED_PARENT_FLAG = 0x100; // Mask used to check for parked blocks. static const uint32_t PARKED_MASK = PARKED_FLAG | PARKED_PARENT_FLAG; public: explicit constexpr BlockStatus() : status(0) {} BlockValidity getValidity() const { return BlockValidity(status & VALIDITY_MASK); } BlockStatus withValidity(BlockValidity validity) const { return BlockStatus((status & ~VALIDITY_MASK) | uint32_t(validity)); } bool hasData() const { return status & HAS_DATA_FLAG; } BlockStatus withData(bool hasData = true) const { return BlockStatus((status & ~HAS_DATA_FLAG) | (hasData ? HAS_DATA_FLAG : 0)); } bool hasUndo() const { return status & HAS_UNDO_FLAG; } BlockStatus withUndo(bool hasUndo = true) const { return BlockStatus((status & ~HAS_UNDO_FLAG) | (hasUndo ? HAS_UNDO_FLAG : 0)); } bool hasFailed() const { return status & FAILED_FLAG; } BlockStatus withFailed(bool hasFailed = true) const { return BlockStatus((status & ~FAILED_FLAG) | (hasFailed ? FAILED_FLAG : 0)); } bool hasFailedParent() const { return status & FAILED_PARENT_FLAG; } BlockStatus withFailedParent(bool hasFailedParent = true) const { return BlockStatus((status & ~FAILED_PARENT_FLAG) | (hasFailedParent ? FAILED_PARENT_FLAG : 0)); } bool isParked() const { return status & PARKED_FLAG; } BlockStatus withParked(bool parked = true) const { return BlockStatus((status & ~PARKED_FLAG) | (parked ? PARKED_FLAG : 0)); } bool hasParkedParent() const { return status & PARKED_PARENT_FLAG; } BlockStatus withParkedParent(bool parkedParent = true) const { return BlockStatus((status & ~PARKED_PARENT_FLAG) | (parkedParent ? PARKED_PARENT_FLAG : 0)); } /** * Check whether this block index entry is valid up to the passed validity * level. */ bool isValid(enum BlockValidity nUpTo = BlockValidity::TRANSACTIONS) const { if (isInvalid()) { return false; } return getValidity() >= nUpTo; } bool isInvalid() const { return status & INVALID_MASK; } BlockStatus withClearedFailureFlags() const { return BlockStatus(status & ~INVALID_MASK); } bool isOnParkedChain() const { return status & PARKED_MASK; } BlockStatus withClearedParkedFlags() const { return BlockStatus(status & ~PARKED_MASK); } - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream &s, Operation ser_action) { - READWRITE(VARINT(status)); - } + SERIALIZE_METHODS(BlockStatus, obj) { READWRITE(VARINT(obj.status)); } friend constexpr bool operator==(const BlockStatus a, const BlockStatus b) { return a.status == b.status; } friend constexpr bool operator!=(const BlockStatus a, const BlockStatus b) { return !(a == b); } }; #endif // BITCOIN_BLOCKSTATUS_H diff --git a/src/feerate.h b/src/feerate.h index ef393a2e1..797b1c207 100644 --- a/src/feerate.h +++ b/src/feerate.h @@ -1,95 +1,90 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Copyright (c) 2017-2019 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_FEERATE_H #define BITCOIN_FEERATE_H #include #include #include #include #include #include /** * Fee rate in satoshis per kilobyte: Amount / kB */ class CFeeRate { private: // unit is satoshis-per-1,000-bytes Amount nSatoshisPerK; public: /** * Fee rate of 0 satoshis per kB. */ constexpr CFeeRate() : nSatoshisPerK() {} explicit constexpr CFeeRate(const Amount _nSatoshisPerK) : nSatoshisPerK(_nSatoshisPerK) {} /** * Constructor for a fee rate in satoshis per kB. The size in bytes must not * exceed (2^63 - 1) */ CFeeRate(const Amount nFeePaid, size_t nBytes); /** * Return the fee in satoshis for the given size in bytes. */ Amount GetFee(size_t nBytes) const; /** * Return the ceiling of a fee calculation in satoshis for the given size in * bytes. */ Amount GetFeeCeiling(size_t nBytes) const; /** * Return the fee in satoshis for a size of 1000 bytes */ Amount GetFeePerK() const { return GetFee(1000); } /** * Equality */ friend constexpr bool operator==(const CFeeRate a, const CFeeRate b) { return a.nSatoshisPerK == b.nSatoshisPerK; } friend constexpr bool operator!=(const CFeeRate a, const CFeeRate b) { return !(a == b); } /** * Comparison */ friend bool operator<(const CFeeRate &a, const CFeeRate &b) { return a.nSatoshisPerK < b.nSatoshisPerK; } friend bool operator>(const CFeeRate &a, const CFeeRate &b) { return a.nSatoshisPerK > b.nSatoshisPerK; } friend bool operator<=(const CFeeRate &a, const CFeeRate &b) { return a.nSatoshisPerK <= b.nSatoshisPerK; } friend bool operator>=(const CFeeRate &a, const CFeeRate &b) { return a.nSatoshisPerK >= b.nSatoshisPerK; } CFeeRate &operator+=(const CFeeRate &a) { nSatoshisPerK += a.nSatoshisPerK; return *this; } std::string ToString() const; - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream &s, Operation ser_action) { - READWRITE(nSatoshisPerK); - } + SERIALIZE_METHODS(CFeeRate, obj) { READWRITE(obj.nSatoshisPerK); } }; #endif // BITCOIN_FEERATE_H diff --git a/src/seeder/db.h b/src/seeder/db.h index ed825fefe..c84a7c903 100644 --- a/src/seeder/db.h +++ b/src/seeder/db.h @@ -1,459 +1,442 @@ // Copyright (c) 2017-2019 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_SEEDER_DB_H #define BITCOIN_SEEDER_DB_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MIN_RETRY 1000 #define REQUIRE_VERSION 70001 static inline int GetRequireHeight() { return Params().Checkpoints().mapCheckpoints.rbegin()->first; } static inline std::string ToString(const CService &ip) { std::string str = ip.ToString(); while (str.size() < 22) { str += ' '; } return str; } class CAddrStat { private: float weight; float count; float reliability; public: CAddrStat() : weight(0), count(0), reliability(0) {} void Update(bool good, int64_t age, double tau) { double f = exp(-age / tau); reliability = reliability * f + (good ? (1.0 - f) : 0); count = count * f + 1; weight = weight * f + (1.0 - f); } - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream &s, Operation ser_action) { - READWRITE(weight); - READWRITE(count); - READWRITE(reliability); + SERIALIZE_METHODS(CAddrStat, obj) { + READWRITE(obj.weight, obj.count, obj.reliability); } friend class SeederAddrInfo; }; class CAddrReport { public: CService ip; int clientVersion; int blocks; double uptime[5]; std::string clientSubVersion; int64_t lastSuccess; bool fGood; uint64_t services; }; class SeederAddrInfo { private: CService ip; uint64_t services; int64_t lastTry; int64_t ourLastTry; int64_t ourLastSuccess; int64_t ignoreTill; CAddrStat stat2H; CAddrStat stat8H; CAddrStat stat1D; CAddrStat stat1W; CAddrStat stat1M; int clientVersion; int blocks; int total; int success; std::string clientSubVersion; public: SeederAddrInfo() : services(0), lastTry(0), ourLastTry(0), ourLastSuccess(0), ignoreTill(0), clientVersion(0), blocks(0), total(0), success(0) {} CAddrReport GetReport() const { CAddrReport ret; ret.ip = ip; ret.clientVersion = clientVersion; ret.clientSubVersion = clientSubVersion; ret.blocks = blocks; ret.uptime[0] = stat2H.reliability; ret.uptime[1] = stat8H.reliability; ret.uptime[2] = stat1D.reliability; ret.uptime[3] = stat1W.reliability; ret.uptime[4] = stat1M.reliability; ret.lastSuccess = ourLastSuccess; ret.fGood = IsReliable(); ret.services = services; return ret; } bool IsReliable() const { if (ip.GetPort() != GetDefaultPort()) { return false; } if (!(services & NODE_NETWORK)) { return false; } if (!ip.IsRoutable()) { return false; } if (clientVersion && clientVersion < REQUIRE_VERSION) { return false; } if (blocks && blocks < GetRequireHeight()) { return false; } if (total <= 3 && success * 2 >= total) { return true; } if (stat2H.reliability > 0.85 && stat2H.count > 2) { return true; } if (stat8H.reliability > 0.70 && stat8H.count > 4) { return true; } if (stat1D.reliability > 0.55 && stat1D.count > 8) { return true; } if (stat1W.reliability > 0.45 && stat1W.count > 16) { return true; } if (stat1M.reliability > 0.35 && stat1M.count > 32) { return true; } return false; } int64_t GetBanTime() const { if (IsReliable()) { return 0; } if (clientVersion && clientVersion < 31900) { return 604800; } if (stat1M.reliability - stat1M.weight + 1.0 < 0.15 && stat1M.count > 32) { return 30 * 86400; } if (stat1W.reliability - stat1W.weight + 1.0 < 0.10 && stat1W.count > 16) { return 7 * 86400; } if (stat1D.reliability - stat1D.weight + 1.0 < 0.05 && stat1D.count > 8) { return 1 * 86400; } return 0; } int64_t GetIgnoreTime() const { if (IsReliable()) { return 0; } if (stat1M.reliability - stat1M.weight + 1.0 < 0.20 && stat1M.count > 2) { return 10 * 86400; } if (stat1W.reliability - stat1W.weight + 1.0 < 0.16 && stat1W.count > 2) { return 3 * 86400; } if (stat1D.reliability - stat1D.weight + 1.0 < 0.12 && stat1D.count > 2) { return 8 * 3600; } if (stat8H.reliability - stat8H.weight + 1.0 < 0.08 && stat8H.count > 2) { return 2 * 3600; } return 0; } void Update(bool good); friend class CAddrDb; - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream &s, Operation ser_action) { + SERIALIZE_METHODS(SeederAddrInfo, obj) { uint8_t version = 4; - READWRITE(version); - READWRITE(ip); - READWRITE(services); - READWRITE(lastTry); - uint8_t tried = ourLastTry != 0; + READWRITE(version, obj.ip, obj.services, obj.lastTry); + uint8_t tried = obj.ourLastTry != 0; READWRITE(tried); if (!tried) { return; } - READWRITE(ourLastTry); - READWRITE(ignoreTill); - READWRITE(stat2H); - READWRITE(stat8H); - READWRITE(stat1D); - READWRITE(stat1W); + READWRITE(obj.ourLastTry, obj.ignoreTill, obj.stat2H, obj.stat8H, + obj.stat1D, obj.stat1W); if (version >= 1) { - READWRITE(stat1M); - } else if (!ser_action.ForRead()) { - *((CAddrStat *)(&stat1M)) = stat1W; + READWRITE(obj.stat1M); + } else { + SER_WRITE(obj, *((CAddrStat *)(&obj.stat1M)) = obj.stat1W); } - READWRITE(total); - READWRITE(success); - READWRITE(clientVersion); + READWRITE(obj.total, obj.success, obj.clientVersion); if (version >= 2) { - READWRITE(clientSubVersion); + READWRITE(obj.clientSubVersion); } if (version >= 3) { - READWRITE(blocks); + READWRITE(obj.blocks); } if (version >= 4) { - READWRITE(ourLastSuccess); + READWRITE(obj.ourLastSuccess); } } }; class CAddrDbStats { public: int nBanned; int nAvail; int nTracked; int nNew; int nGood; int nAge; }; struct CServiceResult { CService service; bool fGood; int nBanTime; int nHeight; int nClientV; std::string strClientV; int64_t ourLastSuccess; }; /** * seen nodes * / \ * (a) banned nodes available nodes-------------- * / | \ * tracked nodes (b) unknown nodes (e) active nodes * / \ * (d) good nodes (c) non-good nodes */ class CAddrDb { private: mutable RecursiveMutex cs; // number of address id's int nId; // map address id to address info (b,c,d,e) std::map idToInfo; // map ip to id (b,c,d,e) std::map ipToId; // sequence of tried nodes, in order we have tried connecting to them (c,d) std::deque ourId; // set of nodes not yet tried (b) std::set unkId; // set of good nodes (d, good e) std::set goodId; int nDirty; protected: // internal routines that assume proper locks are acquired // add an address void Add_(const CAddress &addr, bool force); // get an IP to test (must call Good_ or Bad_ on result afterwards) bool Get_(CServiceResult &ip, int &wait); // mark an IP as good (must have been returned by Get_) void Good_(const CService &ip, int clientV, std::string clientSV, int blocks); // mark an IP as bad (and optionally ban it) (must have been returned by // Get_) void Bad_(const CService &ip, int ban); // look up id of an IP int Lookup_(const CService &ip); // get a random set of IPs (shared lock only) void GetIPs_(std::set &ips, uint64_t requestedFlags, uint32_t max, const bool *nets); public: // nodes that are banned, with their unban time (a) std::map banned; void GetStats(CAddrDbStats &stats) const { LOCK(cs); stats.nBanned = banned.size(); stats.nAvail = idToInfo.size(); stats.nTracked = ourId.size(); stats.nGood = goodId.size(); stats.nNew = unkId.size(); if (ourId.size() > 0) { stats.nAge = GetTime() - idToInfo.at(ourId.at(0)).ourLastTry; } else { stats.nAge = 0; } } void ResetIgnores() { for (std::map::iterator it = idToInfo.begin(); it != idToInfo.end(); it++) { (*it).second.ignoreTill = 0; } } std::vector GetAll() { std::vector ret; LOCK(cs); for (std::deque::const_iterator it = ourId.begin(); it != ourId.end(); it++) { const SeederAddrInfo &info = idToInfo[*it]; if (info.success > 0) { ret.push_back(info.GetReport()); } } return ret; } // serialization code // format: // nVersion (0 for now) // n (number of ips in (b,c,d)) // SeederAddrInfo[n] // banned // acquires a shared lock (this does not suffice for read mode, but we // assume that only happens at startup, single-threaded) this way, dumping // does not interfere with GetIPs_, which is called from the DNS thread template void Serialize(Stream &s) const { LOCK(cs); int nVersion = 0; s << nVersion; CAddrDb *db = const_cast(this); int n = ourId.size() + unkId.size(); s << n; for (std::deque::const_iterator it = ourId.begin(); it != ourId.end(); it++) { std::map::iterator ci = db->idToInfo.find(*it); s << (*ci).second; } for (std::set::const_iterator it = unkId.begin(); it != unkId.end(); it++) { std::map::iterator ci = db->idToInfo.find(*it); s << (*ci).second; } s << banned; } template void Unserialize(Stream &s) { LOCK(cs); int nVersion; s >> nVersion; CAddrDb *db = const_cast(this); db->nId = 0; int n; s >> n; for (int i = 0; i < n; i++) { SeederAddrInfo info; s >> info; if (!info.GetBanTime()) { int id = db->nId++; db->idToInfo[id] = info; db->ipToId[info.ip] = id; if (info.ourLastTry) { db->ourId.push_back(id); if (info.IsReliable()) { db->goodId.insert(id); } } else { db->unkId.insert(id); } } } db->nDirty++; s >> banned; } void Add(const CAddress &addr, bool fForce = false) { LOCK(cs); Add_(addr, fForce); } void Add(const std::vector &vAddr, bool fForce = false) { LOCK(cs); for (size_t i = 0; i < vAddr.size(); i++) { Add_(vAddr[i], fForce); } } void GetMany(std::vector &ips, int max, int &wait) { LOCK(cs); while (max > 0) { CServiceResult ip = {}; if (!Get_(ip, wait)) { return; } ips.push_back(ip); max--; } } void ResultMany(const std::vector &ips) { LOCK(cs); for (size_t i = 0; i < ips.size(); i++) { if (ips[i].fGood) { Good_(ips[i].service, ips[i].nClientV, ips[i].strClientV, ips[i].nHeight); } else { Bad_(ips[i].service, ips[i].nBanTime); } } } void GetIPs(std::set &ips, uint64_t requestedFlags, uint32_t max, const bool *nets) { LOCK(cs); GetIPs_(ips, requestedFlags, max, nets); } }; #endif // BITCOIN_SEEDER_DB_H