/* * This file is part of the Flowee project * Copyright (C) 2017-2021 Tom Zander * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FLOWEE_PRIMITIVES_FASTTRANSACTION_H #define FLOWEE_PRIMITIVES_FASTTRANSACTION_H #include #include class CTransaction; class FastBlock; class TxTokenizer; namespace Streaming { class BufferPool; } /** * @brief The Tx class is a Bitcoin transaction in canonical form. * The Tx object is a thin wrapper around a buffer of data which is known to be a Bitcoin transaction. * * @see CTransaction, FastBlock */ class Tx { public: enum Component { TxVersion = 1, ///< int PrevTxHash = 2 , ///< 32-bytes hash (uint256) PrevTxIndex = 4, ///< int or uint64_t TxInScript = 8, ///< var-length const-buffer Sequence = 0x10, /// uint32_t OutputValue = 0x20, ///< uint64_t OutputScript = 0x40, ///< var-length const-buffer LockTime = 0x80, ///< uint32_t End = 0x100 }; /// creates invalid transaction. Tx(); Tx(const Streaming::ConstBuffer &rawTransaction); Tx(const Tx &other) = default; /** * @brief isValid returns true if it has a known backing memory store. * Notice that this method doesn't do validation of the transaction data. */ inline bool isValid() const { return m_data.isValid(); } /** * Returns the version number of a transaction. */ uint32_t txVersion() const; /** * Hashes the transaction content and returns the sha256 double hash. * The hash is often also called the transaction-ID. */ uint256 createHash() const; /** * for backwards compatibility with existing code this loads the transaction into a CTransaction class. */ CTransaction createOldTransaction() const; /** * @brief offsetInBlock returns the amount of bytes into the block this transaction is positioned. * @param block the block it is supposed to be part of. * @return a simple subtraction of pointers, if the argument block doesn't contain the * tx, the result is unspecified (but typically bad) */ int64_t offsetInBlock(const FastBlock &block) const; static Tx fromOldTransaction(const CTransaction &transaction, Streaming::BufferPool *pool = 0); /** * @return the bytecount of this transaction. */ inline int size() const { return m_data.size(); } /** * @brief The Iterator class allows one to iterate over a ConstBuffer-backed transaction or block. * The Tx class doesn't have a random-access API for its contents because the class doesn't read * all the data into memory. This makes it significantly faster for many usecases and easier on * memory consumtion which is why Flowee followed this trade-off. * The correct way to find certain transaction data is to start an iterator and find it by 'walking' * over the transaction explicitly. * * Notice that little to no checks are done in the API for correct usage, which means that you could * request the LockTime variable as a uint256Data(), which is a bad idea (possible crash). So be * careful about the data-types you read actually matching the tag(). */ class Iterator { public: /// Constructor Iterator(const Tx &tx); /// This iterator skips the block-header and reads the first transaction. After a Tx::End /// it continues to the next transaction. At the end of the block Tx::Ends will continue /// repeatedly. Iterator(const FastBlock &block, int offsetInBlock = 0); Iterator(const Iterator &other) = delete; Iterator(const Iterator && other); ~Iterator(); /** * @brief next seeks to find the next tag. * @param filter allows you to filter which tags you want to find. You can pass in multiple * enum values OR-ed together. Notice that Tx::End will always implicitly be included * in the filter. * @return The output of 'tag()' * Please be aware that this method can throw a runtime_error should the transaction encounter * partial or missing data. */ Component next(int filter = 0); /// Return the current tag found. Component tag() const; /** * @brief prevTx creates a transaction object should you have gotten to the Tx::End tag. * Its very important to realize this method returns the content from the start of the transaction * to the current location, as such the only way to get a proper full transaction is just after * next() returned Tx::End * * Notice that the returned Tx is a zero-copy instance pointing to the same ConstData as backed by * the original Block. */ Tx prevTx() const; /// Return the value of the current tag as a ConstBuffer. Streaming::ConstBuffer byteData() const; /// Return the amount of bytes would be included in the byteData() int dataLength() const; /// Return the value of the current tag as a 32-bit signed int int32_t intData() const; /// Return the value of the current tag as a 32-bit unsigned int uint32_t uintData() const; /// Return the value of the current tag as a 64-bit unsigned int uint64_t longData() const; /// Return the value of the current tag as a 256-bit unsigned 'int' uint256 uint256Data() const; /// (Single) hash the current byte-data with the sha256 algo and return the result. void hashByteData(uint256 &output) const; inline uint256 hashedByteData() const { uint256 b; hashByteData(b); return b; } void operator=(const Iterator &other) = delete; private: TxTokenizer *d; }; struct Input { uint256 txid; int index = -1; int dataFile = -1; // unused here, but useful for the UnspentOutputDatabase }; struct Output { Streaming::ConstBuffer outputScript; int64_t outputValue = -1; }; /** * Takes an iterator and finds + returns all the inputs. * The iterator will halt at the first value of an output (Tx::OutputValue) or Tx::End */ static std::list findInputs(Tx::Iterator &iter); static Output nextOutput(Tx::Iterator &iter); Output output(int index) const; /// \internal inline Streaming::ConstBuffer data() const { return m_data; } inline bool operator==(const Tx &other) const { return m_data.operator==(other.m_data); } inline bool operator!=(const Tx &other) const { return !operator==(other); } Tx &operator=(const Tx &other) = default; private: Streaming::ConstBuffer m_data; }; bool operator==(const Tx::Input &a, const Tx::Input &b); inline bool operator!=(const Tx::Input &a, const Tx::Input &b) { return !operator==(a, b); } namespace boost { template <> struct hash { std::size_t operator()(const Tx::Input& k) const { uint64_t x = k.index; x = x << 32; x += k.dataFile; return k.txid.GetCheapHash() ^ x; } }; } #endif