executor/utxo/manager/transaction.cpp (141 lines of code) (raw):
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "executor/utxo/manager/transaction.h"
#include <glog/logging.h>
#include <iomanip>
#include <sstream>
#include "common/crypto/hash.h"
#include "common/crypto/signature_utils.h"
namespace resdb {
namespace utxo {
Transaction::Transaction(const Config& config, Wallet* wallet)
: config_(config), wallet_(wallet) {
tx_mempool_ = std::make_unique<TxMempool>();
for (const UTXO& trans : config_.genesis_transactions().transactions()) {
int64_t transaction_id = tx_mempool_->AddUTXO(trans);
for (const UTXOOut& output : trans.out()) {
int ret = wallet_->AddCoin(output.address(), output.value());
if (ret) {
LOG(ERROR) << "add coin fail";
assert(ret == 0);
}
}
LOG(ERROR) << "get genesis utxo:" << trans.DebugString()
<< " transaction id:" << transaction_id;
}
}
Transaction::~Transaction() {}
int64_t Transaction::AddTransaction(const UTXO& utxo) {
absl::StatusOr<std::vector<UTXOOut>> ins_or = GetInput(utxo);
if (!ins_or.ok()) {
LOG(ERROR) << "get input fail:" << ins_or.status().message();
return -1;
}
if (!VerifyUTXO(utxo, *ins_or)) {
LOG(ERROR) << "transaction is not valid";
return -1;
}
if (AddCoin(utxo)) {
LOG(ERROR) << "add coin fail";
return -1;
}
return tx_mempool_->AddUTXO(utxo);
}
int64_t Transaction::AddTransaction(const std::string& utxo_str) {
UTXO utxo;
if (!utxo.ParseFromString(utxo_str)) {
LOG(ERROR) << "parse utxo fail";
return -1;
}
return AddTransaction(utxo);
}
absl::StatusOr<std::vector<UTXOOut>> Transaction::GetInput(const UTXO& utxo) {
std::vector<UTXOOut> utxos;
if (utxo.in_size() == 0) {
if (utxo.address() == "0000") {
return utxos;
}
}
int64_t verify_nonce = 0;
std::string public_key;
for (const UTXOIn& input : utxo.in()) {
absl::StatusOr<UTXOOut> utxo_or =
tx_mempool_->GetUTXO(input.prev_id(), input.out_idx(), utxo.address());
if (!utxo_or.ok()) {
LOG(ERROR) << "get pre utxo id fail:" << input.prev_id();
return utxo_or.status();
}
utxos.push_back(*utxo_or);
if (public_key.empty()) {
public_key = (*utxo_or).pub_key();
}
verify_nonce += input.prev_id();
}
if (utxos.empty()) {
LOG(ERROR) << "no input";
return absl::InvalidArgumentError("Input invalid.");
}
if (utxo.sig().empty()) {
LOG(ERROR) << "utxo no sig, invalid";
return absl::InvalidArgumentError("Input invalid.");
}
LOG(ERROR) << "get public key:" << public_key << " nonce:" << verify_nonce;
bool valid = utils::ECDSAVerifyString(
utxo.address() + std::to_string(verify_nonce), public_key, utxo.sig());
if (!valid) {
LOG(ERROR) << "key not valid";
return absl::InvalidArgumentError("Key invalid.");
}
for (const UTXOOut& utxo : utxos) {
if (utxo.pub_key() != public_key) {
LOG(ERROR) << "public key not match";
return absl::InvalidArgumentError("Key invalid.");
}
}
return utxos;
}
bool Transaction::VerifyUTXO(const UTXO& utxo,
const std::vector<UTXOOut>& inputs) {
int64_t total_input_v = 0;
for (const UTXOOut& input : inputs) {
int64_t value = input.value();
total_input_v += value;
}
for (const UTXOOut& output : utxo.out()) {
total_input_v -= output.value();
}
if (total_input_v <= 0) {
LOG(ERROR) << "input is not enough";
return false;
}
return true;
}
int64_t Transaction::GetUTXOOutValue(int64_t transaction_id, int out_idx,
const std::string& address) {
return tx_mempool_->GetUTXOOutValue(transaction_id, out_idx, address);
}
int Transaction::AddCoin(const UTXO& utxo) {
int64_t total_value = 0;
for (const UTXOIn& input : utxo.in()) {
int64_t value = tx_mempool_->MarkSpend(input.prev_id(), input.out_idx(),
utxo.address());
assert(value >= 0);
total_value += value;
}
int ret = wallet_->AddCoin(utxo.address(), -total_value);
if (ret) {
LOG(ERROR) << "add coin fail";
return -1;
}
for (const UTXOOut& output : utxo.out()) {
int ret = wallet_->AddCoin(output.address(), output.value());
if (ret) {
LOG(ERROR) << "add coin fail";
return -1;
}
}
return 0;
}
std::vector<UTXO> Transaction::GetUTXO(int64_t end_id, int num) {
return tx_mempool_->GetUTXO(end_id, num);
}
} // namespace utxo
} // namespace resdb