Compare commits
151 Commits
v1.0.0-rc4
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 1998d79c3e | |||
| ea4d5a9132 | |||
| 585f7b9d05 | |||
| b7c707bd4e | |||
| b57d2c338c | |||
| 28069fc84b | |||
| d7ec62cdbe | |||
| 244fc7acaa | |||
| 3716330ac7 | |||
| c4e7f6e0f0 | |||
| c0862ea66e | |||
| 67b41240d2 | |||
| 16110603a1 | |||
| 83934efaad | |||
| 6fb167308c | |||
| d6f95156bc | |||
| 35804afc90 | |||
| bc59547714 | |||
| f2b6068d8b | |||
| c820967cba | |||
| 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 | |||
| 845d46d7b3 | |||
| e4f8929f2c | |||
| c03402d525 | |||
| d8b18b17f6 | |||
| 5347266ed9 | |||
| 419c26adbf | |||
| 7727bdb51e | |||
| 37a58646fd | |||
| 0e747e3f15 | |||
| caf8a0a962 | |||
| 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: |
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
# Use Ubuntu 24.04 as base
|
||||
FROM ubuntu:24.04 AS builder
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# 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 automake autoconf \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Clone the develop branch
|
||||
WORKDIR /opt
|
||||
RUN git clone --recursive -b develop https://github.com/salvium/salvium.git
|
||||
WORKDIR /opt/salvium
|
||||
|
||||
# make the script runnable
|
||||
RUN chmod +x make_releases.sh
|
||||
|
||||
# Make sure the output folder exists
|
||||
RUN mkdir ~/releases
|
||||
|
||||
# Expose the releases directory for copying zip files to the host
|
||||
VOLUME ["~/releases"]
|
||||
|
||||
ENTRYPOINT ["/opt/salvium/make_releases.sh"]
|
||||
|
||||
|
||||
# To access the generated zip files on your host, run the container with:
|
||||
# docker run -v ~/releases:/root/releases <image>
|
||||
# This will copy the zip files to your host's ~/salvium-releases directory.
|
||||
@@ -1,6 +1,6 @@
|
||||
# Salvium One v1.0.0
|
||||
# Salvium One v1.1.1
|
||||
|
||||
Copyright (c) 2023-2024, Salvium
|
||||
Copyright (c) 2023-2025, Salvium
|
||||
Portions Copyright (c) 2014-2023, The Monero Project
|
||||
Portions Copyright (c) 2012-2013 The Cryptonote developers.
|
||||
|
||||
@@ -47,7 +47,7 @@ As with many development projects, the repository on GitHub is considered to be
|
||||
|
||||
## Supporting the project
|
||||
|
||||
Salvium is a 100% community-sponsored endeavor. If you want to join our efforts, the easiest thing you can do is support the project financially. Go to [https://salvium.io/donate/](https://salvium.io/donate/) for more information.
|
||||
Salvium is a 100% community-sponsored endeavor. If you want to join our efforts, the easiest thing you can do is support the project financially. Go to [https://salvium.io/donate](https://salvium.io/donate) for more information.
|
||||
|
||||
## License
|
||||
|
||||
@@ -172,7 +172,7 @@ invokes cmake commands as needed.
|
||||
|
||||
```bash
|
||||
cd salvium
|
||||
git checkout v1.0.0
|
||||
git checkout v1.1.1
|
||||
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.1.1
|
||||
```
|
||||
|
||||
* 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.1.1'. 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.1.1
|
||||
```
|
||||
|
||||
* If you are on a 64-bit system, run:
|
||||
|
||||
@@ -6,7 +6,7 @@ macro(CHECK_LINKER_FLAG flag VARIABLE)
|
||||
message(STATUS "Looking for ${flag} linker flag")
|
||||
endif()
|
||||
|
||||
set(_cle_source ${CMAKE_SOURCE_DIR}/cmake/CheckLinkerFlag.c)
|
||||
set(_cle_source ${CMAKE_CURRENT_SOURCE_DIR}/cmake/CheckLinkerFlag.c)
|
||||
|
||||
set(saved_CMAKE_C_FLAGS ${CMAKE_C_FLAGS})
|
||||
set(CMAKE_C_FLAGS "${flag}")
|
||||
|
||||
@@ -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 @@
|
||||
e032e42ebac862bf90d71f0a231d9f3ddb5583e321ec153ee05144d87629980b salvium-v1.1.0-macos-aarch64.zip
|
||||
0f31f09cf7be38b50a35510172bc94b7a4fb07c7107c79c4f055754c282c99f5 salvium-v1.1.0-macos-x86_64.zip
|
||||
4424fd93391daab7eee47c54ab7aad7810a16f4c866209d41ba016d984605ffb salvium-v1.1.0-ubuntu22.04-linux-aarch64.zip
|
||||
d1a5138f892189dfccc1d51d72ce24147fe6f1a2a5d465d350651426a71282a4 salvium-v1.1.0-ubuntu22.04-linux-x86_64.zip
|
||||
bb9c9175726b82e061a6a332a27a6845805be4779928df3abbbd5c0f54691c9a salvium-v1.1.0-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
|
||||
@@ -0,0 +1,5 @@
|
||||
23a03277e922c3f41ba6ddb0efc7581c3287d9f3faa2ddd19cc2d018a6797701 salvium-v1.1.0-rc5-ubuntu22.04-linux-x86_64.zip
|
||||
94409b190eae890792b2d04cfffe148828dc23d658cd54a8d7802b12a9f274a4 salvium-v1.1.0-rc5-ubuntu22.04-linux-aarch64.zip
|
||||
76cc02603cb21cd0729f0e5c9a39bb32310428b15580458ec8d74dcba39c9319 salvium-v1.1.0-rc5-macos-x86_64.zip
|
||||
dc477718bfb370ecbafebf3e1736df5978200302f13542374f1b1fd43e07ab45 salvium-v1.1.0-rc5-macos-aarch64.zip
|
||||
0e9b81eeaa32a4709a1cbd4c198721455a3c8cc86c1f1915cfc74af7885f8fee salvium-v1.1.0-rc5-win64.zip
|
||||
@@ -0,0 +1,5 @@
|
||||
b712adec8fa6bbcbe461b0748649e4c9e4fb61934fc1adb064779afed28c0758 salvium-v1.1.0-rc6-macos-aarch64.zip
|
||||
8635955ff784f936a8b8de5be267e5dcdc0b55dfbf0b6f65ba24d098d8b8ab00 salvium-v1.1.0-rc6-macos-x86_64.zip
|
||||
79b65ba9a074aeba87f03d83a07c3a6c51815d376049b3136a38c1a33260bfac salvium-v1.1.0-rc6-ubuntu22.04-linux-aarch64.zip
|
||||
9031763ad60d1c8c572c76c5cb51a96b1680b9708c287264388f3d411bda464f salvium-v1.1.0-rc6-ubuntu22.04-linux-x86_64.zip
|
||||
e3a8e53e092041ed9ed32c98d5d0ecf548f42232402748f862371ba21e1f1f9f salvium-v1.1.0-rc6-win64.zip
|
||||
@@ -0,0 +1,5 @@
|
||||
d22bbe19fa5e7eb7ebaf95eee336d73cbd59ded7b5e737213e89a8b84d5791eb salvium-v1.1.1-macos-aarch64.zip
|
||||
262e2bdffc3c4fee89ec79451a6181175671db5874726d359077c4479754076e salvium-v1.1.1-macos-x86_64.zip
|
||||
72b37fa30df6b136dba380b88e9ccc4d66804d8286a221a87e944e337f4ba593 salvium-v1.1.1-ubuntu22.04-linux-aarch64.zip
|
||||
33321419bb426507de0f5de7c1977e436ca34bf4db620fa00eede1fa318b7994 salvium-v1.1.1-ubuntu22.04-linux-x86_64.zip
|
||||
33e7c1e1bc4e5a1f9c40482eba993a5efb97a8feedfb36dca863bbb5bd9e794f salvium-v1.1.1-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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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,35 @@ 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") {
|
||||
// check here
|
||||
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 +257,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 +273,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 +294,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 +417,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 +453,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 +476,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 +539,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:
|
||||
@@ -1381,12 +1564,7 @@ namespace cryptonote
|
||||
|
||||
for (const auto &o: tx.vout)
|
||||
{
|
||||
if (hf_version >= HF_VERSION_ENFORCE_CARROT)
|
||||
{
|
||||
// from v10, require outputs be carrot outputs
|
||||
CHECK_AND_ASSERT_MES(o.target.type() == typeid(txout_to_carrot_v1), false, "wrong variant type: "
|
||||
<< o.target.type().name() << ", expected txout_to_carrot_v1 in transaction id=" << get_transaction_hash(tx));
|
||||
} else if (hf_version >= HF_VERSION_CARROT) {
|
||||
if (hf_version >= HF_VERSION_CARROT) {
|
||||
if (tx.type != cryptonote::transaction_type::PROTOCOL) {
|
||||
CHECK_AND_ASSERT_MES(o.target.type() == typeid(txout_to_carrot_v1), false, "wrong variant type: "
|
||||
<< o.target.type().name() << ", expected txout_to_carrot_v1 in transaction id=" << get_transaction_hash(tx));
|
||||
@@ -1411,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);
|
||||
|
||||
@@ -175,7 +175,8 @@ namespace cryptonote
|
||||
|
||||
uint64_t seed_height;
|
||||
crypto::hash seed_hash;
|
||||
if(!m_phandler->get_block_template(bl, m_mine_address, di, height, expected_reward, extra_nonce, seed_height, seed_hash))
|
||||
crypto::public_key miner_reward_tx_key;
|
||||
if(!m_phandler->get_block_template(bl, m_mine_address, di, height, expected_reward, extra_nonce, seed_height, seed_hash, miner_reward_tx_key))
|
||||
{
|
||||
LOG_ERROR("Failed to get_block_template(), stopping mining");
|
||||
m_forced_stop = true;
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace cryptonote
|
||||
struct i_miner_handler
|
||||
{
|
||||
virtual bool handle_block_found(block& b, block_verification_context &bvc) = 0;
|
||||
virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash) = 0;
|
||||
virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key) = 0;
|
||||
protected:
|
||||
~i_miner_handler(){};
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
+38
-39
@@ -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
|
||||
@@ -88,8 +89,14 @@
|
||||
#define PREMINE_AMOUNT_UPFRONT ((uint64_t)650000000000000ull) // 3.4% of MONEY_SUPPLY
|
||||
#define PREMINE_AMOUNT_MONTHLY ((uint64_t)65000000000000ull) // 8.6%/24 of MONEY_SUPPLY
|
||||
|
||||
#define TREASURY_SAL1_MINT_AMOUNT ((uint64_t)65000000000000ull) // 650K
|
||||
#define TREASURY_SAL1_MINT_COUNT 16 // 16 times
|
||||
#define TREASURY_SAL1_MINT_AMOUNT ((uint64_t)130000000000000ull) // 1.3M
|
||||
#define TREASURY_SAL1_MINT_COUNT 8 // 8 times
|
||||
|
||||
#define CREATE_TOKEN_LOCK_PERIOD 10
|
||||
|
||||
// HF11 block reward split
|
||||
#define BLOCK_REWARD_TREASURY_PCT 25 // to treasury, 21600-block unlock
|
||||
#define BLOCK_REWARD_STAKER_PCT 20 // to stakers via yield
|
||||
|
||||
#define DIFFICULTY_TARGET_V2 120 // seconds
|
||||
#define DIFFICULTY_TARGET_V1 60 // seconds - before first fork
|
||||
@@ -244,14 +251,14 @@
|
||||
#define HF_VERSION_AUDIT2 8
|
||||
#define HF_VERSION_AUDIT2_PAUSE 9
|
||||
#define HF_VERSION_CARROT 10
|
||||
#define HF_VERSION_ENFORCE_CARROT 11
|
||||
#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 18
|
||||
#define STAGENET_VERSION 1
|
||||
|
||||
#define PER_KB_FEE_QUANTIZATION_DECIMALS 8
|
||||
@@ -316,26 +323,19 @@ namespace config
|
||||
const uint64_t STAKE_LOCK_PERIOD = 30*24*30;
|
||||
const uint64_t TREASURY_SAL1_MINT_PERIOD = 30*24*30; // 1 month of blocks
|
||||
|
||||
std::string const TREASURY_ADDRESS_CARROT = "SC11ksHLFhy7H1yMk9bUZvADG1Z9ZkR1T5QMknm3RbGBbgdPkyanB2WBb5TER3MsiwJC5BnyoiYs2DBcvAfAm6JQ537iNKdtvm";
|
||||
std::string const TREASURY_ADDRESS = "SaLvdZR6w1A21sf2Wh6jYEh1wzY4GSbT7RX6FjyPsnLsffWLrzFQeXUXJcmBLRWDzZC2YXeYe5t7qKsnrg9FpmxmEcxPHsEYfqA";
|
||||
|
||||
// treasury payout {tx-key, output-key, anchor_enc, vie_tag} tuples
|
||||
// treasury payout {tx-key, output-key, anchor_enc, view_tag} tuples
|
||||
const std::map<uint64_t, std::tuple<std::string, std::string, std::string, std::string>> TREASURY_SAL1_MINT_OUTPUT_DATA = {
|
||||
{1000000ULL, {"a1bdd1da651fbbb845232816e1aa2d4ff29b790f10bbd4f574a012f1199e15a4", "b0733ab6f251b16458efa9ebb3fb99bd54d43173b5768fe9ffc42e0fe46ae3a8", "00", "00"}},
|
||||
{1020000ULL, {"47996eccbcc078b06d0f6ece37bf3a700c2bd60adfdd898b22096f16a9ad315c", "fd6bcceb4799ee067d59b97a6f66a0f9a70f134220259d3b4d6a2278ba4aca4c", "00", "00"}},
|
||||
{1040000ULL, {"a3e6754a849b80c21a77e6065fefdae29eeeabf17c407453356244a00545bdb8", "3d395454df1452d715d27190e022b20395871c99af578f7251c3f9752e0274a6", "00", "00"}},
|
||||
{1060000ULL, {"0d5e97a910e0f9c606ad9c711b6595aaed142d857cde2efa519112b9a29240d5", "56c29e28bdcf4f20b4b45906b93ae7c4bf9ee82e18cd45543cb69a14ce5efb88", "00", "00"}},
|
||||
{1080000ULL, {"495fa363de88915aa8b74818c4b80715a882a688b4f7127ab7cd3b6885f3567a", "d42dfe0da5579c82e8255eba8c0a17170023f14a6a5030da6abf9f10abb52cbb", "00", "00"}},
|
||||
{1100000ULL, {"85ea10ec40390e4f406446fb519e974d89536154045c6df28bb3b538b254e20d", "0ce2b7dd3a8ce8b596889dac8081a62f98fd70f1f043944ab4ac592c3c59e77b", "00", "00"}},
|
||||
{1120000ULL, {"40f201b38a319dda81e7201e57fea7924067a4a332ed71b8e51ec29ac2d67310", "8289aa6963b98d1034e94eae55d8be6b33d0a88f14f174ebcbaec70837986c7c", "00", "00"}},
|
||||
{1140000ULL, {"c5a648cc7846341357b7b4653a58f9eb4800d88b5de587bceec7a5c28f98d05a", "3f308a203845d88e5e728fcebcdcea1f90e2f424d461617993c672a6138ad2d8", "00", "00"}},
|
||||
{1160000ULL, {"4c51d6550b8eeb6cc8f0d395cc83a5f90ec2a4d86501b3f68da48d618ccf5711", "53f0bd8cebeefb3a88fffa5d7f6ad43d4712608ded561732467ca499df940454", "00", "00"}},
|
||||
{1180000ULL, {"ce2f5d82118fed03d5e269e285fc16189a6cd34f38999e5c055a5dea5fce61bc", "f7fc6948b194d9bd6f2df6ecb83f04e6c8d1a2556a63fedb310a4631fe1bfc42", "00", "00"}},
|
||||
{1200000ULL, {"6248028fd77fb02b5c6ea72dea10b417891a2da7aaf9565aed382e063b4981a3", "63986e1177499bdb23cd49afb519ec18f38cb1b0c386220b376d8ffdc2e37890", "00", "00"}},
|
||||
{1220000ULL, {"6adcb695aa5d6d01133c68900f29e501e9549816e827ea0c164bbc78f3534dd6", "6a440ccb18f5e703e8000de3865ac40d4c18f081270d32eef377dc831f28d8d0", "00", "00"}},
|
||||
{1240000ULL, {"b97a4d2259480f34f20e41c489ab5c2e5ae9ee84d8672a7eff8012f2260e121e", "e6eb9147ff40e22209d321d0f1bfbfe20acf5ceb6b9d0bfb13688ad28aa1232e", "00", "00"}},
|
||||
{1260000ULL, {"4fd214602a36902f22d16927244c456e8cc5a406a9570131f138a028214ffdf0", "34060b8bd96009b9b298280ebd84fa9587fa8c9df6fb5ad0270fb6cd2098885c", "00", "00"}},
|
||||
{1280000ULL, {"9d60178ec6d6599d7a31298f2559fb9c3111f2c70494b3a1638db877ea55b808", "7985ed03856a929663e954738d0938713407717835f760c7ca4d54844a128c91", "00", "00"}},
|
||||
{1300000ULL, {"cd65718eab234bf419332e53bd2f48e2ade70af48c5e126ab5080321e1493dfc", "581cb4cca7a0a029ee2cac51dfc00a0c3a657d2eaf67ed3c6ae7bacc11b4f007", "00", "00"}},
|
||||
{334750, {"1b2cd3ff56aa77c0cbab0473bfb96697ebdd0b25ad230136bfe41d5dc1ef265f","718cf02eabca157fd7ad7f8537db217624bfe1ca99dd09e758357e7000a5e57a","789cca3def51fb879eb7fbca271869b7","79bd0c"}},
|
||||
{356350, {"b51acbf35265d09f3cfb83dcabde2746991ddf0d30b5a4ecc34043b349a77031","9dc0d2e9534cdccf83494687c55c67c8c1b29834acf97cce53124a08a9549231","588ebc2918d06c009a18a28a8ab76694","ab8c23"}},
|
||||
{377950, {"771c6865980493846cbb049b34f72b937878cf799ae1126775df35064cf53526","60d340613c7721aeb03f2de1b56e2916d6cb023cc668acaca00e1606e0bb7f64","198bb0e5880c8abf8108158284ca7637","9b93f2"}},
|
||||
{399550, {"d9159f9bb654230382a69bb7a739c14f9d506c21dba5be9f4cdf66126ed1b24e","c3b34e7f9977f31f659abd9306b650416a674ed7bcfb8c75b257040a1a06b8b9","6f0de181716e0a9e100228d58f11d42c","4c87fd"}},
|
||||
{421150, {"1e2ab1ae204e7a9375f5b57fed2524e2d1a4702c4bbc6d420eb65d92af44c60c","826ecca0e2837860a6d2be89a410ff6aab22c5f5a66bd0a318de3abe646aa26a","bad663aa8253c40d00de0a2d1dfca60f","4a04d7"}},
|
||||
{442750, {"18267940a2b37c82850f3fb83d935281ee5cc436b0797ec10ca26c9cc3c0ef01","77bdec7bbc4cabd84cc8cf0666e1fdfe299480b9b2a4cf52cc959d728fa899ab","a8807121e33471cf18aa82af4826c9a7","d35d35"}},
|
||||
{464350, {"9ab50223bfecec2508e8233540dc13c3fc7e15a8d77ecb90939afee96859724d","675a1b3f2a2cd2bd0e7ec073c426125d95e12ad13e541bf019925c481c2bca15","591d2f2331832c347654ab255ad97e2d","d87c9c"}},
|
||||
{485950, {"c0d7299fe0b873687e7319f1a0d1f67712ef3ccf6a579d96c5f99743add6f029","dd3d17b5145b56377e023c169ae858cdf48bd6c23e8b2a77e02765ce316e3ca7","6a9e74df01762e2b32db1dc4d69dc3e9","75debe"}}
|
||||
};
|
||||
|
||||
// Hash domain separators
|
||||
@@ -428,26 +428,20 @@ namespace config
|
||||
"KWv3Vo1/Gny+1vfaxsXhBQiG1KlHkafNGarzoL0WHW4ocqaaqF5iv8i35A==\n"
|
||||
"-----END PUBLIC KEY-----\n";
|
||||
|
||||
std::string const TREASURY_ADDRESS = "SaLvTyLFta9BiAXeUfFkKvViBkFt4ay5nEUBpWyDKewYggtsoxBbtCUVqaBjtcCDyY1euun8Giv7LLEgvztuurLo5a6Km1zskZn36";
|
||||
std::string const TREASURY_ADDRESS_CARROT = "SC1TouvX6e4HmkAqsU6AAXLRjgeZnnKHjSpVfMHepTXramNMT2P47AsDmteLH81wdPR2DwMg3cxKvgrhUBeDSUW6MhM3sQb92we";
|
||||
std::string const TREASURY_ADDRESS = "SaLvTyL2pN2SCAPRxwDQ7qjhdg46VbhZrGZTp2wHKJ5sK434a8ivEH35eWp2FTcmyW6LY6ExfBb9chmQ9xAL1eJyZ5FQjtQGTis3v";
|
||||
|
||||
// treasury payout {tx-key, output-key, anchor_enc, vie_tag} tuples
|
||||
// check address type before future development
|
||||
const std::map<uint64_t, std::tuple<std::string, std::string, std::string, std::string>> TREASURY_SAL1_MINT_OUTPUT_DATA = {
|
||||
{1100, {"0d82afb3ae9c5de25a06da49e24e64d37ba05d6144f9f6153cea325c61407106","81440913c1484891d25010986806a62d1fae1b2fe045e3ac1396413638d594f1","40864bbf80397c05516368a1397eb5c3","220bda"}},
|
||||
{1120, {"a032f2de4d3b5d880d3a636d9786a4a2f586bee360c323bc7521f987a2ada51b","cf8e5eeb5d37996d86c9c4c88fabaabc1ec77bab9cd5ce610a61b49f1e079ce2","9877a989e0216f76a8cdf0a568e57d45","017549"}},
|
||||
{1140, {"51da7a4c34aa20a800ada615a931fbba5fd1b9f7df78f0f911097ad9d99dc230","dd784a5bf1adf9c01cb44946ca094ed4d38f8d9cccf8edfdab397db3b7631dbf","97314be452f184b54f16a558ee8a7dbe","357ac3"}},
|
||||
{1160, {"a0cf49118c62cea834fabae83e12f26939ce170f308adadacf7275226e5cdf6d","7ec9cb2dfac8dfa45bab1bab2b1e31a4291a9e3d939e9c193740cc191d1b720d","4e950acc71ffd4f3835ec79202dc46fd","e7da3a"}},
|
||||
{1180, {"5193e35f6cd3ff8408a442be5eaf4aa1871419ecb0c485546e9d995c4c9c2730","39fb9792b88290b3377036a150a6681334ebe5cb201e2005d2c1d7b212b3df4d","cb458163806a277c7b3513df0d5b0028","1939b5"}},
|
||||
{1200, {"a2c71a7381bc48576f4cb4313cb6264d2a6e82cd2ddcbbd5460bce1db8d5021e","d0c46f29f4bbdd5538d64a438c0ab93cb0586e67fbfc7d1671f0c9809d6a7fac","af38cc2ddccb9229728ef704e360e15a","8a8dc1"}},
|
||||
{1220, {"8358105081510e907c98da59636eef777460513d827aba400efbb81d82aead4c","b2b8e2f9bb13e7527d0ee1bc3ebf0ca5057139ee4f9302251455b9f27dca3b16","dc226161830e952b2533fe34b1dd3c48","fb7c67"}},
|
||||
{1240, {"90b0bc153b97a93eefd59af9b47d26c0c91aadda6203de91a15f2f76fc949074","06e6a11b8742ee78beafe89f894fb9078a514ee6dd4e099182ac982ead37eacc","4fbfd0f3c2b8d514f2c675b37b9f0b6f","3308ad"}},
|
||||
{1260, {"cd10148b3175804b17e3c68a6a5556364d585dee89953904b32f005a5f6d8f14","d9d6f64119776e9c97fe2fb540cdb5f40bea0c40127fda0f1f2b831cb7fa7d2b","9c21b83d29324e2d91774a623219ae28","1d647c"}},
|
||||
{1280, {"5963697782de7db5111d12c853f3fec0dd53f597d80cc376c7945f1d4afa224b","cc5e5b1273c10016dc855ec1d9bfa0bcf4736a748125f5341717ad9d080fbba0","19dd1f9a0cac118564e2ccb9f36be53a","7ab2c2"}},
|
||||
{1300, {"cf1d4d2d21b7b3f1a54889e0688a4654687e28a302a2ac3ad046ac2ad323f97a","5b5926da5e9199f190e86073ebd09ccb867faf1e9887c9e52a47c194915b2575","5775fa47af3c7226f2ec3017470410c9","2b03b3"}},
|
||||
{1320, {"b565ec8842f3e6c22c7a23eb23f0e5684b462d2261681dff7b2ce71ab02ff170","4f94cf4d811574c9fcc27888cc972ebc6c01ae97146b9882d397b77c9232d1fd","9d142686627c918a26b6c6853b911143","685b18"}},
|
||||
{1340, {"50d1b1bcacf36ba14b23b3827e36a02a7610dd3199a9f1b9d0a23ebcbd1c2d54","38818e41cf0ce7d54391978801c58087ddd5fe517dadb5e61d840f27a7a81a52","55328f4bff5120d44c5d76fc5a9dfdbd","d17be2"}},
|
||||
{1360, {"7657bf97a9be9beac5f999a1e2c1226613849c41c12df5d7185d4d6613a27328","63d96a47f29fab0e727d13905cc843df40ccb76b14dc48857d0dd9ec0873e8c5","ab4f7ffa6431452f0fac7eec33e89e3e","5acd14"}},
|
||||
{1380, {"845e4cbe9b9d2462fae2225da698d6a4e18340f06b312545ee9cfda8127abd41","cc65f1f22f863b31f3c4a53ac3987186c2a699f387a2bf08d8dfed54037912c6","bda3aa3bdd2b42e8183c1013e4350c98","d4a418"}},
|
||||
{1400, {"4b11413fcba49df61537427ff35fdd47ee12a8467751236ff22681bbea343d2c","32d22bba59559489f422298ae653ef55ec8c87e8705daf4c89da7187c8574cbd","8868e1c1d788ce4560beb4afbe77aa5a","5a35d9"}}
|
||||
{1100, {"71336a480440ed24a53b2cfd5a5292d1e618c3e843637227883ea2cc42fb346f","a135f59a05ea9e33539e4502b187b4789cc7fba79616c6902a902cc6601f0359","7f1c6970232254a9b13f7f063df2a853","62073c"}},
|
||||
{1120, {"2cc49b182addc0106b601c9876c01a4b06532c05f9dd9179b2c4f47e5c7c0d74","fd62e59324389f37d0bb628a39f413c11be34d572ec3a40f465e008b9dfd5e0c","fbf8584222e299bb748009eaf2177123","b76a8d"}},
|
||||
{1140, {"50f1dd5244bb6fdc62b5a18b868a4dc9e9ad15f4a7a54e5a98084a3a5213c846","942de90a3347df3e962d9b0a2745f8aedb265a8caf73aabef9aa713f4ec3e493","933702e29d680bf21ad559a2d3243621","dae32b"}},
|
||||
{1160, {"114a1fef3a03c81b3b6308fb4e51db42d86945d8f71ee8798749a4aa2d8bfe57","fd77f0289b7e25ae68c86f4cd2c72ac0211a4d0e7a31c84b8d58a33bbd3c511b","d8048bb76b9313768a4c69100cba6d43","46b0a3"}},
|
||||
{1180, {"cf7fe9d97a7a4cb881bff1d37bb6981bcb0691649c465d660522a43f14b32849","b76908d56108e7111374f60993e9411def7bb2899118f20f49e67a801936a1ad","f203a5acd48f93f1451d32d70716e585","d72097"}},
|
||||
{1200, {"415e8fa4fb6bed8bc59591883f5e098dde6179e980eccfc4cbb3a64c4168cf47","5426c588d7c5750c76b526ef268b45f7414e2ddea40218ee92d123564c5114c4","fb1926bbef8c70ebd796ae77dffca07d","351edf"}},
|
||||
{1220, {"1f47cce00e8e1c77cb3ff007491d261dae75de5bbfdbe0f4913d74a8503aaa5e","4d8aac299306989aa169982d89457852a1bab858c668c9b740d96b682bb1961b","d8c0be7926ce704290cfd8d5e0500f33","750003"}},
|
||||
{1240, {"3afd93c684638d54e68f5d8d417863035f12482251feea5cf7515725a2bb0f4f","7895e6baded473b093b57993ae3497a47b0ea78cf35b37d1387f226f445d9018","76a0c37e6f3d9d508c4bce1d7162514e","09e146"}}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -480,6 +474,7 @@ namespace config
|
||||
"KWv3Vo1/Gny+1vfaxsXhBQiG1KlHkafNGarzoL0WHW4ocqaaqF5iv8i35A==\n"
|
||||
"-----END PUBLIC KEY-----\n";
|
||||
|
||||
std::string const TREASURY_ADDRESS_CARROT = ""; // TODO: generate stagenet Carrot treasury address ?
|
||||
std::string const TREASURY_ADDRESS = "fuLMowH85abK8nz9BBMEem7MAfUbQu4aSHHUV9j5Z86o6Go9Lv2U5ZQiJCWPY9R9HA8p5idburazjAhCqDngLo7fYPCD9ciM9ee1A";
|
||||
|
||||
// treasury payout {tx-key, output-key, anchor_enc, view_tag} tuples
|
||||
@@ -534,6 +529,7 @@ namespace cryptonote
|
||||
uint64_t TREASURY_SAL1_MINT_PERIOD;
|
||||
std::map<uint8_t, std::pair<uint64_t, std::pair<std::string, std::string>>> const AUDIT_HARD_FORKS;
|
||||
std::string TREASURY_ADDRESS;
|
||||
std::string TREASURY_ADDRESS_CARROT;
|
||||
std::map<uint64_t, std::tuple<std::string, std::string, std::string, std::string>> TREASURY_SAL1_MINT_OUTPUT_DATA;
|
||||
};
|
||||
inline const config_t& get_config(network_type nettype)
|
||||
@@ -557,6 +553,7 @@ namespace cryptonote
|
||||
::config::TREASURY_SAL1_MINT_PERIOD,
|
||||
::config::AUDIT_HARD_FORKS,
|
||||
::config::TREASURY_ADDRESS,
|
||||
::config::TREASURY_ADDRESS_CARROT,
|
||||
::config::TREASURY_SAL1_MINT_OUTPUT_DATA
|
||||
};
|
||||
static const config_t testnet = {
|
||||
@@ -578,6 +575,7 @@ namespace cryptonote
|
||||
::config::testnet::TREASURY_SAL1_MINT_PERIOD,
|
||||
::config::testnet::AUDIT_HARD_FORKS,
|
||||
::config::testnet::TREASURY_ADDRESS,
|
||||
::config::testnet::TREASURY_ADDRESS_CARROT,
|
||||
::config::testnet::TREASURY_SAL1_MINT_OUTPUT_DATA
|
||||
};
|
||||
static const config_t stagenet = {
|
||||
@@ -599,6 +597,7 @@ namespace cryptonote
|
||||
::config::stagenet::TREASURY_SAL1_MINT_PERIOD,
|
||||
::config::stagenet::AUDIT_HARD_FORKS,
|
||||
::config::stagenet::TREASURY_ADDRESS,
|
||||
::config::stagenet::TREASURY_ADDRESS_CARROT,
|
||||
::config::stagenet::TREASURY_SAL1_MINT_OUTPUT_DATA
|
||||
};
|
||||
switch (nettype)
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <limits>
|
||||
#include <boost/asio/dispatch.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
@@ -38,6 +39,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"
|
||||
@@ -56,6 +58,8 @@
|
||||
#include "crypto/hash.h"
|
||||
#include "cryptonote_core.h"
|
||||
#include "ringct/rctSigs.h"
|
||||
#include "carrot_core/payment_proposal.h"
|
||||
#include "carrot_impl/format_utils.h"
|
||||
#include "common/perf_timer.h"
|
||||
#include "common/notify.h"
|
||||
#include "common/varint.h"
|
||||
@@ -66,6 +70,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 +883,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 +982,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 +1354,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");
|
||||
}
|
||||
@@ -1403,14 +1412,22 @@ bool Blockchain::prevalidate_protocol_transaction(const block& b, uint64_t heigh
|
||||
CHECK_AND_ASSERT_MES(b.protocol_tx.vin[0].type() == typeid(txin_gen), false, "coinbase protocol transaction in the block has the wrong type");
|
||||
CHECK_AND_ASSERT_MES(b.protocol_tx.version > 1, false, "Invalid coinbase protocol transaction version");
|
||||
|
||||
if (hf_version >= HF_VERSION_CARROT) {
|
||||
CHECK_AND_ASSERT_MES(b.protocol_tx.version == TRANSACTION_VERSION_CARROT || 2, false, "protocol transaction has wrong version");
|
||||
CHECK_AND_ASSERT_MES(b.protocol_tx.type == cryptonote::transaction_type::PROTOCOL, false, "protocol transaction has wrong type");
|
||||
}
|
||||
// Work out what the HF version _was_ when the STAKE outputs were created
|
||||
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_ENFORCE_CARROT) {
|
||||
CHECK_AND_ASSERT_MES(b.protocol_tx.version == TRANSACTION_VERSION_CARROT, false, "protocol transaction has wrong version");
|
||||
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 {
|
||||
CHECK_AND_ASSERT_MES(b.protocol_tx.version == 2, false, "protocol transaction has wrong version");
|
||||
}
|
||||
CHECK_AND_ASSERT_MES(b.protocol_tx.type == cryptonote::transaction_type::PROTOCOL, false, "protocol transaction has wrong type");
|
||||
} else {
|
||||
hf_version_submitted = hf_version;
|
||||
}
|
||||
|
||||
// for v2 txes (ringct), we only accept empty rct signatures for protocol transactions,
|
||||
@@ -1433,8 +1450,7 @@ bool Blockchain::prevalidate_protocol_transaction(const block& b, uint64_t heigh
|
||||
return false;
|
||||
}
|
||||
|
||||
CHECK_AND_ASSERT_MES(check_output_types(b.protocol_tx, hf_version), false, "protocol transaction has invalid output type(s) in block " << get_block_hash(b));
|
||||
|
||||
CHECK_AND_ASSERT_MES(check_output_types(b.protocol_tx, hf_version_submitted), false, "protocol transaction has invalid output type(s) in block " << get_block_hash(b));
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
@@ -1475,7 +1491,53 @@ std::tuple<bool, size_t> Blockchain::validate_treasury_payout(const transaction&
|
||||
return {false, 0};
|
||||
}
|
||||
|
||||
return {true, output - tx.vout.begin()};
|
||||
// Burning-bug guards: verify view_tag, anchor_enc, and D_e from the hardcoded config
|
||||
// so a miner cannot tamper with those fields while keeping K_o correct.
|
||||
|
||||
// Check view_tag
|
||||
carrot::view_tag_t expected_view_tag{};
|
||||
if (!epee::string_tools::hex_to_pod(viewtag, expected_view_tag)) {
|
||||
MERROR_VER("treasury payout: failed to deserialize expected view_tag");
|
||||
return {false, 0};
|
||||
}
|
||||
if (target.view_tag != expected_view_tag) {
|
||||
MERROR_VER("treasury payout view_tag mismatch (burning bug: K_o correct but view_tag tampered)");
|
||||
return {false, 0};
|
||||
}
|
||||
|
||||
// Check anchor_enc
|
||||
carrot::encrypted_janus_anchor_t expected_anchor_enc{};
|
||||
if (!epee::string_tools::hex_to_pod(anchor_enc, expected_anchor_enc)) {
|
||||
MERROR_VER("treasury payout: failed to deserialize expected anchor_enc");
|
||||
return {false, 0};
|
||||
}
|
||||
if (0 != memcmp(&target.encrypted_janus_anchor, &expected_anchor_enc, sizeof(expected_anchor_enc))) {
|
||||
MERROR_VER("treasury payout anchor_enc mismatch (burning bug: K_o correct but anchor tampered)");
|
||||
return {false, 0};
|
||||
}
|
||||
|
||||
// Check D_e (enote ephemeral pubkey)
|
||||
mx25519_pubkey expected_tx_key_raw{};
|
||||
if (!epee::string_tools::hex_to_pod(tx_key, expected_tx_key_raw)) {
|
||||
MERROR_VER("treasury payout: failed to deserialize expected tx_key");
|
||||
return {false, 0};
|
||||
}
|
||||
const crypto::public_key expected_De = carrot::raw_byte_convert<crypto::public_key>(expected_tx_key_raw);
|
||||
const size_t output_idx = output - tx.vout.begin();
|
||||
const crypto::public_key single_tx_pubkey = cryptonote::get_tx_pub_key_from_extra(tx);
|
||||
const std::vector<crypto::public_key> additional_tx_pubkeys = cryptonote::get_additional_tx_pub_keys_from_extra(tx);
|
||||
crypto::public_key actual_De{};
|
||||
if (!additional_tx_pubkeys.empty() && output_idx < additional_tx_pubkeys.size()) {
|
||||
actual_De = additional_tx_pubkeys[output_idx];
|
||||
} else if (single_tx_pubkey != crypto::null_pkey) {
|
||||
actual_De = single_tx_pubkey;
|
||||
}
|
||||
if (actual_De != expected_De) {
|
||||
MERROR_VER("treasury payout D_e mismatch (burning bug: K_o correct but ephemeral key tampered)");
|
||||
return {false, 0};
|
||||
}
|
||||
|
||||
return {true, output_idx};
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
// This function validates the miner transaction reward
|
||||
@@ -1498,42 +1560,23 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
|
||||
treasury_index_in_tx_outputs = index_in_tx_outputs;
|
||||
}
|
||||
|
||||
//validate reward
|
||||
// Calculate reward being issued
|
||||
uint64_t money_in_use = 0;
|
||||
CHECK_AND_ASSERT_MES(b.miner_tx.amount_burnt > 0 || height == 0, false, "invalid tx.amount_burnt for miner_tx");
|
||||
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;
|
||||
for(size_t i = 0; i < b.miner_tx.vout.size(); i++)
|
||||
{
|
||||
// skip the treasury output
|
||||
if (treasury_payout_exists && (i == treasury_index_in_tx_outputs)) {
|
||||
continue;
|
||||
}
|
||||
CHECK_AND_ASSERT_MES(money_in_use + b.miner_tx.vout[i].amount >= money_in_use, false, "miner transaction is overflowed by output amount");
|
||||
money_in_use += b.miner_tx.vout[i].amount;
|
||||
}
|
||||
partial_block_reward = false;
|
||||
|
||||
switch (version) {
|
||||
case HF_VERSION_BULLETPROOF_PLUS:
|
||||
case HF_VERSION_ENABLE_N_OUTS:
|
||||
case HF_VERSION_FULL_PROOFS:
|
||||
case HF_VERSION_ENFORCE_FULL_PROOFS:
|
||||
case HF_VERSION_SHUTDOWN_USER_TXS:
|
||||
case HF_VERSION_SALVIUM_ONE_PROOFS:
|
||||
case HF_VERSION_AUDIT1_PAUSE:
|
||||
case HF_VERSION_AUDIT2:
|
||||
case HF_VERSION_AUDIT2_PAUSE:
|
||||
case HF_VERSION_CARROT:
|
||||
case HF_VERSION_ENFORCE_CARROT:
|
||||
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;
|
||||
}
|
||||
if (already_generated_coins != 0)
|
||||
CHECK_AND_ASSERT_MES(money_in_use / 5 == b.miner_tx.amount_burnt, false, "miner_transaction has incorrect amount_burnt amount");
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
// Make sure the TOTAL REWARD is correct
|
||||
uint64_t median_weight = m_current_block_cumul_weight_median;
|
||||
if (!get_block_reward(median_weight, cumulative_block_weight, already_generated_coins, base_reward, version))
|
||||
{
|
||||
@@ -1550,11 +1593,119 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
|
||||
MDEBUG("coinbase transaction doesn't use full amount of block reward: spent: " << money_in_use << ", block reward " << base_reward + fee << "(" << base_reward << "+" << fee << ")");
|
||||
return false;
|
||||
}
|
||||
|
||||
// HF-specific additional checks
|
||||
switch (version) {
|
||||
case HF_VERSION_BULLETPROOF_PLUS:
|
||||
case HF_VERSION_ENABLE_N_OUTS:
|
||||
case HF_VERSION_FULL_PROOFS:
|
||||
case HF_VERSION_ENFORCE_FULL_PROOFS:
|
||||
case HF_VERSION_SHUTDOWN_USER_TXS:
|
||||
case HF_VERSION_SALVIUM_ONE_PROOFS:
|
||||
case HF_VERSION_AUDIT1_PAUSE:
|
||||
case HF_VERSION_AUDIT2:
|
||||
case HF_VERSION_AUDIT2_PAUSE:
|
||||
case HF_VERSION_CARROT:
|
||||
if (already_generated_coins != 0) {
|
||||
// HF1-10: block reward split is 80% miner + 20% staker (amount_burnt)
|
||||
CHECK_AND_ASSERT_MES(money_in_use / 5 == b.miner_tx.amount_burnt, false, "miner_transaction has incorrect amount_burnt amount");
|
||||
}
|
||||
break;
|
||||
case HF_VERSION_ENABLE_TOKENS:
|
||||
// HF11: block reward split is 60% miner + 25% treasury + 15% staker (amount_burnt)
|
||||
if (already_generated_coins != 0) {
|
||||
|
||||
// Validate treasury share: one output must equal block_reward * 25 / 100
|
||||
uint64_t expected_treasury_block_reward = money_in_use * BLOCK_REWARD_TREASURY_PCT / 100;
|
||||
// Validate staker share: amount_burnt == block_reward * 15 / 100
|
||||
uint64_t expected_staker_block_reward = (money_in_use - expected_treasury_block_reward) * BLOCK_REWARD_STAKER_PCT / 100;
|
||||
CHECK_AND_ASSERT_MES(expected_staker_block_reward == b.miner_tx.amount_burnt, false,
|
||||
"miner_transaction has incorrect amount_burnt for HF11 (expected " << expected_staker_block_reward << ", got " << b.miner_tx.amount_burnt << ")");
|
||||
uint64_t expected_miner_block_reward = money_in_use - b.miner_tx.amount_burnt - expected_treasury_block_reward;
|
||||
|
||||
// treasury_destination
|
||||
address_parse_info treasury_addr_info;
|
||||
bool addr_ok = cryptonote::get_account_address_from_str(treasury_addr_info, m_nettype, get_config(m_nettype).TREASURY_ADDRESS_CARROT);
|
||||
CHECK_AND_ASSERT_MES(addr_ok, false, "Failed to parse treasury address for validation");
|
||||
|
||||
carrot::CarrotDestinationV1 treasury_destination;
|
||||
carrot::make_carrot_main_address_v1(treasury_addr_info.address.m_spend_public_key,
|
||||
treasury_addr_info.address.m_view_public_key,
|
||||
treasury_destination);
|
||||
|
||||
// deterministic janus anchor
|
||||
const carrot::janus_anchor_t treasury_anchor = get_deterministic_treasury_anchor_from_height(height);
|
||||
const carrot::CarrotPaymentProposalV1 treasury_proposal{
|
||||
.destination = treasury_destination,
|
||||
.amount = expected_treasury_block_reward,
|
||||
.asset_type = "SAL1",
|
||||
.randomness = treasury_anchor
|
||||
};
|
||||
|
||||
carrot::CarrotCoinbaseEnoteV1 expected_enote;
|
||||
carrot::get_coinbase_output_proposal_v1(treasury_proposal, height, expected_enote);
|
||||
|
||||
// Get the ephemeral pubkeys from the TX
|
||||
crypto::public_key tx_pubkey = cryptonote::get_tx_pub_key_from_extra(b.miner_tx.extra);
|
||||
bool has_single = (tx_pubkey != crypto::null_pkey);
|
||||
std::vector<crypto::public_key> additional_tx_pubkeys = cryptonote::get_additional_tx_pub_keys_from_extra(b.miner_tx.extra);
|
||||
|
||||
bool found_treasury_block_reward = false;
|
||||
for (size_t i = 0; i < b.miner_tx.vout.size(); i++) {
|
||||
|
||||
// Get the output
|
||||
CHECK_AND_ASSERT_MES(b.miner_tx.vout[i].target.type() == typeid(txout_to_carrot_v1), false, "Output of miner_tx is not txout_to_carrot_V1");
|
||||
const auto &output = boost::get<txout_to_carrot_v1>(b.miner_tx.vout[i].target);
|
||||
|
||||
// Check the output type is SAL1
|
||||
CHECK_AND_ASSERT_MES(output.asset_type == "SAL1", false, "Output of miner_tx is not SAL1");
|
||||
|
||||
// Skip the premine remint
|
||||
if (treasury_payout_exists && (i == treasury_index_in_tx_outputs)) continue;
|
||||
|
||||
// Could this be the treasury block reward?
|
||||
if (b.miner_tx.vout[i].amount == expected_treasury_block_reward) {
|
||||
|
||||
/// Check Ko
|
||||
if (output.key != expected_enote.onetime_address) continue;
|
||||
|
||||
// Check view_tag
|
||||
CHECK_AND_ASSERT_MES(output.view_tag == expected_enote.view_tag, false,
|
||||
"treasury output view_tag mismatch (burning bug: K_o correct but view_tag tampered)");
|
||||
|
||||
// Check anchor_enc
|
||||
CHECK_AND_ASSERT_MES(0 == memcmp(&output.encrypted_janus_anchor, &expected_enote.anchor_enc, sizeof(expected_enote.anchor_enc)), false,
|
||||
"treasury output anchor_enc mismatch (burning bug: K_o correct but anchor tampered)");
|
||||
|
||||
// Check D_e
|
||||
const crypto::public_key expected_De = carrot::raw_byte_convert<crypto::public_key>(expected_enote.enote_ephemeral_pubkey);
|
||||
crypto::public_key actual_De{};
|
||||
if (!additional_tx_pubkeys.empty() && i < additional_tx_pubkeys.size()) {
|
||||
actual_De = additional_tx_pubkeys[i];
|
||||
} else if (has_single) {
|
||||
actual_De = tx_pubkey;
|
||||
}
|
||||
CHECK_AND_ASSERT_MES(actual_De == expected_De, false,
|
||||
"treasury output D_e mismatch"); //important
|
||||
|
||||
// Passed all checks
|
||||
found_treasury_block_reward = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
CHECK_AND_ASSERT_MES(found_treasury_block_reward, false, "miner_tx missing treasury output with expected amount " << expected_treasury_block_reward);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
CHECK_AND_ASSERT_MES(false, false, "invalid HF detected in miner_tx : " << version);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
// 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__);
|
||||
|
||||
@@ -1630,9 +1781,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"
|
||||
);
|
||||
|
||||
@@ -1679,6 +1841,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;
|
||||
}
|
||||
|
||||
@@ -1804,7 +2031,7 @@ uint64_t Blockchain::get_current_cumulative_block_weight_median() const
|
||||
// in a lot of places. That flag is not referenced in any of the code
|
||||
// nor any of the makefiles, howeve. Need to look into whether or not it's
|
||||
// necessary at all.
|
||||
bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash)
|
||||
bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key)
|
||||
{
|
||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||
size_t median_weight;
|
||||
@@ -1833,6 +2060,7 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
|
||||
expected_reward = m_btc_expected_reward;
|
||||
seed_height = m_btc_seed_height;
|
||||
seed_hash = m_btc_seed_hash;
|
||||
miner_reward_tx_key = m_btc_miner_reward_tx_key;
|
||||
return true;
|
||||
}
|
||||
MDEBUG("Not using cached template: address " << (!memcmp(&miner_address, &m_btc_address, sizeof(cryptonote::account_public_address))) << ", nonce " << (m_btc_nonce == ex_nonce) << ", cookie " << (m_btc_pool_cookie == m_tx_pool.cookie()) << ", from_block " << (!!from_block));
|
||||
@@ -1971,19 +2199,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) {
|
||||
@@ -2093,6 +2348,7 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
|
||||
entry.P_change = audit_entry.first.P_change;
|
||||
entry.return_pubkey = audit_entry.first.return_pubkey;
|
||||
entry.origin_height = matured_audit_height;
|
||||
entry.is_carrot = false;
|
||||
protocol_entries.push_back(entry);
|
||||
}
|
||||
}
|
||||
@@ -2119,10 +2375,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");
|
||||
ok = construct_protocol_tx(height, b.protocol_tx, protocol_entries, b.major_version);
|
||||
CHECK_AND_ASSERT_MES(ok, false, "Failed to construct protocol tx");
|
||||
|
||||
@@ -2186,8 +2438,9 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
|
||||
*/
|
||||
//make blocks coin-base tx looks close to real coinbase tx to get truthful blob weight
|
||||
uint8_t hf_version = b.major_version;
|
||||
size_t max_outs = hf_version >= 4 ? 1 : 11;
|
||||
bool r = construct_miner_tx(height, median_weight, already_generated_coins, txs_weight, fee, miner_address, b.miner_tx, m_nettype, m_hardfork->get_hardforks(), ex_nonce, max_outs, hf_version);
|
||||
size_t max_outs = hf_version >= HF_VERSION_ENABLE_TOKENS ? 3 : (hf_version >= 4 ? 1 : 11);
|
||||
|
||||
bool r = construct_miner_tx(height, median_weight, already_generated_coins, txs_weight, fee, miner_address, miner_reward_tx_key, b.miner_tx, m_nettype, m_hardfork->get_hardforks(), ex_nonce, max_outs, hf_version);
|
||||
CHECK_AND_ASSERT_MES(r, false, "Failed to construct miner tx, first chance");
|
||||
size_t cumulative_weight = txs_weight + get_transaction_weight(b.miner_tx);
|
||||
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
|
||||
@@ -2196,7 +2449,7 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
|
||||
#endif
|
||||
for (size_t try_count = 0; try_count != 10; ++try_count)
|
||||
{
|
||||
r = construct_miner_tx(height, median_weight, already_generated_coins, cumulative_weight, fee, miner_address, b.miner_tx, m_nettype, m_hardfork->get_hardforks(), ex_nonce, max_outs, hf_version);
|
||||
r = construct_miner_tx(height, median_weight, already_generated_coins, cumulative_weight, fee, miner_address, miner_reward_tx_key, b.miner_tx, m_nettype, m_hardfork->get_hardforks(), ex_nonce, max_outs, hf_version);
|
||||
|
||||
CHECK_AND_ASSERT_MES(r, false, "Failed to construct miner tx, second chance");
|
||||
size_t coinbase_weight = get_transaction_weight(b.miner_tx);
|
||||
@@ -2241,16 +2494,16 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
|
||||
#endif
|
||||
|
||||
if (!from_block)
|
||||
cache_block_template(b, miner_address, ex_nonce, diffic, height, expected_reward, seed_height, seed_hash, pool_cookie);
|
||||
cache_block_template(b, miner_address, ex_nonce, diffic, height, expected_reward, seed_height, seed_hash, pool_cookie, miner_reward_tx_key);
|
||||
return true;
|
||||
}
|
||||
LOG_ERROR("Failed to create_block_template with " << 10 << " tries");
|
||||
return false;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
bool Blockchain::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash)
|
||||
bool Blockchain::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key)
|
||||
{
|
||||
return create_block_template(b, NULL, miner_address, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash);
|
||||
return create_block_template(b, NULL, miner_address, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash, miner_reward_tx_key);
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
bool Blockchain::get_miner_data(uint8_t& major_version, uint64_t& height, crypto::hash& prev_id, crypto::hash& seed_hash, difficulty_type& difficulty, uint64_t& median_weight, uint64_t& already_generated_coins, std::vector<tx_block_template_backlog_entry>& tx_backlog)
|
||||
@@ -2950,7 +3203,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);
|
||||
@@ -3724,7 +3977,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;
|
||||
@@ -3779,6 +4032,107 @@ 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.source_asset_type != tx.destination_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 SAL/SAL1 for both");
|
||||
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.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 destination asset: " << tx.destination_asset_type << ", expected BURN" << ", provided source asset: " << tx.source_asset_type << ", expected SAL1");
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
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 != "SAL" && 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 SAL/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
|
||||
{
|
||||
@@ -3805,6 +4159,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) {
|
||||
@@ -3814,7 +4187,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;
|
||||
@@ -3831,6 +4212,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
|
||||
@@ -3838,24 +4232,40 @@ 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 ticker doesn't begin with the reserved chars `SAL`
|
||||
CHECK_AND_ASSERT_MES(tx.token_metadata.asset_type.substr(0,3) != "SAL", false, "Invalid CREATE_TOKEN ticker - SAL* is reserved");
|
||||
// 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");
|
||||
}
|
||||
|
||||
if (tx.type == cryptonote::transaction_type::ROLLUP) {
|
||||
CHECK_AND_ASSERT_MES(tx.layer2_rollup_data.version == 1, false, "Invalid ROLLUP data version");
|
||||
CHECK_AND_ASSERT_MES(!tx.layer2_rollup_data.txs.empty(), false, "ROLLUP must include at least one paid TX entry");
|
||||
|
||||
uint64_t expected_amount_burnt = 0;
|
||||
for (const auto &rollup_tx : tx.layer2_rollup_data.txs) {
|
||||
CHECK_AND_ASSERT_MES(rollup_tx.tx_fee > 0, false, "ROLLUP contains a paid TX entry with zero fee");
|
||||
CHECK_AND_ASSERT_MES(expected_amount_burnt <= std::numeric_limits<uint64_t>::max() - rollup_tx.tx_fee, false, "Numeric overflow in ROLLUP fee total");
|
||||
expected_amount_burnt += rollup_tx.tx_fee;
|
||||
}
|
||||
|
||||
CHECK_AND_ASSERT_MES(tx.amount_burnt == expected_amount_burnt, false, "Invalid amount_burnt for ROLLUP");
|
||||
}
|
||||
|
||||
|
||||
// Check for invalid TX types
|
||||
if (tx.type == cryptonote::transaction_type::UNSET || tx.type > cryptonote::transaction_type::MAX) {
|
||||
@@ -3891,7 +4301,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;
|
||||
|
||||
@@ -4036,11 +4446,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);
|
||||
@@ -4514,8 +4926,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;
|
||||
@@ -4666,7 +5077,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
|
||||
@@ -4719,7 +5130,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
|
||||
@@ -4785,7 +5196,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
|
||||
@@ -4952,6 +5363,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
|
||||
{
|
||||
@@ -5326,6 +5749,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;
|
||||
@@ -5358,7 +5791,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;
|
||||
@@ -5402,7 +5835,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);
|
||||
@@ -5520,7 +5953,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;
|
||||
@@ -6167,7 +6600,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)
|
||||
@@ -6238,7 +6671,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
|
||||
@@ -6510,7 +6943,7 @@ void Blockchain::cancel()
|
||||
}
|
||||
|
||||
#if defined(PER_BLOCK_CHECKPOINT)
|
||||
static const char expected_block_hashes_hash[] = "a2a5a9bc5d606392ac5c14be55b90a92b8577b8ffdac5c63cc6174f41764c753";
|
||||
static const char expected_block_hashes_hash[] = "f3bf12451890c9cbeb9f90b2762e4ee2756aaa726958e280e27b51ed9edf84b3";
|
||||
void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints)
|
||||
{
|
||||
if (get_checkpoints == nullptr || !m_fast_sync)
|
||||
@@ -6652,7 +7085,7 @@ void Blockchain::invalidate_block_template_cache()
|
||||
m_btc_valid = false;
|
||||
}
|
||||
|
||||
void Blockchain::cache_block_template(const block &b, const cryptonote::account_public_address &address, const blobdata &nonce, const difficulty_type &diff, uint64_t height, uint64_t expected_reward, uint64_t seed_height, const crypto::hash &seed_hash, uint64_t pool_cookie)
|
||||
void Blockchain::cache_block_template(const block &b, const cryptonote::account_public_address &address, const blobdata &nonce, const difficulty_type &diff, uint64_t height, uint64_t expected_reward, uint64_t seed_height, const crypto::hash &seed_hash, uint64_t pool_cookie, crypto::public_key &miner_reward_tx_key)
|
||||
{
|
||||
MDEBUG("Setting block template cache");
|
||||
m_btc = b;
|
||||
@@ -6664,6 +7097,7 @@ void Blockchain::cache_block_template(const block &b, const cryptonote::account_
|
||||
m_btc_seed_hash = seed_hash;
|
||||
m_btc_seed_height = seed_height;
|
||||
m_btc_pool_cookie = pool_cookie;
|
||||
m_btc_miner_reward_tx_key = miner_reward_tx_key;
|
||||
m_btc_valid = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -394,8 +394,8 @@ namespace cryptonote
|
||||
*
|
||||
* @return true if block template filled in successfully, else false
|
||||
*/
|
||||
bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash);
|
||||
bool create_block_template(block& b, const crypto::hash *from_block, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash);
|
||||
bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key);
|
||||
bool create_block_template(block& b, const crypto::hash *from_block, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key);
|
||||
|
||||
/**
|
||||
* @brief gets data required to create a block template and start mining on it
|
||||
@@ -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
|
||||
@@ -1318,7 +1333,7 @@ namespace cryptonote
|
||||
crypto::hash m_btc_seed_hash;
|
||||
uint64_t m_btc_seed_height;
|
||||
bool m_btc_valid;
|
||||
|
||||
crypto::public_key m_btc_miner_reward_tx_key;
|
||||
|
||||
bool m_batch_success;
|
||||
|
||||
@@ -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
|
||||
@@ -1743,7 +1759,7 @@ namespace cryptonote
|
||||
*
|
||||
* At some point, may be used to push an update to miners
|
||||
*/
|
||||
void cache_block_template(const block &b, const cryptonote::account_public_address &address, const blobdata &nonce, const difficulty_type &diff, uint64_t height, uint64_t expected_reward, uint64_t seed_height, const crypto::hash &seed_hash, uint64_t pool_cookie);
|
||||
void cache_block_template(const block &b, const cryptonote::account_public_address &address, const blobdata &nonce, const difficulty_type &diff, uint64_t height, uint64_t expected_reward, uint64_t seed_height, const crypto::hash &seed_hash, uint64_t pool_cookie, crypto::public_key &miner_reward_tx_key);
|
||||
|
||||
/**
|
||||
* @brief sends new block notifications to ZMQ `miner_data` subscribers
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
@@ -1522,14 +1542,14 @@ namespace cryptonote
|
||||
notify_txpool_event(tx_blobs, epee::to_span(tx_hashes), epee::to_span(txs), just_broadcasted);
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash)
|
||||
bool core::get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key)
|
||||
{
|
||||
return m_blockchain_storage.create_block_template(b, adr, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash);
|
||||
return m_blockchain_storage.create_block_template(b, adr, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash, miner_reward_tx_key);
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::get_block_template(block& b, const crypto::hash *prev_block, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash)
|
||||
bool core::get_block_template(block& b, const crypto::hash *prev_block, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key)
|
||||
{
|
||||
return m_blockchain_storage.create_block_template(b, prev_block, adr, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash);
|
||||
return m_blockchain_storage.create_block_template(b, prev_block, adr, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash, miner_reward_tx_key);
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::get_miner_data(uint8_t& major_version, uint64_t& height, crypto::hash& prev_id, crypto::hash& seed_hash, difficulty_type& difficulty, uint64_t& median_weight, uint64_t& already_generated_coins, std::vector<tx_block_template_backlog_entry>& tx_backlog)
|
||||
|
||||
@@ -231,8 +231,8 @@ namespace cryptonote
|
||||
*
|
||||
* @note see Blockchain::create_block_template
|
||||
*/
|
||||
virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash) override;
|
||||
virtual bool get_block_template(block& b, const crypto::hash *prev_block, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash);
|
||||
virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key) override;
|
||||
virtual bool get_block_template(block& b, const crypto::hash *prev_block, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key);
|
||||
|
||||
/**
|
||||
* @copydoc Blockchain::get_miner_data
|
||||
|
||||
@@ -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,15 +356,18 @@ 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;
|
||||
}
|
||||
|
||||
if (carrot_found || hard_fork_version >= HF_VERSION_ENFORCE_CARROT)
|
||||
{
|
||||
if (carrot_found && hard_fork_version < HF_VERSION_CARROT) {
|
||||
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
|
||||
tx.version = TRANSACTION_VERSION_CARROT;
|
||||
try
|
||||
@@ -378,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;
|
||||
@@ -388,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)
|
||||
@@ -454,7 +459,17 @@ namespace cryptonote
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool construct_miner_tx(size_t height, size_t median_weight, uint64_t already_generated_coins, size_t current_block_weight, uint64_t fee, const account_public_address &miner_address, transaction& tx, network_type nettype, const std::vector<hardfork_t>& hardforks, const blobdata& extra_nonce, size_t max_outs, uint8_t hard_fork_version) {
|
||||
carrot::janus_anchor_t get_deterministic_treasury_anchor_from_height(uint64_t height)
|
||||
{
|
||||
carrot::janus_anchor_t treasury_anchor{};
|
||||
for (int i = 0; i < 8; ++i)
|
||||
treasury_anchor.bytes[i] = (height >> (8 * i)) & 0xff;
|
||||
for (int i = 8; i < 16; ++i)
|
||||
treasury_anchor.bytes[i] = 0;
|
||||
return treasury_anchor;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool construct_miner_tx(size_t height, size_t median_weight, uint64_t already_generated_coins, size_t current_block_weight, uint64_t fee, const account_public_address &miner_address, crypto::public_key& miner_reward_tx_key, transaction& tx, network_type nettype, const std::vector<hardfork_t>& hardforks, const blobdata& extra_nonce, size_t max_outs, uint8_t hard_fork_version) {
|
||||
|
||||
// Clear the TX contents
|
||||
tx.set_null();
|
||||
@@ -482,33 +497,70 @@ namespace cryptonote
|
||||
{
|
||||
try
|
||||
{
|
||||
// Build the miner payout
|
||||
carrot::CarrotDestinationV1 destination;
|
||||
// miner destination
|
||||
carrot::CarrotDestinationV1 miner_destination;
|
||||
carrot::make_carrot_main_address_v1(miner_address.m_spend_public_key,
|
||||
miner_address.m_view_public_key,
|
||||
destination);
|
||||
miner_destination);
|
||||
|
||||
CHECK_AND_ASSERT_THROW_MES(!miner_destination.is_subaddress,
|
||||
"construct_miner_tx: subaddresses are not allowed in miner transactions");
|
||||
CHECK_AND_ASSERT_THROW_MES(miner_destination.payment_id == carrot::null_payment_id,
|
||||
"construct_miner_tx: integrated addresses are not allowed in miner transactions");
|
||||
|
||||
CHECK_AND_ASSERT_THROW_MES(!destination.is_subaddress,
|
||||
"make_single_enote_carrot_coinbase_transaction_v1: subaddress are not allowed in miner transactions");
|
||||
CHECK_AND_ASSERT_THROW_MES(destination.payment_id == carrot::null_payment_id,
|
||||
"make_single_enote_carrot_coinbase_transaction_v1: integrated addresses are not allowed in miner transactions");
|
||||
const bool do_new_split = (hard_fork_version >= HF_VERSION_ENABLE_TOKENS);
|
||||
uint64_t treasury_reward = do_new_split ? (block_reward * BLOCK_REWARD_TREASURY_PCT / 100) : 0;
|
||||
uint64_t stake_reward = (block_reward - treasury_reward) * BLOCK_REWARD_STAKER_PCT / 100;
|
||||
uint64_t miner_reward = (block_reward - treasury_reward - stake_reward);
|
||||
|
||||
uint64_t stake_reward = block_reward / 5;
|
||||
|
||||
const carrot::CarrotPaymentProposalV1 payment_proposal{
|
||||
.destination = destination,
|
||||
.amount = block_reward - stake_reward,
|
||||
//miner enote
|
||||
const carrot::CarrotPaymentProposalV1 miner_proposal{
|
||||
.destination = miner_destination,
|
||||
.amount = miner_reward,
|
||||
.asset_type = "SAL1",
|
||||
.randomness = carrot::gen_janus_anchor()
|
||||
};
|
||||
|
||||
std::vector<carrot::CarrotCoinbaseEnoteV1> enotes(treasury_payout_exists ? 2 : 1);
|
||||
carrot::get_coinbase_output_proposal_v1(payment_proposal, height, enotes.front());
|
||||
// Determine number of enotes: miner + optional treasury_reward + optional treasury_mint
|
||||
size_t num_enotes = 1;
|
||||
if (do_new_split) num_enotes++; // treasury reward output
|
||||
if (treasury_payout_exists) num_enotes++;
|
||||
std::vector<carrot::CarrotCoinbaseEnoteV1> enotes(num_enotes);
|
||||
carrot::get_coinbase_output_proposal_v1(miner_proposal, height, enotes[0]);
|
||||
|
||||
// Check to see if there needs to be a treasury payout
|
||||
// STORE THE MINER TX_PUB_KEY NOW
|
||||
miner_reward_tx_key = carrot::raw_byte_convert<crypto::public_key>(enotes[0].enote_ephemeral_pubkey);
|
||||
|
||||
size_t enote_idx = 1;
|
||||
|
||||
// Add the treasury reward enote (25% of block reward) for HF11+
|
||||
if (do_new_split) {
|
||||
|
||||
//treasury address for HF11+ block reward split
|
||||
address_parse_info treasury_addr_info;
|
||||
bool treasury_ok = cryptonote::get_account_address_from_str(treasury_addr_info, nettype, get_config(nettype).TREASURY_ADDRESS_CARROT);
|
||||
CHECK_AND_ASSERT_MES(treasury_ok, false, "Failed to parse treasury address for block reward split"); // maybe more check can be added here, but it's enough for now (bcs validation)
|
||||
|
||||
carrot::CarrotDestinationV1 treasury_destination;
|
||||
carrot::make_carrot_main_address_v1(treasury_addr_info.address.m_spend_public_key,
|
||||
treasury_addr_info.address.m_view_public_key,
|
||||
treasury_destination);
|
||||
|
||||
// Derive a deterministic janus anchor from height so validators can independently recompute the expected treasury K_o
|
||||
const carrot::janus_anchor_t treasury_anchor = get_deterministic_treasury_anchor_from_height(height);
|
||||
const carrot::CarrotPaymentProposalV1 treasury_proposal{
|
||||
.destination = treasury_destination,
|
||||
.amount = treasury_reward,
|
||||
.asset_type = "SAL1",
|
||||
.randomness = treasury_anchor
|
||||
};
|
||||
|
||||
carrot::get_coinbase_output_proposal_v1(treasury_proposal, height, enotes[enote_idx]);
|
||||
enote_idx++;
|
||||
}
|
||||
|
||||
//
|
||||
if (treasury_payout_exists) {
|
||||
|
||||
// Convert the strings into meaningful data
|
||||
const auto [tx_public_key_str, onetime_address_str, anchor_enc_str, view_tag_str] = treasury_payout_data.at(height);
|
||||
mx25519_pubkey tx_public_key;
|
||||
CHECK_AND_ASSERT_THROW_MES(epee::string_tools::hex_to_pod(tx_public_key_str, tx_public_key), "fail to deserialize treasury tx public key");
|
||||
@@ -519,8 +571,8 @@ namespace cryptonote
|
||||
carrot::view_tag_t view_tag;
|
||||
CHECK_AND_ASSERT_THROW_MES(epee::string_tools::hex_to_pod(view_tag_str, view_tag), "fail to deserialize treasury tx view_tag");
|
||||
|
||||
// Manually produce an enote for the treasury payout using the hardcoded keys
|
||||
carrot::CarrotCoinbaseEnoteV1 &treasury_enote = enotes.back();
|
||||
//
|
||||
carrot::CarrotCoinbaseEnoteV1 &treasury_enote = enotes[enote_idx];
|
||||
treasury_enote.onetime_address = onetime_address;
|
||||
treasury_enote.amount = TREASURY_SAL1_MINT_AMOUNT;
|
||||
treasury_enote.asset_type = "SAL1";
|
||||
@@ -528,15 +580,15 @@ namespace cryptonote
|
||||
treasury_enote.view_tag = view_tag;
|
||||
treasury_enote.enote_ephemeral_pubkey = tx_public_key;
|
||||
treasury_enote.block_index = height;
|
||||
|
||||
// sort enotes by K_o
|
||||
if (enotes[0].onetime_address > enotes[1].onetime_address) {
|
||||
std::swap(enotes[0], enotes[1]);
|
||||
}
|
||||
}
|
||||
|
||||
tx = carrot::store_carrot_to_coinbase_transaction_v1(enotes, extra_nonce, cryptonote::transaction_type::MINER, height);
|
||||
// Sort enotes by K_o
|
||||
std::sort(enotes.begin(), enotes.end(), [](const carrot::CarrotCoinbaseEnoteV1 &a, const carrot::CarrotCoinbaseEnoteV1 &b) {
|
||||
return a.onetime_address < b.onetime_address;
|
||||
});
|
||||
|
||||
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();
|
||||
}
|
||||
@@ -551,6 +603,8 @@ namespace cryptonote
|
||||
|
||||
keypair txkey = keypair::generate(hw::get_device("default"));
|
||||
add_tx_pub_key_to_extra(tx, txkey.pub);
|
||||
// STORE THE MINER TX_PUB_KEY NOW
|
||||
miner_reward_tx_key = txkey.pub;
|
||||
if(!extra_nonce.empty())
|
||||
if(!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce))
|
||||
return false;
|
||||
@@ -594,7 +648,7 @@ namespace cryptonote
|
||||
case HF_VERSION_AUDIT2:
|
||||
case HF_VERSION_AUDIT2_PAUSE:
|
||||
case HF_VERSION_CARROT:
|
||||
case HF_VERSION_ENFORCE_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;
|
||||
|
||||
@@ -77,7 +77,9 @@ namespace cryptonote
|
||||
//---------------------------------------------------------------
|
||||
bool construct_protocol_tx(const size_t height, transaction& tx, std::vector<protocol_data_entry>& protocol_data, const uint8_t hf_version);
|
||||
//---------------------------------------------------------------
|
||||
bool construct_miner_tx(size_t height, size_t median_weight, uint64_t already_generated_coins, size_t current_block_weight, uint64_t fee, const account_public_address &miner_address, transaction& tx, network_type nettype = network_type::FAKECHAIN, const std::vector<hardfork_t>& hardforks = {}, const blobdata& extra_nonce = blobdata(), size_t max_outs = 999, uint8_t hard_fork_version = 1);
|
||||
carrot::janus_anchor_t get_deterministic_treasury_anchor_from_height(uint64_t height);
|
||||
//---------------------------------------------------------------
|
||||
bool construct_miner_tx(size_t height, size_t median_weight, uint64_t already_generated_coins, size_t current_block_weight, uint64_t fee, const account_public_address &miner_address, crypto::public_key& miner_reward_tx_key, transaction& tx, network_type nettype = network_type::FAKECHAIN, const std::vector<hardfork_t>& hardforks = {}, const blobdata& extra_nonce = blobdata(), size_t max_outs = 999, uint8_t hard_fork_version = 1);
|
||||
|
||||
struct tx_source_entry
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
@@ -1749,6 +1776,49 @@ namespace cryptonote
|
||||
LOG_PRINT_L2(" key images already seen");
|
||||
continue;
|
||||
}
|
||||
if (version < HF_VERSION_CARROT && tx.version >= TRANSACTION_VERSION_CARROT)
|
||||
{
|
||||
LOG_PRINT_L2(" is a Carrot transaction - cannot be mined");
|
||||
continue;
|
||||
}
|
||||
if (version >= HF_VERSION_CARROT && tx.version < TRANSACTION_VERSION_CARROT)
|
||||
{
|
||||
LOG_PRINT_L2(" is not a Carrot transaction - cannot be mined");
|
||||
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;
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
|
||||
+2
-1
@@ -149,7 +149,8 @@ void print_genesis_tx_hex(const cryptonote::network_type nettype) {
|
||||
|
||||
//Prepare genesis_tx
|
||||
cryptonote::transaction tx_genesis;
|
||||
cryptonote::construct_miner_tx(0, 0, 0, 10, 0, miner_acc1.get_keys().m_account_address, tx_genesis, (network_type)nettype, {}, blobdata(), 999, 1);
|
||||
crypto::public_key miner_reward_tx_key = crypto::null_pkey;
|
||||
cryptonote::construct_miner_tx(0, 0, 0, 10, 0, miner_acc1.get_keys().m_account_address, miner_reward_tx_key, tx_genesis, (network_type)nettype, {}, blobdata(), 999, 1);
|
||||
std::cout << "Object:" << std::endl;
|
||||
std::cout << obj_to_json_str(tx_genesis) << std::endl << std::endl;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -58,6 +58,12 @@ const hardfork_t mainnet_hard_forks[] = {
|
||||
|
||||
// version 9 starts from block 179200, which is on or around the 10th of March, 2025. Fork time finalised on 2025-02-24. No fork voting occurs for the v9 fork.
|
||||
{ 9, 179200, 0, 1740393800 },
|
||||
|
||||
// version 10 Carrot - including treasury mint - starts from block 334750, which is on or around the 13th of October, 2025. Fork time finalised on 2025-09-29. No fork voting occurs for the v10 fork.
|
||||
{10, 334750, 0, 1759142500 },
|
||||
|
||||
// version 11 Two Milestone 1 - starts from block 465000, which is on or around the 13th of April, 2026. Fork time finalised on 2026-03-20. No fork voting occurs for the v11 fork.
|
||||
{11, 465000, 0, 1774000000 },
|
||||
};
|
||||
const size_t num_mainnet_hard_forks = sizeof(mainnet_hard_forks) / sizeof(mainnet_hard_forks[0]);
|
||||
const uint64_t mainnet_hard_fork_version_1_till = ((uint64_t)-1);
|
||||
@@ -92,9 +98,9 @@ const hardfork_t testnet_hard_forks[] = {
|
||||
|
||||
// version 10 Carrot - including treasury mint - starts from block 1100
|
||||
{10, 1100, 0, 1739780005 },
|
||||
|
||||
// version 11 Carrot and CryptoNote starts from block 1200
|
||||
{11, 1200, 0, 1756811153 },
|
||||
|
||||
// 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>
|
||||
@@ -1870,15 +1871,14 @@ namespace cryptonote
|
||||
bool core_rpc_server::get_block_template(const account_public_address &address, const crypto::hash *prev_block, const cryptonote::blobdata &extra_nonce, size_t &reserved_offset, cryptonote::difficulty_type &difficulty, uint64_t &height, uint64_t &expected_reward, block &b, uint64_t &seed_height, crypto::hash &seed_hash, crypto::hash &next_seed_hash, epee::json_rpc::error &error_resp)
|
||||
{
|
||||
b = boost::value_initialized<cryptonote::block>();
|
||||
if(!m_core.get_block_template(b, prev_block, address, difficulty, height, expected_reward, extra_nonce, seed_height, seed_hash))
|
||||
crypto::public_key tx_pub_key = crypto::null_pkey;
|
||||
if(!m_core.get_block_template(b, prev_block, address, difficulty, height, expected_reward, extra_nonce, seed_height, seed_hash, tx_pub_key))
|
||||
{
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
|
||||
error_resp.message = "Internal error: failed to create block template";
|
||||
LOG_ERROR("Failed to create block template");
|
||||
return false;
|
||||
}
|
||||
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);
|
||||
if(tx_pub_key == crypto::null_pkey)
|
||||
{
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
|
||||
@@ -1886,7 +1886,7 @@ namespace cryptonote
|
||||
LOG_ERROR("Failed to get tx pub key in coinbase extra");
|
||||
return false;
|
||||
}
|
||||
|
||||
blobdata block_blob = t_serializable_object_to_blob(b);
|
||||
uint64_t next_height;
|
||||
crypto::rx_seedheights(height, &seed_height, &next_height);
|
||||
if (next_height != seed_height)
|
||||
@@ -3045,6 +3045,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 +3154,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);
|
||||
|
||||
|
||||
+602
-100
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-rc4"
|
||||
#define DEF_SALVIUM_VERSION "1.1.1"
|
||||
#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);
|
||||
|
||||
@@ -34,16 +34,60 @@
|
||||
#include "wallet.h"
|
||||
|
||||
#include "crypto/hash.h"
|
||||
#include "cryptonote_basic/cryptonote_format_utils.h"
|
||||
#include "wallet/wallet2.h"
|
||||
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
using namespace epee;
|
||||
|
||||
namespace Monero {
|
||||
|
||||
namespace {
|
||||
|
||||
std::string to_hex_or_empty(const crypto::public_key &key)
|
||||
{
|
||||
if (key == crypto::null_pkey)
|
||||
return {};
|
||||
return string_tools::pod_to_hex(key);
|
||||
}
|
||||
|
||||
std::vector<std::string> extract_return_addresses(const cryptonote::transaction_prefix &tx)
|
||||
{
|
||||
std::vector<std::string> addresses;
|
||||
if (tx.type == cryptonote::transaction_type::STAKE)
|
||||
{
|
||||
auto hex = to_hex_or_empty(tx.protocol_tx_data.return_address);
|
||||
if (hex.empty())
|
||||
hex = to_hex_or_empty(tx.return_address);
|
||||
if (!hex.empty())
|
||||
addresses.push_back(hex);
|
||||
return addresses;
|
||||
}
|
||||
if (tx.type != cryptonote::transaction_type::PROTOCOL)
|
||||
return addresses;
|
||||
|
||||
std::unordered_set<std::string> seen;
|
||||
for (const auto &out : tx.vout)
|
||||
{
|
||||
crypto::public_key output_key = crypto::null_pkey;
|
||||
if (!cryptonote::get_output_public_key(out, output_key))
|
||||
continue;
|
||||
auto hex = to_hex_or_empty(output_key);
|
||||
if (hex.empty())
|
||||
continue;
|
||||
if (seen.emplace(hex).second)
|
||||
addresses.push_back(hex);
|
||||
}
|
||||
return addresses;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TransactionHistory::~TransactionHistory() {}
|
||||
|
||||
|
||||
@@ -130,6 +174,7 @@ void TransactionHistoryImpl::refresh()
|
||||
|
||||
std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> in_payments;
|
||||
m_wallet->m_wallet->get_payments(in_payments, min_height, max_height);
|
||||
std::unordered_map<crypto::hash, TransactionInfoImpl*> protocol_by_hash;
|
||||
for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = in_payments.begin(); i != in_payments.end(); ++i) {
|
||||
const tools::wallet2::payment_details &pd = i->second;
|
||||
std::string payment_id = string_tools::pod_to_hex(i->first);
|
||||
@@ -152,6 +197,8 @@ void TransactionHistoryImpl::refresh()
|
||||
ti->m_unlock_time = pd.m_unlock_time;
|
||||
ti->m_type = static_cast<Monero::transaction_type>(static_cast<uint8_t>(pd.m_tx_type));
|
||||
ti->m_asset = pd.m_asset_type;
|
||||
if (pd.m_tx_type == cryptonote::transaction_type::PROTOCOL)
|
||||
protocol_by_hash.emplace(pd.m_tx_hash, ti);
|
||||
m_history.push_back(ti);
|
||||
|
||||
}
|
||||
@@ -175,7 +222,14 @@ 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 ||
|
||||
pd.m_tx.type == cryptonote::transaction_type::ROLLUP) {
|
||||
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 +238,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);
|
||||
@@ -197,6 +251,7 @@ void TransactionHistoryImpl::refresh()
|
||||
ti->m_confirmations = (wallet_height > pd.m_block_height) ? wallet_height - pd.m_block_height : 0;
|
||||
ti->m_type = static_cast<Monero::transaction_type>(static_cast<uint8_t>(pd.m_tx.type));
|
||||
ti->m_asset = pd.m_tx.source_asset_type;
|
||||
ti->m_return_addresses = extract_return_addresses(pd.m_tx);
|
||||
|
||||
// single output transaction might contain multiple transfers
|
||||
for (const auto &d: pd.m_dests) {
|
||||
@@ -212,8 +267,17 @@ 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 ||
|
||||
pd.m_tx.type == cryptonote::transaction_type::ROLLUP) {
|
||||
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 +285,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;
|
||||
@@ -235,6 +299,7 @@ void TransactionHistoryImpl::refresh()
|
||||
ti->m_confirmations = 0;
|
||||
ti->m_type = static_cast<Monero::transaction_type>(static_cast<uint8_t>(pd.m_tx.type));
|
||||
ti->m_asset = pd.m_tx.source_asset_type;
|
||||
ti->m_return_addresses = extract_return_addresses(pd.m_tx);
|
||||
for (const auto &d : pd.m_dests)
|
||||
{
|
||||
ti->m_transfers.push_back({d.amount, d.address(m_wallet->m_wallet->nettype(), pd.m_payment_id), d.asset_type});
|
||||
@@ -266,11 +331,23 @@ void TransactionHistoryImpl::refresh()
|
||||
ti->m_confirmations = 0;
|
||||
ti->m_type = static_cast<Monero::transaction_type>(static_cast<uint8_t>(pd.m_tx_type));
|
||||
ti->m_asset = pd.m_asset_type;
|
||||
if (pd.m_tx_type == cryptonote::transaction_type::PROTOCOL)
|
||||
protocol_by_hash.emplace(pd.m_tx_hash, ti);
|
||||
m_history.push_back(ti);
|
||||
|
||||
LOG_PRINT_L1(__FUNCTION__ << ": Unconfirmed payment found " << pd.m_amount);
|
||||
}
|
||||
|
||||
|
||||
std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> confirmed_protocol_payments;
|
||||
m_wallet->m_wallet->get_payments_out(confirmed_protocol_payments, min_height, max_height);
|
||||
for (const auto &entry : confirmed_protocol_payments)
|
||||
{
|
||||
const auto it = protocol_by_hash.find(entry.first);
|
||||
if (it == protocol_by_hash.end())
|
||||
continue;
|
||||
it->second->m_return_addresses = extract_return_addresses(entry.second.m_tx);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -134,6 +134,11 @@ string TransactionInfoImpl::paymentId() const
|
||||
return m_paymentid;
|
||||
}
|
||||
|
||||
const std::vector<std::string> &TransactionInfoImpl::returnAddresses() const
|
||||
{
|
||||
return m_return_addresses;
|
||||
}
|
||||
|
||||
const std::vector<TransactionInfo::Transfer> &TransactionInfoImpl::transfers() const
|
||||
{
|
||||
return m_transfers;
|
||||
|
||||
@@ -60,6 +60,7 @@ public:
|
||||
virtual std::string hash() const override;
|
||||
virtual std::time_t timestamp() const override;
|
||||
virtual std::string paymentId() const override;
|
||||
virtual const std::vector<std::string> &returnAddresses() const override;
|
||||
virtual const std::vector<Transfer> &transfers() const override;
|
||||
virtual uint64_t confirmations() const override;
|
||||
virtual uint64_t unlockTime() const override;
|
||||
@@ -81,6 +82,7 @@ private:
|
||||
std::string m_hash;
|
||||
std::time_t m_timestamp;
|
||||
std::string m_paymentid;
|
||||
std::vector<std::string> m_return_addresses;
|
||||
std::vector<Transfer> m_transfers;
|
||||
uint64_t m_confirmations;
|
||||
uint64_t m_unlock_time;
|
||||
|
||||
+171
-9
@@ -806,6 +806,16 @@ std::string WalletImpl::seed(const std::string& seed_offset) const
|
||||
return std::string(seed.data(), seed.size()); // TODO
|
||||
}
|
||||
|
||||
void WalletImpl::setStoreTxInfo(bool store)
|
||||
{
|
||||
m_wallet->store_tx_info(store);
|
||||
}
|
||||
|
||||
bool WalletImpl::storeTxInfo() const
|
||||
{
|
||||
return m_wallet->store_tx_info();
|
||||
}
|
||||
|
||||
std::string WalletImpl::getSeedLanguage() const
|
||||
{
|
||||
return m_wallet->get_seed_language();
|
||||
@@ -877,40 +887,64 @@ bool WalletImpl::setDevicePassphrase(const std::string &passphrase)
|
||||
return status() == Status_Ok;
|
||||
}
|
||||
|
||||
std::string WalletImpl::address(uint32_t accountIndex, uint32_t addressIndex) const
|
||||
std::string WalletImpl::address(uint32_t accountIndex, uint32_t addressIndex, bool carrot) const
|
||||
{
|
||||
return m_wallet->get_subaddress_as_str({accountIndex, addressIndex});
|
||||
return m_wallet->get_subaddress_as_str({{accountIndex, addressIndex}, carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
|
||||
}
|
||||
|
||||
std::string WalletImpl::integratedAddress(const std::string &payment_id) const
|
||||
std::string WalletImpl::integratedAddress(const std::string &payment_id, bool carrot) const
|
||||
{
|
||||
crypto::hash8 pid;
|
||||
if (!tools::wallet2::parse_short_payment_id(payment_id, pid)) {
|
||||
return "";
|
||||
}
|
||||
return m_wallet->get_integrated_address_as_str(pid);
|
||||
return m_wallet->get_integrated_address_as_str(pid, carrot);
|
||||
}
|
||||
|
||||
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 +955,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();
|
||||
@@ -1588,7 +1642,8 @@ PendingTransaction* WalletImpl::restoreMultisigTransaction(const string& signDat
|
||||
PendingTransaction *WalletImpl::createStakeTransaction(uint64_t amount, uint32_t mixin_count, PendingTransaction::Priority priority, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices)
|
||||
{
|
||||
// Need to populate {dst_entr, payment_id, asset_type, is_return}
|
||||
const string dst_addr = m_wallet->get_subaddress_as_str({subaddr_account, 0});//MY LOCAL (SUB)ADDRESS
|
||||
const bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
|
||||
const string dst_addr = m_wallet->get_subaddress_as_str({{subaddr_account, 0}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});//MY LOCAL (SUB)ADDRESS
|
||||
const string payment_id = "";
|
||||
const string asset_type = "SAL1";
|
||||
const bool is_return = false;
|
||||
@@ -1598,6 +1653,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,
|
||||
@@ -1605,7 +1761,8 @@ PendingTransaction *WalletImpl::createAuditTransaction(
|
||||
std::set<uint32_t> subaddr_indices
|
||||
) {
|
||||
// Need to populate {dst_entr, payment_id, asset_type, is_return}
|
||||
const string dst_addr = m_wallet->get_subaddress_as_str({subaddr_account, 0});//MY LOCAL (SUB)ADDRESS
|
||||
const bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
|
||||
const string dst_addr = m_wallet->get_subaddress_as_str({{subaddr_account, 0}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});//MY LOCAL (SUB)ADDRESS
|
||||
const string payment_id = "";
|
||||
const string asset_type = "SAL";
|
||||
const bool is_return = false;
|
||||
@@ -1737,8 +1894,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 */,
|
||||
@@ -2886,4 +3043,9 @@ YieldInfo * WalletImpl::getYieldInfo()
|
||||
return yi;
|
||||
}
|
||||
|
||||
std::vector<std::string> WalletImpl::getAssetTypes()
|
||||
{
|
||||
return m_wallet->list_asset_types();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
+20
-2
@@ -83,6 +83,8 @@ public:
|
||||
Device getDeviceType() const override;
|
||||
bool close(bool store = true);
|
||||
std::string seed(const std::string& seed_offset = "") const override;
|
||||
void setStoreTxInfo(bool store) override;
|
||||
bool storeTxInfo() const override;
|
||||
std::string getSeedLanguage() const override;
|
||||
void setSeedLanguage(const std::string &arg) override;
|
||||
// void setListener(Listener *) {}
|
||||
@@ -93,13 +95,18 @@ public:
|
||||
const std::string& getPassword() const override;
|
||||
bool setDevicePin(const std::string &password) override;
|
||||
bool setDevicePassphrase(const std::string &password) override;
|
||||
std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const override;
|
||||
std::string integratedAddress(const std::string &payment_id) const override;
|
||||
std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0, bool carrot = true) const override;
|
||||
std::string integratedAddress(const std::string &payment_id, bool carrot = true) const override;
|
||||
std::string secretViewKey() const override;
|
||||
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 +165,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 +263,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 {
|
||||
@@ -234,6 +236,7 @@ struct TransactionInfo
|
||||
virtual std::string hash() const = 0;
|
||||
virtual std::time_t timestamp() const = 0;
|
||||
virtual std::string paymentId() const = 0;
|
||||
virtual const std::vector<std::string> & returnAddresses() const = 0;
|
||||
//! only applicable for output transactions
|
||||
virtual const std::vector<Transfer> & transfers() const = 0;
|
||||
virtual Monero::transaction_type type() const = 0;
|
||||
@@ -366,7 +369,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 {
|
||||
@@ -501,6 +504,8 @@ struct Wallet
|
||||
|
||||
virtual ~Wallet() = 0;
|
||||
virtual std::string seed(const std::string& seed_offset = "") const = 0;
|
||||
virtual void setStoreTxInfo(bool store) = 0;
|
||||
virtual bool storeTxInfo() const = 0;
|
||||
virtual std::string getSeedLanguage() const = 0;
|
||||
virtual void setSeedLanguage(const std::string &arg) = 0;
|
||||
//! returns wallet status (Status_Ok | Status_Error)
|
||||
@@ -513,8 +518,8 @@ struct Wallet
|
||||
virtual const std::string& getPassword() const = 0;
|
||||
virtual bool setDevicePin(const std::string &pin) { (void)pin; return false; };
|
||||
virtual bool setDevicePassphrase(const std::string &passphrase) { (void)passphrase; return false; };
|
||||
virtual std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const = 0;
|
||||
std::string mainAddress() const { return address(0, 0); }
|
||||
virtual std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0, bool carrot = true) const = 0;
|
||||
std::string mainAddress(bool carrot = true) const { return address(0, 0, carrot); }
|
||||
virtual std::string path() const = 0;
|
||||
virtual NetworkType nettype() const = 0;
|
||||
bool mainnet() const { return nettype() == MAINNET; }
|
||||
@@ -533,7 +538,7 @@ struct Wallet
|
||||
* generated
|
||||
* \return - 106 characters string representing integrated address
|
||||
*/
|
||||
virtual std::string integratedAddress(const std::string &payment_id) const = 0;
|
||||
virtual std::string integratedAddress(const std::string &payment_id, bool carrot = true) const = 0;
|
||||
|
||||
/*!
|
||||
* \brief secretViewKey - returns secret view key
|
||||
@@ -559,12 +564,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 +938,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 +1290,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));
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user