MWEB: Input versioning
This commit is contained in:
parent
d32dfcca56
commit
a153bd3fe3
@ -25,10 +25,11 @@ public:
|
||||
[](const Output& output) { return output.GetSenderPubKey(); }
|
||||
);
|
||||
|
||||
std::transform(
|
||||
body.GetInputs().cbegin(), body.GetInputs().cend(), std::back_inserter(lhs_keys),
|
||||
[](const Input& input) { return input.GetInputPubKey(); }
|
||||
);
|
||||
for (const Input& input : body.GetInputs()) {
|
||||
if (!!input.GetInputPubKey()) {
|
||||
lhs_keys.push_back(*input.GetInputPubKey());
|
||||
}
|
||||
}
|
||||
|
||||
PublicKey lhs_total;
|
||||
if (!lhs_keys.empty()) {
|
||||
|
||||
@ -19,13 +19,20 @@ public:
|
||||
static constexpr size_t STANDARD_OUTPUT_WEIGHT = BASE_OUTPUT_WEIGHT + STANDARD_OUTPUT_FIELDS_WEIGHT;
|
||||
|
||||
static constexpr size_t MAX_NUM_INPUTS = 50'000;
|
||||
static constexpr size_t INPUT_BYTES = 195;
|
||||
static constexpr size_t INPUT_BYTES = 196;
|
||||
static constexpr size_t MAX_MWEB_BYTES = 180 + (3 * 5) + // 180 bytes per header and 5 bytes each for input, output, and kernel vec size
|
||||
(MAX_NUM_INPUTS * INPUT_BYTES) + // 50k inputs at 195 bytes each
|
||||
(MAX_NUM_INPUTS * INPUT_BYTES) + // 50k inputs at 196 bytes each (ignoring extradata)
|
||||
(mw::MAX_BLOCK_WEIGHT * 60); // Ignoring inputs, no tx component is ever more than 60 bytes per unit of weight
|
||||
|
||||
static size_t Calculate(const TxBody& tx_body)
|
||||
{
|
||||
size_t input_weight = std::accumulate(
|
||||
tx_body.GetInputs().begin(), tx_body.GetInputs().end(), (size_t)0,
|
||||
[](size_t sum, const Input& input) {
|
||||
return sum + CalcInputWeight(input.GetExtraData());
|
||||
}
|
||||
);
|
||||
|
||||
size_t kernel_weight = std::accumulate(
|
||||
tx_body.GetKernels().begin(), tx_body.GetKernels().end(), (size_t)0,
|
||||
[](size_t sum, const Kernel& kernel) {
|
||||
@ -45,7 +52,7 @@ public:
|
||||
}
|
||||
);
|
||||
|
||||
return kernel_weight + output_weight;
|
||||
return input_weight + kernel_weight + output_weight;
|
||||
}
|
||||
|
||||
static bool ExceedsMaximum(const TxBody& tx_body)
|
||||
@ -53,6 +60,11 @@ public:
|
||||
return tx_body.GetInputs().size() > MAX_NUM_INPUTS || Calculate(tx_body) > mw::MAX_BLOCK_WEIGHT;
|
||||
}
|
||||
|
||||
static size_t CalcInputWeight(const std::vector<uint8_t>& extra_data)
|
||||
{
|
||||
return ExtraBytesToWeight(extra_data.size());
|
||||
}
|
||||
|
||||
static size_t CalcKernelWeight(
|
||||
const bool has_stealth_excess,
|
||||
const CScript& pegout_script = CScript{},
|
||||
|
||||
@ -19,12 +19,18 @@ class Input :
|
||||
public Traits::IHashable,
|
||||
public Traits::ISerializable
|
||||
{
|
||||
enum FeatureBit {
|
||||
STEALTH_KEY_FEATURE_BIT = 0x01,
|
||||
EXTRA_DATA_FEATURE_BIT = 0x02
|
||||
};
|
||||
|
||||
public:
|
||||
//
|
||||
// Constructors
|
||||
//
|
||||
Input(mw::Hash output_id, Commitment commitment, PublicKey input_pubkey, PublicKey output_pubkey, Signature signature)
|
||||
: m_outputID(std::move(output_id)),
|
||||
Input(uint8_t features, mw::Hash output_id, Commitment commitment, PublicKey input_pubkey, PublicKey output_pubkey, Signature signature)
|
||||
: m_features(features),
|
||||
m_outputID(std::move(output_id)),
|
||||
m_commitment(std::move(commitment)),
|
||||
m_inputPubKey(std::move(input_pubkey)),
|
||||
m_outputPubKey(std::move(output_pubkey)),
|
||||
@ -57,10 +63,12 @@ public:
|
||||
//
|
||||
// Getters
|
||||
//
|
||||
bool IsStandard() const noexcept { return m_features < FeatureBit::EXTRA_DATA_FEATURE_BIT; }
|
||||
const mw::Hash& GetOutputID() const noexcept { return m_outputID; }
|
||||
const Commitment& GetCommitment() const noexcept final { return m_commitment; }
|
||||
const PublicKey& GetInputPubKey() const noexcept { return m_inputPubKey; }
|
||||
const PublicKey& GetOutputPubKey() const noexcept { return m_outputPubKey; }
|
||||
const boost::optional<PublicKey>& GetInputPubKey() const noexcept { return m_inputPubKey; }
|
||||
const std::vector<uint8_t>& GetExtraData() const noexcept { return m_extraData; }
|
||||
const Signature& GetSignature() const noexcept { return m_signature; }
|
||||
|
||||
SignedMessage BuildSignedMsg() const noexcept;
|
||||
@ -70,10 +78,22 @@ public:
|
||||
//
|
||||
IMPL_SERIALIZABLE(Input, obj)
|
||||
{
|
||||
READWRITE(obj.m_features);
|
||||
READWRITE(obj.m_outputID);
|
||||
READWRITE(obj.m_commitment);
|
||||
READWRITE(obj.m_inputPubKey);
|
||||
READWRITE(obj.m_outputPubKey);
|
||||
|
||||
if (obj.m_features & STEALTH_KEY_FEATURE_BIT) {
|
||||
PublicKey input_pubkey;
|
||||
SER_WRITE(obj, input_pubkey = *obj.m_inputPubKey);
|
||||
READWRITE(input_pubkey);
|
||||
SER_READ(obj, obj.m_inputPubKey = boost::make_optional<PublicKey>(std::move(input_pubkey)));
|
||||
}
|
||||
|
||||
if (obj.m_features & EXTRA_DATA_FEATURE_BIT) {
|
||||
READWRITE(obj.m_extraData);
|
||||
}
|
||||
|
||||
READWRITE(obj.m_signature);
|
||||
SER_READ(obj, obj.m_hash = Hashed(obj));
|
||||
}
|
||||
@ -84,6 +104,8 @@ public:
|
||||
const mw::Hash& GetHash() const noexcept final { return m_hash; }
|
||||
|
||||
private:
|
||||
uint8_t m_features;
|
||||
|
||||
// The ID of the output being spent.
|
||||
mw::Hash m_outputID;
|
||||
|
||||
@ -91,11 +113,13 @@ private:
|
||||
Commitment m_commitment;
|
||||
|
||||
// The input pubkey.
|
||||
PublicKey m_inputPubKey;
|
||||
boost::optional<PublicKey> m_inputPubKey;
|
||||
|
||||
// The public key of the output being spent.
|
||||
PublicKey m_outputPubKey;
|
||||
|
||||
std::vector<uint8_t> m_extraData;
|
||||
|
||||
Signature m_signature;
|
||||
|
||||
mw::Hash m_hash;
|
||||
|
||||
@ -2,8 +2,11 @@
|
||||
#include <mw/crypto/Schnorr.h>
|
||||
#include <mw/crypto/SecretKeys.h>
|
||||
|
||||
// Creates a standard input with a stealth key (feature bit = 1)
|
||||
Input Input::Create(const mw::Hash& output_id, const Commitment& commitment, const SecretKey& input_key, const SecretKey& output_key)
|
||||
{
|
||||
uint8_t features = (uint8_t)FeatureBit::STEALTH_KEY_FEATURE_BIT;
|
||||
|
||||
PublicKey input_pubkey = PublicKey::From(input_key);
|
||||
PublicKey output_pubkey = PublicKey::From(output_key);
|
||||
|
||||
@ -18,26 +21,44 @@ Input Input::Create(const mw::Hash& output_id, const Commitment& commitment, con
|
||||
.Add(input_key)
|
||||
.Total();
|
||||
|
||||
// Hash message
|
||||
Hasher msg_hasher;
|
||||
msg_hasher << features << output_id;
|
||||
mw::Hash msg_hash = msg_hasher.hash();
|
||||
|
||||
return Input(
|
||||
features,
|
||||
output_id,
|
||||
commitment,
|
||||
std::move(input_pubkey),
|
||||
std::move(output_pubkey),
|
||||
Schnorr::Sign(sig_key.data(), output_id)
|
||||
Schnorr::Sign(sig_key.data(), msg_hash)
|
||||
);
|
||||
}
|
||||
|
||||
SignedMessage Input::BuildSignedMsg() const noexcept
|
||||
{
|
||||
// Hash keys (K_i||K_o)
|
||||
Hasher key_hasher;
|
||||
key_hasher << GetInputPubKey() << GetOutputPubKey();
|
||||
SecretKey key_hash = key_hasher.hash();
|
||||
// Calculate message hash
|
||||
Hasher msg_hasher;
|
||||
msg_hasher << m_features << GetOutputID();
|
||||
if (m_features & EXTRA_DATA_FEATURE_BIT) {
|
||||
msg_hasher << GetExtraData();
|
||||
}
|
||||
mw::Hash message = msg_hasher.hash();
|
||||
|
||||
// Calculate aggregated key K_agg = K_i + HASH(K_i||K_o) * K_o
|
||||
PublicKey public_key = GetOutputPubKey()
|
||||
.Mul(key_hash)
|
||||
.Add(GetInputPubKey());
|
||||
// Calculate public key
|
||||
PublicKey public_key = GetOutputPubKey();
|
||||
if (m_features & STEALTH_KEY_FEATURE_BIT) {
|
||||
// Hash keys (K_i||K_o)
|
||||
Hasher key_hasher;
|
||||
key_hasher << (*GetInputPubKey()) << GetOutputPubKey();
|
||||
SecretKey key_hash = key_hasher.hash();
|
||||
|
||||
return SignedMessage{GetOutputID(), public_key, GetSignature()};
|
||||
// Calculate aggregated key K_agg = K_i + HASH(K_i||K_o) * K_o
|
||||
public_key = public_key
|
||||
.Mul(key_hash)
|
||||
.Add(*GetInputPubKey());
|
||||
}
|
||||
|
||||
return SignedMessage{message, public_key, GetSignature()};
|
||||
}
|
||||
@ -28,6 +28,12 @@ Transaction::CPtr Transaction::Create(
|
||||
|
||||
bool Transaction::IsStandard() const noexcept
|
||||
{
|
||||
for (const Input& input : GetInputs()) {
|
||||
if (!input.IsStandard()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const Kernel& kernel : GetKernels()) {
|
||||
if (!kernel.IsStandard()) {
|
||||
return false;
|
||||
|
||||
@ -25,6 +25,7 @@ BOOST_AUTO_TEST_CASE(ValidateStealthSum)
|
||||
SecretKey input1_key = SecretKey::Random();
|
||||
SecretKey input1_output_key = SecretKey::Random();
|
||||
Input input1(
|
||||
1, // features
|
||||
SecretKey::Random().GetBigInt(), // output_id
|
||||
Commitment::Random(), // commitment
|
||||
PublicKey::From(input1_key), // input pubkey
|
||||
@ -36,6 +37,7 @@ BOOST_AUTO_TEST_CASE(ValidateStealthSum)
|
||||
SecretKey input2_key = SecretKey::Random();
|
||||
SecretKey input2_output_key = SecretKey::Random();
|
||||
Input input2(
|
||||
1, // features
|
||||
SecretKey::Random().GetBigInt(), // output_id
|
||||
Commitment::Random(), // commitment
|
||||
PublicKey::From(input2_key), // input pubkey
|
||||
|
||||
@ -16,7 +16,7 @@ BOOST_AUTO_TEST_CASE(PlainTxInput)
|
||||
PublicKey input_pubkey = PublicKey::Random();
|
||||
PublicKey output_pubkey = PublicKey::Random();
|
||||
Signature signature(SecretKey64::Random().GetBigInt());
|
||||
Input input(output_id, commit, input_pubkey, output_pubkey, signature);
|
||||
Input input(1, output_id, commit, input_pubkey, output_pubkey, signature);
|
||||
|
||||
//
|
||||
// Serialization
|
||||
@ -25,6 +25,10 @@ BOOST_AUTO_TEST_CASE(PlainTxInput)
|
||||
std::vector<uint8_t> serialized = input.Serialized();
|
||||
|
||||
CDataStream deserializer(serialized, SER_DISK, PROTOCOL_VERSION);
|
||||
uint8_t features;
|
||||
deserializer >> features;
|
||||
BOOST_REQUIRE(features == 1);
|
||||
|
||||
mw::Hash output_id2;
|
||||
deserializer >> output_id2;
|
||||
BOOST_REQUIRE(output_id2 == output_id);
|
||||
@ -33,14 +37,14 @@ BOOST_AUTO_TEST_CASE(PlainTxInput)
|
||||
deserializer >> commit2;
|
||||
BOOST_REQUIRE(commit2 == commit);
|
||||
|
||||
PublicKey input_pubkey2;
|
||||
deserializer >> input_pubkey2;
|
||||
BOOST_REQUIRE(input_pubkey2 == input_pubkey);
|
||||
|
||||
PublicKey output_pubkey2;
|
||||
deserializer >> output_pubkey2;
|
||||
BOOST_REQUIRE(output_pubkey2 == output_pubkey);
|
||||
|
||||
PublicKey input_pubkey2;
|
||||
deserializer >> input_pubkey2;
|
||||
BOOST_REQUIRE(input_pubkey2 == input_pubkey);
|
||||
|
||||
Signature signature2;
|
||||
deserializer >> signature2;
|
||||
BOOST_REQUIRE(signature2 == signature);
|
||||
@ -54,10 +58,12 @@ BOOST_AUTO_TEST_CASE(PlainTxInput)
|
||||
{
|
||||
BOOST_REQUIRE(input.GetOutputID() == output_id);
|
||||
BOOST_REQUIRE(input.GetCommitment() == commit);
|
||||
BOOST_REQUIRE(input.GetInputPubKey() == input_pubkey);
|
||||
BOOST_REQUIRE(*input.GetInputPubKey() == input_pubkey);
|
||||
BOOST_REQUIRE(input.GetOutputPubKey() == output_pubkey);
|
||||
BOOST_REQUIRE(input.GetSignature() == signature);
|
||||
}
|
||||
|
||||
// MW: TODO - Input version tests
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
Loading…
x
Reference in New Issue
Block a user