aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cryptonote_basic/asset_types.cpp91
-rw-r--r--src/cryptonote_basic/asset_types.h22
2 files changed, 113 insertions, 0 deletions
diff --git a/src/cryptonote_basic/asset_types.cpp b/src/cryptonote_basic/asset_types.cpp
index 7ac078415..aa17b359f 100644
--- a/src/cryptonote_basic/asset_types.cpp
+++ b/src/cryptonote_basic/asset_types.cpp
@@ -20,6 +20,7 @@ namespace assets
constexpr char COLLECTION_MEMBERSHIP_DOMAIN[] = "MonzeroCollectionMembershipV1";
constexpr char ISSUANCE_PAYLOAD_DOMAIN[] = "MonzeroAssetIssuancePayloadV1";
constexpr char REGISTRY_SNAPSHOT_DOMAIN[] = "MonzeroAssetRegistrySnapshotV1";
+ constexpr char TRANSACTION_EXTENSION_DOMAIN[] = "MonzeroAssetTransactionExtensionV1";
constexpr uint8_t REGISTRY_SNAPSHOT_VERSION = 1;
constexpr uint32_t MAX_REGISTRY_SNAPSHOT_RECORDS = 100000;
@@ -365,6 +366,96 @@ namespace assets
return true;
}
+ bool encode_transaction_extension(const transaction_extension& extension, std::vector<uint8_t>& encoded, std::string* error)
+ {
+ if (extension.version != TRANSACTION_EXTENSION_VERSION)
+ return fail(error, "unsupported asset transaction extension version");
+ if (extension.network != MAINNET && extension.network != TESTNET && extension.network != STAGENET)
+ return fail(error, "asset transaction extension requires an explicit public network");
+ if (extension.operation != transaction_operation::issuance)
+ return fail(error, "unsupported asset transaction operation");
+ if (extension.carrier_prefix_hash == crypto::null_hash)
+ return fail(error, "asset transaction extension requires a carrier prefix commitment");
+ if (extension.issuance.descriptor.network != extension.network)
+ return fail(error, "asset transaction extension network does not match its issuance");
+
+ std::vector<uint8_t> operation;
+ if (!encode_issuance_payload(extension.issuance, operation, error))
+ return false;
+ if (operation.size() > std::numeric_limits<uint16_t>::max())
+ return fail(error, "asset transaction operation is too large");
+
+ encoded.clear();
+ encoded.reserve(sizeof(TRANSACTION_EXTENSION_DOMAIN) - 1 + 1 + 16 + 1
+ + sizeof(extension.carrier_prefix_hash) + 2 + operation.size());
+ encoded.insert(encoded.end(), TRANSACTION_EXTENSION_DOMAIN,
+ TRANSACTION_EXTENSION_DOMAIN + sizeof(TRANSACTION_EXTENSION_DOMAIN) - 1);
+ encoded.push_back(extension.version);
+ append_pod(encoded, get_config(extension.network).NETWORK_ID);
+ encoded.push_back(static_cast<uint8_t>(extension.operation));
+ append_pod(encoded, extension.carrier_prefix_hash);
+ append_u16_le(encoded, static_cast<uint16_t>(operation.size()));
+ encoded.insert(encoded.end(), operation.begin(), operation.end());
+ return true;
+ }
+
+ bool decode_transaction_extension(const std::vector<uint8_t>& encoded, transaction_extension& extension, std::string* error)
+ {
+ constexpr size_t domain_size = sizeof(TRANSACTION_EXTENSION_DOMAIN) - 1;
+ constexpr size_t header_size = domain_size + 1 + 16 + 1 + sizeof(crypto::hash) + 2;
+ if (encoded.size() < header_size)
+ return fail(error, "truncated asset transaction extension");
+ if (!std::equal(TRANSACTION_EXTENSION_DOMAIN,
+ TRANSACTION_EXTENSION_DOMAIN + domain_size, encoded.begin()))
+ return fail(error, "invalid asset transaction extension domain");
+
+ transaction_extension parsed;
+ size_t offset = domain_size;
+ parsed.version = encoded[offset++];
+ if (parsed.version != TRANSACTION_EXTENSION_VERSION)
+ return fail(error, "unsupported asset transaction extension version");
+ boost::uuids::uuid network_id{};
+ if (!read_pod(encoded, offset, network_id))
+ return fail(error, "truncated asset transaction extension network");
+ parsed.network = UNDEFINED;
+ for (const network_type candidate : {MAINNET, TESTNET, STAGENET})
+ {
+ if (network_id == get_config(candidate).NETWORK_ID)
+ {
+ parsed.network = candidate;
+ break;
+ }
+ }
+ parsed.operation = static_cast<transaction_operation>(encoded[offset++]);
+ if (!read_pod(encoded, offset, parsed.carrier_prefix_hash))
+ return fail(error, "truncated asset transaction carrier commitment");
+ uint16_t operation_size = 0;
+ if (!read_u16_le(encoded, offset, operation_size)
+ || offset > encoded.size() || operation_size != encoded.size() - offset)
+ return fail(error, "asset transaction operation length is not canonical");
+ const std::vector<uint8_t> operation(
+ encoded.begin() + offset, encoded.begin() + offset + operation_size);
+ if (parsed.operation != transaction_operation::issuance)
+ return fail(error, "unsupported asset transaction operation");
+ if (!decode_issuance_payload(operation, parsed.issuance, error))
+ return false;
+
+ std::vector<uint8_t> canonical;
+ if (!encode_transaction_extension(parsed, canonical, error) || canonical != encoded)
+ return fail(error, "asset transaction extension encoding is not canonical");
+ extension = std::move(parsed);
+ return true;
+ }
+
+ bool derive_transaction_extension_id(const transaction_extension& extension, crypto::hash& extension_id, std::string* error)
+ {
+ std::vector<uint8_t> encoded;
+ if (!encode_transaction_extension(extension, encoded, error))
+ return false;
+ extension_id = crypto::cn_fast_hash(encoded.data(), encoded.size());
+ return true;
+ }
+
bool asset_registry::apply_issuance(
const issuance_descriptor& descriptor,
const crypto::signature& issuer_signature,
diff --git a/src/cryptonote_basic/asset_types.h b/src/cryptonote_basic/asset_types.h
index 2b0e01890..5b2bf6006 100644
--- a/src/cryptonote_basic/asset_types.h
+++ b/src/cryptonote_basic/asset_types.h
@@ -75,6 +75,28 @@ namespace assets
bool encode_issuance_payload(const issuance_payload& payload, std::vector<uint8_t>& encoded, std::string* error = nullptr);
bool decode_issuance_payload(const std::vector<uint8_t>& encoded, issuance_payload& payload, std::string* error = nullptr);
+ constexpr uint8_t TRANSACTION_EXTENSION_VERSION = 1;
+ enum class transaction_operation : uint8_t
+ {
+ issuance = 1
+ };
+
+ // Detached transaction-extension prototype. The carrier commitment binds
+ // the operation to a native transaction prefix without changing active
+ // transaction serialization or validity.
+ struct transaction_extension
+ {
+ uint8_t version = TRANSACTION_EXTENSION_VERSION;
+ network_type network = UNDEFINED;
+ transaction_operation operation = transaction_operation::issuance;
+ crypto::hash carrier_prefix_hash{};
+ issuance_payload issuance;
+ };
+
+ bool encode_transaction_extension(const transaction_extension& extension, std::vector<uint8_t>& encoded, std::string* error = nullptr);
+ bool decode_transaction_extension(const std::vector<uint8_t>& encoded, transaction_extension& extension, std::string* error = nullptr);
+ bool derive_transaction_extension_id(const transaction_extension& extension, crypto::hash& extension_id, std::string* error = nullptr);
+
struct asset_record
{
issuance_descriptor descriptor;