Loading...
Searching...
No Matches
transaction.cpp
Go to the documentation of this file.
1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
21
22#include <glog/logging.h>
23
24#include <iomanip>
25#include <sstream>
26
27#include "common/crypto/hash.h"
29
30namespace resdb {
31namespace utxo {
32
33Transaction::Transaction(const Config& config, Wallet* wallet)
34 : config_(config), wallet_(wallet) {
35 tx_mempool_ = std::make_unique<TxMempool>();
36 for (const UTXO& trans : config_.genesis_transactions().transactions()) {
37 int64_t transaction_id = tx_mempool_->AddUTXO(trans);
38
39 for (const UTXOOut& output : trans.out()) {
40 int ret = wallet_->AddCoin(output.address(), output.value());
41 if (ret) {
42 LOG(ERROR) << "add coin fail";
43 assert(ret == 0);
44 }
45 }
46
47 LOG(ERROR) << "get genesis utxo:" << trans.DebugString()
48 << " transaction id:" << transaction_id;
49 }
50}
51
53
54int64_t Transaction::AddTransaction(const UTXO& utxo) {
55 absl::StatusOr<std::vector<UTXOOut>> ins_or = GetInput(utxo);
56 if (!ins_or.ok()) {
57 LOG(ERROR) << "get input fail:" << ins_or.status().message();
58 return -1;
59 }
60
61 if (!VerifyUTXO(utxo, *ins_or)) {
62 LOG(ERROR) << "transaction is not valid";
63 return -1;
64 }
65
66 if (AddCoin(utxo)) {
67 LOG(ERROR) << "add coin fail";
68 return -1;
69 }
70 return tx_mempool_->AddUTXO(utxo);
71}
72
73int64_t Transaction::AddTransaction(const std::string& utxo_str) {
74 UTXO utxo;
75 if (!utxo.ParseFromString(utxo_str)) {
76 LOG(ERROR) << "parse utxo fail";
77 return -1;
78 }
79 return AddTransaction(utxo);
80}
81
82absl::StatusOr<std::vector<UTXOOut>> Transaction::GetInput(const UTXO& utxo) {
83 std::vector<UTXOOut> utxos;
84 if (utxo.in_size() == 0) {
85 if (utxo.address() == "0000") {
86 return utxos;
87 }
88 }
89
90 int64_t verify_nonce = 0;
91 std::string public_key;
92 for (const UTXOIn& input : utxo.in()) {
93 absl::StatusOr<UTXOOut> utxo_or =
94 tx_mempool_->GetUTXO(input.prev_id(), input.out_idx(), utxo.address());
95 if (!utxo_or.ok()) {
96 LOG(ERROR) << "get pre utxo id fail:" << input.prev_id();
97 return utxo_or.status();
98 }
99 utxos.push_back(*utxo_or);
100 if (public_key.empty()) {
101 public_key = (*utxo_or).pub_key();
102 }
103 verify_nonce += input.prev_id();
104 }
105
106 if (utxos.empty()) {
107 LOG(ERROR) << "no input";
108 return absl::InvalidArgumentError("Input invalid.");
109 }
110
111 if (utxo.sig().empty()) {
112 LOG(ERROR) << "utxo no sig, invalid";
113 return absl::InvalidArgumentError("Input invalid.");
114 }
115
116 LOG(ERROR) << "get public key:" << public_key << " nonce:" << verify_nonce;
117
118 bool valid = utils::ECDSAVerifyString(
119 utxo.address() + std::to_string(verify_nonce), public_key, utxo.sig());
120 if (!valid) {
121 LOG(ERROR) << "key not valid";
122 return absl::InvalidArgumentError("Key invalid.");
123 }
124
125 for (const UTXOOut& utxo : utxos) {
126 if (utxo.pub_key() != public_key) {
127 LOG(ERROR) << "public key not match";
128 return absl::InvalidArgumentError("Key invalid.");
129 }
130 }
131
132 return utxos;
133}
134
135bool Transaction::VerifyUTXO(const UTXO& utxo,
136 const std::vector<UTXOOut>& inputs) {
137 int64_t total_input_v = 0;
138 for (const UTXOOut& input : inputs) {
139 int64_t value = input.value();
140 total_input_v += value;
141 }
142
143 for (const UTXOOut& output : utxo.out()) {
144 total_input_v -= output.value();
145 }
146
147 if (total_input_v <= 0) {
148 LOG(ERROR) << "input is not enough";
149 return false;
150 }
151
152 return true;
153}
154
155int64_t Transaction::GetUTXOOutValue(int64_t transaction_id, int out_idx,
156 const std::string& address) {
157 return tx_mempool_->GetUTXOOutValue(transaction_id, out_idx, address);
158}
159
160int Transaction::AddCoin(const UTXO& utxo) {
161 int64_t total_value = 0;
162 for (const UTXOIn& input : utxo.in()) {
163 int64_t value = tx_mempool_->MarkSpend(input.prev_id(), input.out_idx(),
164 utxo.address());
165 assert(value >= 0);
166 total_value += value;
167 }
168
169 int ret = wallet_->AddCoin(utxo.address(), -total_value);
170 if (ret) {
171 LOG(ERROR) << "add coin fail";
172 return -1;
173 }
174
175 for (const UTXOOut& output : utxo.out()) {
176 int ret = wallet_->AddCoin(output.address(), output.value());
177 if (ret) {
178 LOG(ERROR) << "add coin fail";
179 return -1;
180 }
181 }
182 return 0;
183}
184
185std::vector<UTXO> Transaction::GetUTXO(int64_t end_id, int num) {
186 return tx_mempool_->GetUTXO(end_id, num);
187}
188
189} // namespace utxo
190} // namespace resdb
std::vector< UTXO > GetUTXO(int64_t end_id, int num)
int64_t AddTransaction(const std::string &utxo_string)
Transaction(const Config &config, Wallet *wallet)
int AddCoin(const std::string &address, int64_t value)
Definition wallet.cpp:30
bool ECDSAVerifyString(const std::string &message, const std::string &public_key, const std::string &signature)
std::unique_ptr< ResDBConfig > config_