oxen-core/src/cryptonote_core/tx_blink.h
Jason Rhinelander dd7a4104b5 Blink
This is the bulk of the work for blink.  There is two pieces yet to come
which will follow shortly, which are: the p2p communication of blink
transactions (which needs to be fully synchronized, not just shared,
unlike regular mempool txes); and an implementation of fee burning.

Blink approval, multi-quorum signing, cli wallet and node support for
submission denial are all implemented here.

This overhauls and fixes various parts of the SNNetwork interface to fix
some issues (particularly around non-SN communication with SNs, which
wasn't working).

There are also a few sundry FIXME's and TODO's of other minor details
that will follow shortly under cleanup/testing/etc.
2019-11-27 14:07:52 -04:00

150 lines
6.6 KiB
C++

// Copyright (c) 2019, The Loki Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once
#include "../cryptonote_basic/cryptonote_basic.h"
#include "../common/util.h"
#include "service_node_rules.h"
#include <iostream>
#include <shared_mutex>
namespace service_nodes {
class service_node_list;
}
namespace cryptonote {
// FIXME TODO XXX - rename this file to blink_tx.h
class blink_tx {
public:
enum class subquorum : uint8_t { base, future, _count };
enum class signature_status : uint8_t { none, rejected, approved };
/// The blink authorization height of this blink tx, i.e. the block height at the time the
/// transaction was created.
const uint64_t height;
/// The encapsulated transaction. Any modifications to this (including getting the tx_hash,
/// which gets cached!) should either take place before the object is shared, or protected by
/// the unique_lock.
transaction tx;
class signature_verification_error : public std::runtime_error {
using std::runtime_error::runtime_error;
};
// Not default constructible
blink_tx() = delete;
/** Construct a new blink_tx from just a height; constructs a default transaction.
*/
explicit blink_tx(uint64_t height) : tx{}, height{height} {
assert(quorum_height(subquorum::base) > 0);
for (auto &q : signatures_)
for (auto &s : q)
s.status = signature_status::none;
}
/// Obtains a unique lock on this blink tx; required for any signature-mutating method unless
/// otherwise noted
template <typename... Args>
auto unique_lock(Args &&...args) { return std::unique_lock<std::shared_timed_mutex>{mutex_, std::forward<Args>(args)...}; }
/// Obtains a unique lock on this blink tx; required for any signature-dependent method unless
/// otherwise noted
template <typename... Args>
auto shared_lock(Args &&...args) { return std::shared_lock<std::shared_timed_mutex>{mutex_, std::forward<Args>(args)...}; }
/**
* Adds a signature for the given quorum and position. Returns false if a signature was already
* present; true if the signature was accepted and stored; and throws a
* `blink_tx::signature_verification_error` if the signature fails validation.
*/
bool add_signature(subquorum q, int position, bool approved, const crypto::signature &sig, const service_nodes::service_node_list &snl);
/**
* Adds a signature for the given quorum and position without checking it for validity (i.e.
* because it has already been checked with crypto::check_signature). Returns true if added,
* false if a signature was already present.
*/
bool add_prechecked_signature(subquorum q, int position, bool approved, const crypto::signature &sig);
/**
* Returns the signature status for the given subquorum and position.
*/
signature_status get_signature_status(subquorum q, int position) const;
/**
* Returns true if this blink tx is valid for inclusion in the blockchain, that is, has the
* required number of approval signatures in each quorum. (Note that it is possible for a blink
* tx to be neither approved() nor rejected()). You must hold a shared_lock when calling this.
*/
bool approved() const;
/**
* Returns true if this blink tx has been definitively rejected, that is, has enough rejection
* signatures in at least one of the quorums that it is impossible for it to become approved().
* (Note that it is possible for a blink tx to be neither approved() nor rejected()). You must
* hold a shared_lock when calling this.
*/
bool rejected() const;
/// Returns the quorum height for the given height and quorum (base or future); returns 0 at the
/// beginning of the chain (before there are enough blocks for a blink quorum).
static uint64_t quorum_height(uint64_t h, subquorum q) {
uint64_t bh = h - (h % service_nodes::BLINK_QUORUM_INTERVAL) - service_nodes::BLINK_QUORUM_LAG
+ static_cast<uint8_t>(q) * service_nodes::BLINK_QUORUM_INTERVAL;
return bh > h /*overflow*/ ? 0 : bh;
}
/// Returns the height of the given subquorum (base or future) for this blink tx; returns 0 at
/// the beginning of the chain (before there are enough blocks for a blink quorum). Lock not
/// required.
uint64_t quorum_height(subquorum q) const { return quorum_height(height, q); }
/// Returns the pubkey of the referenced service node, or null if there is no such service node.
crypto::public_key get_sn_pubkey(subquorum q, int position, const service_nodes::service_node_list &snl) const;
/// Returns the hashed signing value for this blink TX for a tx with status `approved`. The
/// result is a fast hash of the height + tx hash + approval value. Lock not required.
crypto::hash hash(bool approved) const;
struct quorum_signature {
signature_status status;
crypto::signature sig;
};
private:
std::array<std::array<quorum_signature, service_nodes::BLINK_SUBQUORUM_SIZE>, tools::enum_count<subquorum>> signatures_;
std::shared_timed_mutex mutex_;
};
}