limenkaconsensus.cpp raw
1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2022 The Limenka developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6 #include <script/limenkaconsensus.h>
7
8 #include <primitives/transaction.h>
9 #include <pubkey.h>
10 #include <script/interpreter.h>
11
12 namespace {
13
14 /** A class that deserializes a single CTransaction one time. */
15 class TxInputStream
16 {
17 public:
18 TxInputStream(const unsigned char *txTo, size_t txToLen) :
19 m_data(txTo),
20 m_remaining(txToLen)
21 {}
22
23 void read(Span<std::byte> dst)
24 {
25 if (dst.size() > m_remaining) {
26 throw std::ios_base::failure(std::string(__func__) + ": end of data");
27 }
28
29 if (dst.data() == nullptr) {
30 throw std::ios_base::failure(std::string(__func__) + ": bad destination buffer");
31 }
32
33 if (m_data == nullptr) {
34 throw std::ios_base::failure(std::string(__func__) + ": bad source buffer");
35 }
36
37 memcpy(dst.data(), m_data, dst.size());
38 m_remaining -= dst.size();
39 m_data += dst.size();
40 }
41
42 template<typename T>
43 TxInputStream& operator>>(T&& obj)
44 {
45 ::Unserialize(*this, obj);
46 return *this;
47 }
48
49 private:
50 const unsigned char* m_data;
51 size_t m_remaining;
52 };
53
54 inline int set_error(limenkaconsensus_error* ret, limenkaconsensus_error serror)
55 {
56 if (ret)
57 *ret = serror;
58 return 0;
59 }
60
61 } // namespace
62
63 /** Check that all specified flags are part of the libconsensus interface. */
64 static bool verify_flags(unsigned int flags)
65 {
66 return (flags & ~(limenkaconsensus_SCRIPT_FLAGS_VERIFY_ALL)) == 0;
67 }
68
69 static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, CAmount amount,
70 const unsigned char *txTo , unsigned int txToLen,
71 const UTXO *spentOutputs, unsigned int spentOutputsLen,
72 unsigned int nIn, unsigned int flags, limenkaconsensus_error* err)
73 {
74 if (!verify_flags(flags)) {
75 return set_error(err, limenkaconsensus_ERR_INVALID_FLAGS);
76 }
77
78 if (flags & limenkaconsensus_SCRIPT_FLAGS_VERIFY_TAPROOT && spentOutputs == nullptr) {
79 return set_error(err, limenkaconsensus_ERR_SPENT_OUTPUTS_REQUIRED);
80 }
81
82 try {
83 TxInputStream stream(txTo, txToLen);
84 CTransaction tx(deserialize, TX_WITH_WITNESS, stream);
85
86 std::vector<CTxOut> spent_outputs;
87 if (spentOutputs != nullptr) {
88 if (spentOutputsLen != tx.vin.size()) {
89 return set_error(err, limenkaconsensus_ERR_SPENT_OUTPUTS_MISMATCH);
90 }
91 for (size_t i = 0; i < spentOutputsLen; i++) {
92 CScript spk = CScript(spentOutputs[i].scriptPubKey, spentOutputs[i].scriptPubKey + spentOutputs[i].scriptPubKeySize);
93 const CAmount& value = spentOutputs[i].value;
94 CTxOut tx_out = CTxOut(value, spk);
95 spent_outputs.push_back(tx_out);
96 }
97 }
98
99 if (nIn >= tx.vin.size())
100 return set_error(err, limenkaconsensus_ERR_TX_INDEX);
101 if (GetSerializeSize(TX_WITH_WITNESS(tx)) != txToLen)
102 return set_error(err, limenkaconsensus_ERR_TX_SIZE_MISMATCH);
103
104 // Regardless of the verification result, the tx did not error.
105 set_error(err, limenkaconsensus_ERR_OK);
106
107 PrecomputedTransactionData txdata(tx);
108
109 if (spentOutputs != nullptr && flags & limenkaconsensus_SCRIPT_FLAGS_VERIFY_TAPROOT) {
110 txdata.Init(tx, std::move(spent_outputs));
111 }
112
113 return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), &tx.vin[nIn].scriptWitness, flags, TransactionSignatureChecker(&tx, nIn, amount, txdata, MissingDataBehavior::FAIL), nullptr);
114 } catch (const std::exception&) {
115 return set_error(err, limenkaconsensus_ERR_TX_DESERIALIZE); // Error deserializing
116 }
117 }
118
119 int limenkaconsensus_verify_script_with_spent_outputs(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, int64_t amount,
120 const unsigned char *txTo , unsigned int txToLen,
121 const UTXO *spentOutputs, unsigned int spentOutputsLen,
122 unsigned int nIn, unsigned int flags, limenkaconsensus_error* err)
123 {
124 CAmount am(amount);
125 return ::verify_script(scriptPubKey, scriptPubKeyLen, am, txTo, txToLen, spentOutputs, spentOutputsLen, nIn, flags, err);
126 }
127
128 int limenkaconsensus_verify_script_with_amount(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, int64_t amount,
129 const unsigned char *txTo , unsigned int txToLen,
130 unsigned int nIn, unsigned int flags, limenkaconsensus_error* err)
131 {
132 CAmount am(amount);
133 UTXO *spentOutputs = nullptr;
134 unsigned int spentOutputsLen = 0;
135 return ::verify_script(scriptPubKey, scriptPubKeyLen, am, txTo, txToLen, spentOutputs, spentOutputsLen, nIn, flags, err);
136 }
137
138
139 int limenkaconsensus_verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen,
140 const unsigned char *txTo , unsigned int txToLen,
141 unsigned int nIn, unsigned int flags, limenkaconsensus_error* err)
142 {
143 if (flags & limenkaconsensus_SCRIPT_FLAGS_VERIFY_WITNESS) {
144 return set_error(err, limenkaconsensus_ERR_AMOUNT_REQUIRED);
145 }
146
147 CAmount am(0);
148 UTXO *spentOutputs = nullptr;
149 unsigned int spentOutputsLen = 0;
150 return ::verify_script(scriptPubKey, scriptPubKeyLen, am, txTo, txToLen, spentOutputs, spentOutputsLen, nIn, flags, err);
151 }
152
153 unsigned int limenkaconsensus_version()
154 {
155 // Just use the API version for now
156 return LIMENKACONSENSUS_API_VER;
157 }
158