MWEB: Input versioning

This commit is contained in:
David Burkett 2022-01-29 17:35:11 -05:00 committed by Loshan T
parent d32dfcca56
commit a153bd3fe3
7 changed files with 100 additions and 28 deletions

View File

@ -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()) {

View File

@ -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{},

View File

@ -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;

View File

@ -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()};
}

View File

@ -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;

View File

@ -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

View File

@ -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()