text: /*
   * Copyright (c) 2018 IOTA Stiftung
   * https://github.com/iotaledger/entangled
   *
   * Refer to the LICENSE file for licensing information
   */
  
  #ifdef __cplusplus
  extern "C" {
  #endif
  
  #ifndef __COMMON_MODEL_TRANSACTION_H_
  #define __COMMON_MODEL_TRANSACTION_H_
  
  #include <assert.h>
  
  #include "common/trinary/flex_trit.h"
  
  #define NUM_TRITS_SERIALIZED_TRANSACTION 8019
  #define NUM_TRITS_SIGNATURE 6561
  #define NUM_TRITS_MESSAGE 6561
  #define NUM_TRITS_ADDRESS 243
  #define NUM_TRITS_VALUE 81
  #define NUM_TRITS_OBSOLETE_TAG 81
  #define NUM_TRITS_TIMESTAMP 27
  #define NUM_TRITS_CURRENT_INDEX 27
  #define NUM_TRITS_LAST_INDEX 27
  #define NUM_TRITS_BUNDLE 243
  #define NUM_TRITS_TRUNK 243
  #define NUM_TRITS_BRANCH 243
  #define NUM_TRITS_TAG 81
  #define NUM_TRITS_ATTACHMENT_TIMESTAMP 27
  #define NUM_TRITS_ATTACHMENT_TIMESTAMP_LOWER 27
  #define NUM_TRITS_ATTACHMENT_TIMESTAMP_UPPER 27
  #define NUM_TRITS_NONCE 81
  #define NUM_TRITS_HASH 243
  
  #define NUM_TRYTES_SERIALIZED_TRANSACTION 2673
  #define NUM_TRYTES_SIGNATURE 2187
  #define NUM_TRYTES_MESSAGE 2187
  #define NUM_TRYTES_ADDRESS 81
  #define NUM_TRYTES_VALUE 27
  #define NUM_TRYTES_OBSOLETE_TAG 27
  #define NUM_TRYTES_TIMESTAMP 9
  #define NUM_TRYTES_CURRENT_INDEX 9
  #define NUM_TRYTES_LAST_INDEX 9
  #define NUM_TRYTES_BUNDLE 81
  #define NUM_TRYTES_TRUNK 81
  #define NUM_TRYTES_BRANCH 81
  #define NUM_TRYTES_TAG 27
  #define NUM_TRYTES_ATTACHMENT_TIMESTAMP 9
  #define NUM_TRYTES_ATTACHMENT_TIMESTAMP_LOWER 9
  #define NUM_TRYTES_ATTACHMENT_TIMESTAMP_UPPER 9
  #define NUM_TRYTES_NONCE 27
  #define NUM_TRYTES_HASH 81
  
  // bundle essence = 486 trits
  #define NUM_TRITS_ESSENCE                                         \
    (NUM_TRITS_ADDRESS + NUM_TRITS_VALUE + NUM_TRITS_OBSOLETE_TAG + \
     NUM_TRITS_TIMESTAMP + NUM_TRITS_CURRENT_INDEX + NUM_TRITS_LAST_INDEX)
  
  /***********************************************************************************************************
   * Transaction data structure
   ***********************************************************************************************************/
  
  typedef struct iota_transaction_fields_essence_s {
    // 81 trytes = 243 trits
    flex_trit_t address[FLEX_TRIT_SIZE_243];
    // 27 trytes = 81 trits
    int64_t value;
    // 27 trytes = 81 trits
    flex_trit_t obsolete_tag[FLEX_TRIT_SIZE_81];
    // 9 trytes = 27 trits
    uint64_t timestamp;
    // 9 trytes = 27 trits
    int64_t current_index;
    // 9 trytes = 27 trits
    int64_t last_index;
    // 81 trytes = 243 trits
    flex_trit_t bundle[FLEX_TRIT_SIZE_243];
  } iota_transaction_fields_essence_t;
  
  typedef struct iota_transaction_fields_attachment_s {
    // 81 trytes = 243 trits
    flex_trit_t trunk[FLEX_TRIT_SIZE_243];
    // 81 trytes = 243 trits
    flex_trit_t branch[FLEX_TRIT_SIZE_243];
    // 9 trytes = 27 trits
    uint64_t attachment_timestamp;
    // 9 trytes = 27 trits
    uint64_t attachment_timestamp_lower;
    // 9 trytes = 27 trits
    uint64_t attachment_timestamp_upper;
    // 27 trytes = 81 trits
    flex_trit_t nonce[FLEX_TRIT_SIZE_81];
    // 27 trytes = 81 trits
    flex_trit_t tag[FLEX_TRIT_SIZE_81];
  } iota_transaction_fields_attachment_t;
  
  typedef struct iota_transaction_fields_consensus_s {
    // 81 trytes = 243 trits
    flex_trit_t hash[FLEX_TRIT_SIZE_243];
  } iota_transaction_fields_consensus_t;
  
  typedef struct iota_transaction_fields_data_s {
    // 2187 trytes = 6561 trits
    flex_trit_t signature_or_message[FLEX_TRIT_SIZE_6561];
  } iota_transaction_fields_data_t;
  
  typedef struct iota_transaction_fields_metadata_s {
    uint64_t snapshot_index;
    bool solid;
    uint64_t arrival_timestamp;
  } iota_transaction_fields_metadata_t;
  
  typedef struct field_mask_s {
    int16_t essence;
    int16_t attachment;
    int16_t consensus;
    int16_t data;
    int16_t metadata;
  } field_mask_t;
  
  typedef struct iota_transaction_s {
    iota_transaction_fields_essence_t essence;
    iota_transaction_fields_attachment_t attachment;
    iota_transaction_fields_consensus_t consensus;
    iota_transaction_fields_data_t data;
    iota_transaction_fields_metadata_t metadata;
    field_mask_t loaded_columns_mask;
  } iota_transaction_t;
  
  typedef enum _field_mask_essence {
    MASK_ESSENCE_ADDRESS = (1u << 0),
    MASK_ESSENCE_VALUE = (1u << 1),
    MASK_ESSENCE_OBSOLETE_TAG = (1u << 2),
    MASK_ESSENCE_TIMESTAMP = (1u << 3),
    MASK_ESSENCE_CURRENT_INDEX = (1u << 4),
    MASK_ESSENCE_LAST_INDEX = (1u << 5),
    MASK_ESSENCE_BUNDLE = (1u << 6),
    MASK_ESSENCE_ALL = MASK_ESSENCE_ADDRESS | MASK_ESSENCE_VALUE |
                       MASK_ESSENCE_OBSOLETE_TAG | MASK_ESSENCE_TIMESTAMP |
                       MASK_ESSENCE_CURRENT_INDEX | MASK_ESSENCE_LAST_INDEX |
                       MASK_ESSENCE_BUNDLE
  } field_mask_essence_e;
  
  typedef enum _field_mask_attachment {
    MASK_ATTACHMENT_TRUNK = (1u << 0),
    MASK_ATTACHMENT_BRANCH = (1u << 1),
    MASK_ATTACHMENT_TIMESTAMP = (1u << 2),
    MASK_ATTACHMENT_TIMESTAMP_LOWER = (1u << 3),
    MASK_ATTACHMENT_TIMESTAMP_UPPER = (1u << 4),
    MASK_ATTACHMENT_NONCE = (1u << 5),
    MASK_ATTACHMENT_TAG = (1u << 6),
    MASK_ATTACHMENT_ALL = MASK_ATTACHMENT_TRUNK | MASK_ATTACHMENT_BRANCH |
                          MASK_ATTACHMENT_TIMESTAMP |
                          MASK_ATTACHMENT_TIMESTAMP_LOWER |
                          MASK_ATTACHMENT_TIMESTAMP_UPPER |
                          MASK_ATTACHMENT_NONCE | MASK_ATTACHMENT_TAG
  } field_mask_attachment_e;
  
  typedef enum _field_mask_consensus {
    MASK_CONSENSUS_HASH = (1u << 0),
    MASK_CONSENSUS_ALL = MASK_CONSENSUS_HASH
  } field_mask_consensus_e;
  
  typedef enum _field_mask_data {
    MASK_DATA_SIG_OR_MSG = (1u << 0),
    MASK_DATA_ALL = MASK_DATA_SIG_OR_MSG
  } field_mask_data_e;
  
  typedef enum _field_mask_metadata {
    MASK_METADATA_SNAPSHOT_INDEX = (1u << 0),
    MASK_METADATA_SOLID = (1u << 1),
    MASK_METADATA_ARRIVAL_TIMESTAMP = (1u << 2),
    MASK_METADATA_ALL = MASK_METADATA_SNAPSHOT_INDEX | MASK_METADATA_SOLID |
                        MASK_METADATA_ARRIVAL_TIMESTAMP
  } field_mask_metadata_e;
  
  /***********************************************************************************************************
   * Accessors
   ***********************************************************************************************************/
  
  // Get the transaction signature
  static inline flex_trit_t *transaction_signature(
      iota_transaction_t const *const transaction) {
    assert(transaction->loaded_columns_mask.data & MASK_DATA_SIG_OR_MSG);
    return (flex_trit_t *)transaction->data.signature_or_message;
  }
  
  // Set the transaction signature (copy argument)
  static inline void transaction_set_signature(
      iota_transaction_t *const transaction, flex_trit_t const *const signature) {
    memcpy(transaction->data.signature_or_message, signature,
           sizeof(transaction->data.signature_or_message));
    transaction->loaded_columns_mask.data |= MASK_DATA_SIG_OR_MSG;
  }
  
  // Get the transaction message
  static inline flex_trit_t *transaction_message(
      iota_transaction_t const *const transaction) {
    assert(transaction->loaded_columns_mask.data & MASK_DATA_SIG_OR_MSG);
    return (flex_trit_t *)transaction->data.signature_or_message;
  }
  
  // Set the transaction message (copy argument)
  static inline void transaction_set_message(
      iota_transaction_t *const transaction, flex_trit_t const *const message) {
    memcpy(transaction->data.signature_or_message, message,
           sizeof(transaction->data.signature_or_message));
    transaction->loaded_columns_mask.data |= MASK_DATA_SIG_OR_MSG;
  }
  
  // Get the transaction address
  static inline flex_trit_t *transaction_address(
      iota_transaction_t const *const transaction) {
    assert(transaction->loaded_columns_mask.essence & MASK_ESSENCE_ADDRESS);
    return (flex_trit_t *)transaction->essence.address;
  }
  
  // Set the transaction address (copy argument)
  static inline void transaction_set_address(
      iota_transaction_t *const transaction, flex_trit_t const *const address) {
    memcpy(transaction->essence.address, address,
           sizeof(transaction->essence.address));
    transaction->loaded_columns_mask.essence |= MASK_ESSENCE_ADDRESS;
  }
  
  // Get the transaction value
  static inline int64_t transaction_value(
      iota_transaction_t const *const transaction) {
    assert(transaction->loaded_columns_mask.essence & MASK_ESSENCE_VALUE);
    return transaction->essence.value;
  }
  
  // Set the transaction value
  static inline void transaction_set_value(iota_transaction_t *const transaction,
                                           int64_t const value) {
    transaction->essence.value = value;
    transaction->loaded_columns_mask.essence |= MASK_ESSENCE_VALUE;
  }
  
  // Get the transaction obsolete tag
  static inline flex_trit_t *transaction_obsolete_tag(
      iota_transaction_t const *const transaction) {
    assert(transaction->loaded_columns_mask.essence & MASK_ESSENCE_OBSOLETE_TAG);
    return (flex_trit_t *)transaction->essence.obsolete_tag;
  }
  
  // Set the transaction obsolete tag
  static inline void transaction_set_obsolete_tag(
      iota_transaction_t *const transaction,
      flex_trit_t const *const obsolete_tag) {
    memcpy(transaction->essence.obsolete_tag, obsolete_tag,
           sizeof(transaction->essence.obsolete_tag));
    transaction->loaded_columns_mask.essence |= MASK_ESSENCE_OBSOLETE_TAG;
  }
  
  // Get the transaction timestamp
  static inline uint64_t transaction_timestamp(
      iota_transaction_t const *const transaction) {
    assert(transaction->loaded_columns_mask.essence & MASK_ESSENCE_TIMESTAMP);
    return transaction->essence.timestamp;
  }
  
  // Set the transaction timestamp
  static inline void transaction_set_timestamp(
      iota_transaction_t *const transaction, uint64_t const timestamp) {
    transaction->essence.timestamp = timestamp;
    transaction->loaded_columns_mask.essence |= MASK_ESSENCE_TIMESTAMP;
  }
  
  // Get the transaction current index
  static inline int64_t transaction_current_index(
      iota_transaction_t const *const transaction) {
    assert(transaction->loaded_columns_mask.essence & MASK_ESSENCE_CURRENT_INDEX);
    return transaction->essence.current_index;
  }
  
  // Set the transaction current index
  static inline void transaction_set_current_index(
      iota_transaction_t *const transaction, int64_t const index) {
    transaction->essence.current_index = index;
    transaction->loaded_columns_mask.essence |= MASK_ESSENCE_CURRENT_INDEX;
  }
  
  // Get the transaction last index
  static inline int64_t transaction_last_index(
      iota_transaction_t const *const transaction) {
    assert(transaction->loaded_columns_mask.essence & MASK_ESSENCE_LAST_INDEX);
    return transaction->essence.last_index;
  }
  
  // Set the transaction last index
  static inline void transaction_set_last_index(
      iota_transaction_t *const transaction, int64_t const index) {
    transaction->essence.last_index = index;
    transaction->loaded_columns_mask.essence |= MASK_ESSENCE_LAST_INDEX;
  }
  
  // Get the transaction bundle
  static inline flex_trit_t *transaction_bundle(
      iota_transaction_t const *const transaction) {
    assert(transaction->loaded_columns_mask.essence & MASK_ESSENCE_BUNDLE);
    return (flex_trit_t *)transaction->essence.bundle;
  }
  
  // Set the transaction bundle (copy argument)
  static inline void transaction_set_bundle(iota_transaction_t *const transaction,
                                            flex_trit_t const *const bundle) {
    memcpy(transaction->essence.bundle, bundle,
           sizeof(transaction->essence.bundle));
    transaction->loaded_columns_mask.essence |= MASK_ESSENCE_BUNDLE;
  }
  
  // Get the transaction trunk
  static inline flex_trit_t *transaction_trunk(
      iota_transaction_t const *const transaction) {
    assert(transaction->loaded_columns_mask.attachment & MASK_ATTACHMENT_TRUNK);
    return (flex_trit_t *)transaction->attachment.trunk;
  }
  
  // Set the transaction trunk (copy argument)
  static inline void transaction_set_trunk(iota_transaction_t *const transaction,
                                           flex_trit_t const *const trunk) {
    memcpy(transaction->attachment.trunk, trunk,
           sizeof(transaction->attachment.trunk));
    transaction->loaded_columns_mask.attachment |= MASK_ATTACHMENT_TRUNK;
  }
  
  // Get the transaction branch
  static inline flex_trit_t *transaction_branch(
      iota_transaction_t const *const transaction) {
    assert(transaction->loaded_columns_mask.attachment & MASK_ATTACHMENT_BRANCH);
    return (flex_trit_t *)transaction->attachment.branch;
  }
  
  // Set the transaction branch (copy argument)
  static inline void transaction_set_branch(iota_transaction_t *const transaction,
                                            flex_trit_t const *const branch) {
    memcpy(transaction->attachment.branch, branch,
           sizeof(transaction->attachment.branch));
    transaction->loaded_columns_mask.attachment |= MASK_ATTACHMENT_BRANCH;
  }
  
  // Get the transaction tag
  static inline flex_trit_t *transaction_tag(
      iota_transaction_t const *const transaction) {
    assert(transaction->loaded_columns_mask.attachment & MASK_ATTACHMENT_TAG);
    return (flex_trit_t *)transaction->attachment.tag;
  }
  
  // Set the transaction tag (copy argument)
  static inline void transaction_set_tag(iota_transaction_t *const transaction,
                                         flex_trit_t const *const tag) {
    memcpy(transaction->attachment.tag, tag, sizeof(transaction->attachment.tag));
    transaction->loaded_columns_mask.attachment |= MASK_ATTACHMENT_TAG;
  }
  
  // Get the transaction attachment timestamp
  static inline uint64_t transaction_attachment_timestamp(
      iota_transaction_t const *const transaction) {
    assert(transaction->loaded_columns_mask.attachment &
           MASK_ATTACHMENT_TIMESTAMP);
    return transaction->attachment.attachment_timestamp;
  }
  
  // Set the transaction attachment timestamp
  static inline void transaction_set_attachment_timestamp(
      iota_transaction_t *const transaction, uint64_t const timestamp) {
    transaction->attachment.attachment_timestamp = timestamp;
    transaction->loaded_columns_mask.attachment |= MASK_ATTACHMENT_TIMESTAMP;
  }
  
  // Get the transaction attachment timestamp lower
  static inline uint64_t transaction_attachment_timestamp_lower(
      iota_transaction_t const *const transaction) {
    assert(transaction->loaded_columns_mask.attachment &
           MASK_ATTACHMENT_TIMESTAMP_LOWER);
    return transaction->attachment.attachment_timestamp_lower;
  }
  
  // Set the transaction attachment timestamp lower
  static inline void transaction_set_attachment_timestamp_lower(
      iota_transaction_t *const transaction, uint64_t const timestamp) {
    transaction->attachment.attachment_timestamp_lower = timestamp;
    transaction->loaded_columns_mask.attachment |=
        MASK_ATTACHMENT_TIMESTAMP_LOWER;
  }
  
  // Get the transaction attachment timestamp upper
  static inline uint64_t transaction_attachment_timestamp_upper(
      iota_transaction_t const *const transaction) {
    assert(transaction->loaded_columns_mask.attachment &
           MASK_ATTACHMENT_TIMESTAMP_UPPER);
    return transaction->attachment.attachment_timestamp_upper;
  }
  
  // Set the transaction attachment timestamp upper
  static inline void transaction_set_attachment_timestamp_upper(
      iota_transaction_t *const transaction, uint64_t const timestamp) {
    transaction->attachment.attachment_timestamp_upper = timestamp;
    transaction->loaded_columns_mask.attachment |=
        MASK_ATTACHMENT_TIMESTAMP_UPPER;
  }
  
  // Get the transaction nonce
  static inline flex_trit_t *transaction_nonce(
      iota_transaction_t const *const transaction) {
    assert(transaction->loaded_columns_mask.attachment & MASK_ATTACHMENT_NONCE);
    return (flex_trit_t *)transaction->attachment.nonce;
  }
  
  // Set the transaction nonce (copy argument)
  static inline void transaction_set_nonce(iota_transaction_t *const transaction,
                                           flex_trit_t const *const nonce) {
    memcpy(transaction->attachment.nonce, nonce,
           sizeof(transaction->attachment.nonce));
    transaction->loaded_columns_mask.attachment |= MASK_ATTACHMENT_NONCE;
  }
  
  // Get the transaction hash
  static inline flex_trit_t *transaction_hash(
      iota_transaction_t const *const transaction) {
    assert(transaction->loaded_columns_mask.consensus & MASK_CONSENSUS_HASH);
    return (flex_trit_t *)transaction->consensus.hash;
  }
  
  // Set the transaction hash (copy argument)
  static inline void transaction_set_hash(iota_transaction_t *const transaction,
                                          flex_trit_t const *const hash) {
    memcpy(transaction->consensus.hash, hash,
           sizeof(transaction->consensus.hash));
    transaction->loaded_columns_mask.consensus |= MASK_CONSENSUS_HASH;
  }
  
  static inline uint64_t transaction_snapshot_index(
      iota_transaction_t const *const transaction) {
    assert(transaction->loaded_columns_mask.metadata &
           MASK_METADATA_SNAPSHOT_INDEX);
    return transaction->metadata.snapshot_index;
  }
  // Set the transaction snapshot index
  static inline void transaction_set_snapshot_index(
      iota_transaction_t *const transaction, uint64_t const snapshot_index) {
    transaction->metadata.snapshot_index = snapshot_index;
    transaction->loaded_columns_mask.metadata |= MASK_METADATA_SNAPSHOT_INDEX;
  }
  
  static inline bool transaction_solid(
      iota_transaction_t const *const transaction) {
    assert(transaction->loaded_columns_mask.metadata & MASK_METADATA_SOLID);
    return transaction->metadata.solid;
  }
  // Set the transaction solid state
  static inline void transaction_set_solid(iota_transaction_t *const transaction,
                                           bool const state) {
    transaction->metadata.solid = state;
    transaction->loaded_columns_mask.metadata |= MASK_METADATA_SOLID;
  }
  
  // Get the transaction arrival timestamp
  static inline uint64_t transaction_arrival_timestamp(
      iota_transaction_t const *const transaction) {
    assert(transaction->loaded_columns_mask.metadata &
           MASK_METADATA_ARRIVAL_TIMESTAMP);
    return transaction->metadata.arrival_timestamp;
  }
  
  // Set the transaction arrival timestamp
  static inline void transaction_set_arrival_timestamp(
      iota_transaction_t *const transaction, uint64_t const timestamp) {
    transaction->metadata.arrival_timestamp = timestamp;
    transaction->loaded_columns_mask.metadata |= MASK_METADATA_ARRIVAL_TIMESTAMP;
  }
  
  /***********************************************************************************************************
   * Utility functions
   ***********************************************************************************************************/
  
  void transaction_reset(iota_transaction_t *const transaction);
  #ifdef DEBUG
  void transaction_obj_dump(iota_transaction_t *tx_obj);
  #endif
  
  uint8_t transaction_weight_magnitude(
      iota_transaction_t const *const transaction);
  
  /***********************************************************************************************************
   * Constructors
   ***********************************************************************************************************/
  // Creates and returns a new transaction without data
  iota_transaction_t *transaction_new(void);
  // Creates and returns a new transaction from serialized data - NULL if failed
  iota_transaction_t *transaction_deserialize(flex_trit_t const *const trits,
                                              bool const compute_hash);
  /***********************************************************************************************************
   * Serialization
   ***********************************************************************************************************/
  // Returns the serialized data from an existing transaction - NULL if failed
  flex_trit_t *transaction_serialize(iota_transaction_t const *const transaction);
  // Places the serialized data from an existing transaction in pre-allocated
  // string - returns non 0 on success
  size_t transaction_serialize_on_flex_trits(
      iota_transaction_t const *const transaction, flex_trit_t *const trits);
  /***********************************************************************************************************
   * Deserialization
   ***********************************************************************************************************/
  // Fills up an existing transaction with the serialized data in trits - returns
  // non 0 on success
  size_t transaction_deserialize_from_trits(iota_transaction_t *const transaction,
                                            flex_trit_t const *const trits,
                                            bool compute_hash);
  /***********************************************************************************************************
   * Destructor
   ***********************************************************************************************************/
  // Free an existing transaction - compatible with free()
  void transaction_free(iota_transaction_t *const transaction);
  
  #endif  // __COMMON_MODEL_TRANSACTION_H_
  #ifdef __cplusplus
  }
  #endif
  