Compare commits
121 Commits
v1.0.0
...
v1.1.0-rc4
| Author | SHA1 | Date | |
|---|---|---|---|
| eadaa21e83 | |||
| 465b384195 | |||
| 768ca26a8d | |||
| 6fb131a52b | |||
| 3f18c388f6 | |||
| 2f646cdc10 | |||
| a5523be459 | |||
| 81d322b2ce | |||
| 3b1a939417 | |||
| a4e7ebc591 | |||
| 25dccd5423 | |||
| d05ac7f44e | |||
| 35b1a03b3d | |||
| 7acf8068ea | |||
| 383f3d36e6 | |||
| 7d06436d08 | |||
| 3bee380b18 | |||
| 58c70115f2 | |||
| 6be4081332 | |||
| 538e4a5d1f | |||
| 3e49572539 | |||
| 05c7152ad5 | |||
| 8d31fa2842 | |||
| 2abe39f178 | |||
| a6f47a9f92 | |||
| ac13287c78 | |||
| 305b92909e | |||
| 248667016a | |||
| 10b58aac73 | |||
| 0221fe8a34 | |||
| 1ff480e64d | |||
| fd121aae19 | |||
| 0deb19c53c | |||
| 7f3e389d92 | |||
| 87da2d4661 | |||
| f6075ae9ec | |||
| c424e84f4b | |||
| 3b4efe9636 | |||
| dfa27e78c6 | |||
| 9b57fe3eae | |||
| 8f60758a3c | |||
| 3a7ec4db32 | |||
| 679bc9f0d7 | |||
| 362eb38ff8 | |||
| 6243e992cf | |||
| e872414d57 | |||
| fcaf640bcb | |||
| f5237ceaf5 | |||
| 1503ec6629 | |||
| 0f744520ad | |||
| caf52cca20 | |||
| 1c4309c400 | |||
| 9725b921a5 | |||
| 38d2515dc5 | |||
| 13efd79f88 | |||
| 0c273d3571 | |||
| d22389b37a | |||
| f9b060e552 | |||
| 7d13f90e4a | |||
| f7a75a4fdc | |||
| b9fa97daad | |||
| 6f0f5b5e83 | |||
| 6fbd3184b1 | |||
| d5fee31ec6 | |||
| 2a93e04180 | |||
| b902ec9406 | |||
| e3ba570fb1 | |||
| fac65e5093 | |||
| eebc1f1d26 | |||
| 502ece5ba3 | |||
| 62967db201 | |||
| 4b7f863c71 | |||
| 55a6f17c91 | |||
| 8a32d7f73b | |||
| dfb6a705ea | |||
| 5246138398 | |||
| cc3e6a0822 | |||
| 8e68f58eff | |||
| 0a79a4d9fd | |||
| 48fb95bdc1 | |||
| bb4d3768b2 | |||
| cea3f0f341 | |||
| 35fc88c0ec | |||
| 312413aeb0 | |||
| 6ba060e116 | |||
| 88d5e1f50e | |||
| 644a8d3b4d | |||
| e80d135c15 | |||
| a40026f941 | |||
| fb25a36bf2 | |||
| 6f3f7c8e9a | |||
| c6c35d5639 | |||
| 63433cc58f | |||
| 940c0e03f7 | |||
| 445e498fbf | |||
| 09ad75f0cc | |||
| f8d9f9335e | |||
| cd31dafa97 | |||
| 6aa32701b8 | |||
| f84a622bfa | |||
| f60b7209f8 | |||
| 3b00a41fff | |||
| 119a7fab57 | |||
| 6d0a4a4d7b | |||
| 45404ecc71 | |||
| e5bfc2f6ad | |||
| c5be51053a | |||
| 4bfb5f51bf | |||
| d7a9facee9 | |||
| 260bc3721b | |||
| 3e0457de09 | |||
| ce6f6e0593 | |||
| f1efb700d2 | |||
| dd246296b9 | |||
| 22334d7c6c | |||
| c07698fcc8 | |||
| ce69272638 | |||
| 2b95d100b3 | |||
| b7706d4def | |||
| 24f9916287 | |||
| cd22e55296 |
@@ -22,7 +22,7 @@ env:
|
||||
|
||||
jobs:
|
||||
build-cross:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
CCACHE_TEMPDIR: /tmp/.ccache-temp
|
||||
strategy:
|
||||
@@ -52,10 +52,10 @@ jobs:
|
||||
packages: "gperf cmake python3-zmq libdbus-1-dev libharfbuzz-dev"
|
||||
- name: "Cross-Mac x86_64"
|
||||
host: "x86_64-apple-darwin"
|
||||
packages: "cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev python3-setuptools-git"
|
||||
packages: "cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python3-dev python3-setuptools-git"
|
||||
- name: "Cross-Mac aarch64"
|
||||
host: "aarch64-apple-darwin"
|
||||
packages: "cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev python3-setuptools-git"
|
||||
packages: "cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python3-dev python3-setuptools-git"
|
||||
- name: "x86_64 Freebsd"
|
||||
host: "x86_64-unknown-freebsd"
|
||||
packages: "clang-8 gperf cmake python3-zmq libdbus-1-dev libharfbuzz-dev"
|
||||
@@ -105,7 +105,7 @@ jobs:
|
||||
${{env.CCACHE_SETTINGS}}
|
||||
make depends target=${{ matrix.toolchain.host }} -j2
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ matrix.toolchain.host == 'x86_64-w64-mingw32' || matrix.toolchain.host == 'x86_64-apple-darwin' || matrix.toolchain.host == 'aarch64-apple-darwin' || matrix.toolchain.host == 'x86_64-unknown-linux-gnu' }}
|
||||
if: ${{ matrix.toolchain.host == 'x86_64-w64-mingw32' || matrix.toolchain.host == 'x86_64-apple-darwin' || matrix.toolchain.host == 'aarch64-apple-darwin' || matrix.toolchain.host == 'x86_64-unknown-linux-gnu' || matrix.toolchain.host == 'aarch64-linux-gnu' }}
|
||||
with:
|
||||
name: ${{ matrix.toolchain.name }}
|
||||
path: |
|
||||
|
||||
+4
-2
@@ -3,14 +3,16 @@ FROM ubuntu:24.04 AS builder
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Install build dependencies
|
||||
# Install build dependencies including cross-compilers
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
build-essential cmake pkg-config git imagemagick libcap-dev librsvg2-bin libz-dev \
|
||||
g++-mingw-w64-x86-64 clang gcc-arm-none-eabi binutils-x86-64-linux-gnu libtiff-tools \
|
||||
g++-x86-64-linux-gnu gcc-x86-64-linux-gnu binutils-aarch64-linux-gnu \
|
||||
g++-aarch64-linux-gnu gcc-aarch64-linux-gnu crossbuild-essential-amd64 \
|
||||
libssl-dev libzmq3-dev libunbound-dev libsodium-dev libunwind8-dev liblzma-dev \
|
||||
libreadline-dev libexpat1-dev libpgm-dev qttools5-dev-tools libhidapi-dev libusb-1.0-0-dev \
|
||||
libprotobuf-dev protobuf-compiler libudev-dev libboost-all-dev python3 ccache doxygen graphviz \
|
||||
ca-certificates curl zip libtool gperf \
|
||||
ca-certificates curl zip libtool gperf automake autoconf \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Clone the develop branch
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Salvium One v1.0.0
|
||||
# Salvium One v1.0.7
|
||||
|
||||
Copyright (c) 2023-2025, Salvium
|
||||
Portions Copyright (c) 2014-2023, The Monero Project
|
||||
@@ -172,7 +172,7 @@ invokes cmake commands as needed.
|
||||
|
||||
```bash
|
||||
cd salvium
|
||||
git checkout v1.0.0
|
||||
git checkout v1.0.7
|
||||
make
|
||||
```
|
||||
|
||||
@@ -251,7 +251,7 @@ Tested on a Raspberry Pi Zero with a clean install of minimal Raspbian Stretch (
|
||||
```bash
|
||||
git clone https://github.com/salvium/salvium
|
||||
cd salvium
|
||||
git checkout v1.0.0
|
||||
git checkout v1.0.7
|
||||
```
|
||||
|
||||
* Build:
|
||||
@@ -370,10 +370,10 @@ application.
|
||||
cd salvium
|
||||
```
|
||||
|
||||
* If you would like a specific [version/tag](https://github.com/salvium/salvium/tags), do a git checkout for that version. eg. 'v1.0.0'. If you don't care about the version and just want binaries from master, skip this step:
|
||||
* If you would like a specific [version/tag](https://github.com/salvium/salvium/tags), do a git checkout for that version. eg. 'v1.0.7'. If you don't care about the version and just want binaries from master, skip this step:
|
||||
|
||||
```bash
|
||||
git checkout v1.0.0
|
||||
git checkout v1.0.7
|
||||
```
|
||||
|
||||
* If you are on a 64-bit system, run:
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
48417220800f174a3613881a56131d25c49d344228fca124c8331e0b58a8ff06 salvium-v1.0.7-ubuntu22.04-linux-x86_64.zip
|
||||
52226551a9e1842df46cf068292cfa3c1e05328e0695cf6723365d4401af19a6 salvium-v1.0.7-ubuntu22.04-linux-aarch64.zip
|
||||
b40c3765479c9d5712e766ddd01314b63e5080472b7639d34388e6b74b36142e salvium-v1.0.7-macos-x86_64.zip
|
||||
60b05548f69040fe901e336e8fc4189a1028a8d7ded09bad555b3c854a0e8d6e salvium-v1.0.7-macos-aarch64.zip
|
||||
b2c3e0bb8254ad1f62a78d6670c6e5adba1e42bb823919faad1cbf5796d53910 salvium-v1.0.7-win64.zip
|
||||
@@ -0,0 +1,5 @@
|
||||
d0a8b0515ae2ee79849cbc17b0639bb7859e30efcd50e5b058540874cd0919ce salvium-v1.1.0-rc4-ubuntu22.04-linux-x86_64.zip
|
||||
6528b8b23f09c574fc9383b48b88e87f99609ff5ce1b727872b5554505a69d77 salvium-v1.1.0-rc4-ubuntu22.04-linux-aarch64.zip
|
||||
00ca183c47f852b8b30e4b87ce3b3536a0e97866e6027bf25d389b4a1bc9c471 salvium-v1.1.0-rc4-macos-x86_64.zip
|
||||
50480b1043e9b5901576ab45f926b0b51a5d21237c6da549603ad1f13819e6ed salvium-v1.1.0-rc4-macos-aarch64.zip
|
||||
adf96eee17e16f318fc047721acb5bfa8ae7ed1c0ff8d022da8ab5df64d8ce3f salvium-v1.1.0-rc4-win64.zip
|
||||
@@ -294,7 +294,22 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
|
||||
time1 = epee::misc_utils::get_tick_count();
|
||||
|
||||
uint64_t num_rct_outs = 0;
|
||||
oracle::asset_type_counts num_rct_outs_by_asset_type;
|
||||
oracle::asset_type_counts_v2 num_rct_outs_by_asset_type;
|
||||
|
||||
// add newly created tokens
|
||||
for (const std::pair<transaction, blobdata>& tx : txs)
|
||||
{
|
||||
if (tx.first.type == cryptonote::transaction_type::CREATE_TOKEN)
|
||||
{
|
||||
uint32_t asset_type_id = cryptonote::asset_id_from_type("sal" + tx.first.token_metadata.asset_type);
|
||||
if (!num_rct_outs_by_asset_type.add_asset_type(asset_type_id))
|
||||
throw std::runtime_error("Failed to add asset_type 'sal" + tx.first.token_metadata.asset_type + "'");
|
||||
}
|
||||
|
||||
if (!is_tx_paid_for(tx.first))
|
||||
throw std::runtime_error("TX is not paid for");
|
||||
}
|
||||
|
||||
blobdata miner_bd = tx_to_blob(blk.miner_tx);
|
||||
add_transaction(blk_hash, std::make_pair(blk.miner_tx, blobdata_ref(miner_bd)));
|
||||
blobdata protocol_bd = tx_to_blob(blk.protocol_tx);
|
||||
@@ -309,12 +324,14 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
|
||||
std::string asset_type;
|
||||
if (!get_output_asset_type(vout, asset_type))
|
||||
throw std::runtime_error("Failed to get output asset type");
|
||||
num_rct_outs_by_asset_type.add(asset_type, 1);
|
||||
uint32_t asset_type_id = cryptonote::asset_id_from_type(asset_type);
|
||||
num_rct_outs_by_asset_type.add_asset_type(asset_type_id);
|
||||
num_rct_outs_by_asset_type.add(asset_type_id, 1);
|
||||
}
|
||||
}
|
||||
|
||||
std::map<std::string, int64_t> slippage_counts;
|
||||
uint64_t audit_total = 0, yield_total = 0;
|
||||
uint64_t audit_total = 0, token_total = 0, yield_total = 0;
|
||||
if (blk.protocol_tx.version >= 2)
|
||||
{
|
||||
num_rct_outs += blk.protocol_tx.vout.size();
|
||||
@@ -326,10 +343,12 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
|
||||
std::string asset_type;
|
||||
if (!get_output_asset_type(vout, asset_type))
|
||||
throw std::runtime_error("Failed to get output asset type");
|
||||
uint32_t asset_type_id = cryptonote::asset_id_from_type(asset_type);
|
||||
|
||||
// Update the RCT outs for the asset_type
|
||||
num_rct_outs_by_asset_type.add_asset_type(asset_type_id);
|
||||
num_rct_outs_by_asset_type.add(asset_type_id, 1);
|
||||
|
||||
// Update the RCT outs
|
||||
num_rct_outs_by_asset_type.add(asset_type, 1);
|
||||
|
||||
// Update the amount tallies by DEDUCTING the minted amount
|
||||
if (slippage_counts.count(asset_type) == 0)
|
||||
slippage_counts[asset_type] = 0;
|
||||
@@ -350,7 +369,9 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
|
||||
throw std::runtime_error("Failed to get output asset type");
|
||||
if (vout.amount == 0) {
|
||||
++num_rct_outs;
|
||||
num_rct_outs_by_asset_type.add(asset_type, 1);
|
||||
uint32_t asset_type_id = cryptonote::asset_id_from_type(asset_type);
|
||||
num_rct_outs_by_asset_type.add_asset_type(asset_type_id);
|
||||
num_rct_outs_by_asset_type.add(asset_type_id, 1);
|
||||
}
|
||||
|
||||
// Is this a CONVERT TX?
|
||||
@@ -367,6 +388,16 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
|
||||
audit_total += tx.first.amount_burnt;
|
||||
}
|
||||
|
||||
// Is this an create_token TX?
|
||||
if (tx.first.type == cryptonote::transaction_type::CREATE_TOKEN) {
|
||||
token_total += tx.first.amount_burnt;
|
||||
/*
|
||||
uint32_t asset_type_id = cryptonote::asset_id_from_type(tx.first.token_metadata.asset_type);
|
||||
if (!num_rct_outs_by_asset_type.add_asset_type(asset_type_id))
|
||||
throw std::runtime_error("Failed to add asset_type '" + tx.first.token_metadata.asset_type + "' to RCT outputs");
|
||||
*/
|
||||
}
|
||||
|
||||
// Is this a STAKE TX?
|
||||
if (tx.first.type == cryptonote::transaction_type::STAKE) {
|
||||
yield_total += tx.first.amount_burnt;
|
||||
@@ -420,7 +451,7 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
|
||||
|
||||
// call out to subclass implementation to add the block & metadata
|
||||
time1 = epee::misc_utils::get_tick_count();
|
||||
add_block(blk, block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, num_rct_outs, num_rct_outs_by_asset_type, blk_hash, slippage_total, yield_total, audit_total, nettype, ybi, abi);
|
||||
add_block(blk, block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, num_rct_outs, num_rct_outs_by_asset_type, blk_hash, slippage_total, yield_total, audit_total, token_total, nettype, ybi, abi);
|
||||
TIME_MEASURE_FINISH(time1);
|
||||
time_add_block1 += time1;
|
||||
|
||||
|
||||
@@ -218,6 +218,18 @@ typedef struct yield_tx_info_carrot {
|
||||
carrot::encrypted_janus_anchor_t return_anchor_enc;
|
||||
} yield_tx_info_carrot;
|
||||
|
||||
typedef struct token_tx_info_carrot {
|
||||
uint64_t block_height;
|
||||
uint8_t version;
|
||||
crypto::hash tx_hash;
|
||||
uint64_t locked_coins;
|
||||
crypto::public_key return_address;
|
||||
crypto::public_key return_pubkey;
|
||||
carrot::view_tag_t return_view_tag;
|
||||
carrot::encrypted_janus_anchor_t return_anchor_enc;
|
||||
uint32_t asset_type_id;
|
||||
} token_tx_info_carrot;
|
||||
|
||||
#define DBF_SAFE 1
|
||||
#define DBF_FAST 2
|
||||
#define DBF_FASTEST 4
|
||||
@@ -437,11 +449,12 @@ private:
|
||||
const difficulty_type& cumulative_difficulty,
|
||||
const uint64_t& coins_generated,
|
||||
uint64_t num_rct_outs,
|
||||
oracle::asset_type_counts& cum_rct_by_asset_type,
|
||||
oracle::asset_type_counts_v2& cum_rct_by_asset_type,
|
||||
const crypto::hash& blk_hash,
|
||||
uint64_t slippage_total,
|
||||
uint64_t yield_total,
|
||||
uint64_t audit_total,
|
||||
uint64_t token_total,
|
||||
const cryptonote::network_type nettype,
|
||||
cryptonote::yield_block_info& ybi,
|
||||
cryptonote::audit_block_info& abi
|
||||
@@ -1212,6 +1225,20 @@ public:
|
||||
virtual std::map<std::string, uint64_t> get_circulating_supply() const = 0;
|
||||
|
||||
|
||||
/**
|
||||
* @brief fetch the tokens from the blockchain
|
||||
*
|
||||
* @return the current token values
|
||||
*/
|
||||
virtual std::map<std::string, cryptonote::token_metadata_t> get_tokens() const = 0;
|
||||
|
||||
/**
|
||||
* @brief determine if a TX has been paid for
|
||||
*
|
||||
* @return the current token values
|
||||
*/
|
||||
virtual bool is_tx_paid_for(const cryptonote::transaction& tx) const = 0;
|
||||
|
||||
/**
|
||||
* <!--
|
||||
* TODO: Rewrite (if necessary) such that all calls to remove_* are
|
||||
@@ -1937,6 +1964,8 @@ public:
|
||||
|
||||
virtual int get_carrot_yield_tx_info(const uint64_t height, std::vector<yield_tx_info_carrot>& yti_container) const = 0;
|
||||
|
||||
virtual int get_token_tx_info(const uint64_t height, std::vector<token_tx_info_carrot>& tti_container) const = 0;
|
||||
|
||||
|
||||
/**
|
||||
* @brief set whether or not to automatically remove logs
|
||||
|
||||
@@ -64,7 +64,7 @@ using epee::string_tools::pod_to_hex;
|
||||
using namespace crypto;
|
||||
|
||||
// Increase when the DB structure changes
|
||||
#define VERSION 2
|
||||
#define VERSION 3
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -222,6 +222,12 @@ namespace
|
||||
* audit_block_data block height {locked_coins, lc_total}
|
||||
* audit_tx_data block height {txn hash, locked_coins, return_address}
|
||||
*
|
||||
* token_tx_data block height {txn hash, asset_type, supply, return_address}
|
||||
*
|
||||
* rct_count_info block height {serialised RCT asset counts}
|
||||
*
|
||||
* rollup_tx_info rollup_tag {serialised rollup_tx_data_t struct}
|
||||
*
|
||||
* Note: where the data items are of uniform size, DUPFIXED tables have
|
||||
* been used to save space. In most of these cases, a dummy "zerokval"
|
||||
* key is used when accessing the table; the Key listed above will be
|
||||
@@ -300,6 +306,14 @@ const char* const LMDB_AUDIT_TXS = "audit_txs";
|
||||
const char* const LMDB_AUDIT_BLOCKS = "audit_blocks";
|
||||
const char* const LMDB_CARROT_YIELD_TXS = "carrot_yield_txs";
|
||||
|
||||
const char* const LMDB_TOKEN_TXS = "token_txs";
|
||||
|
||||
const char* const LMDB_TOKENS = "tokens";
|
||||
|
||||
const char* const LMDB_RCT_COUNT_INFO = "rct_count_info";
|
||||
|
||||
const char* const LMDB_ROLLUP_TX_INFO = "rollup_tx_info";
|
||||
|
||||
const char zerokey[8] = {0};
|
||||
const MDB_val zerokval = { sizeof(zerokey), (void *)zerokey };
|
||||
|
||||
@@ -357,7 +371,21 @@ typedef struct mdb_block_info_1
|
||||
oracle::asset_type_counts bi_cum_rct_by_asset_type;
|
||||
} mdb_block_info_1;
|
||||
|
||||
typedef mdb_block_info_1 mdb_block_info;
|
||||
typedef struct mdb_block_info_2
|
||||
{
|
||||
uint64_t bi_height;
|
||||
uint64_t bi_timestamp;
|
||||
uint64_t bi_coins;
|
||||
uint64_t bi_weight; // a size_t really but we need 32-bit compat
|
||||
uint64_t bi_diff_lo;
|
||||
uint64_t bi_diff_hi;
|
||||
crypto::hash bi_hash;
|
||||
uint64_t bi_cum_rct;
|
||||
uint64_t bi_long_term_block_weight;
|
||||
oracle::pricing_record bi_pricing_record;
|
||||
} mdb_block_info_2;
|
||||
|
||||
typedef mdb_block_info_2 mdb_block_info;
|
||||
|
||||
typedef struct blk_height {
|
||||
crypto::hash bh_hash;
|
||||
@@ -879,6 +907,44 @@ int BlockchainLMDB::get_audit_tx_info(const uint64_t height, std::vector<yield_t
|
||||
return 0;
|
||||
}
|
||||
|
||||
int BlockchainLMDB::get_token_tx_info(const uint64_t height, std::vector<token_tx_info_carrot>& tti_container) const
|
||||
{
|
||||
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||
check_open();
|
||||
|
||||
// Clear the container
|
||||
tti_container.clear();
|
||||
|
||||
// Query for the (presumably matured) TOKEN_TX_INFO information
|
||||
TXN_PREFIX_RDONLY();
|
||||
RCURSOR(token_txs);
|
||||
|
||||
MDB_val v;
|
||||
MDB_val_set(k, height);
|
||||
MDB_cursor_op op = MDB_SET;
|
||||
while (1)
|
||||
{
|
||||
int ret = mdb_cursor_get(m_cur_token_txs, &k, &v, op);
|
||||
op = MDB_NEXT_DUP;
|
||||
if (ret == MDB_NOTFOUND)
|
||||
break;
|
||||
if (ret)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to enumerate audit TX info: ", ret).c_str()));
|
||||
|
||||
// Get the data
|
||||
token_tx_info_carrot *p = (token_tx_info_carrot*)v.mv_data;
|
||||
// Push result back into the container
|
||||
tti_container.emplace_back(*p);
|
||||
// Update the height retrospectively (because the DB stores the count of elements there to handle duplicates, because it's rubbish)
|
||||
tti_container.back().block_height = height;
|
||||
}
|
||||
|
||||
TXN_POSTFIX_RDONLY();
|
||||
|
||||
// Return success to caller
|
||||
return 0;
|
||||
}
|
||||
|
||||
int BlockchainLMDB::get_yield_block_info(const uint64_t height, yield_block_info& ybi) const
|
||||
{
|
||||
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||
@@ -985,7 +1051,21 @@ int BlockchainLMDB::get_carrot_yield_tx_info(const uint64_t height, std::vector<
|
||||
return 0;
|
||||
}
|
||||
|
||||
void BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t long_term_block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, uint64_t num_rct_outs, oracle::asset_type_counts& cum_rct_by_asset_type, const crypto::hash& blk_hash, uint64_t slippage_total, uint64_t yield_total, uint64_t audit_total, const cryptonote::network_type nettype, cryptonote::yield_block_info& ybi, cryptonote::audit_block_info& abi)
|
||||
void BlockchainLMDB::add_block(const block& blk,
|
||||
size_t block_weight,
|
||||
uint64_t long_term_block_weight,
|
||||
const difficulty_type& cumulative_difficulty,
|
||||
const uint64_t& coins_generated,
|
||||
uint64_t num_rct_outs,
|
||||
oracle::asset_type_counts_v2& cum_rct_by_asset_type,
|
||||
const crypto::hash& blk_hash,
|
||||
uint64_t slippage_total,
|
||||
uint64_t yield_total,
|
||||
uint64_t audit_total,
|
||||
uint64_t token_total,
|
||||
const cryptonote::network_type nettype,
|
||||
cryptonote::yield_block_info& ybi,
|
||||
cryptonote::audit_block_info& abi)
|
||||
{
|
||||
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||
check_open();
|
||||
@@ -1020,7 +1100,7 @@ void BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t l
|
||||
abi.block_height = m_height;
|
||||
abi.locked_coins_this_block = audit_total;
|
||||
|
||||
// Put the YBI into the table
|
||||
// Put the ABI into the table
|
||||
MDB_val_set(key, m_height);
|
||||
MDB_val_set(abi_val, abi);
|
||||
result = mdb_cursor_put(m_cur_audit_blocks, &key, &abi_val, MDB_APPEND);
|
||||
@@ -1070,6 +1150,7 @@ void BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t l
|
||||
|
||||
CURSOR(blocks)
|
||||
CURSOR(block_info)
|
||||
CURSOR(rct_count_info)
|
||||
|
||||
// this call to mdb_cursor_put will change height()
|
||||
cryptonote::blobdata block_blob(block_to_blob(blk));
|
||||
@@ -1088,6 +1169,7 @@ void BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t l
|
||||
bi.bi_hash = blk_hash;
|
||||
bi.bi_cum_rct = num_rct_outs;
|
||||
bi.bi_pricing_record = blk.pricing_record;
|
||||
|
||||
if (m_height > 0)
|
||||
{
|
||||
uint64_t last_height = m_height-1;
|
||||
@@ -1096,21 +1178,40 @@ void BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t l
|
||||
throw1(BLOCK_DNE(lmdb_error("Failed to get block info: ", result).c_str()));
|
||||
const mdb_block_info *bi_prev = (const mdb_block_info*)h.mv_data;
|
||||
bi.bi_cum_rct += bi_prev->bi_cum_rct;
|
||||
for (auto const& asset_type : oracle::ASSET_TYPES)
|
||||
cum_rct_by_asset_type.add(asset_type, bi_prev->bi_cum_rct_by_asset_type[asset_type]);
|
||||
|
||||
oracle::asset_type_counts_v2 cum_rct_counts_prev;
|
||||
MDB_val_copy<uint64_t> key_prev_height(last_height);
|
||||
MDB_val val_prev_counts;
|
||||
if ((result = mdb_cursor_get(m_cur_rct_count_info, &key_prev_height, &val_prev_counts, MDB_SET)))
|
||||
throw1(BLOCK_DNE(lmdb_error("Failed to get previous rct count info: ", result).c_str()));
|
||||
cum_rct_counts_prev.deserialize((uint8_t*)val_prev_counts.mv_data, val_prev_counts.mv_size);
|
||||
for (auto const& asset_type_id : cum_rct_counts_prev.get_asset_types()) {
|
||||
if (!cum_rct_by_asset_type.has_asset_type(asset_type_id)) {
|
||||
cum_rct_by_asset_type.add_asset_type(asset_type_id);
|
||||
}
|
||||
cum_rct_by_asset_type.add(asset_type_id, cum_rct_counts_prev[asset_type_id]);
|
||||
}
|
||||
}
|
||||
bi.bi_long_term_block_weight = long_term_block_weight;
|
||||
bi.bi_cum_rct_by_asset_type = cum_rct_by_asset_type;
|
||||
|
||||
MDB_val_set(val, bi);
|
||||
result = mdb_cursor_put(m_cur_block_info, (MDB_val *)&zerokval, &val, MDB_APPENDDUP);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to add block info to db transaction: ", result).c_str()));
|
||||
|
||||
|
||||
result = mdb_cursor_put(m_cur_block_heights, (MDB_val *)&zerokval, &val_h, 0);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to add block height by hash to db transaction: ", result).c_str()));
|
||||
|
||||
// Update the rct_count_info table
|
||||
std::vector<uint8_t> serialized_counts = cum_rct_by_asset_type.serialize();
|
||||
MDB_val_copy<uint64_t> key_height(m_height);
|
||||
MDB_val_sized(val_counts, serialized_counts);
|
||||
CURSOR(rct_count_info)
|
||||
result = mdb_cursor_put(m_cur_rct_count_info, &key_height, &val_counts, 0);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to add asset_type_counts to db transaction: ", result).c_str()));
|
||||
|
||||
// we use weight as a proxy for size, since we don't have size but weight is >= size
|
||||
// and often actually equal
|
||||
m_cum_size += block_weight;
|
||||
@@ -1135,6 +1236,8 @@ void BlockchainLMDB::remove_block()
|
||||
CURSOR(circ_supply_tally)
|
||||
CURSOR(yield_blocks)
|
||||
CURSOR(audit_blocks)
|
||||
CURSOR(rct_count_info)
|
||||
|
||||
MDB_val_copy<uint64_t> k(m_height - 1);
|
||||
MDB_val h = k;
|
||||
if ((result = mdb_cursor_get(m_cur_block_info, (MDB_val *)&zerokval, &h, MDB_GET_BOTH)))
|
||||
@@ -1157,7 +1260,6 @@ void BlockchainLMDB::remove_block()
|
||||
throw1(DB_ERROR(lmdb_error("Failed to add removal of block info to db transaction: ", result).c_str()));
|
||||
|
||||
MDB_val_copy<uint64_t> k2(m_height - 1);
|
||||
MDB_val v = k2;
|
||||
if ((result = mdb_cursor_get(m_cur_yield_blocks, &k2, NULL, MDB_SET)))
|
||||
throw1(BLOCK_DNE(lmdb_error("Attempting to remove yield block info that's not in the db: ", result).c_str()));
|
||||
if ((result = mdb_cursor_del(m_cur_yield_blocks, 0)))
|
||||
@@ -1167,6 +1269,11 @@ void BlockchainLMDB::remove_block()
|
||||
if ((result = mdb_cursor_get(m_cur_audit_blocks, &k2, NULL, MDB_SET)) == 0)
|
||||
if ((result = mdb_cursor_del(m_cur_audit_blocks, 0)))
|
||||
throw1(DB_ERROR(lmdb_error("Failed to add removal of audit block info to db transaction: ", result).c_str()));
|
||||
|
||||
if ((result = mdb_cursor_get(m_cur_rct_count_info, &k2, NULL, MDB_SET)))
|
||||
throw1(BLOCK_DNE(lmdb_error("Attempting to remove rct count info that's not in the db: ", result).c_str()));
|
||||
if ((result = mdb_cursor_del(m_cur_rct_count_info, 0)))
|
||||
throw1(DB_ERROR(lmdb_error("Failed to add removal of rct count info to db transaction: ", result).c_str()));
|
||||
}
|
||||
|
||||
boost::multiprecision::int128_t
|
||||
@@ -1234,6 +1341,32 @@ void write_circulating_supply_data(MDB_cursor *cur_circ_supply_tally, MDB_val id
|
||||
throw0(DB_ERROR(lmdb_error("Failed to update tally for source circulating supply: ", result).c_str()));
|
||||
}
|
||||
|
||||
inline std::string write_token_metadata(const cryptonote::token_metadata_t& token_metadata)
|
||||
{
|
||||
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||
|
||||
std::stringstream token_blob_str;
|
||||
binary_archive<true> ba(token_blob_str);
|
||||
bool r = ::serialization::serialize(ba, const_cast<cryptonote::token_metadata_t&>(token_metadata));
|
||||
if (!r)
|
||||
throw0(DB_ERROR("Failed to serialize token_metadata"));
|
||||
|
||||
return token_blob_str.str();
|
||||
}
|
||||
|
||||
inline void read_token_metadata(const std::string& token_blob, cryptonote::token_metadata_t& token_metadata)
|
||||
{
|
||||
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||
|
||||
if (token_blob.empty())
|
||||
throw0(DB_ERROR("Empty token_blob provided to read_token_metadata"));
|
||||
|
||||
binary_archive<false> ba{epee::strspan<std::uint8_t>(token_blob)};
|
||||
bool r = ::serialization::serialize(ba, token_metadata);
|
||||
if (!r)
|
||||
throw0(DB_ERROR("Failed to deserialize token_metadata"));
|
||||
}
|
||||
|
||||
uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, const std::pair<transaction, blobdata_ref>& txp, const crypto::hash& tx_hash, const crypto::hash& tx_prunable_hash, const bool miner_tx)
|
||||
{
|
||||
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||
@@ -1254,6 +1387,9 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
|
||||
CURSOR(yield_txs)
|
||||
CURSOR(audit_txs)
|
||||
CURSOR(carrot_yield_txs)
|
||||
CURSOR(token_txs)
|
||||
CURSOR(tokens)
|
||||
CURSOR(rollup_tx_info)
|
||||
|
||||
MDB_val_set(val_tx_id, tx_id);
|
||||
MDB_val_set(val_h, tx_hash);
|
||||
@@ -1364,7 +1500,8 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
|
||||
tx.type == cryptonote::transaction_type::CONVERT ||
|
||||
tx.type == cryptonote::transaction_type::STAKE ||
|
||||
tx.type == cryptonote::transaction_type::AUDIT ||
|
||||
tx.type == cryptonote::transaction_type::TRANSFER)
|
||||
tx.type == cryptonote::transaction_type::TRANSFER ||
|
||||
tx.type == cryptonote::transaction_type::CREATE_TOKEN)
|
||||
{
|
||||
|
||||
// Get the current tally value for the source currency type
|
||||
@@ -1395,7 +1532,7 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
|
||||
if (tx.type == cryptonote::transaction_type::PROTOCOL) {
|
||||
|
||||
// Iterate over all of the outputs for a PROTOCOL_TX since they're all MINTED
|
||||
std::map<uint32_t, uint64_t> minted_amounts;
|
||||
std::map<uint32_t, uint64_t> minted_amounts;
|
||||
for (const auto& out: tx.vout) {
|
||||
|
||||
// Fetch the amount and output_asset_type for this output
|
||||
@@ -1413,14 +1550,22 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
|
||||
MDB_val_copy<uint64_t> source_idx(asset.first);
|
||||
boost::multiprecision::int128_t source_tally = 0;
|
||||
result = read_circulating_supply_data(m_cur_circ_supply_tally, source_idx, source_tally);
|
||||
if (result == MDB_NOTFOUND)
|
||||
throw0(DB_ERROR("minted asset not found"));
|
||||
if (result == MDB_NOTFOUND){
|
||||
std::string asset_symbol = cryptonote::asset_type_from_id(asset.first);
|
||||
if (asset.first == 0x53414C31 /*SAL1*/ || asset.first == 0x53414C00 /*SAL*/) {
|
||||
throw0(DB_ERROR(("Asset " + asset_symbol + " not found when updating circulating supply for PROTOCOL_TX").c_str()));
|
||||
}
|
||||
else{
|
||||
LOG_PRINT_L1("First time minting asset, ID: " << asset.first << " symbol: " << asset_symbol << " in tx_id: " << tx_id);
|
||||
}
|
||||
}
|
||||
else if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to get circulating supply tally when adding db transaction: ", result).c_str()));
|
||||
if (source_tally > source_tally + asset.second)
|
||||
throw0(DB_ERROR("add_transaction_data() - mint overflow"));
|
||||
boost::multiprecision::int128_t final_source_tally = source_tally + asset.second;
|
||||
write_circulating_supply_data(m_cur_circ_supply_tally, source_idx, final_source_tally);
|
||||
// Log can be changed - not burning here
|
||||
LOG_PRINT_L1("tx ID " << tx_id << "\n\tAsset Type = " << cryptonote::asset_type_from_id(asset.first) << "\n\tTally before burn =" << source_tally.str() << "\n\tTally after burn =" << final_source_tally.str());
|
||||
|
||||
MDB_val_copy<uint64_t> burn_idx(cryptonote::asset_id_from_type("BURN"));
|
||||
@@ -1428,13 +1573,80 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
|
||||
result = read_circulating_supply_data(m_cur_circ_supply_tally, burn_idx, burn_tally);
|
||||
if (result && result != MDB_NOTFOUND)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to get circulating supply tally when adding db transaction: ", result).c_str()));
|
||||
if (burn_tally < asset.second)
|
||||
uint64_t burn_amount = 0;
|
||||
if (asset.first == 0x53414C00 || asset.first == 0x53414C31 || asset.first == 0x4255524E) { //BURN SAL and SAL1 // check
|
||||
burn_amount = asset.second;
|
||||
} else {
|
||||
burn_amount = asset.second / 200;
|
||||
}
|
||||
if (burn_tally < burn_amount)
|
||||
throw0(DB_ERROR("add_transaction_data() - burn underflow"));
|
||||
boost::multiprecision::int128_t final_burn_tally = burn_tally - asset.second;
|
||||
boost::multiprecision::int128_t final_burn_tally = burn_tally - burn_amount;
|
||||
write_circulating_supply_data(m_cur_circ_supply_tally, burn_idx, final_burn_tally);
|
||||
}
|
||||
}
|
||||
|
||||
// Is there a rollup_tx to add?
|
||||
if (tx.version >= TRANSACTION_VERSION_ENABLE_TOKENS &&
|
||||
tx.type == cryptonote::transaction_type::ROLLUP &&
|
||||
tx.layer2_rollup_data.txs.size())
|
||||
{
|
||||
// We already have the necessary values in the `layer2_rollup_data` member of the TX
|
||||
for (const auto &rollup_tx: tx.layer2_rollup_data.txs) {
|
||||
MDB_val_set(rtx_key, tx.rollup_binding_tag);
|
||||
MDB_val_set(rtx_val, rollup_tx);
|
||||
result = mdb_cursor_put(m_cur_rollup_tx_info, &rtx_key, &rtx_val, MDB_APPENDDUP);
|
||||
}
|
||||
}
|
||||
|
||||
// Is there token_tx data to add?
|
||||
if (tx.version >= TRANSACTION_VERSION_ENABLE_TOKENS && tx.type == cryptonote::transaction_type::CREATE_TOKEN)
|
||||
{
|
||||
// Create the object we are going to write to the database
|
||||
token_tx_info_carrot token_data;
|
||||
token_data.block_height = m_height;
|
||||
token_data.tx_hash = tx_hash;
|
||||
token_data.return_pubkey = tx.protocol_tx_data.return_pubkey;
|
||||
token_data.return_address = tx.protocol_tx_data.return_address;
|
||||
token_data.return_view_tag = tx.protocol_tx_data.return_view_tag;
|
||||
token_data.return_anchor_enc = tx.protocol_tx_data.return_anchor_enc;
|
||||
token_data.locked_coins = tx.amount_burnt;
|
||||
token_data.asset_type_id = cryptonote::asset_id_from_type("sal" + tx.token_metadata.asset_type);
|
||||
|
||||
// Because LMDB is shockingly bad at handling duplicates, we have resorted to using a counter of elements
|
||||
// in the first element of the struct.
|
||||
MDB_val data;
|
||||
MDB_val_set(val_height, m_height);
|
||||
result = mdb_cursor_get(m_cur_token_txs, &val_height, &data, MDB_SET);
|
||||
if (!result)
|
||||
{
|
||||
mdb_size_t num_elems = 0;
|
||||
result = mdb_cursor_count(m_cur_token_txs, &num_elems);
|
||||
if (result)
|
||||
throw0(DB_ERROR(std::string("Failed to get number of token TXs for height: ").append(mdb_strerror(result)).c_str()));
|
||||
token_data.block_height = num_elems;
|
||||
}
|
||||
else if (result != MDB_NOTFOUND)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to get output amount in db transaction: ", result).c_str()));
|
||||
else
|
||||
token_data.block_height = 0;
|
||||
|
||||
// Now we know how many there are, write out the data to the DB
|
||||
MDB_val_set(val_token_tx_data, token_data);
|
||||
result = mdb_cursor_put(m_cur_token_txs, &val_height, &val_token_tx_data, MDB_APPENDDUP);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to add tx carrot token data to db transaction: ", result).c_str()));
|
||||
|
||||
// Now add an entry to the `tokens` table
|
||||
MDB_val_copy<uint64_t> asset_type_id(token_data.asset_type_id);
|
||||
std::stringstream token_blob_str;
|
||||
std::string token_blob = write_token_metadata(tx.token_metadata);
|
||||
MDB_val_sized(token, token_blob);
|
||||
int result = mdb_cursor_put(m_cur_tokens, &asset_type_id, &token, 0);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to update token table: ", result).c_str()));
|
||||
}
|
||||
|
||||
// Is there yield_tx data to add?
|
||||
if (tx.version >= TRANSACTION_VERSION_CARROT && tx.type == cryptonote::transaction_type::STAKE)
|
||||
{
|
||||
@@ -1601,6 +1813,9 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const
|
||||
CURSOR(yield_txs)
|
||||
CURSOR(audit_txs)
|
||||
CURSOR(carrot_yield_txs)
|
||||
CURSOR(token_txs)
|
||||
CURSOR(tokens)
|
||||
CURSOR(rollup_tx_info)
|
||||
|
||||
MDB_val_set(val_h, tx_hash);
|
||||
|
||||
@@ -1842,6 +2057,64 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Is there token_tx data to remove?
|
||||
if (tx.version >= TRANSACTION_VERSION_ENABLE_TOKENS && tx.type == cryptonote::transaction_type::CREATE_TOKEN)
|
||||
{
|
||||
// Remove all CREATE_TOKEN entries for this transaction
|
||||
MDB_val_set(val_height, m_height);
|
||||
MDB_val v;
|
||||
MDB_cursor_op op = MDB_SET;
|
||||
while (1) {
|
||||
result = mdb_cursor_get(m_cur_token_txs, &val_height, &v, op);
|
||||
if (result == MDB_NOTFOUND) {
|
||||
throw1(DB_ERROR("Failed to locate token tx for removal from db transaction"));
|
||||
} else if (result) {
|
||||
throw1(DB_ERROR(lmdb_error("Failed to locate token_tx data for removal: ", result).c_str()));
|
||||
}
|
||||
op = MDB_NEXT_DUP;
|
||||
const token_tx_info_carrot tti = *(const token_tx_info_carrot*)v.mv_data;
|
||||
if (tti.tx_hash == tx_hash) {
|
||||
result = mdb_cursor_del(m_cur_token_txs, 0);
|
||||
if (result)
|
||||
throw1(DB_ERROR(lmdb_error("Failed to add removal of token_tx data to db transaction: ", result).c_str()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Now remove an entry from the `tokens` table
|
||||
MDB_val_copy<uint64_t> asset_type_id(cryptonote::asset_id_from_type("sal" + tx.token_metadata.asset_type));
|
||||
op = MDB_SET;
|
||||
result = mdb_cursor_get(m_cur_tokens, &asset_type_id, &v, op);
|
||||
if (result == MDB_NOTFOUND) {
|
||||
throw1(DB_ERROR("Failed to locate token for removal from db transaction"));
|
||||
} else if (result) {
|
||||
throw1(DB_ERROR(lmdb_error("Failed to locate token data for removal: ", result).c_str()));
|
||||
}
|
||||
result = mdb_cursor_del(m_cur_tokens, 0);
|
||||
if (result)
|
||||
throw1(DB_ERROR(lmdb_error("Failed to remove token data to db transaction: ", result).c_str()));
|
||||
}
|
||||
|
||||
// Is there a rollup_tx to add?
|
||||
if (tx.version >= TRANSACTION_VERSION_ENABLE_TOKENS &&
|
||||
tx.type == cryptonote::transaction_type::ROLLUP &&
|
||||
tx.layer2_rollup_data.txs.size())
|
||||
{
|
||||
for (const auto &rollup_tx: tx.layer2_rollup_data.txs) {
|
||||
MDB_val_set(rtx_key, tx.rollup_binding_tag);
|
||||
MDB_val_set(rtx_val, rollup_tx);
|
||||
result = mdb_cursor_get(m_cur_rollup_tx_info, &rtx_key, &rtx_val, MDB_GET_BOTH);
|
||||
if (result == MDB_NOTFOUND)
|
||||
continue;
|
||||
if (result)
|
||||
throw1(DB_ERROR(lmdb_error("Failed to locate rollup_tx data for removal: ", result).c_str()));
|
||||
result = mdb_cursor_del(m_cur_rollup_tx_info, 0);
|
||||
if (result)
|
||||
throw1(DB_ERROR(lmdb_error("Error adding removal of tx id to db transaction", result).c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
// Don't delete the tx_indices entry until the end, after we're done with val_tx_id
|
||||
if (mdb_cursor_del(m_cur_tx_indices, 0))
|
||||
@@ -2390,6 +2663,13 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags)
|
||||
|
||||
lmdb_db_open(txn, LMDB_CARROT_YIELD_TXS, MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED | MDB_CREATE, m_carrot_yield_txs, "Failed to open db handle for m_carrot_yield_txs");
|
||||
|
||||
lmdb_db_open(txn, LMDB_TOKEN_TXS, MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED | MDB_CREATE, m_token_txs, "Failed to open db handle for m_token_txs");
|
||||
lmdb_db_open(txn, LMDB_TOKENS, MDB_CREATE, m_tokens, "Failed to open db handle for m_tokens");
|
||||
|
||||
lmdb_db_open(txn, LMDB_RCT_COUNT_INFO, MDB_INTEGERKEY | MDB_CREATE, m_rct_count_info, "Failed to open db handle for m_rct_count_info");
|
||||
|
||||
lmdb_db_open(txn, LMDB_ROLLUP_TX_INFO, MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED | MDB_CREATE, m_rollup_tx_info, "Failed to open db handle for m_rollup_tx_info");
|
||||
|
||||
mdb_set_dupsort(txn, m_spent_keys, compare_hash32);
|
||||
mdb_set_dupsort(txn, m_block_heights, compare_hash32);
|
||||
mdb_set_dupsort(txn, m_tx_indices, compare_hash32);
|
||||
@@ -2415,6 +2695,11 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags)
|
||||
mdb_set_dupsort(txn, m_yield_txs, compare_uint64);
|
||||
mdb_set_dupsort(txn, m_audit_txs, compare_uint64);
|
||||
mdb_set_dupsort(txn, m_carrot_yield_txs, compare_uint64);
|
||||
mdb_set_dupsort(txn, m_token_txs, compare_uint64);
|
||||
|
||||
mdb_set_compare(txn, m_tokens, compare_uint64);
|
||||
|
||||
mdb_set_dupsort(txn, m_rollup_tx_info, compare_uint64);
|
||||
|
||||
if (!(mdb_flags & MDB_RDONLY))
|
||||
{
|
||||
@@ -2602,6 +2887,12 @@ void BlockchainLMDB::reset()
|
||||
throw0(DB_ERROR(lmdb_error("Failed to drop m_audit_blocks: ", result).c_str()));
|
||||
if (auto result = mdb_drop(txn, m_carrot_yield_txs, 0))
|
||||
throw0(DB_ERROR(lmdb_error("Failed to drop m_carrot_yield_txs: ", result).c_str()));
|
||||
if (auto result = mdb_drop(txn, m_token_txs, 0))
|
||||
throw0(DB_ERROR(lmdb_error("Failed to drop m_token_txs: ", result).c_str()));
|
||||
if (auto result = mdb_drop(txn, m_rct_count_info, 0))
|
||||
throw0(DB_ERROR(lmdb_error("Failed to drop m_rct_count_info: ", result).c_str()));
|
||||
if (auto result = mdb_drop(txn, m_rollup_tx_info, 0))
|
||||
throw0(DB_ERROR(lmdb_error("Failed to drop m_rollup_tx_info: ", result).c_str()));
|
||||
|
||||
// init with current version
|
||||
MDB_val_str(k, "version");
|
||||
@@ -3454,8 +3745,13 @@ uint64_t BlockchainLMDB::get_block_timestamp(const uint64_t& height) const
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::pair<std::vector<uint64_t>, uint64_t> BlockchainLMDB::get_block_cumulative_rct_outputs(const std::vector<uint64_t> &heights, const std::string asset_type) const
|
||||
std::pair<std::vector<uint64_t>, uint64_t> BlockchainLMDB::get_block_cumulative_rct_outputs_old(const std::vector<uint64_t> &heights, const std::string asset_type) const
|
||||
{
|
||||
// Handle tokens separately
|
||||
if (cryptonote::is_asset_type_token(asset_type)) {
|
||||
return get_block_cumulative_rct_outputs(heights, asset_type);
|
||||
}
|
||||
|
||||
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||
check_open();
|
||||
std::vector<uint64_t> res;
|
||||
@@ -3484,11 +3780,11 @@ std::pair<std::vector<uint64_t>, uint64_t> BlockchainLMDB::get_block_cumulative_
|
||||
{
|
||||
if (height >= range_begin && height < range_end)
|
||||
{
|
||||
// nohting to do
|
||||
// nothing to do
|
||||
}
|
||||
else
|
||||
{
|
||||
if (height == prev_height + 1)
|
||||
if (false/*height == prev_height + 1*/)
|
||||
{
|
||||
MDB_val k2;
|
||||
result = mdb_cursor_get(m_cur_block_info, &k2, &v, MDB_NEXT_MULTIPLE);
|
||||
@@ -3508,14 +3804,18 @@ std::pair<std::vector<uint64_t>, uint64_t> BlockchainLMDB::get_block_cumulative_
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Error attempting to retrieve rct distribution from the db: ", result).c_str()));
|
||||
}
|
||||
const mdb_block_info *bi = ((const mdb_block_info *)v.mv_data) + (height - range_begin);
|
||||
|
||||
assert(false);
|
||||
//const mdb_block_info *bi = ((const mdb_block_info *)v.mv_data) + (height - range_begin);
|
||||
|
||||
// if no asset type is provided in the request, an old client is requesting the cumulative outputs,
|
||||
// and is expecting the global output distribution that isn't bucketed by asset type in response
|
||||
res.push_back(asset_type.empty() ? bi->bi_cum_rct : bi->bi_cum_rct_by_asset_type[asset_type]);
|
||||
|
||||
if (height == heights[heights.size() - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE])
|
||||
num_spendable_global_outs = bi->bi_cum_rct;
|
||||
//res.push_back(asset_type.empty() ? bi->bi_cum_rct : bi->bi_cum_rct_by_asset_type[asset_type]);
|
||||
|
||||
//if (height == heights[heights.size() - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE])
|
||||
// num_spendable_global_outs = asset_type.empty() ? bi->bi_cum_rct : bi->bi_cum_rct_by_asset_type[asset_type];
|
||||
assert(true);
|
||||
|
||||
prev_height = height;
|
||||
}
|
||||
@@ -3524,6 +3824,64 @@ std::pair<std::vector<uint64_t>, uint64_t> BlockchainLMDB::get_block_cumulative_
|
||||
return std::make_pair(res, num_spendable_global_outs);
|
||||
}
|
||||
|
||||
std::pair<std::vector<uint64_t>, uint64_t> BlockchainLMDB::get_block_cumulative_rct_outputs(const std::vector<uint64_t> &heights, const std::string asset_type) const
|
||||
{
|
||||
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||
check_open();
|
||||
std::vector<uint64_t> res;
|
||||
uint64_t num_spendable_global_outs = 0;
|
||||
int result;
|
||||
|
||||
// Verify the asset_type matches a token
|
||||
//if (!cryptonote::is_asset_type_token(asset_type)) {
|
||||
// throw0(BLOCK_DNE(std::string("Attempt to get token rct distribution failed - '" + asset_type + "' is not a valid token").c_str()));
|
||||
//}
|
||||
|
||||
// Get the token asset_type_id
|
||||
uint32_t asset_type_id = cryptonote::asset_id_from_type(asset_type);
|
||||
|
||||
if (heights.empty())
|
||||
return {};
|
||||
res.reserve(heights.size());
|
||||
|
||||
TXN_PREFIX_RDONLY();
|
||||
RCURSOR(rct_count_info);
|
||||
|
||||
MDB_stat db_stats;
|
||||
if ((result = mdb_stat(m_txn, m_blocks, &db_stats)))
|
||||
throw0(DB_ERROR(lmdb_error("Failed to query m_blocks: ", result).c_str()));
|
||||
for (size_t i = 0; i < heights.size(); ++i)
|
||||
if (heights[i] >= db_stats.ms_entries)
|
||||
throw0(BLOCK_DNE(std::string("Attempt to get rct distribution from height " + std::to_string(heights[i]) + " failed -- block size not in db").c_str()));
|
||||
|
||||
uint64_t prev_height = heights[0];
|
||||
for (uint64_t height: heights)
|
||||
{
|
||||
MDB_val v;
|
||||
MDB_val_set(k, height);
|
||||
result = mdb_cursor_get(m_cur_rct_count_info, &k, &v, MDB_SET);
|
||||
if (result == MDB_NOTFOUND) {
|
||||
throw0(DB_ERROR(lmdb_error("Error attempting to retrieve rct distribution from the db: ", result).c_str()));
|
||||
}
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to enumerate rct distribution info: ", result).c_str()));
|
||||
|
||||
oracle::asset_type_counts_v2 rct_count_info;
|
||||
rct_count_info.deserialize(reinterpret_cast<const uint8_t*>(v.mv_data), v.mv_size);
|
||||
//if (!rct_count_info.has_asset_type(asset_type_id))
|
||||
// throw0(BLOCK_DNE(std::string("Attempt to get rct distribution failed - '" + asset_type + "' not found").c_str()));
|
||||
res.push_back(rct_count_info[asset_type_id]);
|
||||
|
||||
if (height == heights[heights.size() - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE])
|
||||
num_spendable_global_outs = rct_count_info[asset_type_id];
|
||||
|
||||
prev_height = height;
|
||||
}
|
||||
|
||||
TXN_POSTFIX_RDONLY();
|
||||
return std::make_pair(res, num_spendable_global_outs);
|
||||
}
|
||||
|
||||
uint64_t BlockchainLMDB::get_top_block_timestamp() const
|
||||
{
|
||||
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||
@@ -3970,6 +4328,102 @@ std::map<std::string,uint64_t> BlockchainLMDB::get_circulating_supply() const
|
||||
return circulating_supply;
|
||||
}
|
||||
|
||||
std::map<std::string, cryptonote::token_metadata_t> BlockchainLMDB::get_tokens() const
|
||||
{
|
||||
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||
check_open();
|
||||
TXN_PREFIX_RDONLY();
|
||||
int result;
|
||||
|
||||
// Open the tokens table read-only
|
||||
RCURSOR(tokens)
|
||||
|
||||
// Create the map<asset_type, token_data> we are going to return
|
||||
std::map<std::string, cryptonote::token_metadata_t> token_map;
|
||||
|
||||
MDB_val k;
|
||||
MDB_val v;
|
||||
MDB_cursor_op op = MDB_FIRST;
|
||||
while (1)
|
||||
{
|
||||
int result = mdb_cursor_get(m_cur_tokens, &k, &v, op);
|
||||
op = MDB_NEXT;
|
||||
if (result == MDB_NOTFOUND)
|
||||
break;
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to get tokens: ", result).c_str()));
|
||||
|
||||
// Push the data into the tokens return struct
|
||||
const uint64_t asset_type_id = *(const uint64_t*)k.mv_data;
|
||||
const std::string asset_type = cryptonote::asset_type_from_id(asset_type_id);
|
||||
if (!v.mv_size)
|
||||
throw0(DB_ERROR("zero-length token_blob provided"));
|
||||
std::string token_blob(reinterpret_cast<const char *>(v.mv_data), v.mv_size);
|
||||
cryptonote::token_metadata_t token_metadata;
|
||||
read_token_metadata(token_blob, token_metadata);
|
||||
if (token_metadata.token.type() == typeid(cryptonote::erc_token_t)) {
|
||||
|
||||
// ERC20 token found
|
||||
erc_token_t token = boost::get<cryptonote::erc_token_t>(token_metadata.token);
|
||||
LOG_PRINT_L3("erc_token_t version " << token.version);
|
||||
|
||||
} else if (token_metadata.token.type() == typeid(cryptonote::sal_token_t)) {
|
||||
|
||||
// SAL token found
|
||||
sal_token_t token = boost::get<cryptonote::sal_token_t>(token_metadata.token);
|
||||
LOG_PRINT_L3("sal_token_t version " << token.version);
|
||||
|
||||
} else {
|
||||
throw0(DB_ERROR("invalid token_blob provided"));
|
||||
}
|
||||
token_map[asset_type] = token_metadata;
|
||||
}
|
||||
|
||||
TXN_POSTFIX_RDONLY();
|
||||
|
||||
return token_map;
|
||||
}
|
||||
|
||||
bool BlockchainLMDB::is_tx_paid_for(const cryptonote::transaction& tx) const
|
||||
{
|
||||
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||
check_open();
|
||||
TXN_PREFIX_RDONLY();
|
||||
int result;
|
||||
|
||||
// Is it a TRANSFER?
|
||||
if (tx.type != cryptonote::transaction_type::TRANSFER)
|
||||
return true;
|
||||
// Is is a TOKEN?
|
||||
if (!cryptonote::is_asset_type_token(tx.source_asset_type))
|
||||
return true;
|
||||
|
||||
RCURSOR(rollup_tx_info)
|
||||
|
||||
// Calculate the TX prefix hash
|
||||
crypto::hash tx_prefix_hash;
|
||||
cryptonote::get_transaction_hash(tx, tx_prefix_hash);
|
||||
|
||||
// get the key image from the first input
|
||||
if (tx.vin.size() == 0)
|
||||
return false;
|
||||
const txin_to_key &in_to_key = boost::get < txin_to_key > (tx.vin[0]);
|
||||
|
||||
cryptonote::layer2_rollup_tx_t rollup_tx{tx_prefix_hash, in_to_key.k_image, tx.rct_signatures.txnFee};
|
||||
MDB_val_set(key, tx.rollup_binding_tag);
|
||||
MDB_val_set(val, rollup_tx);
|
||||
result = mdb_cursor_get(m_cur_rollup_tx_info, &key, &val, MDB_GET_BOTH);
|
||||
if (result == MDB_NOTFOUND) {
|
||||
LOG_PRINT_L2("Transaction ID " << tx_prefix_hash << " has not been paid for");
|
||||
return false;
|
||||
}
|
||||
if (result)
|
||||
throw1(DB_ERROR(lmdb_error("Failed to locate rollup TX data: ", result).c_str()));
|
||||
|
||||
// Can only find a matching record if the ROLLUP is already mined
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t BlockchainLMDB::num_outputs() const
|
||||
{
|
||||
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||
@@ -5683,8 +6137,169 @@ void BlockchainLMDB::migrate_2_3()
|
||||
|
||||
MGINFO_YELLOW("Migrating blockchain from DB version 2 to 3 - this may take a while:");
|
||||
|
||||
// Create the missing (and empty) "audit_block_info" records for all blocks
|
||||
std::map<uint64_t, oracle::asset_type_counts> map_counts;
|
||||
|
||||
// Create the updated asset_type_counts_v2 entries
|
||||
do {
|
||||
LOG_PRINT_L1("migrating block info:");
|
||||
|
||||
result = mdb_txn_begin(m_env, NULL, 0, txn);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str()));
|
||||
|
||||
MDB_stat db_stats;
|
||||
if ((result = mdb_stat(txn, m_blocks, &db_stats)))
|
||||
throw0(DB_ERROR(lmdb_error("Failed to query m_blocks: ", result).c_str()));
|
||||
const uint64_t blockchain_height = db_stats.ms_entries;
|
||||
|
||||
/* the block_info table name is the same but the old version and new version
|
||||
* have incompatible data. Create a new table. We want the name to be similar
|
||||
* to the old name so that it will occupy the same location in the DB.
|
||||
*/
|
||||
MDB_dbi o_block_info = m_block_info;
|
||||
lmdb_db_open(txn, "block_infn", MDB_INTEGERKEY | MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED, m_block_info, "Failed to open db handle for block_infn");
|
||||
mdb_set_dupsort(txn, m_block_info, compare_uint64);
|
||||
|
||||
|
||||
MDB_cursor *c_blocks;
|
||||
result = mdb_cursor_open(txn, m_blocks, &c_blocks);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to open a cursor for blocks: ", result).c_str()));
|
||||
|
||||
MDB_cursor *c_old, *c_cur;
|
||||
i = 0;
|
||||
while(1) {
|
||||
if (!(i % 1000)) {
|
||||
if (i) {
|
||||
LOGIF(el::Level::Info) {
|
||||
std::cout << i << " / " << blockchain_height << " \r" << std::flush;
|
||||
}
|
||||
txn.commit();
|
||||
result = mdb_txn_begin(m_env, NULL, 0, txn);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str()));
|
||||
}
|
||||
result = mdb_cursor_open(txn, m_block_info, &c_cur);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to open a cursor for block_infn: ", result).c_str()));
|
||||
result = mdb_cursor_open(txn, o_block_info, &c_old);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to open a cursor for block_info: ", result).c_str()));
|
||||
if (!i) {
|
||||
MDB_stat db_stat;
|
||||
result = mdb_stat(txn, m_block_info, &db_stats);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to query m_block_info: ", result).c_str()));
|
||||
i = db_stats.ms_entries;
|
||||
}
|
||||
}
|
||||
result = mdb_cursor_get(c_old, &k, &v, MDB_NEXT);
|
||||
if (result == MDB_NOTFOUND) {
|
||||
txn.commit();
|
||||
break;
|
||||
}
|
||||
else if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to get a record from block_info: ", result).c_str()));
|
||||
const mdb_block_info_1 *bi_old = (const mdb_block_info_1*)v.mv_data;
|
||||
mdb_block_info_2 bi;
|
||||
bi.bi_height = bi_old->bi_height;
|
||||
bi.bi_timestamp = bi_old->bi_timestamp;
|
||||
bi.bi_coins = bi_old->bi_coins;
|
||||
bi.bi_weight = bi_old->bi_weight;
|
||||
bi.bi_diff_lo = bi_old->bi_diff_lo;
|
||||
bi.bi_diff_hi = bi_old->bi_diff_hi;
|
||||
bi.bi_hash = bi_old->bi_hash;
|
||||
bi.bi_cum_rct = bi_old->bi_cum_rct;
|
||||
bi.bi_long_term_block_weight = bi_old->bi_long_term_block_weight;
|
||||
bi.bi_pricing_record = bi_old->bi_pricing_record;
|
||||
map_counts[bi.bi_height] = bi_old->bi_cum_rct_by_asset_type;
|
||||
|
||||
MDB_val_set(nv, bi);
|
||||
result = mdb_cursor_put(c_cur, (MDB_val *)&zerokval, &nv, MDB_APPENDDUP);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to put a record into block_infn: ", result).c_str()));
|
||||
/* we delete the old records immediately, so the overall DB and mapsize should not grow.
|
||||
* This is a little slower than just letting mdb_drop() delete it all at the end, but
|
||||
* it saves a significant amount of disk space.
|
||||
*/
|
||||
result = mdb_cursor_del(c_old, 0);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to delete a record from block_info: ", result).c_str()));
|
||||
i++;
|
||||
}
|
||||
|
||||
result = mdb_txn_begin(m_env, NULL, 0, txn);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str()));
|
||||
/* Delete the old table */
|
||||
result = mdb_drop(txn, o_block_info, 1);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to delete old block_info table: ", result).c_str()));
|
||||
|
||||
RENAME_DB("block_infn");
|
||||
mdb_dbi_close(m_env, m_block_info);
|
||||
|
||||
lmdb_db_open(txn, "block_info", MDB_INTEGERKEY | MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED, m_block_info, "Failed to open db handle for block_infn");
|
||||
mdb_set_dupsort(txn, m_block_info, compare_uint64);
|
||||
|
||||
txn.commit();
|
||||
|
||||
// Now create the new table with our asset_type_counts
|
||||
LOG_PRINT_L1("migrating RCT asset_type count info:");
|
||||
|
||||
result = mdb_txn_begin(m_env, NULL, 0, txn);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str()));
|
||||
|
||||
oracle::asset_type_counts_v2 rct_counts;
|
||||
uint32_t asset_type_id_sal = cryptonote::asset_id_from_type("SAL");
|
||||
rct_counts.add_asset_type(asset_type_id_sal);
|
||||
uint32_t asset_type_id_one = cryptonote::asset_id_from_type("SAL1");
|
||||
rct_counts.add_asset_type(asset_type_id_one);
|
||||
uint32_t asset_type_id_burn = cryptonote::asset_id_from_type("BURN");
|
||||
rct_counts.add_asset_type(asset_type_id_burn);
|
||||
MDB_cursor *c_cur_rct;
|
||||
i = 0;
|
||||
uint64_t i_max = map_counts.size();
|
||||
do {
|
||||
if (!(i % 1000)) {
|
||||
if (i) {
|
||||
LOGIF(el::Level::Info) {
|
||||
std::cout << i << " / " << blockchain_height << " \r" << std::flush;
|
||||
}
|
||||
txn.commit();
|
||||
result = mdb_txn_begin(m_env, NULL, 0, txn);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str()));
|
||||
}
|
||||
result = mdb_cursor_open(txn, m_rct_count_info, &c_cur_rct);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to open a cursor for rct_count_info: ", result).c_str()));
|
||||
if (!i) {
|
||||
result = mdb_stat(txn, m_rct_count_info, &db_stats);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to query m_rct_count_info: ", result).c_str()));
|
||||
i = db_stats.ms_entries;
|
||||
}
|
||||
}
|
||||
|
||||
// populate the RCT counts
|
||||
oracle::asset_type_counts old_counts = map_counts.at(i);
|
||||
rct_counts.asset_counts[asset_type_id_sal] = old_counts["SAL"];
|
||||
rct_counts.asset_counts[asset_type_id_one] = old_counts["SAL1"];
|
||||
rct_counts.asset_counts[asset_type_id_burn] = old_counts["BURN"];
|
||||
|
||||
std::vector<uint8_t> rct_counts_serialized = rct_counts.serialize();
|
||||
MDB_val_set(key, i);
|
||||
MDB_val_sized(value, rct_counts_serialized);
|
||||
result = mdb_cursor_put(c_cur_rct, &key, &value, MDB_APPEND);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to put a record into : ", result).c_str()));
|
||||
i++;
|
||||
} while (i<i_max);
|
||||
|
||||
txn.commit();
|
||||
|
||||
} while(0);
|
||||
|
||||
uint32_t version = VERSION;
|
||||
@@ -5693,7 +6308,7 @@ void BlockchainLMDB::migrate_2_3()
|
||||
MDB_val_str(vk, "version");
|
||||
result = mdb_txn_begin(m_env, NULL, 0, txn);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str()));
|
||||
throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str()));
|
||||
result = mdb_put(txn, m_properties, &vk, &v, 0);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to update version for the db: ", result).c_str()));
|
||||
@@ -5702,8 +6317,8 @@ void BlockchainLMDB::migrate_2_3()
|
||||
|
||||
void BlockchainLMDB::migrate(const uint32_t oldversion)
|
||||
{
|
||||
//if (oldversion < 3)
|
||||
// migrate_2_3();
|
||||
if (oldversion < 3)
|
||||
migrate_2_3();
|
||||
}
|
||||
|
||||
} // namespace cryptonote
|
||||
|
||||
@@ -82,6 +82,14 @@ typedef struct mdb_txn_cursors
|
||||
MDB_cursor *m_txc_audit_blocks;
|
||||
MDB_cursor *m_txc_carrot_yield_txs;
|
||||
|
||||
MDB_cursor *m_txc_token_txs;
|
||||
MDB_cursor *m_txc_tokens;
|
||||
MDB_cursor *m_txc_asset_type_counts;
|
||||
|
||||
MDB_cursor *m_txc_rct_count_info;
|
||||
|
||||
MDB_cursor *m_txc_rollup_tx_info;
|
||||
|
||||
} mdb_txn_cursors;
|
||||
|
||||
#define m_cur_blocks m_cursors->m_txc_blocks
|
||||
@@ -111,6 +119,14 @@ typedef struct mdb_txn_cursors
|
||||
#define m_cur_audit_blocks m_cursors->m_txc_audit_blocks
|
||||
#define m_cur_carrot_yield_txs m_cursors->m_txc_carrot_yield_txs
|
||||
|
||||
#define m_cur_token_txs m_cursors->m_txc_token_txs
|
||||
#define m_cur_tokens m_cursors->m_txc_tokens
|
||||
#define m_cur_asset_type_counts m_cursors->m_txc_asset_type_counts
|
||||
|
||||
#define m_cur_rct_count_info m_cursors->m_txc_rct_count_info
|
||||
|
||||
#define m_cur_rollup_tx_info m_cursors->m_txc_rollup_tx_info
|
||||
|
||||
typedef struct mdb_rflags
|
||||
{
|
||||
bool m_rf_txn;
|
||||
@@ -140,6 +156,15 @@ typedef struct mdb_rflags
|
||||
bool m_rf_audit_txs;
|
||||
bool m_rf_audit_blocks;
|
||||
bool m_rf_carrot_yield_txs;
|
||||
|
||||
bool m_rf_token_txs;
|
||||
bool m_rf_tokens;
|
||||
bool m_rf_asset_type_counts;
|
||||
|
||||
bool m_rf_rct_count_info;
|
||||
|
||||
bool m_rf_rollup_tx_info;
|
||||
|
||||
} mdb_rflags;
|
||||
|
||||
typedef struct mdb_threadinfo
|
||||
@@ -242,6 +267,8 @@ public:
|
||||
|
||||
virtual cryptonote::blobdata get_block_blob_from_height(const uint64_t& height) const;
|
||||
|
||||
virtual std::pair<std::vector<uint64_t>, uint64_t> get_block_cumulative_rct_outputs_old(const std::vector<uint64_t> &heights, const std::string asset_type) const;
|
||||
|
||||
virtual std::pair<std::vector<uint64_t>, uint64_t> get_block_cumulative_rct_outputs(const std::vector<uint64_t> &heights, const std::string asset_type) const;
|
||||
|
||||
virtual uint64_t get_block_timestamp(const uint64_t& height) const;
|
||||
@@ -277,6 +304,10 @@ public:
|
||||
virtual uint64_t height() const;
|
||||
|
||||
virtual std::map<std::string, uint64_t> get_circulating_supply() const;
|
||||
|
||||
virtual std::map<std::string, cryptonote::token_metadata_t> get_tokens() const;
|
||||
|
||||
virtual bool is_tx_paid_for(const cryptonote::transaction& tx) const;
|
||||
|
||||
virtual bool tx_exists(const crypto::hash& h) const;
|
||||
virtual bool tx_exists(const crypto::hash& h, uint64_t& tx_index) const;
|
||||
@@ -406,11 +437,12 @@ private:
|
||||
const difficulty_type& cumulative_difficulty,
|
||||
const uint64_t& coins_generated,
|
||||
uint64_t num_rct_outs,
|
||||
oracle::asset_type_counts& cum_rct_by_asset_type,
|
||||
oracle::asset_type_counts_v2& cum_rct_by_asset_type,
|
||||
const crypto::hash& blk_hash,
|
||||
uint64_t slippage_total,
|
||||
uint64_t yield_total,
|
||||
uint64_t audit_total,
|
||||
uint64_t token_total,
|
||||
const cryptonote::network_type nettype,
|
||||
cryptonote::yield_block_info& ybi,
|
||||
cryptonote::audit_block_info& abi
|
||||
@@ -479,6 +511,8 @@ private:
|
||||
virtual int get_yield_tx_info(const uint64_t height, std::vector<yield_tx_info>& yti_container) const;
|
||||
virtual int get_carrot_yield_tx_info(const uint64_t height, std::vector<yield_tx_info_carrot>& yti_container) const;
|
||||
|
||||
virtual int get_token_tx_info(const uint64_t height, std::vector<token_tx_info_carrot>& tti_container) const;
|
||||
|
||||
private:
|
||||
MDB_env* m_env;
|
||||
|
||||
@@ -521,6 +555,14 @@ private:
|
||||
|
||||
MDB_dbi m_carrot_yield_txs;
|
||||
|
||||
MDB_dbi m_token_txs;
|
||||
MDB_dbi m_tokens;
|
||||
MDB_dbi m_asset_type_counts;
|
||||
|
||||
MDB_dbi m_rct_count_info;
|
||||
|
||||
MDB_dbi m_rollup_tx_info;
|
||||
|
||||
mutable uint64_t m_cum_size; // used in batch size estimation
|
||||
mutable unsigned int m_cum_count;
|
||||
std::string m_folder;
|
||||
|
||||
@@ -156,15 +156,20 @@ public:
|
||||
const difficulty_type& cumulative_difficulty,
|
||||
const uint64_t& coins_generated,
|
||||
uint64_t num_rct_outs,
|
||||
oracle::asset_type_counts& cum_rct_by_asset_type,
|
||||
oracle::asset_type_counts_v2& cum_rct_by_asset_type,
|
||||
const crypto::hash& blk_hash,
|
||||
uint64_t slippage_total,
|
||||
uint64_t yield_total,
|
||||
uint64_t audit_total,
|
||||
uint64_t token_total,
|
||||
const cryptonote::network_type nettype,
|
||||
cryptonote::yield_block_info& ybi,
|
||||
cryptonote::audit_block_info& abi
|
||||
) override { }
|
||||
|
||||
virtual std::map<std::string, cryptonote::token_metadata_t> get_tokens() const override { return {}; }
|
||||
virtual bool is_tx_paid_for(const cryptonote::transaction& tx) const override { return true; }
|
||||
virtual int get_token_tx_info(const uint64_t height, std::vector<token_tx_info_carrot>& tti_container) const override { return 0; }
|
||||
virtual cryptonote::block get_block_from_height(const uint64_t& height) const override { return cryptonote::block(); }
|
||||
virtual void set_hard_fork_version(uint64_t height, uint8_t version) override {}
|
||||
virtual uint8_t get_hard_fork_version(uint64_t height) const override { return 0; }
|
||||
|
||||
@@ -214,11 +214,9 @@ plot 'stats.csv' index "DATA" using (timecolumn(1,"%Y-%m-%d")):4 with lines, ''
|
||||
#define MAX_RINGS 0xffffffff
|
||||
|
||||
struct tm prevtm = {0}, currtm;
|
||||
uint64_t prevsz = 0, currsz = 0;
|
||||
uint64_t prevtxs = 0, currtxs = 0;
|
||||
uint64_t currblks = 0;
|
||||
uint32_t txhr[24] = {0};
|
||||
unsigned int i;
|
||||
// uint64_t currsz = 0;
|
||||
// uint64_t currtxs = 0;
|
||||
// uint64_t currblks = 0;
|
||||
|
||||
const std::map<uint8_t, std::pair<uint64_t, std::pair<std::string, std::string>>> audit_hard_forks = get_config(net_type).AUDIT_HARD_FORKS;
|
||||
|
||||
@@ -246,9 +244,7 @@ plot 'stats.csv' index "DATA" using (timecolumn(1,"%Y-%m-%d")):4 with lines, ''
|
||||
prevtm = currtm;
|
||||
}
|
||||
skip:
|
||||
currsz += bd.size();
|
||||
uint64_t coinbase_amount;
|
||||
uint64_t tx_fee_amount = 0;
|
||||
// currsz += bd.size();
|
||||
std::set<std::string> used_assets, miner_tx_assets, protocol_tx_assets;
|
||||
std::map<size_t, std::vector<std::string>> used_tx_versions;
|
||||
used_assets.insert("SAL");
|
||||
@@ -325,10 +321,11 @@ skip:
|
||||
std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter << "invalid TX detected" << delimiter << std::endl;
|
||||
continue;
|
||||
}
|
||||
currsz += bd.size();
|
||||
if (db->get_prunable_tx_blob(tx_id, bd))
|
||||
currsz += bd.size();
|
||||
currtxs++;
|
||||
// currsz += bd.size();
|
||||
// if (db->get_prunable_tx_blob(tx_id, bd))
|
||||
// currsz += bd.size();
|
||||
// currtxs++;
|
||||
db->get_prunable_tx_blob(tx_id, bd);
|
||||
|
||||
if (tx.type != cryptonote::transaction_type::TRANSFER &&
|
||||
tx.type != cryptonote::transaction_type::BURN &&
|
||||
@@ -374,7 +371,7 @@ skip:
|
||||
}
|
||||
}
|
||||
|
||||
currblks++;
|
||||
// currblks++;
|
||||
|
||||
if (stop_requested)
|
||||
break;
|
||||
|
||||
@@ -211,8 +211,15 @@ int main(int argc, char* argv[])
|
||||
throw std::runtime_error("Failed to initialize a database");
|
||||
}
|
||||
|
||||
const std::string filename = (boost::filesystem::path(opt_data_dir) / db->get_db_name()).string();
|
||||
LOG_PRINT_L0("Loading blockchain from folder " << filename << " ...");
|
||||
boost::filesystem::path folder(opt_data_dir);
|
||||
if (opt_stagenet) {
|
||||
folder /= std::to_string(STAGENET_VERSION);
|
||||
} else if (opt_testnet) {
|
||||
folder /= std::to_string(TESTNET_VERSION);
|
||||
}
|
||||
folder /= db->get_db_name();
|
||||
LOG_PRINT_L0("Loading blockchain from folder " << folder << " ...");
|
||||
const std::string filename = folder.string();
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
Binary file not shown.
@@ -282,13 +282,62 @@ crypto::key_image carrot_and_legacy_account::derive_key_image(const crypto::publ
|
||||
return L;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void carrot_and_legacy_account::generate_subaddress_map()
|
||||
crypto::key_image carrot_and_legacy_account::derive_key_image_view_only(const crypto::public_key &address_spend_pubkey,
|
||||
const crypto::secret_key &sender_extension_g,
|
||||
const crypto::secret_key &sender_extension_t,
|
||||
const crypto::public_key &onetime_address) const
|
||||
{
|
||||
const auto it = subaddress_map.find(address_spend_pubkey);
|
||||
CHECK_AND_ASSERT_THROW_MES(it != subaddress_map.cend(),
|
||||
"carrot and legacy account: derive key image view only: cannot find subaddress");
|
||||
|
||||
const bool is_subaddress = it->second.index.is_subaddress();
|
||||
const uint32_t major_index = it->second.index.major;
|
||||
const uint32_t minor_index = it->second.index.minor;
|
||||
|
||||
const cryptonote::account_keys &keys = get_keys();
|
||||
|
||||
crypto::secret_key address_index_generator;
|
||||
crypto::secret_key subaddress_scalar;
|
||||
crypto::secret_key subaddress_extension;
|
||||
crypto::secret_key address_privkey_g;
|
||||
crypto::secret_key x;
|
||||
|
||||
CHECK_AND_ASSERT_THROW_MES(it->second.derive_type == AddressDeriveType::Carrot,
|
||||
"carrot and legacy account: derive key image view only: not a Carrot address");
|
||||
|
||||
// s^j_gen = H_32[s_ga](j_major, j_minor)
|
||||
make_carrot_index_extension_generator(keys.s_generate_address, major_index, minor_index, address_index_generator);
|
||||
|
||||
if (is_subaddress)
|
||||
{
|
||||
// k^j_subscal = H_n(K_s, j_major, j_minor, s^j_gen)
|
||||
make_carrot_subaddress_scalar(keys.m_carrot_account_address.m_spend_public_key, address_index_generator, major_index, minor_index, subaddress_scalar);
|
||||
}
|
||||
else
|
||||
{
|
||||
// k^j_subscal = 1
|
||||
sc_1(to_bytes(subaddress_scalar));
|
||||
}
|
||||
|
||||
// k^g_a = k_gi * k^j_subscal
|
||||
sc_mul(to_bytes(address_privkey_g), to_bytes(keys.k_generate_image), to_bytes(subaddress_scalar));
|
||||
|
||||
// x = k^{j,g}_addr + k^g_o
|
||||
sc_add(to_bytes(x), to_bytes(address_privkey_g), to_bytes(sender_extension_g));
|
||||
|
||||
crypto::key_image L;
|
||||
crypto::generate_key_image(onetime_address, x, L);
|
||||
return L;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void carrot_and_legacy_account::generate_subaddress_map(const std::pair<size_t, size_t>& lookahead_size)
|
||||
{
|
||||
const std::vector<AddressDeriveType> derive_types{AddressDeriveType::Carrot, AddressDeriveType::PreCarrot};
|
||||
|
||||
for (uint32_t major_index = 0; major_index <= MAX_SUBADDRESS_MAJOR_INDEX; ++major_index)
|
||||
for (uint32_t major_index = 0; major_index <= lookahead_size.first; ++major_index)
|
||||
{
|
||||
for (uint32_t minor_index = 0; minor_index <= MAX_SUBADDRESS_MINOR_INDEX; ++minor_index)
|
||||
for (uint32_t minor_index = 0; minor_index <= lookahead_size.second; ++minor_index)
|
||||
{
|
||||
for (const AddressDeriveType derive_type : derive_types)
|
||||
{
|
||||
@@ -337,7 +386,7 @@ void carrot_and_legacy_account::set_keys(const cryptonote::account_keys& keys, b
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void carrot_and_legacy_account::create_from_svb_key(const cryptonote::account_public_address& address, const crypto::secret_key& svb_key)
|
||||
{
|
||||
{
|
||||
// top level keys
|
||||
m_keys.s_master = crypto::null_skey;
|
||||
make_carrot_provespend_key(m_keys.s_master, m_keys.k_prove_spend);
|
||||
@@ -359,9 +408,18 @@ void carrot_and_legacy_account::create_from_svb_key(const cryptonote::account_pu
|
||||
k_view_incoming_dev.view_key_scalar_mult_ed25519(crypto::get_G(),
|
||||
m_keys.m_carrot_main_address.m_view_public_key
|
||||
);
|
||||
|
||||
|
||||
// Store fields for Carrot
|
||||
m_keys.m_spend_secret_key = crypto::null_skey;
|
||||
m_keys.m_view_secret_key = crypto::null_skey;
|
||||
m_keys.m_account_address = {crypto::null_pkey, crypto::null_pkey, false};
|
||||
|
||||
// Update ALL addresses to be Carrot-only
|
||||
m_keys.m_carrot_account_address.m_is_carrot = true;
|
||||
m_keys.m_carrot_main_address.m_is_carrot = true;
|
||||
|
||||
// Set the default derive type for addresses
|
||||
this->default_derive_type = AddressDeriveType::Carrot;
|
||||
generate_subaddress_map();
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void carrot_and_legacy_account::set_carrot_keys(const AddressDeriveType default_derive_type)
|
||||
@@ -393,13 +451,19 @@ void carrot_and_legacy_account::set_carrot_keys(const AddressDeriveType default_
|
||||
m_keys.m_carrot_main_address.m_is_carrot = true;
|
||||
|
||||
this->default_derive_type = default_derive_type;
|
||||
generate_subaddress_map();
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void carrot_and_legacy_account::insert_subaddresses(const std::unordered_map<crypto::public_key, subaddress_index_extended>& subaddress_map_cn)
|
||||
{
|
||||
for (const auto &p : subaddress_map_cn)
|
||||
subaddress_map.insert({p.first, {{p.second.index.major, p.second.index.minor}, p.second.derive_type, p.second.is_return_spend_key}});
|
||||
for (const auto &p : subaddress_map_cn) {
|
||||
subaddress_map.insert({p.first, {{p.second.index.major, p.second.index.minor}, p.second.derive_type, p.second.is_return_spend_key}});
|
||||
if (p.second.derive_type == AddressDeriveType::PreCarrot) {
|
||||
// Create a matching Carrot address
|
||||
const subaddress_index_extended subaddr_index{{p.second.index.major, p.second.index.minor}, AddressDeriveType::Carrot, p.second.is_return_spend_key};
|
||||
const CarrotDestinationV1 addr = subaddress(subaddr_index);
|
||||
subaddress_map.insert({addr.address_spend_pubkey, subaddr_index});
|
||||
}
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void carrot_and_legacy_account::insert_return_output_info(const std::unordered_map<crypto::public_key, return_output_info_t>& roi_map)
|
||||
|
||||
+61
-21
@@ -48,12 +48,59 @@ namespace carrot
|
||||
input_context_t input_context;
|
||||
crypto::public_key K_o; // output onetime address
|
||||
crypto::public_key K_change; // change output onetime address
|
||||
crypto::public_key K_spend_pubkey; // change output spend pubkey
|
||||
crypto::key_image key_image;
|
||||
crypto::secret_key sum_g;
|
||||
crypto::secret_key sender_extension_t;
|
||||
|
||||
return_output_info_t() {
|
||||
// Default constructor for serialization
|
||||
input_context = input_context_t();
|
||||
K_o = crypto::public_key();
|
||||
K_change = crypto::public_key();
|
||||
K_spend_pubkey = crypto::public_key();
|
||||
key_image = crypto::key_image();
|
||||
sum_g = crypto::secret_key();
|
||||
sender_extension_t = crypto::secret_key();
|
||||
}
|
||||
|
||||
return_output_info_t(
|
||||
const input_context_t &input_context,
|
||||
const crypto::public_key &K_o,
|
||||
const crypto::public_key &K_change,
|
||||
const crypto::public_key &K_spend_pubkey,
|
||||
const crypto::key_image &key_image,
|
||||
const crypto::secret_key &sum_g,
|
||||
const crypto::secret_key &sender_extension_t):
|
||||
input_context(input_context),
|
||||
K_o(K_o),
|
||||
K_change(K_change),
|
||||
K_spend_pubkey(K_spend_pubkey),
|
||||
key_image(key_image),
|
||||
sum_g(sum_g),
|
||||
sender_extension_t(sender_extension_t) {}
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
FIELD(input_context)
|
||||
FIELD(K_o)
|
||||
FIELD(K_change)
|
||||
FIELD(K_spend_pubkey)
|
||||
FIELD(key_image)
|
||||
FIELD(sum_g)
|
||||
FIELD(sender_extension_t)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
// Old return_output_info_t format (for deserializing version 2 wallet caches)
|
||||
struct return_output_info_retired_t {
|
||||
input_context_t input_context;
|
||||
crypto::public_key K_o;
|
||||
crypto::public_key K_change;
|
||||
crypto::key_image key_image;
|
||||
crypto::secret_key x;
|
||||
crypto::secret_key y;
|
||||
|
||||
return_output_info_t() {
|
||||
// Default constructor for serialization
|
||||
return_output_info_retired_t() {
|
||||
input_context = input_context_t();
|
||||
K_o = crypto::public_key();
|
||||
K_change = crypto::public_key();
|
||||
@@ -62,20 +109,6 @@ namespace carrot
|
||||
y = crypto::secret_key();
|
||||
}
|
||||
|
||||
return_output_info_t(
|
||||
const input_context_t &input_context,
|
||||
const crypto::public_key &K_o,
|
||||
const crypto::public_key &K_change,
|
||||
const crypto::key_image &key_image,
|
||||
const crypto::secret_key &x,
|
||||
const crypto::secret_key &y):
|
||||
input_context(input_context),
|
||||
K_o(K_o),
|
||||
K_change(K_change),
|
||||
key_image(key_image),
|
||||
x(x),
|
||||
y(y) {}
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
FIELD(input_context)
|
||||
FIELD(K_o)
|
||||
@@ -144,7 +177,12 @@ namespace carrot
|
||||
const crypto::secret_key &sender_extension_t,
|
||||
const crypto::public_key &onetime_address) const;
|
||||
|
||||
void generate_subaddress_map();
|
||||
crypto::key_image derive_key_image_view_only(const crypto::public_key &address_spend_pubkey,
|
||||
const crypto::secret_key &sender_extension_g,
|
||||
const crypto::secret_key &sender_extension_t,
|
||||
const crypto::public_key &onetime_address) const;
|
||||
|
||||
void generate_subaddress_map(const std::pair<size_t, size_t>& lookahead_size);
|
||||
|
||||
crypto::secret_key generate(
|
||||
const crypto::secret_key& recovery_key = crypto::secret_key(),
|
||||
@@ -183,9 +221,10 @@ namespace boost
|
||||
x.input_context = carrot::input_context_t();
|
||||
x.K_o = crypto::public_key();
|
||||
x.K_change = crypto::public_key();
|
||||
x.K_spend_pubkey = crypto::public_key();
|
||||
x.key_image = crypto::key_image();
|
||||
x.x = crypto::secret_key();
|
||||
x.y = crypto::secret_key();
|
||||
x.sum_g = crypto::secret_key();
|
||||
x.sender_extension_t = crypto::secret_key();
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
@@ -194,9 +233,10 @@ namespace boost
|
||||
a & x.input_context;
|
||||
a & x.K_o;
|
||||
a & x.K_change;
|
||||
a & x.K_spend_pubkey;
|
||||
a & x.key_image;
|
||||
a & x.x;
|
||||
a & x.y;
|
||||
a & x.sum_g;
|
||||
a & x.sender_extension_t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,5 +75,6 @@ static constexpr const unsigned int CARROT_MAX_TX_INPUTS = 64;
|
||||
// SPARC addressing protocol domain separators
|
||||
static constexpr const unsigned char SPARC_DOMAIN_SEP_RETURN_PUBKEY_ENCRYPTION_MASK[] = "SPARC return pubkey encryption mask";
|
||||
static constexpr const unsigned char SPARC_DOMAIN_SEP_RETURN_ADDRESS_SCALAR[] = "SPARC return address scalar";
|
||||
static constexpr const unsigned char SPARC_DOMAIN_SEP_RETURN_INDEX_SCALAR[] = "SPARC return index scalar";
|
||||
|
||||
} //namespace carrot
|
||||
|
||||
@@ -122,6 +122,11 @@ bool operator==(const view_tag_t &a, const view_tag_t &b)
|
||||
return memcmp(&a, &b, sizeof(view_tag_t)) == 0;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool operator==(const rollup_binding_tag_t &a, const rollup_binding_tag_t &b)
|
||||
{
|
||||
return memcmp(&a, &b, sizeof(rollup_binding_tag_t)) == 0;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
janus_anchor_t gen_janus_anchor()
|
||||
{
|
||||
return crypto::rand<janus_anchor_t>();
|
||||
|
||||
@@ -106,6 +106,13 @@ struct encrypted_return_pubkey_t final
|
||||
unsigned char bytes[ENCRYPTED_RETURN_PUBKEY_BYTES];
|
||||
};
|
||||
|
||||
/// Salvium rollup binding tag
|
||||
constexpr std::size_t ROLLUP_BINDING_TAG_BYTES{8};
|
||||
struct rollup_binding_tag_t final
|
||||
{
|
||||
unsigned char bytes[ROLLUP_BINDING_TAG_BYTES];
|
||||
};
|
||||
|
||||
/// overloaded operators: address tag
|
||||
bool operator==(const janus_anchor_t &a, const janus_anchor_t &b);
|
||||
static inline bool operator!=(const janus_anchor_t &a, const janus_anchor_t &b) { return !(a == b); }
|
||||
@@ -139,6 +146,10 @@ bool operator==(const encrypted_return_pubkey_t &a, const encrypted_return_pubke
|
||||
static inline bool operator!=(const encrypted_return_pubkey_t &a, const encrypted_return_pubkey_t &b) { return !(a == b); }
|
||||
encrypted_return_pubkey_t operator^(const encrypted_return_pubkey_t &a, const encrypted_return_pubkey_t &b);
|
||||
|
||||
/// overloaded operators: rollup binding tag
|
||||
bool operator==(const rollup_binding_tag_t &a, const rollup_binding_tag_t &b);
|
||||
static inline bool operator!=(const rollup_binding_tag_t &a, const rollup_binding_tag_t &b) { return !(a == b); }
|
||||
|
||||
/// generate a random janus anchor
|
||||
janus_anchor_t gen_janus_anchor();
|
||||
/// generate a random (non-null) payment ID
|
||||
|
||||
@@ -39,6 +39,7 @@ extern "C"
|
||||
#include "crypto/wallet/crypto.h"
|
||||
#include "hash_functions.h"
|
||||
#include "int-util.h"
|
||||
#include "string_tools.h"
|
||||
#include "misc_language.h"
|
||||
#include "ringct/rctOps.h"
|
||||
#include "transcript_fixed.h"
|
||||
@@ -211,6 +212,17 @@ void make_sparc_return_privkey(const unsigned char s_sender_receiver_unctx[32],
|
||||
derive_scalar(transcript.data(), transcript.size(), s_sender_receiver_unctx, &return_privkey_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void make_sparc_return_index(const unsigned char s_sender_receiver_unctx[32],
|
||||
const input_context_t &input_context,
|
||||
const crypto::public_key &onetime_address,
|
||||
const uint64_t idx,
|
||||
crypto::secret_key &return_index_out)
|
||||
{
|
||||
// k_idx = H_32(s_sr || input_context || Ko || idx)
|
||||
const auto transcript = sp::make_fixed_transcript<SPARC_DOMAIN_SEP_RETURN_INDEX_SCALAR>(input_context, onetime_address, idx);
|
||||
derive_scalar(transcript.data(), transcript.size(), s_sender_receiver_unctx, &return_index_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void make_sparc_return_pubkey_encryption_mask(const unsigned char s_sender_receiver_unctx[32],
|
||||
const input_context_t &input_context,
|
||||
const crypto::public_key &onetime_address,
|
||||
@@ -222,22 +234,50 @@ void make_sparc_return_pubkey_encryption_mask(const unsigned char s_sender_recei
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void make_sparc_return_pubkey(const unsigned char s_sender_receiver_unctx[32],
|
||||
const input_context_t &input_context,
|
||||
const view_balance_secret_device *s_view_balance_dev,
|
||||
const crypto::public_key &onetime_address,
|
||||
encrypted_return_pubkey_t &return_pubkey_out)
|
||||
const input_context_t &input_context,
|
||||
const view_balance_secret_device *s_view_balance_dev,
|
||||
const crypto::public_key &onetime_address,
|
||||
const uint64_t idx,
|
||||
encrypted_return_pubkey_t &return_pubkey_out)
|
||||
{
|
||||
// K_return = k_return G ^ m_return
|
||||
// compute k_return
|
||||
crypto::secret_key k_return;
|
||||
crypto::public_key return_pub;
|
||||
encrypted_return_pubkey_t K_return;
|
||||
encrypted_return_pubkey_t m_return;
|
||||
s_view_balance_dev->make_internal_return_privkey(input_context, onetime_address, k_return);
|
||||
crypto::secret_key_to_public_key(k_return, return_pub);
|
||||
static_assert(sizeof(K_return.bytes) == sizeof(return_pub.data), "Size mismatch");
|
||||
memcpy(K_return.bytes, return_pub.data, sizeof(encrypted_return_pubkey_t));
|
||||
make_sparc_return_pubkey_encryption_mask(s_sender_receiver_unctx, input_context, onetime_address, m_return);
|
||||
return_pubkey_out = K_return ^ m_return;
|
||||
|
||||
// compute k_idx
|
||||
crypto::secret_key k_idx;
|
||||
make_sparc_return_index(s_sender_receiver_unctx, input_context, onetime_address, idx, k_idx);
|
||||
|
||||
// compute m_return
|
||||
encrypted_return_pubkey_t m_return;
|
||||
make_sparc_return_pubkey_encryption_mask(s_sender_receiver_unctx,
|
||||
input_context,
|
||||
onetime_address,
|
||||
m_return);
|
||||
|
||||
#if 1
|
||||
// compute SPARC K_return = k_return * G
|
||||
crypto::public_key K_return;
|
||||
crypto::secret_key_to_public_key(k_return, K_return);
|
||||
|
||||
// compute return_enc
|
||||
encrypted_return_pubkey_t return_pub;
|
||||
static_assert(sizeof(K_return.data) == sizeof(return_pub.bytes), "Size mismatch");
|
||||
memcpy(return_pub.bytes, K_return.data, sizeof(encrypted_return_pubkey_t));
|
||||
#else
|
||||
// compute SPARC K_return = k_return * G + k_idx * T
|
||||
rct::key K_return;
|
||||
rct::addKeys2(K_return,
|
||||
rct::sk2rct(k_return),
|
||||
rct::sk2rct(k_idx),
|
||||
rct::pk2rct(crypto::get_T()));
|
||||
|
||||
// compute return_enc
|
||||
encrypted_return_pubkey_t return_pub;
|
||||
static_assert(sizeof(K_return.bytes) == sizeof(return_pub.bytes), "Size mismatch");
|
||||
memcpy(return_pub.bytes, K_return.bytes, sizeof(encrypted_return_pubkey_t));
|
||||
#endif
|
||||
return_pubkey_out = return_pub ^ m_return;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
@@ -470,7 +510,6 @@ bool test_carrot_view_tag(const unsigned char s_sender_receiver_unctx[32],
|
||||
// vt' = H_3(s_sr || input_context || Ko)
|
||||
view_tag_t nominal_view_tag;
|
||||
make_carrot_view_tag(s_sender_receiver_unctx, input_context, onetime_address, nominal_view_tag);
|
||||
|
||||
// vt' ?= vt
|
||||
return nominal_view_tag == view_tag;
|
||||
}
|
||||
|
||||
@@ -138,6 +138,20 @@ void make_sparc_return_privkey(const unsigned char s_sender_receiver_unctx[32],
|
||||
const crypto::public_key &onetime_address,
|
||||
crypto::secret_key &return_privkey_out);
|
||||
/**
|
||||
* brief: make_sparc_return_index - return index, given non-secret data
|
||||
* m_return = H_32(s_sr || input_context || Ko || idx)
|
||||
* param: s_sender_receiver_unctx - s_sr
|
||||
* param: input_context - input_context
|
||||
* param: onetime_address - Ko
|
||||
* param: idx - idx
|
||||
* outparam: return_index_out - m_return
|
||||
*/
|
||||
void make_sparc_return_index(const unsigned char s_sender_receiver_unctx[32],
|
||||
const input_context_t &input_context,
|
||||
const crypto::public_key &onetime_address,
|
||||
const uint64_t idx,
|
||||
crypto::secret_key &return_index_out);
|
||||
/**
|
||||
* brief: make_sparc_return_pubkey_encryption_mask - used for hiding return pubkey
|
||||
* m_return = H_32(s_sr || input_context || Ko)
|
||||
* param: s_sender_receiver_unctx - s_sr
|
||||
@@ -159,10 +173,11 @@ void make_sparc_return_pubkey_encryption_mask(const unsigned char s_sender_recei
|
||||
* outparam: return_pubkey_mask_out - K_return
|
||||
*/
|
||||
void make_sparc_return_pubkey(const unsigned char s_sender_receiver_unctx[32],
|
||||
const input_context_t &input_context,
|
||||
const view_balance_secret_device *s_view_balance_dev,
|
||||
const crypto::public_key &onetime_address,
|
||||
encrypted_return_pubkey_t &return_pubkey_out);
|
||||
const input_context_t &input_context,
|
||||
const view_balance_secret_device *s_view_balance_dev,
|
||||
const crypto::public_key &onetime_address,
|
||||
const uint64_t idx,
|
||||
encrypted_return_pubkey_t &return_pubkey_out);
|
||||
/**
|
||||
* brief: make_carrot_input_context_coinbase - input context for a sender-receiver secret (coinbase txs)
|
||||
* input_context = "C" || IntToBytes256(block_index)
|
||||
|
||||
@@ -41,9 +41,9 @@
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
#define CARROT_DEFINE_SIMPLE_ERROR_TYPE(e, b) class e: b { using b::b; };
|
||||
#define CARROT_DEFINE_SIMPLE_ERROR_TYPE(e, b) class e: public b { using b::b; };
|
||||
|
||||
class carrot_logic_error: std::logic_error { using std::logic_error::logic_error; };
|
||||
class carrot_logic_error: public std::logic_error { using std::logic_error::logic_error; };
|
||||
|
||||
CARROT_DEFINE_SIMPLE_ERROR_TYPE(bad_address_type, carrot_logic_error)
|
||||
CARROT_DEFINE_SIMPLE_ERROR_TYPE(component_out_of_order, carrot_logic_error)
|
||||
@@ -55,7 +55,7 @@ CARROT_DEFINE_SIMPLE_ERROR_TYPE(too_few_outputs, carrot_logic_error)
|
||||
CARROT_DEFINE_SIMPLE_ERROR_TYPE(too_many_outputs, carrot_logic_error)
|
||||
CARROT_DEFINE_SIMPLE_ERROR_TYPE(invalid_tx_type, carrot_logic_error)
|
||||
|
||||
class carrot_runtime_error: std::runtime_error { using std::runtime_error::runtime_error; };
|
||||
class carrot_runtime_error: public std::runtime_error { using std::runtime_error::runtime_error; };
|
||||
|
||||
CARROT_DEFINE_SIMPLE_ERROR_TYPE(crypto_function_failed, carrot_runtime_error)
|
||||
CARROT_DEFINE_SIMPLE_ERROR_TYPE(not_enough_money, carrot_runtime_error)
|
||||
|
||||
@@ -77,7 +77,7 @@ std::optional<AdditionalOutputType> get_additional_output_type(const size_t num_
|
||||
}
|
||||
else if (!need_change_output)
|
||||
{
|
||||
return AdditionalOutputType::DUMMY;
|
||||
return AdditionalOutputType::CHANGE_UNIQUE;
|
||||
}
|
||||
else // num_selfsend == 1 && need_change_output
|
||||
{
|
||||
@@ -174,7 +174,10 @@ void get_output_enote_proposals(const std::vector<CarrotPaymentProposalV1> &norm
|
||||
// assert payment proposals numbers
|
||||
const size_t num_selfsend_proposals = selfsend_payment_proposals.size();
|
||||
const size_t num_proposals = normal_payment_proposals.size() + num_selfsend_proposals;
|
||||
if (tx_type == cryptonote::transaction_type::STAKE || tx_type == cryptonote::transaction_type::BURN) {
|
||||
if (tx_type == cryptonote::transaction_type::STAKE ||
|
||||
tx_type == cryptonote::transaction_type::BURN ||
|
||||
tx_type == cryptonote::transaction_type::CREATE_TOKEN ||
|
||||
tx_type == cryptonote::transaction_type::ROLLUP) {
|
||||
CARROT_CHECK_AND_THROW(num_proposals == 1, too_few_outputs, "tx doesn't have correct number of proposals");
|
||||
} else {
|
||||
CARROT_CHECK_AND_THROW(num_proposals >= CARROT_MIN_TX_OUTPUTS, too_few_outputs, "too few payment proposals");
|
||||
@@ -221,11 +224,12 @@ void get_output_enote_proposals(const std::vector<CarrotPaymentProposalV1> &norm
|
||||
|
||||
encrypted_payment_id_t encrypted_payment_id;
|
||||
if (tx_type == cryptonote::transaction_type::RETURN) {
|
||||
const uint64_t idx = 0;
|
||||
get_output_proposal_return_v1(normal_payment_proposals[i],
|
||||
tx_first_key_image,
|
||||
s_view_balance_dev,
|
||||
output_entry.first,
|
||||
encrypted_payment_id);
|
||||
tx_first_key_image,
|
||||
s_view_balance_dev,
|
||||
output_entry.first,
|
||||
encrypted_payment_id);
|
||||
} else {
|
||||
get_output_proposal_normal_v1(normal_payment_proposals[i],
|
||||
tx_first_key_image,
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "misc_language.h"
|
||||
#include "misc_log_ex.h"
|
||||
#include "ringct/rctOps.h"
|
||||
#include "crypto/generators.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
@@ -208,10 +209,11 @@ static void get_external_output_proposal_parts(const mx25519_pubkey &s_sender_re
|
||||
// 4. construct the return pubkey
|
||||
if (s_view_balance_dev != nullptr)
|
||||
make_sparc_return_pubkey(s_sender_receiver_unctx.data,
|
||||
input_context,
|
||||
s_view_balance_dev,
|
||||
onetime_address_out,
|
||||
return_pubkey_out);
|
||||
input_context,
|
||||
s_view_balance_dev,
|
||||
onetime_address_out,
|
||||
0,
|
||||
return_pubkey_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
@@ -219,6 +221,7 @@ bool operator==(const CarrotPaymentProposalV1 &a, const CarrotPaymentProposalV1
|
||||
{
|
||||
return a.destination == b.destination &&
|
||||
a.amount == b.amount &&
|
||||
a.asset_type == b.asset_type &&
|
||||
a.randomness == b.randomness;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
@@ -228,6 +231,7 @@ bool operator==(const CarrotPaymentProposalSelfSendV1 &a, const CarrotPaymentPro
|
||||
a.amount == b.amount &&
|
||||
a.enote_type == b.enote_type &&
|
||||
a.internal_message == b.internal_message &&
|
||||
a.asset_type == b.asset_type &&
|
||||
0 == memcmp(&a.enote_ephemeral_pubkey, &b.enote_ephemeral_pubkey, sizeof(mx25519_pubkey));
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
@@ -421,11 +425,95 @@ void get_output_proposal_special_v1(const CarrotPaymentProposalSelfSendV1 &propo
|
||||
// 8. save the enote ephemeral pubkey, first tx key image, and amount
|
||||
output_enote_out.enote.enote_ephemeral_pubkey = enote_ephemeral_pubkey;
|
||||
output_enote_out.enote.tx_first_key_image = tx_first_key_image;
|
||||
output_enote_out.enote.asset_type = "SAL1";
|
||||
output_enote_out.enote.asset_type = proposal.asset_type;
|
||||
output_enote_out.enote.return_enc = crypto::rand<carrot::encrypted_return_pubkey_t>();
|
||||
output_enote_out.amount = proposal.amount;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void get_output_proposal_paymentchannel_v1(const CarrotPaymentProposalV1 &proposal,
|
||||
const crypto::key_image &tx_first_key_image,
|
||||
const view_balance_secret_device *s_view_balance_dev,
|
||||
const crypto::public_key &K_o,
|
||||
const uint64_t idx,
|
||||
RCTOutputEnoteProposal &output_enote_out,
|
||||
encrypted_payment_id_t &encrypted_payment_id_out)
|
||||
{
|
||||
// 1. sanity checks
|
||||
CARROT_CHECK_AND_THROW(proposal.randomness != null_anchor,
|
||||
missing_randomness, "invalid randomness for janus anchor (zero).");
|
||||
|
||||
// 2. input context: input_context = "R" || KI_1
|
||||
const input_context_t input_context = make_carrot_input_context(tx_first_key_image);
|
||||
/*
|
||||
// K_v = K_return - k_idx * T
|
||||
CarrotPaymentProposalV1 proposal_with_kv = proposal;
|
||||
const bool is_sparc = (k_idx != crypto::null_skey) && sc_isnonzero(to_bytes(k_idx)); // check here! for zero its not needed
|
||||
if (is_sparc)
|
||||
{
|
||||
// Calculate K_v = K_return - k_idx * T
|
||||
const rct::key K_return_rct = rct::pk2rct(proposal.destination.address_view_pubkey);
|
||||
const rct::key k_idx_T = rct::scalarmultKey(rct::pk2rct(crypto::get_T()), rct::sk2rct(k_idx));
|
||||
rct::key K_v_rct;
|
||||
rct::subKeys(K_v_rct, K_return_rct, k_idx_T);
|
||||
proposal_with_kv.destination.address_view_pubkey = rct::rct2pk(K_v_rct);
|
||||
}
|
||||
*/
|
||||
mx25519_pubkey s_sender_receiver_unctx; auto dhe_wiper = auto_wiper(s_sender_receiver_unctx);
|
||||
get_normal_proposal_ecdh_parts(proposal,
|
||||
input_context,
|
||||
output_enote_out.enote.enote_ephemeral_pubkey,
|
||||
s_sender_receiver_unctx);
|
||||
|
||||
// 4. build the output enote address pieces
|
||||
crypto::hash s_sender_receiver; auto q_wiper = auto_wiper(s_sender_receiver);
|
||||
encrypted_return_pubkey_t return_pubkey;
|
||||
get_external_output_proposal_parts(
|
||||
s_sender_receiver_unctx,
|
||||
proposal.destination.address_spend_pubkey,
|
||||
proposal.destination.payment_id,
|
||||
proposal.amount,
|
||||
CarrotEnoteType::PAYMENT,
|
||||
output_enote_out.enote.enote_ephemeral_pubkey,
|
||||
input_context,
|
||||
s_view_balance_dev,
|
||||
false, // coinbase_amount_commitment
|
||||
s_sender_receiver,
|
||||
output_enote_out.amount_blinding_factor,
|
||||
output_enote_out.enote.amount_commitment,
|
||||
output_enote_out.enote.onetime_address,
|
||||
output_enote_out.enote.amount_enc,
|
||||
encrypted_payment_id_out,
|
||||
output_enote_out.enote.view_tag,
|
||||
return_pubkey
|
||||
);
|
||||
|
||||
// Override the onetime address
|
||||
crypto::secret_key k_idx;
|
||||
make_sparc_return_index(s_sender_receiver_unctx.data, input_context, K_o, idx, k_idx);
|
||||
rct::key K_r = rct::addKeys(rct::pk2rct(proposal.destination.address_spend_pubkey),rct::pk2rct(proposal.destination.address_view_pubkey));
|
||||
rct::key K_idx = rct::scalarmultKey(rct::pk2rct(crypto::get_T()), rct::sk2rct(k_idx));
|
||||
output_enote_out.enote.onetime_address = rct::rct2pk(rct::addKeys(K_r, K_idx));
|
||||
|
||||
// Recalculate the view tag : vt = H_3(s_sr || input_context || Ksra)
|
||||
make_carrot_view_tag(s_sender_receiver_unctx.data, input_context, output_enote_out.enote.onetime_address, output_enote_out.enote.view_tag);
|
||||
|
||||
// Recalculate a_enc = BytesToInt64(a) XOR m_a
|
||||
output_enote_out.enote.amount_enc = encrypt_carrot_amount(proposal.amount, s_sender_receiver, output_enote_out.enote.onetime_address);
|
||||
|
||||
// Recalculate anchor_enc = anchor XOR m_anchor
|
||||
output_enote_out.enote.anchor_enc = encrypt_carrot_anchor(proposal.randomness, s_sender_receiver, output_enote_out.enote.onetime_address);
|
||||
|
||||
// Recalculate the pid_enc = pid XOR m_pid
|
||||
encrypted_payment_id_out = encrypt_legacy_payment_id(proposal.destination.payment_id, s_sender_receiver, output_enote_out.enote.onetime_address);
|
||||
|
||||
// 6. save the amount & first key image & asset type
|
||||
output_enote_out.amount = proposal.amount;
|
||||
output_enote_out.enote.asset_type = proposal.asset_type;
|
||||
output_enote_out.enote.tx_first_key_image = tx_first_key_image;
|
||||
// disable returning an already returned tx.
|
||||
output_enote_out.enote.return_enc = crypto::rand<carrot::encrypted_return_pubkey_t>();
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void get_output_proposal_return_v1(const CarrotPaymentProposalV1 &proposal,
|
||||
const crypto::key_image &tx_first_key_image,
|
||||
const view_balance_secret_device *s_view_balance_dev,
|
||||
@@ -559,12 +647,12 @@ void get_output_proposal_internal_v1(const CarrotPaymentProposalSelfSendV1 &prop
|
||||
// 9. save the enote ephemeral pubkey, first tx key image, and amount
|
||||
output_enote_out.enote.enote_ephemeral_pubkey = enote_ephemeral_pubkey;
|
||||
output_enote_out.enote.tx_first_key_image = tx_first_key_image;
|
||||
output_enote_out.enote.asset_type = "SAL1";
|
||||
output_enote_out.enote.asset_type = proposal.asset_type;
|
||||
output_enote_out.enote.return_enc = crypto::rand<carrot::encrypted_return_pubkey_t>();
|
||||
output_enote_out.amount = proposal.amount;
|
||||
|
||||
// 10. construct the stake return enote
|
||||
if (tx_type == cryptonote::transaction_type::STAKE) {
|
||||
if (tx_type == cryptonote::transaction_type::STAKE || tx_type == cryptonote::transaction_type::CREATE_TOKEN) {
|
||||
// make k_return
|
||||
crypto::secret_key k_return;
|
||||
s_view_balance_dev.make_internal_return_privkey(input_context, output_enote_out.enote.onetime_address, k_return);
|
||||
|
||||
@@ -82,6 +82,8 @@ struct CarrotPaymentProposalSelfSendV1 final
|
||||
std::optional<mx25519_pubkey> enote_ephemeral_pubkey;
|
||||
/// anchor: arbitrary, pre-encrypted message for _internal_ selfsends
|
||||
std::optional<janus_anchor_t> internal_message;
|
||||
/// asset type
|
||||
std::string asset_type;
|
||||
};
|
||||
|
||||
struct RCTOutputEnoteProposal
|
||||
@@ -137,6 +139,21 @@ void get_output_proposal_normal_v1(const CarrotPaymentProposalV1 &proposal,
|
||||
RCTOutputEnoteProposal &output_enote_out,
|
||||
encrypted_payment_id_t &encrypted_payment_id_out);
|
||||
/**
|
||||
* brief: get_output_proposal_paymentchannel_v1 - convert the carrot proposal to an output proposal
|
||||
* param: proposal -
|
||||
* param: tx_first_key_image -
|
||||
* param: k_view_dev -
|
||||
* outparam: output_enote_out -
|
||||
* outparam: encrypted_payment_id_out - pid_enc
|
||||
*/
|
||||
void get_output_proposal_paymentchannel_v1(const CarrotPaymentProposalV1 &proposal,
|
||||
const crypto::key_image &tx_first_key_image,
|
||||
const view_balance_secret_device *s_view_balance_dev,
|
||||
const crypto::public_key &K_o,
|
||||
const uint64_t idx,
|
||||
RCTOutputEnoteProposal &output_enote_out,
|
||||
encrypted_payment_id_t &encrypted_payment_id_out);
|
||||
/**
|
||||
* brief: get_output_proposal_return_v1 - convert the carrot proposal to an output proposal
|
||||
* param: proposal -
|
||||
* param: tx_first_key_image -
|
||||
@@ -145,10 +162,10 @@ void get_output_proposal_normal_v1(const CarrotPaymentProposalV1 &proposal,
|
||||
* outparam: encrypted_payment_id_out - pid_enc
|
||||
*/
|
||||
void get_output_proposal_return_v1(const CarrotPaymentProposalV1 &proposal,
|
||||
const crypto::key_image &tx_first_key_image,
|
||||
const view_balance_secret_device *s_view_balance_dev,
|
||||
RCTOutputEnoteProposal &output_enote_out,
|
||||
encrypted_payment_id_t &encrypted_payment_id_out);
|
||||
const crypto::key_image &tx_first_key_image,
|
||||
const view_balance_secret_device *s_view_balance_dev,
|
||||
RCTOutputEnoteProposal &output_enote_out,
|
||||
encrypted_payment_id_t &encrypted_payment_id_out);
|
||||
/**
|
||||
* brief: get_output_proposal_special_v1 - convert the carrot proposal to an output proposal (external selfsend)
|
||||
* param: proposal -
|
||||
|
||||
+19
-19
@@ -387,6 +387,11 @@ bool try_scan_carrot_enote_external_sender(const CarrotEnoteV1 &enote,
|
||||
CarrotEnoteType &enote_type_out,
|
||||
const bool check_pid)
|
||||
{
|
||||
epee::span<const crypto::public_key> main_address_spend_pubkeys;
|
||||
if (destination.is_subaddress)
|
||||
main_address_spend_pubkeys = {};
|
||||
else
|
||||
main_address_spend_pubkeys = {&destination.address_spend_pubkey, 1};
|
||||
crypto::public_key recovered_address_spend_pubkey;
|
||||
payment_id_t recovered_payment_id;
|
||||
CarrotEnoteType recovered_enote_type;
|
||||
@@ -395,7 +400,7 @@ bool try_scan_carrot_enote_external_sender(const CarrotEnoteV1 &enote,
|
||||
if (!try_scan_carrot_enote_external_normal_checked(enote,
|
||||
encrypted_payment_id,
|
||||
s_sender_receiver_unctx,
|
||||
{&destination.address_spend_pubkey, 1},
|
||||
main_address_spend_pubkeys,
|
||||
sender_extension_g_out,
|
||||
sender_extension_t_out,
|
||||
recovered_address_spend_pubkey,
|
||||
@@ -470,6 +475,9 @@ bool try_scan_carrot_enote_internal_receiver(const CarrotEnoteV1 &enote,
|
||||
crypto::public_key &return_address_out,
|
||||
bool &is_return_out)
|
||||
{
|
||||
// Determine whether this is a full wallet or a watch-only wallet
|
||||
const cryptonote::account_keys &keys = account.get_keys();
|
||||
|
||||
// input_context
|
||||
const input_context_t input_context = make_carrot_input_context(enote.tx_first_key_image);
|
||||
|
||||
@@ -488,7 +496,6 @@ bool try_scan_carrot_enote_internal_receiver(const CarrotEnoteV1 &enote,
|
||||
input_context,
|
||||
s_sender_receiver);
|
||||
|
||||
bool normal_change_found = true;
|
||||
if (!try_scan_carrot_enote_internal_burnt(enote,
|
||||
s_sender_receiver,
|
||||
sender_extension_g_out,
|
||||
@@ -518,24 +525,17 @@ bool try_scan_carrot_enote_internal_receiver(const CarrotEnoteV1 &enote,
|
||||
// calculate the key image for the return output
|
||||
crypto::secret_key sum_g;
|
||||
sc_add(to_bytes(sum_g), to_bytes(sender_extension_g_out), to_bytes(k_return));
|
||||
crypto::key_image key_image = account.derive_key_image(
|
||||
account.get_keys().m_carrot_account_address.m_spend_public_key,
|
||||
sum_g,
|
||||
sender_extension_t_out,
|
||||
K_r
|
||||
);
|
||||
crypto::key_image key_image = account.derive_key_image_view_only(address_spend_pubkey_out,
|
||||
sum_g,
|
||||
sender_extension_t_out,
|
||||
K_r
|
||||
);
|
||||
|
||||
crypto::secret_key x, y;
|
||||
account.try_searching_for_opening_for_onetime_address(
|
||||
account.get_keys().m_carrot_account_address.m_spend_public_key,
|
||||
sum_g,
|
||||
sender_extension_t_out,
|
||||
x,
|
||||
y
|
||||
);
|
||||
|
||||
// save the input context & change output key
|
||||
account.insert_return_output_info({{K_r, {input_context, output_key, enote.onetime_address, key_image, x, y}}});
|
||||
// HERE BE DRAGONS!!!
|
||||
// SRCG: test whether this will even work for return_payment detection
|
||||
account.insert_return_output_info({{K_r, {input_context, output_key, enote.onetime_address, address_spend_pubkey_out, key_image, sum_g, sender_extension_t_out}}});
|
||||
//account.insert_return_output_info({{K_r, {input_context, output_key, address_spend_pubkey_out, key_image, sum_g, sender_extension_t_out}}});
|
||||
// LAND AHOY!!!
|
||||
}
|
||||
|
||||
// janus protection checks are not needed for internal scans
|
||||
|
||||
@@ -86,6 +86,12 @@ inline void serialize(Archive &a, carrot::encrypted_return_pubkey_t &x, const bo
|
||||
}
|
||||
//---------------------------------------------------
|
||||
template <class Archive>
|
||||
inline void serialize(Archive &a, carrot::rollup_binding_tag_t &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
a & x.bytes;
|
||||
}
|
||||
//---------------------------------------------------
|
||||
template <class Archive>
|
||||
inline void serialize(Archive &a, carrot::CarrotDestinationV1 &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
a & x.address_spend_pubkey;
|
||||
|
||||
@@ -42,3 +42,4 @@ BLOB_SERIALIZER(carrot::view_tag_t);
|
||||
BLOB_SERIALIZER(carrot::encrypted_janus_anchor_t);
|
||||
BLOB_SERIALIZER(carrot::encrypted_payment_id_t);
|
||||
BLOB_SERIALIZER(carrot::encrypted_return_pubkey_t);
|
||||
BLOB_SERIALIZER(carrot::rollup_binding_tag_t);
|
||||
|
||||
@@ -193,31 +193,51 @@ bool try_load_carrot_extra_v1(
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
cryptonote::transaction store_carrot_to_transaction_v1(const std::vector<CarrotEnoteV1> &enotes,
|
||||
const std::vector<crypto::key_image> &key_images,
|
||||
const std::vector<cryptonote::tx_source_entry> &sources,
|
||||
const rct::xmr_amount fee,
|
||||
const cryptonote::transaction_type tx_type,
|
||||
const rct::xmr_amount tx_amount_burnt,
|
||||
const std::vector<uint8_t> &change_masks,
|
||||
const carrot::RCTOutputEnoteProposal &return_enote,
|
||||
const encrypted_payment_id_t encrypted_payment_id)
|
||||
const std::vector<crypto::key_image> &key_images,
|
||||
const std::vector<cryptonote::tx_source_entry> &sources,
|
||||
const rct::xmr_amount fee,
|
||||
const cryptonote::transaction_type tx_type,
|
||||
const rct::xmr_amount tx_amount_burnt,
|
||||
const std::vector<uint8_t> &change_masks,
|
||||
const cryptonote::token_metadata_t &token,
|
||||
const carrot::RCTOutputEnoteProposal &return_enote,
|
||||
const encrypted_payment_id_t encrypted_payment_id,
|
||||
const uint8_t hf_version)
|
||||
{
|
||||
const size_t nins = key_images.size();
|
||||
const size_t nouts = enotes.size();
|
||||
CHECK_AND_ASSERT_THROW_MES(nins == sources.size(), "invalid inputs/sources size");
|
||||
CHECK_AND_ASSERT_THROW_MES(change_masks.size() == nouts, "invalid change masks size. Expected: " << nouts - 1 << " got: " << change_masks.size());
|
||||
|
||||
// Sanity check asset types - all enotes and sources must be the same
|
||||
std::string asset_type = "";
|
||||
for (const auto &enote: enotes) {
|
||||
if (asset_type == "")
|
||||
asset_type = enote.asset_type;
|
||||
else
|
||||
CHECK_AND_ASSERT_THROW_MES(enote.asset_type == asset_type, "invalid asset_type in enote. Expected: " << asset_type << " got: " << enote.asset_type);
|
||||
}
|
||||
for (const auto &source: sources) {
|
||||
if (asset_type == "")
|
||||
asset_type = source.asset_type;
|
||||
else
|
||||
CHECK_AND_ASSERT_THROW_MES(source.asset_type == asset_type, "invalid asset_type in source. Expected: " << asset_type << " got: " << source.asset_type);
|
||||
}
|
||||
|
||||
cryptonote::transaction tx;
|
||||
tx.pruned = true;
|
||||
tx.unlock_time = 0;
|
||||
tx.source_asset_type = "SAL1";
|
||||
tx.destination_asset_type = "SAL1";
|
||||
tx.version = TRANSACTION_VERSION_CARROT;
|
||||
tx.source_asset_type = asset_type;
|
||||
tx.destination_asset_type = asset_type;
|
||||
tx.version = (hf_version >= HF_VERSION_ENABLE_TOKENS) ? TRANSACTION_VERSION_ENABLE_TOKENS : TRANSACTION_VERSION_CARROT;
|
||||
tx.type =
|
||||
tx_type == cryptonote::transaction_type::RETURN ? cryptonote::transaction_type::TRANSFER : tx_type;
|
||||
tx.amount_burnt = (
|
||||
tx.type == cryptonote::transaction_type::STAKE || tx.type == cryptonote::transaction_type::BURN
|
||||
) ? tx_amount_burnt : 0;
|
||||
tx.type == cryptonote::transaction_type::STAKE ||
|
||||
tx.type == cryptonote::transaction_type::BURN ||
|
||||
tx.type == cryptonote::transaction_type::CREATE_TOKEN ||
|
||||
tx.type == cryptonote::transaction_type::ROLLUP
|
||||
) ? tx_amount_burnt : 0;
|
||||
tx.return_address_change_mask.assign(change_masks.begin(), change_masks.end());
|
||||
tx.vin.reserve(nins);
|
||||
tx.vout.reserve(nouts);
|
||||
@@ -239,7 +259,7 @@ cryptonote::transaction store_carrot_to_transaction_v1(const std::vector<CarrotE
|
||||
//L
|
||||
tx.vin.emplace_back(cryptonote::txin_to_key{ //@TODO: can save 2 bytes by using slim input type
|
||||
.amount = 0,
|
||||
.asset_type = "SAL1",
|
||||
.asset_type = asset_type,
|
||||
.key_offsets = cryptonote::absolute_output_offsets_to_relative(key_offsets),
|
||||
.k_image = key_images.at(i)
|
||||
});
|
||||
@@ -264,7 +284,7 @@ cryptonote::transaction store_carrot_to_transaction_v1(const std::vector<CarrotE
|
||||
tx.rct_signatures.outPk.push_back(rct::ctkey{rct::key{}, enote.amount_commitment});
|
||||
|
||||
//K_return
|
||||
if (tx_type != cryptonote::transaction_type::STAKE) {
|
||||
if (tx_type != cryptonote::transaction_type::STAKE && tx_type != cryptonote::transaction_type::CREATE_TOKEN) {
|
||||
crypto::public_key K_return;
|
||||
memcpy(K_return.data, enote.return_enc.bytes, sizeof(crypto::public_key));
|
||||
tx.return_address_list.push_back(K_return);
|
||||
@@ -272,7 +292,7 @@ cryptonote::transaction store_carrot_to_transaction_v1(const std::vector<CarrotE
|
||||
}
|
||||
|
||||
// store the return pubkey for stake txs
|
||||
if (tx_type == cryptonote::transaction_type::STAKE)
|
||||
if (tx_type == cryptonote::transaction_type::STAKE || tx_type == cryptonote::transaction_type::CREATE_TOKEN)
|
||||
{
|
||||
tx.protocol_tx_data.version = 1;
|
||||
memcpy(tx.protocol_tx_data.return_address.data, return_enote.enote.onetime_address.data, sizeof(crypto::public_key));
|
||||
@@ -280,7 +300,12 @@ cryptonote::transaction store_carrot_to_transaction_v1(const std::vector<CarrotE
|
||||
tx.protocol_tx_data.return_view_tag = return_enote.enote.view_tag;
|
||||
tx.protocol_tx_data.return_anchor_enc = return_enote.enote.anchor_enc;
|
||||
}
|
||||
|
||||
if (tx_type == cryptonote::transaction_type::CREATE_TOKEN) {
|
||||
tx.token_metadata = token;
|
||||
} else {
|
||||
tx.token_metadata = cryptonote::token_metadata_t{};
|
||||
}
|
||||
|
||||
//ephemeral pubkeys: D_e
|
||||
store_carrot_ephemeral_pubkeys_to_extra(enotes, tx.extra);
|
||||
|
||||
|
||||
@@ -102,14 +102,16 @@ bool try_load_carrot_extra_v1(
|
||||
* return: a fully populated, pruned, non-coinbase transaction containing given Carrot information
|
||||
*/
|
||||
cryptonote::transaction store_carrot_to_transaction_v1(const std::vector<CarrotEnoteV1> &enotes,
|
||||
const std::vector<crypto::key_image> &key_images,
|
||||
const std::vector<cryptonote::tx_source_entry> &sources,
|
||||
const rct::xmr_amount fee,
|
||||
const cryptonote::transaction_type tx_type,
|
||||
const rct::xmr_amount tx_amount_burnt,
|
||||
const std::vector<uint8_t> &change_masks,
|
||||
const RCTOutputEnoteProposal &return_enote,
|
||||
const encrypted_payment_id_t encrypted_payment_id);
|
||||
const std::vector<crypto::key_image> &key_images,
|
||||
const std::vector<cryptonote::tx_source_entry> &sources,
|
||||
const rct::xmr_amount fee,
|
||||
const cryptonote::transaction_type tx_type,
|
||||
const rct::xmr_amount tx_amount_burnt,
|
||||
const std::vector<uint8_t> &change_masks,
|
||||
const cryptonote::token_metadata_t &token,
|
||||
const RCTOutputEnoteProposal &return_enote,
|
||||
const encrypted_payment_id_t encrypted_payment_id,
|
||||
const uint8_t hf_version);
|
||||
/**
|
||||
* brief: try_load_carrot_enote_from_transaction_v1 - load one non-coinbase Carrot enote from a cryptonote::transaction
|
||||
* param: tx -
|
||||
|
||||
@@ -88,7 +88,8 @@ static std::pair<std::size_t, boost::multiprecision::uint128_t> input_count_for_
|
||||
const epee::span<const InputCandidate> input_candidates,
|
||||
const std::set<std::size_t> &selectable_inputs,
|
||||
std::size_t max_num_input_count,
|
||||
const std::map<std::size_t, rct::xmr_amount> &fee_by_input_count)
|
||||
const std::map<std::size_t, rct::xmr_amount> &fee_by_input_count,
|
||||
const bool is_token_transfer)
|
||||
{
|
||||
// Returns (N, X) where the X is the sum of the amounts of the greatest N <= max_num_input_count
|
||||
// inputs from selectable_inputs, maximizing X - F(N). F(N) is the fee for this transaction,
|
||||
@@ -96,6 +97,7 @@ static std::pair<std::size_t, boost::multiprecision::uint128_t> input_count_for_
|
||||
// the fee, but greater than or equal to the difference of the fee compared to excluding that
|
||||
// input. If this function returns N == 0, then there aren't enough usable funds, i.e. no N
|
||||
// exists such that X - F(N) > 0.
|
||||
// For token transfers, fee is paid separately in SAL1 so we just accumulate all inputs.
|
||||
|
||||
if (fee_by_input_count.empty() || selectable_inputs.empty())
|
||||
return {0, 0};
|
||||
@@ -125,6 +127,15 @@ static std::pair<std::size_t, boost::multiprecision::uint128_t> input_count_for_
|
||||
{
|
||||
const rct::xmr_amount amount = *amount_it;
|
||||
const rct::xmr_amount current_fee = fee_by_input_count.at(num_ins + 1);
|
||||
|
||||
// For token transfers, fee is paid separately in SAL1 - just accumulate inputs
|
||||
if (is_token_transfer)
|
||||
{
|
||||
++num_ins;
|
||||
cumulative_input_sum += amount;
|
||||
continue;
|
||||
}
|
||||
|
||||
CARROT_CHECK_AND_THROW(current_fee > last_fee,
|
||||
carrot_logic_error, "provided fee by input count is not monotonically increasing");
|
||||
const rct::xmr_amount marginal_fee_diff = current_fee - last_fee;
|
||||
@@ -372,11 +383,12 @@ select_inputs_func_t make_single_transfer_input_selector(
|
||||
|
||||
// 3. Calculate minimum required input money sum for a given input count
|
||||
const bool subtract_fee = flags & IS_KNOWN_FEE_SUBTRACTABLE;
|
||||
const bool is_token_transfer = flags & IS_TOKEN_TRANSFER;
|
||||
std::map<std::size_t, boost::multiprecision::uint128_t> required_money_by_input_count;
|
||||
for (const auto &fee_and_input_count : fee_by_input_count)
|
||||
{
|
||||
required_money_by_input_count[fee_and_input_count.first] =
|
||||
nominal_output_sum + (subtract_fee ? 0 : fee_and_input_count.second);
|
||||
nominal_output_sum + ((subtract_fee || is_token_transfer) ? 0 : fee_and_input_count.second);
|
||||
}
|
||||
const boost::multiprecision::uint128_t absolute_minimum_required_money
|
||||
= required_money_by_input_count.cbegin()->second;
|
||||
@@ -390,9 +402,10 @@ select_inputs_func_t make_single_transfer_input_selector(
|
||||
"Not enough money in all inputs (" << cryptonote::print_money(total_candidate_money)
|
||||
<< ") to fund minimum output sum (" << cryptonote::print_money(absolute_minimum_required_money) << ')');
|
||||
|
||||
// const bool is_token_transfer = flags & IS_TOKEN_TRANSFER;
|
||||
std::set<std::size_t> all_idxs; for (std::size_t i = 0; i < input_candidates.size(); ++i) all_idxs.insert(i);
|
||||
const std::pair<std::size_t, boost::multiprecision::uint128_t> max_usable_money =
|
||||
input_count_for_max_usable_money(input_candidates, all_idxs, CARROT_MAX_TX_INPUTS, fee_by_input_count);
|
||||
input_count_for_max_usable_money(input_candidates, all_idxs, CARROT_MAX_TX_INPUTS, fee_by_input_count, is_token_transfer);
|
||||
CARROT_CHECK_AND_THROW(max_usable_money.second >= absolute_minimum_required_money,
|
||||
not_enough_usable_money,
|
||||
"Not enough usable money in top " << max_usable_money.first << " inputs ("
|
||||
@@ -417,7 +430,7 @@ select_inputs_func_t make_single_transfer_input_selector(
|
||||
|
||||
// Skip if not enough money in this selectable set for max number of tx inputs...
|
||||
const auto max_usable_money = input_count_for_max_usable_money(input_candidates,
|
||||
input_candidate_subset, CARROT_MAX_TX_INPUTS, fee_by_input_count);
|
||||
input_candidate_subset, CARROT_MAX_TX_INPUTS, fee_by_input_count, is_token_transfer);
|
||||
if (!max_usable_money.first)
|
||||
continue;
|
||||
else if (max_usable_money.second < required_money_by_input_count.at(max_usable_money.first))
|
||||
@@ -441,7 +454,7 @@ select_inputs_func_t make_single_transfer_input_selector(
|
||||
|
||||
// Skip if not enough money in this selectable set for exact number of inputs...
|
||||
const auto max_usable_money = input_count_for_max_usable_money(input_candidates,
|
||||
input_candidate_subset, n_inputs, fee_by_input_count);
|
||||
input_candidate_subset, n_inputs, fee_by_input_count, is_token_transfer);
|
||||
if (max_usable_money.first != n_inputs)
|
||||
continue;
|
||||
else if (max_usable_money.second < required_money)
|
||||
|
||||
@@ -52,14 +52,17 @@ struct InputCandidate
|
||||
namespace InputSelectionFlags
|
||||
{
|
||||
// Quantum forward secrecy (ON = unsafe)
|
||||
static constexpr std::uint32_t ALLOW_EXTERNAL_INPUTS_IN_NORMAL_TRANSFERS = 1 << 0;
|
||||
static constexpr std::uint32_t ALLOW_PRE_CARROT_INPUTS_IN_NORMAL_TRANSFERS = 1 << 1;
|
||||
static constexpr std::uint32_t ALLOW_EXTERNAL_INPUTS_IN_NORMAL_TRANSFERS = 1 << 0; // 000..00000001
|
||||
static constexpr std::uint32_t ALLOW_PRE_CARROT_INPUTS_IN_NORMAL_TRANSFERS = 1 << 1; // 000..00000010
|
||||
static constexpr std::uint32_t ALLOW_MIXED_INTERNAL_EXTERNAL = 1 << 2;
|
||||
static constexpr std::uint32_t ALLOW_MIXED_CARROT_PRE_CARROT = 1 << 3;
|
||||
|
||||
// Amount handling
|
||||
static constexpr std::uint32_t IS_KNOWN_FEE_SUBTRACTABLE = 1 << 4;
|
||||
static constexpr std::uint32_t ALLOW_DUST = 1 << 5;
|
||||
|
||||
// Token transfer (fee is paid separately in SAL1 via rollup tx, so no fee for input selection)
|
||||
static constexpr std::uint32_t IS_TOKEN_TRANSFER = 1 << 6;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -132,7 +132,9 @@ void make_pruned_transaction_from_proposal_v1(const CarrotTransactionProposalV1
|
||||
0, // tx_amount_burnt
|
||||
{}, // change_masks
|
||||
{}, // return_enote
|
||||
encrypted_payment_id);
|
||||
{0},
|
||||
encrypted_payment_id,
|
||||
/*hf_version=*/10);
|
||||
|
||||
// add extra payload and sort
|
||||
if (!tx_proposal.extra.empty())
|
||||
|
||||
@@ -67,8 +67,8 @@ struct CarrotPaymentProposalVerifiableSelfSendV1
|
||||
*
|
||||
* For exact details on what goes into the signable transaction hash, see `rct::get_pre_mlsag_hash`.
|
||||
*/
|
||||
struct CarrotTransactionProposalV1
|
||||
{
|
||||
struct CarrotTransactionProposalV1
|
||||
{
|
||||
/// Key images sorted in std::greater order
|
||||
std::vector<crypto::key_image> key_images_sorted;
|
||||
// sources in the same order as key_images_sorted.
|
||||
@@ -89,8 +89,18 @@ struct CarrotTransactionProposalV1
|
||||
/// how much money tx burns
|
||||
rct::xmr_amount amount_burnt;
|
||||
|
||||
/// used if this is a CREATE_TOKEN transaction
|
||||
cryptonote::token_metadata_t token;
|
||||
|
||||
/// This field is truly "extra". It should contain only tx.extra fields that aren't present in a
|
||||
/// normal Carrot transaction, i.e. NOT ephemeral pubkeys nor encrypted PIDs
|
||||
std::vector<std::uint8_t> extra;
|
||||
};
|
||||
|
||||
/// Used if this is a TOKEN_TRANSFER or ROLLUP transaction
|
||||
carrot::rollup_binding_tag_t rollup_binding_tag;
|
||||
|
||||
/// Used if this is a ROLLUP transaction
|
||||
cryptonote::layer2_rollup_data_t layer2_rollup_data;
|
||||
};
|
||||
|
||||
} //namespace carrot
|
||||
|
||||
@@ -209,17 +209,34 @@ void make_carrot_transaction_proposal_v1(const std::vector<CarrotPaymentProposal
|
||||
input_amount_sum += selected_input.amount;
|
||||
|
||||
// callback to balance the outputs with the fee and input sum
|
||||
carve_fees_and_balance(input_amount_sum, tx_proposal_out.fee, normal_payment_proposals, selfsend_payment_proposals);
|
||||
std::string asset_type = "SAL1"; // default to SAL1
|
||||
if (!normal_payment_proposals.empty()) {
|
||||
asset_type = normal_payment_proposals.at(0).asset_type;
|
||||
} else if (!selfsend_payment_proposals.empty()) {
|
||||
asset_type = selfsend_payment_proposals.at(0).proposal.asset_type;
|
||||
}
|
||||
uint64_t fee = tx_proposal_out.fee;
|
||||
if (asset_type != "SAL1") {
|
||||
if (tx_type != cryptonote::transaction_type::BURN || asset_type != "SAL") {
|
||||
CARROT_CHECK_AND_THROW(cryptonote::is_valid_custom_asset_type(asset_type),
|
||||
carrot_logic_error, "make_carrot_transaction_proposal_v1: invalid asset type in payment proposals: " << asset_type);
|
||||
}
|
||||
|
||||
fee = 0; //fee is always in SAL1
|
||||
}
|
||||
carve_fees_and_balance(input_amount_sum, fee, normal_payment_proposals, selfsend_payment_proposals);
|
||||
|
||||
// sanity check balance
|
||||
input_amount_sum -= tx_proposal_out.fee;
|
||||
input_amount_sum -= fee;
|
||||
for (const CarrotPaymentProposalV1 &normal_payment_proposal : normal_payment_proposals)
|
||||
input_amount_sum -= normal_payment_proposal.amount;
|
||||
for (const CarrotPaymentProposalVerifiableSelfSendV1 &selfsend_payment_proposal : selfsend_payment_proposals)
|
||||
input_amount_sum -= selfsend_payment_proposal.proposal.amount;
|
||||
|
||||
if (tx_type != cryptonote::transaction_type::STAKE &&
|
||||
tx_type != cryptonote::transaction_type::BURN)
|
||||
tx_type != cryptonote::transaction_type::BURN &&
|
||||
tx_type != cryptonote::transaction_type::CREATE_TOKEN &&
|
||||
tx_type != cryptonote::transaction_type::ROLLUP)
|
||||
{
|
||||
CHECK_AND_ASSERT_THROW_MES(input_amount_sum == 0,
|
||||
"make_carrot_transaction_proposal_v1: post-carved transaction does not balance");
|
||||
@@ -239,7 +256,7 @@ void make_carrot_transaction_proposal_v1(const std::vector<CarrotPaymentProposal
|
||||
tx_proposal_out.key_images_sorted.end(),
|
||||
std::greater{}); // consensus rules dictate inputs sorted in *reverse* lexicographical order since v7
|
||||
|
||||
// set the transaction type
|
||||
// set the transaction type & new asset type
|
||||
tx_proposal_out.tx_type = tx_type;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
@@ -255,6 +272,7 @@ void make_carrot_transaction_proposal_v1_transfer(
|
||||
const subaddress_index_extended &change_address_index,
|
||||
const std::set<std::size_t> &subtractable_normal_payment_proposals,
|
||||
const std::set<std::size_t> &subtractable_selfsend_payment_proposals,
|
||||
const std::string &asset_type,
|
||||
CarrotTransactionProposalV1 &tx_proposal_out)
|
||||
{
|
||||
std::vector<CarrotPaymentProposalVerifiableSelfSendV1> selfsend_payment_proposals = selfsend_payment_proposals_in;
|
||||
@@ -275,7 +293,8 @@ void make_carrot_transaction_proposal_v1_transfer(
|
||||
.proposal = CarrotPaymentProposalSelfSendV1{
|
||||
.destination_address_spend_pubkey = change_address_spend_pubkey,
|
||||
.amount = 0,
|
||||
.enote_type = add_payment_type_selfsend ? CarrotEnoteType::PAYMENT : CarrotEnoteType::CHANGE
|
||||
.enote_type = add_payment_type_selfsend ? CarrotEnoteType::PAYMENT : CarrotEnoteType::CHANGE,
|
||||
.asset_type = asset_type
|
||||
},
|
||||
.subaddr_index = change_address_index
|
||||
});
|
||||
@@ -397,10 +416,13 @@ void make_carrot_transaction_proposal_v1_transfer(
|
||||
|
||||
// remove the self send payment we have made to ourself now that we have our change payment.
|
||||
if (tx_type == cryptonote::transaction_type::STAKE ||
|
||||
tx_type == cryptonote::transaction_type::BURN)
|
||||
tx_type == cryptonote::transaction_type::BURN ||
|
||||
tx_type == cryptonote::transaction_type::CREATE_TOKEN ||
|
||||
tx_type == cryptonote::transaction_type::ROLLUP)
|
||||
{
|
||||
selfsend_payment_proposals.back().proposal.enote_ephemeral_pubkey =
|
||||
selfsend_payment_proposals.front().proposal.enote_ephemeral_pubkey;
|
||||
|
||||
selfsend_payment_proposals.erase(selfsend_payment_proposals.begin());
|
||||
}
|
||||
|
||||
@@ -430,6 +452,7 @@ void make_carrot_transaction_proposal_v1_sweep(
|
||||
std::vector<CarrotSelectedInput> &&selected_inputs,
|
||||
const crypto::public_key &change_address_spend_pubkey,
|
||||
const subaddress_index_extended &change_address_index,
|
||||
const std::string &asset_type,
|
||||
CarrotTransactionProposalV1 &tx_proposal_out)
|
||||
{
|
||||
// sanity check payment proposals are provided
|
||||
@@ -452,6 +475,19 @@ void make_carrot_transaction_proposal_v1_sweep(
|
||||
CHECK_AND_ASSERT_THROW_MES(bool(normal_payment_proposals.size()) ^ bool(selfsend_payment_proposals.size()),
|
||||
"make carrot transaction proposal v1 sweep: both normal and self-send payment proposals are provided");
|
||||
|
||||
std::vector<CarrotPaymentProposalVerifiableSelfSendV1> selfsend_payment_proposals_inout{selfsend_payment_proposals};
|
||||
//if (tx_type == cryptonote::transaction_type::RETURN) {
|
||||
selfsend_payment_proposals_inout.push_back(carrot::CarrotPaymentProposalVerifiableSelfSendV1{
|
||||
.proposal = carrot::CarrotPaymentProposalSelfSendV1{
|
||||
.destination_address_spend_pubkey = change_address_spend_pubkey,
|
||||
.amount = 0,
|
||||
.enote_type = carrot::CarrotEnoteType::CHANGE,
|
||||
.asset_type = asset_type
|
||||
},
|
||||
.subaddr_index = change_address_index
|
||||
});
|
||||
// }
|
||||
|
||||
const bool is_selfsend_sweep = !selfsend_payment_proposals.empty();
|
||||
|
||||
// define input selection callback, which is just a shuttle for `selected_inputs`
|
||||
@@ -502,7 +538,7 @@ void make_carrot_transaction_proposal_v1_sweep(
|
||||
|
||||
// make unsigned transaction with sweep carving callback and selected inputs
|
||||
make_carrot_transaction_proposal_v1(normal_payment_proposals,
|
||||
selfsend_payment_proposals,
|
||||
selfsend_payment_proposals_inout,
|
||||
fee_per_weight,
|
||||
fee_quantization_mask,
|
||||
extra,
|
||||
|
||||
@@ -213,6 +213,7 @@ void make_carrot_transaction_proposal_v1_transfer(
|
||||
const subaddress_index_extended &change_address_index,
|
||||
const std::set<std::size_t> &subtractable_normal_payment_proposals,
|
||||
const std::set<std::size_t> &subtractable_selfsend_payment_proposals,
|
||||
const std::string &asset_type,
|
||||
CarrotTransactionProposalV1 &tx_proposal_out);
|
||||
|
||||
void make_carrot_transaction_proposal_v1_sweep(
|
||||
@@ -225,6 +226,7 @@ void make_carrot_transaction_proposal_v1_sweep(
|
||||
std::vector<CarrotSelectedInput> &&selected_inputs,
|
||||
const crypto::public_key &change_address_spend_pubkey,
|
||||
const subaddress_index_extended &change_address_index,
|
||||
const std::string &asset_type,
|
||||
CarrotTransactionProposalV1 &tx_proposal_out);
|
||||
|
||||
} //namespace carrot
|
||||
|
||||
@@ -183,28 +183,33 @@ namespace cryptonote
|
||||
bool checkpoints::init_default_checkpoints(network_type nettype)
|
||||
{
|
||||
if (nettype == MAINNET) {
|
||||
ADD_CHECKPOINT2(1, "b6b45052e7e182ebaeb14ab713db29ad979115e664d766aa0910e325564a27a6", "0x2");
|
||||
ADD_CHECKPOINT2(10, "82724681cf6bd934eb3253d041de50206a77627ce40ffe418ce6e0fe392ec684", "0x7812a");
|
||||
ADD_CHECKPOINT2(20, "4dac7b512d876df05bfa4f39b8dbacd75cb1483fbced8bfc5446ebe21b25a04f", "0xba98f");
|
||||
ADD_CHECKPOINT2(30, "668246360c93ef791a59157cec9cd09722b32a966051feea399082433138f07b", "0xcc235");
|
||||
ADD_CHECKPOINT2(40, "9a4183bc1d6e9828eac46505c5ef37ae5447ba6c9325dca02be9e1201f939a7d", "0xdf077");
|
||||
ADD_CHECKPOINT2(50, "5cd8b089d2e77aed9b803b398c6bff07ca652100cb8fa114c91b72509aeeb7e9", "0xf37eb");
|
||||
ADD_CHECKPOINT2(60, "0e1acf00dd38e0757996dcdc4b69ad54baf7ebe10ae1e8168b192acb1a0ed7f2", "0x10993b");
|
||||
ADD_CHECKPOINT2(70, "988977507f388221a927e279307b548a0ae0de10ded8c4f22c315e1b483f921a", "0x121537");
|
||||
ADD_CHECKPOINT2(80, "88ea1c49b20e7596e21ca8137b2a9fa98558df269a15816fe7d7495f1c63ea43", "0x13a290");
|
||||
ADD_CHECKPOINT2(90, "254800bb6f9794aad95b2226ffc1a1eef0a817472e1877ae08fac6becb55b147", "0x153a55");
|
||||
ADD_CHECKPOINT2(100, "ba8d75fad878af26ac2504b4868893a7f86c59f013d0f096925cf53271dd04e8", "0x16e91e");
|
||||
ADD_CHECKPOINT2(110, "dca0779bfe403730b923fa0918645daeec6096b953be2c554f133460c6fcce35", "0x18acb9");
|
||||
ADD_CHECKPOINT2(120, "5a57287f6b5c105ae264b88050731c5b9ad1313b916143d7585af1d345e70247", "0x1a88f5");
|
||||
ADD_CHECKPOINT2(130, "4fd292ac0774461e968924f8097e78ec03eee43a2997deaddbc7993e470a61d6", "0x1c6edd");
|
||||
ADD_CHECKPOINT2(140, "5a3b6ceeef5fd498ea3330acb8a0e87f2c1566c9b0100ad67237e5664d1f053b", "0x1e4991");
|
||||
ADD_CHECKPOINT2(150, "78f26d08d39f7d5e1a3548277321471e16c95096fa9bcecbe8a420d406ee249b", "0x202406");
|
||||
ADD_CHECKPOINT2(160, "7acaab1037ccfbadd3126d2612d5dc154020297f980df0b8df462f9c761d3326", "0x22154b");
|
||||
ADD_CHECKPOINT2(170, "9541ae934e40fa6749ca3453e47cf5fdf38efbac9efcaa2714121e8a21dd2d24", "0x241ce7");
|
||||
ADD_CHECKPOINT2(180, "e20bc8ac6aabb6b0792f23a29ce42a577c6a57d177a8ac1a51b68fb6de508045", "0x262b40");
|
||||
ADD_CHECKPOINT2(190, "f69fdad7a15471b63a82668b618ee5b2a384291269d944b11974a723c1604124", "0x2856a3");
|
||||
ADD_CHECKPOINT2(200, "eba53fa7006dfcdc837a56c0bc8f0e1883cf34861c26934d680252a6878a3f5d", "0x2aa022");
|
||||
ADD_CHECKPOINT2(90000, "e125b5c1b26521f98e29df6ec88f041c176a2c0a3fcacd5bd0ad2278e9b02fd2", "0xc99801f937888"); // 3546475285149832
|
||||
ADD_CHECKPOINT2(1, "b6b45052e7e182ebaeb14ab713db29ad979115e664d766aa0910e325564a27a6", "0x2");
|
||||
ADD_CHECKPOINT2(10, "82724681cf6bd934eb3253d041de50206a77627ce40ffe418ce6e0fe392ec684", "0x7812a");
|
||||
ADD_CHECKPOINT2(20, "4dac7b512d876df05bfa4f39b8dbacd75cb1483fbced8bfc5446ebe21b25a04f", "0xba98f");
|
||||
ADD_CHECKPOINT2(30, "668246360c93ef791a59157cec9cd09722b32a966051feea399082433138f07b", "0xcc235");
|
||||
ADD_CHECKPOINT2(40, "9a4183bc1d6e9828eac46505c5ef37ae5447ba6c9325dca02be9e1201f939a7d", "0xdf077");
|
||||
ADD_CHECKPOINT2(50, "5cd8b089d2e77aed9b803b398c6bff07ca652100cb8fa114c91b72509aeeb7e9", "0xf37eb");
|
||||
ADD_CHECKPOINT2(60, "0e1acf00dd38e0757996dcdc4b69ad54baf7ebe10ae1e8168b192acb1a0ed7f2", "0x10993b");
|
||||
ADD_CHECKPOINT2(70, "988977507f388221a927e279307b548a0ae0de10ded8c4f22c315e1b483f921a", "0x121537");
|
||||
ADD_CHECKPOINT2(80, "88ea1c49b20e7596e21ca8137b2a9fa98558df269a15816fe7d7495f1c63ea43", "0x13a290");
|
||||
ADD_CHECKPOINT2(90, "254800bb6f9794aad95b2226ffc1a1eef0a817472e1877ae08fac6becb55b147", "0x153a55");
|
||||
ADD_CHECKPOINT2(100, "ba8d75fad878af26ac2504b4868893a7f86c59f013d0f096925cf53271dd04e8", "0x16e91e");
|
||||
ADD_CHECKPOINT2(110, "dca0779bfe403730b923fa0918645daeec6096b953be2c554f133460c6fcce35", "0x18acb9");
|
||||
ADD_CHECKPOINT2(120, "5a57287f6b5c105ae264b88050731c5b9ad1313b916143d7585af1d345e70247", "0x1a88f5");
|
||||
ADD_CHECKPOINT2(130, "4fd292ac0774461e968924f8097e78ec03eee43a2997deaddbc7993e470a61d6", "0x1c6edd");
|
||||
ADD_CHECKPOINT2(140, "5a3b6ceeef5fd498ea3330acb8a0e87f2c1566c9b0100ad67237e5664d1f053b", "0x1e4991");
|
||||
ADD_CHECKPOINT2(150, "78f26d08d39f7d5e1a3548277321471e16c95096fa9bcecbe8a420d406ee249b", "0x202406");
|
||||
ADD_CHECKPOINT2(160, "7acaab1037ccfbadd3126d2612d5dc154020297f980df0b8df462f9c761d3326", "0x22154b");
|
||||
ADD_CHECKPOINT2(170, "9541ae934e40fa6749ca3453e47cf5fdf38efbac9efcaa2714121e8a21dd2d24", "0x241ce7");
|
||||
ADD_CHECKPOINT2(180, "e20bc8ac6aabb6b0792f23a29ce42a577c6a57d177a8ac1a51b68fb6de508045", "0x262b40");
|
||||
ADD_CHECKPOINT2(190, "f69fdad7a15471b63a82668b618ee5b2a384291269d944b11974a723c1604124", "0x2856a3");
|
||||
ADD_CHECKPOINT2(200, "eba53fa7006dfcdc837a56c0bc8f0e1883cf34861c26934d680252a6878a3f5d", "0x2aa022");
|
||||
ADD_CHECKPOINT2(90000, "e125b5c1b26521f98e29df6ec88f041c176a2c0a3fcacd5bd0ad2278e9b02fd2", "0xc99801f937888"); // 3546475285149832
|
||||
ADD_CHECKPOINT2(100000, "ff4e8ec805d5bfbcd01f350ac071be1d944ba73e0d27e37d12acb549902b3f3d", "0xDA97F5697F7D8"); // 3845539075979224
|
||||
ADD_CHECKPOINT2(150000, "c43281eb5b2a41ee77a4465735e4820c6d14d473b568df7987541afc48f18568", "0x12BDA9ED687AAF"); // 5275087110961839
|
||||
ADD_CHECKPOINT2(225000, "7648405f7cfb24341d9580275b518bb3713c68e970f547faa0b3bcae450d9ec2", "0x18CD5F1B7BA43A"); // 6981207807730746
|
||||
ADD_CHECKPOINT2(300000, "a18ca65464c1f2d876dd3a00643c9be265655d6bca364eccc5e3d628b9a5cd2c", "0x1F0C2A50FE631C"); // 8739100165038876
|
||||
ADD_CHECKPOINT2(375000, "07f0c907cc5cb44cef88e5899a411adddd4b0a4419210906e0583efdcafb499f", "0x240C7F9742F265"); // 10146841299710565
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(__x86_64__)
|
||||
#define DEBUG_BREAK() asm volatile("int $3")
|
||||
#elif defined(__aarch64__)
|
||||
#define DEBUG_BREAK() asm volatile("brk #0xf000")
|
||||
#elif defined(__arm__)
|
||||
#define DEBUG_BREAK() asm volatile(".inst 0xe7f001f0") // Encoding for bkpt #0xf01, common for ARM32
|
||||
#else
|
||||
#error "Unsupported architecture for DEBUG_BREAK"
|
||||
#endif
|
||||
@@ -71,6 +71,7 @@ monero_add_library(cncrypto
|
||||
target_link_libraries(cncrypto
|
||||
PUBLIC
|
||||
epee
|
||||
mx25519_static
|
||||
randomx
|
||||
${Boost_SYSTEM_LIBRARY}
|
||||
${SODIUM_LIBRARY}
|
||||
|
||||
@@ -887,4 +887,7 @@ const ge_p3 ge_p3_H = {
|
||||
{5858699, 5096796, 21321203, -7536921, -5553480, -11439507, -5627669, 15045946, 19977121, 5275251},
|
||||
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{23443568, -5110398, -8776029, -4345135, 6889568, -14710814, 7474843, 3279062, 14550766, -7453428}
|
||||
};
|
||||
};
|
||||
|
||||
// Precomputed sqrt(-486664) mod p as 32-byte little-endian array
|
||||
const fe fe_sqrt_m486664 = {12222970, 8312128, 11511410, -9067497, 15300785, 241793, -25456130, -14121551, 12187136, -3972024};
|
||||
|
||||
+125
-2
@@ -104,6 +104,21 @@ void fe_1(fe h) {
|
||||
h[9] = 0;
|
||||
}
|
||||
|
||||
int fe_equal(const fe a, const fe b)
|
||||
{
|
||||
fe t;
|
||||
fe_sub(t, a, b);
|
||||
return fe_isnonzero(t) == 0;
|
||||
}
|
||||
|
||||
void ge_from_xy(ge_p3 *out, const fe x, const fe y)
|
||||
{
|
||||
fe_1(out->Z); // Z = 1
|
||||
fe_copy(out->X, x); // X = x
|
||||
fe_copy(out->Y, y); // Y = y
|
||||
fe_mul(out->T, x, y); // T = x*y
|
||||
}
|
||||
|
||||
/* From fe_add.c */
|
||||
|
||||
/*
|
||||
@@ -365,7 +380,7 @@ int fe_isnegative(const fe f) {
|
||||
|
||||
/* From fe_isnonzero.c, modified */
|
||||
|
||||
static int fe_isnonzero(const fe f) {
|
||||
int fe_isnonzero(const fe f) {
|
||||
unsigned char s[32];
|
||||
fe_tobytes(s, f);
|
||||
return (((int) (s[0] | s[1] | s[2] | s[3] | s[4] | s[5] | s[6] | s[7] | s[8] |
|
||||
@@ -3967,6 +3982,114 @@ int edwards_bytes_to_x25519_vartime(unsigned char *xbytes, const unsigned char *
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Precomputed sqrt(-1) from Monero (fe_sqrtm1 in crypto-ops-data.c)
|
||||
extern const fe fe_sqrtm1;
|
||||
extern const fe fe_sqrt_m486664;
|
||||
|
||||
// Function to recover v from u (returns 0 on success, -1 if not on curve)
|
||||
int fe_sqrt_mont(fe v_out, const fe u_in) {
|
||||
fe rhs;
|
||||
fe t0, t1, t2;
|
||||
fe candidate, c2, check;
|
||||
|
||||
// Compute rhs = u^3 + A u^2 + u, A=486662
|
||||
fe A_fe;
|
||||
fe_frombytes_vartime(A_fe, (const unsigned char[]){0x06, 0x6D, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); // Correct little-endian 486662
|
||||
|
||||
fe_sq(t0, u_in); // t0 = u^2
|
||||
fe_mul(t1, t0, u_in); // t1 = u^3
|
||||
fe_mul(t2, t0, A_fe); // t2 = A u^2
|
||||
fe_add(rhs, t1, t2); // u^3 + A u^2
|
||||
fe_add(rhs, rhs, u_in); // + u
|
||||
|
||||
// The exponentiation chain for (p+3)/8
|
||||
fe_1(t1);
|
||||
fe_sq(t0, t1);
|
||||
fe_mul(t0, t0, t1);
|
||||
fe_sq(candidate, t0);
|
||||
fe_mul(candidate, candidate, t1);
|
||||
fe_mul(candidate, candidate, rhs);
|
||||
fe_pow22523(candidate, candidate);
|
||||
fe_mul(candidate, candidate, t0);
|
||||
fe_mul(candidate, candidate, rhs);
|
||||
|
||||
// Check c^2 == rhs or -rhs
|
||||
fe_sq(c2, candidate);
|
||||
fe_sub(check, c2, rhs);
|
||||
if (fe_isnonzero(check)) {
|
||||
fe_add(check, c2, rhs);
|
||||
if (fe_isnonzero(check)) {
|
||||
return -1; // Not a quadratic residue
|
||||
}
|
||||
fe_mul(candidate, candidate, fe_sqrtm1); // Adjust with sqrt(-1)
|
||||
}
|
||||
|
||||
// Output v (principal root; flip to -v if needed for your map)
|
||||
fe_copy(v_out, candidate);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mont_to_ed(fe x_out, fe y_out, const fe u, const fe v) {
|
||||
fe inv_v, temp;
|
||||
fe t1, t2, inv_t2;
|
||||
fe one;
|
||||
fe_1(one);
|
||||
|
||||
fe_invert(inv_v, v); // 1/v
|
||||
fe_mul(temp, u, inv_v); // u/v
|
||||
fe_mul(x_out, fe_sqrt_m486664, temp); // sqrt(-486664) * (u/v)
|
||||
|
||||
fe_sub(t1, u, one); // u - 1
|
||||
fe_add(t2, u, one); // u + 1
|
||||
fe_invert(inv_t2, t2);
|
||||
fe_mul(y_out, t1, inv_t2); // (u-1)/(u+1)
|
||||
}
|
||||
|
||||
void ed_to_mont(fe u_out, fe v_out, const fe x, const fe y) {
|
||||
fe t1, t2, inv_t2;
|
||||
fe one;
|
||||
fe_1(one);
|
||||
|
||||
fe_add(t1, one, y); // 1 + y
|
||||
fe_sub(t2, one, y); // 1 - y
|
||||
fe_invert(inv_t2, t2);
|
||||
fe_mul(u_out, t1, inv_t2); // (1+y)/(1-y)
|
||||
|
||||
fe inv_x;
|
||||
fe_invert(inv_x, x); // 1/x
|
||||
fe_mul(t1, u_out, inv_x); // u / x
|
||||
fe_mul(v_out, fe_sqrt_m486664, t1); // sqrt(-486664) * (u/x)
|
||||
}
|
||||
|
||||
// Usage: Add two Montgomery points
|
||||
void add_mont_points(fe u3, fe v3, const fe u1, const fe v1, const fe u2, const fe v2) {
|
||||
ge_p3 P_ed, Q_ed, sum_ed;
|
||||
|
||||
// Convert to Edwards
|
||||
fe x1, y1, x2, y2;
|
||||
mont_to_ed(x1, y1, u1, v1);
|
||||
mont_to_ed(x2, y2, u2, v2);
|
||||
|
||||
// Load into ge_p3 (assume Z=1, T=x*y for affine)
|
||||
fe_1(P_ed.Z); fe_mul(P_ed.T, x1, y1); fe_copy(P_ed.X, x1); fe_copy(P_ed.Y, y1);
|
||||
fe_1(Q_ed.Z); fe_mul(Q_ed.T, x2, y2); fe_copy(Q_ed.X, x2); fe_copy(Q_ed.Y, y2);
|
||||
|
||||
// Add using ge_
|
||||
ge_cached Q_cached;
|
||||
ge_p3_to_cached(&Q_cached, &Q_ed);
|
||||
ge_p1p1 sum_p1p1;
|
||||
ge_add(&sum_p1p1, &P_ed, &Q_cached);
|
||||
ge_p1p1_to_p3(&sum_ed, &sum_p1p1);
|
||||
|
||||
// Convert back (normalize to affine: divide by Z)
|
||||
fe inv_z;
|
||||
fe_invert(inv_z, sum_ed.Z);
|
||||
fe x_out, y_out;
|
||||
fe_mul(x_out, sum_ed.X, inv_z);
|
||||
fe_mul(y_out, sum_ed.Y, inv_z);
|
||||
ed_to_mont(u3, v3, x_out, y_out);
|
||||
}
|
||||
|
||||
int ge_p3_is_point_at_infinity_vartime(const ge_p3 *p) {
|
||||
// https://eprint.iacr.org/2008/522
|
||||
// X == T == 0 and Y/Z == 1
|
||||
@@ -4063,4 +4186,4 @@ void fe_dbl(fe h, const fe f)
|
||||
// Reduce the output for safety to ensure the result can be used as input to
|
||||
// fe_add or fe_sub without an extra call to fe_reduce
|
||||
fe_reduce(h, h_res);
|
||||
}
|
||||
}
|
||||
|
||||
+10
-1
@@ -176,6 +176,11 @@ int sc_isnonzero(const unsigned char *); /* Doesn't normalize */
|
||||
void ge_p3_to_x25519(unsigned char *xbytes, const ge_p3 *h);
|
||||
int edwards_bytes_to_x25519_vartime(unsigned char *xbytes, const unsigned char *s);
|
||||
|
||||
int fe_sqrt_mont(fe v_out, const fe u_in);
|
||||
void mont_to_ed(fe x_out, fe y_out, const fe u, const fe v);
|
||||
void ed_to_mont(fe u_out, fe v_out, const fe x, const fe y);
|
||||
void add_mont_points(fe u3, fe v3, const fe u1, const fe v1, const fe u2, const fe v2);
|
||||
|
||||
// internal
|
||||
uint64_t load_3(const unsigned char *in);
|
||||
uint64_t load_4(const unsigned char *in);
|
||||
@@ -192,10 +197,14 @@ void fe_sq(fe h, const fe f);
|
||||
void fe_sub(fe h, const fe f, const fe g);
|
||||
void fe_0(fe h);
|
||||
void fe_1(fe h);
|
||||
int fe_equal(const fe a, const fe b);
|
||||
void ge_from_xy(ge_p3 *out, const fe x, const fe y);
|
||||
|
||||
int fe_isnonzero(const fe f);
|
||||
|
||||
int ge_p3_is_point_at_infinity_vartime(const ge_p3 *p);
|
||||
|
||||
void fe_ed_y_derivatives_to_wei_x(unsigned char *wei_x, const fe inv_one_minus_y, const fe one_plus_y);
|
||||
|
||||
void fe_reduce(fe reduced_f, const fe f);
|
||||
void fe_dbl(fe h, const fe f);
|
||||
void fe_dbl(fe h, const fe f);
|
||||
|
||||
+558
-1
@@ -41,6 +41,7 @@
|
||||
#include "common/varint.h"
|
||||
#include "warnings.h"
|
||||
#include "crypto.h"
|
||||
#include "mx25519.h"
|
||||
#include "hash.h"
|
||||
|
||||
#include "cryptonote_config.h"
|
||||
@@ -90,6 +91,16 @@ namespace crypto {
|
||||
return &reinterpret_cast<const unsigned char &>(scalar);
|
||||
}
|
||||
|
||||
static const mx25519_impl* get_mx25519_impl()
|
||||
{
|
||||
static std::once_flag of;
|
||||
static const mx25519_impl *impl;
|
||||
std::call_once(of, [&](){ impl = mx25519_select_impl(MX25519_TYPE_AUTO); });
|
||||
if (impl == nullptr)
|
||||
throw std::runtime_error("failed to obtain a mx25519 implementation");
|
||||
return impl;
|
||||
}
|
||||
|
||||
boost::mutex &get_random_lock()
|
||||
{
|
||||
static boost::mutex random_lock;
|
||||
@@ -504,6 +515,391 @@ namespace crypto {
|
||||
memwipe(&k, sizeof(k));
|
||||
}
|
||||
|
||||
void crypto_ops::generate_carrot_tx_proof(
|
||||
const hash &prefix_hash,
|
||||
const public_key &R, // X25519 u-coordinate
|
||||
const public_key &A, // Ed25519
|
||||
const boost::optional<public_key> &B, // Ed if present
|
||||
const public_key &D, // X25519 u-coordinate
|
||||
const secret_key &r,
|
||||
const secret_key &a,
|
||||
signature &sig)
|
||||
{
|
||||
// Check if we are sender or receiver
|
||||
if (r != crypto::null_skey) {
|
||||
// SENDER
|
||||
generate_carrot_tx_proof_as_sender(prefix_hash, R, A, B, D, r, a, sig);
|
||||
return;
|
||||
}
|
||||
|
||||
// RECEIVER
|
||||
|
||||
// Load points (A and B and R) into ge_p3
|
||||
ge_p3 A_p3;
|
||||
ge_p3 B_p3;
|
||||
ge_p3 R_p3;
|
||||
|
||||
if (ge_frombytes_vartime(&A_p3, &A) != 0)
|
||||
throw std::runtime_error("recipient view pubkey is invalid");
|
||||
|
||||
if (B && ge_frombytes_vartime(&B_p3, &*B) != 0)
|
||||
throw std::runtime_error("recipient spend pubkey is invalid");
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
{
|
||||
// Debug check D == a*R
|
||||
mx25519_pubkey D_x25519;
|
||||
mx25519_scmul_key(get_mx25519_impl(),
|
||||
&D_x25519,
|
||||
reinterpret_cast<const mx25519_privkey*>(&a),
|
||||
reinterpret_cast<const mx25519_pubkey*>(&R));
|
||||
public_key dbg_D;
|
||||
memcpy(&dbg_D, &D_x25519, sizeof(mx25519_pubkey));
|
||||
assert(D == dbg_D);
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// 1. Pick random nonce k
|
||||
//
|
||||
crypto::secret_key k;
|
||||
random_scalar(k);
|
||||
|
||||
static const public_key zero = {{
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
|
||||
}};
|
||||
|
||||
s_comm_2 buf;
|
||||
buf.msg = prefix_hash;
|
||||
buf.D = D; // X25519 u-coord
|
||||
buf.R = R; // X25519 u-coord
|
||||
buf.A = A; // Ed25519
|
||||
buf.B = B ? *B : zero;
|
||||
|
||||
cn_fast_hash(config::HASH_KEY_TXPROOF_V2,
|
||||
sizeof(config::HASH_KEY_TXPROOF_V2)-1,
|
||||
buf.sep);
|
||||
|
||||
//
|
||||
// 2. Compute X = ConvertPointE(k*G or k*B)
|
||||
//
|
||||
ge_p3 kB_or_kG_p3;
|
||||
if (B)
|
||||
ge_scalarmult_p3(&kB_or_kG_p3, &k, &B_p3);
|
||||
else
|
||||
ge_scalarmult_base(&kB_or_kG_p3, &k);
|
||||
mx25519_pubkey X_x25519;
|
||||
ge_p3_to_x25519(X_x25519.data, &kB_or_kG_p3);
|
||||
memcpy(&buf.X, &X_x25519, sizeof(mx25519_pubkey));
|
||||
|
||||
//
|
||||
// 3. Compute Y = k*R
|
||||
//
|
||||
mx25519_pubkey Y;
|
||||
mx25519_scmul_key(get_mx25519_impl(),
|
||||
&Y,
|
||||
reinterpret_cast<const mx25519_privkey*>(&k),
|
||||
reinterpret_cast<const mx25519_pubkey*>(&R));
|
||||
memcpy(&buf.Y, &Y, sizeof(mx25519_pubkey));
|
||||
|
||||
// ---------- Extract and lift R ----------
|
||||
fe u_R;
|
||||
fe_frombytes_vartime(u_R, (const unsigned char *)&R);
|
||||
|
||||
fe v_R_cand;
|
||||
if (fe_sqrt_mont(v_R_cand, u_R) != 0)
|
||||
throw std::runtime_error("R not on curve");
|
||||
|
||||
fe x1, y1, x2, y2, v_R_neg;
|
||||
ge_p3 R_ed1, R_ed2;
|
||||
|
||||
// +v (principal)
|
||||
mont_to_ed(x1, y1, u_R, v_R_cand);
|
||||
ge_from_xy(&R_ed1, x1, y1);
|
||||
|
||||
// -v
|
||||
fe_neg(v_R_neg, v_R_cand);
|
||||
mont_to_ed(x2, y2, u_R, v_R_neg);
|
||||
ge_from_xy(&R_ed2, x2, y2);
|
||||
|
||||
// Arbitrarily choose R_sign = true (principal v from fe_sqrt_mont)
|
||||
bool R_sign = true;
|
||||
ge_p3 R_ed_correct = R_ed1; // +v
|
||||
|
||||
// ---------- Extract and lift D (consistent with chosen R_sign) ----------
|
||||
fe u_D;
|
||||
fe_frombytes_vartime(u_D, (const unsigned char *)&D);
|
||||
|
||||
fe v_D_cand;
|
||||
if (fe_sqrt_mont(v_D_cand, u_D) != 0)
|
||||
throw std::runtime_error("D not on curve");
|
||||
|
||||
fe x3, y3, x4, y4, v_D_neg;
|
||||
|
||||
// Compute D_ed_true = a * R_ed_correct
|
||||
ge_p3 D_ed_true;
|
||||
ge_scalarmult_p3(&D_ed_true, &a, &R_ed_correct);
|
||||
|
||||
// Normalize to affine for matching
|
||||
fe inv_z;
|
||||
fe_invert(inv_z, D_ed_true.Z);
|
||||
fe xd_true, yd_true;
|
||||
fe_mul(xd_true, D_ed_true.X, inv_z);
|
||||
fe_mul(yd_true, D_ed_true.Y, inv_z);
|
||||
|
||||
// +v for D
|
||||
mont_to_ed(x3, y3, u_D, v_D_cand);
|
||||
bool D_match1 = fe_equal(x3, xd_true) && fe_equal(y3, yd_true); // Affine match (mont_to_ed gives affine x,y)
|
||||
|
||||
// -v for D
|
||||
fe_neg(v_D_neg, v_D_cand);
|
||||
mont_to_ed(x4, y4, u_D, v_D_neg);
|
||||
bool D_match2 = fe_equal(x4, xd_true) && fe_equal(y4, yd_true);
|
||||
|
||||
bool D_sign = false;
|
||||
if (D_match1)
|
||||
D_sign = true;
|
||||
else if (D_match2)
|
||||
D_sign = false;
|
||||
else
|
||||
throw std::runtime_error("D lift mismatch with computed D_ed_true");
|
||||
|
||||
// Pack signs (MSB is set to [1] for outbound, [0] for inbound
|
||||
sig.sign_mask =
|
||||
(R_sign ? 0x01 : 0x00) |
|
||||
(D_sign ? 0x02 : 0x00);
|
||||
|
||||
struct {
|
||||
s_comm_2 buf;
|
||||
uint8_t sign_mask;
|
||||
} challenge_hash;
|
||||
|
||||
challenge_hash.buf = buf;
|
||||
challenge_hash.sign_mask = sig.sign_mask;
|
||||
|
||||
//
|
||||
// 7. Compute challenge c = H(prefix_hash || … || sign_mask)
|
||||
//
|
||||
hash_to_scalar(&challenge_hash, sizeof(challenge_hash), sig.c);
|
||||
|
||||
//
|
||||
// 8. Compute response z = k - c*a
|
||||
//
|
||||
sc_mulsub(&sig.r, &sig.c, &unwrap(a), &k);
|
||||
|
||||
memwipe(&k, sizeof(k));
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
bool ok = check_carrot_tx_proof(prefix_hash, R, A, B, D, sig);
|
||||
assert(ok);
|
||||
#endif
|
||||
}
|
||||
|
||||
void crypto_ops::generate_carrot_tx_proof_as_sender(
|
||||
const hash &prefix_hash,
|
||||
const public_key &R, // X25519 u-coordinate
|
||||
const public_key &A, // Ed25519
|
||||
const boost::optional<public_key> &B, // Ed if present
|
||||
const public_key &D, // X25519 u-coordinate
|
||||
const secret_key &r,
|
||||
const secret_key &a,
|
||||
signature &sig)
|
||||
{
|
||||
// Load only Ed points (A and B) into ge_p3
|
||||
ge_p3 A_p3;
|
||||
ge_p3 B_p3;
|
||||
|
||||
if (ge_frombytes_vartime(&A_p3, &A) != 0)
|
||||
throw std::runtime_error("recipient view pubkey is invalid");
|
||||
|
||||
if (B && ge_frombytes_vartime(&B_p3, &*B) != 0)
|
||||
throw std::runtime_error("recipient spend pubkey is invalid");
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
{
|
||||
assert(sc_check(&r) == 0);
|
||||
|
||||
// Debug check R == ConvertPointE(r*G or r*B)
|
||||
public_key dbg_R;
|
||||
ge_p3 dbg_R_p3;
|
||||
|
||||
if (B)
|
||||
ge_scalarmult_p3(&dbg_R_p3, &r, &B_p3);
|
||||
else
|
||||
ge_scalarmult_base(&dbg_R_p3, &r);
|
||||
|
||||
mx25519_pubkey R_x25519;
|
||||
ge_p3_to_x25519(R_x25519.data, &dbg_R_p3);
|
||||
|
||||
memcpy(&dbg_R, &R_x25519, sizeof(mx25519_pubkey));
|
||||
assert(R == dbg_R);
|
||||
|
||||
// Debug check D == ConvertPointE(r*A)
|
||||
public_key dbg_D;
|
||||
ge_p3 dbg_D_p3;
|
||||
ge_scalarmult_p3(&dbg_D_p3, &r, &A_p3);
|
||||
|
||||
mx25519_pubkey D_x25519;
|
||||
ge_p3_to_x25519(D_x25519.data, &dbg_D_p3);
|
||||
|
||||
memcpy(&dbg_D, &D_x25519, sizeof(mx25519_pubkey));
|
||||
assert(D == dbg_D);
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// 1. Pick random nonce k
|
||||
//
|
||||
ec_scalar k;
|
||||
random_scalar(k);
|
||||
|
||||
static const ec_point zero = {{
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
|
||||
}};
|
||||
|
||||
s_comm_2 buf;
|
||||
buf.msg = prefix_hash;
|
||||
buf.D = D; // X25519 u-coord
|
||||
buf.R = R; // X25519 u-coord
|
||||
buf.A = A; // Ed25519
|
||||
buf.B = B ? *B : zero;
|
||||
|
||||
cn_fast_hash(config::HASH_KEY_TXPROOF_V2,
|
||||
sizeof(config::HASH_KEY_TXPROOF_V2)-1,
|
||||
buf.sep);
|
||||
|
||||
//
|
||||
// 2. Compute X = ConvertPointE(k*G or k*B)
|
||||
//
|
||||
ge_p3 kB_or_kG_p3;
|
||||
if (B)
|
||||
ge_scalarmult_p3(&kB_or_kG_p3, &k, &B_p3);
|
||||
else
|
||||
ge_scalarmult_base(&kB_or_kG_p3, &k);
|
||||
|
||||
mx25519_pubkey X_x25519;
|
||||
ge_p3_to_x25519(X_x25519.data, &kB_or_kG_p3);
|
||||
memcpy(&buf.X, &X_x25519, sizeof(mx25519_pubkey));
|
||||
|
||||
//
|
||||
// 3. Compute Y = ConvertPointE(k*A)
|
||||
//
|
||||
ge_p3 kA_p3;
|
||||
ge_scalarmult_p3(&kA_p3, &k, &A_p3);
|
||||
|
||||
mx25519_pubkey Y_x25519;
|
||||
ge_p3_to_x25519(Y_x25519.data, &kA_p3);
|
||||
memcpy(&buf.Y, &Y_x25519, sizeof(mx25519_pubkey));
|
||||
|
||||
//
|
||||
// 4. Compute true Ed points R_ed_true and D_ed_true
|
||||
//
|
||||
ge_p3 R_ed_true, D_ed_true;
|
||||
|
||||
if (B)
|
||||
ge_scalarmult_p3(&R_ed_true, &r, &B_p3);
|
||||
else
|
||||
ge_scalarmult_base(&R_ed_true, &r);
|
||||
|
||||
ge_scalarmult_p3(&D_ed_true, &r, &A_p3);
|
||||
|
||||
//
|
||||
// 5. Determine sign bits for R and D
|
||||
//
|
||||
|
||||
// ---------- Extract and lift R ----------
|
||||
fe u_R;
|
||||
fe_frombytes_vartime(u_R, (const unsigned char *)&R);
|
||||
|
||||
fe v_R_cand;
|
||||
if (fe_sqrt_mont(v_R_cand, u_R) != 0)
|
||||
throw std::runtime_error("R not on curve");
|
||||
|
||||
fe x1, y1, x2, y2, v_R_neg, v_D_neg;
|
||||
mont_to_ed(x1, y1, u_R, v_R_cand);
|
||||
|
||||
fe_neg(v_R_neg, v_R_cand);
|
||||
mont_to_ed(x2, y2, u_R, v_R_neg);
|
||||
|
||||
// Compute affine Edwards coords of R_ed_true
|
||||
fe inv_z, xr_true, yr_true;
|
||||
fe_invert(inv_z, R_ed_true.Z);
|
||||
fe_mul(xr_true, R_ed_true.X, inv_z);
|
||||
fe_mul(yr_true, R_ed_true.Y, inv_z);
|
||||
|
||||
bool R_match1 = fe_equal(xr_true, x1) && fe_equal(yr_true, y1);
|
||||
bool R_match2 = fe_equal(xr_true, x2) && fe_equal(yr_true, y2);
|
||||
|
||||
if (!R_match1 && !R_match2)
|
||||
throw std::runtime_error("R mapping mismatch");
|
||||
|
||||
bool R_sign = R_match1;
|
||||
|
||||
// ---------- Extract and lift D ----------
|
||||
fe u_D;
|
||||
fe_frombytes_vartime(u_D, (const unsigned char *)&D);
|
||||
|
||||
fe v_D_cand;
|
||||
if (fe_sqrt_mont(v_D_cand, u_D) != 0)
|
||||
throw std::runtime_error("D not on curve");
|
||||
|
||||
mont_to_ed(x1, y1, u_D, v_D_cand);
|
||||
|
||||
fe_neg(v_D_neg, v_D_cand);
|
||||
mont_to_ed(x2, y2, u_D, v_D_neg);
|
||||
|
||||
fe_invert(inv_z, D_ed_true.Z);
|
||||
fe_mul(xr_true, D_ed_true.X, inv_z);
|
||||
fe_mul(yr_true, D_ed_true.Y, inv_z);
|
||||
|
||||
bool D_match1 = fe_equal(xr_true, x1) && fe_equal(yr_true, y1);
|
||||
bool D_match2 = fe_equal(xr_true, x2) && fe_equal(yr_true, y2);
|
||||
|
||||
if (!D_match1 && !D_match2)
|
||||
throw std::runtime_error("D mapping mismatch");
|
||||
|
||||
bool D_sign = D_match1;
|
||||
|
||||
//
|
||||
// 6. Pack sign bits into signature, include in challenge hash
|
||||
//
|
||||
sig.sign_mask =
|
||||
(R_sign ? 0x01 : 0x00) |
|
||||
(D_sign ? 0x02 : 0x00) |
|
||||
0x80;
|
||||
|
||||
struct {
|
||||
s_comm_2 buf;
|
||||
uint8_t sign_mask;
|
||||
} challenge_hash;
|
||||
|
||||
challenge_hash.buf = buf;
|
||||
challenge_hash.sign_mask = sig.sign_mask;
|
||||
|
||||
//
|
||||
// 7. Compute challenge c = H(prefix_hash || … || sign_mask)
|
||||
//
|
||||
hash_to_scalar(&challenge_hash, sizeof(challenge_hash), sig.c);
|
||||
|
||||
//
|
||||
// 8. Compute response z = k - c*r
|
||||
//
|
||||
sc_mulsub(&sig.r, &sig.c, &unwrap(r), &k);
|
||||
|
||||
memwipe(&k, sizeof(k));
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
bool ok = check_carrot_tx_proof(prefix_hash, R, A, B, D, sig);
|
||||
assert(ok);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Verify a proof: either v1 (version == 1) or v2 (version == 2)
|
||||
bool crypto_ops::check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig, const int version) {
|
||||
// sanity check
|
||||
@@ -608,6 +1004,167 @@ namespace crypto {
|
||||
return sc_isnonzero(&c2) == 0;
|
||||
}
|
||||
|
||||
// R and D are provided in X25519 format (u-coordinate), A and B in Ed25519.
|
||||
bool crypto_ops::check_carrot_tx_proof(
|
||||
const hash &prefix_hash,
|
||||
const public_key &R, // X25519 u
|
||||
const public_key &A, // Ed25519 viewkey
|
||||
const boost::optional<public_key> &B, // Ed25519 spendkey if any
|
||||
const public_key &D, // X25519 u
|
||||
const signature &sig)
|
||||
{
|
||||
ge_p3 A_p3, B_p3;
|
||||
if (ge_frombytes_vartime(&A_p3, &A) != 0)
|
||||
return false;
|
||||
if (B && ge_frombytes_vartime(&B_p3, &*B) != 0)
|
||||
return false;
|
||||
|
||||
if (sc_check(&sig.c) != 0 || sc_check(&sig.r) != 0)
|
||||
return false;
|
||||
|
||||
// Extract sign bits and direction flag
|
||||
const bool R_sign = (sig.sign_mask & 0x01) != 0;
|
||||
const bool D_sign = (sig.sign_mask & 0x02) != 0;
|
||||
const bool outbound = (sig.sign_mask & 0x80) != 0;
|
||||
|
||||
//
|
||||
// 1. Reconstruct R_ed and D_ed from X25519 u-coords + sign bits
|
||||
//
|
||||
|
||||
// ----- R -----
|
||||
fe u_R, v_R_candidate, v_R;
|
||||
fe_frombytes_vartime(u_R, (const unsigned char *)&R);
|
||||
if (fe_sqrt_mont(v_R_candidate, u_R) != 0)
|
||||
return false;
|
||||
if (R_sign) fe_copy(v_R, v_R_candidate);
|
||||
else fe_neg(v_R, v_R_candidate);
|
||||
|
||||
fe x_R, y_R;
|
||||
mont_to_ed(x_R, y_R, u_R, v_R);
|
||||
ge_p3 R_ed;
|
||||
ge_from_xy(&R_ed, x_R, y_R); // Z=1, T=X*Y
|
||||
|
||||
// ----- D -----
|
||||
fe u_D, v_D_candidate, v_D;
|
||||
fe_frombytes_vartime(u_D, (const unsigned char *)&D);
|
||||
if (fe_sqrt_mont(v_D_candidate, u_D) != 0)
|
||||
return false;
|
||||
if (D_sign) fe_copy(v_D, v_D_candidate);
|
||||
else fe_neg(v_D, v_D_candidate);
|
||||
|
||||
fe x_D, y_D;
|
||||
mont_to_ed(x_D, y_D, u_D, v_D);
|
||||
ge_p3 D_ed;
|
||||
ge_from_xy(&D_ed, x_D, y_D);
|
||||
|
||||
//
|
||||
// 2. Compute X'
|
||||
// If inbound proof, X`= z*G + c*A (or z*B + c*A)
|
||||
// If outbound proof, X`= z*G + c*R_ed (or z*B + c*R_ed)
|
||||
//
|
||||
|
||||
ge_p3 c_p3;
|
||||
if (outbound)
|
||||
ge_scalarmult_p3(&c_p3, &sig.c, &R_ed);
|
||||
else
|
||||
ge_scalarmult_p3(&c_p3, &sig.c, &A_p3);
|
||||
|
||||
ge_p1p1 X_p1p1;
|
||||
if (B)
|
||||
{
|
||||
// Subaddress: X' = c*A + z*B
|
||||
ge_p3 rB_p3;
|
||||
ge_scalarmult_p3(&rB_p3, &sig.r, &B_p3);
|
||||
ge_cached rB_cached;
|
||||
ge_p3_to_cached(&rB_cached, &rB_p3);
|
||||
ge_add(&X_p1p1, &c_p3, &rB_cached);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Main address: X' = c*R_ed + z*G
|
||||
ge_p3 rG_p3;
|
||||
ge_scalarmult_base(&rG_p3, &sig.r);
|
||||
ge_cached rG_cached;
|
||||
ge_p3_to_cached(&rG_cached, &rG_p3);
|
||||
ge_add(&X_p1p1, &c_p3, &rG_cached);
|
||||
}
|
||||
|
||||
ge_p3 X_ed_p3;
|
||||
ge_p1p1_to_p3(&X_ed_p3, &X_p1p1);
|
||||
|
||||
mx25519_pubkey X_x25519;
|
||||
ge_p3_to_x25519(X_x25519.data, &X_ed_p3);
|
||||
|
||||
//
|
||||
// 3. Compute Y'
|
||||
// If inbound, Y' = c*D_ed + z*R
|
||||
// If outbound, Y' = c*D_ed + z*A
|
||||
//
|
||||
|
||||
ge_p3 cD_p3;
|
||||
ge_scalarmult_p3(&cD_p3, &sig.c, &D_ed);
|
||||
|
||||
ge_p3 z_p3;
|
||||
if (outbound)
|
||||
ge_scalarmult_p3(&z_p3, &sig.r, &A_p3);
|
||||
else
|
||||
ge_scalarmult_p3(&z_p3, &sig.r, &R_ed);
|
||||
|
||||
ge_cached z_cached;
|
||||
ge_p3_to_cached(&z_cached, &z_p3);
|
||||
|
||||
ge_p1p1 Y_p1p1;
|
||||
ge_add(&Y_p1p1, &cD_p3, &z_cached);
|
||||
|
||||
ge_p3 Y_ed_p3;
|
||||
ge_p1p1_to_p3(&Y_ed_p3, &Y_p1p1);
|
||||
|
||||
mx25519_pubkey Y_x25519;
|
||||
ge_p3_to_x25519(Y_x25519.data, &Y_ed_p3);
|
||||
|
||||
//
|
||||
// 4. Rebuild the hash transcript exactly as the prover did
|
||||
//
|
||||
|
||||
static const ec_point zero = {{
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
|
||||
}};
|
||||
|
||||
s_comm_2 buf;
|
||||
buf.msg = prefix_hash;
|
||||
buf.D = D; // X25519 (same bytes as prover)
|
||||
buf.R = R; // X25519
|
||||
buf.A = A; // Ed25519
|
||||
buf.B = B ? *B : zero;
|
||||
|
||||
cn_fast_hash(config::HASH_KEY_TXPROOF_V2,
|
||||
sizeof(config::HASH_KEY_TXPROOF_V2)-1,
|
||||
buf.sep);
|
||||
|
||||
memcpy(&buf.X, &X_x25519, sizeof(mx25519_pubkey));
|
||||
memcpy(&buf.Y, &Y_x25519, sizeof(mx25519_pubkey));
|
||||
|
||||
struct {
|
||||
s_comm_2 buf;
|
||||
uint8_t sign_mask;
|
||||
} challenge_hash;
|
||||
|
||||
challenge_hash.buf = buf;
|
||||
challenge_hash.sign_mask = sig.sign_mask;
|
||||
|
||||
//
|
||||
// 5. Recompute challenge and compare with sig.c
|
||||
//
|
||||
|
||||
ec_scalar c2;
|
||||
hash_to_scalar(&challenge_hash, sizeof(challenge_hash), c2);
|
||||
sc_sub(&c2, &c2, &sig.c);
|
||||
return sc_isnonzero(&c2) == 0;
|
||||
}
|
||||
|
||||
static void hash_to_ec(const public_key &key, ge_p3 &res) {
|
||||
hash h;
|
||||
ge_p2 point;
|
||||
@@ -796,4 +1353,4 @@ POP_WARNINGS
|
||||
ki.data[31] ^= 0x80;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+28
-3
@@ -85,6 +85,7 @@ namespace crypto {
|
||||
|
||||
POD_CLASS signature {
|
||||
ec_scalar c, r;
|
||||
uint8_t sign_mask;
|
||||
friend class crypto_ops;
|
||||
};
|
||||
|
||||
@@ -99,7 +100,7 @@ namespace crypto {
|
||||
static_assert(sizeof(ec_point) == 32 && sizeof(ec_scalar) == 32 &&
|
||||
sizeof(public_key) == 32 && sizeof(public_key_memsafe) == 32 && sizeof(secret_key) == 32 &&
|
||||
sizeof(key_derivation) == 32 && sizeof(key_image) == 32 && sizeof(key_image_y) == 32 &&
|
||||
sizeof(signature) == 64 && sizeof(view_tag) == 1, "Invalid structure size");
|
||||
sizeof(signature) == 65 && sizeof(view_tag) == 1, "Invalid structure size");
|
||||
|
||||
class crypto_ops {
|
||||
crypto_ops();
|
||||
@@ -131,8 +132,14 @@ namespace crypto {
|
||||
friend void generate_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, signature &);
|
||||
static void generate_tx_proof_v1(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, signature &);
|
||||
friend void generate_tx_proof_v1(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, signature &);
|
||||
static void generate_carrot_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, const secret_key &, signature &);
|
||||
friend void generate_carrot_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, const secret_key &, signature &);
|
||||
static void generate_carrot_tx_proof_as_sender(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, const secret_key &, signature &);
|
||||
friend void generate_carrot_tx_proof_as_sender(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, const secret_key &, signature &);
|
||||
static bool check_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &, const int);
|
||||
friend bool check_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &, const int);
|
||||
static bool check_carrot_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &);
|
||||
friend bool check_carrot_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &);
|
||||
static void derive_key_image_generator(const public_key &, ec_point &);
|
||||
friend void derive_key_image_generator(const public_key &, ec_point &);
|
||||
static void generate_key_image(const public_key &, const secret_key &, key_image &);
|
||||
@@ -260,10 +267,28 @@ namespace crypto {
|
||||
inline void generate_tx_proof_v1(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const secret_key &r, signature &sig) {
|
||||
crypto_ops::generate_tx_proof_v1(prefix_hash, R, A, B, D, r, sig);
|
||||
}
|
||||
/* Generation of a carrot tx proof; for carrot transactions, D is in X25519 domain (D = r*ConvertPointE(A))
|
||||
* instead of Ed25519 domain (D = r*A). This version applies ConvertPointE transformation.
|
||||
*/
|
||||
inline void generate_carrot_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const secret_key &r, const secret_key &a, signature &sig) {
|
||||
crypto_ops::generate_carrot_tx_proof(prefix_hash, R, A, B, D, r, a, sig);
|
||||
}
|
||||
inline void generate_carrot_tx_proof_as_sender(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const secret_key &r, const secret_key &a, signature &sig) {
|
||||
crypto_ops::generate_carrot_tx_proof_as_sender(prefix_hash, R, A, B, D, r, a, sig);
|
||||
}
|
||||
inline bool check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig, const int version) {
|
||||
return crypto_ops::check_tx_proof(prefix_hash, R, A, B, D, sig, version);
|
||||
}
|
||||
|
||||
/* Verification of a carrot tx proof; R and D should be in Ed25519 domain for verification,
|
||||
*/
|
||||
inline bool check_carrot_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig) {
|
||||
return crypto_ops::check_carrot_tx_proof(prefix_hash, R, A, B, D, sig);
|
||||
}
|
||||
/*
|
||||
inline bool check_carrot_tx_proof_as_sender(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig) {
|
||||
return crypto_ops::check_carrot_tx_proof_as_sender(prefix_hash, R, A, B, D, sig);
|
||||
}
|
||||
*/
|
||||
inline void derive_key_image_generator(const public_key &pub, ec_point &ki_gen) {
|
||||
crypto_ops::derive_key_image_generator(pub, ki_gen);
|
||||
}
|
||||
@@ -378,4 +403,4 @@ CRYPTO_MAKE_HASHABLE_CONSTANT_TIME(public_key_memsafe)
|
||||
CRYPTO_MAKE_HASHABLE(key_image)
|
||||
CRYPTO_MAKE_HASHABLE(key_image_y)
|
||||
CRYPTO_MAKE_COMPARABLE(signature)
|
||||
CRYPTO_MAKE_COMPARABLE(view_tag)
|
||||
CRYPTO_MAKE_COMPARABLE(view_tag)
|
||||
|
||||
@@ -89,12 +89,24 @@ DISABLE_VS_WARNINGS(4244 4345)
|
||||
void account_keys::xor_with_key_stream(const crypto::chacha_key &key)
|
||||
{
|
||||
// encrypt a large enough byte stream with chacha20
|
||||
epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * (2 + m_multisig_keys.size()));
|
||||
epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * (8 + m_multisig_keys.size()));
|
||||
const char *ptr = key_stream.data();
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
m_spend_secret_key.data[i] ^= *ptr++;
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
m_view_secret_key.data[i] ^= *ptr++;
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
s_master.data[i] ^= *ptr++;
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
k_prove_spend.data[i] ^= *ptr++;
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
s_view_balance.data[i] ^= *ptr++;
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
k_view_incoming.data[i] ^= *ptr++;
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
k_generate_image.data[i] ^= *ptr++;
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
s_generate_address.data[i] ^= *ptr++;
|
||||
for (crypto::secret_key &k: m_multisig_keys)
|
||||
{
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
@@ -116,11 +128,20 @@ DISABLE_VS_WARNINGS(4244 4345)
|
||||
void account_keys::encrypt_viewkey(const crypto::chacha_key &key)
|
||||
{
|
||||
// encrypt a large enough byte stream with chacha20
|
||||
epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * 2);
|
||||
epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * 8);
|
||||
const char *ptr = key_stream.data();
|
||||
ptr += sizeof(crypto::secret_key);
|
||||
ptr += sizeof(crypto::secret_key); // Skip m_spend_secret_key
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
m_view_secret_key.data[i] ^= *ptr++;
|
||||
ptr += (2 * sizeof(crypto::secret_key)); // Skip s_master, k_prove_spend
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
s_view_balance.data[i] ^= *ptr++;
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
k_view_incoming.data[i] ^= *ptr++;
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
k_generate_image.data[i] ^= *ptr++;
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
s_generate_address.data[i] ^= *ptr++;
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
void account_keys::decrypt_viewkey(const crypto::chacha_key &key)
|
||||
@@ -283,9 +304,9 @@ DISABLE_VS_WARNINGS(4244 4345)
|
||||
std::string account_base::get_carrot_public_address_str(network_type nettype) const
|
||||
{
|
||||
// Build the cryptonote::account_public_address
|
||||
account_public_address addr{m_keys.m_carrot_main_address.m_spend_public_key, m_keys.m_carrot_main_address.m_view_public_key};
|
||||
account_public_address addr{m_keys.m_carrot_main_address.m_spend_public_key, m_keys.m_carrot_main_address.m_view_public_key, true};
|
||||
// change this code into base 58
|
||||
return get_account_address_as_str(nettype, false, addr, true);
|
||||
return get_account_address_as_str(nettype, false, addr);
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
std::string account_base::get_public_integrated_address_str(const crypto::hash8 &payment_id, network_type nettype) const
|
||||
|
||||
@@ -75,6 +75,11 @@ namespace cryptonote
|
||||
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_multisig_keys)
|
||||
const crypto::chacha_iv default_iv{{0, 0, 0, 0, 0, 0, 0, 0}};
|
||||
KV_SERIALIZE_VAL_POD_AS_BLOB_OPT(m_encryption_iv, default_iv)
|
||||
if (m_account_address.m_spend_public_key == crypto::null_pkey) {
|
||||
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(s_view_balance)
|
||||
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(k_view_incoming)
|
||||
KV_SERIALIZE(m_carrot_account_address)
|
||||
}
|
||||
END_KV_SERIALIZE_MAP()
|
||||
|
||||
void encrypt(const crypto::chacha_key &key);
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include "serialization/debug_archive.h"
|
||||
#include "serialization/crypto.h"
|
||||
#include "serialization/keyvalue_serialization.h" // eepe named serialization
|
||||
#include "serialization/pair.h"
|
||||
#include "serialization/string.h"
|
||||
#include "carrot_core/core_types.h"
|
||||
#include "carrot_impl/carrot_chain_serialization.h"
|
||||
@@ -193,8 +194,6 @@ namespace cryptonote
|
||||
VARINT_FIELD(amount)
|
||||
FIELD(target)
|
||||
END_SERIALIZE()
|
||||
|
||||
|
||||
};
|
||||
|
||||
class protocol_tx_data_t {
|
||||
@@ -214,6 +213,99 @@ namespace cryptonote
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
struct erc_token_t
|
||||
{
|
||||
uint8_t version;
|
||||
std::string contract_address;
|
||||
std::string lockbox_address;
|
||||
std::string ticker;
|
||||
uint64_t erc20_asset_id; // NOTE: this is NOT the Salvium `asset_type_id`!!!
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
VARINT_FIELD(version)
|
||||
FIELD(contract_address)
|
||||
FIELD(lockbox_address)
|
||||
FIELD(ticker)
|
||||
VARINT_FIELD(erc20_asset_id)
|
||||
END_SERIALIZE()
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(contract_address)
|
||||
KV_SERIALIZE(lockbox_address)
|
||||
KV_SERIALIZE(ticker)
|
||||
KV_SERIALIZE(erc20_asset_id)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
struct sal_token_t
|
||||
{
|
||||
uint8_t version;
|
||||
uint64_t supply;
|
||||
uint64_t size;
|
||||
std::string name;
|
||||
std::string url;
|
||||
crypto::hash signature;
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
VARINT_FIELD(version)
|
||||
VARINT_FIELD(supply)
|
||||
VARINT_FIELD(size)
|
||||
FIELD(name)
|
||||
FIELD(url)
|
||||
FIELD(signature)
|
||||
END_SERIALIZE()
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(supply)
|
||||
KV_SERIALIZE(size)
|
||||
KV_SERIALIZE(name)
|
||||
KV_SERIALIZE(url)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
#define TOKEN_TYPE_UNSET 0
|
||||
#define TOKEN_TYPE_ERC20 1
|
||||
#define TOKEN_TYPE_SAL 2
|
||||
|
||||
typedef boost::variant<erc_token_t, sal_token_t> token_v;
|
||||
|
||||
struct token_metadata_t
|
||||
{
|
||||
uint8_t version;
|
||||
std::string asset_type;
|
||||
token_v token;
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
VARINT_FIELD(version)
|
||||
FIELD(asset_type)
|
||||
FIELD(token)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
class layer2_rollup_tx_t {
|
||||
public:
|
||||
crypto::hash tx_prefix_hash;
|
||||
crypto::key_image first_key_image;
|
||||
uint64_t tx_fee;
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
FIELD(tx_prefix_hash)
|
||||
FIELD(first_key_image)
|
||||
VARINT_FIELD(tx_fee)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
class layer2_rollup_data_t {
|
||||
public:
|
||||
uint8_t version;
|
||||
std::vector<layer2_rollup_tx_t> txs;
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
VARINT_FIELD(version)
|
||||
FIELD(txs)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
class transaction_prefix
|
||||
{
|
||||
|
||||
@@ -246,6 +338,10 @@ namespace cryptonote
|
||||
|
||||
protocol_tx_data_t protocol_tx_data;
|
||||
|
||||
carrot::rollup_binding_tag_t rollup_binding_tag;
|
||||
token_metadata_t token_metadata;
|
||||
layer2_rollup_data_t layer2_rollup_data;
|
||||
|
||||
BEGIN_SERIALIZE()
|
||||
VARINT_FIELD(version)
|
||||
if(version == 0 || CURRENT_TRANSACTION_VERSION < version) return false;
|
||||
@@ -263,9 +359,8 @@ namespace cryptonote
|
||||
FIELD(return_address_list)
|
||||
FIELD(return_address_change_mask)
|
||||
} else {
|
||||
if (type == cryptonote::transaction_type::STAKE &&
|
||||
version >= TRANSACTION_VERSION_CARROT)
|
||||
{
|
||||
if ((type == cryptonote::transaction_type::STAKE || type == cryptonote::transaction_type::CREATE_TOKEN) &&
|
||||
(version >= TRANSACTION_VERSION_CARROT)) {
|
||||
FIELD(protocol_tx_data)
|
||||
} else {
|
||||
FIELD(return_address)
|
||||
@@ -277,6 +372,17 @@ namespace cryptonote
|
||||
VARINT_FIELD(amount_slippage_limit)
|
||||
}
|
||||
}
|
||||
if (version < TRANSACTION_VERSION_ENABLE_TOKENS) {
|
||||
return true;
|
||||
}
|
||||
if (type == cryptonote::transaction_type::CREATE_TOKEN) {
|
||||
FIELD(token_metadata)
|
||||
} else if (type == cryptonote::transaction_type::TRANSFER) {
|
||||
FIELD(rollup_binding_tag)
|
||||
} else if (type == cryptonote::transaction_type::ROLLUP) {
|
||||
FIELD(rollup_binding_tag)
|
||||
FIELD(layer2_rollup_data)
|
||||
}
|
||||
END_SERIALIZE()
|
||||
|
||||
public:
|
||||
@@ -297,10 +403,14 @@ namespace cryptonote
|
||||
protocol_tx_data.return_pubkey = crypto::null_pkey;
|
||||
protocol_tx_data.return_view_tag = {};
|
||||
protocol_tx_data.return_anchor_enc = {};
|
||||
token_metadata.version = 0;
|
||||
source_asset_type.clear();
|
||||
destination_asset_type.clear();
|
||||
amount_burnt = 0;
|
||||
amount_slippage_limit = 0;
|
||||
rollup_binding_tag = {0};
|
||||
token_metadata = {};
|
||||
layer2_rollup_data = {};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -564,6 +674,91 @@ namespace cryptonote
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
struct token_block_info {
|
||||
uint64_t block_height;
|
||||
std::map<uint32_t, uint64_t> token_info; // Replaces the vector
|
||||
|
||||
token_block_info() = default; // default ctor
|
||||
|
||||
explicit token_block_info(const uint64_t h)
|
||||
: block_height(h) {}
|
||||
|
||||
void clear() {
|
||||
block_height = 0;
|
||||
token_info.clear();
|
||||
}
|
||||
|
||||
std::vector<uint8_t> serialize() const noexcept {
|
||||
constexpr uint8_t version = 1; // Bump if format changes
|
||||
uint32_t sz = static_cast<uint32_t>(token_info.size());
|
||||
size_t total_size = sizeof(uint8_t) + sizeof(uint64_t) + sizeof(uint32_t) + (sz * (sizeof(uint32_t) + sizeof(uint64_t)));
|
||||
std::vector<uint8_t> buf(total_size);
|
||||
uint8_t* ptr = buf.data();
|
||||
|
||||
std::memcpy(ptr, &version, sizeof(uint8_t));
|
||||
ptr += sizeof(uint8_t);
|
||||
|
||||
std::memcpy(ptr, &block_height, sizeof(uint64_t));
|
||||
ptr += sizeof(uint64_t);
|
||||
|
||||
std::memcpy(ptr, &sz, sizeof(uint32_t));
|
||||
ptr += sizeof(uint32_t);
|
||||
|
||||
for (const auto& p : token_info) { // Iterates in sorted order
|
||||
std::memcpy(ptr, &p.first, sizeof(uint32_t));
|
||||
ptr += sizeof(uint32_t);
|
||||
std::memcpy(ptr, &p.second, sizeof(uint64_t));
|
||||
ptr += sizeof(uint64_t);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
void deserialize(const uint8_t* data, size_t len) {
|
||||
if (len < sizeof(uint32_t)) {
|
||||
throw std::runtime_error("Invalid serialized data");
|
||||
}
|
||||
|
||||
uint8_t version;
|
||||
std::memcpy(&version, data, sizeof(uint8_t));
|
||||
data += sizeof(uint8_t);
|
||||
len -= sizeof(uint8_t);
|
||||
|
||||
if (version == 0) { // Legacy: No height/version (your current format)
|
||||
// Handle old blobs: Caller must set block_height externally
|
||||
block_height = 0; // Or throw if height is mandatory
|
||||
} else if (version == 1) {
|
||||
if (len < sizeof(uint64_t) + sizeof(uint32_t)) {
|
||||
throw std::runtime_error("Invalid serialized data");
|
||||
}
|
||||
std::memcpy(&block_height, data, sizeof(uint64_t));
|
||||
data += sizeof(uint64_t);
|
||||
len -= sizeof(uint64_t);
|
||||
} else {
|
||||
throw std::runtime_error("Unsupported version");
|
||||
}
|
||||
|
||||
uint32_t sz;
|
||||
std::memcpy(&sz, data, sizeof(uint32_t));
|
||||
data += sizeof(uint32_t);
|
||||
len -= sizeof(uint32_t);
|
||||
|
||||
if (len != sz * (sizeof(uint32_t) + sizeof(uint64_t))) {
|
||||
throw std::runtime_error("Invalid serialized data length");
|
||||
}
|
||||
|
||||
token_info.clear();
|
||||
for (uint32_t i = 0; i < sz; ++i) {
|
||||
uint32_t k;
|
||||
uint64_t v;
|
||||
std::memcpy(&k, data, sizeof(uint32_t));
|
||||
data += sizeof(uint32_t);
|
||||
std::memcpy(&v, data, sizeof(uint64_t));
|
||||
data += sizeof(uint64_t);
|
||||
token_info[k] = v; // Map handles sorting/uniquness
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct yield_block_info {
|
||||
uint64_t block_height;
|
||||
uint64_t slippage_total_this_block;
|
||||
@@ -784,6 +979,9 @@ VARIANT_TAG(binary_archive, cryptonote::txout_to_key, 0x2);
|
||||
VARIANT_TAG(binary_archive, cryptonote::txout_to_tagged_key, 0x3);
|
||||
VARIANT_TAG(binary_archive, cryptonote::txout_to_carrot_v1, 0x4);
|
||||
VARIANT_TAG(binary_archive, cryptonote::protocol_tx_data_t, 0x0);
|
||||
VARIANT_TAG(binary_archive, cryptonote::token_metadata_t, 0x0);
|
||||
VARIANT_TAG(binary_archive, cryptonote::erc_token_t, 0x0);
|
||||
VARIANT_TAG(binary_archive, cryptonote::sal_token_t, 0x1);
|
||||
VARIANT_TAG(binary_archive, cryptonote::transaction, 0xcc);
|
||||
VARIANT_TAG(binary_archive, cryptonote::block, 0xbb);
|
||||
|
||||
@@ -797,6 +995,9 @@ VARIANT_TAG(json_archive, cryptonote::txout_to_key, "key");
|
||||
VARIANT_TAG(json_archive, cryptonote::txout_to_tagged_key, "tagged_key");
|
||||
VARIANT_TAG(json_archive, cryptonote::txout_to_carrot_v1, "carrot_v1");
|
||||
VARIANT_TAG(json_archive, cryptonote::protocol_tx_data_t, "protocol_tx_data");
|
||||
VARIANT_TAG(json_archive, cryptonote::token_metadata_t, "token_metadata");
|
||||
VARIANT_TAG(json_archive, cryptonote::erc_token_t, "erc_token");
|
||||
VARIANT_TAG(json_archive, cryptonote::sal_token_t, "sal_token");
|
||||
VARIANT_TAG(json_archive, cryptonote::transaction, "tx");
|
||||
VARIANT_TAG(json_archive, cryptonote::block, "block");
|
||||
|
||||
@@ -810,5 +1011,8 @@ VARIANT_TAG(debug_archive, cryptonote::txout_to_key, "key");
|
||||
VARIANT_TAG(debug_archive, cryptonote::txout_to_tagged_key, "tagged_key");
|
||||
VARIANT_TAG(debug_archive, cryptonote::txout_to_carrot_v1, "carrot_v1");
|
||||
VARIANT_TAG(debug_archive, cryptonote::protocol_tx_data_t, "protocol_tx_data");
|
||||
VARIANT_TAG(debug_archive, cryptonote::token_metadata_t, "token_metadata");
|
||||
VARIANT_TAG(debug_archive, cryptonote::erc_token_t, "erc_token");
|
||||
VARIANT_TAG(debug_archive, cryptonote::sal_token_t, "sal_token");
|
||||
VARIANT_TAG(debug_archive, cryptonote::transaction, "tx");
|
||||
VARIANT_TAG(debug_archive, cryptonote::block, "block");
|
||||
|
||||
@@ -155,10 +155,9 @@ namespace cryptonote {
|
||||
network_type nettype
|
||||
, bool subaddress
|
||||
, account_public_address const & adr
|
||||
, bool is_carrot
|
||||
)
|
||||
{
|
||||
uint64_t address_prefix = is_carrot
|
||||
uint64_t address_prefix = adr.m_is_carrot
|
||||
? (subaddress ? get_config(nettype).CARROT_PUBLIC_SUBADDRESS_BASE58_PREFIX : get_config(nettype).CARROT_PUBLIC_ADDRESS_BASE58_PREFIX)
|
||||
: (subaddress ? get_config(nettype).CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX : get_config(nettype).CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX);
|
||||
|
||||
@@ -169,10 +168,9 @@ namespace cryptonote {
|
||||
network_type nettype
|
||||
, account_public_address const & adr
|
||||
, crypto::hash8 const & payment_id
|
||||
, bool is_carrot
|
||||
)
|
||||
{
|
||||
uint64_t integrated_address_prefix = is_carrot ? get_config(nettype).CARROT_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX : get_config(nettype).CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX;
|
||||
uint64_t integrated_address_prefix = adr.m_is_carrot ? get_config(nettype).CARROT_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX : get_config(nettype).CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX;
|
||||
|
||||
integrated_address iadr = {
|
||||
adr, payment_id
|
||||
|
||||
@@ -88,14 +88,12 @@ namespace cryptonote {
|
||||
network_type nettype
|
||||
, bool subaddress
|
||||
, const account_public_address& adr
|
||||
, bool is_carrot = false
|
||||
);
|
||||
|
||||
std::string get_account_integrated_address_as_str(
|
||||
network_type nettype
|
||||
, const account_public_address& adr
|
||||
, const crypto::hash8& payment_id
|
||||
, bool is_carrot = false
|
||||
);
|
||||
|
||||
bool get_account_address_from_str(
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
#include "crypto/crypto.h"
|
||||
#include "ringct/rctTypes.h"
|
||||
#include "ringct/rctOps.h"
|
||||
#include "serialization/containers.h"
|
||||
|
||||
namespace boost
|
||||
{
|
||||
@@ -181,6 +182,50 @@ namespace boost
|
||||
a & x.return_anchor_enc;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
inline void serialize(Archive &a, cryptonote::erc_token_t &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
a & x.version;
|
||||
a & x.contract_address;
|
||||
a & x.lockbox_address;
|
||||
a & x.ticker;
|
||||
a & x.erc20_asset_id;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
inline void serialize(Archive &a, cryptonote::sal_token_t &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
a & x.version;
|
||||
a & x.supply;
|
||||
a & x.size;
|
||||
a & x.name;
|
||||
a & x.url;
|
||||
a & x.signature;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
inline void serialize(Archive &a, cryptonote::token_metadata_t &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
a & x.version;
|
||||
a & x.asset_type;
|
||||
a & x.token;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
inline void serialize(Archive &a, cryptonote::layer2_rollup_tx_t &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
a & x.tx_prefix_hash;
|
||||
a & x.first_key_image;
|
||||
a & x.tx_fee;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
inline void serialize(Archive &a, cryptonote::layer2_rollup_data_t &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
a & x.version;
|
||||
a & x.txs;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
inline void serialize(Archive &a, cryptonote::transaction_prefix &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
@@ -197,9 +242,8 @@ namespace boost
|
||||
a & x.return_address_list;
|
||||
a & x.return_address_change_mask;
|
||||
} else {
|
||||
if (x.type == cryptonote::transaction_type::STAKE &&
|
||||
x.version >= TRANSACTION_VERSION_CARROT)
|
||||
{
|
||||
if ((x.type == cryptonote::transaction_type::STAKE || x.type == cryptonote::transaction_type::CREATE_TOKEN) &&
|
||||
(x.version >= TRANSACTION_VERSION_CARROT)) {
|
||||
a & x.protocol_tx_data;
|
||||
} else {
|
||||
a & x.return_address;
|
||||
@@ -211,6 +255,16 @@ namespace boost
|
||||
a & x.amount_slippage_limit;
|
||||
}
|
||||
}
|
||||
if (x.version >= TRANSACTION_VERSION_ENABLE_TOKENS) {
|
||||
if (x.type == cryptonote::transaction_type::CREATE_TOKEN) {
|
||||
a & x.token_metadata;
|
||||
} else if (x.type == cryptonote::transaction_type::TRANSFER) {
|
||||
a & x.rollup_binding_tag;
|
||||
} else if (x.type == cryptonote::transaction_type::ROLLUP) {
|
||||
a & x.rollup_binding_tag;
|
||||
a & x.layer2_rollup_data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
@@ -229,9 +283,8 @@ namespace boost
|
||||
a & x.return_address_list;
|
||||
a & x.return_address_change_mask;
|
||||
} else {
|
||||
if (x.type == cryptonote::transaction_type::STAKE &&
|
||||
x.version >= TRANSACTION_VERSION_CARROT)
|
||||
{
|
||||
if ((x.type == cryptonote::transaction_type::STAKE || x.type == cryptonote::transaction_type::CREATE_TOKEN) &&
|
||||
(x.version >= TRANSACTION_VERSION_CARROT)) {
|
||||
a & x.protocol_tx_data;
|
||||
} else {
|
||||
a & x.return_address;
|
||||
@@ -242,6 +295,16 @@ namespace boost
|
||||
a & x.destination_asset_type;
|
||||
a & x.amount_slippage_limit;
|
||||
}
|
||||
if (x.version >= TRANSACTION_VERSION_ENABLE_TOKENS) {
|
||||
if (x.type == cryptonote::transaction_type::CREATE_TOKEN) {
|
||||
a & x.token_metadata;
|
||||
} else if (x.type == cryptonote::transaction_type::TRANSFER) {
|
||||
a & x.rollup_binding_tag;
|
||||
} else if (x.type == cryptonote::transaction_type::ROLLUP) {
|
||||
a & x.rollup_binding_tag;
|
||||
a & x.layer2_rollup_data;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (x.version == 1)
|
||||
{
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <csignal>
|
||||
#include <map>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include "wipeable_string.h"
|
||||
#include "string_tools.h"
|
||||
@@ -43,6 +44,7 @@
|
||||
#include "crypto/hash.h"
|
||||
#include "ringct/rctSigs.h"
|
||||
#include "oracle/asset_types.h"
|
||||
#include "common/debugging.h"
|
||||
|
||||
using namespace epee;
|
||||
|
||||
@@ -1114,7 +1116,37 @@ namespace cryptonote
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (asset_type_id)
|
||||
{
|
||||
// Check to see if 1st byte is permitted
|
||||
std::string s, s_prefix;
|
||||
switch (asset_type_id >> 24) {
|
||||
case 0x01:
|
||||
// We have a user-generated token
|
||||
s_prefix = "sal";
|
||||
break;
|
||||
case 0x02:
|
||||
s_prefix = "erc";
|
||||
break;
|
||||
default:
|
||||
ASSERT_MES_AND_THROW("Invalid asset_type_id: " << asset_type_id);
|
||||
}
|
||||
|
||||
// Break up the remaining 3 bytes into 4 chunks of base36/64
|
||||
static const char alphabet[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
uint32_t asset_type_id_temp = asset_type_id & 0x00FFFFFF;
|
||||
for (int i=0; i<4; ++i) {
|
||||
uint8_t val = asset_type_id_temp & 0x0000003F;
|
||||
if (val >= 36)
|
||||
ASSERT_MES_AND_THROW("Invalid asset_type_id: " << asset_type_id);
|
||||
s.push_back(alphabet[val]);
|
||||
asset_type_id_temp >>= 6;
|
||||
}
|
||||
std::reverse(s.begin(), s.end());
|
||||
return s_prefix + s;
|
||||
}
|
||||
// Should probably throw() here
|
||||
ASSERT_MES_AND_THROW("Invalid asset_type_id: " << asset_type_id);
|
||||
return "";
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
@@ -1129,11 +1161,162 @@ namespace cryptonote
|
||||
} else if (asset_type == "") {
|
||||
return 0x00000000;
|
||||
} else {
|
||||
// Should probably throw() here
|
||||
return static_cast<uint32_t>(-1);
|
||||
if (asset_type.length() != 7) {
|
||||
LOG_ERROR("Custom asset type '" << asset_type << "' has invalid length.");
|
||||
return 0x00000000;
|
||||
}
|
||||
static const std::string alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
|
||||
// Check the 4-char type
|
||||
std::string s_type = asset_type.substr(3);
|
||||
std::transform(s_type.begin(), s_type.end(), s_type.begin(),
|
||||
[](unsigned char c){ return std::toupper(c); });
|
||||
uint32_t asset_id = 0x00000000;
|
||||
for (int i=0; i<s_type.length(); ++i) {
|
||||
uint8_t idx = alphabet.find(s_type.at(i));
|
||||
if (idx == std::string::npos || idx >= 36) {
|
||||
LOG_ERROR("Custom asset type contains invalid char.");
|
||||
return 0x00000000;
|
||||
}
|
||||
asset_id = (asset_id << 6) | (idx & 0x3F);
|
||||
}
|
||||
|
||||
// Check the 3-char prefix
|
||||
std::string s_prefix = asset_type.substr(0,3);
|
||||
std::transform(s_prefix.begin(), s_prefix.end(), s_prefix.begin(),
|
||||
[](unsigned char c){ return std::tolower(c); });
|
||||
if (s_prefix == "sal") {
|
||||
asset_id |= 0x01000000;
|
||||
} else if (s_prefix == "erc") {
|
||||
asset_id |= 0x02000000;
|
||||
} else {
|
||||
LOG_ERROR("Custom asset type has invalid prefix.");
|
||||
return 0x00000000;
|
||||
}
|
||||
return asset_id;
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool is_asset_type_token(const std::string& asset_type)
|
||||
{
|
||||
// SAL, SAL1, BURN are base asset types
|
||||
// erc prefix is not included here
|
||||
if (asset_type.length() >= 3) {
|
||||
std::string prefix = asset_type.substr(0, 3);
|
||||
return (prefix == "sal");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool is_valid_asset_type(const std::string& asset_type) {
|
||||
// This method does NOT throw()
|
||||
try {
|
||||
uint32_t asset_id = asset_id_from_type(asset_type);
|
||||
std::string asset_type_check = asset_type_from_id(asset_id);
|
||||
return (asset_type_check == asset_type);
|
||||
}
|
||||
catch (std::exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool is_valid_custom_asset_type(const std::string& asset_type)
|
||||
{
|
||||
if (!is_valid_asset_type(asset_type))
|
||||
return false;
|
||||
|
||||
// double check with reserved IDs
|
||||
uint32_t id = asset_id_from_type(asset_type);
|
||||
if (id == 0x53414C00 || id == 0x53414C31 || id == 0x4255524E || id == 0x00000000) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
uint64_t get_token_creation_price(const std::string& ticker)
|
||||
{
|
||||
static const std::map<std::string, uint64_t> premium_tickers = {
|
||||
// add more
|
||||
{"USDT", 10000 * COIN},
|
||||
{"USDC", 10000 * COIN},
|
||||
{"WBTC", 10000 * COIN},
|
||||
{"DOGE", 10000 * COIN},
|
||||
{"SHIB", 10000 * COIN},
|
||||
{"AVAX", 10000 * COIN},
|
||||
{"ATOM", 10000 * COIN},
|
||||
{"NEAR", 10000 * COIN},
|
||||
{"TRON", 10000 * COIN},
|
||||
{"HBAR", 10000 * COIN},
|
||||
{"AAVE", 10000 * COIN},
|
||||
{"FLOW", 10000 * COIN},
|
||||
{"EGLD", 10000 * COIN},
|
||||
{"KLAY", 10000 * COIN},
|
||||
{"LUNA", 10000 * COIN},
|
||||
{"DASH", 10000 * COIN},
|
||||
{"NANO", 10000 * COIN},
|
||||
{"CORE", 10000 * COIN},
|
||||
{"BEAM", 10000 * COIN},
|
||||
{"DYDX", 10000 * COIN},
|
||||
{"COMP", 10000 * COIN},
|
||||
{"SAND", 10000 * COIN},
|
||||
{"MANA", 10000 * COIN},
|
||||
{"RUNE", 10000 * COIN},
|
||||
{"PYTH", 10000 * COIN},
|
||||
{"ARKM", 10000 * COIN},
|
||||
{"BLUR", 10000 * COIN},
|
||||
{"STRK", 10000 * COIN},
|
||||
{"PEPE", 10000 * COIN},
|
||||
{"BONK", 10000 * COIN},
|
||||
{"VIUM", 10000 * COIN},
|
||||
{"GOLD", 10000 * COIN},
|
||||
{"SILV", 10000 * COIN},
|
||||
{"CASH", 10000 * COIN},
|
||||
{"EURO", 10000 * COIN},
|
||||
{"PESO", 10000 * COIN},
|
||||
{"BOND", 10000 * COIN},
|
||||
{"FUND", 10000 * COIN},
|
||||
{"BANK", 10000 * COIN},
|
||||
{"SWAP", 10000 * COIN},
|
||||
{"LEND", 10000 * COIN},
|
||||
{"LOAN", 10000 * COIN},
|
||||
{"NOTE", 10000 * COIN},
|
||||
{"HOLD", 10000 * COIN},
|
||||
{"BULL", 10000 * COIN},
|
||||
{"BEAR", 10000 * COIN},
|
||||
{"TECH", 10000 * COIN},
|
||||
{"DATA", 10000 * COIN},
|
||||
{"HASH", 10000 * COIN},
|
||||
{"NODE", 10000 * COIN},
|
||||
{"BYTE", 10000 * COIN},
|
||||
{"GRID", 10000 * COIN},
|
||||
{"CODE", 10000 * COIN},
|
||||
{"META", 10000 * COIN},
|
||||
{"WEB3", 10000 * COIN},
|
||||
{"NFTS", 10000 * COIN},
|
||||
{"DEFI", 10000 * COIN},
|
||||
{"LAND", 10000 * COIN},
|
||||
{"REAL", 10000 * COIN},
|
||||
{"RENT", 10000 * COIN},
|
||||
{"FARM", 10000 * COIN},
|
||||
{"OILX", 10000 * COIN},
|
||||
{"ENRG", 10000 * COIN},
|
||||
{"FUEL", 10000 * COIN},
|
||||
{"VOTE", 10000 * COIN},
|
||||
{"PASS", 10000 * COIN},
|
||||
{"LOCK", 10000 * COIN},
|
||||
};
|
||||
|
||||
// is it a premium ticker
|
||||
auto it = premium_tickers.find(ticker);
|
||||
if (it != premium_tickers.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// not premium
|
||||
return 1000 * COIN;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
/**
|
||||
* The various scenarios that are permitted for Salvium are more extensive than
|
||||
* they are for Zepyhr / Havan. Specifically, we permit:
|
||||
@@ -1406,32 +1589,6 @@ namespace cryptonote
|
||||
<< o.target.type().name() << " and " << tx.vout[0].target.type().name() << ", "
|
||||
<< "expected matching variant types in transaction");
|
||||
}
|
||||
|
||||
// Verify the asset type
|
||||
std::string asset_type;
|
||||
CHECK_AND_ASSERT_MES(cryptonote::get_output_asset_type(o, asset_type), false, "failed to get asset type");
|
||||
if (hf_version < HF_VERSION_SALVIUM_ONE_PROOFS) {
|
||||
// Prior to the first audit, ONLY SAL was supported
|
||||
CHECK_AND_ASSERT_MES(asset_type == "SAL", false, "wrong output asset type:" << asset_type);
|
||||
} else {
|
||||
if (tx.type == cryptonote::transaction_type::AUDIT) {
|
||||
// HERE BE DRAGONS!!!
|
||||
// SRCG: This will NOT always be the case - when we add an audit for SALx it'll need to support that as well
|
||||
// The CHANGE for an AUDIT TX must be SAL (and 0 value, and unspendable, and to the origin wallet, and ...)
|
||||
CHECK_AND_ASSERT_MES(asset_type == "SAL", false, "wrong output asset type:" << asset_type);
|
||||
// LAND AHOY!!!
|
||||
} else if (tx.type == cryptonote::transaction_type::PROTOCOL) {
|
||||
if (hf_version < HF_VERSION_AUDIT1_PAUSE) {
|
||||
// PROTOCOL TXs are responsible for paying out SAL and SAL1 during the first AUDIT
|
||||
CHECK_AND_ASSERT_MES(asset_type == "SAL1" || asset_type == "SAL", false, "wrong output asset type:" << asset_type);
|
||||
} else {
|
||||
CHECK_AND_ASSERT_MES(asset_type == "SAL1", false, "wrong output asset type:" << asset_type);
|
||||
}
|
||||
} else {
|
||||
// All other TX types must only spend + create SAL1 (MINER, TRANSFER)
|
||||
CHECK_AND_ASSERT_MES(asset_type == "SAL1", false, "wrong output asset type:" << asset_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -135,6 +135,10 @@ namespace cryptonote
|
||||
uint64_t get_outs_money_amount(const transaction& tx);
|
||||
std::string asset_type_from_id(const uint32_t asset_type_id);
|
||||
uint32_t asset_id_from_type(const std::string asset_type);
|
||||
bool is_valid_custom_asset_type(const std::string& asset_type);
|
||||
bool is_valid_asset_type(const std::string& asset_type);
|
||||
bool is_asset_type_token(const std::string& asset_type);
|
||||
uint64_t get_token_creation_price(const std::string& ticker);
|
||||
bool get_tx_asset_types(const transaction& tx, const crypto::hash &txid, std::string& source, std::string& destination, const bool is_miner_tx);
|
||||
bool get_output_public_key(const cryptonote::tx_out& out, crypto::public_key& output_public_key);
|
||||
boost::optional<crypto::view_tag> get_output_view_tag(const cryptonote::tx_out& out);
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#define TX_EXTRA_NONCE 0x02
|
||||
#define TX_EXTRA_MERGE_MINING_TAG 0x03
|
||||
#define TX_EXTRA_TAG_ADDITIONAL_PUBKEYS 0x04
|
||||
#define TX_EXTRA_TAG_TOKEN 0x80
|
||||
#define TX_EXTRA_MYSTERIOUS_MINERGATE_TAG 0xDE
|
||||
|
||||
#define TX_EXTRA_NONCE_PAYMENT_ID 0x00
|
||||
@@ -167,6 +168,15 @@ namespace cryptonote
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
struct tx_extra_token
|
||||
{
|
||||
cryptonote::token_metadata_t token;
|
||||
|
||||
BEGIN_SERIALIZE()
|
||||
FIELD(token)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
struct tx_extra_mysterious_minergate
|
||||
{
|
||||
std::string data;
|
||||
@@ -180,7 +190,7 @@ namespace cryptonote
|
||||
// varint tag;
|
||||
// varint size;
|
||||
// varint data[];
|
||||
typedef boost::variant<tx_extra_padding, tx_extra_pub_key, tx_extra_nonce, tx_extra_merge_mining_tag, tx_extra_additional_pub_keys, tx_extra_mysterious_minergate> tx_extra_field;
|
||||
typedef boost::variant<tx_extra_padding, tx_extra_pub_key, tx_extra_nonce, tx_extra_merge_mining_tag, tx_extra_additional_pub_keys, tx_extra_mysterious_minergate, tx_extra_token> tx_extra_field;
|
||||
}
|
||||
|
||||
VARIANT_TAG(binary_archive, cryptonote::tx_extra_padding, TX_EXTRA_TAG_PADDING);
|
||||
@@ -189,3 +199,4 @@ VARIANT_TAG(binary_archive, cryptonote::tx_extra_nonce, TX_EXTRA_NONCE);
|
||||
VARIANT_TAG(binary_archive, cryptonote::tx_extra_merge_mining_tag, TX_EXTRA_MERGE_MINING_TAG);
|
||||
VARIANT_TAG(binary_archive, cryptonote::tx_extra_additional_pub_keys, TX_EXTRA_TAG_ADDITIONAL_PUBKEYS);
|
||||
VARIANT_TAG(binary_archive, cryptonote::tx_extra_mysterious_minergate, TX_EXTRA_MYSTERIOUS_MINERGATE_TAG);
|
||||
VARIANT_TAG(binary_archive, cryptonote::tx_extra_token, TX_EXTRA_TAG_TOKEN);
|
||||
|
||||
@@ -44,10 +44,11 @@
|
||||
#define CRYPTONOTE_MAX_TX_PER_BLOCK 0x10000000
|
||||
#define CRYPTONOTE_PUBLIC_ADDRESS_TEXTBLOB_VER 0
|
||||
#define CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW 60
|
||||
#define CURRENT_TRANSACTION_VERSION 4
|
||||
#define CURRENT_TRANSACTION_VERSION 5
|
||||
#define TRANSACTION_VERSION_2_OUTS 2
|
||||
#define TRANSACTION_VERSION_N_OUTS 3
|
||||
#define TRANSACTION_VERSION_CARROT 4
|
||||
#define TRANSACTION_VERSION_ENABLE_TOKENS 5
|
||||
#define CURRENT_BLOCK_MAJOR_VERSION 1
|
||||
#define CURRENT_BLOCK_MINOR_VERSION 1
|
||||
#define CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT 60*60*2
|
||||
@@ -91,6 +92,8 @@
|
||||
#define TREASURY_SAL1_MINT_AMOUNT ((uint64_t)130000000000000ull) // 1.3M
|
||||
#define TREASURY_SAL1_MINT_COUNT 8 // 8 times
|
||||
|
||||
#define CREATE_TOKEN_LOCK_PERIOD 10
|
||||
|
||||
#define DIFFICULTY_TARGET_V2 120 // seconds
|
||||
#define DIFFICULTY_TARGET_V1 60 // seconds - before first fork
|
||||
#define DIFFICULTY_WINDOW_V2 70 // blocks
|
||||
@@ -244,13 +247,14 @@
|
||||
#define HF_VERSION_AUDIT2 8
|
||||
#define HF_VERSION_AUDIT2_PAUSE 9
|
||||
#define HF_VERSION_CARROT 10
|
||||
#define HF_VERSION_ENABLE_TOKENS 11
|
||||
|
||||
#define HF_VERSION_REQUIRE_VIEW_TAGS 255
|
||||
#define HF_VERSION_ENABLE_CONVERT 255
|
||||
#define HF_VERSION_ENABLE_ORACLE 255
|
||||
#define HF_VERSION_SLIPPAGE_YIELD 255
|
||||
|
||||
#define TESTNET_VERSION 15
|
||||
#define TESTNET_VERSION 16
|
||||
#define STAGENET_VERSION 1
|
||||
|
||||
#define PER_KB_FEE_QUANTIZATION_DECIMALS 8
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
|
||||
#include "include_base_utils.h"
|
||||
#include "cryptonote_basic/cryptonote_basic_impl.h"
|
||||
#include "cryptonote_basic/cryptonote_format_utils.h"
|
||||
#include "tx_pool.h"
|
||||
#include "blockchain.h"
|
||||
#include "blockchain_db/locked_txn.h"
|
||||
@@ -66,6 +67,8 @@
|
||||
#include "net/http_client.h"
|
||||
#include "storages/http_abstract_invoke.h"
|
||||
|
||||
#include "common/debugging.h"
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "blockchain"
|
||||
|
||||
@@ -877,7 +880,7 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
|
||||
std::vector<uint64_t> timestamps;
|
||||
std::vector<difficulty_type> difficulties;
|
||||
uint64_t height;
|
||||
auto new_top_hash = get_tail_id(height); // get it again now that we have the lock
|
||||
top_hash = get_tail_id(height); // get it again now that we have the lock
|
||||
++height;
|
||||
|
||||
uint8_t version = get_current_hard_fork_version();
|
||||
@@ -976,11 +979,11 @@ size_t Blockchain::recalculate_difficulties(boost::optional<uint64_t> start_heig
|
||||
if (start_height_opt) {
|
||||
start_height = *start_height_opt;
|
||||
} else {
|
||||
bool found = false;
|
||||
// bool found;
|
||||
for (size_t i=0; i<num_mainnet_hard_forks; ++i) {
|
||||
if (version == mainnet_hard_forks[i].version) {
|
||||
start_height = mainnet_hard_forks[i].height;
|
||||
found = true;
|
||||
//found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1348,7 +1351,10 @@ bool Blockchain::prevalidate_miner_transaction(const block& b, uint64_t height,
|
||||
CHECK_AND_ASSERT_MES(b.miner_tx.vin[0].type() == typeid(txin_gen), false, "coinbase transaction in the block has the wrong type");
|
||||
CHECK_AND_ASSERT_MES(b.miner_tx.version > 1, false, "Invalid coinbase transaction version");
|
||||
|
||||
if (hf_version >= HF_VERSION_CARROT) {
|
||||
if (hf_version >= HF_VERSION_ENABLE_TOKENS) {
|
||||
CHECK_AND_ASSERT_MES(b.miner_tx.version == TRANSACTION_VERSION_ENABLE_TOKENS, false, "miner transaction has wrong version");
|
||||
CHECK_AND_ASSERT_MES(b.miner_tx.type == cryptonote::transaction_type::MINER, false, "miner transaction has wrong type");
|
||||
} else if (hf_version >= HF_VERSION_CARROT) {
|
||||
CHECK_AND_ASSERT_MES(b.miner_tx.version == TRANSACTION_VERSION_CARROT, false, "miner transaction has wrong version");
|
||||
CHECK_AND_ASSERT_MES(b.miner_tx.type == cryptonote::transaction_type::MINER, false, "miner transaction has wrong type");
|
||||
}
|
||||
@@ -1407,7 +1413,10 @@ bool Blockchain::prevalidate_protocol_transaction(const block& b, uint64_t heigh
|
||||
uint64_t stake_lock_period = get_config(m_nettype).STAKE_LOCK_PERIOD;
|
||||
uint8_t hf_version_submitted = get_ideal_hard_fork_version(height - stake_lock_period - 1);
|
||||
|
||||
if (hf_version >= HF_VERSION_CARROT) {
|
||||
if (hf_version >= HF_VERSION_ENABLE_TOKENS) {
|
||||
CHECK_AND_ASSERT_MES(b.protocol_tx.version == TRANSACTION_VERSION_ENABLE_TOKENS, false, "protocol transaction has wrong version");
|
||||
hf_version_submitted = hf_version;
|
||||
} else if (hf_version == HF_VERSION_CARROT) {
|
||||
if (hf_version_submitted >= HF_VERSION_CARROT || b.protocol_tx.vout.size() == 0) {
|
||||
CHECK_AND_ASSERT_MES(b.protocol_tx.version == TRANSACTION_VERSION_CARROT, false, "protocol transaction has wrong version");
|
||||
} else {
|
||||
@@ -1525,6 +1534,7 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
|
||||
case HF_VERSION_AUDIT2:
|
||||
case HF_VERSION_AUDIT2_PAUSE:
|
||||
case HF_VERSION_CARROT:
|
||||
case HF_VERSION_ENABLE_TOKENS:
|
||||
if (b.miner_tx.amount_burnt > 0) {
|
||||
CHECK_AND_ASSERT_MES(money_in_use + b.miner_tx.amount_burnt > money_in_use, false, "miner transaction is overflowed by amount_burnt");
|
||||
money_in_use += b.miner_tx.amount_burnt;
|
||||
@@ -1557,7 +1567,7 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
// SRCG
|
||||
bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height, uint8_t hf_version)
|
||||
bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height, uint8_t hf_version, const std::vector<std::pair<transaction, blobdata>>& txs)
|
||||
{
|
||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||
|
||||
@@ -1633,9 +1643,20 @@ bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height,
|
||||
}
|
||||
}
|
||||
|
||||
// Count CREATE_TOKEN transactions if we're at the right hard fork version
|
||||
size_t create_token_count = 0;
|
||||
if (hf_version >= HF_VERSION_ENABLE_TOKENS && !txs.empty()) {
|
||||
for (const auto& tx_pair : txs) {
|
||||
const transaction& tx = tx_pair.first;
|
||||
if (tx.type == cryptonote::transaction_type::CREATE_TOKEN) {
|
||||
create_token_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check we have the correct number of entries
|
||||
CHECK_AND_ASSERT_MES(
|
||||
b.protocol_tx.vout.size() == yield_payouts.size() + audit_payouts.size() + carrot_yield_payouts.size(),
|
||||
b.protocol_tx.vout.size() == yield_payouts.size() + audit_payouts.size() + carrot_yield_payouts.size() + create_token_count,
|
||||
false, "Invalid number of outputs in protocol_tx - aborting"
|
||||
);
|
||||
|
||||
@@ -1682,6 +1703,71 @@ bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height,
|
||||
);
|
||||
}
|
||||
|
||||
if (hf_version >= HF_VERSION_ENABLE_TOKENS) {
|
||||
// Validate create_coin transaction outputs
|
||||
std::vector<std::pair<protocol_tx_data_t, token_metadata_t>> create_token_data;
|
||||
|
||||
for (const auto& tx_pair : txs) {
|
||||
const transaction& tx = tx_pair.first;
|
||||
if (tx.type == cryptonote::transaction_type::CREATE_TOKEN) {
|
||||
create_token_data.push_back(std::make_pair(tx.protocol_tx_data, tx.token_metadata));
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& ct_data : create_token_data) {
|
||||
// Verify the output key
|
||||
crypto::public_key out_key;
|
||||
cryptonote::get_output_public_key(b.protocol_tx.vout[output_idx], out_key);
|
||||
CHECK_AND_ASSERT_MES(out_key == ct_data.first.return_address, false, "Incorrect CREATE_TOKEN output key detected in protocol_tx");
|
||||
|
||||
// Verify the return pubkey
|
||||
if (b.protocol_tx.vout.size() > 1) {
|
||||
const auto additional_pubkeys = cryptonote::get_additional_tx_pub_keys_from_extra(b.protocol_tx.extra);
|
||||
CHECK_AND_ASSERT_MES(additional_pubkeys.size() > output_idx, false, "Missing CREATE_TOKEN return pubkey detected in protocol_tx");
|
||||
CHECK_AND_ASSERT_MES(additional_pubkeys[output_idx] == ct_data.first.return_pubkey, false, "Incorrect CREATE_TOKEN return pubkey detected in protocol_tx");
|
||||
} else {
|
||||
const auto main_pubkey = cryptonote::get_tx_pub_key_from_extra(b.protocol_tx.extra);
|
||||
CHECK_AND_ASSERT_MES(main_pubkey == ct_data.first.return_pubkey, false, "Incorrect CREATE_TOKEN return pubkey detected in protocol_tx");
|
||||
}
|
||||
|
||||
// Verify the correct metadata type is provided
|
||||
CHECK_AND_ASSERT_MES(ct_data.second.token.type() == typeid(cryptonote::sal_token_t), false, "Incorrect CREATE_TOKEN metadata type detected in protocol_tx");
|
||||
cryptonote::sal_token_t token = boost::get<cryptonote::sal_token_t>(ct_data.second.token);
|
||||
|
||||
// Verify the output amount
|
||||
uint64_t hi, lo;
|
||||
CHECK_AND_ASSERT_MES(token.supply <= (MONEY_SUPPLY / COIN), false, "Invalid SUPPLY value for CREATE_TOKEN when constructing protocol_tx");
|
||||
lo = mul128(token.supply, COIN, &hi);
|
||||
CHECK_AND_ASSERT_MES(hi == 0, false, "Numeric overflow in CREATE_TOKEN supply");
|
||||
CHECK_AND_ASSERT_MES(b.protocol_tx.vout[output_idx].amount == lo, false, "Incorrect CREATE_TOKEN output amount detected in protocol_tx");
|
||||
|
||||
// Verify the output asset type (should be the new asset type from protocol_tx_data)
|
||||
std::string expected_asset_type = "sal" + ct_data.second.asset_type;
|
||||
std::string out_asset_type;
|
||||
cryptonote::get_output_asset_type(b.protocol_tx.vout[output_idx], out_asset_type);
|
||||
CHECK_AND_ASSERT_MES(out_asset_type == expected_asset_type, false, "Incorrect CREATE_TOKEN output asset_type detected in protocol_tx");
|
||||
|
||||
// Validate custom asset type format
|
||||
CHECK_AND_ASSERT_MES(cryptonote::is_valid_custom_asset_type(out_asset_type), false, "CREATE_TOKEN asset type is invalid");
|
||||
|
||||
// Verify the view tag
|
||||
CHECK_AND_ASSERT_MES(
|
||||
boost::get<cryptonote::txout_to_carrot_v1>(
|
||||
b.protocol_tx.vout[output_idx].target
|
||||
).view_tag == ct_data.first.return_view_tag, false, "Incorrect CREATE_TOKEN view tag detected in protocol_tx"
|
||||
);
|
||||
|
||||
// Verify the anchor encrypted
|
||||
CHECK_AND_ASSERT_MES(
|
||||
boost::get<cryptonote::txout_to_carrot_v1>(
|
||||
b.protocol_tx.vout[output_idx].target
|
||||
).encrypted_janus_anchor == ct_data.first.return_anchor_enc, false, "Incorrect CREATE_TOKEN anchor detected in protocol_tx"
|
||||
);
|
||||
|
||||
output_idx++;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1974,19 +2060,46 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
|
||||
|
||||
CHECK_AND_ASSERT_MES(diffic, false, "difficulty overhead.");
|
||||
|
||||
std::vector<cryptonote::protocol_data_entry> protocol_entries;
|
||||
size_t txs_weight;
|
||||
uint64_t fee;
|
||||
|
||||
if (!m_tx_pool.fill_block_template(b, median_weight, already_generated_coins, txs_weight, fee, expected_reward, b.major_version))
|
||||
std::vector<std::pair<protocol_tx_data_t, token_metadata_t>> create_token_entries;
|
||||
if (!m_tx_pool.fill_block_template(b, median_weight, already_generated_coins, txs_weight, fee, expected_reward, create_token_entries, b.major_version))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// create the protocol transaction entries for CREATE_TOKEN TXs
|
||||
if (b.major_version >= HF_VERSION_ENABLE_TOKENS) {
|
||||
for (const auto& ct_data_entry: create_token_entries) {
|
||||
CHECK_AND_ASSERT_MES(ct_data_entry.second.token.type() == typeid(cryptonote::sal_token_t), false, "Incorrect CREATE_TOKEN metadata type detected in protocol_tx");
|
||||
cryptonote::sal_token_t token = boost::get<cryptonote::sal_token_t>(ct_data_entry.second.token);
|
||||
cryptonote::protocol_data_entry entry;
|
||||
uint64_t hi, lo;
|
||||
CHECK_AND_ASSERT_MES(token.supply <= (MONEY_SUPPLY / COIN), false, "Invalid SUPPLY value for CREATE_TOKEN when constructing protocol_tx");
|
||||
CHECK_AND_ASSERT_MES(token.supply > 0, false, "Invalid SUPPLY value for CREATE_TOKEN when constructing protocol_tx");
|
||||
lo = mul128(token.supply, COIN, &hi);
|
||||
CHECK_AND_ASSERT_MES(hi == 0, false, "Numeric overflow in CREATE_TOKEN supply");
|
||||
entry.amount_burnt = cryptonote::get_token_creation_price(ct_data_entry.second.asset_type);
|
||||
entry.amount_minted = lo;
|
||||
entry.amount_slippage_limit = 0;
|
||||
entry.source_asset = "SAL1";
|
||||
entry.destination_asset = "sal" + ct_data_entry.second.asset_type;
|
||||
entry.type = cryptonote::transaction_type::CREATE_TOKEN;
|
||||
entry.return_address = ct_data_entry.first.return_address;
|
||||
entry.return_pubkey = ct_data_entry.first.return_pubkey;
|
||||
entry.return_view_tag = ct_data_entry.first.return_view_tag;
|
||||
entry.return_anchor_enc = ct_data_entry.first.return_anchor_enc;
|
||||
entry.is_carrot = true;
|
||||
entry.origin_height = height;
|
||||
protocol_entries.push_back(entry);
|
||||
}
|
||||
}
|
||||
|
||||
// Check to see if there are any matured YIELD TXs
|
||||
uint64_t yield_lock_period = get_config(m_nettype).STAKE_LOCK_PERIOD;
|
||||
uint64_t start_height = (height > yield_lock_period) ? height - yield_lock_period - 1 : 0;
|
||||
|
||||
std::vector<cryptonote::protocol_data_entry> protocol_entries;
|
||||
cryptonote::yield_block_info ybi_matured;
|
||||
bool ok = get_ybi_entry(start_height, ybi_matured);
|
||||
if (ok && ybi_matured.locked_coins_this_block > 0) {
|
||||
@@ -2123,7 +2236,6 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
|
||||
*/
|
||||
|
||||
// Time to construct the protocol_tx
|
||||
uint64_t protocol_fee = 0;
|
||||
address_parse_info treasury_address_info;
|
||||
ok = cryptonote::get_account_address_from_str(treasury_address_info, m_nettype, get_config(m_nettype).TREASURY_ADDRESS);
|
||||
CHECK_AND_ASSERT_MES(ok, false, "Failed to obtain treasury address info");
|
||||
@@ -2954,7 +3066,7 @@ bool Blockchain::get_pricing_record(oracle::pricing_record &pr, std::map<std::st
|
||||
|
||||
bool r = false;
|
||||
const uint64_t height = get_current_blockchain_height();
|
||||
const uint8_t hf_version = m_hardfork->get_current_version();
|
||||
// const uint8_t hf_version = m_hardfork->get_current_version();
|
||||
|
||||
epee::net_utils::http::http_simple_client http_client;
|
||||
COMMAND_RPC_GET_PRICING_RECORD::request req = AUTO_VAL_INIT(req);
|
||||
@@ -3728,7 +3840,7 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
|
||||
|
||||
if (hf_version >= HF_VERSION_CARROT) {
|
||||
// from v10, force the new SalviumOne RCT data
|
||||
if (tx.type == cryptonote::transaction_type::TRANSFER || tx.type == cryptonote::transaction_type::STAKE || tx.type == cryptonote::transaction_type::BURN || tx.type == cryptonote::transaction_type::CONVERT || tx.type == cryptonote::transaction_type::AUDIT) {
|
||||
if (tx.type == cryptonote::transaction_type::TRANSFER || tx.type == cryptonote::transaction_type::STAKE || tx.type == cryptonote::transaction_type::BURN || tx.type == cryptonote::transaction_type::CONVERT || tx.type == cryptonote::transaction_type::AUDIT || tx.type == cryptonote::transaction_type::CREATE_TOKEN || tx.type == cryptonote::transaction_type::ROLLUP) {
|
||||
if (tx.rct_signatures.type != rct::RCTTypeSalviumOne) {
|
||||
MERROR_VER("SalviumOne data required after v" + std::to_string(HF_VERSION_CARROT));
|
||||
tvc.m_invalid_output = true;
|
||||
@@ -3783,6 +3895,100 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
bool Blockchain::check_tx_asset_types(const transaction& tx, tx_verification_context &tvc) const
|
||||
{
|
||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
||||
|
||||
const uint8_t hf_version = m_hardfork->get_current_version();
|
||||
if (hf_version >= HF_VERSION_ENABLE_TOKENS) {
|
||||
|
||||
// Get the valid tokens according to the DB
|
||||
std::map<std::string, cryptonote::token_metadata_t> mapTokens = m_db->get_tokens();
|
||||
|
||||
if (tx.type == cryptonote::transaction_type::PROTOCOL || tx.type == cryptonote::transaction_type::MINER) {
|
||||
return true;
|
||||
} else if (
|
||||
tx.type == cryptonote::transaction_type::CREATE_TOKEN ||
|
||||
tx.type == cryptonote::transaction_type::ROLLUP ||
|
||||
tx.type == cryptonote::transaction_type::STAKE
|
||||
) {
|
||||
if (tx.source_asset_type != "SAL1" || tx.destination_asset_type != "SAL1") {
|
||||
MERROR_VER("Invalid source/dest asset type for CREATE_TOKEN / ROLLUP / STAKE - provided source asset: " << tx.source_asset_type << ", and destination asset: " << tx.destination_asset_type << ", expected SAL1");
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
} else if (tx.type == cryptonote::transaction_type::BURN) {
|
||||
if ((tx.source_asset_type != "SAL" && tx.source_asset_type != "SAL1") || tx.destination_asset_type != tx.source_asset_type) {
|
||||
MERROR_VER("Invalid source/dest asset type for BURN - provided source asset: " << tx.source_asset_type << ", and destination asset: " << tx.destination_asset_type << ", expected (matching) SAL/SAL1");
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
} else if (tx.type == cryptonote::transaction_type::TRANSFER) {
|
||||
if (tx.source_asset_type != tx.destination_asset_type) {
|
||||
MERROR_VER("Mismatched asset types for TRANSFER - provided source asset: " << tx.source_asset_type << ", provided destination asset: " << tx.destination_asset_type << ", expected them to match");
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
} else if (!(tx.source_asset_type == "SAL1" || mapTokens.count(tx.source_asset_type))) {
|
||||
MERROR_VER("Invalid source asset type for TRANSFER - provided source asset: " << tx.source_asset_type << ", expected valid token type or SAL1");
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
} else if (tx.destination_asset_type != "SAL1" && !mapTokens.count(tx.destination_asset_type)) {
|
||||
MERROR_VER("Invalid destination asset type for TRANSFER - provided destination asset: " << tx.destination_asset_type << ", expected valid token type or SAL1");
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
} else if (tx.type == cryptonote::transaction_type::AUDIT || tx.type == cryptonote::transaction_type::CONVERT) {
|
||||
MERROR_VER("AUDIT and CONVERT transaction types are not allowed in this hardfork version:" << std::to_string(HF_VERSION_ENABLE_TOKENS));
|
||||
} else {
|
||||
MERROR_VER("Unknown transaction type: " << tx.type << ".");
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
} else if (hf_version >= HF_VERSION_CARROT) {
|
||||
if (tx.source_asset_type != "SAL1" || tx.destination_asset_type != "SAL1") {
|
||||
MERROR_VER("Invalid destination/source asset type - provided destination asset: " << tx.destination_asset_type << ", expected SAL1" << ", provided source asset: " << tx.source_asset_type << ", expected SAL1");
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
// }
|
||||
} else if (hf_version >= HF_VERSION_SALVIUM_ONE_PROOFS) {
|
||||
// protocol and miner txs don't have source and destination asset types
|
||||
if (tx.type == cryptonote::transaction_type::PROTOCOL || tx.type == cryptonote::transaction_type::MINER) {
|
||||
return true;
|
||||
} else if (tx.type == cryptonote::transaction_type::AUDIT) {
|
||||
CHECK_AND_ASSERT_MES(tx.source_asset_type == "SAL" && tx.destination_asset_type == "SAL", false, "wrong source/destination asset type: provided source asset " << tx.source_asset_type << " expected SAL and provided destination asset " << tx.destination_asset_type << " expected SAL");
|
||||
} else if (tx.type == cryptonote::transaction_type::BURN) {
|
||||
if (tx.source_asset_type != "SAL1" || tx.destination_asset_type != "BURN") {
|
||||
MERROR_VER("Invalid source/dest asset type for BURN - provided source asset: " << tx.source_asset_type << ", and destination asset: " << tx.destination_asset_type << ", expected SAL1 and BURN respectively");
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
CHECK_AND_ASSERT_MES(tx.source_asset_type == "SAL1" && tx.destination_asset_type == "SAL1", false, "wrong source/destination asset type: provided source asset: " << tx.source_asset_type << " expected SAL1 and provided destination asset: " << tx.destination_asset_type << " expected SAL1");
|
||||
}
|
||||
} else {
|
||||
if (tx.type == cryptonote::transaction_type::BURN) {
|
||||
if (tx.source_asset_type != "SAL" || tx.destination_asset_type != "BURN") {
|
||||
MERROR_VER("Invalid source/dest asset type for BURN - provided source asset: " << tx.source_asset_type << ", and destination asset: " << tx.destination_asset_type << ", expected SAL and BURN respectively");
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
//only SAL existed before hf 6
|
||||
else{
|
||||
if (tx.source_asset_type != "SAL" || tx.destination_asset_type != "SAL") {
|
||||
MERROR_VER("Invalid destination/source asset type - provided destination asset: " << tx.destination_asset_type << ", expected SAL" << ", provided source asset: " << tx.source_asset_type << ", expected SAL");
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------
|
||||
bool Blockchain::check_tx_type_and_version(const transaction& tx, tx_verification_context &tvc) const
|
||||
{
|
||||
@@ -3809,6 +4015,25 @@ bool Blockchain::check_tx_type_and_version(const transaction& tx, tx_verificatio
|
||||
}
|
||||
}
|
||||
|
||||
// Reverse the order of checking now, because we're using >=
|
||||
if (hf_version >= HF_VERSION_ENABLE_TOKENS) {
|
||||
if (tx.type == cryptonote::transaction_type::TRANSFER ||
|
||||
tx.type == cryptonote::transaction_type::RETURN ||
|
||||
tx.type == cryptonote::transaction_type::ROLLUP ||
|
||||
tx.type == cryptonote::transaction_type::CREATE_TOKEN) {
|
||||
if (tx.version < TRANSACTION_VERSION_ENABLE_TOKENS) {
|
||||
}
|
||||
} else {
|
||||
if (tx.version != TRANSACTION_VERSION_ENABLE_TOKENS) {
|
||||
}
|
||||
if (tx.type == cryptonote::transaction_type::PROTOCOL) {
|
||||
// Allowed multiple asset_types for `vout` entries
|
||||
} else {
|
||||
// No mixing of asset_types permitted - verify here
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// After v2 allow N-out TXs for TRANSFER ONLY
|
||||
if (hf_version >= HF_VERSION_ENABLE_N_OUTS && hf_version < HF_VERSION_CARROT) {
|
||||
if (tx.version >= TRANSACTION_VERSION_N_OUTS && tx.type != cryptonote::transaction_type::TRANSFER) {
|
||||
@@ -3818,7 +4043,15 @@ bool Blockchain::check_tx_type_and_version(const transaction& tx, tx_verificatio
|
||||
}
|
||||
}
|
||||
|
||||
if (hf_version >= HF_VERSION_CARROT) {
|
||||
if (hf_version >= HF_VERSION_ENABLE_TOKENS) {
|
||||
if (tx.version != TRANSACTION_VERSION_ENABLE_TOKENS) {
|
||||
MERROR("TX version " + std::to_string(tx.version) + " is not supported, expected " + std::to_string(TRANSACTION_VERSION_ENABLE_TOKENS));
|
||||
tvc.m_version_mismatch = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (hf_version == HF_VERSION_CARROT) {
|
||||
if (tx.version != TRANSACTION_VERSION_CARROT) {
|
||||
MERROR("TX version " + std::to_string(tx.version) + " is not supported, expected " + std::to_string(TRANSACTION_VERSION_CARROT));
|
||||
tvc.m_version_mismatch = true;
|
||||
@@ -3835,6 +4068,19 @@ bool Blockchain::check_tx_type_and_version(const transaction& tx, tx_verificatio
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure CREATE_TOKEN TXs are disabled until we are ready - belt and braces!
|
||||
if (hf_version < HF_VERSION_ENABLE_TOKENS) {
|
||||
if (tx.type == cryptonote::transaction_type::CREATE_TOKEN) {
|
||||
MERROR("CREATE_TOKEN TXs are not permitted prior to v" + std::to_string(HF_VERSION_ENABLE_TOKENS));
|
||||
tvc.m_version_mismatch = true;
|
||||
return false;
|
||||
}
|
||||
if (tx.type == cryptonote::transaction_type::ROLLUP) {
|
||||
MERROR("ROLLUP TXs are not permitted prior to v" + std::to_string(HF_VERSION_ENABLE_TOKENS));
|
||||
tvc.m_version_mismatch = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (tx.type == cryptonote::transaction_type::AUDIT) {
|
||||
// Make sure we are supposed to accept AUDIT txs at this point
|
||||
@@ -3842,25 +4088,25 @@ bool Blockchain::check_tx_type_and_version(const transaction& tx, tx_verificatio
|
||||
CHECK_AND_ASSERT_MES(audit_hard_forks.find(hf_version) != audit_hard_forks.end(), false, "trying to audit outside an audit fork");
|
||||
std::string expected_asset_type = audit_hard_forks.at(hf_version).second.first;
|
||||
CHECK_AND_ASSERT_MES(tx.source_asset_type == expected_asset_type, false, "trying to spend " << tx.source_asset_type << " coins in an AUDIT TX");
|
||||
} else {
|
||||
if (hf_version >= HF_VERSION_SALVIUM_ONE_PROOFS) {
|
||||
CHECK_AND_ASSERT_MES(tx.source_asset_type == "SAL1", false, "trying to spend " << tx.source_asset_type << " coins in a non-AUDIT TX");
|
||||
} else {
|
||||
CHECK_AND_ASSERT_MES(tx.source_asset_type == "SAL", false, "trying to spend " << tx.source_asset_type << " coins in a non-AUDIT TX");
|
||||
}
|
||||
}
|
||||
|
||||
if (tx.type == cryptonote::transaction_type::BURN) {
|
||||
CHECK_AND_ASSERT_MES(tx.destination_asset_type == "BURN", false, "incorrect burn tx destination type:" << tx.destination_asset_type);
|
||||
} else {
|
||||
if (tx.source_asset_type != tx.destination_asset_type) {
|
||||
MERROR_VER("Tx " << get_transaction_hash(tx) << " has mismatched asset types: " << tx.source_asset_type << " != " << tx.destination_asset_type);
|
||||
if (tx.type == cryptonote::transaction_type::CREATE_TOKEN) {
|
||||
// Check that the specific asset_type being created isn't already in our list of tokens
|
||||
std::map<std::string, cryptonote::token_metadata_t> mapTokens = m_db->get_tokens();
|
||||
std::string asset_type = "sal" + tx.token_metadata.asset_type;
|
||||
if (mapTokens.count(asset_type)) {
|
||||
MERROR("TX attempting to create '" + asset_type + "', which already exists");
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
// Validate token metadata //! check */
|
||||
CHECK_AND_ASSERT_MES(tx.token_metadata.token.type() == typeid(cryptonote::sal_token_t), false, "Invalid CREATE_TOKEN metadata type");
|
||||
cryptonote::sal_token_t token = boost::get<cryptonote::sal_token_t>(tx.token_metadata.token);
|
||||
CHECK_AND_ASSERT_MES(token.supply > 0 && token.supply <= (MONEY_SUPPLY / COIN), false, "Invalid SUPPLY value for CREATE_TOKEN");
|
||||
// Validate the amount burnt matches the token creation price
|
||||
CHECK_AND_ASSERT_MES(tx.amount_burnt == cryptonote::get_token_creation_price(tx.token_metadata.asset_type), false, "Invalid fee paid for CREATE_TOKEN");
|
||||
}
|
||||
|
||||
|
||||
// Check for invalid TX types
|
||||
if (tx.type == cryptonote::transaction_type::UNSET || tx.type > cryptonote::transaction_type::MAX) {
|
||||
MERROR("TX type `" + std::to_string(tx.type) + "' is not supported");
|
||||
@@ -3895,7 +4141,7 @@ bool Blockchain::have_tx_keyimges_as_spent(const transaction &tx) const
|
||||
bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_prefix_hash, const std::vector<std::vector<rct::ctkey>> &pubkeys, const uint8_t &hf_version)
|
||||
{
|
||||
PERF_TIMER(expand_transaction_2);
|
||||
CHECK_AND_ASSERT_MES(tx.version == 2 || tx.version == 3 || tx.version == 4, false, "Transaction version is not 2/3/4");
|
||||
CHECK_AND_ASSERT_MES(tx.version == 2 || tx.version == 3 || tx.version == 4 || tx.version == 5, false, "Transaction version is not 2/3/5");
|
||||
|
||||
rct::rctSig &rv = tx.rct_signatures;
|
||||
|
||||
@@ -4040,11 +4286,13 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
|
||||
const txin_to_key& in_to_key = boost::get<txin_to_key>(txin);
|
||||
if (in_to_key.amount == 0)
|
||||
{
|
||||
// always consider rct inputs mixable. Even if there's not enough rct
|
||||
// inputs on the chain to mix with, this is going to be the case for
|
||||
// just a few blocks right after the fork at most
|
||||
++n_mixable;
|
||||
}
|
||||
// don't always consider rct inputs mixable!
|
||||
uint64_t n_outputs = m_db->get_num_outputs_of_asset_type(in_to_key.asset_type);
|
||||
if (n_outputs <= min_mixin)
|
||||
++n_unmixable;
|
||||
else
|
||||
++n_mixable;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint64_t n_outputs = m_db->get_num_outputs(in_to_key.amount);
|
||||
@@ -4518,8 +4766,7 @@ void Blockchain::get_dynamic_base_fee_estimate_2021_scaling(uint64_t grace_block
|
||||
//------------------------------------------------------------------
|
||||
uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const
|
||||
{
|
||||
const uint8_t version = get_current_hard_fork_version();
|
||||
const uint64_t db_height = m_db->height();
|
||||
// const uint64_t db_height = m_db->height();
|
||||
|
||||
if (grace_blocks >= CRYPTONOTE_REWARD_BLOCKS_WINDOW)
|
||||
grace_blocks = CRYPTONOTE_REWARD_BLOCKS_WINDOW - 1;
|
||||
@@ -4670,7 +4917,7 @@ bool Blockchain::calculate_audit_payouts(const uint64_t start_height, std::vecto
|
||||
// Get the AUDIT TX information for matured staked coins
|
||||
std::vector<cryptonote::yield_tx_info> audit_entries;
|
||||
// We get the audit_tx_info from the block where they entered the chain
|
||||
int audit_tx_result = m_db->get_audit_tx_info(start_height, audit_entries);
|
||||
m_db->get_audit_tx_info(start_height, audit_entries);
|
||||
if (!audit_entries.size()) {
|
||||
|
||||
// Report error and abort
|
||||
@@ -4723,7 +4970,7 @@ bool Blockchain::calculate_yield_payouts(const uint64_t start_height, std::vecto
|
||||
// Get the YIELD TX information for matured staked coins
|
||||
std::vector<cryptonote::yield_tx_info_carrot> yield_entries;
|
||||
// We get the yield_tx_info from the block _before_ they started to accrue yield
|
||||
int yield_tx_result = m_db->get_carrot_yield_tx_info(start_height, yield_entries);
|
||||
m_db->get_carrot_yield_tx_info(start_height, yield_entries);
|
||||
if (!yield_entries.size()) {
|
||||
|
||||
// Report error and abort
|
||||
@@ -4789,7 +5036,7 @@ bool Blockchain::calculate_yield_payouts(const uint64_t start_height, std::vecto
|
||||
// Get the YIELD TX information for matured staked coins
|
||||
std::vector<cryptonote::yield_tx_info> yield_entries;
|
||||
// We get the yield_tx_info from the block _before_ they started to accrue yield
|
||||
int yield_tx_result = m_db->get_yield_tx_info(start_height, yield_entries);
|
||||
m_db->get_yield_tx_info(start_height, yield_entries);
|
||||
if (!yield_entries.size()) {
|
||||
|
||||
// Report error and abort
|
||||
@@ -4956,6 +5203,18 @@ bool Blockchain::get_ybi_entry(const uint64_t height, cryptonote::yield_block_in
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
bool Blockchain::is_tx_paid_for(const cryptonote::transaction& tx)
|
||||
{
|
||||
// Is it a TRANSFER?
|
||||
if (tx.type != cryptonote::transaction_type::TRANSFER)
|
||||
return true;
|
||||
// Is is a TOKEN?
|
||||
if (!cryptonote::is_asset_type_token(tx.source_asset_type))
|
||||
return true;
|
||||
// Has the TRANSFER TOKEN been paid for?
|
||||
return m_db->is_tx_paid_for(tx);
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
//TODO: revisit, has changed a bit on upstream
|
||||
bool Blockchain::check_block_timestamp(std::vector<uint64_t>& timestamps, const block& b, uint64_t& median_ts) const
|
||||
{
|
||||
@@ -5330,6 +5589,16 @@ leave:
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Check the TX type
|
||||
tx_verification_context tvc;
|
||||
if (!check_tx_type_and_version(tx, tvc)) {
|
||||
MERROR("Block with id: " << id << " failed to pass transaction type and version check for transaction id: " << tx_id);
|
||||
bvc.m_verifivation_failed = true;
|
||||
return_tx_to_pool(txs);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
TIME_MEASURE_FINISH(cc);
|
||||
t_checktx += cc;
|
||||
fee_summary += fee;
|
||||
@@ -5362,7 +5631,7 @@ leave:
|
||||
TIME_MEASURE_FINISH(vmt);
|
||||
|
||||
TIME_MEASURE_START(vpt);
|
||||
if(!validate_protocol_transaction(bl, blockchain_height, m_hardfork->get_current_version()))
|
||||
if(!validate_protocol_transaction(bl, blockchain_height, m_hardfork->get_current_version(), txs))
|
||||
{
|
||||
MERROR_VER("Block with id: " << id << " has incorrect protocol transaction");
|
||||
bvc.m_verifivation_failed = true;
|
||||
@@ -5406,7 +5675,7 @@ leave:
|
||||
|
||||
// Update the YBI cache data
|
||||
uint64_t yield_lock_period = cryptonote::get_config(m_nettype).STAKE_LOCK_PERIOD;
|
||||
uint64_t ybi_cache_expected_size = std::min(new_height, yield_lock_period);
|
||||
// uint64_t ybi_cache_expected_size = std::min(new_height, yield_lock_period);
|
||||
if (new_height > yield_lock_period) {
|
||||
if (m_yield_block_info_cache.count(new_height - yield_lock_period - 2) != 0) {
|
||||
m_yield_block_info_cache.erase(new_height - yield_lock_period - 2);
|
||||
@@ -5524,7 +5793,7 @@ uint64_t Blockchain::get_next_long_term_block_weight(uint64_t block_weight) cons
|
||||
const uint64_t db_height = m_db->height();
|
||||
const uint64_t nblocks = std::min<uint64_t>(m_long_term_block_weights_window, db_height);
|
||||
|
||||
const uint8_t hf_version = get_current_hard_fork_version();
|
||||
// const uint8_t hf_version = get_current_hard_fork_version();
|
||||
|
||||
if (db_height < CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE)
|
||||
return block_weight;
|
||||
@@ -6171,7 +6440,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
|
||||
} while(0); \
|
||||
|
||||
// generate sorted tables for all amounts and absolute offsets
|
||||
size_t tx_index = 0, block_index = 0;
|
||||
size_t tx_index = 0; //, block_index = 0;
|
||||
for (const auto &entry : blocks_entry)
|
||||
{
|
||||
if (m_cancel)
|
||||
@@ -6242,7 +6511,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
|
||||
|
||||
}
|
||||
}
|
||||
++block_index;
|
||||
//++block_index;
|
||||
}
|
||||
|
||||
// sort and remove duplicate absolute_offsets in offset_map
|
||||
@@ -6514,7 +6783,7 @@ void Blockchain::cancel()
|
||||
}
|
||||
|
||||
#if defined(PER_BLOCK_CHECKPOINT)
|
||||
static const char expected_block_hashes_hash[] = "a2a5a9bc5d606392ac5c14be55b90a92b8577b8ffdac5c63cc6174f41764c753";
|
||||
static const char expected_block_hashes_hash[] = "7e6c9efd949afb36bce062bbb720605f0dd67b03c0124e5167c527fa59c15ce3";
|
||||
void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints)
|
||||
{
|
||||
if (get_checkpoints == nullptr || !m_fast_sync)
|
||||
|
||||
@@ -726,6 +726,19 @@ namespace cryptonote
|
||||
*/
|
||||
bool check_tx_outputs(const transaction& tx, tx_verification_context &tvc) const;
|
||||
|
||||
/**
|
||||
* @brief check a transaction's input/output/TX asset types conform to current standards
|
||||
*
|
||||
* This function checks, for example at the time of this writing, that
|
||||
* the asset types are supported by the given TX for the current HF version on-chain.
|
||||
*
|
||||
* @param tx the transaction to check the asset types of
|
||||
* @param tvc returned info about tx verification
|
||||
*
|
||||
* @return false if the TX version and/or type is unsupported, otherwise true
|
||||
*/
|
||||
bool check_tx_asset_types(const transaction& tx, tx_verification_context &tvc) const;
|
||||
|
||||
/**
|
||||
* @brief check that a transaction's version & type conforms to current standards
|
||||
*
|
||||
@@ -1233,6 +1246,8 @@ namespace cryptonote
|
||||
*/
|
||||
bool validate_ybi_cache();
|
||||
|
||||
bool is_tx_paid_for(const cryptonote::transaction& tx);
|
||||
|
||||
#ifndef IN_UNIT_TESTS
|
||||
private:
|
||||
#endif
|
||||
@@ -1564,10 +1579,11 @@ namespace cryptonote
|
||||
* @param b the block containing the miner transaction to be validated
|
||||
* @param height the blockchain's weight
|
||||
* @param version hard fork version for that transaction
|
||||
* @param txs create_coin transactions in the block
|
||||
*
|
||||
* @return false if anything is found wrong with the protocol transaction, otherwise true
|
||||
*/
|
||||
bool validate_protocol_transaction(const block& b, uint64_t height, uint8_t hf_version);
|
||||
bool validate_protocol_transaction(const block& b, uint64_t height, uint8_t hf_version, const std::vector<std::pair<transaction, blobdata>>& txs = std::vector<std::pair<transaction, blobdata>>());
|
||||
|
||||
/**
|
||||
* @brief reverts the blockchain to its previous state following a failed switch
|
||||
|
||||
@@ -843,7 +843,8 @@ namespace cryptonote
|
||||
bad_semantics_txes_lock.unlock();
|
||||
|
||||
uint8_t hf_version = m_blockchain_storage.get_current_hard_fork_version();
|
||||
const size_t max_tx_version = hf_version >= HF_VERSION_CARROT ? TRANSACTION_VERSION_CARROT :
|
||||
const size_t max_tx_version = hf_version >= HF_VERSION_ENABLE_TOKENS ? TRANSACTION_VERSION_ENABLE_TOKENS :
|
||||
hf_version >= HF_VERSION_CARROT ? TRANSACTION_VERSION_CARROT :
|
||||
hf_version >= HF_VERSION_ENABLE_N_OUTS ? TRANSACTION_VERSION_N_OUTS :
|
||||
TRANSACTION_VERSION_2_OUTS;
|
||||
if (tx.version == 0 || tx.version > max_tx_version)
|
||||
@@ -904,6 +905,7 @@ namespace cryptonote
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::handle_incoming_tx_accumulated_batch(std::vector<tx_verification_batch_info> &tx_info, bool keeped_by_block)
|
||||
{
|
||||
const uint8_t hf_version = m_blockchain_storage.get_current_hard_fork_version();
|
||||
bool ret = true;
|
||||
if (keeped_by_block && get_blockchain_storage().is_within_compiled_block_hash_area())
|
||||
{
|
||||
@@ -925,7 +927,6 @@ namespace cryptonote
|
||||
if (tx_info[n].tx->version < 2)
|
||||
continue;
|
||||
const rct::rctSig &rv = tx_info[n].tx->rct_signatures;
|
||||
const uint8_t hf_version = m_blockchain_storage.get_current_hard_fork_version();
|
||||
if (hf_version >= HF_VERSION_CARROT) {
|
||||
if (rv.type != rct::RCTTypeNull && rv.type != rct::RCTTypeSalviumOne) {
|
||||
MERROR_VER("Invalid RCT type provided");
|
||||
@@ -950,7 +951,11 @@ namespace cryptonote
|
||||
tx_info[n].result = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
bool need_rollup = false;
|
||||
if (hf_version >= HF_VERSION_CARROT) {
|
||||
need_rollup = (tx_info[n].tx->source_asset_type != "SAL1");
|
||||
}
|
||||
switch (rv.type) {
|
||||
case rct::RCTTypeNull:
|
||||
// coinbase should not come here, so we reject for all other types
|
||||
@@ -965,7 +970,8 @@ namespace cryptonote
|
||||
tx_info[n].tx->type == cryptonote::transaction_type::CONVERT ? tx_info[n].tx->amount_burnt :
|
||||
tx_info[n].tx->type == cryptonote::transaction_type::STAKE ? tx_info[n].tx->amount_burnt :
|
||||
tx_info[n].tx->type == cryptonote::transaction_type::AUDIT ? tx_info[n].tx->amount_burnt :
|
||||
0
|
||||
0,
|
||||
need_rollup
|
||||
))
|
||||
{
|
||||
MERROR_VER("rct signature semantics check failed");
|
||||
@@ -1030,12 +1036,19 @@ namespace cryptonote
|
||||
continue;
|
||||
if (tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproof && tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproof2 && tx_info[n].tx->rct_signatures.type != rct::RCTTypeCLSAG && tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproofPlus && tx_info[n].tx->rct_signatures.type != rct::RCTTypeFullProofs && tx_info[n].tx->rct_signatures.type != rct::RCTTypeSalviumZero && tx_info[n].tx->rct_signatures.type != rct::RCTTypeSalviumOne)
|
||||
continue;
|
||||
bool need_rollup = false;
|
||||
if (hf_version >= HF_VERSION_CARROT) {
|
||||
need_rollup = (tx_info[n].tx->source_asset_type != "SAL1");
|
||||
}
|
||||
if (!rct::verRctSemanticsSimple(tx_info[n].tx->rct_signatures,
|
||||
tx_info[n].tx->type == cryptonote::transaction_type::BURN ? tx_info[n].tx->amount_burnt :
|
||||
tx_info[n].tx->type == cryptonote::transaction_type::CONVERT ? tx_info[n].tx->amount_burnt :
|
||||
tx_info[n].tx->type == cryptonote::transaction_type::STAKE ? tx_info[n].tx->amount_burnt :
|
||||
tx_info[n].tx->type == cryptonote::transaction_type::AUDIT ? tx_info[n].tx->amount_burnt :
|
||||
0
|
||||
tx_info[n].tx->type == cryptonote::transaction_type::CREATE_TOKEN ? tx_info[n].tx->amount_burnt :
|
||||
tx_info[n].tx->type == cryptonote::transaction_type::ROLLUP ? tx_info[n].tx->amount_burnt :
|
||||
0,
|
||||
need_rollup
|
||||
))
|
||||
{
|
||||
set_semantics_failed(tx_info[n].tx_hash);
|
||||
@@ -1249,6 +1262,13 @@ namespace cryptonote
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
if (hf_version >= HF_VERSION_ENABLE_TOKENS && !m_blockchain_storage.is_tx_paid_for(tx))
|
||||
{
|
||||
MERROR_VER("tx has not been paid for by ROLLUP");
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -346,7 +346,7 @@ namespace cryptonote
|
||||
|
||||
// Clear the TX contents
|
||||
tx.set_null();
|
||||
tx.version = 2;
|
||||
tx.version = (hard_fork_version >= HF_VERSION_ENABLE_TOKENS) ? 3 : 2;
|
||||
bool carrot_found = false;
|
||||
bool noncarrot_found = false;
|
||||
tx.type = cryptonote::transaction_type::PROTOCOL;
|
||||
@@ -356,7 +356,7 @@ namespace cryptonote
|
||||
if (entry.is_carrot) carrot_found = true;
|
||||
else noncarrot_found = true;
|
||||
}
|
||||
|
||||
|
||||
if (carrot_found && noncarrot_found) {
|
||||
LOG_ERROR("Cannot mix Carrot and non-Carrot outputs in the same protocol transaction");
|
||||
return false;
|
||||
@@ -365,7 +365,7 @@ namespace cryptonote
|
||||
LOG_ERROR("Carrot outputs found in CryptoNote protocol transaction");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (carrot_found || (!noncarrot_found && hard_fork_version >= HF_VERSION_CARROT))
|
||||
{
|
||||
// Ensure the TX version is correct
|
||||
@@ -381,7 +381,8 @@ namespace cryptonote
|
||||
// Build the proposal
|
||||
carrot::CarrotCoinbaseEnoteV1 e;
|
||||
e.onetime_address = entry.return_address;
|
||||
e.amount = entry.amount_burnt;
|
||||
// amount_minted for CREATE_TOKEN, amount_burnt for STAKE/AUDIT
|
||||
e.amount = (entry.type == cryptonote::transaction_type::CREATE_TOKEN) ? entry.amount_minted : entry.amount_burnt;
|
||||
e.asset_type = entry.destination_asset;
|
||||
e.view_tag = entry.return_view_tag;
|
||||
e.anchor_enc = entry.return_anchor_enc;
|
||||
@@ -391,6 +392,7 @@ namespace cryptonote
|
||||
}
|
||||
tx = store_carrot_to_coinbase_transaction_v1(enotes, std::string{}, cryptonote::transaction_type::PROTOCOL, height);
|
||||
tx.amount_burnt = 0;
|
||||
tx.version = (hard_fork_version >= HF_VERSION_ENABLE_TOKENS) ? TRANSACTION_VERSION_ENABLE_TOKENS : TRANSACTION_VERSION_CARROT;
|
||||
tx.invalidate_hashes();
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
@@ -539,7 +541,7 @@ namespace cryptonote
|
||||
}
|
||||
|
||||
tx = carrot::store_carrot_to_coinbase_transaction_v1(enotes, extra_nonce, cryptonote::transaction_type::MINER, height);
|
||||
|
||||
tx.version = (hard_fork_version >= HF_VERSION_ENABLE_TOKENS) ? TRANSACTION_VERSION_ENABLE_TOKENS : TRANSACTION_VERSION_CARROT;
|
||||
tx.amount_burnt = stake_reward;
|
||||
tx.invalidate_hashes();
|
||||
}
|
||||
@@ -597,6 +599,7 @@ namespace cryptonote
|
||||
case HF_VERSION_AUDIT2:
|
||||
case HF_VERSION_AUDIT2_PAUSE:
|
||||
case HF_VERSION_CARROT:
|
||||
case HF_VERSION_ENABLE_TOKENS:
|
||||
// SRCG: subtract 20% that will be rewarded to staking users
|
||||
CHECK_AND_ASSERT_MES(tx.amount_burnt == 0, false, "while creating outs: amount_burnt is nonzero");
|
||||
tx.amount_burnt = amount / 5;
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
#include "common/perf_timer.h"
|
||||
#include "crypto/hash.h"
|
||||
#include "crypto/duration.h"
|
||||
#include "common/debugging.h"
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "txpool"
|
||||
@@ -266,6 +267,20 @@ namespace cryptonote
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the TX asset types
|
||||
if (!m_blockchain.check_tx_asset_types(tx, tvc)) {
|
||||
LOG_PRINT_L1("Transaction with id= "<< id << " has invalid asset type(s)");
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// HERE BE DRAGONS!!!
|
||||
// Check that CREATE_TOKEN txs are unique in the pool
|
||||
if (tx.type == cryptonote::transaction_type::CREATE_TOKEN) {
|
||||
// TODO: ...scan the existing entries - requires either a registry of CREATE_TOKEN TXs, or to interatively process the pool
|
||||
}
|
||||
// LAND AHOY!!!
|
||||
|
||||
// assume failure during verification steps until success is certain
|
||||
tvc.m_verifivation_failed = true;
|
||||
@@ -1505,6 +1520,10 @@ namespace cryptonote
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that `transfer token` TXs have been paid for before accepting them into a block template
|
||||
if (!m_blockchain.is_tx_paid_for(lazy_tx()))
|
||||
return false;
|
||||
|
||||
//transaction is ok.
|
||||
return true;
|
||||
}
|
||||
@@ -1614,7 +1633,7 @@ namespace cryptonote
|
||||
//---------------------------------------------------------------------------------
|
||||
//---------------------------------------------------------------------------------
|
||||
//TODO: investigate whether boolean return is appropriate
|
||||
bool tx_memory_pool::fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, uint8_t version)
|
||||
bool tx_memory_pool::fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, std::vector<std::pair<protocol_tx_data_t, token_metadata_t>> &create_token_entries, uint8_t version)
|
||||
{
|
||||
CRITICAL_REGION_LOCAL(m_transactions_lock);
|
||||
CRITICAL_REGION_LOCAL1(m_blockchain);
|
||||
@@ -1630,7 +1649,6 @@ namespace cryptonote
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
size_t max_total_weight_pre_v5 = (130 * median_weight) / 100 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
|
||||
size_t max_total_weight_v5 = 2 * median_weight - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
|
||||
size_t max_total_weight = version >= 5 ? max_total_weight_v5 : max_total_weight_pre_v5;
|
||||
@@ -1638,6 +1656,15 @@ namespace cryptonote
|
||||
|
||||
LOG_PRINT_L2("Filling block template, median weight " << median_weight << ", " << m_txs_by_fee_and_receive_time.size() << " txes in the pool");
|
||||
|
||||
// Store list of tokens being created
|
||||
std::set<std::string> tokens;
|
||||
|
||||
// Get the list of already-created tokens that are in the DB
|
||||
std::map<std::string, cryptonote::token_metadata_t> mapTokens = m_blockchain.get_db().get_tokens();
|
||||
for (const auto &entry: mapTokens) {
|
||||
tokens.insert(entry.second.asset_type);
|
||||
}
|
||||
|
||||
LockedTXN lock(m_blockchain.get_db());
|
||||
|
||||
auto sorted_it = m_txs_by_fee_and_receive_time.begin();
|
||||
@@ -1760,6 +1787,39 @@ namespace cryptonote
|
||||
continue;
|
||||
}
|
||||
|
||||
// check for ROLLUP TXs
|
||||
if (tx.type == cryptonote::transaction_type::ROLLUP)
|
||||
{
|
||||
if (version < HF_VERSION_ENABLE_TOKENS) {
|
||||
LOG_PRINT_L2(" is a ROLLUP transaction before they are permitted - cannot be mined");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// check and include create token entries
|
||||
if (tx.type == transaction_type::CREATE_TOKEN)
|
||||
{
|
||||
if (version < HF_VERSION_ENABLE_TOKENS) {
|
||||
LOG_PRINT_L2(" is a CREATE_TOKEN transaction before they are permitted - cannot be mined");
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string asset_type = "sal" + tx.token_metadata.asset_type;
|
||||
if (!cryptonote::is_valid_asset_type(asset_type)) {
|
||||
LOG_PRINT_L2(" is a CREATE_TOKEN transaction with an invalid asset_type - cannot be mined");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check to see if token is already being created
|
||||
if (tokens.find(tx.token_metadata.asset_type) != tokens.end()) {
|
||||
LOG_PRINT_L2(" is a CREATE_TOKEN transaction with an asset_type already in the block template - cannot be mined");
|
||||
continue;
|
||||
}
|
||||
|
||||
tokens.insert(tx.token_metadata.asset_type);
|
||||
create_token_entries.push_back(std::make_pair(tx.protocol_tx_data, tx.token_metadata));
|
||||
}
|
||||
|
||||
bl.tx_hashes.push_back(sorted_it->second);
|
||||
total_weight += meta.weight;
|
||||
fee += meta.fee;
|
||||
|
||||
@@ -226,11 +226,12 @@ namespace cryptonote
|
||||
* @param total_weight return-by-reference the total weight of the new block
|
||||
* @param fee return-by-reference the total of fees from the included transactions
|
||||
* @param expected_reward return-by-reference the total reward awarded to the miner finding this block, including transaction fees
|
||||
* @param create_token_entries list of create coin entries to include in protocol tx
|
||||
* @param version hard fork version to use for consensus rules
|
||||
*
|
||||
* @return true
|
||||
*/
|
||||
bool fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, uint8_t version);
|
||||
bool fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, std::vector<std::pair<protocol_tx_data_t, token_metadata_t>> &create_token_entries, uint8_t version);
|
||||
|
||||
/**
|
||||
* @brief get a list of all transactions in the pool
|
||||
|
||||
@@ -187,7 +187,7 @@ bool ver_rct_non_semantics_simple_cached
|
||||
// mixring. Future versions of the protocol may differ in this regard, but if this assumptions
|
||||
// holds true in the future, enable the verification hash by modifying the `untested_tx`
|
||||
// condition below.
|
||||
const bool untested_tx = tx.version > 4 || tx.rct_signatures.type > rct::RCTTypeSalviumOne;
|
||||
const bool untested_tx = tx.version > TRANSACTION_VERSION_ENABLE_TOKENS || tx.rct_signatures.type > rct::RCTTypeSalviumOne;
|
||||
VER_ASSERT(!untested_tx, "Unknown TX type. Make sure RCT cache works correctly with this type and then enable it in the code here.");
|
||||
|
||||
// Don't cache older (or newer) rctSig types
|
||||
|
||||
@@ -54,6 +54,8 @@ namespace cryptonote
|
||||
STAKE = 6,
|
||||
RETURN = 7,
|
||||
AUDIT = 8,
|
||||
MAX = 8
|
||||
CREATE_TOKEN = 9,
|
||||
ROLLUP = 10,
|
||||
MAX = 10
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1355,7 +1355,7 @@ bool t_rpc_command_executor::print_transaction_pool_stats() {
|
||||
bool t_rpc_command_executor::start_mining(cryptonote::account_public_address address, uint64_t num_threads, cryptonote::network_type nettype, bool do_background_mining, bool ignore_battery, bool is_carrot) {
|
||||
cryptonote::COMMAND_RPC_START_MINING::request req;
|
||||
cryptonote::COMMAND_RPC_START_MINING::response res;
|
||||
req.miner_address = cryptonote::get_account_address_as_str(nettype, false, address, is_carrot);
|
||||
req.miner_address = cryptonote::get_account_address_as_str(nettype, false, address);
|
||||
req.threads_count = num_threads;
|
||||
req.do_background_mining = do_background_mining;
|
||||
req.ignore_battery = ignore_battery;
|
||||
|
||||
@@ -202,6 +202,10 @@ namespace hw {
|
||||
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
|
||||
crypto::signature &sig) = 0;
|
||||
|
||||
virtual void generate_carrot_tx_proof(const crypto::hash &prefix_hash,
|
||||
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
|
||||
const crypto::secret_key &a, crypto::signature &sig) = 0;
|
||||
|
||||
virtual bool open_tx(crypto::secret_key &tx_key) = 0;
|
||||
|
||||
virtual void get_transaction_prefix_hash(const cryptonote::transaction_prefix& tx, crypto::hash& h) = 0;
|
||||
|
||||
@@ -282,6 +282,12 @@ namespace hw {
|
||||
crypto::generate_tx_proof(prefix_hash, R, A, B, D, r, sig);
|
||||
}
|
||||
|
||||
void device_default::generate_carrot_tx_proof(const crypto::hash &prefix_hash,
|
||||
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
|
||||
const crypto::secret_key &a, crypto::signature &sig) {
|
||||
crypto::generate_carrot_tx_proof(prefix_hash, R, A, B, D, r, a, sig);
|
||||
}
|
||||
|
||||
bool device_default::open_tx(crypto::secret_key &tx_key) {
|
||||
cryptonote::keypair txkey = cryptonote::keypair::generate(*this);
|
||||
tx_key = txkey.sec;
|
||||
|
||||
@@ -112,6 +112,10 @@ namespace hw {
|
||||
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
|
||||
crypto::signature &sig) override;
|
||||
|
||||
void generate_carrot_tx_proof(const crypto::hash &prefix_hash,
|
||||
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
|
||||
const crypto::secret_key &a, crypto::signature &sig) override;
|
||||
|
||||
bool open_tx(crypto::secret_key &tx_key) override;
|
||||
void get_transaction_prefix_hash(const cryptonote::transaction_prefix& tx, crypto::hash& h) override;
|
||||
|
||||
|
||||
@@ -1433,6 +1433,14 @@ namespace hw {
|
||||
#endif
|
||||
}
|
||||
|
||||
void device_ledger::generate_carrot_tx_proof(const crypto::hash &prefix_hash,
|
||||
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
|
||||
const crypto::secret_key &a, crypto::signature &sig) {
|
||||
// to-do: For now, carrot tx proofs are not supported
|
||||
AUTO_LOCK_CMD();
|
||||
crypto::generate_carrot_tx_proof(prefix_hash, R, A, B, D, r, a, sig);
|
||||
}
|
||||
|
||||
bool device_ledger::open_tx(crypto::secret_key &tx_key) {
|
||||
AUTO_LOCK_CMD();
|
||||
this->lock();
|
||||
|
||||
@@ -253,6 +253,10 @@ namespace hw {
|
||||
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
|
||||
crypto::signature &sig) override;
|
||||
|
||||
void generate_carrot_tx_proof(const crypto::hash &prefix_hash,
|
||||
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
|
||||
const crypto::secret_key &a, crypto::signature &sig) override;
|
||||
|
||||
bool open_tx(crypto::secret_key &tx_key) override;
|
||||
|
||||
void get_transaction_prefix_hash(const cryptonote::transaction_prefix& tx, crypto::hash& h) override;
|
||||
|
||||
@@ -95,6 +95,9 @@ const hardfork_t testnet_hard_forks[] = {
|
||||
|
||||
// version 10 Carrot - including treasury mint - starts from block 1100
|
||||
{10, 1100, 0, 1739780005 },
|
||||
|
||||
// version 11
|
||||
{11, 1200, 0, 1739880005 },
|
||||
};
|
||||
const size_t num_testnet_hard_forks = sizeof(testnet_hard_forks) / sizeof(testnet_hard_forks[0]);
|
||||
const uint64_t testnet_hard_fork_version_1_till = ((uint64_t)-1);
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
#include <cstdint> // For uint8_t, etc.
|
||||
#include <cstring> // For memcpy
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -74,4 +76,122 @@ namespace oracle {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class asset_type_counts_v2
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
// Fields
|
||||
std::map<uint32_t, uint64_t> asset_counts;
|
||||
|
||||
asset_type_counts_v2() noexcept
|
||||
: asset_counts{}
|
||||
{
|
||||
}
|
||||
|
||||
uint64_t operator[](const uint32_t asset_type_id) const noexcept
|
||||
{
|
||||
if (asset_counts.count(asset_type_id) == 0)
|
||||
return 0;
|
||||
else
|
||||
return asset_counts.at(asset_type_id);
|
||||
}
|
||||
|
||||
bool add(const uint32_t asset_type_id, const uint64_t amount)
|
||||
{
|
||||
if (asset_counts.count(asset_type_id) == 0)
|
||||
return false;
|
||||
asset_counts[asset_type_id] += amount;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool add_asset_type(const uint32_t asset_type_id)
|
||||
{
|
||||
if (asset_counts.count(asset_type_id) != 0)
|
||||
return false;
|
||||
asset_counts[asset_type_id] = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::map<uint32_t, uint64_t> get()
|
||||
{
|
||||
return asset_counts;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> get_asset_types() const noexcept
|
||||
{
|
||||
std::vector<uint32_t> asset_type_ids;
|
||||
for (const auto& entry: asset_counts) {
|
||||
asset_type_ids.push_back(entry.first);
|
||||
}
|
||||
return asset_type_ids;
|
||||
}
|
||||
|
||||
bool has_asset_type(const uint32_t asset_type_id) const noexcept
|
||||
{
|
||||
return asset_counts.count(asset_type_id) == 1;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> serialize() const noexcept {
|
||||
constexpr uint8_t version = 1; // Bump if format changes
|
||||
uint32_t sz = static_cast<uint32_t>(asset_counts.size());
|
||||
size_t total_size = sizeof(uint8_t) + sizeof(uint32_t) + (sz * (sizeof(uint32_t) + sizeof(uint64_t)));
|
||||
std::vector<uint8_t> buf(total_size);
|
||||
uint8_t* ptr = buf.data();
|
||||
|
||||
std::memcpy(ptr, &version, sizeof(uint8_t));
|
||||
ptr += sizeof(uint8_t);
|
||||
|
||||
std::memcpy(ptr, &sz, sizeof(uint32_t));
|
||||
ptr += sizeof(uint32_t);
|
||||
|
||||
for (const auto& p : asset_counts) { // Iterates in sorted order
|
||||
std::memcpy(ptr, &p.first, sizeof(uint32_t));
|
||||
ptr += sizeof(uint32_t);
|
||||
std::memcpy(ptr, &p.second, sizeof(uint64_t));
|
||||
ptr += sizeof(uint64_t);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
void deserialize(const uint8_t* data, size_t len) {
|
||||
if (len < sizeof(uint32_t)) {
|
||||
throw std::runtime_error("Invalid serialized data");
|
||||
}
|
||||
|
||||
uint8_t version;
|
||||
std::memcpy(&version, data, sizeof(uint8_t));
|
||||
data += sizeof(uint8_t);
|
||||
len -= sizeof(uint8_t);
|
||||
|
||||
if (version == 1) {
|
||||
if (len < sizeof(uint32_t)) {
|
||||
throw std::runtime_error("Invalid serialized data");
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error("Unsupported version");
|
||||
}
|
||||
|
||||
uint32_t sz;
|
||||
std::memcpy(&sz, data, sizeof(uint32_t));
|
||||
data += sizeof(uint32_t);
|
||||
len -= sizeof(uint32_t);
|
||||
|
||||
if (len != sz * (sizeof(uint32_t) + sizeof(uint64_t))) {
|
||||
throw std::runtime_error("Invalid serialized data length");
|
||||
}
|
||||
|
||||
asset_counts.clear();
|
||||
for (uint32_t i = 0; i < sz; ++i) {
|
||||
uint32_t k;
|
||||
uint64_t v;
|
||||
std::memcpy(&k, data, sizeof(uint32_t));
|
||||
data += sizeof(uint32_t);
|
||||
std::memcpy(&v, data, sizeof(uint64_t));
|
||||
data += sizeof(uint64_t);
|
||||
asset_counts[k] = v; // Map handles sorting/uniquness
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
+1
-19
@@ -881,12 +881,6 @@ namespace nodetool
|
||||
if (m_nettype == cryptonote::MAINNET)
|
||||
{
|
||||
return {
|
||||
"xwvz3ekocr3dkyxfkmgm2hvbpzx2ysqmaxgter7znnqrhoicygkfswid.onion:18083",
|
||||
"4pixvbejrvihnkxmduo2agsnmc3rrulrqc7s3cbwwrep6h6hrzsibeqd.onion:18083",
|
||||
"zbjkbsxc5munw3qusl7j2hpcmikhqocdf4pqhnhtpzw5nt5jrmofptid.onion:18083",
|
||||
"qz43zul2x56jexzoqgkx2trzwcfnr6l3hbtfcfx54g4r3eahy3bssjyd.onion:18083",
|
||||
"plowsof3t5hogddwabaeiyrno25efmzfxyro2vligremt7sxpsclfaid.onion:18083",
|
||||
"plowsoffjexmxalw73tkjmf422gq6575fc7vicuu4javzn2ynnte6tyd.onion:18083",
|
||||
};
|
||||
}
|
||||
return {};
|
||||
@@ -894,10 +888,6 @@ namespace nodetool
|
||||
if (m_nettype == cryptonote::MAINNET)
|
||||
{
|
||||
return {
|
||||
"s3l6ke4ed3df466khuebb4poienoingwof7oxtbo6j4n56sghe3a.b32.i2p:18080",
|
||||
"sel36x6fibfzujwvt4hf5gxolz6kd3jpvbjqg6o3ud2xtionyl2q.b32.i2p:18080",
|
||||
"uqj3aphckqtjsitz7kxx5flqpwjlq5ppr3chazfued7xucv3nheq.b32.i2p:18080",
|
||||
"vdmnehdjkpkg57nthgnjfuaqgku673r5bpbqg56ix6fyqoywgqrq.b32.i2p:18080",
|
||||
};
|
||||
}
|
||||
return {};
|
||||
@@ -2054,20 +2044,13 @@ namespace nodetool
|
||||
return true;
|
||||
|
||||
static const std::vector<std::string> dns_urls = {
|
||||
"blocklist.moneropulse.se"
|
||||
, "blocklist.moneropulse.org"
|
||||
, "blocklist.moneropulse.net"
|
||||
, "blocklist.moneropulse.no"
|
||||
, "blocklist.moneropulse.fr"
|
||||
, "blocklist.moneropulse.de"
|
||||
, "blocklist.moneropulse.ch"
|
||||
};
|
||||
|
||||
std::vector<std::string> records;
|
||||
if (!tools::dns_utils::load_txt_records_from_dns(records, dns_urls))
|
||||
return true;
|
||||
|
||||
unsigned good = 0, bad = 0;
|
||||
unsigned good = 0;
|
||||
for (const auto& record : records)
|
||||
{
|
||||
std::vector<std::string> ips;
|
||||
@@ -2091,7 +2074,6 @@ namespace nodetool
|
||||
continue;
|
||||
}
|
||||
MWARNING("Invalid IP address or subnet from DNS blocklist: " << ip << " - " << parsed_addr.error());
|
||||
++bad;
|
||||
}
|
||||
}
|
||||
if (good > 0)
|
||||
|
||||
@@ -1961,7 +1961,7 @@ namespace rct {
|
||||
|
||||
//ver RingCT simple
|
||||
//assumes only post-rct style inputs (at least for max anonymity)
|
||||
bool verRctSemanticsSimple(const rctSig & rv, const uint64_t amount_burnt)
|
||||
bool verRctSemanticsSimple(const rctSig & rv, const uint64_t amount_burnt, const bool fee_paid_for_in_rollup)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -2027,8 +2027,10 @@ namespace rct {
|
||||
}
|
||||
key sumOutpks = addKeys(masks);
|
||||
DP(sumOutpks);
|
||||
const key txnFeeKey = scalarmultH(d2h(rv.txnFee));
|
||||
addKeys(sumOutpks, txnFeeKey, sumOutpks);
|
||||
if (!fee_paid_for_in_rollup) {
|
||||
const key txnFeeKey = scalarmultH(d2h(rv.txnFee));
|
||||
addKeys(sumOutpks, txnFeeKey, sumOutpks);
|
||||
}
|
||||
|
||||
const key txnAmountBurntKey = scalarmultH(d2h(amount_burnt));
|
||||
addKeys(sumOutpks, txnAmountBurntKey, sumOutpks);
|
||||
|
||||
@@ -202,9 +202,11 @@ namespace rct {
|
||||
);
|
||||
bool verRct(const rctSig & rv, bool semantics);
|
||||
static inline bool verRct(const rctSig & rv) { return verRct(rv, true) && verRct(rv, false); }
|
||||
bool verRctSemanticsSimple(const rctSig & rv, const uint64_t amount_burnt=0);
|
||||
bool verRctSemanticsSimple(const rctSig & rv, const uint64_t amount_burnt=0, const bool fee_paid_for_in_rollup=false);
|
||||
bool verRctNonSemanticsSimple(const rctSig & rv);
|
||||
static inline bool verRctSimple(const rctSig & rv, const uint64_t amount_burnt=0) { return verRctSemanticsSimple(rv, amount_burnt) && verRctNonSemanticsSimple(rv); }
|
||||
static inline bool verRctSimple(const rctSig & rv, const uint64_t amount_burnt=0, const bool fee_paid_for_in_rollup=false) {
|
||||
return verRctSemanticsSimple(rv, amount_burnt, fee_paid_for_in_rollup) && verRctNonSemanticsSimple(rv);
|
||||
}
|
||||
xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask, hw::device &hwdev);
|
||||
xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, hw::device &hwdev);
|
||||
xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key & mask, hw::device &hwdev);
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
#include <regex>
|
||||
#include <boost/preprocessor/stringize.hpp>
|
||||
#include <boost/uuid/nil_generator.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
@@ -1879,12 +1880,29 @@ namespace cryptonote
|
||||
}
|
||||
blobdata block_blob = t_serializable_object_to_blob(b);
|
||||
crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(b.miner_tx);
|
||||
const std::vector<crypto::public_key> additional_tx_pub_keys = cryptonote::get_additional_tx_pub_keys_from_extra(b.miner_tx);
|
||||
if(tx_pub_key == crypto::null_pkey)
|
||||
{
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
|
||||
error_resp.message = "Internal error: failed to create block template";
|
||||
LOG_ERROR("Failed to get tx pub key in coinbase extra");
|
||||
return false;
|
||||
// Check for Carrot treasury payout
|
||||
const uint8_t hf_version = m_core.get_blockchain_storage().get_current_hard_fork_version();
|
||||
if (hf_version >= HF_VERSION_CARROT && b.miner_tx.vout.size() == 2) {
|
||||
|
||||
const auto treasury_payout_data = get_config(nettype()).TREASURY_SAL1_MINT_OUTPUT_DATA;
|
||||
const bool treasury_payout_exists = (treasury_payout_data.count(height) == 1);
|
||||
if (!treasury_payout_exists) {
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
|
||||
error_resp.message = "Internal error: failed to create block template (missing treasury payout)";
|
||||
LOG_ERROR("Failed to get tx pub key in coinbase extra (missing treasury payout)");
|
||||
return false;
|
||||
}
|
||||
tx_pub_key = additional_tx_pub_keys.back();
|
||||
|
||||
} else {
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
|
||||
error_resp.message = "Internal error: failed to create block template";
|
||||
LOG_ERROR("Failed to get tx pub key in coinbase extra");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t next_height;
|
||||
@@ -3045,6 +3063,52 @@ namespace cryptonote
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
bool core_rpc_server::on_get_tokens(const COMMAND_RPC_GET_TOKENS::request& req, COMMAND_RPC_GET_TOKENS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
|
||||
{
|
||||
PERF_TIMER(on_get_tokens);
|
||||
std::map<std::string, cryptonote::token_metadata_t> tokens = m_core.get_blockchain_storage().get_db().get_tokens();
|
||||
for (const auto &token: tokens) {
|
||||
if (req.filter == "") {
|
||||
// Match anything
|
||||
res.tokens.push_back(token.first);
|
||||
} else if (token.first.find(req.filter) != std::string::npos) {
|
||||
// Match ticker
|
||||
res.tokens.push_back(token.first);
|
||||
} else if (token.second.token.type() == typeid(cryptonote::sal_token_t)) {
|
||||
// Check SAL tokens
|
||||
if (boost::get<cryptonote::sal_token_t>(token.second.token).name.find(req.filter) != std::string::npos) {
|
||||
// Match name
|
||||
res.tokens.push_back(token.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
res.status = CORE_RPC_STATUS_OK;
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
bool core_rpc_server::on_get_token_info(const COMMAND_RPC_GET_TOKEN_INFO::request& req, COMMAND_RPC_GET_TOKEN_INFO::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
|
||||
{
|
||||
PERF_TIMER(on_token_info);
|
||||
std::map<std::string, cryptonote::token_metadata_t> tokens = m_core.get_blockchain_storage().get_db().get_tokens();
|
||||
if (tokens.count(req.asset_type) == 0) {
|
||||
res.status = "Token not found";
|
||||
return true;
|
||||
}
|
||||
|
||||
res.token = tokens[req.asset_type];
|
||||
|
||||
if (res.token.token.type() == typeid(cryptonote::erc_token_t)) {
|
||||
res.token_type = TOKEN_TYPE_ERC20;
|
||||
res.erc_token = boost::get<cryptonote::erc_token_t>(res.token.token);
|
||||
} else if (res.token.token.type() == typeid(cryptonote::sal_token_t)) {
|
||||
res.token_type = TOKEN_TYPE_SAL;
|
||||
res.sal_token = boost::get<cryptonote::sal_token_t>(res.token.token);
|
||||
}
|
||||
|
||||
res.status = CORE_RPC_STATUS_OK;
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
bool core_rpc_server::on_get_yield_info(const COMMAND_RPC_GET_YIELD_INFO::request& req, COMMAND_RPC_GET_YIELD_INFO::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
|
||||
{
|
||||
CHECK_CORE_READY();
|
||||
@@ -3108,7 +3172,6 @@ namespace cryptonote
|
||||
|
||||
CHECK_PAYMENT(req, res, COST_PER_FEE_ESTIMATE);
|
||||
|
||||
const uint8_t version = m_core.get_blockchain_storage().get_current_hard_fork_version();
|
||||
m_core.get_blockchain_storage().get_dynamic_base_fee_estimate_2021_scaling(req.grace_blocks, res.fees);
|
||||
res.fee = res.fees[0];
|
||||
res.quantization_mask = Blockchain::get_fee_quantization_mask();
|
||||
|
||||
@@ -175,6 +175,8 @@ namespace cryptonote
|
||||
MAP_JON_RPC_WE_IF("get_coinbase_tx_sum", on_get_coinbase_tx_sum, COMMAND_RPC_GET_COINBASE_TX_SUM, !m_restricted)
|
||||
MAP_JON_RPC_WE("get_supply_info", on_get_supply_info, COMMAND_RPC_GET_SUPPLY_INFO)
|
||||
MAP_JON_RPC_WE("get_yield_info", on_get_yield_info, COMMAND_RPC_GET_YIELD_INFO)
|
||||
MAP_JON_RPC_WE("get_tokens", on_get_tokens, COMMAND_RPC_GET_TOKENS)
|
||||
MAP_JON_RPC_WE("get_token_info", on_get_token_info, COMMAND_RPC_GET_TOKEN_INFO)
|
||||
MAP_JON_RPC_WE("get_fee_estimate", on_get_base_fee_estimate, COMMAND_RPC_GET_BASE_FEE_ESTIMATE)
|
||||
MAP_JON_RPC_WE_IF("get_alternate_chains",on_get_alternate_chains, COMMAND_RPC_GET_ALTERNATE_CHAINS, !m_restricted)
|
||||
MAP_JON_RPC_WE_IF("relay_tx", on_relay_tx, COMMAND_RPC_RELAY_TX, !m_restricted)
|
||||
@@ -254,6 +256,8 @@ namespace cryptonote
|
||||
bool on_get_coinbase_tx_sum(const COMMAND_RPC_GET_COINBASE_TX_SUM::request& req, COMMAND_RPC_GET_COINBASE_TX_SUM::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
|
||||
bool on_get_supply_info(const COMMAND_RPC_GET_SUPPLY_INFO::request& req, COMMAND_RPC_GET_SUPPLY_INFO::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
|
||||
bool on_get_yield_info(const COMMAND_RPC_GET_YIELD_INFO::request& req, COMMAND_RPC_GET_YIELD_INFO::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
|
||||
bool on_get_tokens(const COMMAND_RPC_GET_TOKENS::request& req, COMMAND_RPC_GET_TOKENS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
|
||||
bool on_get_token_info(const COMMAND_RPC_GET_TOKEN_INFO::request& req, COMMAND_RPC_GET_TOKEN_INFO::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
|
||||
bool on_get_base_fee_estimate(const COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request& req, COMMAND_RPC_GET_BASE_FEE_ESTIMATE::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
|
||||
bool on_get_alternate_chains(const COMMAND_RPC_GET_ALTERNATE_CHAINS::request& req, COMMAND_RPC_GET_ALTERNATE_CHAINS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
|
||||
bool on_relay_tx(const COMMAND_RPC_RELAY_TX::request& req, COMMAND_RPC_RELAY_TX::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
|
||||
|
||||
@@ -1488,6 +1488,76 @@ namespace cryptonote
|
||||
typedef epee::misc_utils::struct_init<response_t> response;
|
||||
};
|
||||
|
||||
struct COMMAND_RPC_GET_TOKENS
|
||||
{
|
||||
struct request_t
|
||||
{
|
||||
std::string filter;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE_OPT(filter, (std::string)"")
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<request_t> request;
|
||||
|
||||
struct response_t
|
||||
{
|
||||
std::string status;
|
||||
std::vector<std::string> tokens;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(status)
|
||||
KV_SERIALIZE(tokens)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<response_t> response;
|
||||
};
|
||||
|
||||
struct COMMAND_RPC_GET_TOKEN_INFO
|
||||
{
|
||||
struct request_t
|
||||
{
|
||||
std::string asset_type;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(asset_type)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<request_t> request;
|
||||
|
||||
struct response_t
|
||||
{
|
||||
std::string status;
|
||||
std::string asset_type;
|
||||
uint8_t token_type; // erc20 or sal
|
||||
cryptonote::token_metadata_t token;
|
||||
cryptonote::erc_token_t erc_token;
|
||||
cryptonote::sal_token_t sal_token;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(status)
|
||||
KV_SERIALIZE(token_type)
|
||||
KV_SERIALIZE(token.version)
|
||||
KV_SERIALIZE(token.asset_type)
|
||||
if (token_type == TOKEN_TYPE_ERC20) {
|
||||
KV_SERIALIZE(erc_token)
|
||||
token.token = erc_token;
|
||||
} else {
|
||||
KV_SERIALIZE(sal_token)
|
||||
token.token = sal_token;
|
||||
}
|
||||
END_KV_SERIALIZE_MAP()
|
||||
//BEGIN_KV_SERIALIZE_MAP()
|
||||
//KV_SERIALIZE(status)
|
||||
//KV_SERIALIZE(token)
|
||||
//KV_SERIALIZE(asset_type)
|
||||
//KV_SERIALIZE(sal_token)
|
||||
//KV_SERIALIZE(erc_token)
|
||||
//END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<response_t> response;
|
||||
};
|
||||
|
||||
struct peer {
|
||||
uint64_t id;
|
||||
std::string host;
|
||||
|
||||
@@ -277,9 +277,8 @@ void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::t
|
||||
INSERT_INTO_JSON_OBJECT(dest, return_address_list, tx.return_address_list);
|
||||
INSERT_INTO_JSON_OBJECT(dest, return_address_change_mask, tx.return_address_change_mask);
|
||||
} else {
|
||||
if (tx.type == cryptonote::transaction_type::STAKE &&
|
||||
tx.version >= TRANSACTION_VERSION_CARROT)
|
||||
{
|
||||
if ((tx.type == cryptonote::transaction_type::STAKE || tx.type == cryptonote::transaction_type::CREATE_TOKEN) &&
|
||||
(tx.version >= TRANSACTION_VERSION_CARROT)) {
|
||||
INSERT_INTO_JSON_OBJECT(dest, protocol_tx_data, tx.protocol_tx_data);
|
||||
} else {
|
||||
INSERT_INTO_JSON_OBJECT(dest, return_address, tx.return_address);
|
||||
@@ -290,6 +289,16 @@ void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::t
|
||||
INSERT_INTO_JSON_OBJECT(dest, destination_asset_type, tx.destination_asset_type);
|
||||
INSERT_INTO_JSON_OBJECT(dest, amount_slippage_limit, tx.amount_slippage_limit);
|
||||
}
|
||||
if (tx.version >= TRANSACTION_VERSION_ENABLE_TOKENS) {
|
||||
if (tx.type == cryptonote::transaction_type::CREATE_TOKEN) {
|
||||
INSERT_INTO_JSON_OBJECT(dest, token_metadata, tx.token_metadata);
|
||||
} else if (tx.type == cryptonote::transaction_type::TRANSFER) {
|
||||
INSERT_INTO_JSON_OBJECT(dest, rollup_binding_tag, tx.rollup_binding_tag);
|
||||
} else if (tx.type == cryptonote::transaction_type::ROLLUP) {
|
||||
INSERT_INTO_JSON_OBJECT(dest, rollup_binding_tag, tx.rollup_binding_tag);
|
||||
INSERT_INTO_JSON_OBJECT(dest, layer2_rollup_data, tx.layer2_rollup_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!tx.pruned)
|
||||
{
|
||||
@@ -326,9 +335,8 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::transaction& tx)
|
||||
GET_FROM_JSON_OBJECT(val, tx.return_address_list, return_address_list);
|
||||
GET_FROM_JSON_OBJECT(val, tx.return_address_change_mask, return_address_change_mask);
|
||||
} else {
|
||||
if (tx.type == cryptonote::transaction_type::STAKE &&
|
||||
tx.version >= TRANSACTION_VERSION_CARROT)
|
||||
{
|
||||
if ((tx.type == cryptonote::transaction_type::STAKE || tx.type == cryptonote::transaction_type::CREATE_TOKEN) &&
|
||||
(tx.version >= TRANSACTION_VERSION_CARROT)) {
|
||||
GET_FROM_JSON_OBJECT(val, tx.protocol_tx_data, protocol_tx_data);
|
||||
} else {
|
||||
GET_FROM_JSON_OBJECT(val, tx.return_address, return_address);
|
||||
@@ -339,6 +347,16 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::transaction& tx)
|
||||
GET_FROM_JSON_OBJECT(val, tx.destination_asset_type, destination_asset_type);
|
||||
GET_FROM_JSON_OBJECT(val, tx.amount_slippage_limit, amount_slippage_limit);
|
||||
}
|
||||
if (tx.version >= TRANSACTION_VERSION_ENABLE_TOKENS) {
|
||||
if (tx.type == cryptonote::transaction_type::CREATE_TOKEN) {
|
||||
GET_FROM_JSON_OBJECT(val, tx.token_metadata, token_metadata);
|
||||
} else if (tx.type == cryptonote::transaction_type::TRANSFER) {
|
||||
GET_FROM_JSON_OBJECT(val, tx.rollup_binding_tag, rollup_binding_tag);
|
||||
} else if (tx.type == cryptonote::transaction_type::ROLLUP) {
|
||||
GET_FROM_JSON_OBJECT(val, tx.rollup_binding_tag, rollup_binding_tag);
|
||||
GET_FROM_JSON_OBJECT(val, tx.layer2_rollup_data, layer2_rollup_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
GET_FROM_JSON_OBJECT(val, tx.rct_signatures, ringct);
|
||||
|
||||
@@ -518,6 +536,141 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_scripthash&
|
||||
}
|
||||
}
|
||||
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::erc_token_t& erc_token)
|
||||
{
|
||||
dest.StartObject();
|
||||
|
||||
INSERT_INTO_JSON_OBJECT(dest, version, erc_token.version);
|
||||
INSERT_INTO_JSON_OBJECT(dest, contract_address, erc_token.contract_address);
|
||||
INSERT_INTO_JSON_OBJECT(dest, lockbox_address, erc_token.lockbox_address);
|
||||
INSERT_INTO_JSON_OBJECT(dest, ticker, erc_token.ticker);
|
||||
INSERT_INTO_JSON_OBJECT(dest, erc20_asset_id, erc_token.erc20_asset_id);
|
||||
|
||||
dest.EndObject();
|
||||
}
|
||||
|
||||
|
||||
void fromJsonValue(const rapidjson::Value& val, cryptonote::erc_token_t& erc_token)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, erc_token.version, version);
|
||||
GET_FROM_JSON_OBJECT(val, erc_token.contract_address, contract_address);
|
||||
GET_FROM_JSON_OBJECT(val, erc_token.lockbox_address, lockbox_address);
|
||||
GET_FROM_JSON_OBJECT(val, erc_token.ticker, ticker);
|
||||
GET_FROM_JSON_OBJECT(val, erc_token.erc20_asset_id, erc20_asset_id);
|
||||
}
|
||||
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::sal_token_t& sal_token)
|
||||
{
|
||||
dest.StartObject();
|
||||
|
||||
INSERT_INTO_JSON_OBJECT(dest, version, sal_token.version);
|
||||
INSERT_INTO_JSON_OBJECT(dest, supply, sal_token.supply);
|
||||
INSERT_INTO_JSON_OBJECT(dest, size, sal_token.size);
|
||||
INSERT_INTO_JSON_OBJECT(dest, name, sal_token.name);
|
||||
INSERT_INTO_JSON_OBJECT(dest, url, sal_token.url);
|
||||
INSERT_INTO_JSON_OBJECT(dest, signature, sal_token.signature);
|
||||
|
||||
dest.EndObject();
|
||||
}
|
||||
|
||||
|
||||
void fromJsonValue(const rapidjson::Value& val, cryptonote::sal_token_t& sal_token)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, sal_token.version, version);
|
||||
GET_FROM_JSON_OBJECT(val, sal_token.supply, supply);
|
||||
GET_FROM_JSON_OBJECT(val, sal_token.size, size);
|
||||
GET_FROM_JSON_OBJECT(val, sal_token.name, name);
|
||||
GET_FROM_JSON_OBJECT(val, sal_token.url, url);
|
||||
GET_FROM_JSON_OBJECT(val, sal_token.signature, signature);
|
||||
}
|
||||
|
||||
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::token_v& token)
|
||||
{
|
||||
dest.StartObject();
|
||||
struct add_token
|
||||
{
|
||||
using result_type = void;
|
||||
|
||||
rapidjson::Writer<epee::byte_stream>& dest;
|
||||
|
||||
void operator()(cryptonote::erc_token_t const& token) const
|
||||
{
|
||||
INSERT_INTO_JSON_OBJECT(dest, erc_token, token);
|
||||
}
|
||||
void operator()(cryptonote::sal_token_t const& token) const
|
||||
{
|
||||
INSERT_INTO_JSON_OBJECT(dest, sal_token, token);
|
||||
}
|
||||
};
|
||||
boost::apply_visitor(add_token{dest}, token);
|
||||
dest.EndObject();
|
||||
}
|
||||
|
||||
|
||||
void fromJsonValue(const rapidjson::Value& val, cryptonote::token_v& token)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
if (val.MemberCount() != 1)
|
||||
{
|
||||
throw MISSING_KEY("Invalid token object");
|
||||
}
|
||||
|
||||
for (auto const& elem : val.GetObject())
|
||||
{
|
||||
if (elem.name == "erc_token")
|
||||
{
|
||||
cryptonote::erc_token_t tmpVal;
|
||||
fromJsonValue(elem.value, tmpVal);
|
||||
token = std::move(tmpVal);
|
||||
}
|
||||
else if (elem.name == "sal_token")
|
||||
{
|
||||
cryptonote::sal_token_t tmpVal;
|
||||
fromJsonValue(elem.value, tmpVal);
|
||||
token = std::move(tmpVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::token_metadata_t& token_metadata)
|
||||
{
|
||||
dest.StartObject();
|
||||
|
||||
INSERT_INTO_JSON_OBJECT(dest, version, token_metadata.version);
|
||||
INSERT_INTO_JSON_OBJECT(dest, asset_type, token_metadata.asset_type);
|
||||
INSERT_INTO_JSON_OBJECT(dest, token, token_metadata.token);
|
||||
dest.EndObject();
|
||||
}
|
||||
|
||||
|
||||
void fromJsonValue(const rapidjson::Value& val, cryptonote::token_metadata_t& token_metadata)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, token_metadata.version, version);
|
||||
GET_FROM_JSON_OBJECT(val, token_metadata.asset_type, asset_type);
|
||||
GET_FROM_JSON_OBJECT(val, token_metadata.token, token);
|
||||
}
|
||||
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::protocol_tx_data_t& ptd)
|
||||
{
|
||||
dest.StartObject();
|
||||
@@ -545,6 +698,50 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::protocol_tx_data_t&
|
||||
GET_FROM_JSON_OBJECT(val, ptd.return_anchor_enc, return_anchor_enc);
|
||||
}
|
||||
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::layer2_rollup_tx_t& lrt)
|
||||
{
|
||||
dest.StartObject();
|
||||
|
||||
INSERT_INTO_JSON_OBJECT(dest, tx_prefix_hash, lrt.tx_prefix_hash);
|
||||
INSERT_INTO_JSON_OBJECT(dest, first_key_image, lrt.first_key_image);
|
||||
INSERT_INTO_JSON_OBJECT(dest, tx_fee, lrt.tx_fee);
|
||||
|
||||
dest.EndObject();
|
||||
}
|
||||
|
||||
void fromJsonValue(const rapidjson::Value& val, cryptonote::layer2_rollup_tx_t& lrt)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, lrt.tx_prefix_hash, tx_prefix_hash);
|
||||
GET_FROM_JSON_OBJECT(val, lrt.first_key_image, first_key_image);
|
||||
GET_FROM_JSON_OBJECT(val, lrt.tx_fee, tx_fee);
|
||||
}
|
||||
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::layer2_rollup_data_t& lrd)
|
||||
{
|
||||
dest.StartObject();
|
||||
|
||||
INSERT_INTO_JSON_OBJECT(dest, version, lrd.version);
|
||||
INSERT_INTO_JSON_OBJECT(dest, txs, lrd.txs);
|
||||
|
||||
dest.EndObject();
|
||||
}
|
||||
|
||||
void fromJsonValue(const rapidjson::Value& val, cryptonote::layer2_rollup_data_t& lrd)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, lrd.version, version);
|
||||
GET_FROM_JSON_OBJECT(val, lrd.txs, txs);
|
||||
}
|
||||
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txin_to_key& txin)
|
||||
{
|
||||
dest.StartObject();
|
||||
|
||||
@@ -218,9 +218,27 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_scripthash&
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txin_to_key& txin);
|
||||
void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_key& txin);
|
||||
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::erc_token_t& token);
|
||||
void fromJsonValue(const rapidjson::Value& val, cryptonote::erc_token_t& token);
|
||||
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::sal_token_t& token);
|
||||
void fromJsonValue(const rapidjson::Value& val, cryptonote::sal_token_t& token);
|
||||
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::token_v& token);
|
||||
void fromJsonValue(const rapidjson::Value& val, cryptonote::token_v& token);
|
||||
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::token_metadata_t& token);
|
||||
void fromJsonValue(const rapidjson::Value& val, cryptonote::token_metadata_t& token);
|
||||
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::protocol_tx_data_t& ptd);
|
||||
void fromJsonValue(const rapidjson::Value& val, cryptonote::protocol_tx_data_t& ptd);
|
||||
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::layer2_rollup_tx_t& lrt);
|
||||
void fromJsonValue(const rapidjson::Value& val, cryptonote::layer2_rollup_tx_t& lrt);
|
||||
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::layer2_rollup_data_t& lrd);
|
||||
void fromJsonValue(const rapidjson::Value& val, cryptonote::layer2_rollup_data_t& lrd);
|
||||
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txout_target_v& txout);
|
||||
void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_target_v& txout);
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -194,6 +194,9 @@ namespace cryptonote
|
||||
bool burn(const std::vector<std::string> &args);
|
||||
bool convert(const std::vector<std::string> &args);
|
||||
bool audit(const std::vector<std::string> &args);
|
||||
bool create_token(const std::vector<std::string> &args);
|
||||
bool get_tokens(const std::vector<std::string> &args);
|
||||
bool token_info(const std::vector<std::string> &args);
|
||||
bool stake(const std::vector<std::string> &args);
|
||||
bool price_info(const std::vector<std::string> &args);
|
||||
bool supply_info(const std::vector<std::string> &args);
|
||||
@@ -205,8 +208,8 @@ namespace cryptonote
|
||||
std::vector<cryptonote::tx_destination_entry> dsts, size_t num_splits
|
||||
);
|
||||
bool account(const std::vector<std::string> &args = std::vector<std::string>());
|
||||
void print_accounts();
|
||||
void print_accounts(const std::string& tag);
|
||||
void print_accounts(bool show_all = false);
|
||||
void print_accounts(const std::string& tag, bool show_all = false);
|
||||
bool print_address(const std::vector<std::string> &args = std::vector<std::string>());
|
||||
bool print_integrated_address(const std::vector<std::string> &args = std::vector<std::string>());
|
||||
bool address_book(const std::vector<std::string> &args = std::vector<std::string>());
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
#define DEF_SALVIUM_VERSION_TAG "@VERSIONTAG@"
|
||||
#define DEF_SALVIUM_VERSION "1.0.0"
|
||||
#define DEF_SALVIUM_VERSION "1.1.0-rc4"
|
||||
#define DEF_MONERO_VERSION_TAG "release"
|
||||
#define DEF_MONERO_VERSION "0.18.3.4"
|
||||
#define DEF_MONERO_VERSION "0.18.4.0"
|
||||
#define DEF_MONERO_RELEASE_NAME "One"
|
||||
#define DEF_MONERO_VERSION_FULL DEF_SALVIUM_VERSION "-" DEF_SALVIUM_VERSION_TAG ", based on Monero " DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG
|
||||
#define DEF_MONERO_VERSION_IS_RELEASE @VERSION_IS_RELEASE@
|
||||
|
||||
@@ -166,9 +166,16 @@ uint64_t PendingTransactionImpl::amount() const
|
||||
{
|
||||
uint64_t result = 0;
|
||||
for (const auto &ptx : m_pending_tx) {
|
||||
if (ptx.tx.type == cryptonote::transaction_type::AUDIT ||
|
||||
ptx.tx.type == cryptonote::transaction_type::BURN ||
|
||||
ptx.tx.type == cryptonote::transaction_type::STAKE ||
|
||||
ptx.tx.type == cryptonote::transaction_type::CREATE_TOKEN) {
|
||||
result += ptx.tx.amount_burnt;
|
||||
} else {
|
||||
for (const auto &dest : ptx.dests) {
|
||||
result += dest.amount;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -44,16 +44,16 @@ SubaddressAccountImpl::SubaddressAccountImpl(WalletImpl *wallet)
|
||||
void SubaddressAccountImpl::addRow(const std::string &label)
|
||||
{
|
||||
m_wallet->m_wallet->add_subaddress_account(label);
|
||||
refresh();
|
||||
refresh("SAL1");
|
||||
}
|
||||
|
||||
void SubaddressAccountImpl::setLabel(uint32_t accountIndex, const std::string &label)
|
||||
{
|
||||
m_wallet->m_wallet->set_subaddress_label({accountIndex, 0}, label);
|
||||
refresh();
|
||||
refresh("SAL1");
|
||||
}
|
||||
|
||||
void SubaddressAccountImpl::refresh()
|
||||
void SubaddressAccountImpl::refresh(const std::string &asset_type)
|
||||
{
|
||||
LOG_PRINT_L2("Refreshing subaddress account");
|
||||
|
||||
@@ -64,8 +64,8 @@ void SubaddressAccountImpl::refresh()
|
||||
i,
|
||||
m_wallet->m_wallet->get_subaddress_as_str({i,0}),
|
||||
m_wallet->m_wallet->get_subaddress_label({i,0}),
|
||||
cryptonote::print_money(m_wallet->m_wallet->balance(i, "SAL", false)),
|
||||
cryptonote::print_money(m_wallet->m_wallet->unlocked_balance(i, "SAL", false)),
|
||||
cryptonote::print_money(m_wallet->m_wallet->balance(i, asset_type, false)),
|
||||
cryptonote::print_money(m_wallet->m_wallet->unlocked_balance(i, asset_type, false)),
|
||||
cryptonote::print_money(m_wallet->m_wallet->balance(i, "SAL1", false)),
|
||||
cryptonote::print_money(m_wallet->m_wallet->unlocked_balance(i, "SAL1", false))
|
||||
));
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
~SubaddressAccountImpl();
|
||||
|
||||
// Fetches addresses from Wallet2
|
||||
void refresh();
|
||||
void refresh(const std::string &asset_type);
|
||||
std::vector<SubaddressAccountRow*> getAll() const;
|
||||
void addRow(const std::string &label);
|
||||
void setLabel(uint32_t accountIndex, const std::string &label);
|
||||
|
||||
@@ -175,7 +175,13 @@ void TransactionHistoryImpl::refresh()
|
||||
|
||||
uint64_t change = pd.m_change == (uint64_t)-1 ? 0 : pd.m_change; // change may not be known
|
||||
uint64_t fee = pd.m_amount_in - pd.m_amount_out;
|
||||
|
||||
uint64_t amount = pd.m_amount_in - change - fee;
|
||||
if (pd.m_tx.type == cryptonote::transaction_type::AUDIT ||
|
||||
pd.m_tx.type == cryptonote::transaction_type::BURN ||
|
||||
pd.m_tx.type == cryptonote::transaction_type::STAKE) {
|
||||
amount = pd.m_tx.amount_burnt;
|
||||
if (fee > amount) fee -= amount;
|
||||
}
|
||||
|
||||
std::string payment_id = string_tools::pod_to_hex(i->second.m_payment_id);
|
||||
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
|
||||
@@ -184,7 +190,7 @@ void TransactionHistoryImpl::refresh()
|
||||
|
||||
TransactionInfoImpl * ti = new TransactionInfoImpl();
|
||||
ti->m_paymentid = payment_id;
|
||||
ti->m_amount = pd.m_amount_in - change - fee;
|
||||
ti->m_amount = amount;
|
||||
ti->m_fee = fee;
|
||||
ti->m_direction = TransactionInfo::Direction_Out;
|
||||
ti->m_hash = string_tools::pod_to_hex(hash);
|
||||
@@ -212,8 +218,16 @@ void TransactionHistoryImpl::refresh()
|
||||
for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments_out.begin(); i != upayments_out.end(); ++i) {
|
||||
const tools::wallet2::unconfirmed_transfer_details &pd = i->second;
|
||||
const crypto::hash &hash = i->first;
|
||||
uint64_t amount = pd.m_amount_in;
|
||||
uint64_t fee = amount - pd.m_amount_out;
|
||||
uint64_t change = pd.m_change == (uint64_t)-1 ? 0 : pd.m_change; // change may not be known
|
||||
uint64_t fee = pd.m_amount_in - pd.m_amount_out;
|
||||
uint64_t amount = pd.m_amount_in - change - fee;
|
||||
if (pd.m_tx.type == cryptonote::transaction_type::AUDIT ||
|
||||
pd.m_tx.type == cryptonote::transaction_type::BURN ||
|
||||
pd.m_tx.type == cryptonote::transaction_type::STAKE) {
|
||||
amount = pd.m_tx.amount_burnt;
|
||||
if (fee > amount) fee -= amount;
|
||||
}
|
||||
|
||||
std::string payment_id = string_tools::pod_to_hex(i->second.m_payment_id);
|
||||
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
|
||||
payment_id = payment_id.substr(0,16);
|
||||
@@ -221,7 +235,7 @@ void TransactionHistoryImpl::refresh()
|
||||
|
||||
TransactionInfoImpl * ti = new TransactionInfoImpl();
|
||||
ti->m_paymentid = payment_id;
|
||||
ti->m_amount = amount - pd.m_change - fee;
|
||||
ti->m_amount = amount;
|
||||
ti->m_fee = fee;
|
||||
ti->m_direction = TransactionInfo::Direction_Out;
|
||||
ti->m_failed = is_failed;
|
||||
|
||||
+153
-3
@@ -893,24 +893,48 @@ std::string WalletImpl::integratedAddress(const std::string &payment_id, bool ca
|
||||
|
||||
std::string WalletImpl::secretViewKey() const
|
||||
{
|
||||
uint32_t hf_version = m_wallet->estimate_current_hard_fork();
|
||||
if (hf_version >= HF_VERSION_CARROT)
|
||||
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().k_view_incoming)));
|
||||
else
|
||||
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().m_view_secret_key)));
|
||||
}
|
||||
|
||||
std::string WalletImpl::publicViewKey() const
|
||||
{
|
||||
uint32_t hf_version = m_wallet->estimate_current_hard_fork();
|
||||
if (hf_version >= HF_VERSION_CARROT)
|
||||
return epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_carrot_account_address.m_view_public_key);
|
||||
else
|
||||
return epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_view_public_key);
|
||||
}
|
||||
|
||||
std::string WalletImpl::secretSpendKey() const
|
||||
{
|
||||
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().m_spend_secret_key)));
|
||||
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().m_spend_secret_key)));
|
||||
}
|
||||
|
||||
std::string WalletImpl::publicSpendKey() const
|
||||
{
|
||||
uint32_t hf_version = m_wallet->estimate_current_hard_fork();
|
||||
if (hf_version >= HF_VERSION_CARROT)
|
||||
return epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_carrot_account_address.m_spend_public_key);
|
||||
else
|
||||
return epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_spend_public_key);
|
||||
}
|
||||
|
||||
std::vector<std::string> WalletImpl::carrotKeys() const
|
||||
{
|
||||
return {
|
||||
epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().s_master))),
|
||||
epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().k_prove_spend))),
|
||||
epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().s_view_balance))),
|
||||
epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().k_view_incoming))),
|
||||
epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().k_generate_image))),
|
||||
epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().s_generate_address)))
|
||||
};
|
||||
}
|
||||
|
||||
std::string WalletImpl::publicMultisigSignerKey() const
|
||||
{
|
||||
try {
|
||||
@@ -921,6 +945,26 @@ std::string WalletImpl::publicMultisigSignerKey() const
|
||||
}
|
||||
}
|
||||
|
||||
std::string WalletImpl::secretViewBalance() const
|
||||
{
|
||||
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().s_view_balance)));
|
||||
}
|
||||
|
||||
std::string WalletImpl::secretProveSpend() const
|
||||
{
|
||||
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().k_prove_spend)));
|
||||
}
|
||||
|
||||
std::string WalletImpl::secretGenerateAddress() const
|
||||
{
|
||||
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().s_generate_address)));
|
||||
}
|
||||
|
||||
std::string WalletImpl::secretGenerateImage() const
|
||||
{
|
||||
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().k_generate_image)));
|
||||
}
|
||||
|
||||
std::string WalletImpl::path() const
|
||||
{
|
||||
return m_wallet->path();
|
||||
@@ -1599,6 +1643,107 @@ PendingTransaction *WalletImpl::createStakeTransaction(uint64_t amount, uint32_t
|
||||
return createTransactionMultDest(Monero::transaction_type::STAKE, std::vector<string> {dst_addr}, payment_id, amount ? (std::vector<uint64_t> {amount}) : (optional<std::vector<uint64_t>>()), mixin_count, asset_type, is_return, priority, subaddr_account, subaddr_indices);
|
||||
}
|
||||
|
||||
PendingTransaction *WalletImpl::createCreateTokenTransaction(const std::string &asset_type, uint64_t supply, const std::string &metadata, const std::string &name, uint32_t size, std::string hash, std::string url, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices){
|
||||
|
||||
PendingTransactionImpl * transaction = new PendingTransactionImpl(*this);
|
||||
std::string token_metadata_hex;
|
||||
crypto::hash hash_signature{};
|
||||
if (!hash.empty()) {
|
||||
if (!epee::string_tools::hex_to_pod(hash, hash_signature)) {
|
||||
if (metadata.empty()){
|
||||
setStatusError(tr("Invalid hash format."));
|
||||
return transaction;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (metadata.empty()) {
|
||||
std::string json = (boost::format("{\"name\":\"%s\",\"size\":%d,\"hash\":\"%s\",\"url\":\"%s\"}") % name % size % hash % url).str();
|
||||
token_metadata_hex = epee::string_tools::buff_to_hex_nodelimer(json);
|
||||
} else {
|
||||
token_metadata_hex = metadata;
|
||||
}
|
||||
|
||||
cryptonote::sal_token_t sal_token;
|
||||
sal_token.version = 1;
|
||||
sal_token.supply = supply;
|
||||
// todo: if not provided, use token_metadata_hex
|
||||
sal_token.size = size;
|
||||
sal_token.name = name;
|
||||
sal_token.url = url;
|
||||
sal_token.signature = hash_signature;
|
||||
|
||||
cryptonote::token_metadata_t token_meta;
|
||||
token_meta.version = 1;
|
||||
token_meta.asset_type = asset_type;
|
||||
token_meta.token = sal_token;
|
||||
|
||||
clearStatus();
|
||||
try {
|
||||
// check if token already exists before attempting to create
|
||||
std::vector<std::string> existing_tokens;
|
||||
if (m_wallet->get_tokens(asset_type, existing_tokens) && !existing_tokens.empty()) {
|
||||
setStatusError(tr("Token with ticker '") + asset_type + tr("' already exists"));
|
||||
statusWithErrorString(transaction->m_status, transaction->m_errorString);
|
||||
return transaction;
|
||||
}
|
||||
|
||||
transaction->m_pending_tx = m_wallet->create_token(asset_type, supply, token_metadata_hex, subaddr_account, subaddr_indices
|
||||
);
|
||||
pendingTxPostProcess(transaction);
|
||||
} catch (const tools::error::daemon_busy&) {
|
||||
setStatusError(tr("daemon is busy. Please try again later."));
|
||||
} catch (const tools::error::no_connection_to_daemon&) {
|
||||
setStatusError(tr("no connection to daemon. Please make sure daemon is running."));
|
||||
} catch (const tools::error::wallet_rpc_error& e) {
|
||||
setStatusError(tr("RPC error: ") + e.to_string());
|
||||
} catch (const tools::error::get_outs_error &e) {
|
||||
setStatusError((boost::format(tr("failed to get outputs to mix: %s")) % e.what()).str());
|
||||
} catch (const tools::error::not_enough_unlocked_money& e) {
|
||||
std::ostringstream writer;
|
||||
writer << boost::format(tr("not enough SAL1 to create token, available only %s, required %s")) %
|
||||
print_money(e.available()) %
|
||||
print_money(e.tx_amount());
|
||||
setStatusError(writer.str());
|
||||
} catch (const tools::error::not_enough_money& e) {
|
||||
std::ostringstream writer;
|
||||
writer << boost::format(tr("not enough SAL1 to create token, overall balance only %s, required %s")) %
|
||||
print_money(e.available()) %
|
||||
print_money(e.tx_amount());
|
||||
setStatusError(writer.str());
|
||||
} catch (const tools::error::tx_not_possible& e) {
|
||||
std::ostringstream writer;
|
||||
writer << boost::format(tr("not enough SAL1 to create token, available only %s, required %s = %s (locked) + %s (fee)")) %
|
||||
print_money(e.available()) %
|
||||
print_money(e.tx_amount() + e.fee()) %
|
||||
print_money(e.tx_amount()) %
|
||||
print_money(e.fee());
|
||||
setStatusError(writer.str());
|
||||
} catch (const tools::error::tx_not_constructed&) {
|
||||
setStatusError(tr("transaction for token creation was not constructed"));
|
||||
} catch (const tools::error::tx_rejected& e) {
|
||||
std::ostringstream writer;
|
||||
writer << (boost::format(tr("create token transaction %s was rejected by daemon with status: ")) % get_transaction_hash(e.tx())) << e.status();
|
||||
std::string reason = e.reason();
|
||||
if (!reason.empty())
|
||||
writer << tr(". Reason: ") << reason;
|
||||
setStatusError(writer.str());
|
||||
} catch (const tools::error::tx_sum_overflow& e) {
|
||||
setStatusError(e.what());
|
||||
} catch (const tools::error::tx_too_big& e) {
|
||||
setStatusError(tr("token creation transaction is too large"));
|
||||
} catch (const tools::error::transfer_error& e) {
|
||||
setStatusError(string(tr("token creation error: ")) + e.what());
|
||||
} catch (const tools::error::wallet_internal_error& e) {
|
||||
setStatusError(string(tr("internal error: ")) + e.what());
|
||||
} catch (const std::exception& e) {
|
||||
setStatusError(string(tr("unexpected error: ")) + e.what());
|
||||
} catch (...) {
|
||||
setStatusError(tr("unknown error"));
|
||||
}
|
||||
statusWithErrorString(transaction->m_status, transaction->m_errorString);
|
||||
return transaction;
|
||||
}
|
||||
|
||||
PendingTransaction *WalletImpl::createAuditTransaction(
|
||||
uint32_t mixin_count,
|
||||
PendingTransaction::Priority priority,
|
||||
@@ -1739,8 +1884,8 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const Monero::transact
|
||||
const auto result = m_wallet->create_transactions_all(0,
|
||||
converted_tx_type,
|
||||
asset_type,
|
||||
m_wallet->get_subaddress({subaddr_account, subaddr_index}),
|
||||
(subaddr_index > 0),
|
||||
info.address,
|
||||
info.is_subaddress,
|
||||
1,
|
||||
fake_outs_count,
|
||||
0 /* unlock_time */,
|
||||
@@ -2888,4 +3033,9 @@ YieldInfo * WalletImpl::getYieldInfo()
|
||||
return yi;
|
||||
}
|
||||
|
||||
std::vector<std::string> WalletImpl::getAssetTypes()
|
||||
{
|
||||
return m_wallet->list_asset_types();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -99,7 +99,12 @@ public:
|
||||
std::string publicViewKey() const override;
|
||||
std::string secretSpendKey() const override;
|
||||
std::string publicSpendKey() const override;
|
||||
std::vector<std::string> carrotKeys() const override;
|
||||
std::string publicMultisigSignerKey() const override;
|
||||
std::string secretViewBalance() const override;
|
||||
std::string secretProveSpend() const override;
|
||||
std::string secretGenerateAddress() const override;
|
||||
std::string secretGenerateImage() const override;
|
||||
std::string path() const override;
|
||||
void stop() override;
|
||||
bool store(const std::string &path) override;
|
||||
@@ -158,6 +163,15 @@ public:
|
||||
PendingTransaction::Priority priority = PendingTransaction::Priority_Low,
|
||||
uint32_t subaddr_account = 0,
|
||||
std::set<uint32_t> subaddr_indices = {}) override;
|
||||
PendingTransaction * createCreateTokenTransaction(const std::string &asset_type,
|
||||
uint64_t supply,
|
||||
const std::string &metadata,
|
||||
const std::string &name,
|
||||
uint32_t size,
|
||||
std::string hash,
|
||||
std::string url,
|
||||
uint32_t subaddr_account,
|
||||
std::set<uint32_t> subaddr_indices = {}) override;
|
||||
PendingTransaction * createAuditTransaction(uint32_t mixin_count,
|
||||
PendingTransaction::Priority priority = PendingTransaction::Priority_Low,
|
||||
uint32_t subaddr_account = 0,
|
||||
@@ -247,6 +261,8 @@ public:
|
||||
|
||||
YieldInfo * getYieldInfo() override;
|
||||
|
||||
virtual std::vector<std::string> getAssetTypes() override;
|
||||
|
||||
private:
|
||||
void clearStatus() const;
|
||||
void setStatusError(const std::string& message) const;
|
||||
|
||||
@@ -59,7 +59,9 @@ enum transaction_type : uint8_t {
|
||||
STAKE = 6,
|
||||
RETURN = 7,
|
||||
AUDIT = 8,
|
||||
MAX = 8
|
||||
CREATE_TOKEN = 9,
|
||||
ROLLUP = 10,
|
||||
MAX = 10
|
||||
};
|
||||
|
||||
namespace Utils {
|
||||
@@ -366,7 +368,7 @@ struct SubaddressAccount
|
||||
virtual std::vector<SubaddressAccountRow*> getAll() const = 0;
|
||||
virtual void addRow(const std::string &label) = 0;
|
||||
virtual void setLabel(uint32_t accountIndex, const std::string &label) = 0;
|
||||
virtual void refresh() = 0;
|
||||
virtual void refresh(const std::string &asset_type) = 0;
|
||||
};
|
||||
|
||||
struct MultisigState {
|
||||
@@ -559,12 +561,48 @@ struct Wallet
|
||||
*/
|
||||
virtual std::string publicSpendKey() const = 0;
|
||||
|
||||
/*!
|
||||
* \brief allCarrotKeys - returns all Carrot keys
|
||||
* [0] - s_master
|
||||
* [1] - k_prove_spend
|
||||
* [2] - s_view_balance
|
||||
* [3] - k_view_incoming
|
||||
* [4] - k_generate_image
|
||||
* [5] - s_generate_address
|
||||
* \return - vector of all Carrot keys
|
||||
*/
|
||||
virtual std::vector<std::string> carrotKeys() const = 0;
|
||||
|
||||
/*!
|
||||
* \brief publicMultisigSignerKey - returns public signer key
|
||||
* \return - public multisignature signer key or empty string if wallet is not multisig
|
||||
*/
|
||||
virtual std::string publicMultisigSignerKey() const = 0;
|
||||
|
||||
/*!
|
||||
* \brief secretViewBalance - returns Carrot "view balance" secret
|
||||
* \return - Carrot s_vb
|
||||
*/
|
||||
virtual std::string secretViewBalance() const = 0;
|
||||
|
||||
/*!
|
||||
* \brief secretProveSpend - returns Carrot "prove spend" secret
|
||||
* \return - Carrot secret k_ps
|
||||
*/
|
||||
virtual std::string secretProveSpend() const = 0;
|
||||
|
||||
/*!
|
||||
* \brief secretGenerateAddress - returns Carrot "generate address" secret
|
||||
* \return - Carrot secret s_ga
|
||||
*/
|
||||
virtual std::string secretGenerateAddress() const = 0;
|
||||
|
||||
/*!
|
||||
* \brief secretGenerateImage - returns Carrot "generate key image" secret
|
||||
* \return - Carrot secret k_gi
|
||||
*/
|
||||
virtual std::string secretGenerateImage() const = 0;
|
||||
|
||||
/*!
|
||||
* \brief stop - interrupts wallet refresh() loop once (doesn't stop background refresh thread)
|
||||
*/
|
||||
@@ -897,6 +935,31 @@ struct Wallet
|
||||
uint32_t subaddr_account = 0,
|
||||
std::set<uint32_t> subaddr_indices = {}) = 0;
|
||||
|
||||
/*!
|
||||
* \brief createCreateTokenTransaction creates token creation transaction.
|
||||
* \param asset_type the type of asset to create
|
||||
* \param supply token supply amount
|
||||
* \param metadata
|
||||
* \param name
|
||||
* \param size
|
||||
* \param hash
|
||||
* \param url
|
||||
* \param subaddr_account subaddress account from which the input funds are taken
|
||||
* \param subaddr_indices set of subaddress indices to use for transfer or sweeping. if set empty, all are chosen when sweeping, and one or more are automatically chosen when transferring. after execution, returns the set of actually used indices
|
||||
* \return PendingTransaction object. caller is responsible to check PendingTransaction::status()
|
||||
* after object returned
|
||||
*/
|
||||
|
||||
virtual PendingTransaction * createCreateTokenTransaction(const std::string &asset_type,
|
||||
uint64_t supply,
|
||||
const std::string &metadata,
|
||||
const std::string &name,
|
||||
uint32_t size,
|
||||
const std::string hash,
|
||||
const std::string url,
|
||||
uint32_t subaddr_account = 0,
|
||||
std::set<uint32_t> subaddr_indices = {}) = 0;
|
||||
|
||||
/*!
|
||||
* \brief createAuditTransaction creates audit transaction.
|
||||
* \param mixin_count mixin count. if 0 passed, wallet will use default value
|
||||
@@ -1224,6 +1287,9 @@ struct Wallet
|
||||
|
||||
//! get yield information
|
||||
virtual YieldInfo * getYieldInfo() = 0;
|
||||
|
||||
//! get asset types for which the wallet has/had outputs
|
||||
virtual std::vector<std::string> getAssetTypes() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -973,7 +973,14 @@ std::optional<crypto::key_image> try_derive_enote_key_image(
|
||||
// x = k_s + k^j_subext + k^g_o
|
||||
rct::key x;
|
||||
if (enote_scan_info.is_carrot) {
|
||||
return acc.derive_key_image(enote_scan_info.address_spend_pubkey,
|
||||
// if we don't have the s_master key, derive view-only key image
|
||||
if (acc.get_keys().s_master == crypto::null_skey) {
|
||||
return acc.derive_key_image_view_only(enote_scan_info.address_spend_pubkey,
|
||||
enote_scan_info.sender_extension_g,
|
||||
enote_scan_info.sender_extension_t,
|
||||
rct::rct2pk(onetime_address));
|
||||
}
|
||||
return acc.derive_key_image(enote_scan_info.address_spend_pubkey,
|
||||
enote_scan_info.sender_extension_g,
|
||||
enote_scan_info.sender_extension_t,
|
||||
rct::rct2pk(onetime_address));
|
||||
|
||||
+298
-89
@@ -52,6 +52,8 @@
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
#include <iterator>
|
||||
#include <set>
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.tx_builder"
|
||||
@@ -71,11 +73,12 @@ static constexpr T div_ceil(T dividend, T divisor)
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
static bool is_transfer_usable_for_input_selection(const wallet2::transfer_details &td,
|
||||
const std::uint32_t from_account,
|
||||
const std::set<std::uint32_t> from_subaddresses,
|
||||
const rct::xmr_amount ignore_above,
|
||||
const rct::xmr_amount ignore_below,
|
||||
const uint64_t top_block_index)
|
||||
const std::uint32_t from_account,
|
||||
const std::set<std::uint32_t> from_subaddresses,
|
||||
const rct::xmr_amount ignore_above,
|
||||
const rct::xmr_amount ignore_below,
|
||||
const uint64_t top_block_index,
|
||||
const std::string asset_type)
|
||||
{
|
||||
/**
|
||||
* This additional check appears to be for fcmp++.
|
||||
@@ -91,13 +94,13 @@ static bool is_transfer_usable_for_input_selection(const wallet2::transfer_detai
|
||||
&& td.m_key_image_known
|
||||
&& !td.m_key_image_partial
|
||||
&& !td.m_frozen
|
||||
&& (top_block_index >= td.m_block_height + blocks_locked_for)
|
||||
&& (top_block_index +1 >= td.m_block_height + blocks_locked_for)
|
||||
// && last_locked_block_index <= top_block_index
|
||||
&& td.m_subaddr_index.major == from_account
|
||||
&& (from_subaddresses.empty() || from_subaddresses.count(td.m_subaddr_index.minor) == 1)
|
||||
&& td.amount() >= ignore_below
|
||||
&& td.amount() <= ignore_above
|
||||
&& td.asset_type == "SAL1"
|
||||
&& td.asset_type == asset_type
|
||||
;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
@@ -118,7 +121,8 @@ static bool build_payment_proposals(std::vector<carrot::CarrotPaymentProposalV1>
|
||||
.proposal = carrot::CarrotPaymentProposalSelfSendV1{
|
||||
.destination_address_spend_pubkey = tx_dest_entry.addr.m_spend_public_key,
|
||||
.amount = tx_dest_entry.amount,
|
||||
.enote_type = carrot::CarrotEnoteType::PAYMENT
|
||||
.enote_type = carrot::CarrotEnoteType::PAYMENT,
|
||||
.asset_type = tx_dest_entry.asset_type
|
||||
},
|
||||
.subaddr_index = {subaddr_index, carrot::AddressDeriveType::Carrot, false},
|
||||
});
|
||||
@@ -148,9 +152,10 @@ static cryptonote::tx_destination_entry make_tx_destination_entry(
|
||||
const carrot::CarrotPaymentProposalV1 &payment_proposal)
|
||||
{
|
||||
cryptonote::tx_destination_entry dest = cryptonote::tx_destination_entry(payment_proposal.amount,
|
||||
{payment_proposal.destination.address_spend_pubkey, payment_proposal.destination.address_view_pubkey},
|
||||
{payment_proposal.destination.address_spend_pubkey, payment_proposal.destination.address_view_pubkey, /*m_is_carrot*/true},
|
||||
payment_proposal.destination.is_subaddress);
|
||||
dest.is_integrated = payment_proposal.destination.payment_id != carrot::null_payment_id;
|
||||
dest.asset_type = payment_proposal.asset_type;
|
||||
return dest;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
@@ -165,9 +170,11 @@ static cryptonote::tx_destination_entry make_tx_destination_entry(
|
||||
address_view_pubkey),
|
||||
"make_tx_destination_entry: view-key multiplication failed");
|
||||
|
||||
return cryptonote::tx_destination_entry(payment_proposal.proposal.amount,
|
||||
{payment_proposal.proposal.destination_address_spend_pubkey, address_view_pubkey},
|
||||
payment_proposal.subaddr_index.index.is_subaddress());
|
||||
cryptonote::tx_destination_entry dest = cryptonote::tx_destination_entry(payment_proposal.proposal.amount,
|
||||
{payment_proposal.proposal.destination_address_spend_pubkey, address_view_pubkey, /*m_is_carrot*/true},
|
||||
payment_proposal.subaddr_index.index.is_subaddress());
|
||||
dest.asset_type = payment_proposal.proposal.asset_type;
|
||||
return dest;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
@@ -258,6 +265,7 @@ carrot::select_inputs_func_t make_wallet2_single_transfer_input_selector(
|
||||
const std::uint64_t top_block_index,
|
||||
const bool allow_carrot_external_inputs_in_normal_transfers,
|
||||
const bool allow_pre_carrot_inputs_in_normal_transfers,
|
||||
const std::string &asset_type,
|
||||
std::set<size_t> &selected_transfer_indices_out)
|
||||
{
|
||||
// Collect transfer_container into a `std::vector<carrot::InputCandidate>` for usable inputs
|
||||
@@ -269,11 +277,12 @@ carrot::select_inputs_func_t make_wallet2_single_transfer_input_selector(
|
||||
{
|
||||
const wallet2::transfer_details &td = transfers.at(i);
|
||||
if (is_transfer_usable_for_input_selection(td,
|
||||
from_account,
|
||||
from_subaddresses,
|
||||
ignore_above,
|
||||
ignore_below,
|
||||
top_block_index))
|
||||
from_account,
|
||||
from_subaddresses,
|
||||
ignore_above,
|
||||
ignore_below,
|
||||
top_block_index,
|
||||
asset_type))
|
||||
{
|
||||
input_candidates.push_back(carrot::InputCandidate{
|
||||
.core = carrot::CarrotSelectedInput{
|
||||
@@ -293,6 +302,7 @@ carrot::select_inputs_func_t make_wallet2_single_transfer_input_selector(
|
||||
input_candidates_transfer_indices = std::move(input_candidates_transfer_indices),
|
||||
allow_carrot_external_inputs_in_normal_transfers,
|
||||
allow_pre_carrot_inputs_in_normal_transfers,
|
||||
asset_type,
|
||||
&selected_transfer_indices_out
|
||||
](
|
||||
const boost::multiprecision::uint128_t &nominal_output_sum,
|
||||
@@ -310,6 +320,9 @@ carrot::select_inputs_func_t make_wallet2_single_transfer_input_selector(
|
||||
flags |= carrot::InputSelectionFlags::ALLOW_EXTERNAL_INPUTS_IN_NORMAL_TRANSFERS;
|
||||
if (allow_pre_carrot_inputs_in_normal_transfers)
|
||||
flags |= carrot::InputSelectionFlags::ALLOW_PRE_CARROT_INPUTS_IN_NORMAL_TRANSFERS;
|
||||
// for token transfers; fee is paid separately in SAL1 via rollup tx
|
||||
if (cryptonote::is_asset_type_token(asset_type))
|
||||
flags |= carrot::InputSelectionFlags::IS_TOKEN_TRANSFER;
|
||||
|
||||
// Make inner input selection functor
|
||||
std::set<size_t> selected_input_indices;
|
||||
@@ -340,13 +353,27 @@ std::vector<cryptonote::tx_source_entry> get_sources(
|
||||
wallet2 &w
|
||||
) {
|
||||
// get decoys
|
||||
const size_t fake_outputs_count = 15;
|
||||
size_t fake_outputs_count = (cryptonote::is_asset_type_token(source_asset)) ? 0 : 15;
|
||||
// check here!
|
||||
// for tokens, check if there are enough decoys in the chain - if so ring_size= 16
|
||||
if (cryptonote::is_asset_type_token(source_asset))
|
||||
{
|
||||
uint64_t rct_start_height = 0;
|
||||
std::vector<uint64_t> distribution;
|
||||
uint64_t num_spendable_global_outs = 0;
|
||||
if (w.get_rct_distribution(false, source_asset, rct_start_height, distribution, num_spendable_global_outs)
|
||||
&& num_spendable_global_outs > 15)
|
||||
{
|
||||
fake_outputs_count = 15;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::vector<tools::wallet2::get_outs_entry>> outs;
|
||||
std::unordered_set<crypto::public_key> valid_public_keys_cache;
|
||||
w.get_outs(outs, selected_transfers, fake_outputs_count, true, valid_public_keys_cache); // may throw
|
||||
w.get_outs(outs, transfers, selected_transfers, fake_outputs_count, true, valid_public_keys_cache); // may throw
|
||||
|
||||
LOG_PRINT_L2("preparing outputs");
|
||||
size_t i = 0, out_index = 0;
|
||||
size_t out_index = 0;
|
||||
std::vector<cryptonote::tx_source_entry> sources;
|
||||
for(size_t idx: selected_transfers)
|
||||
{
|
||||
@@ -387,7 +414,6 @@ std::vector<cryptonote::tx_source_entry> get_sources(
|
||||
oe.second.mask = std::get<2>(outs[out_index][n]);
|
||||
src.outputs.push_back(oe);
|
||||
}
|
||||
++i;
|
||||
|
||||
//paste real transaction to the random index
|
||||
auto it_to_replace = std::find_if(src.outputs.begin(), src.outputs.end(), [&](const tx_output_entry& a)
|
||||
@@ -433,6 +459,134 @@ std::vector<cryptonote::tx_source_entry> get_sources(
|
||||
return sources;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposals_wallet2_createtoken(
|
||||
wallet2 &w,
|
||||
const cryptonote::token_metadata_t &token,
|
||||
const cryptonote::tx_destination_entry &de,
|
||||
const rct::xmr_amount fee_per_weight,
|
||||
const rct::xmr_amount fee_quantization_mask,
|
||||
const uint32_t subaddr_account,
|
||||
const std::set<uint32_t> &subaddr_indices,
|
||||
const std::uint64_t top_block_index)
|
||||
{
|
||||
wallet2::transfer_container unused_transfers;
|
||||
w.get_transfers(unused_transfers, "SAL1");
|
||||
|
||||
std::vector<carrot::CarrotTransactionProposalV1> tx_proposals;
|
||||
tx_proposals.reserve(1 / (carrot::CARROT_MAX_TX_OUTPUTS - 1) + 1);
|
||||
|
||||
const crypto::public_key change_address_spend_pubkey
|
||||
= find_change_address_spend_pubkey(w.get_account().get_subaddress_map_ref(), subaddr_account);
|
||||
|
||||
std::vector<cryptonote::tx_destination_entry> dsts = {de};
|
||||
while (!dsts.empty())
|
||||
{
|
||||
const std::size_t num_dsts_to_complete = std::min<std::size_t>(dsts.size(), carrot::CARROT_MAX_TX_OUTPUTS - 1);
|
||||
|
||||
// build payment proposals and subtractable info from last `num_dsts_to_complete` dsts
|
||||
std::vector<carrot::CarrotPaymentProposalV1> normal_payment_proposals;
|
||||
std::vector<carrot::CarrotPaymentProposalVerifiableSelfSendV1> selfsend_payment_proposals;
|
||||
std::set<std::size_t> subtractable_normal_payment_proposals;
|
||||
std::set<std::size_t> subtractable_selfsend_payment_proposals;
|
||||
for (size_t i = 0; i < num_dsts_to_complete && !dsts.empty(); ++i)
|
||||
{
|
||||
const cryptonote::tx_destination_entry &dst = dsts.back();
|
||||
const bool is_selfsend = build_payment_proposals(normal_payment_proposals,
|
||||
selfsend_payment_proposals,
|
||||
dst,
|
||||
w.get_account().get_subaddress_map_cn());
|
||||
dsts.pop_back();
|
||||
}
|
||||
|
||||
// make input selector
|
||||
std::set<size_t> selected_transfer_indices;
|
||||
carrot::select_inputs_func_t select_inputs = make_wallet2_single_transfer_input_selector(
|
||||
unused_transfers,
|
||||
subaddr_account,
|
||||
subaddr_indices,
|
||||
w.ignore_outputs_above(),
|
||||
w.ignore_outputs_below(),
|
||||
top_block_index,
|
||||
true, //allow_carrot_external_inputs_in_normal_transfers
|
||||
true, //allow_pre_carrot_inputs_in_normal_transfers
|
||||
std::string("SAL1"),
|
||||
selected_transfer_indices);
|
||||
|
||||
// make proposal
|
||||
carrot::CarrotTransactionProposalV1 tx_proposal;
|
||||
carrot::make_carrot_transaction_proposal_v1_transfer(
|
||||
normal_payment_proposals,
|
||||
selfsend_payment_proposals,
|
||||
fee_per_weight,
|
||||
fee_quantization_mask,
|
||||
{},
|
||||
cryptonote::transaction_type::CREATE_TOKEN,
|
||||
std::move(select_inputs),
|
||||
change_address_spend_pubkey,
|
||||
{{subaddr_account, 0}, carrot::AddressDeriveType::Carrot, false},
|
||||
subtractable_normal_payment_proposals,
|
||||
subtractable_selfsend_payment_proposals,
|
||||
"SAL1",
|
||||
tx_proposal);
|
||||
|
||||
// populate the sources
|
||||
std::vector<size_t> selected_transfer_indices_sorted;
|
||||
for (const auto &ki: tx_proposal.key_images_sorted) {
|
||||
// Scan the transfer_container instead of all of the transfers in the wallet
|
||||
selected_transfer_indices_sorted.push_back(w.get_transfer_details_from_container(ki, unused_transfers));
|
||||
}
|
||||
tx_proposal.sources = get_sources(unused_transfers, selected_transfer_indices_sorted, "SAL1", w);
|
||||
|
||||
// update `unused_transfers` for next proposal by removing selected transfers
|
||||
tools::for_all_in_vector_erase_no_preserve_order_if(unused_transfers,
|
||||
[&tx_proposal](const wallet2::transfer_details &td) -> bool {
|
||||
const auto &used_kis = tx_proposal.key_images_sorted;
|
||||
const auto ki_it = std::find(used_kis.cbegin(), used_kis.cend(), td.m_key_image);
|
||||
return ki_it != used_kis.cend();
|
||||
}
|
||||
);
|
||||
|
||||
tx_proposal.token = token;
|
||||
|
||||
tx_proposals.push_back(std::move(tx_proposal));
|
||||
}
|
||||
return tx_proposals;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposals_wallet2_createtoken(
|
||||
wallet2 &w,
|
||||
const cryptonote::token_metadata_t &token,
|
||||
const cryptonote::tx_destination_entry &de,
|
||||
const std::uint32_t priority,
|
||||
const std::uint32_t subaddr_account,
|
||||
const std::set<uint32_t> &subaddr_indices)
|
||||
{
|
||||
const bool use_per_byte_fee = w.use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0);
|
||||
CHECK_AND_ASSERT_THROW_MES(use_per_byte_fee,
|
||||
"make_carrot_transaction_proposals_wallet2_createtoken: not using per-byte base fee");
|
||||
|
||||
const rct::xmr_amount fee_per_weight = w.get_base_fee(priority);
|
||||
MDEBUG("fee_per_weight = " << fee_per_weight << ", from priority = " << priority);
|
||||
|
||||
const rct::xmr_amount fee_quantization_mask = w.get_fee_quantization_mask();
|
||||
MDEBUG("fee_quantization_mask = " << fee_quantization_mask << ", from priority = " << priority);
|
||||
|
||||
const std::uint64_t current_chain_height = w.get_blockchain_current_height();
|
||||
CHECK_AND_ASSERT_THROW_MES(current_chain_height > 0,
|
||||
"make_carrot_transaction_proposals_wallet2_createtoken: chain height is 0, there is no top block");
|
||||
const std::uint64_t top_block_index = current_chain_height - 1;
|
||||
|
||||
return make_carrot_transaction_proposals_wallet2_createtoken(
|
||||
w,
|
||||
token,
|
||||
de,
|
||||
fee_per_weight,
|
||||
fee_quantization_mask,
|
||||
subaddr_account,
|
||||
subaddr_indices,
|
||||
top_block_index);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposals_wallet2_transfer(
|
||||
wallet2 &w,
|
||||
std::vector<cryptonote::tx_destination_entry> dsts,
|
||||
@@ -445,8 +599,16 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
|
||||
wallet2::unique_index_container subtract_fee_from_outputs,
|
||||
const std::uint64_t top_block_index)
|
||||
{
|
||||
// Make sure all destinations have the SAME asset_type
|
||||
std::string asset_type = dsts[0].asset_type;
|
||||
if (dsts.size() > 1) {
|
||||
for (size_t i=0; i<dsts.size(); ++i) {
|
||||
CHECK_AND_ASSERT_THROW_MES(dsts[i].asset_type == asset_type, "Mixed asset_types in transaction outputs is forbidden");
|
||||
}
|
||||
}
|
||||
|
||||
wallet2::transfer_container unused_transfers;
|
||||
w.get_transfers(unused_transfers);
|
||||
w.get_transfers(unused_transfers, asset_type);
|
||||
|
||||
std::vector<carrot::CarrotTransactionProposalV1> tx_proposals;
|
||||
tx_proposals.reserve(dsts.size() / (carrot::CARROT_MAX_TX_OUTPUTS - 1) + 1);
|
||||
@@ -491,30 +653,33 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
|
||||
top_block_index,
|
||||
/*allow_carrot_external_inputs_in_normal_transfers=*/true,
|
||||
/*allow_pre_carrot_inputs_in_normal_transfers=*/true,
|
||||
asset_type,
|
||||
selected_transfer_indices);
|
||||
|
||||
// make proposal
|
||||
carrot::CarrotTransactionProposalV1 tx_proposal;
|
||||
carrot::make_carrot_transaction_proposal_v1_transfer(
|
||||
normal_payment_proposals,
|
||||
selfsend_payment_proposals,
|
||||
fee_per_weight,
|
||||
fee_quantization_mask,
|
||||
extra,
|
||||
tx_type,
|
||||
std::move(select_inputs),
|
||||
change_address_spend_pubkey,
|
||||
{{subaddr_account, 0}, carrot::AddressDeriveType::Carrot, false},
|
||||
subtractable_normal_payment_proposals,
|
||||
subtractable_selfsend_payment_proposals,
|
||||
tx_proposal);
|
||||
normal_payment_proposals,
|
||||
selfsend_payment_proposals,
|
||||
fee_per_weight,
|
||||
fee_quantization_mask,
|
||||
extra,
|
||||
tx_type,
|
||||
std::move(select_inputs),
|
||||
change_address_spend_pubkey,
|
||||
{{subaddr_account, 0}, carrot::AddressDeriveType::Carrot, false},
|
||||
subtractable_normal_payment_proposals,
|
||||
subtractable_selfsend_payment_proposals,
|
||||
asset_type,
|
||||
tx_proposal);
|
||||
|
||||
// populate the sources
|
||||
std::vector<size_t> selected_transfer_indices_sorted;
|
||||
for (const auto &ki: tx_proposal.key_images_sorted) {
|
||||
selected_transfer_indices_sorted.push_back(w.get_transfer_details(ki));
|
||||
// Scan the transfer_container instead of all of the transfers in the wallet
|
||||
selected_transfer_indices_sorted.push_back(w.get_transfer_details_from_container(ki, unused_transfers));
|
||||
}
|
||||
tx_proposal.sources = get_sources(unused_transfers, selected_transfer_indices_sorted, "SAL1", w);
|
||||
tx_proposal.sources = get_sources(unused_transfers, selected_transfer_indices_sorted, asset_type, w);
|
||||
|
||||
// update `unused_transfers` for next proposal by removing selected transfers
|
||||
tools::for_all_in_vector_erase_no_preserve_order_if(unused_transfers,
|
||||
@@ -545,8 +710,12 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
|
||||
CHECK_AND_ASSERT_THROW_MES(use_per_byte_fee,
|
||||
"make_carrot_transaction_proposals_wallet2_transfer: not using per-byte base fee");
|
||||
|
||||
const rct::xmr_amount fee_per_weight = w.get_base_fee(priority);
|
||||
MDEBUG("fee_per_weight = " << fee_per_weight << ", from priority = " << priority);
|
||||
// check if this is a token transfer (sal prefix) - tokens have zero fees
|
||||
const std::string asset_type = dsts.empty() ? "SAL1" : (tx_type == cryptonote::transaction_type::BURN ? "SAL1" : dsts[0].asset_type);
|
||||
const bool is_token = cryptonote::is_asset_type_token(asset_type);
|
||||
|
||||
const rct::xmr_amount fee_per_weight = /*is_token ? 0 :*/ w.get_base_fee(priority);
|
||||
MDEBUG("fee_per_weight = " << fee_per_weight << ", from priority = " << priority << ", is_token = " << is_token);
|
||||
|
||||
const rct::xmr_amount fee_quantization_mask = w.get_fee_quantization_mask();
|
||||
MDEBUG("fee_quantization_mask = " << fee_quantization_mask << ", from priority = " << priority);
|
||||
@@ -581,33 +750,44 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
|
||||
const cryptonote::transaction_type tx_type,
|
||||
const std::uint64_t top_block_index)
|
||||
{
|
||||
wallet2::transfer_container transfers;
|
||||
w.get_transfers(transfers);
|
||||
|
||||
const size_t n_inputs = input_key_images.size();
|
||||
CARROT_CHECK_AND_THROW(n_inputs, carrot::too_few_inputs, "no key images provided");
|
||||
CARROT_CHECK_AND_THROW(n_dests_per_tx, carrot::too_few_outputs, "sweep must have at least one destination");
|
||||
CARROT_CHECK_AND_THROW(n_dests_per_tx <= carrot::CARROT_MAX_TX_OUTPUTS,
|
||||
carrot::too_many_outputs, "too many sweep destinations per transaction");
|
||||
|
||||
// Make sure all destinations have the SAME asset_type
|
||||
std::string asset_type = "";
|
||||
for (const crypto::key_image &ki : input_key_images) {
|
||||
const wallet2::transfer_details &td = w.get_transfer_details(w.get_transfer_details(ki));
|
||||
if (asset_type == "")
|
||||
asset_type = td.asset_type;
|
||||
else
|
||||
CHECK_AND_ASSERT_THROW_MES(td.asset_type == asset_type, "Mixed asset_types in transaction inputs is forbidden");
|
||||
}
|
||||
|
||||
wallet2::transfer_container unused_transfers;
|
||||
w.get_transfers(unused_transfers, asset_type);
|
||||
|
||||
// Check that the key image is usable and isn't spent, collect amounts, and get subaddress account index
|
||||
std::vector<rct::xmr_amount> input_amounts;
|
||||
input_amounts.reserve(input_key_images.size());
|
||||
std::uint32_t subaddr_account = std::numeric_limits<std::uint32_t>::max();
|
||||
const auto best_transfers_by_ki = collect_non_burned_transfers_by_key_image(transfers);
|
||||
const auto best_transfers_by_ki = collect_non_burned_transfers_by_key_image(unused_transfers);
|
||||
for (const crypto::key_image &ki : input_key_images)
|
||||
{
|
||||
const auto ki_it = best_transfers_by_ki.find(ki);
|
||||
CHECK_AND_ASSERT_THROW_MES(ki_it != best_transfers_by_ki.cend(),
|
||||
__func__ << ": unknown key image");
|
||||
const wallet2::transfer_details &td = transfers.at(ki_it->second);
|
||||
const wallet2::transfer_details &td = unused_transfers.at(ki_it->second);
|
||||
CHECK_AND_ASSERT_THROW_MES(is_transfer_usable_for_input_selection(td,
|
||||
td.m_subaddr_index.major,
|
||||
/*from_subaddresses=*/{},
|
||||
/*ignore_above=*/MONEY_SUPPLY,
|
||||
/*ignore_below=*/0,
|
||||
top_block_index),
|
||||
__func__ << ": transfer not usable as an input");
|
||||
td.m_subaddr_index.major,
|
||||
/*from_subaddresses=*/{},
|
||||
/*ignore_above=*/MONEY_SUPPLY,
|
||||
/*ignore_below=*/0,
|
||||
top_block_index,
|
||||
asset_type),
|
||||
__func__ << ": transfer not usable as an input");
|
||||
input_amounts.push_back(td.amount());
|
||||
subaddr_account = std::min(subaddr_account, td.m_subaddr_index.major);
|
||||
}
|
||||
@@ -624,7 +804,7 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
|
||||
de.amount = 0;
|
||||
de.addr = address;
|
||||
de.is_subaddress = is_subaddress;
|
||||
de.asset_type = "SAL1";
|
||||
de.asset_type = asset_type;
|
||||
const bool is_selfsend_dest = build_payment_proposals(normal_payment_proposals,
|
||||
selfsend_payment_proposals,
|
||||
de,
|
||||
@@ -654,22 +834,23 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
|
||||
selected_inputs.push_back({input_amounts.at(ki_idx), input_key_images.at(ki_idx)});
|
||||
|
||||
carrot::make_carrot_transaction_proposal_v1_sweep(normal_payment_proposals,
|
||||
selfsend_payment_proposals,
|
||||
fee_per_weight,
|
||||
fee_quantization_mask,
|
||||
extra,
|
||||
tx_type,
|
||||
std::move(selected_inputs),
|
||||
change_address_spend_pubkey,
|
||||
{{subaddr_account, 0}, carrot::AddressDeriveType::Carrot},
|
||||
tx_proposal);
|
||||
selfsend_payment_proposals,
|
||||
fee_per_weight,
|
||||
fee_quantization_mask,
|
||||
extra,
|
||||
tx_type,
|
||||
std::move(selected_inputs),
|
||||
change_address_spend_pubkey,
|
||||
{{subaddr_account, 0}, carrot::AddressDeriveType::Carrot},
|
||||
asset_type,
|
||||
tx_proposal);
|
||||
|
||||
// populate the sources
|
||||
std::vector<size_t> selected_transfer_indices_sorted;
|
||||
for (const auto &ki: tx_proposal.key_images_sorted) {
|
||||
selected_transfer_indices_sorted.push_back(w.get_transfer_details(ki));
|
||||
selected_transfer_indices_sorted.push_back(w.get_transfer_details_from_container(ki, unused_transfers));
|
||||
}
|
||||
tx_proposal.sources = get_sources(transfers, selected_transfer_indices_sorted, "SAL1", w);
|
||||
tx_proposal.sources = get_sources(unused_transfers, selected_transfer_indices_sorted, asset_type, w);
|
||||
}
|
||||
|
||||
CARROT_CHECK_AND_THROW(ki_idx == input_key_images.size(),
|
||||
@@ -719,12 +900,13 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
|
||||
const rct::xmr_amount fee_quantization_mask,
|
||||
const std::vector<uint8_t> &extra,
|
||||
const cryptonote::transaction_type tx_type,
|
||||
const std::string &asset_type,
|
||||
const std::uint32_t subaddr_account,
|
||||
const std::set<uint32_t> &subaddr_indices,
|
||||
const std::uint64_t top_block_index)
|
||||
{
|
||||
wallet2::transfer_container transfers;
|
||||
w.get_transfers(transfers);
|
||||
w.get_transfers(transfers, asset_type);
|
||||
|
||||
const std::unordered_map<crypto::key_image, size_t> unburned_transfers_by_key_image =
|
||||
collect_non_burned_transfers_by_key_image(transfers);
|
||||
@@ -736,12 +918,13 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
|
||||
const wallet2::transfer_details &td = transfers.at(transfer_idx);
|
||||
|
||||
if (!is_transfer_usable_for_input_selection(td,
|
||||
subaddr_account,
|
||||
subaddr_indices,
|
||||
only_below ? only_below : MONEY_SUPPLY,
|
||||
1, // ignore_below
|
||||
top_block_index))
|
||||
continue;
|
||||
subaddr_account,
|
||||
subaddr_indices,
|
||||
only_below ? only_below : MONEY_SUPPLY,
|
||||
1, // ignore_below
|
||||
top_block_index,
|
||||
asset_type))
|
||||
continue;
|
||||
|
||||
const auto ki_it = unburned_transfers_by_key_image.find(td.m_key_image);
|
||||
if (ki_it == unburned_transfers_by_key_image.cend())
|
||||
@@ -776,6 +959,7 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
|
||||
const std::uint32_t priority,
|
||||
const std::vector<uint8_t> &extra,
|
||||
const cryptonote::transaction_type tx_type,
|
||||
const std::string &asset_type,
|
||||
const std::uint32_t subaddr_account,
|
||||
const std::set<uint32_t> &subaddr_indices)
|
||||
{
|
||||
@@ -797,6 +981,7 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
|
||||
fee_quantization_mask,
|
||||
extra,
|
||||
tx_type,
|
||||
asset_type,
|
||||
subaddr_account,
|
||||
subaddr_indices,
|
||||
top_block_index);
|
||||
@@ -815,8 +1000,14 @@ bool get_address_openings_x_y(
|
||||
if (return_output_map.find(rct::rct2pk(src.outputs[src.real_output].second.dest)) != return_output_map.end())
|
||||
{
|
||||
const auto &return_output = return_output_map.at(rct::rct2pk(src.outputs[src.real_output].second.dest));
|
||||
x_out = return_output.x;
|
||||
y_out = return_output.y;
|
||||
bool r = w.get_account().try_searching_for_opening_for_onetime_address(
|
||||
return_output.K_spend_pubkey,
|
||||
return_output.sum_g,
|
||||
return_output.sender_extension_t,
|
||||
x_out,
|
||||
y_out
|
||||
);
|
||||
CHECK_AND_ASSERT_THROW_MES(r, "Failed to obtain openings for onetime address (return_payment)");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1074,28 +1265,37 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details(
|
||||
|
||||
// serialize transaction
|
||||
cryptonote::transaction tx = carrot::store_carrot_to_transaction_v1(enotes,
|
||||
tx_proposal.key_images_sorted,
|
||||
tx_proposal.sources,
|
||||
tx_proposal.fee,
|
||||
tx_proposal.tx_type,
|
||||
tx_proposal.amount_burnt,
|
||||
change_masks,
|
||||
return_enote_out,
|
||||
encrypted_payment_id);
|
||||
tx_proposal.key_images_sorted,
|
||||
tx_proposal.sources,
|
||||
tx_proposal.fee,
|
||||
tx_proposal.tx_type,
|
||||
tx_proposal.amount_burnt,
|
||||
change_masks,
|
||||
tx_proposal.token,
|
||||
return_enote_out,
|
||||
encrypted_payment_id,
|
||||
const_cast<wallet2&>(w).get_current_hard_fork());
|
||||
|
||||
// store the binding tag
|
||||
tx.rollup_binding_tag = tx_proposal.rollup_binding_tag;
|
||||
|
||||
// Is this a ROLLUP TX? If so, store the rollup data
|
||||
if (tx_proposal.tx_type == cryptonote::transaction_type::ROLLUP)
|
||||
tx.layer2_rollup_data = tx_proposal.layer2_rollup_data;
|
||||
|
||||
// aliases
|
||||
hw::device &hwdev = acc_keys.get_device();
|
||||
const auto &sources = tx_proposal.sources;
|
||||
|
||||
// inputs
|
||||
uint64_t amount_in = 0;
|
||||
// uint64_t amount_in = 0;
|
||||
rct::carrot_ctkeyV inSk;
|
||||
inSk.reserve(sources.size());
|
||||
std::vector<uint64_t> inamounts;
|
||||
std::vector<unsigned int> index;
|
||||
for (const auto& src: sources)
|
||||
{
|
||||
amount_in += src.amount;
|
||||
// amount_in += src.amount;
|
||||
inamounts.push_back(src.amount);
|
||||
index.push_back(src.real_output);
|
||||
|
||||
@@ -1144,7 +1344,7 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details(
|
||||
}
|
||||
|
||||
// outputs
|
||||
uint64_t amount_out = 0;
|
||||
// uint64_t amount_out = 0;
|
||||
std::vector<uint64_t> outamounts;
|
||||
rct::keyV destinations;
|
||||
std::vector<std::string> destination_asset_types;
|
||||
@@ -1154,7 +1354,7 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details(
|
||||
destinations.push_back(rct::pk2rct(oep.enote.onetime_address));
|
||||
destination_asset_types.push_back(oep.enote.asset_type);
|
||||
outamounts.push_back(oep.amount);
|
||||
amount_out += oep.amount;
|
||||
// amount_out += oep.amount;
|
||||
|
||||
rct::ctkey key;
|
||||
key.mask = rct::sk2rct(oep.amount_blinding_factor);
|
||||
@@ -1255,7 +1455,10 @@ wallet2::pending_tx make_pending_carrot_tx(const carrot::CarrotTransactionPropos
|
||||
"make_pending_carrot_tx: tx proposal has unset tx type"
|
||||
);
|
||||
CARROT_CHECK_AND_THROW(n_inputs >= 1, carrot::too_few_inputs, "carrot tx proposal missing inputs");
|
||||
if (tx_proposal.tx_type == cryptonote::transaction_type::STAKE || tx_proposal.tx_type == cryptonote::transaction_type::BURN) {
|
||||
if (tx_proposal.tx_type == cryptonote::transaction_type::STAKE ||
|
||||
tx_proposal.tx_type == cryptonote::transaction_type::BURN ||
|
||||
tx_proposal.tx_type == cryptonote::transaction_type::CREATE_TOKEN ||
|
||||
tx_proposal.tx_type == cryptonote::transaction_type::ROLLUP) {
|
||||
CARROT_CHECK_AND_THROW(n_outputs == 1, carrot::too_few_outputs, "carrot tx proposal doesn't have correct number of outputs");
|
||||
} else {
|
||||
CARROT_CHECK_AND_THROW(n_outputs >= 2, carrot::too_few_outputs, "carrot tx proposal missing outputs");
|
||||
@@ -1288,7 +1491,8 @@ wallet2::pending_tx make_pending_carrot_tx(const carrot::CarrotTransactionPropos
|
||||
carrot::encrypted_payment_id_t encrypted_payment_id;
|
||||
std::vector<std::pair<bool, std::size_t>> sorted_payment_proposal_indices;
|
||||
carrot::get_output_enote_proposals_from_proposal_v1(tx_proposal,
|
||||
/*s_view_balance_dev=*/nullptr,
|
||||
&account.s_view_balance_dev,
|
||||
///*s_view_balance_dev=*/nullptr,
|
||||
&account.k_view_incoming_dev,
|
||||
output_enote_proposals,
|
||||
encrypted_payment_id,
|
||||
@@ -1360,11 +1564,16 @@ wallet2::pending_tx make_pending_carrot_tx(const carrot::CarrotTransactionPropos
|
||||
ptx.change_dts = change_dts;
|
||||
ptx.selected_transfers = std::move(selected_transfers);
|
||||
ptx.key_images = key_images_string.str();
|
||||
ptx.tx_key = shared_ephemeral_pubkey ? ephemeral_privkeys.at(0) : crypto::null_skey;
|
||||
if (shared_ephemeral_pubkey)
|
||||
ptx.additional_tx_keys = std::move(ephemeral_privkeys);
|
||||
else
|
||||
ptx.additional_tx_keys.clear();
|
||||
if (ephemeral_privkeys.size() == 1) {
|
||||
ptx.tx_key = ephemeral_privkeys.at(0);
|
||||
ptx.additional_tx_keys.clear();
|
||||
} else if (ephemeral_privkeys.size() == 2 && shared_ephemeral_pubkey) {
|
||||
ptx.tx_key = (ephemeral_privkeys.at(0) == crypto::null_skey) ? ephemeral_privkeys.at(1) : ephemeral_privkeys.at(0);
|
||||
ptx.additional_tx_keys.clear();
|
||||
} else {
|
||||
ptx.tx_key = crypto::null_skey;
|
||||
ptx.additional_tx_keys = std::move(ephemeral_privkeys);
|
||||
}
|
||||
ptx.dests = std::move(dests);
|
||||
ptx.multisig_sigs = {};
|
||||
ptx.multisig_tx_key_entropy = {};
|
||||
|
||||
@@ -55,8 +55,26 @@ carrot::select_inputs_func_t make_wallet2_single_transfer_input_selector(
|
||||
const std::uint64_t top_block_index,
|
||||
const bool allow_carrot_external_inputs_in_normal_transfers,
|
||||
const bool allow_pre_carrot_inputs_in_normal_transfers,
|
||||
const std::string &asset_type,
|
||||
std::set<size_t> &selected_transfer_indices_out);
|
||||
|
||||
std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposals_wallet2_createtoken(
|
||||
wallet2 &w,
|
||||
const cryptonote::token_metadata_t &token,
|
||||
const cryptonote::tx_destination_entry &de,
|
||||
const rct::xmr_amount fee_per_weight,
|
||||
const rct::xmr_amount fee_quantization_mask,
|
||||
const uint32_t subaddr_account,
|
||||
const std::set<uint32_t> &subaddr_indices,
|
||||
const std::uint64_t top_block_index);
|
||||
std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposals_wallet2_createtoken(
|
||||
wallet2 &w,
|
||||
const cryptonote::token_metadata_t &token,
|
||||
const cryptonote::tx_destination_entry &de,
|
||||
const std::uint32_t priority,
|
||||
const std::uint32_t subaddr_account,
|
||||
const std::set<uint32_t> &subaddr_indices);
|
||||
|
||||
std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposals_wallet2_transfer(
|
||||
wallet2 &w,
|
||||
std::vector<cryptonote::tx_destination_entry> dsts,
|
||||
@@ -109,6 +127,7 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
|
||||
const rct::xmr_amount fee_quantization_mask,
|
||||
const std::vector<uint8_t> &extra,
|
||||
const cryptonote::transaction_type tx_type,
|
||||
const std::string &asset_type,
|
||||
const std::uint32_t subaddr_account,
|
||||
const std::set<uint32_t> &subaddr_indices,
|
||||
const std::uint64_t top_block_index);
|
||||
@@ -121,6 +140,7 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
|
||||
const std::uint32_t priority,
|
||||
const std::vector<uint8_t> &extra,
|
||||
const cryptonote::transaction_type tx_type,
|
||||
const std::string &asset_type,
|
||||
const std::uint32_t subaddr_account,
|
||||
const std::set<uint32_t> &subaddr_indices);
|
||||
wallet2::pending_tx make_pending_carrot_tx(const carrot::CarrotTransactionProposalV1 &tx_proposal,
|
||||
|
||||
+988
-192
File diff suppressed because it is too large
Load Diff
+49
-9
@@ -496,9 +496,10 @@ private:
|
||||
bool m_coinbase;
|
||||
cryptonote::subaddress_index m_subaddr_index;
|
||||
cryptonote::transaction_type m_tx_type;
|
||||
bool m_is_carrot;
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
VERSION_FIELD(0)
|
||||
VERSION_FIELD(1)
|
||||
FIELD(m_tx_hash)
|
||||
VARINT_FIELD(m_amount)
|
||||
FIELD(m_asset_type)
|
||||
@@ -510,6 +511,11 @@ private:
|
||||
FIELD(m_coinbase)
|
||||
FIELD(m_subaddr_index)
|
||||
VARINT_FIELD(m_tx_type)
|
||||
if (version < 1) {
|
||||
m_is_carrot = false;
|
||||
return true;
|
||||
}
|
||||
FIELD(m_is_carrot)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
@@ -1197,6 +1203,7 @@ private:
|
||||
std::vector<crypto::public_key> get_subaddress_spend_public_keys(uint32_t account, uint32_t begin, uint32_t end) const;
|
||||
//std::string get_subaddress_as_str(const cryptonote::subaddress_index& index) const;
|
||||
std::string get_subaddress_as_str(const carrot::subaddress_index_extended& index) const;
|
||||
std::string get_subaddress_as_str(const carrot::subaddress_index_extended& index, const uint64_t height) const;
|
||||
std::string get_address_as_str() const { return get_subaddress_as_str({0, 0}); }
|
||||
std::string get_integrated_address_as_str(const crypto::hash8& payment_id, bool carrot = true) const;
|
||||
void add_subaddress_account(const std::string& label);
|
||||
@@ -1287,6 +1294,17 @@ private:
|
||||
bool parse_unsigned_tx_from_str(const std::string &unsigned_tx_st, unsigned_tx_set &exported_txs) const;
|
||||
bool load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set&)> accept_func = NULL);
|
||||
bool parse_tx_from_str(const std::string &signed_tx_st, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set &)> accept_func);
|
||||
|
||||
/**
|
||||
* Receives a hex-encoded metadata blob that contains the fields necessary to create a `sal_token_t` instance
|
||||
*/
|
||||
std::vector<wallet2::pending_tx> create_token(const std::string& token_ticker, const uint64_t token_supply, const std::string& token_metadata_hex, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices);
|
||||
|
||||
/**
|
||||
* Creates a new token using the provided `sal_token_t` struct (verifies the target first)
|
||||
*/
|
||||
std::vector<wallet2::pending_tx> create_token(const cryptonote::token_metadata_t& token, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices);
|
||||
|
||||
std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const std::string& source_asset, const std::string& dest_asset, const cryptonote::transaction_type tx_type, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const unique_index_container& subtract_fee_from_outputs = {}); // pass subaddr_indices by value on purpose
|
||||
std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, cryptonote::transaction_type tx_type, const std::string& asset_type, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices);
|
||||
std::vector<wallet2::pending_tx> create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, const cryptonote::transaction_type tx_type, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra);
|
||||
@@ -1308,7 +1326,8 @@ private:
|
||||
bool check_connection(uint32_t *version = NULL, bool *ssl = NULL, uint32_t timeout = 200000, bool *wallet_is_outdated = NULL, bool *daemon_is_outdated = NULL);
|
||||
bool check_version(uint32_t *version, bool *wallet_is_outdated, bool *daemon_is_outdated);
|
||||
bool check_hard_fork_version(cryptonote::network_type nettype, const std::vector<std::pair<uint8_t, uint64_t>> &daemon_hard_forks, const uint64_t height, const uint64_t target_height, bool *wallet_is_outdated, bool *daemon_is_outdated);
|
||||
void get_transfers(wallet2::transfer_container& incoming_transfers) const;
|
||||
void get_transfers(wallet2::transfer_container& incoming_transfers, const std::string& asset_type = "") const;
|
||||
void get_transfers_indices(std::set<size_t>& indices, const std::string& asset_type = "SAL1") const;
|
||||
void get_payments(const crypto::hash& payment_id, std::list<wallet2::payment_details>& payments, uint64_t min_height = 0, const boost::optional<uint32_t>& subaddr_account = boost::none, const std::set<uint32_t>& subaddr_indices = {}) const;
|
||||
void get_payments(std::list<std::pair<crypto::hash,wallet2::payment_details>>& payments, uint64_t min_height, uint64_t max_height = (uint64_t)-1, const boost::optional<uint32_t>& subaddr_account = boost::none, const std::set<uint32_t>& subaddr_indices = {}) const;
|
||||
void get_payments_out(std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>>& confirmed_payments,
|
||||
@@ -1470,7 +1489,7 @@ private:
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
MAGIC_FIELD("monero wallet cache")
|
||||
VERSION_FIELD(2)
|
||||
VERSION_FIELD(3)
|
||||
FIELD(m_blockchain)
|
||||
FIELD(m_transfers)
|
||||
FIELD(m_transfers_indices)
|
||||
@@ -1514,6 +1533,14 @@ private:
|
||||
}
|
||||
FIELD(m_background_sync_data)
|
||||
FIELD(m_subaddresses_extended)
|
||||
if (version == 2)
|
||||
{
|
||||
serializable_unordered_map<crypto::public_key, carrot::return_output_info_retired_t> old_roi;
|
||||
FIELD(old_roi)
|
||||
m_return_output_info = {};
|
||||
m_force_rescan = true;
|
||||
return true;
|
||||
}
|
||||
FIELD(m_return_output_info)
|
||||
END_SERIALIZE()
|
||||
|
||||
@@ -1548,6 +1575,8 @@ private:
|
||||
void set_default_priority(uint32_t p) { m_default_priority = p; }
|
||||
bool auto_refresh() const { return m_auto_refresh; }
|
||||
void auto_refresh(bool r) { m_auto_refresh = r; }
|
||||
bool force_rescan() const { return m_force_rescan; }
|
||||
void force_rescan(bool r) { m_force_rescan = r; }
|
||||
AskPasswordType ask_password() const { return m_ask_password; }
|
||||
void ask_password(AskPasswordType ask) { m_ask_password = ask; }
|
||||
void set_min_output_count(uint32_t count) { m_min_output_count = count; }
|
||||
@@ -1661,6 +1690,7 @@ private:
|
||||
size_t get_num_transfer_details() const { return m_transfers.size(); }
|
||||
const transfer_details &get_transfer_details(size_t idx) const;
|
||||
|
||||
uint8_t estimate_current_hard_fork(const uint64_t height = 0) const;
|
||||
uint8_t get_current_hard_fork();
|
||||
void get_hard_fork_info(uint8_t version, uint64_t &earliest_height);
|
||||
bool use_fork_rules(uint8_t version, int64_t early_blocks = 0);
|
||||
@@ -1913,10 +1943,13 @@ private:
|
||||
|
||||
bool get_pricing_record(oracle::pricing_record& pr, const uint64_t height);
|
||||
bool get_circulating_supply(std::vector<std::pair<std::string, std::string>> &amounts);
|
||||
bool get_yield_info(std::vector<cryptonote::yield_block_info>& ybi_data);
|
||||
|
||||
bool get_tokens(const std::string& regex, std::vector<std::string>& tokens);
|
||||
cryptonote::token_metadata_t get_token_info(const std::string& asset_type); // const;
|
||||
|
||||
typedef std::tuple<size_t, std::string, std::string, uint64_t, uint64_t> yield_payout_t;
|
||||
|
||||
bool get_yield_info(std::vector<cryptonote::yield_block_info>& ybi_data);
|
||||
bool get_yield_summary_info(uint64_t &total_burnt,
|
||||
uint64_t &total_supply,
|
||||
uint64_t &total_locked,
|
||||
@@ -2065,9 +2098,11 @@ private:
|
||||
bool is_spent(const transfer_details &td, bool strict = true) const;
|
||||
bool is_spent(size_t idx, bool strict = true) const;
|
||||
public:
|
||||
void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, bool rct, std::unordered_set<crypto::public_key> &valid_public_keys_cache);
|
||||
void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, std::vector<uint64_t> &rct_offsets, std::unordered_set<crypto::public_key> &valid_public_keys_cache, uint64_t &num_spendable_global_outs, uint64_t &num_outs);
|
||||
void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const transfer_container &transfers, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, bool rct, std::unordered_set<crypto::public_key> &valid_public_keys_cache);
|
||||
void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const transfer_container &transfers, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, std::vector<uint64_t> &rct_offsets, std::unordered_set<crypto::public_key> &valid_public_keys_cache, uint64_t &num_spendable_global_outs, uint64_t &num_outs);
|
||||
size_t get_transfer_details(const crypto::key_image &ki) const;
|
||||
size_t get_transfer_details_from_container(const crypto::key_image &ki, const transfer_container& container) const;
|
||||
bool get_rct_distribution(const bool use_global_outs, const std::string &rct_asset_type, uint64_t &start_height, std::vector<uint64_t> &distribution, uint64_t &num_spendable_global_outs);
|
||||
private:
|
||||
bool tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& tx_public_key, const rct::key& mask, uint64_t real_index, bool unlocked, std::unordered_set<crypto::public_key> &valid_public_keys_cache) const;
|
||||
bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) const;
|
||||
@@ -2104,8 +2139,6 @@ private:
|
||||
void register_devices();
|
||||
hw::device& lookup_device(const std::string & device_descriptor);
|
||||
|
||||
bool get_rct_distribution(const bool use_global_outs, const std::string &rct_asset_type, uint64_t &start_height, std::vector<uint64_t> &distribution, uint64_t &num_spendable_global_outs);
|
||||
|
||||
uint64_t get_segregation_fork_height() const;
|
||||
|
||||
std::map<std::pair<uint64_t, uint64_t>, size_t> create_output_tracker_cache() const;
|
||||
@@ -2192,6 +2225,7 @@ private:
|
||||
uint32_t m_default_priority;
|
||||
RefreshType m_refresh_type;
|
||||
bool m_auto_refresh;
|
||||
bool m_force_rescan = false;
|
||||
bool m_first_refresh_done;
|
||||
uint64_t m_refresh_from_block_height;
|
||||
// If m_refresh_from_block_height is explicitly set to zero we need this to differentiate it from the case that
|
||||
@@ -2293,7 +2327,7 @@ BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 12)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::multisig_tx_set, 1)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::payment_details, 5)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::payment_details, 6)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::pool_payment_details, 1)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 8)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 6)
|
||||
@@ -2622,6 +2656,12 @@ namespace boost
|
||||
a & x.m_amounts;
|
||||
a & x.m_asset_type;
|
||||
a & x.m_tx_type;
|
||||
if (ver < 6)
|
||||
{
|
||||
x.m_is_carrot = false;
|
||||
return;
|
||||
}
|
||||
a & x.m_is_carrot;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
|
||||
@@ -152,7 +152,7 @@ bool wallet2::search_for_rpc_payment(uint64_t credits_target, uint32_t n_threads
|
||||
{
|
||||
tpool.submit(&waiter, [&, i] {
|
||||
*(uint32_t*)(hashing_blob.data() + 39) = SWAP32LE(local_nonce-i);
|
||||
const uint8_t major_version = hashing_blob[0];
|
||||
// const uint8_t major_version = hashing_blob[0];
|
||||
crypto::rx_slow_hash(seed_hash.data, hashing_blob.data(), hashing_blob.size(), hash[i].data);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -439,7 +439,7 @@ namespace tools
|
||||
entry.subaddr_index = pd.m_subaddr_index;
|
||||
entry.subaddr_indices.push_back(pd.m_subaddr_index);
|
||||
//entry.address = m_wallet->get_subaddress_as_str(pd.m_subaddr_index);
|
||||
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
|
||||
bool is_carrot = m_wallet->estimate_current_hard_fork(entry.height) >= HF_VERSION_CARROT;
|
||||
entry.address = m_wallet->get_subaddress_as_str({{pd.m_subaddr_index.major, pd.m_subaddr_index.minor}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
|
||||
set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward(), pd.m_unlock_time);
|
||||
}
|
||||
@@ -473,7 +473,7 @@ namespace tools
|
||||
for (uint32_t i: pd.m_subaddr_indices)
|
||||
entry.subaddr_indices.push_back({pd.m_subaddr_account, i});
|
||||
//entry.address = m_wallet->get_subaddress_as_str({pd.m_subaddr_account, 0});
|
||||
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
|
||||
bool is_carrot = m_wallet->estimate_current_hard_fork(entry.height) >= HF_VERSION_CARROT;
|
||||
entry.address = m_wallet->get_subaddress_as_str({{pd.m_subaddr_account, 0}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
|
||||
set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward(), pd.m_unlock_time);
|
||||
}
|
||||
@@ -508,7 +508,7 @@ namespace tools
|
||||
for (uint32_t i: pd.m_subaddr_indices)
|
||||
entry.subaddr_indices.push_back({pd.m_subaddr_account, i});
|
||||
//entry.address = m_wallet->get_subaddress_as_str({pd.m_subaddr_account, 0});
|
||||
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
|
||||
bool is_carrot = m_wallet->estimate_current_hard_fork(entry.height) >= HF_VERSION_CARROT;
|
||||
entry.address = m_wallet->get_subaddress_as_str({{pd.m_subaddr_account, 0}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
|
||||
set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward(), pd.m_tx.unlock_time);
|
||||
}
|
||||
@@ -533,7 +533,7 @@ namespace tools
|
||||
entry.subaddr_index = pd.m_subaddr_index;
|
||||
entry.subaddr_indices.push_back(pd.m_subaddr_index);
|
||||
//entry.address = m_wallet->get_subaddress_as_str(pd.m_subaddr_index);
|
||||
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
|
||||
bool is_carrot = m_wallet->estimate_current_hard_fork(entry.height) >= HF_VERSION_CARROT;
|
||||
entry.address = m_wallet->get_subaddress_as_str({{pd.m_subaddr_index.major, pd.m_subaddr_index.minor}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
|
||||
set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward(), pd.m_unlock_time);
|
||||
}
|
||||
@@ -542,12 +542,25 @@ namespace tools
|
||||
{
|
||||
if (!m_wallet) return not_open(er);
|
||||
|
||||
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
|
||||
|
||||
bool is_carrot = m_wallet->estimate_current_hard_fork() >= HF_VERSION_CARROT;
|
||||
|
||||
std::vector<std::string> assets_in_wallet = m_wallet->list_asset_types();
|
||||
std::string asset_type = req.asset_type.empty() ? "SAL1" : boost::algorithm::to_upper_copy(req.asset_type);
|
||||
std::string asset_type = req.asset_type.empty() ? "SAL1" : req.asset_type;
|
||||
if (asset_type.length() > 4) {
|
||||
// Assume it is a token and format accordingly
|
||||
if (boost::algorithm::to_lower_copy(asset_type.substr(0, 3)) == "sal") {
|
||||
asset_type = "sal" + boost::algorithm::to_upper_copy(asset_type.substr(3));
|
||||
} else if (boost::algorithm::to_lower_copy(asset_type.substr(0, 3)) == "erc") {
|
||||
asset_type = "erc" + boost::algorithm::to_upper_copy(asset_type.substr(3));
|
||||
} else {
|
||||
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
|
||||
er.message = std::string("Invalid token asset_type '") + asset_type + "' specified";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// verify that the asset is in the list of in-wallet assets
|
||||
if (std::find(assets_in_wallet.begin(), assets_in_wallet.end(), asset_type) == assets_in_wallet.end()) {
|
||||
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
|
||||
er.message = std::string("Source asset '") + asset_type + "' not found in wallet";
|
||||
return false;
|
||||
}
|
||||
@@ -627,8 +640,6 @@ namespace tools
|
||||
{
|
||||
if (!m_wallet) return not_open(er);
|
||||
|
||||
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
|
||||
|
||||
try
|
||||
{
|
||||
THROW_WALLET_EXCEPTION_IF(req.account_index >= m_wallet->get_num_subaddress_accounts(), error::account_index_outofbound);
|
||||
@@ -651,14 +662,14 @@ namespace tools
|
||||
res.addresses.resize(res.addresses.size() + 1);
|
||||
auto& info = res.addresses.back();
|
||||
const cryptonote::subaddress_index index = {req.account_index, i};
|
||||
//info.address = m_wallet->get_subaddress_as_str(index);
|
||||
info.address = m_wallet->get_subaddress_as_str({{req.account_index, i}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
|
||||
info.address = m_wallet->get_subaddress_as_str({req.account_index, i});
|
||||
info.address_cn = req.cryptonote ? m_wallet->get_subaddress_as_str({{req.account_index, i}, carrot::AddressDeriveType::PreCarrot}) : "";
|
||||
info.address_carrot = req.carrot ? m_wallet->get_subaddress_as_str({{req.account_index, i}, carrot::AddressDeriveType::Carrot}) : "";
|
||||
info.label = m_wallet->get_subaddress_label(index);
|
||||
info.address_index = index.minor;
|
||||
info.used = std::find_if(transfers.begin(), transfers.end(), [&](const tools::wallet2::transfer_details& td) { return td.m_subaddr_index == index; }) != transfers.end();
|
||||
}
|
||||
//res.address = m_wallet->get_subaddress_as_str({req.account_index, 0});
|
||||
res.address = m_wallet->get_subaddress_as_str({{req.account_index, 0}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
|
||||
res.address = m_wallet->get_subaddress_as_str({req.account_index, 0});
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
@@ -701,8 +712,6 @@ namespace tools
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
|
||||
|
||||
std::vector<std::string> addresses;
|
||||
std::vector<uint32_t> address_indices;
|
||||
|
||||
@@ -713,8 +722,7 @@ namespace tools
|
||||
m_wallet->add_subaddress(req.account_index, req.label);
|
||||
uint32_t new_address_index = m_wallet->get_num_subaddresses(req.account_index) - 1;
|
||||
address_indices.push_back(new_address_index);
|
||||
//addresses.push_back(m_wallet->get_subaddress_as_str({req.account_index, new_address_index}));
|
||||
addresses.push_back(m_wallet->get_subaddress_as_str({{req.account_index, new_address_index}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot}));
|
||||
addresses.push_back(m_wallet->get_subaddress_as_str({req.account_index, new_address_index}));
|
||||
}
|
||||
|
||||
res.address = addresses[0];
|
||||
@@ -750,8 +758,6 @@ namespace tools
|
||||
{
|
||||
if (!m_wallet) return not_open(er);
|
||||
|
||||
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
|
||||
|
||||
try
|
||||
{
|
||||
res.total_balance = 0;
|
||||
@@ -772,8 +778,7 @@ namespace tools
|
||||
continue;
|
||||
wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::subaddress_account_info info;
|
||||
info.account_index = subaddr_index.major;
|
||||
//info.base_address = m_wallet->get_subaddress_as_str(subaddr_index);
|
||||
info.base_address = m_wallet->get_subaddress_as_str({{subaddr_index.major, subaddr_index.minor}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
|
||||
info.base_address = m_wallet->get_subaddress_as_str({{subaddr_index.major, 0}, carrot::AddressDeriveType::Auto});
|
||||
|
||||
//for (const auto& asset: asset_types) {
|
||||
info.balance = m_wallet->balance(subaddr_index.major, "SAL1", req.strict_balances);
|
||||
@@ -810,9 +815,7 @@ namespace tools
|
||||
{
|
||||
m_wallet->add_subaddress_account(req.label);
|
||||
res.account_index = m_wallet->get_num_subaddress_accounts() - 1;
|
||||
//res.address = m_wallet->get_subaddress_as_str({res.account_index, 0});
|
||||
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
|
||||
res.address = m_wallet->get_subaddress_as_str({{res.account_index, 0}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
|
||||
res.address = m_wallet->get_subaddress_as_str({res.account_index, 0});
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
@@ -1282,6 +1285,275 @@ namespace tools
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
bool wallet_rpc_server::on_create_token(const wallet_rpc::COMMAND_RPC_CREATE_TOKEN::request& req, wallet_rpc::COMMAND_RPC_CREATE_TOKEN::response& res, epee::json_rpc::error& er, const connection_context *ctx)
|
||||
{
|
||||
if (!m_wallet) return not_open(er);
|
||||
if (m_restricted)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_DENIED;
|
||||
er.message = "Command unavailable in restricted mode.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_wallet->get_current_hard_fork() < HF_VERSION_ENABLE_TOKENS) {
|
||||
er.code = WALLET_RPC_ERROR_CODE_DENIED;
|
||||
er.message = "Create_token is not available yet.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (req.ticker.empty()) {
|
||||
er.code = WALLET_RPC_ERROR_CODE_DENIED;
|
||||
er.message = "Empty ticker is not allowed.";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string asset_type = req.ticker;
|
||||
std::transform(asset_type.begin(), asset_type.end(), asset_type.begin(), ::toupper);
|
||||
if (asset_type.length() != 4) {
|
||||
er.code = WALLET_RPC_ERROR_CODE_DENIED;
|
||||
er.message = "Asset type must be exactly 4 characters long.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate characters
|
||||
for (char c : asset_type) {
|
||||
if (!((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))) {
|
||||
er.code = WALLET_RPC_ERROR_CODE_DENIED;
|
||||
er.message = "Asset type can only contain uppercase letters and digits.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if starts with "SAL"
|
||||
if (asset_type.substr(0, 3) == "SAL") {
|
||||
er.code = WALLET_RPC_ERROR_CODE_DENIED;
|
||||
er.message = "Asset type cannot start with 'SAL'.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// todo: update reserved names
|
||||
if (asset_type == "SAL" || asset_type == "SAL1" || asset_type == "SAL2" || asset_type == "BURN") {
|
||||
er.code = WALLET_RPC_ERROR_CODE_DENIED;
|
||||
er.message = "Asset type is reserved and cannot be used.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// get tokens
|
||||
std::vector<std::string> tokens;
|
||||
if(!m_wallet->get_tokens(asset_type, tokens)) {
|
||||
er.code = WALLET_RPC_ERROR_CODE_DENIED;
|
||||
er.message = "Failed to get token data.";
|
||||
return false;
|
||||
}
|
||||
if (!tokens.empty()) {
|
||||
er.code = WALLET_RPC_ERROR_CODE_DENIED;
|
||||
er.message = "Token already exists.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify supply amount is acceptable
|
||||
if (req.supply < 1 || req.supply > MONEY_SUPPLY)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_DENIED;
|
||||
er.message = "Supply amount must be between 1 and MONEY_SUPPLY.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// convert req.hash to hash
|
||||
crypto::hash hash_signature{};
|
||||
if (!req.hash.empty()) {
|
||||
if (!epee::string_tools::hex_to_pod(req.hash, hash_signature)) {
|
||||
//if (metadata.empty()){
|
||||
er.message = "Invalid hash format.";
|
||||
return false;
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
std::string token_metadata_hex;
|
||||
if (req.token_metadata_hex.empty()) {
|
||||
std::string json = (boost::format("{\"name\":\"%s\",\"size\":%d,\"hash\":\"%s\",\"url\":\"%s\"}") % req.name % req.size % req.hash % req.url).str();
|
||||
token_metadata_hex = epee::string_tools::buff_to_hex_nodelimer(json);
|
||||
} else {
|
||||
token_metadata_hex = req.token_metadata_hex;
|
||||
}
|
||||
|
||||
//cryptonote::sal_token_t sal_token{1, req.supply, req.size, req.name, req.url, hash_signature};
|
||||
//cryptonote::token_metadata_t token{1, asset_type, sal_token};
|
||||
|
||||
try
|
||||
{
|
||||
// Call the wallet create_token() method
|
||||
auto ptx_vector = m_wallet->create_token(
|
||||
asset_type,
|
||||
req.supply,
|
||||
token_metadata_hex,
|
||||
req.account_index,
|
||||
req.subaddr_indices
|
||||
);
|
||||
|
||||
if (ptx_vector.empty())
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_DENIED;
|
||||
er.message = "ptx_vector is empty after create_token.";
|
||||
return false;
|
||||
}
|
||||
if (ptx_vector.size() > 1)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_DENIED;
|
||||
er.message = "ptx_vector contains more than one transaction.";
|
||||
return false;
|
||||
}
|
||||
|
||||
return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.amounts_by_dest_list, res.fee_list, res.weight_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay,
|
||||
res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, res.spent_key_images_list, er);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
bool wallet_rpc_server::on_get_tokens(const wallet_rpc::COMMAND_RPC_GET_TOKENS::request& req, wallet_rpc::COMMAND_RPC_GET_TOKENS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
|
||||
{
|
||||
LOG_PRINT_L3("on_get_tokens starts");
|
||||
if (!m_wallet) return not_open(er);
|
||||
|
||||
// Get the full list of tokens from the wallet - filtered if necessary
|
||||
if (!m_wallet->get_tokens(req.regex, res.tokens)) {
|
||||
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
|
||||
er.message = "Failed to get token data from wallet.";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
bool wallet_rpc_server::on_return_payment(const wallet_rpc::COMMAND_RPC_RETURN_PAYMENT::request& req, wallet_rpc::COMMAND_RPC_RETURN_PAYMENT::response& res, epee::json_rpc::error& er, const connection_context *ctx)
|
||||
{
|
||||
LOG_PRINT_L3("on_return_payment starts");
|
||||
if (!m_wallet) return not_open(er);
|
||||
if (m_restricted)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_DENIED;
|
||||
er.message = "Command unavailable in restricted mode.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the TX hash we are interested in
|
||||
crypto::hash txid;
|
||||
if (!epee::string_tools::hex_to_pod(req.txid, txid))
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID;
|
||||
er.message = "TX ID has invalid format";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the transfers from the wallet
|
||||
tools::wallet2::transfer_container transfers;
|
||||
m_wallet->get_transfers(transfers);
|
||||
std::vector<size_t> transfers_indices = {};
|
||||
for (size_t idx=0; idx < transfers.size(); ++idx) {
|
||||
|
||||
// Get the TD by reference
|
||||
tools::wallet2::transfer_details& td = transfers[idx];
|
||||
|
||||
// Skip entries we don't care about
|
||||
if (td.m_txid != txid) continue;
|
||||
|
||||
// Found the specified entry - make sure we can return it
|
||||
if (td.m_tx.type != cryptonote::transaction_type::TRANSFER) {
|
||||
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
|
||||
er.message = tr("incorrect TX type for txid");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (td.m_tx.version >= TRANSACTION_VERSION_N_OUTS) {
|
||||
if (td.m_tx.return_address_list.empty() || td.m_tx.return_address_change_mask.empty()) {
|
||||
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
|
||||
er.message = tr("invalid return_address_list for txid");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Verify we have a valid return_address and tx_pubkey
|
||||
if (td.m_tx.return_address == crypto::null_pkey || td.m_tx.return_pubkey != crypto::null_pkey) {
|
||||
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
|
||||
er.message = tr("invalid return_address/return_pubkey for txid");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check that we have the key image information, and that it is usable
|
||||
if (!td.m_key_image_known || td.m_key_image_partial || td.m_spent || td.m_frozen) {
|
||||
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
|
||||
er.message = tr("key image is unavailable (partial / unknown / spent / frozen) for txid");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify that the correct asset type is being returned
|
||||
if (m_wallet->get_current_hard_fork() >= HF_VERSION_ENABLE_TOKENS) {
|
||||
if (td.asset_type == "SAL") {
|
||||
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
|
||||
er.message = tr("SAL may not be returned for txid");
|
||||
return false;
|
||||
}
|
||||
} else if (m_wallet->get_current_hard_fork() >= HF_VERSION_SALVIUM_ONE_PROOFS) {
|
||||
if (td.asset_type != "SAL1") {
|
||||
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
|
||||
er.message = tr("Only SAL1 may be returned for txid");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// We found the one(s) we were looking for - take a copy of the key_image, etc.
|
||||
transfers_indices.push_back(idx);
|
||||
}
|
||||
|
||||
// Check we have a valid key_image
|
||||
if (transfers_indices.empty()) {
|
||||
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
|
||||
er.message = tr("No matching transfers found in wallet");
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Call the wallet create_transactions_return() method
|
||||
auto ptx_vector = m_wallet->create_transactions_return(transfers_indices);
|
||||
if (ptx_vector.empty())
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_TX_NOT_POSSIBLE;
|
||||
er.message = "No transaction created";
|
||||
return false;
|
||||
}
|
||||
if (ptx_vector.size() > 2)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_TX_TOO_LARGE;
|
||||
er.message = "Multiple transactions are created, which is not supposed to happen.";
|
||||
return false;
|
||||
}
|
||||
if (ptx_vector[0].selected_transfers.size() != transfers_indices.size())
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_TX_NOT_POSSIBLE;
|
||||
er.message = tr("The transaction uses incorrect number of inputs, which is not supposed to happen");
|
||||
return false;
|
||||
}
|
||||
|
||||
//return fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.amounts_by_dest, res.fee, res.weight, res.multisig_txset, res.unsigned_txset, req.do_not_relay,
|
||||
// res.tx_hash, req.get_tx_hex, res.tx_blob, req.get_tx_metadata, res.tx_metadata, res.spent_key_images, er);
|
||||
return fill_response(ptx_vector, req.get_tx_key, res.tx_key_list, res.amount_list, res.amounts_by_dest_list, res.fee_list, res.weight_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay,
|
||||
res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, res.spent_key_images_list, er);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er, const connection_context *ctx)
|
||||
{
|
||||
|
||||
@@ -2109,8 +2381,6 @@ namespace tools
|
||||
{
|
||||
if (!m_wallet) return not_open(er);
|
||||
|
||||
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
|
||||
|
||||
crypto::hash payment_id;
|
||||
crypto::hash8 payment_id8;
|
||||
cryptonote::blobdata payment_id_blob;
|
||||
@@ -2151,7 +2421,7 @@ namespace tools
|
||||
rpc_payment.unlock_time = payment.m_unlock_time;
|
||||
rpc_payment.locked = !m_wallet->is_transfer_unlocked(payment.m_unlock_time, payment.m_block_height);
|
||||
rpc_payment.subaddr_index = payment.m_subaddr_index;
|
||||
//rpc_payment.address = m_wallet->get_subaddress_as_str(payment.m_subaddr_index);
|
||||
bool is_carrot = m_wallet->estimate_current_hard_fork(payment.m_block_height) >= HF_VERSION_CARROT;
|
||||
rpc_payment.address = m_wallet->get_subaddress_as_str({{payment.m_subaddr_index.major, payment.m_subaddr_index.minor}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
|
||||
res.payments.push_back(rpc_payment);
|
||||
}
|
||||
@@ -2163,8 +2433,6 @@ namespace tools
|
||||
{
|
||||
res.payments.clear();
|
||||
|
||||
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
|
||||
|
||||
if (!m_wallet) return not_open(er);
|
||||
|
||||
/* If the payment ID list is empty, we get payments to any payment ID (or lack thereof) */
|
||||
@@ -2182,7 +2450,7 @@ namespace tools
|
||||
rpc_payment.block_height = payment.second.m_block_height;
|
||||
rpc_payment.unlock_time = payment.second.m_unlock_time;
|
||||
rpc_payment.subaddr_index = payment.second.m_subaddr_index;
|
||||
//rpc_payment.address = m_wallet->get_subaddress_as_str(payment.second.m_subaddr_index);
|
||||
bool is_carrot = m_wallet->estimate_current_hard_fork(payment.second.m_block_height) >= HF_VERSION_CARROT;
|
||||
rpc_payment.address = m_wallet->get_subaddress_as_str({{payment.second.m_subaddr_index.major, payment.second.m_subaddr_index.minor}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
|
||||
rpc_payment.locked = !m_wallet->is_transfer_unlocked(payment.second.m_unlock_time, payment.second.m_block_height);
|
||||
res.payments.push_back(std::move(rpc_payment));
|
||||
@@ -2238,7 +2506,7 @@ namespace tools
|
||||
rpc_payment.block_height = payment.m_block_height;
|
||||
rpc_payment.unlock_time = payment.m_unlock_time;
|
||||
rpc_payment.subaddr_index = payment.m_subaddr_index;
|
||||
//rpc_payment.address = m_wallet->get_subaddress_as_str(payment.m_subaddr_index);
|
||||
bool is_carrot = m_wallet->estimate_current_hard_fork(payment.m_block_height) >= HF_VERSION_CARROT;
|
||||
rpc_payment.address = m_wallet->get_subaddress_as_str({{payment.m_subaddr_index.major, payment.m_subaddr_index.minor}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
|
||||
rpc_payment.locked = !m_wallet->is_transfer_unlocked(payment.m_unlock_time, payment.m_block_height);
|
||||
res.payments.push_back(std::move(rpc_payment));
|
||||
@@ -2369,6 +2637,55 @@ namespace tools
|
||||
epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().m_spend_secret_key);
|
||||
res.key = std::string(key.data(), key.size());
|
||||
}
|
||||
else if(req.key_type.compare("s_master") == 0)
|
||||
{
|
||||
if (m_wallet->watch_only())
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_WATCH_ONLY;
|
||||
er.message = "The wallet is watch-only. Cannot retrieve s_master key.";
|
||||
return false;
|
||||
}
|
||||
CHECK_IF_BACKGROUND_SYNCING();
|
||||
epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().s_master);
|
||||
res.key = std::string(key.data(), key.size());
|
||||
}
|
||||
else if(req.key_type.compare("k_prove_spend") == 0)
|
||||
{
|
||||
if (m_wallet->watch_only())
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_WATCH_ONLY;
|
||||
er.message = "The wallet is watch-only. Cannot retrieve k_prove_spend key.";
|
||||
return false;
|
||||
}
|
||||
CHECK_IF_BACKGROUND_SYNCING();
|
||||
epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().k_prove_spend);
|
||||
res.key = std::string(key.data(), key.size());
|
||||
}
|
||||
else if(req.key_type.compare("s_view_balance") == 0)
|
||||
{
|
||||
epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().s_view_balance);
|
||||
res.key = std::string(key.data(), key.size());
|
||||
}
|
||||
else if(req.key_type.compare("s_view_balance") == 0)
|
||||
{
|
||||
epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().s_view_balance);
|
||||
res.key = std::string(key.data(), key.size());
|
||||
}
|
||||
else if(req.key_type.compare("k_view_incoming") == 0)
|
||||
{
|
||||
epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().k_view_incoming);
|
||||
res.key = std::string(key.data(), key.size());
|
||||
}
|
||||
else if(req.key_type.compare("k_generate_image") == 0)
|
||||
{
|
||||
epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().k_generate_image);
|
||||
res.key = std::string(key.data(), key.size());
|
||||
}
|
||||
else if(req.key_type.compare("s_generate_address") == 0)
|
||||
{
|
||||
epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().s_generate_address);
|
||||
res.key = std::string(key.data(), key.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
er.message = "key_type " + req.key_type + " not found";
|
||||
@@ -3319,9 +3636,9 @@ namespace tools
|
||||
const auto &entry = ab[idx];
|
||||
std::string address;
|
||||
if (entry.m_has_payment_id)
|
||||
address = cryptonote::get_account_integrated_address_as_str(m_wallet->nettype(), entry.m_address, entry.m_payment_id, entry.m_is_carrot);
|
||||
address = cryptonote::get_account_integrated_address_as_str(m_wallet->nettype(), entry.m_address, entry.m_payment_id);
|
||||
else
|
||||
address = get_account_address_as_str(m_wallet->nettype(), entry.m_is_subaddress, entry.m_address, entry.m_is_carrot);
|
||||
address = get_account_address_as_str(m_wallet->nettype(), entry.m_is_subaddress, entry.m_address);
|
||||
res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx, address, entry.m_description});
|
||||
}
|
||||
}
|
||||
@@ -4945,6 +5262,32 @@ namespace tools
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
bool wallet_rpc_server::on_get_token_info(const wallet_rpc::COMMAND_RPC_GET_TOKEN_INFO::request& req, wallet_rpc::COMMAND_RPC_GET_TOKEN_INFO::response& res, epee::json_rpc::error& er, const connection_context *ctx)
|
||||
{
|
||||
// check
|
||||
if (!m_wallet) return not_open(er);
|
||||
|
||||
try {
|
||||
cryptonote::token_metadata_t token = m_wallet->get_token_info(req.asset_type);
|
||||
res.status = CORE_RPC_STATUS_OK;
|
||||
res.version = token.version;
|
||||
res.asset_type = token.asset_type;
|
||||
if (token.token.type() == typeid(cryptonote::sal_token_t)) {
|
||||
res.token_type = TOKEN_TYPE_SAL;
|
||||
res.sal_token = boost::get<cryptonote::sal_token_t>(token.token);
|
||||
} else if (token.token.type() == typeid(cryptonote::erc_token_t)) {
|
||||
res.token_type = TOKEN_TYPE_ERC20;
|
||||
res.erc_token = boost::get<cryptonote::erc_token_t>(token.token);
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
|
||||
er.message = "Failed to query daemon for token info";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
class t_daemon
|
||||
|
||||
@@ -88,6 +88,9 @@ namespace tools
|
||||
MAP_JON_RPC_WE("thaw", on_thaw, wallet_rpc::COMMAND_RPC_THAW)
|
||||
MAP_JON_RPC_WE("frozen", on_frozen, wallet_rpc::COMMAND_RPC_FROZEN)
|
||||
MAP_JON_RPC_WE("audit", on_audit, wallet_rpc::COMMAND_RPC_AUDIT)
|
||||
MAP_JON_RPC_WE("create_token", on_create_token, wallet_rpc::COMMAND_RPC_CREATE_TOKEN)
|
||||
MAP_JON_RPC_WE("get_tokens", on_get_tokens, wallet_rpc::COMMAND_RPC_GET_TOKENS)
|
||||
MAP_JON_RPC_WE("return_payment", on_return_payment, wallet_rpc::COMMAND_RPC_RETURN_PAYMENT)
|
||||
MAP_JON_RPC_WE("transfer", on_transfer, wallet_rpc::COMMAND_RPC_TRANSFER)
|
||||
MAP_JON_RPC_WE("transfer_split", on_transfer_split, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT)
|
||||
MAP_JON_RPC_WE("sign_transfer", on_sign_transfer, wallet_rpc::COMMAND_RPC_SIGN_TRANSFER)
|
||||
@@ -163,6 +166,7 @@ namespace tools
|
||||
MAP_JON_RPC_WE("estimate_tx_size_and_weight", on_estimate_tx_size_and_weight, wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT)
|
||||
MAP_JON_RPC_WE("get_default_fee_priority", on_get_default_fee_priority, wallet_rpc::COMMAND_RPC_GET_DEFAULT_FEE_PRIORITY)
|
||||
MAP_JON_RPC_WE("get_version", on_get_version, wallet_rpc::COMMAND_RPC_GET_VERSION)
|
||||
MAP_JON_RPC_WE("get_token_info", on_get_token_info, wallet_rpc::COMMAND_RPC_GET_TOKEN_INFO)
|
||||
MAP_JON_RPC_WE("setup_background_sync", on_setup_background_sync, wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC)
|
||||
MAP_JON_RPC_WE("start_background_sync", on_start_background_sync, wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC)
|
||||
MAP_JON_RPC_WE("stop_background_sync", on_stop_background_sync, wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC)
|
||||
@@ -187,6 +191,9 @@ namespace tools
|
||||
bool on_thaw(const wallet_rpc::COMMAND_RPC_THAW::request& req, wallet_rpc::COMMAND_RPC_THAW::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_frozen(const wallet_rpc::COMMAND_RPC_FROZEN::request& req, wallet_rpc::COMMAND_RPC_FROZEN::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_audit(const wallet_rpc::COMMAND_RPC_AUDIT::request& req, wallet_rpc::COMMAND_RPC_AUDIT::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_create_token(const wallet_rpc::COMMAND_RPC_CREATE_TOKEN::request& req, wallet_rpc::COMMAND_RPC_CREATE_TOKEN::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_get_tokens(const wallet_rpc::COMMAND_RPC_GET_TOKENS::request& req, wallet_rpc::COMMAND_RPC_GET_TOKENS::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_return_payment(const wallet_rpc::COMMAND_RPC_RETURN_PAYMENT::request& req, wallet_rpc::COMMAND_RPC_RETURN_PAYMENT::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_transfer_split(const wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_sign_transfer(const wallet_rpc::COMMAND_RPC_SIGN_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_SIGN_TRANSFER::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
@@ -260,6 +267,7 @@ namespace tools
|
||||
bool on_estimate_tx_size_and_weight(const wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT::request& req, wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_get_default_fee_priority(const wallet_rpc::COMMAND_RPC_GET_DEFAULT_FEE_PRIORITY::request& req, wallet_rpc::COMMAND_RPC_GET_DEFAULT_FEE_PRIORITY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_get_version(const wallet_rpc::COMMAND_RPC_GET_VERSION::request& req, wallet_rpc::COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_get_token_info(const wallet_rpc::COMMAND_RPC_GET_TOKEN_INFO::request& req, wallet_rpc::COMMAND_RPC_GET_TOKEN_INFO::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_setup_background_sync(const wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_start_background_sync(const wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_stop_background_sync(const wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
|
||||
@@ -139,9 +139,13 @@ namespace wallet_rpc
|
||||
{
|
||||
uint32_t account_index;
|
||||
std::vector<uint32_t> address_index;
|
||||
bool carrot;
|
||||
bool cryptonote;
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(account_index)
|
||||
KV_SERIALIZE(address_index)
|
||||
KV_SERIALIZE_OPT(carrot, true);
|
||||
KV_SERIALIZE_OPT(cryptonote, true);
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<request_t> request;
|
||||
@@ -149,12 +153,16 @@ namespace wallet_rpc
|
||||
struct address_info
|
||||
{
|
||||
std::string address;
|
||||
std::string address_cn;
|
||||
std::string address_carrot;
|
||||
std::string label;
|
||||
uint32_t address_index;
|
||||
bool used;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(address)
|
||||
KV_SERIALIZE(address_cn)
|
||||
KV_SERIALIZE(address_carrot)
|
||||
KV_SERIALIZE(label)
|
||||
KV_SERIALIZE(address_index)
|
||||
KV_SERIALIZE(used)
|
||||
@@ -585,6 +593,82 @@ namespace wallet_rpc
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
struct split_transfer_response
|
||||
{
|
||||
std::list<std::string> tx_hash_list;
|
||||
std::list<std::string> tx_key_list;
|
||||
std::list<uint64_t> amount_list;
|
||||
std::list<amounts_list> amounts_by_dest_list;
|
||||
std::list<uint64_t> fee_list;
|
||||
std::list<uint64_t> weight_list;
|
||||
std::list<std::string> tx_blob_list;
|
||||
std::list<std::string> tx_metadata_list;
|
||||
std::string multisig_txset;
|
||||
std::string unsigned_txset;
|
||||
std::list<key_image_list> spent_key_images_list;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(tx_hash_list)
|
||||
KV_SERIALIZE(tx_key_list)
|
||||
KV_SERIALIZE(amount_list)
|
||||
KV_SERIALIZE_OPT(amounts_by_dest_list, decltype(amounts_by_dest_list)())
|
||||
KV_SERIALIZE(fee_list)
|
||||
KV_SERIALIZE(weight_list)
|
||||
KV_SERIALIZE(tx_blob_list)
|
||||
KV_SERIALIZE(tx_metadata_list)
|
||||
KV_SERIALIZE(multisig_txset)
|
||||
KV_SERIALIZE(unsigned_txset)
|
||||
KV_SERIALIZE(spent_key_images_list)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
|
||||
struct COMMAND_RPC_RETURN_PAYMENT
|
||||
{
|
||||
struct request_t
|
||||
{
|
||||
std::string txid;
|
||||
/* std::list<transfer_destination> destinations;
|
||||
std::string source_asset;
|
||||
std::string dest_asset;
|
||||
uint8_t tx_type;
|
||||
uint32_t account_index;
|
||||
std::set<uint32_t> subaddr_indices;
|
||||
std::set<uint32_t> subtract_fee_from_outputs;
|
||||
uint32_t priority;
|
||||
uint64_t ring_size;
|
||||
uint64_t unlock_time;
|
||||
std::string payment_id; */
|
||||
bool get_tx_key;
|
||||
bool do_not_relay;
|
||||
bool get_tx_hex;
|
||||
bool get_tx_metadata;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(txid)
|
||||
// KV_SERIALIZE(destinations)
|
||||
// KV_SERIALIZE(source_asset)
|
||||
// KV_SERIALIZE(dest_asset)
|
||||
// KV_SERIALIZE(tx_type)
|
||||
// KV_SERIALIZE(account_index)
|
||||
// KV_SERIALIZE(subaddr_indices)
|
||||
// KV_SERIALIZE_OPT(subtract_fee_from_outputs, decltype(subtract_fee_from_outputs)())
|
||||
// KV_SERIALIZE(priority)
|
||||
// KV_SERIALIZE_OPT(ring_size, (uint64_t)0)
|
||||
// KV_SERIALIZE(unlock_time)
|
||||
// KV_SERIALIZE(payment_id)
|
||||
KV_SERIALIZE(get_tx_key)
|
||||
KV_SERIALIZE_OPT(do_not_relay, false)
|
||||
KV_SERIALIZE_OPT(get_tx_hex, false)
|
||||
KV_SERIALIZE_OPT(get_tx_metadata, false)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<request_t> request;
|
||||
|
||||
typedef split_transfer_response response_t;
|
||||
typedef epee::misc_utils::struct_init<response_t> response;
|
||||
};
|
||||
|
||||
struct COMMAND_RPC_TRANSFER
|
||||
{
|
||||
struct request_t
|
||||
@@ -629,34 +713,6 @@ namespace wallet_rpc
|
||||
typedef epee::misc_utils::struct_init<response_t> response;
|
||||
};
|
||||
|
||||
struct split_transfer_response
|
||||
{
|
||||
std::list<std::string> tx_hash_list;
|
||||
std::list<std::string> tx_key_list;
|
||||
std::list<uint64_t> amount_list;
|
||||
std::list<amounts_list> amounts_by_dest_list;
|
||||
std::list<uint64_t> fee_list;
|
||||
std::list<uint64_t> weight_list;
|
||||
std::list<std::string> tx_blob_list;
|
||||
std::list<std::string> tx_metadata_list;
|
||||
std::string multisig_txset;
|
||||
std::string unsigned_txset;
|
||||
std::list<key_image_list> spent_key_images_list;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(tx_hash_list)
|
||||
KV_SERIALIZE(tx_key_list)
|
||||
KV_SERIALIZE(amount_list)
|
||||
KV_SERIALIZE_OPT(amounts_by_dest_list, decltype(amounts_by_dest_list)())
|
||||
KV_SERIALIZE(fee_list)
|
||||
KV_SERIALIZE(weight_list)
|
||||
KV_SERIALIZE(tx_blob_list)
|
||||
KV_SERIALIZE(tx_metadata_list)
|
||||
KV_SERIALIZE(multisig_txset)
|
||||
KV_SERIALIZE(unsigned_txset)
|
||||
KV_SERIALIZE(spent_key_images_list)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
struct COMMAND_RPC_TRANSFER_SPLIT
|
||||
{
|
||||
@@ -2882,5 +2938,105 @@ namespace wallet_rpc
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<response_t> response;
|
||||
};
|
||||
|
||||
struct COMMAND_RPC_CREATE_TOKEN
|
||||
{
|
||||
struct request_t
|
||||
{
|
||||
std::string ticker;
|
||||
uint64_t supply;
|
||||
uint32_t account_index;
|
||||
std::set<uint32_t> subaddr_indices;
|
||||
std::string name;
|
||||
std::string url;
|
||||
std::string hash;
|
||||
uint64_t size;
|
||||
std::string token_metadata_hex;
|
||||
|
||||
bool get_tx_keys;
|
||||
bool do_not_relay;
|
||||
bool get_tx_hex;
|
||||
bool get_tx_metadata;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(ticker)
|
||||
KV_SERIALIZE(supply)
|
||||
KV_SERIALIZE(account_index)
|
||||
KV_SERIALIZE(subaddr_indices)
|
||||
KV_SERIALIZE_OPT(name, (std::string)"")
|
||||
KV_SERIALIZE_OPT(url, (std::string)"")
|
||||
KV_SERIALIZE_OPT(hash, (std::string)"")
|
||||
KV_SERIALIZE_OPT(size, (uint64_t)0)
|
||||
KV_SERIALIZE_OPT(token_metadata_hex, (std::string)"")
|
||||
KV_SERIALIZE_OPT(get_tx_keys, false)
|
||||
KV_SERIALIZE_OPT(do_not_relay, false)
|
||||
KV_SERIALIZE_OPT(get_tx_hex, false)
|
||||
KV_SERIALIZE_OPT(get_tx_metadata, false)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<request_t> request;
|
||||
|
||||
typedef split_transfer_response response_t;
|
||||
typedef epee::misc_utils::struct_init<response_t> response;
|
||||
};
|
||||
|
||||
struct COMMAND_RPC_GET_TOKENS
|
||||
{
|
||||
struct request_t
|
||||
{
|
||||
std::string regex;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE_OPT(regex, (std::string)"")
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<request_t> request;
|
||||
|
||||
struct response_t
|
||||
{
|
||||
std::vector<std::string> tokens;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(tokens)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<response_t> response;
|
||||
};
|
||||
|
||||
struct COMMAND_RPC_GET_TOKEN_INFO
|
||||
{
|
||||
struct request_t
|
||||
{
|
||||
std::string asset_type;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(asset_type)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<request_t> request;
|
||||
|
||||
struct response_t
|
||||
{
|
||||
std::string status;
|
||||
uint8_t token_type; // erc20 or sal
|
||||
uint8_t version;
|
||||
std::string asset_type;
|
||||
cryptonote::sal_token_t sal_token;
|
||||
cryptonote::erc_token_t erc_token;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(status)
|
||||
KV_SERIALIZE(token_type)
|
||||
KV_SERIALIZE(version)
|
||||
KV_SERIALIZE(asset_type)
|
||||
if (token_type == TOKEN_TYPE_ERC20) {
|
||||
KV_SERIALIZE(erc_token)
|
||||
} else {
|
||||
KV_SERIALIZE(sal_token)
|
||||
}
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<response_t> response;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,11 +65,12 @@ public:
|
||||
, const cryptonote::difficulty_type& cumulative_difficulty
|
||||
, const uint64_t& coins_generated
|
||||
, uint64_t num_rct_outs
|
||||
, oracle::asset_type_counts& cum_rct_by_asset_type
|
||||
, oracle::asset_type_counts_v2& cum_rct_by_asset_type
|
||||
, const crypto::hash& blk_hash
|
||||
, uint64_t slippage_total
|
||||
, uint64_t yield_total
|
||||
, uint64_t audit_total
|
||||
, uint64_t token_total
|
||||
, const cryptonote::network_type nettype
|
||||
, cryptonote::yield_block_info& ybi
|
||||
, cryptonote::audit_block_info& abi
|
||||
|
||||
@@ -87,11 +87,12 @@ namespace
|
||||
const difficulty_type& cumulative_difficulty,
|
||||
const uint64_t& coins_generated,
|
||||
uint64_t num_rct_outs,
|
||||
oracle::asset_type_counts& cum_rct_by_asset_type,
|
||||
oracle::asset_type_counts_v2& cum_rct_by_asset_type,
|
||||
const crypto::hash& blk_hash,
|
||||
uint64_t slippage_total,
|
||||
uint64_t yield_total,
|
||||
uint64_t audit_total,
|
||||
uint64_t token_total,
|
||||
const cryptonote::network_type nettype,
|
||||
cryptonote::yield_block_info& ybi,
|
||||
cryptonote::audit_block_info& abi
|
||||
@@ -178,10 +179,10 @@ static std::unique_ptr<cryptonote::BlockchainAndPool> init_blockchain(const std:
|
||||
|
||||
const block *blk = &boost::get<block>(ev);
|
||||
auto blk_hash = get_block_hash(*blk);
|
||||
oracle::asset_type_counts num_rct_outs_by_asset_type;
|
||||
oracle::asset_type_counts_v2 num_rct_outs_by_asset_type;
|
||||
cryptonote::yield_block_info ybi;
|
||||
cryptonote::audit_block_info abi;
|
||||
bdb->add_block(*blk, 1, 1, 1, 0, 0, num_rct_outs_by_asset_type, blk_hash, 0, 0, 0, cryptonote::FAKECHAIN, ybi, abi);
|
||||
bdb->add_block(*blk, 1, 1, 1, 0, 0, num_rct_outs_by_asset_type, blk_hash, 0, 0, 0, 0, cryptonote::FAKECHAIN, ybi, abi);
|
||||
}
|
||||
|
||||
bool r = bap->blockchain.init(bdb, nettype, true, test_options, 2, nullptr);
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user