/* * Copyright (c) 2015 Cryptonomex, Inc., and contributors. * * The MIT License * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #pragma once #include #include #include namespace graphene { namespace chain { /** * @defgroup transactions Transactions * * All transactions are sets of operations that must be applied atomically. Transactions must refer to a recent * block that defines the context of the operation so that they assert a known binding to the object id's referenced * in the transaction. * * Rather than specify a full block number, we only specify the lower 16 bits of the block number which means you * can reference any block within the last 65,536 blocks which is 3.5 days with a 5 second block interval or 18 * hours with a 1 second interval. * * All transactions must expire so that the network does not have to maintain a permanent record of all transactions * ever published. A transaction may not have an expiration date too far in the future because this would require * keeping too much transaction history in memory. * * The block prefix is the first 4 bytes of the block hash of the reference block number, which is the second 4 * bytes of the @ref block_id_type (the first 4 bytes of the block ID are the block number) * * Note: A transaction which selects a reference block cannot be migrated between forks outside the period of * ref_block_num.time to (ref_block_num.time + rel_exp * interval). This fact can be used to protect market orders * which should specify a relatively short re-org window of perhaps less than 1 minute. Normal payments should * probably have a longer re-org window to ensure their transaction can still go through in the event of a momentary * disruption in service. * * @note It is not recommended to set the @ref ref_block_num, @ref ref_block_prefix, and @ref expiration * fields manually. Call the appropriate overload of @ref set_expiration instead. * * @{ */ /** * @brief groups operations that should be applied atomically */ struct transaction { /** * Least significant 16 bits from the reference block number. If @ref relative_expiration is zero, this field * must be zero as well. */ uint16_t ref_block_num = 0; /** * The first non-block-number 32-bits of the reference block ID. Recall that block IDs have 32 bits of block * number followed by the actual block hash, so this field should be set using the second 32 bits in the * @ref block_id_type */ uint32_t ref_block_prefix = 0; /** * This field specifies the absolute expiration for this transaction. */ fc::time_point_sec expiration; vector operations; extensions_type extensions; /// Calculate the digest for a transaction digest_type digest()const; transaction_id_type id()const; void validate() const; /// Calculate the digest used for signature validation digest_type sig_digest( const chain_id_type& chain_id )const; void set_expiration( fc::time_point_sec expiration_time ); void set_reference_block( const block_id_type& reference_block ); /// visit all operations template vector visit( Visitor&& visitor ) { vector results; for( auto& op : operations ) results.push_back(op.visit( std::forward( visitor ) )); return results; } template vector visit( Visitor&& visitor )const { vector results; for( auto& op : operations ) results.push_back(op.visit( std::forward( visitor ) )); return results; } void get_required_authorities( flat_set& active, flat_set& owner, vector& other, bool ignore_custom_operation_required_auths )const; }; /** * @brief adds a signature to a transaction */ struct signed_transaction : public transaction { signed_transaction( const transaction& trx = transaction() ) : transaction(trx){} /** signs and appends to signatures */ const signature_type& sign( const private_key_type& key, const chain_id_type& chain_id ); /** returns signature but does not append */ signature_type sign( const private_key_type& key, const chain_id_type& chain_id )const; /** * The purpose of this method is to identify some subset of * @ref available_keys that will produce sufficient signatures * for a transaction. The result is not always a minimal set of * signatures, but any non-minimal result will still pass * validation. */ set get_required_signatures( const chain_id_type& chain_id, const flat_set& available_keys, const std::function& get_active, const std::function& get_owner, const std::function(account_id_type, const operation&)>& get_custom, bool ignore_custom_operation_required_authorities, uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH )const; void verify_authority( const chain_id_type& chain_id, const std::function& get_active, const std::function& get_owner, const std::function(account_id_type, const operation&)>& get_custom, bool ignore_custom_operation_required_auths, uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH )const; /** * This is a slower replacement for get_required_signatures() * which returns a minimal set in all cases, including * some cases where get_required_signatures() returns a * non-minimal set. */ set minimize_required_signatures( const chain_id_type& chain_id, const flat_set& available_keys, const std::function& get_active, const std::function& get_owner, const std::function(account_id_type, const operation&)>& get_custom, bool ignore_custom_operation_required_auths, uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH ) const; /** * @brief Extract public keys from signatures with given chain ID. * @param chain_id A chain ID * @return Public keys * @note If @ref signees is empty, E.G. when it's the first time calling * this function for the signed transaction, public keys will be * extracted with given chain ID, and be stored into the mutable * @ref signees field, then @ref signees will be returned; * otherwise, the @ref chain_id parameter will be ignored, and * @ref signees will be returned directly. */ const flat_set& get_signature_keys( const chain_id_type& chain_id )const; /** Signatures */ vector signatures; /** Public keys extracted from signatures */ mutable flat_set signees; /// Removes all operations, signatures and signees void clear() { operations.clear(); signatures.clear(); signees.clear(); } /// Removes all signatures and signees void clear_signatures() { signatures.clear(); signees.clear(); } }; void verify_authority( const vector& ops, const flat_set& sigs, const std::function& get_active, const std::function& get_owner, const std::function(account_id_type, const operation&)>& get_custom, bool ignore_custom_operation_required_auths, uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH, bool allow_committee = false, const flat_set& active_aprovals = flat_set(), const flat_set& owner_approvals = flat_set() ); /** * @brief captures the result of evaluating the operations contained in the transaction * * When processing a transaction some operations generate * new object IDs and these IDs cannot be known until the * transaction is actually included into a block. When a * block is produced these new ids are captured and included * with every transaction. The index in operation_results should * correspond to the same index in operations. * * If an operation did not create any new object IDs then 0 * should be returned. */ struct processed_transaction : public signed_transaction { processed_transaction( const signed_transaction& trx = signed_transaction() ) : signed_transaction(trx){} vector operation_results; digest_type merkle_digest()const; }; /// @} transactions group } } // graphene::chain FC_REFLECT( graphene::chain::transaction, (ref_block_num)(ref_block_prefix)(expiration)(operations)(extensions) ) // Note: not reflecting signees field for backward compatibility; in addition, it should not be in p2p messages FC_REFLECT_DERIVED( graphene::chain::signed_transaction, (graphene::chain::transaction), (signatures) ) FC_REFLECT_DERIVED( graphene::chain::processed_transaction, (graphene::chain::signed_transaction), (operation_results) ) GRAPHENE_EXTERNAL_SERIALIZATION(extern, graphene::chain::transaction) GRAPHENE_EXTERNAL_SERIALIZATION(extern, graphene::chain::signed_transaction) GRAPHENE_EXTERNAL_SERIALIZATION(extern, graphene::chain::processed_transaction)