diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml
index d684b2de0..653176e24 100644
--- a/.buildkite/pipeline.yml
+++ b/.buildkite/pipeline.yml
@@ -1,9 +1,21 @@
steps:
- label: ":docker: build docker image"
- command: ".buildkite/steps/build-image.sh"
-
+ command:
+ - ".buildkite/steps/build-image.sh"
+ - ".buildkite/steps/upload-image.sh"
- wait
- label: ":cop::skin-tone-2: deploy check"
command: ".buildkite/steps/deploy-test.sh"
- timeout: 20
+ timeout: 30
+
+ - wait
+
+ - label: ":floppy-disk: publish image"
+ command: ".buildkite/steps/publish-image.sh"
+ branches: "master develop ci-* v*.*.*"
+
+ - wait
+
+ - label: ":slack:"
+ command: ".buildkite/steps/slack.sh \"Pipeline complete successfully: ${BUILDKITE_MESSAGE}\" \"good\""
diff --git a/.buildkite/steps/build-image.sh b/.buildkite/steps/build-image.sh
old mode 100644
new mode 100755
index a0b8e3b5b..1e341426b
--- a/.buildkite/steps/build-image.sh
+++ b/.buildkite/steps/build-image.sh
@@ -1,14 +1,27 @@
#!/bin/bash
set -euo pipefail
-IMAGETAG=${BUILDKITE_BRANCH:-master}
-BRANCHNAME=${BUILDKITE_BRANCH:-master}
+REVISION=$(git rev-parse HEAD)
-if [[ "${IMAGETAG}" == "alfa" ]]; then
- BUILDTYPE="alfa"
+if [[ "${BUILDKITE_BRANCH}" == "master" ]]; then
+ BUILDTYPE="stable"
else
BUILDTYPE="latest"
fi
-cd Docker
-docker build -t cyberway/cyberway.contracts:${IMAGETAG} --build-arg branch=${BRANCHNAME} --build-arg buildtype=${BUILDTYPE} .
+if [[ -z ${CDT_TAG+x} ]]; then
+ CDT_TAG=${BUILDTYPE}
+ docker pull cyberway/cyberway.cdt:${CDT_TAG}
+fi
+
+if [[ -z ${CW_TAG+x} ]]; then
+ CW_TAG=${BUILDTYPE}
+ docker pull cyberway/cyberway:${CW_TAG}
+fi
+
+if [[ -z ${BUILDER_TAG+x} ]]; then
+ BUILDER_TAG=${BUILDTYPE}
+ docker pull cyberway/builder:${BUILDER_TAG}
+fi
+
+docker build -t cyberway/cyberway.contracts:${REVISION} --build-arg=version=${REVISION} --build-arg=cw_tag=${CW_TAG} --build-arg=cdt_tag=${CDT_TAG} --build-arg=builder_tag=${BUILDER_TAG} -f Docker/Dockerfile .
diff --git a/.buildkite/steps/deploy-test.sh b/.buildkite/steps/deploy-test.sh
old mode 100644
new mode 100755
index 284381453..36f30edef
--- a/.buildkite/steps/deploy-test.sh
+++ b/.buildkite/steps/deploy-test.sh
@@ -9,12 +9,14 @@ docker volume create --name=cyberway-mongodb-data
cd Docker
-IMAGETAG=${BUILDKITE_BRANCH:-master}
+IMAGETAG=$(git rev-parse HEAD)
docker-compose up -d
# Run unit-tests
sleep 10s
+
+docker pull cyberway/cyberway.contracts:$IMAGETAG
docker run --network cyberway-tests_contracts-net -ti cyberway/cyberway.contracts:$IMAGETAG /bin/bash -c 'export MONGO_URL=mongodb://mongo:27017; /opt/cyberway.contracts/unit_test -l message -r detailed'
result=$?
diff --git a/.buildkite/steps/publish-image.sh b/.buildkite/steps/publish-image.sh
new file mode 100755
index 000000000..d57e8a27f
--- /dev/null
+++ b/.buildkite/steps/publish-image.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+set -euo pipefail
+
+REVISION=$(git rev-parse HEAD)
+
+docker images
+
+docker login -u=$DHUBU -p=$DHUBP
+
+if [[ ${BUILDKITE_BRANCH} == "master" ]]; then
+ TAG=stable
+elif [[ ${BUILDKITE_BRANCH} == "develop" ]]; then
+ TAG=latest
+else
+ TAG=${BUILDKITE_BRANCH}
+fi
+
+docker pull cyberway/cyberway.contracts:${REVISION}
+docker tag cyberway/cyberway.contracts:${REVISION} cyberway/cyberway.contracts:${TAG}
+docker push cyberway/cyberway.contracts:${TAG}
\ No newline at end of file
diff --git a/.buildkite/steps/slack.sh b/.buildkite/steps/slack.sh
new file mode 100755
index 000000000..91249aede
--- /dev/null
+++ b/.buildkite/steps/slack.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+set -euo pipefail
+
+SLACK_MESSAGE=${1:-"No message"}
+SLACK_COLOR=${2:-"bad"}
+
+curl -X POST -H 'Content-type: application/json' \
+--data "{\"text\":\"${SLACK_MESSAGE}\", \
+ \"attachments\":[ \
+ { \
+ \"fallback\":\"${SLACK_MESSAGE}\", \
+ \"color\":\"${SLACK_COLOR}\", \
+ \"fields\":[ \
+ { \
+ \"title\":\"Branch\", \
+ \"value\":\"$BUILDKITE_BRANCH\", \
+ \"short\":false \
+ }, \
+ { \
+ \"title\":\"Creator\", \
+ \"value\":\"$BUILDKITE_BUILD_CREATOR\", \
+ \"short\":false \
+ }, \
+ { \
+ \"title\":\"URL\", \
+ \"value\":\"<$BUILDKITE_BUILD_URL|$BUILDKITE_BUILD_URL>\", \
+ \"short\":false \
+ } \
+ ] \
+ } \
+ ] \
+}" ${WEBHOOK}
diff --git a/.buildkite/steps/upload-image.sh b/.buildkite/steps/upload-image.sh
new file mode 100755
index 000000000..ba51669fa
--- /dev/null
+++ b/.buildkite/steps/upload-image.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+set -euo pipefail
+
+REVISION=$(git rev-parse HEAD)
+
+docker images
+
+docker login -u=$DHUBU -p=$DHUBP
+
+docker push cyberway/cyberway.contracts:${REVISION}
+
+
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 000000000..46083306e
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,5 @@
+.git
+.gitmodules
+.gitignore
+.buildkite
+.idea
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 880c18e5b..46251c045 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -30,11 +30,15 @@ else()
set(TEST_BUILD_TYPE ${CMAKE_BUILD_TYPE})
endif()
+macro(install_contract TARGET)
+ install (TARGETS ${TARGET}.wasm DESTINATION ${CMAKE_INSTALL_PREFIX}/${TARGET}/)
+ install (FILES ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}.abi DESTINATION ${CMAKE_INSTALL_PREFIX}/${TARGET}/)
+endmacro()
+
macro(add_contract_with_abi TARGET ABIFILE)
add_executable( ${TARGET}.wasm ${ARGN} )
configure_file(${ABIFILE} ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}.abi COPYONLY)
- install (TARGETS ${TARGET}.wasm DESTINATION ${CMAKE_INSTALL_PREFIX}/${TARGET}/)
- install (FILES ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}.abi DESTINATION ${CMAKE_INSTALL_PREFIX}/${TARGET}/)
+ install_contract(TARGET)
endmacro()
add_subdirectory(cyber.bios)
@@ -46,6 +50,8 @@ add_subdirectory(cyber.govern)
add_subdirectory(cyber.rejector)
add_subdirectory(cyber.incomereject)
+add_subdirectory(scripts/base-genesis)
+
if (APPLE)
set(OPENSSL_ROOT "/usr/local/opt/openssl")
elseif (UNIX)
diff --git a/Docker/Dockerfile b/Docker/Dockerfile
index 50d7e1c61..e2b094f6c 100644
--- a/Docker/Dockerfile
+++ b/Docker/Dockerfile
@@ -1,34 +1,37 @@
-ARG buildtype=stable
-FROM cyberway/cyberway:$buildtype as cyberway
-FROM cyberway/cyberway.cdt:$buildtype as cdt
+ARG cw_tag=stable
+ARG cdt_tag=stable
+ARG builder_tag=stable
+
+FROM cyberway/cyberway:$cw_tag as cyberway
+FROM cyberway/cyberway.cdt:$cdt_tag as cdt
+FROM cyberway/builder:$builder_tag as builder
-FROM cyberway/builder:$buildtype as builder
COPY --from=cdt /opt/cyberway.cdt /opt/cyberway.cdt
COPY --from=cyberway /opt/cyberway /opt/cyberway
-ARG branch=master
-ADD https://api.github.com/repos/cyberway/cyberway.contracts/git/refs/heads/$branch /etc/version.json
-RUN git clone -b $branch https://github.com/cyberway/cyberway.contracts.git --recursive
+ENV CYBERWAY /opt/cyberway/
+
+COPY . /cyberway.contracts
-RUN cd cyberway.contracts \
- && echo "$branch:$(git rev-parse HEAD)" > version \
+RUN ldconfig && cd cyberway.contracts \
&& cmake -H. -B"build" \
-GNinja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/opt/cyberway.contracts/ \
-Dcyberway.cdt_DIR=/opt/cyberway.cdt/lib/cmake/cyberway.cdt \
- -DEOSIO_ROOT=/opt/cyberway \
+ -DEOSIO_ROOT=$CYBERWAY \
&& cmake --build build --target install
-
FROM ubuntu:18.04
+ARG version=unknown
RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get -y install openssl ca-certificates python3 python3-numpy python3-bson libusb-1.0-0-dev libcurl4-gnutls-dev \
&& rm -rf /var/lib/apt/lists/*
-COPY --from=builder /cyberway.contracts/version /opt/cyberway.contracts/version
+
COPY --from=builder /opt/cyberway.contracts/ /opt/cyberway.contracts/
+
COPY --from=builder /cyberway.contracts/build/tests/unit_test /opt/cyberway.contracts/unit_test
COPY --from=builder /cyberway.contracts/tests/test_contracts /opt/cyberway.contracts/test_contracts
@@ -39,7 +42,7 @@ COPY --from=cyberway /opt/cyberway/bin/cleos /opt/cyberway/bin/cleos
COPY --from=cyberway /opt/cyberway/bin/keosd /opt/cyberway/bin/keosd
COPY --from=cyberway /opt/cyberway/bin/create-genesis /opt/cyberway/bin/create-genesis
-RUN ldconfig && ln -s ../cyberway.contracts /opt/cyberway.contracts
+RUN echo $version > /opt/cyberway.contracts/version && ldconfig
ENV CYBERWAY_TEST_CONTRACTS /opt/cyberway.contracts/test_contracts/
ENV CYBERWAY_CONTRACTS /opt/cyberway.contracts/
diff --git a/README.md b/README.md
index 718abe3df..58d8185ba 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
*****
-[![buildkite](https://badge.buildkite.com/cbc4061f218d570917e365bfff8a251c03996f43f35f4deb66.svg?branch=master)](https://buildkite.com/cyberway.contracts)
+[![buildkite](https://badge.buildkite.com/f0940b2380542f6c80c1c01aa773d61c1d3470007fa5b9e6c3.svg?branch=master)](https://buildkite.com/cyberway.contracts)
[![GitHub](https://img.shields.io/github/license/cyberway/cyberway.contracts.svg)](https://github.com/cyberway/cyberway.contracts/blob/master/LICENSE)
diff --git a/build.sh b/build.sh
index 5ef9e1ebf..9c6c9e9e6 100755
--- a/build.sh
+++ b/build.sh
@@ -1,6 +1,6 @@
#! /bin/bash
-printf "\t=========== Building eosio.contracts ===========\n\n"
+printf "\t=========== Building cyberway.contracts ===========\n\n"
RED='\033[0;31m'
NC='\033[0m'
diff --git a/common/config.hpp b/common/config.hpp
index a16aa2724..88114f1a4 100644
--- a/common/config.hpp
+++ b/common/config.hpp
@@ -16,6 +16,9 @@ inline eosio::chain::name operator ""_n() {
}
#endif
+#define CYBER_TOKEN "cyber.token"
+#define CYBER_STAKE "cyber.stake"
+
namespace cyber { namespace config {
// contracts
diff --git a/common/dispatchers.hpp b/common/dispatchers.hpp
index d7673489d..6c70a8bae 100644
--- a/common/dispatchers.hpp
+++ b/common/dispatchers.hpp
@@ -50,19 +50,14 @@ extern "C" { \
} \
} \
+#define ON_SIMPLE_TRANSFER(TOKEN) [[eosio::on_notify(TOKEN "::transfer")]]
-#define DISPATCH_WITH_UNSTAKING(TYPE, STAKE, WITHDRAW, PROVIDE, MEMBERS) \
-extern "C" { \
- void apply(uint64_t receiver, uint64_t code, uint64_t action) { \
- if (code == receiver) { \
- switch (action) { \
- EOSIO_DISPATCH_HELPER(TYPE, MEMBERS) \
- } \
- } else if (code == STAKE.value && action == "withdraw"_n.value) { \
- eosio::execute_action(eosio::name(receiver), eosio::name(code), &TYPE::WITHDRAW); \
- } else if (code == STAKE.value && action == "provide"_n.value) { \
- eosio::execute_action(eosio::name(receiver), eosio::name(code), &TYPE::PROVIDE); \
- } \
- } \
-} \
+#define ON_BULK_TRANSFER(TOKEN) [[eosio::on_notify(TOKEN "::bulktransfer")]]
+#define ON_TRANSFER(TOKEN, ON_TRANSFER_HANDLER) \
+ ON_BULK_TRANSFER(TOKEN) void on_bulk_transfer(name from, std::vector recipients) { \
+ for (auto& recipient : recipients) { \
+ ON_TRANSFER_HANDLER(from, recipient.to, recipient.quantity, recipient.memo); \
+ } \
+ } \
+ ON_SIMPLE_TRANSFER(TOKEN)
diff --git a/cyber.bios/CMakeLists.txt b/cyber.bios/CMakeLists.txt
index 44a8427b8..0a7d720df 100644
--- a/cyber.bios/CMakeLists.txt
+++ b/cyber.bios/CMakeLists.txt
@@ -1,4 +1,6 @@
-add_contract_with_abi(cyber.bios abi/cyber.bios.abi ${CMAKE_CURRENT_SOURCE_DIR}/src/cyber.bios.cpp)
+add_contract(cyber.bios cyber.bios ${CMAKE_CURRENT_SOURCE_DIR}/src/cyber.bios.cpp)
+install_contract(cyber.bios)
+
target_include_directories(cyber.bios.wasm
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../cyber.govern/include
diff --git a/cyber.bios/abi/cyber.bios.abi b/cyber.bios/abi/cyber.bios.abi
deleted file mode 100644
index 3387c9679..000000000
--- a/cyber.bios/abi/cyber.bios.abi
+++ /dev/null
@@ -1,203 +0,0 @@
-{
- "version": "cyberway::abi/1.1",
- "structs": [
- {
- "name": "authority",
- "base": "",
- "fields": [
- {
- "name": "threshold",
- "type": "uint32"
- },
- {
- "name": "keys",
- "type": "key_weight[]"
- },
- {
- "name": "accounts",
- "type": "permission_level_weight[]"
- },
- {
- "name": "waits",
- "type": "wait_weight[]"
- }
- ]
- },
- {
- "name": "name_bid",
- "base": "",
- "fields": [
- {"name":"newname", "type":"name"},
- {"name":"high_bidder", "type":"name"},
- {"name":"high_bid", "type":"int64"},
- {"name":"last_bid_time", "type":"time_point_sec"}
- ]
- },
- {
- "name": "bid_refund",
- "base": "",
- "fields": [
- {"name":"bidder", "type":"name"},
- {"name":"amount", "type":"asset"}
- ]
- },
- {
- "name": "key_weight",
- "base": "",
- "fields": [
- {
- "name": "key",
- "type": "public_key"
- },
- {
- "name": "weight",
- "type": "uint16"
- }
- ]
- },
- {
- "name": "permission_level",
- "base": "",
- "fields": [
- {
- "name": "actor",
- "type": "name"
- },
- {
- "name": "permission",
- "type": "name"
- }
- ]
- },
- {
- "name": "permission_level_weight",
- "base": "",
- "fields": [
- {
- "name": "permission",
- "type": "permission_level"
- },
- {
- "name": "weight",
- "type": "uint16"
- }
- ]
- },
- {
- "name": "producer_key",
- "base": "",
- "fields": [
- {
- "name": "producer_name",
- "type": "name"
- },
- {
- "name": "block_signing_key",
- "type": "public_key"
- }
- ]
- },
- {
- "name": "setprods",
- "base": "",
- "fields": [
- {
- "name": "schedule",
- "type": "producer_key[]"
- }
- ]
- },
- {
- "name": "wait_weight",
- "base": "",
- "fields": [
- {
- "name": "wait_sec",
- "type": "uint32"
- },
- {
- "name": "weight",
- "type": "uint16"
- }
- ]
- },
- {
- "name": "state_info",
- "base": "",
- "fields": [
- {"type": "uint64", "name": "id"},
- {"type": "time_point_sec", "name": "last_close_bid"}
- ]
- },
- {
- "name": "checkwin",
- "base": "",
- "fields": []
- },
- {
- "name": "bidname",
- "base": "",
- "fields": [
- {"type": "name", "name": "bidder"},
- {"type": "name", "name": "newname"},
- {"type": "asset", "name": "bid"}
- ]
- },
- {
- "name": "bidrefund",
- "base": "",
- "fields": [
- {"type": "name", "name": "bidder"}
- ]
- }
- ],
- "types": [],
- "actions": [
- {
- "name": "setprods",
- "type": "setprods"
- },
- {
- "name": "checkwin",
- "type": "checkwin"
- },
- {
- "name": "bidname",
- "type": "bidname"
- },
- {
- "name": "bidrefund",
- "type": "bidrefund"
- }
- ],
- "tables": [
- {
- "name": "namebids",
- "type": "name_bid",
- "indexes": [
- { "name": "primary", "unique": "true", "orders": [ {"field": "newname", "order": "asc"} ] },
- { "name": "highbid", "unique": "false", "orders": [ {"field": "high_bid", "order": "desc"} ] }
- ]
- },
- {
- "name": "bidrefunds",
- "type": "bid_refund",
- "indexes": [
- { "name": "primary", "unique": "true", "orders": [ {"field": "bidder", "order": "asc"} ] }
- ]
- },
- {
- "name": "biosstate",
- "type": "state_info",
- "indexes": [{
- "name": "primary",
- "unique": "true",
- "orders": [
- {"field": "id", "order": "asc"}
- ]
- }]
- }
- ],
- "variants": [],
- "abi_extensions": []
-}
diff --git a/cyber.bios/include/cyber.bios/cyber.bios.hpp b/cyber.bios/include/cyber.bios/cyber.bios.hpp
index aae3f5bbc..99d786568 100644
--- a/cyber.bios/include/cyber.bios/cyber.bios.hpp
+++ b/cyber.bios/include/cyber.bios/cyber.bios.hpp
@@ -7,6 +7,8 @@
#include
#include
#include
+#include
+#include
namespace cyber {
using eosio::permission_level;
@@ -63,20 +65,22 @@ namespace cyber {
};
class [[eosio::contract("cyber.bios")]] bios : public contract {
- struct [[eosio::table("state")]] state_info {
+ struct state_info {
time_point_sec last_close_bid;
};
- using state_singleton = eosio::singleton<"biosstate"_n, state_info>;
+ using state_singleton [[eosio::order("id","asc")]] =
+ eosio::singleton<"biosstate"_n, state_info>;
- struct [[eosio::table, eosio::contract("cyber.bios")]] bid_refund {
+ struct bid_refund {
name bidder;
eosio::asset amount;
uint64_t primary_key()const { return bidder.value; }
};
- typedef eosio::multi_index< "bidrefunds"_n, bid_refund > bid_refund_table;
+ using bid_refund_table [[eosio::order("bidder","asc")]] =
+ eosio::multi_index<"bidrefunds"_n, bid_refund>;
- struct [[eosio::table, eosio::contract("cyber.bios")]] name_bid {
+ struct name_bid {
name newname;
name high_bidder;
int64_t high_bid = 0; ///< negative high_bid == closed auction waiting to be claimed
@@ -85,10 +89,27 @@ namespace cyber {
uint64_t primary_key()const { return newname.value; }
uint64_t by_high_bid()const { return static_cast(-high_bid); }
};
- typedef eosio::multi_index< "namebids"_n, name_bid,
- eosio::indexed_by<"highbid"_n, eosio::const_mem_fun >
- > name_bid_table;
-
+ using by_high [[using eosio: order("high_bid","desc"), non_unique]] =
+ eosio::indexed_by<"highbid"_n, eosio::const_mem_fun>;
+ using name_bid_table [[eosio::order("newname","asc")]] =
+ eosio::multi_index<"namebids"_n, name_bid, by_high>;
+
+ struct auto_recall {
+ uint64_t id;
+ eosio::symbol_code token_code;
+ name account;
+ bool break_fee_enabled = false;
+ bool break_min_stake_enabled = false;
+ uint64_t primary_key()const { return id; }
+ using key_t = std::tuple;
+ key_t by_key()const { return std::make_tuple(token_code, account); }
+ };
+ using autorc_key_index [[using eosio: order("token_code"), order("account")]] =
+ eosio::indexed_by<"bykey"_n, eosio::const_mem_fun >;
+ using autorcs [[eosio::order("id")]] =
+ eosio::multi_index<"stake.autorc"_n, auto_recall, autorc_key_index>;
+ void autorcs_dummy() { autorcs autorcs_tabl(_self, _self.value); } // an ugly way to make abi appear
+
void check_stake(name account);
public:
using contract::contract;
@@ -159,9 +180,12 @@ namespace cyber {
void bidrefund( name bidder );
[[eosio::action]] void onblock(ignore header);
-
- void on_stake_withdraw(name account, asset quantity);
- void on_stake_provide(name provider_name, name consumer_name, asset quantity);
+
+ [[eosio::action]]
+ void providebw(name provider, name account) {} // defined in cyberway/libraries/chain/cyberway/cyberway_contract.cpp
+
+ [[eosio::on_notify(CYBER_STAKE "::withdraw")]] void on_stake_withdraw(name account, asset quantity);
+ [[eosio::on_notify(CYBER_STAKE "::provide")]] void on_stake_provide(name provider_name, name consumer_name, asset quantity);
};
diff --git a/cyber.bios/src/cyber.bios.cpp b/cyber.bios/src/cyber.bios.cpp
index 1f4f1c277..4e216f0d2 100644
--- a/cyber.bios/src/cyber.bios.cpp
+++ b/cyber.bios/src/cyber.bios.cpp
@@ -1,9 +1,7 @@
#include
#include
#include
-#include
#include
-#include
#include
@@ -178,9 +176,3 @@ void bios::on_stake_provide(name provider_name, name consumer_name, asset quanti
}
}
-
-DISPATCH_WITH_UNSTAKING(cyber::bios, cyber::config::stake_name, on_stake_withdraw, on_stake_provide,
- (newaccount)(setprods)(setparams)(reqauth)(setabi)(setcode)(onblock)(checkwin)(bidname)(bidrefund)
-)
-
-
diff --git a/cyber.domain/CMakeLists.txt b/cyber.domain/CMakeLists.txt
index c4940da84..7148dc1a2 100644
--- a/cyber.domain/CMakeLists.txt
+++ b/cyber.domain/CMakeLists.txt
@@ -1,4 +1,6 @@
-add_contract_with_abi(cyber.domain cyber.domain.abi ${CMAKE_CURRENT_SOURCE_DIR}/cyber.domain.cpp)
+add_contract(cyber.domain cyber.domain ${CMAKE_CURRENT_SOURCE_DIR}/cyber.domain.cpp)
+install_contract(cyber.domain)
+
target_include_directories(cyber.domain.wasm
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../cyber.token/include
@@ -7,6 +9,3 @@ target_include_directories(cyber.domain.wasm
set_target_properties(cyber.domain.wasm
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
-
-install (TARGETS cyber.domain.wasm DESTINATION ${CMAKE_INSTALL_PREFIX}/cyber.domain/)
-#install (FILES ${CMAKE_CURRENT_BINARY_DIR}/cyber.domain.abi DESTINATION ${CMAKE_INSTALL_PREFIX}/cyber.domain/)
diff --git a/cyber.domain/cyber.domain.abi b/cyber.domain/cyber.domain.abi
deleted file mode 100644
index ab216f950..000000000
--- a/cyber.domain/cyber.domain.abi
+++ /dev/null
@@ -1,185 +0,0 @@
-{
- "version": "cyberway::abi/1.0",
- "types": [
- {"new_type_name": "domain_name", "type": "string"},
- {"new_type_name": "username", "type": "string"}
- ]
- "structs": [
- {
- "name": "newdomain",
- "base": "",
- "fields": [
- {"type": "name", "name": "creator"},
- {"type": "domain_name", "name": "name"}
- ]
- },{
- "name": "passdomain",
- "base": "",
- "fields": [
- {"type": "name", "name": "from"},
- {"type": "name", "name": "to"},
- {"type": "domain_name", "name": "name"}
- ]
- },{
- "name": "linkdomain",
- "base": "",
- "fields": [
- {"type": "name", "name": "owner"},
- {"type": "name", "name": "to"},
- {"type": "domain_name", "name": "name"}
- ]
- },{
- "name": "unlinkdomain",
- "base": "",
- "fields": [
- {"type": "name", "name": "owner"},
- {"type": "domain_name", "name": "name"}
- ]
- },{
- "name": "newusername",
- "base": "",
- "fields": [
- {"type": "name", "name": "creator"},
- {"type": "name", "name": "owner"},
- {"type": "username", "name": "name"}
- ]
- },{
- "name": "biddomain",
- "base": "",
- "fields": [
- {"type": "name", "name": "bidder"},
- {"type": "domain_name", "name": "name"},
- {"type": "asset", "name": "bid"}
- ]
- },{
- "name": "biddmrefund",
- "base": "",
- "fields": [
- {"type": "name", "name": "bidder"}
- ]
- },{
- "name": "checkwin",
- "base": "",
- "fields": []
- },{
- "name": "declarenames",
- "base": "",
- "fields": [
- {"type": "name_info[]", "name": "domains"}
- ]
- },{
- "name": "name_info",
- "base": "",
- "fields": [
- {"type": "domain_name", "name": "domain"},
- {"type": "name", "name": "account"},
- {"type": "username[]", "name": "users"}
- ]
- },{
- "name": "domain_bid_state",
- "base": "",
- "fields": [
- {"type": "uint64", "name": "id"},
- {"type": "time_point_sec", "name": "last_win"}
- ]
- },{
- "name": "domain_bid",
- "base": "",
- "fields": [
- {"type": "uint64", "name": "id"},
- {"type": "domain_name", "name": "name"},
- {"type": "name", "name": "high_bidder"},
- {"type": "int64", "name": "high_bid"},
- {"type": "time_point_sec", "name": "last_bid_time"}
- ]
- },{
- "name": "domain_bid_refund",
- "base": "",
- "fields": [
- {"type": "name", "name": "bidder"},
- {"type": "asset", "name": "amount"}
- ]
- }
- ],
- "types": [],
- "actions": [
- {
- "name": "biddomain",
- "type": "biddomain"
- },{
- "name": "biddmrefund",
- "type": "biddmrefund"
- },{
- "name": "newdomain",
- "type": "newdomain"
- },{
- "name": "checkwin",
- "type": "checkwin"
- },{
- "name": "declarenames",
- "type": "declarenames"
- },{
- "name": "passdomain",
- "type": "passdomain",
- },{
- "name": "linkdomain",
- "type": "linkdomain",
- },{
- "name": "unlinkdomain",
- "type": "unlinkdomain",
- },{
- "name": "newusername",
- "type": "newusername",
- }
- ],
- "tables": [
- {
- "name": "domainbid",
- "type": "domain_bid",
- "indexes": [
- {
- "name": "primary",
- "unique": "true",
- "orders": [
- {"field": "id", "order": "asc"}
- ]
- },{
- "name": "domain",
- "unique": "true",
- "orders": [
- {"field": "name", "order": "asc"}
- ]
- },{
- "name": "highbid",
- "unique": "true",
- "orders": [
- {"field": "high_bid", "order": "desc"},
- {"field": "name", "order": "asc"}
- ]
- }
- ]
- },{
- "name": "dbidrefund",
- "type": "domain_bid_refund",
- "indexes": [{
- "name": "primary",
- "unique": "true",
- "orders": [
- {"field": "bidder", "order": "asc"}
- ]
- }]
- },{
- "name": "dbidstate",
- "type": "domain_bid_state",
- "indexes": [{
- "name": "primary",
- "unique": "true",
- "orders": [
- {"field": "id", "order": "asc"}
- ]
- }]
- }
- ],
- "variants": [],
- "abi_extensions": []
-}
\ No newline at end of file
diff --git a/cyber.domain/cyber.domain.cpp b/cyber.domain/cyber.domain.cpp
index a02adc42f..be02702c5 100644
--- a/cyber.domain/cyber.domain.cpp
+++ b/cyber.domain/cyber.domain.cpp
@@ -2,7 +2,6 @@
#include
#include
#include
-#include
#include "domain_validate.cpp"
@@ -80,7 +79,7 @@ void domain::biddomain(name bidder, const domain_name& name, asset bid) {
if (current == idx.end()) {
bids.emplace(bidder, [&](auto& b) {
b.id = bids.available_primary_key();
- b.domain = name;
+ b.name = name;
set_bid(b);
});
} else {
@@ -169,8 +168,3 @@ void domain::declarenames(const std::vector& domains) {
}
} // eosiosystem
-
-
-EOSIO_DISPATCH(eosiosystem::domain,
- (newdomain)(checkwin)(biddomain)(biddmrefund)(declarenames)
-)
diff --git a/cyber.domain/cyber.domain.hpp b/cyber.domain/cyber.domain.hpp
index c1be38a79..21d6c6368 100644
--- a/cyber.domain/cyber.domain.hpp
+++ b/cyber.domain/cyber.domain.hpp
@@ -17,44 +17,47 @@ using eosio::time_point_sec;
// declares domain and linked account to ensure deferred tx applied for right account
-struct [[eosio::table, eosio::contract("cyber.domain")]] name_info {
+struct name_info {
domain_name domain; // domain. empty value = username@@account case
name account; // account_name linked to given domain
std::vector users; // usernames of this domain used in tx
};
-struct [[eosio::table, eosio::contract("cyber.domain")]] domain_bid {
+struct domain_bid {
uint64_t id;
- domain_name domain;
- name high_bidder;
+ domain_name name;
+ eosio::name high_bidder;
int64_t high_bid = 0; ///< negative high_bid == closed auction waiting to be claimed
time_point_sec last_bid_time;
uint64_t primary_key() const { return id; }
- domain_name by_domain() const { return domain; }
+ domain_name by_domain() const { return name; }
int64_t by_high_bid() const { return high_bid; } // ordered desc, check abi
};
-struct [[eosio::table, eosio::contract("cyber.domain")]] domain_bid_refund {
+using domain_index [[using eosio: order("name","asc"), contract("cyber.domain")]] =
+ eosio::indexed_by<"domain"_n, eosio::const_mem_fun>;
+using domain_high_index [[using eosio: order("high_bid","desc"), order("name","asc"), contract("cyber.domain")]] =
+ eosio::indexed_by<"highbid"_n, eosio::const_mem_fun>;
+using domain_bid_tbl [[using eosio: order("id","asc"), contract("cyber.domain")]] =
+ eosio::multi_index<"domainbid"_n, domain_bid, domain_index, domain_high_index>;
+
+struct domain_bid_refund {
name bidder;
asset amount;
uint64_t primary_key() const { return bidder.value; }
};
-using domain_bid_tbl = eosio::multi_index<"domainbid"_n, domain_bid,
- eosio::indexed_by<"domain"_n, eosio::const_mem_fun>,
- eosio::indexed_by<"highbid"_n, eosio::const_mem_fun>
->;
-using domain_bid_refund_tbl = eosio::multi_index< "dbidrefund"_n, domain_bid_refund>;
+using domain_bid_refund_tbl [[using eosio: order("bidder","asc"), contract("cyber.domain")]] = eosio::multi_index<"dbidrefund"_n, domain_bid_refund>;
-struct [[eosio::table("state"), eosio::contract("cyber.domain")]] domain_bid_state {
+struct domain_bid_state {
time_point_sec last_win;
// explicit serialization macro is not necessary, used here only to improve compilation time
EOSLIB_SERIALIZE(domain_bid_state, (last_win))
};
-using state_singleton = eosio::singleton<"dbidstate"_n, domain_bid_state>;
+using state_singleton [[using eosio: order("id","asc"), contract("cyber.domain")]] = eosio::singleton<"dbidstate"_n, domain_bid_state>;
class [[eosio::contract("cyber.domain")]] domain: public domain_native {
@@ -76,6 +79,15 @@ class [[eosio::contract("cyber.domain")]] domain: public domain_native {
// * there must be no 2+ domains with the same `.domain` value except empty ("") value
// * there must be no 2+ domains with empty value and the same `.account`
[[eosio::action]] void declarenames(const std::vector& domains);
+
+ [[eosio::action]] void newusername(name creator, name owner, const std::string& name) {} // defined in cyberway/libraries/chain/cyberway/cyberway_contract.cpp
+
+ [[eosio::action]] void linkdomain(name owner, name to, const std::string& name) {} // defined in cyberway/libraries/chain/cyberway/cyberway_contract.cpp
+
+ [[eosio::action]] void unlinkdomain(name owner, const std::string& name) {} // defined in cyberway/libraries/chain/cyberway/cyberway_contract.cpp
+
+ [[eosio::action]] void passdomain(name from, name to, const std::string& name) {} // defined in cyberway/libraries/chain/cyberway/cyberway_contract.cpp
+
};
} /// eosiosystem
diff --git a/cyber.govern/CMakeLists.txt b/cyber.govern/CMakeLists.txt
index d49cc1b83..08ff9604a 100644
--- a/cyber.govern/CMakeLists.txt
+++ b/cyber.govern/CMakeLists.txt
@@ -1,4 +1,6 @@
-add_contract_with_abi(cyber.govern abi/cyber.govern.abi ${CMAKE_CURRENT_SOURCE_DIR}/src/cyber.govern.cpp)
+add_contract(cyber.govern cyber.govern ${CMAKE_CURRENT_SOURCE_DIR}/src/cyber.govern.cpp)
+install_contract(cyber.govern)
+
target_include_directories(cyber.govern.wasm
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
@@ -9,6 +11,3 @@ target_include_directories(cyber.govern.wasm
set_target_properties(cyber.govern.wasm
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
-
-install (TARGETS cyber.govern.wasm DESTINATION ${CMAKE_INSTALL_PREFIX}/cyber.govern/)
-install (FILES ${CMAKE_CURRENT_BINARY_DIR}/cyber.govern.abi DESTINATION ${CMAKE_INSTALL_PREFIX}/cyber.govern/)
diff --git a/cyber.govern/abi/cyber.govern.abi b/cyber.govern/abi/cyber.govern.abi
deleted file mode 100644
index 01498035a..000000000
--- a/cyber.govern/abi/cyber.govern.abi
+++ /dev/null
@@ -1,143 +0,0 @@
-{
- "version": "cyberway::abi/1.1",
- "structs": [
- {
- "name": "state_info",
- "base": "",
- "fields": [
- { "type": "uint64", "name": "id" },
- { "type": "time_point_sec", "name": "last_schedule_increase" },
- { "type": "uint32", "name": "block_num" },
- { "type": "int64", "name": "target_emission_per_block" },
- { "type": "int64", "name": "funds" },
- { "type": "uint32", "name": "last_propose_block_num" },
- { "type": "uint16", "name": "required_producers_num" },
- { "type": "uint16", "name": "last_producers_num" }
- ]
- },{
- "name": "schedule_resize_info",
- "base": "",
- "fields": [
- { "type": "uint64", "name": "id" },
- { "type": "time_point_sec", "name": "last_step" },
- { "type": "int8", "name": "shift" }
- ]
- },{
- "name": "balance_struct",
- "base": "",
- "fields": [
- { "type": "name", "name": "account" },
- { "type": "int64", "name": "amount" }
- ]
- },{
- "name": "producer_struct",
- "base": "",
- "fields": [
- { "type": "name", "name": "account" }
- ]
- },{
- "name": "pending_producers_info",
- "base": "",
- "fields": [
- {"type": "uint64", "name": "id" },
- { "type": "name[]", "name": "accounts" }
- ]
- },{
- "name": "omission_struct",
- "base": "",
- "fields": [
- { "type": "name", "name": "account" },
- { "type": "uint16", "name": "count" }
- ]
- },{
- "name": "onblock",
- "base": "",
- "fields": [
- { "type": "name", "name": "producer" }
- ]
- },{
- "name": "setactprods",
- "base": "",
- "fields": [
- { "type": "name[]", "name": "pending_active_producers" }
- ]
- },{
- "name": "setshift",
- "base": "",
- "fields": [
- { "type": "int8", "name": "shift" }
- ]
- }
- ],
- "types": [],
- "events": [
- {
- "name":"burnreward",
- "type":"balance_struct"
- }
- ],
- "actions": [
- {
- "name": "onblock",
- "type": "onblock"
- },{
- "name": "setactprods",
- "type": "setactprods"
- },{
- "name": "setshift",
- "type": "setshift"
- }
- ],
- "tables": [
- {
- "name": "governstate",
- "type": "state_info",
- "indexes": [{
- "name": "primary",
- "unique": "true",
- "orders": [
- {"field": "id", "order": "asc"}
- ]
- }]
- },{
- "name": "schedresize",
- "type": "schedule_resize_info",
- "indexes": [
- { "name": "primary", "unique": "true", "orders": [ {"field": "id", "order": "asc"} ] }
- ]
- },{
- "name": "balance",
- "type": "balance_struct",
- "indexes": [
- { "name": "primary", "unique": "true", "orders": [ {"field": "account", "order": "asc"} ] }
- ]
- },{
- "name": "uncbalance",
- "type": "balance_struct",
- "indexes": [
- { "name": "primary", "unique": "true", "orders": [ {"field": "account", "order": "asc"} ] }
- ]
- },{
- "name": "obligedprod",
- "type": "producer_struct",
- "indexes": [
- { "name": "primary", "unique": "true", "orders": [ {"field": "account", "order": "asc"} ] }
- ]
- },{
- "name": "pendingprods",
- "type": "pending_producers_info",
- "indexes": [
- { "name": "primary", "unique": "true", "orders": [ {"field": "id", "order": "asc"} ] }
- ]
- },{
- "name": "omission",
- "type": "omission_struct",
- "indexes": [
- { "name": "primary", "unique": "true", "orders": [ {"field": "account", "order": "asc"} ] },
- { "name": "bycount", "unique": "false", "orders": [ {"field": "count", "order": "desc"} ] }
- ]
- }
- ],
- "variants": [],
- "abi_extensions": []
-}
diff --git a/cyber.govern/include/cyber.govern/config.hpp b/cyber.govern/include/cyber.govern/config.hpp
index e8d4abec5..8e5b3a97d 100644
--- a/cyber.govern/include/cyber.govern/config.hpp
+++ b/cyber.govern/include/cyber.govern/config.hpp
@@ -35,7 +35,7 @@ static constexpr auto block_reward_pct = 10 * _1percent;
static constexpr auto workers_reward_pct = 2222 * _1percent / 100; // not including block reward
static uint16_t omission_limit = 100;
-
+static uint16_t resets_limit = 4;
}
} // cyber::config
diff --git a/cyber.govern/include/cyber.govern/cyber.govern.hpp b/cyber.govern/include/cyber.govern/cyber.govern.hpp
index 39528549e..c7581dc59 100644
--- a/cyber.govern/include/cyber.govern/cyber.govern.hpp
+++ b/cyber.govern/include/cyber.govern/cyber.govern.hpp
@@ -14,7 +14,7 @@ using eosio::time_point_sec;
class [[eosio::contract("cyber.govern")]] govern : public contract {
struct structures {
- struct [[eosio::table("state")]] state_info {
+ struct state_info {
time_point_sec last_schedule_increase;
uint32_t block_num = 0;
int64_t target_emission_per_block = 0;
@@ -24,43 +24,44 @@ struct structures {
uint16_t last_producers_num = 1;
};
- struct [[eosio::table]] schedule_resize_info {
+ struct schedule_resize_info {
time_point_sec last_step;
int8_t shift = 1;
};
- struct [[eosio::table]] balance {
+ struct [[using eosio: event("burnreward"), contract("cyber.govern")]] balance_struct {
name account;
int64_t amount;
uint64_t primary_key()const { return account.value; }
};
- struct [[eosio::table]] producer {
+ struct producer_struct {
name account;
uint64_t primary_key()const { return account.value; }
};
- struct [[eosio::table]] pending_producers_state {
+ struct pending_producers_info {
std::vector accounts;
};
- struct [[eosio::table]] omission {
+ struct omission_struct {
name account;
uint16_t count;
+ uint16_t resets = 0;
uint64_t primary_key()const { return account.value; }
uint16_t by_count()const { return count; }
};
};
- using state_singleton = eosio::singleton<"governstate"_n, structures::state_info>;
- using schedule_resize_singleton = eosio::singleton<"schedresize"_n, structures::schedule_resize_info>;
- using balances = eosio::multi_index<"balance"_n, structures::balance>;
- using unconfirmed_balances = eosio::multi_index<"uncbalance"_n, structures::balance>;
- using obliged_producers = eosio::multi_index<"obligedprod"_n, structures::producer>;
- using pending_producers = eosio::singleton<"pendingprods"_n, structures::pending_producers_state>;
+
+ using state_singleton [[eosio::order("id","asc")]] = eosio::singleton<"governstate"_n, structures::state_info>;
+ using schedule_resize_singleton [[eosio::order("id","asc")]] = eosio::singleton<"schedresize"_n, structures::schedule_resize_info>;
+ using balances [[eosio::order("account","asc")]] = eosio::multi_index<"balance"_n, structures::balance_struct>;
+ using unconfirmed_balances [[eosio::order("account","asc")]] = eosio::multi_index<"uncbalance"_n, structures::balance_struct>;
+ using obliged_producers [[eosio::order("account","asc")]] = eosio::multi_index<"obligedprod"_n, structures::producer_struct>;
+ using pending_producers [[eosio::order("id","asc")]] = eosio::singleton<"pendingprods"_n, structures::pending_producers_info>;
- using omission_id_index = eosio::indexed_by<"omissionid"_n, eosio::const_mem_fun >;
- using omission_count_index = eosio::indexed_by<"bycount"_n, eosio::const_mem_fun >;
- using omissions = eosio::multi_index<"omission"_n, structures::omission, omission_id_index, omission_count_index>;
+ using omission_count_index [[using eosio: order("count","desc"), non_unique]] = eosio::indexed_by<"bycount"_n, eosio::const_mem_fun >;
+ using omissions [[eosio::order("account","asc")]] = eosio::multi_index<"omission"_n, structures::omission_struct, omission_count_index>;
void maybe_promote_producers();
void propose_producers(structures::state_info& s);
diff --git a/cyber.govern/src/cyber.govern.cpp b/cyber.govern/src/cyber.govern.cpp
index 2a2a55f2a..694684c90 100644
--- a/cyber.govern/src/cyber.govern.cpp
+++ b/cyber.govern/src/cyber.govern.cpp
@@ -57,7 +57,7 @@ void govern::onblock(name producer) {
balances_table.modify(b, name(), [&](auto& b) { b.amount += block_reward + just_confirmed_balance; } );
}
else {
- balances_table.emplace(_self, [&](auto& b) { b = structures::balance {
+ balances_table.emplace(_self, [&](auto& b) { b = structures::balance_struct {
.account = producer,
.amount = block_reward + just_confirmed_balance
};});
@@ -99,14 +99,14 @@ void govern::reward_producers(balances& balances_table, structures::state_info&
{std::vector >(rewards.begin(), rewards.end()), system_token});
}
- auto top = stake::get_top(system_token.code(), s.required_producers_num + rewarded_for_votes_limit_displ, 0, false);
+ auto top = stake::get_top(system_token.code(), s.required_producers_num + rewarded_for_votes_limit_displ, 0);
auto actual_elected_num = top.size();
int64_t votes_sum = 0;
for (const auto& t : top) {
votes_sum += t.votes;
}
-
+
auto reward_of_elected = safe_pct(s.funds, config::_100percent - config::workers_reward_pct);
if (!votes_sum || !reward_of_elected) {
@@ -134,7 +134,7 @@ void govern::reward_producers(balances& balances_table, structures::state_info&
unconfirmed_balances_table.modify(b, name(), [&](auto& b) { b.amount += r.second; } );
}
else {
- unconfirmed_balances_table.emplace(_self, [&](auto& b) { b = structures::balance {
+ unconfirmed_balances_table.emplace(_self, [&](auto& b) { b = structures::balance_struct {
.account = r.first,
.amount = r.second
};});
@@ -201,7 +201,7 @@ int64_t govern::get_target_emission_per_block(int64_t supply) const {
void govern::setactprods(std::vector pending_active_producers) {
require_auth(_self);
pending_producers pending_prods_table(_self, _self.value);
- auto prods = pending_prods_table.get_or_default(structures::pending_producers_state{});
+ auto prods = pending_prods_table.get_or_default(structures::pending_producers_info{});
if (!prods.accounts.empty()) {
eosio::print("WARNING! govern::setactprods, pending_prods_table was not empty\n");
}
@@ -221,7 +221,7 @@ void govern::setshift(int8_t shift) {
void govern::maybe_promote_producers() {
pending_producers pending_prods_table(_self, _self.value);
- auto prods = pending_prods_table.get_or_default(structures::pending_producers_state{});
+ auto prods = pending_prods_table.get_or_default(structures::pending_producers_info{});
if (prods.accounts.empty()) {
return;
}
@@ -235,7 +235,7 @@ void govern::maybe_promote_producers() {
omissions_table.modify(o, name(), [&](auto& o) { o.count += 1; } );
}
else {
- omissions_table.emplace(_self, [&](auto& o) { o = structures::omission {
+ omissions_table.emplace(_self, [&](auto& o) { o = structures::omission_struct {
.account = i->account,
.count = 1
};});
@@ -251,22 +251,30 @@ void govern::maybe_promote_producers() {
symbol_code token_code = system_token.code();
auto omissions_idx = omissions_table.get_index<"bycount"_n>();
- auto omission_itr = omissions_idx.lower_bound(std::numeric_limits::max());
+ auto omission_itr = omissions_idx.lower_bound(std::numeric_limits::max());
if (omission_itr != omissions_idx.end() && omission_itr->count >= config::omission_limit) {
- if (cyber::stake::candidate_exists(omission_itr->account, token_code)) {
- INLINE_ACTION_SENDER(cyber::stake, setkey)(config::stake_name, {config::stake_name, config::active_name},
- {omission_itr->account, token_code, public_key{}});
+ if (omission_itr->resets >= config::resets_limit) {
+ INLINE_ACTION_SENDER(cyber::stake, setproxylvl)(config::stake_name, {config::issuer_name, config::active_name},
+ {omission_itr->account, token_code, stake::get_max_level(token_code)}); // agent cannot disappear
+ omissions_idx.erase(omission_itr);
+ }
+ else {
+ if (cyber::stake::candidate_exists(omission_itr->account, token_code)) {
+ INLINE_ACTION_SENDER(cyber::stake, setkey)(config::stake_name, {config::issuer_name, config::active_name},
+ {omission_itr->account, token_code, public_key{}});
+ }
+ omissions_idx.modify(omission_itr, name(), [&](auto& o) {
+ o.count = 0;
+ o.resets += 1;
+ });
}
- omissions_idx.erase(omission_itr);
}
for (const auto& acc : prods.accounts) {
- obliged_prods_table.emplace(_self, [&](auto& p) { p = structures::producer { .account = acc }; });
+ obliged_prods_table.emplace(_self, [&](auto& p) { p = structures::producer_struct { .account = acc }; });
}
prods.accounts.clear();
pending_prods_table.set(prods, _self);
}
}
-
-EOSIO_DISPATCH( cyber::govern, (onblock)(setactprods)(setshift))
diff --git a/cyber.incomereject/CMakeLists.txt b/cyber.incomereject/CMakeLists.txt
index d74a28e4d..e0fe428e2 100644
--- a/cyber.incomereject/CMakeLists.txt
+++ b/cyber.incomereject/CMakeLists.txt
@@ -1,4 +1,6 @@
-add_contract_with_abi(cyber.incomereject abi/cyber.incomereject.abi ${CMAKE_CURRENT_SOURCE_DIR}/src/cyber.incomereject.cpp)
+add_contract(cyber.incomereject cyber.incomereject ${CMAKE_CURRENT_SOURCE_DIR}/src/cyber.incomereject.cpp)
+install_contract(cyber.incomereject)
+
target_include_directories(cyber.incomereject.wasm
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
diff --git a/cyber.incomereject/abi/cyber.incomereject.abi b/cyber.incomereject/abi/cyber.incomereject.abi
deleted file mode 100644
index 004eb7c96..000000000
--- a/cyber.incomereject/abi/cyber.incomereject.abi
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "version": "cyberway::abi/1.1",
- "structs": [],
- "types": [],
- "actions": [],
- "events": [],
- "tables": [],
- "variants": [],
- "abi_extensions": []
-}
diff --git a/cyber.incomereject/include/cyber.incomereject/cyber.incomereject.hpp b/cyber.incomereject/include/cyber.incomereject/cyber.incomereject.hpp
index fb39f0b47..59c773de3 100644
--- a/cyber.incomereject/include/cyber.incomereject/cyber.incomereject.hpp
+++ b/cyber.incomereject/include/cyber.incomereject/cyber.incomereject.hpp
@@ -6,6 +6,8 @@
#include
#include
+#include
+#include
namespace eosio {
@@ -13,7 +15,7 @@ namespace eosio {
public:
using contract::contract;
- void on_transfer(name from, name to, asset quantity, std::string memo);
+ ON_TRANSFER(CYBER_TOKEN, on_transfer) void on_transfer(name from, name to, asset quantity, std::string memo);
};
} /// namespace eosio
diff --git a/cyber.incomereject/src/cyber.incomereject.cpp b/cyber.incomereject/src/cyber.incomereject.cpp
index 551e18b9f..d4b03558b 100644
--- a/cyber.incomereject/src/cyber.incomereject.cpp
+++ b/cyber.incomereject/src/cyber.incomereject.cpp
@@ -4,8 +4,6 @@
*/
#include
-#include
-#include
namespace eosio {
@@ -14,16 +12,3 @@ void incomereject::on_transfer(name from, name to, asset quantity, std::string m
}
} /// namespace eosio
-
-extern "C" {
- [[eosio::wasm_entry]]
- void apply( uint64_t receiver, uint64_t code, uint64_t action ) {
- if (code == "cyber.token"_n.value) {
- if (action == "transfer"_n.value) {
- eosio::execute_action(eosio::name(receiver), eosio::name(code), &eosio::incomereject::on_transfer);
- } else if (action == "bulktransfer"_n.value) {
- dispatch_with_transfer_helper(eosio::name(receiver), eosio::name(code), &eosio::incomereject::on_transfer);
- }
- }
- }
-}
diff --git a/cyber.msig/CMakeLists.txt b/cyber.msig/CMakeLists.txt
index 8f8280b4b..6fcbb2a3e 100644
--- a/cyber.msig/CMakeLists.txt
+++ b/cyber.msig/CMakeLists.txt
@@ -1,4 +1,6 @@
-add_contract_with_abi(cyber.msig abi/cyber.msig.abi ${CMAKE_CURRENT_SOURCE_DIR}/src/cyber.msig.cpp)
+add_contract(cyber.msig cyber.msig ${CMAKE_CURRENT_SOURCE_DIR}/src/cyber.msig.cpp)
+install_contract(cyber.msig)
+
target_include_directories(cyber.msig.wasm
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include)
diff --git a/cyber.msig/abi/cyber.msig.abi b/cyber.msig/abi/cyber.msig.abi
deleted file mode 100644
index 455f813b3..000000000
--- a/cyber.msig/abi/cyber.msig.abi
+++ /dev/null
@@ -1,384 +0,0 @@
-{
- "version": "cyberway::abi/1.1",
- "structs": [
- {
- "name": "action",
- "base": "",
- "fields": [
- {
- "name": "account",
- "type": "name"
- },
- {
- "name": "name",
- "type": "name"
- },
- {
- "name": "authorization",
- "type": "permission_level[]"
- },
- {
- "name": "data",
- "type": "bytes"
- }
- ]
- },
- {
- "name": "approval",
- "base": "",
- "fields": [
- {
- "name": "level",
- "type": "permission_level"
- },
- {
- "name": "time",
- "type": "time_point"
- }
- ]
- },
- {
- "name": "approvals_info",
- "base": "",
- "fields": [
- {
- "name": "version",
- "type": "uint8"
- },
- {
- "name": "proposal_name",
- "type": "name"
- },
- {
- "name": "requested_approvals",
- "type": "approval[]"
- },
- {
- "name": "provided_approvals",
- "type": "approval[]"
- }
- ]
- },
- {
- "name": "approve",
- "base": "",
- "fields": [
- {
- "name": "proposer",
- "type": "name"
- },
- {
- "name": "proposal_name",
- "type": "name"
- },
- {
- "name": "level",
- "type": "permission_level"
- },
- {
- "name": "proposal_hash",
- "type": "checksum256$"
- }
- ]
- },
- {
- "name": "cancel",
- "base": "",
- "fields": [
- {
- "name": "proposer",
- "type": "name"
- },
- {
- "name": "proposal_name",
- "type": "name"
- },
- {
- "name": "canceler",
- "type": "name"
- }
- ]
- },
- {
- "name": "exec",
- "base": "",
- "fields": [
- {
- "name": "proposer",
- "type": "name"
- },
- {
- "name": "proposal_name",
- "type": "name"
- },
- {
- "name": "executer",
- "type": "name"
- }
- ]
- },
- {
- "name": "extension",
- "base": "",
- "fields": [
- {
- "name": "type",
- "type": "uint16"
- },
- {
- "name": "data",
- "type": "bytes"
- }
- ]
- },
- {
- "name": "invalidate",
- "base": "",
- "fields": [
- {
- "name": "account",
- "type": "name"
- }
- ]
- },
- {
- "name": "invalidation",
- "base": "",
- "fields": [
- {
- "name": "account",
- "type": "name"
- },
- {
- "name": "last_invalidation_time",
- "type": "time_point"
- }
- ]
- },
- {
- "name": "old_approvals_info",
- "base": "",
- "fields": [
- {
- "name": "proposal_name",
- "type": "name"
- },
- {
- "name": "requested_approvals",
- "type": "permission_level[]"
- },
- {
- "name": "provided_approvals",
- "type": "permission_level[]"
- }
- ]
- },
- {
- "name": "permission_level",
- "base": "",
- "fields": [
- {
- "name": "actor",
- "type": "name"
- },
- {
- "name": "permission",
- "type": "name"
- }
- ]
- },
- {
- "name": "proposal",
- "base": "",
- "fields": [
- {
- "name": "proposal_name",
- "type": "name"
- },
- {
- "name": "packed_transaction",
- "type": "bytes"
- }
- ]
- },
- {
- "name": "propose",
- "base": "",
- "fields": [
- {
- "name": "proposer",
- "type": "name"
- },
- {
- "name": "proposal_name",
- "type": "name"
- },
- {
- "name": "requested",
- "type": "permission_level[]"
- },
- {
- "name": "trx",
- "type": "transaction"
- }
- ]
- },
- {
- "name": "transaction",
- "base": "transaction_header",
- "fields": [
- {
- "name": "context_free_actions",
- "type": "action[]"
- },
- {
- "name": "actions",
- "type": "action[]"
- },
- {
- "name": "transaction_extensions",
- "type": "extension[]"
- }
- ]
- },
- {
- "name": "transaction_header",
- "base": "",
- "fields": [
- {
- "name": "expiration",
- "type": "time_point_sec"
- },
- {
- "name": "ref_block_num",
- "type": "uint16"
- },
- {
- "name": "ref_block_prefix",
- "type": "uint32"
- },
- {
- "name": "max_net_usage_words",
- "type": "varuint32"
- },
- {
- "name": "max_cpu_usage_ms",
- "type": "uint8"
- },
- {
- "name": "max_ram_kbytes",
- "type": "varuint32"
- },
- {
- "name": "max_storage_kbytes",
- "type": "varuint32"
- },
- {
- "name": "delay_sec",
- "type": "varuint32"
- }
- ]
- },
- {
- "name": "unapprove",
- "base": "",
- "fields": [
- {
- "name": "proposer",
- "type": "name"
- },
- {
- "name": "proposal_name",
- "type": "name"
- },
- {
- "name": "level",
- "type": "permission_level"
- }
- ]
- }
- ],
- "types": [],
- "actions": [
- {
- "name": "approve",
- "type": "approve"
- },
- {
- "name": "cancel",
- "type": "cancel"
- },
- {
- "name": "exec",
- "type": "exec"
- },
- {
- "name": "invalidate",
- "type": "invalidate"
- },
- {
- "name": "propose",
- "type": "propose"
- },
- {
- "name": "unapprove",
- "type": "unapprove"
- }
- ],
- "tables": [
- {
- "name": "approvals",
- "type": "old_approvals_info",
- "indexes": [
- {
- "name": "primary",
- "unique": "true",
- "orders": [
- {"field": "proposal_name", "order": "asc"}
- ]
- }
- ]
- },
- {
- "name": "approvals2",
- "type": "approvals_info",
- "indexes": [
- {
- "name": "primary",
- "unique": "true",
- "orders": [
- {"field": "proposal_name", "order": "asc"}
- ]
- }
- ]
- },
- {
- "name": "invals",
- "type": "invalidation",
- "indexes": [
- {
- "name": "primary",
- "unique": "true",
- "orders": [
- {"field": "account", "order": "asc"}
- ]
- }
- ]
- },
- {
- "name": "proposal",
- "type": "proposal",
- "indexes": [
- {
- "name": "primary",
- "unique": "true",
- "orders": [
- {"field": "proposal_name", "order": "asc"}
- ]
- }
- ]
- }
- ],
- "variants": [],
- "abi_extensions": []
-}
diff --git a/cyber.msig/include/cyber.msig/cyber.msig.hpp b/cyber.msig/include/cyber.msig/cyber.msig.hpp
index 5f2cd5468..f1cb8514c 100644
--- a/cyber.msig/include/cyber.msig/cyber.msig.hpp
+++ b/cyber.msig/include/cyber.msig/cyber.msig.hpp
@@ -12,7 +12,8 @@ namespace eosio {
[[eosio::action]]
void propose(ignore proposer, ignore proposal_name,
- ignore> requested, ignore trx);
+ ignore> requested, ignore trx,
+ ignore> description);
[[eosio::action]]
void approve( name proposer, name proposal_name, permission_level level,
const eosio::binary_extension& proposal_hash );
@@ -21,35 +22,29 @@ namespace eosio {
[[eosio::action]]
void cancel( name proposer, name proposal_name, name canceler );
[[eosio::action]]
+ void schedule( name proposer, name proposal_name, name actor );
+ [[eosio::action]]
void exec( name proposer, name proposal_name, name executer );
[[eosio::action]]
void invalidate( name account );
private:
- struct [[eosio::table]] proposal {
+ struct proposal {
name proposal_name;
std::vector packed_transaction;
uint64_t primary_key()const { return proposal_name.value; }
};
- typedef eosio::multi_index< "proposal"_n, proposal > proposals;
-
- struct [[eosio::table]] old_approvals_info {
- name proposal_name;
- std::vector requested_approvals;
- std::vector provided_approvals;
-
- uint64_t primary_key()const { return proposal_name.value; }
- };
- typedef eosio::multi_index< "approvals"_n, old_approvals_info > old_approvals;
+ using proposals [[eosio::order("proposal_name")]] =
+ eosio::multi_index<"proposal"_n, proposal>;
struct approval {
permission_level level;
time_point time;
};
- struct [[eosio::table]] approvals_info {
+ struct approvals_info {
uint8_t version = 1;
name proposal_name;
//requested approval doesn't need to cointain time, but we want requested approval
@@ -60,16 +55,30 @@ namespace eosio {
uint64_t primary_key()const { return proposal_name.value; }
};
- typedef eosio::multi_index< "approvals2"_n, approvals_info > approvals;
- struct [[eosio::table]] invalidation {
+ using approvals [[eosio::order("proposal_name")]] =
+ eosio::multi_index<"approvals2"_n, approvals_info> ;
+
+ struct invalidation {
name account;
time_point last_invalidation_time;
uint64_t primary_key() const { return account.value; }
};
- typedef eosio::multi_index< "invals"_n, invalidation > invalidations;
+ using invalidations [[eosio::order("account")]] =
+ eosio::multi_index<"invals"_n, invalidation>;
+
+ struct proposal_wait {
+ name proposal_name;
+ time_point_sec started;
+
+ uint64_t primary_key()const { return proposal_name.value; }
+ };
+
+ using waits [[eosio::order("proposal_name")]] = eosio::multi_index<"waits"_n, proposal_wait> ;
+
+ void check_proposal_authorization(const proposal &prop, const approvals_info &apps);
};
} /// namespace eosio
diff --git a/cyber.msig/src/cyber.msig.cpp b/cyber.msig/src/cyber.msig.cpp
index dbdd6f82f..37fd4f2f6 100644
--- a/cyber.msig/src/cyber.msig.cpp
+++ b/cyber.msig/src/cyber.msig.cpp
@@ -8,7 +8,8 @@ namespace eosio {
void multisig::propose( ignore proposer,
ignore proposal_name,
ignore> requested,
- ignore trx )
+ ignore trx,
+ ignore> description )
{
name _proposer;
name _proposal_name;
@@ -75,16 +76,7 @@ void multisig::approve( name proposer, name proposal_name, permission_level leve
a.requested_approvals.erase( itr );
});
} else {
- old_approvals old_apptable( _self, proposer.value );
- auto& apps = old_apptable.get( proposal_name.value, "proposal not found" );
-
- auto itr = std::find( apps.requested_approvals.begin(), apps.requested_approvals.end(), level );
- eosio::check( itr != apps.requested_approvals.end(), "approval is not on the list of requested approvals" );
-
- old_apptable.modify( apps, proposer, [&]( auto& a ) {
- a.provided_approvals.push_back( level );
- a.requested_approvals.erase( itr );
- });
+ eosio::check(false, "proposal not found");
}
}
@@ -101,14 +93,7 @@ void multisig::unapprove( name proposer, name proposal_name, permission_level le
a.provided_approvals.erase( itr );
});
} else {
- old_approvals old_apptable( _self, proposer.value );
- auto& apps = old_apptable.get( proposal_name.value, "proposal not found" );
- auto itr = std::find( apps.provided_approvals.begin(), apps.provided_approvals.end(), level );
- eosio::check( itr != apps.provided_approvals.end(), "no approval previously granted" );
- old_apptable.modify( apps, proposer, [&]( auto& a ) {
- a.requested_approvals.push_back( level );
- a.provided_approvals.erase( itr );
- });
+ eosio::check(false, "proposal not found");
}
}
@@ -123,64 +108,85 @@ void multisig::cancel( name proposer, name proposal_name, name canceler ) {
}
proptable.erase(prop);
- //remove from new table
approvals apptable( _self, proposer.value );
auto apps_it = apptable.find( proposal_name.value );
- if ( apps_it != apptable.end() ) {
- apptable.erase(apps_it);
- } else {
- old_approvals old_apptable( _self, proposer.value );
- auto apps_it = old_apptable.find( proposal_name.value );
- eosio::check( apps_it != old_apptable.end(), "proposal not found" );
- old_apptable.erase(apps_it);
+ eosio::check(apps_it != apptable.end(), "proposal not found");
+ apptable.erase(apps_it);
+
+ waits waittable(_self, proposer.value);
+ auto itr = waittable.find(proposal_name.value);
+ if (itr != waittable.end()) {
+ waittable.erase(itr);
}
}
-void multisig::exec( name proposer, name proposal_name, name executer ) {
- require_auth( executer );
-
- proposals proptable( _self, proposer.value );
- auto& prop = proptable.get( proposal_name.value, "proposal not found" );
- transaction_header trx_header;
- datastream ds( prop.packed_transaction.data(), prop.packed_transaction.size() );
- ds >> trx_header;
- eosio::check( trx_header.expiration >= current_time_point(), "transaction expired" );
-
- approvals apptable( _self, proposer.value );
- auto apps_it = apptable.find( proposal_name.value );
+void multisig::check_proposal_authorization(const proposal &prop, const approvals_info &apps) {
std::vector approvals;
invalidations inv_table( _self, _self.value );
- if ( apps_it != apptable.end() ) {
- approvals.reserve( apps_it->provided_approvals.size() );
- for ( auto& p : apps_it->provided_approvals ) {
- auto it = inv_table.find( p.level.actor.value );
- if ( it == inv_table.end() || it->last_invalidation_time < p.time ) {
- approvals.push_back(p.level);
- }
+ approvals.reserve( apps.provided_approvals.size() );
+ for ( auto& p : apps.provided_approvals ) {
+ auto it = inv_table.find( p.level.actor.value );
+ if ( it == inv_table.end() || it->last_invalidation_time < p.time ) {
+ approvals.push_back(p.level);
}
- apptable.erase(apps_it);
- } else {
- old_approvals old_apptable( _self, proposer.value );
- auto& apps = old_apptable.get( proposal_name.value, "proposal not found" );
- for ( auto& level : apps.provided_approvals ) {
- auto it = inv_table.find( level.actor.value );
- if ( it == inv_table.end() ) {
- approvals.push_back( level );
- }
- }
- old_apptable.erase(apps);
}
+
auto packed_provided_approvals = pack(approvals);
- auto res = eosio::check_transaction_authorization( prop.packed_transaction.data(), prop.packed_transaction.size(),
+ auto res = eosio::check_transaction_authorization(prop.packed_transaction.data(), prop.packed_transaction.size(),
(const char*)0, 0,
packed_provided_approvals.data(), packed_provided_approvals.size()
);
eosio::check( res > 0, "transaction authorization failed" );
+}
+
+void multisig::schedule(name proposer, name proposal_name, name actor) {
+ require_auth(actor);
+
+ proposals proptable( _self, proposer.value );
+ approvals apptable( _self, proposer.value );
+ waits waittable(_self, proposer.value);
+
+ auto& prop = proptable.get( proposal_name.value, "proposal not found" );
+ auto& apps = apptable.get( proposal_name.value, "approvals not found" );
+ auto wait = waittable.find(proposal_name.value);
+ eosio::check(wait == waittable.end(), "proposal already scheduled");
- eosio::send_deferred( (uint128_t(proposer.value) << 64) | proposal_name.value, executer,
- prop.packed_transaction.data(), prop.packed_transaction.size() );
+ auto trx_header = unpack(prop.packed_transaction);
+ eosio::check( trx_header.expiration >= current_time_point(), "transacton expired" );
+ eosio::check( trx_header.delay_sec != 0u, "can't schedule transaction with zero delay_sec, call exec");
+
+ check_proposal_authorization(prop, apps);
+
+ waittable.emplace(actor, [&](auto& w) {
+ w.proposal_name = proposal_name;
+ w.started = current_time_point();
+ });
+}
+
+void multisig::exec( name proposer, name proposal_name, name executer ) {
+ require_auth( executer );
+
+ proposals proptable( _self, proposer.value );
+ approvals apptable( _self, proposer.value );
+
+ auto& prop = proptable.get( proposal_name.value, "proposal not found" );
+ auto& apps = apptable.get( proposal_name.value, "approvals not found" );
+
+ auto trx_header = unpack(prop.packed_transaction);
+ eosio::check( trx_header.expiration >= current_time_point(), "transacton expired" );
+ if ( trx_header.delay_sec ) {
+ waits waittable(_self, proposer.value);
+ auto wait = waittable.get( proposal_name.value, "proposal was not scheduled" );
+ uint32_t waited = current_time_point().sec_since_epoch() - wait.started.sec_since_epoch();
+ eosio::check( (uint32_t)trx_header.delay_sec <= waited, "too early" );
+ waittable.erase(wait);
+ }
+
+ check_proposal_authorization(prop, apps);
+ eosio::send_nested(prop.packed_transaction.data(), prop.packed_transaction.size());
proptable.erase(prop);
+ apptable.erase(apps);
}
void multisig::invalidate( name account ) {
@@ -199,6 +205,5 @@ void multisig::invalidate( name account ) {
}
}
-} /// namespace eosio
-EOSIO_DISPATCH( eosio::multisig, (propose)(approve)(unapprove)(cancel)(exec)(invalidate) )
+} /// namespace eosio
diff --git a/cyber.rejector/CMakeLists.txt b/cyber.rejector/CMakeLists.txt
index d415cb606..ee12491f1 100644
--- a/cyber.rejector/CMakeLists.txt
+++ b/cyber.rejector/CMakeLists.txt
@@ -1,4 +1,6 @@
-add_contract_with_abi(cyber.rejector abi/cyber.rejector.abi ${CMAKE_CURRENT_SOURCE_DIR}/src/cyber.rejector.cpp)
+add_contract(cyber.rejector cyber.rejector ${CMAKE_CURRENT_SOURCE_DIR}/src/cyber.rejector.cpp)
+install_contract(cyber.rejector)
+
target_include_directories(cyber.rejector.wasm
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include)
diff --git a/cyber.rejector/abi/cyber.rejector.abi b/cyber.rejector/abi/cyber.rejector.abi
deleted file mode 100644
index 004eb7c96..000000000
--- a/cyber.rejector/abi/cyber.rejector.abi
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "version": "cyberway::abi/1.1",
- "structs": [],
- "types": [],
- "actions": [],
- "events": [],
- "tables": [],
- "variants": [],
- "abi_extensions": []
-}
diff --git a/cyber.stake/CMakeLists.txt b/cyber.stake/CMakeLists.txt
index 3d640ae32..ef43bb8ab 100644
--- a/cyber.stake/CMakeLists.txt
+++ b/cyber.stake/CMakeLists.txt
@@ -1,4 +1,6 @@
-add_contract_with_abi(cyber.stake abi/cyber.stake.abi ${CMAKE_CURRENT_SOURCE_DIR}/src/cyber.stake.cpp)
+add_contract(cyber.stake cyber.stake ${CMAKE_CURRENT_SOURCE_DIR}/src/cyber.stake.cpp)
+install_contract(cyber.stake)
+
target_include_directories(cyber.stake.wasm
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
diff --git a/cyber.stake/abi/cyber.stake.abi b/cyber.stake/abi/cyber.stake.abi
deleted file mode 100644
index 18e46e20b..000000000
--- a/cyber.stake/abi/cyber.stake.abi
+++ /dev/null
@@ -1,244 +0,0 @@
-{
- "version": "cyberway::abi/1.1",
- "structs": [
- {
- "name": "provision_struct",
- "base": "",
- "fields": [
- { "type": "uint64", "name": "id" },
- { "type": "symbol_code", "name": "token_code" },
- { "type": "name", "name": "grantor_name" },
- { "type": "name", "name": "recipient_name" },
- { "type": "int64", "name": "amount" }
- ]
- },{
- "name": "prov_payout_struct",
- "base": "",
- "fields": [
- { "type": "uint64", "name": "id" },
- { "type": "symbol_code", "name": "token_code" },
- { "type": "name", "name": "grantor_name" },
- { "type": "name", "name": "recipient_name" },
- { "type": "int64", "name": "amount" },
- {"name":"date", "type":"time_point_sec"}
- ]
- },{
- "name": "create_args",
- "base": "",
- "fields": [
- { "type": "symbol", "name": "token_symbol" },
- { "type": "uint8[]", "name": "max_proxies" },
- { "type": "int64", "name": "depriving_window" },
- { "type": "int64", "name": "min_own_staked_for_election" }
- ]
- },{
- "name": "open_args",
- "base": "",
- "fields": [
- { "type": "name", "name": "owner" },
- { "type": "symbol_code", "name": "token_code" },
- { "type": "name?", "name": "ram_payer" }
- ]
- },{
- "name": "enable_args",
- "base": "",
- "fields": [
- { "type": "symbol_code", "name": "token_code" }
- ]
- },{
- "name": "delegate_args",
- "base": "",
- "fields": [
- { "type": "name", "name": "grantor_name" },
- { "type": "name", "name": "recipient_name" },
- { "type": "asset", "name": "quantity" }
- ]
- },{
- "name": "recallvote_args",
- "base": "",
- "fields": [
- { "type": "name", "name": "grantor_name" },
- { "type": "name", "name": "recipient_name" },
- { "type": "symbol_code", "name": "token_code" },
- { "type": "int16", "name": "pct" }
- ]
- },{
- "name": "setterms_args",
- "base": "recallvote_args",
- "fields": [
- { "type": "int16", "name": "break_fee" },
- { "type": "int64", "name": "break_min_own_staked" }
- ]
- },{
- "name": "withdraw_args",
- "base": "",
- "fields": [
- { "type": "name", "name": "account" },
- { "type": "asset", "name": "quantity" }
- ]
- },{
- "name": "acc_code_args",
- "base": "",
- "fields": [
- { "type": "name", "name": "account" },
- { "type": "symbol_code", "name": "token_code" }
- ]
- },{
- "name": "setproxylvl_args",
- "base": "acc_code_args",
- "fields": [
- { "type": "uint8", "name": "level" }
- ]
- },{
- "name": "setproxyfee_args",
- "base": "acc_code_args",
- "fields": [
- { "type": "int16", "name": "fee" }
- ]
- },{
- "name": "setminstaked_args",
- "base": "acc_code_args",
- "fields": [
- { "type": "int64", "name": "min_own_staked" }
- ]
- },{
- "name": "updatefunds_args",
- "base": "",
- "fields": [
- { "type": "name", "name": "account" },
- { "type": "symbol_code", "name": "token_code" }
- ]
- },{
- "name": "setkey_args",
- "base": "",
- "fields": [
- { "type": "name", "name": "account" },
- { "type": "symbol_code", "name": "token_code" },
- { "type": "public_key?", "name": "signing_key" }
- ]
- },{
- "name": "name_int_pair",
- "base": "",
- "fields": [
- { "type": "name", "name": "first" },
- { "type": "int64", "name": "second" }
- ]
- },{
- "name": "reward_args",
- "base": "",
- "fields": [
- { "type": "name_int_pair[]","name": "rewards" },
- { "type": "symbol", "name": "sym" }
- ]
- },{
- "name": "pick_args",
- "base": "",
- "fields": [
- { "type": "symbol_code", "name": "token_code" },
- { "type": "name[]", "name": "accounts" }
- ]
- },{
- "name": "claim_args",
- "base": "",
- "fields": [
- { "type": "name", "name": "grantor_name" },
- { "type": "name", "name": "recipient_name" },
- { "type": "symbol_code", "name": "token_code" }
- ]
- }
- ],
- "types": [],
- "actions": [
- {
- "name": "create",
- "type": "create_args",
- "ricardian_contract": ""
- },{
- "name": "open",
- "type": "open_args",
- "ricardian_contract": ""
- },{
- "name": "enable",
- "type": "enable_args",
- "ricardian_contract": ""
- },{
- "name": "delegatevote",
- "type": "delegate_args",
- "ricardian_contract": ""
- },{
- "name": "setgrntterms",
- "type": "setterms_args",
- "ricardian_contract": ""
- },{
- "name": "recallvote",
- "type": "recallvote_args",
- "ricardian_contract": ""
- },{
- "name": "withdraw",
- "type": "withdraw_args",
- "ricardian_contract": ""
- },{
- "name": "setproxylvl",
- "type": "setproxylvl_args",
- "ricardian_contract": ""
- },{
- "name": "setproxyfee",
- "type": "setproxyfee_args",
- "ricardian_contract": ""
- },{
- "name": "setminstaked",
- "type": "setminstaked_args",
- "ricardian_contract": ""
- },{
- "name": "setkey",
- "type": "setkey_args",
- "ricardian_contract": ""
- },{
- "name": "updatefunds",
- "type": "updatefunds_args",
- "ricardian_contract": ""
- },{
- "name": "reward",
- "type": "reward_args",
- "ricardian_contract": ""
- },{
- "name": "pick",
- "type": "pick_args",
- "ricardian_contract": ""
- },{
- "name": "delegateuse",
- "type": "delegate_args",
- "ricardian_contract": ""
- },{
- "name": "recalluse",
- "type": "delegate_args",
- "ricardian_contract": ""
- },{
- "name": "claim",
- "type": "claim_args",
- "ricardian_contract": ""
- }
- ],
- "tables": [
- {
- "name": "provision",
- "type": "provision_struct",
- "indexes": [
- { "name": "primary", "unique": "true", "orders": [ {"field": "id", "order": "asc"} ] },
- { "name": "bykey", "unique": "true", "orders": [
- {"field": "token_code", "order": "asc"}, {"field": "grantor_name", "order": "asc"}, {"field": "recipient_name", "order": "asc"} ] }
- ]
- },{
- "name": "provpayout",
- "type": "prov_payout_struct",
- "indexes": [
- { "name": "primary", "unique": "true", "orders": [ {"field": "id", "order": "asc"} ] },
- { "name": "bykey", "unique": "true", "orders": [
- {"field": "token_code", "order": "asc"}, {"field": "grantor_name", "order": "asc"}, {"field": "recipient_name", "order": "asc"} ] }
- ]
- }
- ],
- "ricardian_clauses": [],
- "variants": [],
- "abi_extensions": []
-}
diff --git a/cyber.stake/include/cyber.stake/config.hpp b/cyber.stake/include/cyber.stake/config.hpp
index 9b434d9c1..cadf7b319 100644
--- a/cyber.stake/include/cyber.stake/config.hpp
+++ b/cyber.stake/include/cyber.stake/config.hpp
@@ -4,6 +4,8 @@
namespace cyber { namespace config {
static const int32_t priority_precision = 1000000;
static const auto key_recovery_delay = 60 * 60 * 24;
+static const auto proxylvl_recovery_delay = 60 * 60 * 24 * 7;
+static const auto max_no_pick_period = 60 * 60 * 24 * 30;
static const auto reward_memo = "$reward";
}
diff --git a/cyber.stake/include/cyber.stake/cyber.stake.hpp b/cyber.stake/include/cyber.stake/cyber.stake.hpp
index 55945f078..50b0ecb77 100644
--- a/cyber.stake/include/cyber.stake/cyber.stake.hpp
+++ b/cyber.stake/include/cyber.stake/cyber.stake.hpp
@@ -7,11 +7,14 @@
#include
#include
#include
+#include
#include "config.hpp"
#include
#include
#include
#include
+#include
+#include
#define table_owner name()
@@ -28,11 +31,11 @@ using std::string;
class [[eosio::contract("cyber.stake")]] stake : public eosio::contract {
struct structures {
- struct [[eosio::table]] candidate {
+ struct candidate {
uint64_t id;
symbol_code token_code;
name account;
- time_point_sec latest_pick; //note that the field is also used to delay key recovery
+ time_point_sec latest_pick;
int64_t votes = 0;
int64_t priority = std::numeric_limits::max();
public_key signing_key = {};
@@ -53,7 +56,7 @@ struct structures {
void update_priority(int64_t cur_supply, bool can_increase);
};
- struct [[eosio::table]] agent {
+ struct agent {
uint64_t id;
symbol_code token_code;
name account;
@@ -97,11 +100,21 @@ struct structures {
"min_own_staked can't be less than min_own_staked_for_election for users with an ultimate level");
eosio::check(get_own_funds() >= min_own_staked, "own staked funds can't be less than min_own_staked");
}
-
}
};
+
+ struct auto_recall {
+ uint64_t id;
+ symbol_code token_code;
+ name account;
+ bool break_fee_enabled = false;
+ bool break_min_stake_enabled = false;
+ uint64_t primary_key()const { return id; }
+ using key_t = std::tuple;
+ key_t by_key()const { return std::make_tuple(token_code, account); }
+ };
- struct [[eosio::table]] grant {
+ struct grant {
uint64_t id;
symbol_code token_code;
name grantor_name;
@@ -116,7 +129,7 @@ struct structures {
key_t by_key()const { return std::make_tuple(token_code, grantor_name, recipient_name); }
};
- struct [[eosio::table]] param {
+ struct param {
uint64_t id;
symbol token_symbol;
std::vector max_proxies;
@@ -125,7 +138,7 @@ struct structures {
uint64_t primary_key()const { return id; }
};
- struct [[eosio::table]] stat {
+ struct stat {
uint64_t id;
symbol_code token_code;
int64_t total_staked;
@@ -135,7 +148,7 @@ struct structures {
uint64_t primary_key()const { return id; }
};
- struct [[eosio::table]] provision {
+ struct provision_struct {
uint64_t id;
symbol_code token_code;
name grantor_name;
@@ -147,7 +160,7 @@ struct structures {
key_t by_key()const { return std::make_tuple(token_code, grantor_name, recipient_name); }
};
- struct [[eosio::table]] prov_payout {
+ struct prov_payout_struct {
uint64_t id;
symbol_code token_code;
name grantor_name;
@@ -159,6 +172,23 @@ struct structures {
using key_t = std::tuple;
key_t by_key()const { return std::make_tuple(token_code, grantor_name, recipient_name); }
};
+
+ struct suspense {
+ uint64_t id;
+ symbol_code token_code;
+ name account;
+ name action_name;
+ time_point_sec expiration_time;
+ uint64_t primary_key()const { return id; }
+ using key_t = std::tuple;
+ key_t by_key()const { return std::make_tuple(token_code, account, action_name); }
+ };
+
+ struct losses {
+ time_point_sec time;
+ symbol_code token_code;
+ int64_t total;
+ };
};
using agent_id_index = eosio::indexed_by<"agentid"_n, eosio::const_mem_fun >;
@@ -166,6 +196,10 @@ struct structures {
using agents = eosio::multi_index<"stake.agent"_n, structures::agent, agent_id_index, agent_key_index>;
using agents_idx_t = decltype(agents(table_owner, table_owner.value).get_index<"bykey"_n>());
+ using autorc_id_index = eosio::indexed_by<"autorcid"_n, eosio::const_mem_fun >;
+ using autorc_key_index = eosio::indexed_by<"bykey"_n, eosio::const_mem_fun >;
+ using autorcs = eosio::multi_index<"stake.autorc"_n, structures::auto_recall, autorc_id_index, autorc_key_index>;
+
using candidate_id_index = eosio::indexed_by<"candidateid"_n, eosio::const_mem_fun >;
using candidate_key_index = eosio::indexed_by<"bykey"_n, eosio::const_mem_fun >;
using candidate_votes_index = eosio::indexed_by<"byvotes"_n, eosio::const_mem_fun >;
@@ -184,13 +218,23 @@ struct structures {
using stats = eosio::multi_index<"stake.stat"_n, structures::stat, stat_id_index>;
- using prov_id_index = eosio::indexed_by<"provid"_n, eosio::const_mem_fun >;
- using prov_key_index = eosio::indexed_by<"bykey"_n, eosio::const_mem_fun >;
- using provs = eosio::multi_index<"provision"_n, structures::provision, prov_id_index, prov_key_index>;
+ using prov_key_index [[using eosio: order("token_code"), order("grantor_name"), order("recipient_name")]] =
+ eosio::indexed_by<"bykey"_n, eosio::const_mem_fun >;
+ using provs [[eosio::order("id")]] =
+ eosio::multi_index<"provision"_n, structures::provision_struct, prov_key_index>;
+
+ using prov_payout_acc_index [[using eosio: order("token_code"), order("grantor_name"), order("recipient_name")]] =
+ eosio::indexed_by<"bykey"_n, eosio::const_mem_fun >;
+ using prov_payouts [[eosio::order("id")]] =
+ eosio::multi_index<"provpayout"_n, structures::prov_payout_struct, prov_payout_acc_index>;
+
+ using susp_key_index [[using eosio: order("token_code"), order("account"), order("action_name")]] =
+ eosio::indexed_by<"bykey"_n, eosio::const_mem_fun >;
+ using susps [[eosio::order("id")]] =
+ eosio::multi_index<"suspense"_n, structures::suspense, susp_key_index>;
+ using susps_idx_t = decltype(susps(_self, _self.value).get_index<"bykey"_n>());
- using prov_payout_id_index = eosio::indexed_by<"provpayoutid"_n, eosio::const_mem_fun >;
- using prov_payout_acc_index = eosio::indexed_by<"bykey"_n, eosio::const_mem_fun >;
- using prov_payouts = eosio::multi_index<"provpayout"_n, structures::prov_payout, prov_payout_id_index, prov_payout_acc_index>;
+ using losses_singleton [[eosio::order("id","asc")]] = eosio::singleton<"losses"_n, structures::losses>;
void update_stake_proxied(symbol_code token_code, name agent_name) {
eosio::update_stake_proxied(token_code, agent_name, true);
@@ -234,6 +278,9 @@ struct structures {
void set_votes(symbol_code token_code, const std::map& votes_changes);
void update_provided(name grantor_name, name recipient_name, asset quantity);
+
+ void check_suspense(susps& susps_table, susps_idx_t& susps_idx, symbol_code token_code, name account, name action_name);
+ void set_suspense(name ram_payer, susps& susps_table, susps_idx_t& susps_idx, symbol_code token_code, name account, name action_name, int delay);
public:
@@ -242,8 +289,13 @@ struct structures {
int64_t votes = 0;
public_key signing_key = {};
};
+
+ static inline uint8_t get_max_level(symbol_code token_code) {
+ params params_table(table_owner, table_owner.value);
+ return params_table.get(token_code.raw(), "no staking for token").max_proxies.size();
+ }
- static inline std::vector get_top(symbol_code token_code, uint16_t elected_num, uint16_t reserve_num, bool strict = true) {
+ static inline std::vector get_top(symbol_code token_code, uint16_t elected_num, uint16_t reserve_num) {
staking_exists(token_code);
candidates candidates_table(table_owner, table_owner.value);
@@ -255,7 +307,7 @@ struct structures {
size_t i = 0;
auto cands_itr = cands_idx.lower_bound(std::make_tuple(token_code, true, maxval, name()));
while ((cands_itr != cands_idx.end()) && (cands_itr->token_code == token_code) && (i < elected_num)) {
- if (!strict || (cands_itr->signing_key != public_key{})) {
+ if (cands_itr->signing_key != public_key{}) {
ret.emplace_back(elected_t{cands_itr->account, cands_itr->votes, cands_itr->signing_key});
++i;
}
@@ -287,7 +339,7 @@ struct structures {
}
if (new_one) {
- if (!strict || (cands_itr->signing_key != public_key{})) {
+ if (cands_itr->signing_key != public_key{}) {
ret.emplace_back(elected_t{cands_itr->account, cands_itr->votes, cands_itr->signing_key});
++i;
}
@@ -331,7 +383,7 @@ struct structures {
[[eosio::action]] void create(symbol token_symbol, std::vector max_proxies, int64_t depriving_window,
int64_t min_own_staked_for_election);
- [[eosio::action]] void enable(symbol_code token_symbol);
+ [[eosio::action]] void enable(symbol_code token_code);
static inline bool enabled(symbol_code token_code) {
staking_exists(token_code);
@@ -349,7 +401,7 @@ struct structures {
[[eosio::action]] void withdraw(name account, asset quantity);
- void on_transfer(name from, name to, asset quantity, std::string memo);
+ ON_TRANSFER(CYBER_TOKEN, on_transfer) void on_transfer(name from, name to, asset quantity, std::string memo);
[[eosio::action]] void setproxylvl(name account, symbol_code token_code, uint8_t level);
[[eosio::action]] void setproxyfee(name account, symbol_code token_code, int16_t fee);
@@ -357,11 +409,14 @@ struct structures {
[[eosio::action]] void setkey(name account, symbol_code token_code, std::optional signing_key);
[[eosio::action]] void updatefunds(name account, symbol_code token_code);
+ [[eosio::action]] void suspendcand(name account, symbol_code token_code);
[[eosio::action]] void reward(std::vector > rewards, symbol sym);
[[eosio::action]] void pick(symbol_code token_code, std::vector accounts);
+ [[eosio::action]] void returnlosses();
+
static inline int64_t get_effective_stake(name account, symbol_code token_code) {
agents agents_table(table_owner, table_owner.value);
auto agents_idx = agents_table.get_index<"bykey"_n>();
@@ -373,5 +428,8 @@ struct structures {
[[eosio::action]] void recalluse(name grantor_name, name recipient_name, asset quantity);
[[eosio::action]] void claim(name grantor_name, name recipient_name, symbol_code token_code);
+
+ [[eosio::action]] void setautorc(name account, symbol_code token_code, bool break_fee_enabled, bool break_min_stake_enabled);
+ [[eosio::action]] void setautorcmode(symbol_code token_code, bool enabled);
};
} /// namespace cyber
diff --git a/cyber.stake/src/cyber.stake.cpp b/cyber.stake/src/cyber.stake.cpp
index 50dbc5ea0..b082c4843 100644
--- a/cyber.stake/src/cyber.stake.cpp
+++ b/cyber.stake/src/cyber.stake.cpp
@@ -9,8 +9,6 @@
#include
#include
#include
-#include
-#include
#include
namespace cyber {
@@ -252,9 +250,10 @@ void stake::on_transfer(name from, name to, asset quantity, std::string memo) {
auto agent = agents_idx.find(std::make_tuple(token_code, account));
if (agent == agents_idx.end()) {
emplace_agent(account, agents_table, param, from);
- agent = agents_idx.find(std::make_tuple(token_code, account));
}
update_stake_proxied(token_code, account);
+ agents_table.flush_cache();
+ agent = agents_idx.find(std::make_tuple(token_code, account));
grants grants_table(table_owner, table_owner.value);
auto grants_idx = grants_table.get_index<"bykey"_n>();
@@ -317,6 +316,32 @@ void stake::setminstaked(name account, symbol_code token_code, int64_t min_own_s
modify_agent(account, token_code, [min_own_staked](auto& a) { a.min_own_staked = min_own_staked; });
}
+void stake::check_suspense(susps& susps_table, susps_idx_t& susps_idx, symbol_code token_code, name account, name action_name) {
+ auto susps_itr = susps_idx.find(std::make_tuple(token_code, account, action_name));
+ if (susps_itr != susps_idx.end()) {
+ eosio::check(time_point_sec(eosio::current_time_point()) >= susps_itr->expiration_time, "action is temporarily unavailable");
+ susps_idx.erase(susps_itr);
+ }
+}
+
+void stake::set_suspense(name ram_payer, susps& susps_table, susps_idx_t& susps_idx, symbol_code token_code, name account, name action_name, int delay) {
+ auto susps_itr = susps_idx.find(std::make_tuple(token_code, account, action_name));
+ if (susps_itr != susps_idx.end()) {
+ susps_idx.modify(susps_itr, name(), [&](auto& s) {
+ s.expiration_time = std::max(s.expiration_time, time_point_sec(eosio::current_time_point() + seconds(delay)));
+ });
+ }
+ else {
+ susps_table.emplace(ram_payer, [&]( auto &s ) { s = structures::suspense {
+ .id = susps_table.available_primary_key(),
+ .token_code = token_code,
+ .account = account,
+ .action_name = action_name,
+ .expiration_time = eosio::current_time_point() + seconds(delay)
+ };});
+ }
+}
+
void stake::setkey(name account, symbol_code token_code, std::optional signing_key = std::nullopt) {
staking_exists(token_code);
auto actual_signing_key = signing_key.value_or(public_key{});
@@ -328,33 +353,51 @@ void stake::setkey(name account, symbol_code token_code, std::optional();
auto agent = get_agent_itr(token_code, agents_idx, account);
eosio::check(!agent->proxy_level, account.to_string() + " is not a candidate");
- bool has_self_auth = has_auth(_self);
+
+ auto issuer = eosio::token::get_issuer(config::token_name, token_code);
+ bool has_issuer_auth = has_auth(issuer);
candidates candidates_table(table_owner, table_owner.value);
auto cands_idx = candidates_table.get_index<"bykey"_n>();
auto cand = cands_idx.find(std::make_tuple(token_code, account));
eosio::check(cand != cands_idx.end(), ("SYSTEM: candidate " + account.to_string() + " doesn't exist").c_str());
+ susps susps_table(_self, _self.value);
+ auto susps_idx = susps_table.get_index<"bykey"_n>();
+ auto action_name = "setkey"_n;
+
if (actual_signing_key != public_key{}) {
require_auth(account);
- eosio::check(time_point_sec(eosio::current_time_point()) >= cand->latest_pick, "action is temporarily unavailable");
+ check_suspense(susps_table, susps_idx, token_code, account, action_name);
agent->check_own_staked(_self, min_own_staked_for_election);
}
- else if (!has_self_auth && agent->min_own_staked >= min_own_staked_for_election && agent->get_own_funds() >= agent->min_own_staked) {
+ else if (!has_issuer_auth && agent->min_own_staked >= min_own_staked_for_election && agent->get_own_funds() >= agent->min_own_staked) {
require_auth(account);
}
- cands_idx.modify(cand, name(), [actual_signing_key, has_self_auth](auto& a) {
+ cands_idx.modify(cand, name(), [actual_signing_key](auto& a) {
a.signing_key = actual_signing_key;
a.enabled = actual_signing_key != public_key{};
- if (has_self_auth && !a.enabled) {
- a.latest_pick = eosio::current_time_point() + seconds(config::key_recovery_delay);
- }
});
+
+ if (has_issuer_auth && !cand->enabled) {
+ set_suspense(issuer, susps_table, susps_idx, token_code, account, action_name, config::key_recovery_delay);
+ }
}
void stake::setproxylvl(name account, symbol_code token_code, uint8_t level) {
- require_auth(account);
+ auto issuer = eosio::token::get_issuer(config::token_name, token_code);
+ bool has_issuer_auth = has_auth(issuer);
+
+ susps susps_table(_self, _self.value);
+ auto susps_idx = susps_table.get_index<"bykey"_n>();
+ auto action_name = "setproxylvl"_n;
+
+ if (!has_issuer_auth) {
+ require_auth(account);
+ check_suspense(susps_table, susps_idx, token_code, account, action_name);
+ }
+
params params_table(table_owner, table_owner.value);
const auto& param = params_table.get(token_code.raw(), "no staking for token");
@@ -362,7 +405,7 @@ void stake::setproxylvl(name account, symbol_code token_code, uint8_t level) {
agents agents_table(table_owner, table_owner.value);
auto agents_idx = agents_table.get_index<"bykey"_n>();
auto agent = get_agent_itr(token_code, agents_idx, account);
- eosio::check(level != agent->proxy_level, "proxy level has not been changed");
+ eosio::check(has_issuer_auth || level != agent->proxy_level, "proxy level has not been changed");
grants grants_table(table_owner, table_owner.value);
auto grants_idx = grants_table.get_index<"bykey"_n>();
uint8_t proxies_num = 0;
@@ -379,7 +422,6 @@ void stake::setproxylvl(name account, symbol_code token_code, uint8_t level) {
if (!agent->proxy_level && level) {
auto cand = cands_idx.find(std::make_tuple(token_code, account));
eosio::check(cand != cands_idx.end(), ("SYSTEM: candidate " + account.to_string() + " doesn't exist").c_str());
- eosio::check(time_point_sec(eosio::current_time_point()) >= cand->latest_pick, "action is temporarily unavailable");
cands_idx.erase(cand);
stats stats_table(table_owner, table_owner.value);
@@ -403,7 +445,11 @@ void stake::setproxylvl(name account, symbol_code token_code, uint8_t level) {
a.proxy_level = level;
});
agent->check_own_staked(_self, param.min_own_staked_for_election);
-}
+
+ if (has_issuer_auth) {
+ set_suspense(issuer, susps_table, susps_idx, token_code, account, action_name, config::proxylvl_recovery_delay);
+ }
+}
void stake::create(symbol token_symbol, std::vector max_proxies, int64_t depriving_window, int64_t min_own_staked_for_election)
{
@@ -487,6 +533,20 @@ void stake::updatefunds(name account, symbol_code token_code) {
update_stake_proxied(token_code, account);
}
+void stake::suspendcand(name account, symbol_code token_code) {
+ //require_auth(anyone);
+ auto issuer = eosio::token::get_issuer(config::token_name, token_code);
+
+ candidates candidates_table(table_owner, table_owner.value);
+ auto cands_idx = candidates_table.get_index<"bykey"_n>();
+ auto cand = cands_idx.find(std::make_tuple(token_code, account));
+ eosio::check(cand != cands_idx.end(), "candidate doesn't exist");
+ eosio::check(cand->latest_pick < eosio::current_time_point() - eosio::seconds(config::max_no_pick_period), "candidate is active");
+
+ INLINE_ACTION_SENDER(cyber::stake, setproxylvl)(config::stake_name, {issuer, config::active_name},
+ {account, token_code, get_max_level(token_code)});
+}
+
void stake::reward(std::vector > rewards, symbol sym) {
eosio::check(rewards.size(), "no rewards");
auto token_code = sym.code();
@@ -603,7 +663,7 @@ void stake::update_provided(name grantor_name, name recipient_name, asset quanti
provs_index.modify(prov_itr, name(), [&](auto& p) { p.amount += quantity.amount; });
}
else {
- provs_table.emplace(grantor_name, [&]( auto &item ) { item = structures::provision {
+ provs_table.emplace(grantor_name, [&]( auto &item ) { item = structures::provision_struct {
.id = provs_table.available_primary_key(),
.token_code = token_code,
.grantor_name = grantor_name,
@@ -627,7 +687,7 @@ void stake::update_provided(name grantor_name, name recipient_name, asset quanti
});
}
else {
- payouts_table.emplace(grantor_name, [&]( auto &item ) { item = structures::prov_payout {
+ payouts_table.emplace(grantor_name, [&]( auto &item ) { item = structures::prov_payout_struct {
.id = provs_table.available_primary_key(),
.token_code = token_code,
.grantor_name = grantor_name,
@@ -677,14 +737,103 @@ void stake::claim(name grantor_name, name recipient_name, symbol_code token_code
agents_idx.modify(grantor, name(), [&](auto& a) { a.provided -= payout_itr->amount; });
payouts_index.erase(payout_itr);
+}
+void stake::setautorc(name account, symbol_code token_code, bool break_fee_enabled, bool break_min_stake_enabled) {
+ require_auth(account);
+ staking_exists(token_code);
+
+ agents agents_table(table_owner, table_owner.value);
+ auto agents_idx = agents_table.get_index<"bykey"_n>();
+ get_agent_itr(token_code, agents_idx, account); //checking that the agent exists
+
+ autorcs autorcs_table(table_owner, table_owner.value);
+ auto autorcs_idx = autorcs_table.get_index<"bykey"_n>();
+ autorcs_idx.get(std::make_tuple(token_code, name()), "custom auto recall mode is disabled for this token");
+
+ if (!break_fee_enabled && !break_min_stake_enabled) {
+ auto autorc_itr = autorcs_idx.find(std::make_tuple(token_code, account));
+ eosio::check(autorc_itr != autorcs_idx.end(), "no params changed");
+ autorcs_idx.erase(autorc_itr);
+ }
+ else {
+ auto autorc_itr = autorcs_idx.find(std::make_tuple(token_code, account));
+ if (autorc_itr != autorcs_idx.end()) {
+ eosio::check(break_fee_enabled != autorc_itr->break_fee_enabled || break_min_stake_enabled != autorc_itr->break_min_stake_enabled, "no params changed");
+ autorcs_idx.modify(autorc_itr, account, [&](auto& p) {
+ p.break_fee_enabled = break_fee_enabled;
+ p.break_min_stake_enabled = break_min_stake_enabled;
+ });
+ }
+ else {
+ autorcs_table.emplace(account, [&](auto& p) { p = {
+ .id = autorcs_table.available_primary_key(),
+ .token_code = token_code,
+ .account = account,
+ .break_fee_enabled = break_fee_enabled,
+ .break_min_stake_enabled = break_min_stake_enabled
+ };});
+ }
+ }
}
-} /// namespace cyber
+void stake::setautorcmode(symbol_code token_code, bool enabled) {
+ auto issuer = eosio::token::get_issuer(config::token_name, token_code);
+ require_auth(issuer);
+ staking_exists(token_code);
+
+ autorcs autorcs_table(table_owner, table_owner.value);
+ auto autorcs_idx = autorcs_table.get_index<"bykey"_n>();
+ auto autorc_itr = autorcs_idx.find(std::make_tuple(token_code, name()));
+ if (enabled) {
+ eosio::check(autorc_itr == autorcs_idx.end(), "custom auto recall mode is already enabled for this token");
+ autorcs_table.emplace(issuer, [&](auto& p) { p = {
+ .id = autorcs_table.available_primary_key(),
+ .token_code = token_code,
+ .account = name()
+ };});
+ }
+ else {
+ eosio::check(autorc_itr != autorcs_idx.end(), "custom auto recall mode is already disabled for this token");
+ autorcs_idx.erase(autorc_itr);
+ }
+}
+
+void stake::returnlosses() {
+ auto losses_state = losses_singleton(_self, _self.value);
+ eosio::check(!losses_state.exists(), "losses already have been returned");
+
+ agents agents_table(table_owner, table_owner.value);
+ auto agents_idx = agents_table.get_index<"bykey"_n>();
+ auto token_code = eosio::symbol_code("CYBER");
+ int64_t total = 0;
+
+ auto return_loss = [&](const eosio::name account, const int64_t amount) {
+ auto agent = agents_idx.find(std::make_tuple(token_code, account));
+ eosio::check(agent != agents_idx.end(), "agent doesn't exist");
+ agents_idx.modify(agent, eosio::same_payer, [&](auto& a) {
+ a.balance += amount;
+ });
+ total += amount;
+ };
+
+ // account: mike2mike@golos trx: f039154469c34af0fb5f90aed0e383c86304d7e3059a841f8fa52a707d688c64
+ return_loss("dsqy4k5ym3uk"_n, 10);
+
+ // account: ltrack@golos trx: 8f373abe3429bb44e97c26fbf2d4d40dfc71007a7063c31ee3f710eb7a7fbcb8
+ return_loss("ym2imjop4l5n"_n, 16748);
+
+ // account: drugan7@golos trx: 55bb2a8a9540026f7f7a5db96eccc999e85f55a27761f73846b7017a21862f8c
+ return_loss("rzi4tcizzvyw"_n, 3236);
-DISPATCH_WITH_TRANSFER(cyber::stake, cyber::config::token_name, on_transfer,
- (create)(enable)(open)(delegatevote)(setgrntterms)(recallvote)(withdraw)
- (setproxylvl)(setproxyfee)(setminstaked)(setkey)
- (updatefunds)(reward)(pick)
- (delegateuse)(recalluse)(claim)
-)
+ // account: gunblade trx: 3c54eecfe65722ab5817433ed7cbffbaa847b62ef0a1ce0abab9cc3f83ff96ec
+ return_loss("xt351mrztghr"_n, 826);
+
+ structures::losses s;
+ s.time = eosio::current_time_point();
+ s.token_code = token_code;
+ s.total = total;
+ losses_state.set(s, _self);
+}
+
+} /// namespace cyber
diff --git a/cyber.token/CMakeLists.txt b/cyber.token/CMakeLists.txt
index 7ddfc9c2e..f11ae17af 100644
--- a/cyber.token/CMakeLists.txt
+++ b/cyber.token/CMakeLists.txt
@@ -1,4 +1,6 @@
-add_contract_with_abi(cyber.token abi/cyber.token.abi ${CMAKE_CURRENT_SOURCE_DIR}/src/cyber.token.cpp)
+add_contract(cyber.token cyber.token ${CMAKE_CURRENT_SOURCE_DIR}/src/cyber.token.cpp)
+install_contract(cyber.token)
+
target_include_directories(cyber.token.wasm
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include)
diff --git a/cyber.token/abi/cyber.token.abi b/cyber.token/abi/cyber.token.abi
deleted file mode 100644
index 6ba9147a5..000000000
--- a/cyber.token/abi/cyber.token.abi
+++ /dev/null
@@ -1,279 +0,0 @@
-{
- "version": "cyberway::abi/1.1",
- "structs": [
- {
- "name": "account",
- "base": "",
- "fields": [
- {
- "name": "balance",
- "type": "asset"
- },
- {
- "name": "payments",
- "type": "asset"
- }
- ]
- },
- {
- "name": "close",
- "base": "",
- "fields": [
- {
- "name": "owner",
- "type": "name"
- },
- {
- "name": "symbol",
- "type": "symbol"
- }
- ]
- },
- {
- "name": "create",
- "base": "",
- "fields": [
- {
- "name": "issuer",
- "type": "name"
- },
- {
- "name": "maximum_supply",
- "type": "asset"
- }
- ]
- },
- {
- "name": "currency_stats",
- "base": "",
- "fields": [
- {
- "name": "supply",
- "type": "asset"
- },
- {
- "name": "max_supply",
- "type": "asset"
- },
- {
- "name": "issuer",
- "type": "name"
- }
- ]
- },
- {
- "name": "balance_event",
- "base": "",
- "fields": [
- {
- "name": "account",
- "type": "name"
- },
- {
- "name": "balance",
- "type": "asset"
- },
- {
- "name": "payments",
- "type": "asset"
- }
- ]
- },
- {
- "name": "issue",
- "base": "",
- "fields": [
- {
- "name": "to",
- "type": "name"
- },
- {
- "name": "quantity",
- "type": "asset"
- },
- {
- "name": "memo",
- "type": "string"
- }
- ]
- },
- {
- "name": "open",
- "base": "",
- "fields": [
- {
- "name": "owner",
- "type": "name"
- },
- {
- "name": "symbol",
- "type": "symbol"
- },
- {
- "name": "ram_payer",
- "type": "name"
- }
- ]
- },
- {
- "name": "retire",
- "base": "",
- "fields": [
- {
- "name": "quantity",
- "type": "asset"
- },
- {
- "name": "memo",
- "type": "string"
- }
- ]
- },
- {
- "name": "recipient",
- "base": "",
- "fields": [
- {
- "name": "to",
- "type": "name"
- },
- {
- "name": "quantity",
- "type": "asset"
- },
- {
- "name": "memo",
- "type": "string"
- }
- ]
- },
- {
- "name": "bulktransfer",
- "base": "",
- "fields": [
- {
- "name": "from",
- "type": "name"
- },
- {
- "name": "recipients",
- "type": "recipient[]"
- }
- ]
- },
- {
- "name": "transfer",
- "base": "",
- "fields": [
- {
- "name": "from",
- "type": "name"
- },
- {
- "name": "to",
- "type": "name"
- },
- {
- "name": "quantity",
- "type": "asset"
- },
- {
- "name": "memo",
- "type": "string"
- }
- ]
- },
- {
- "name": "claim",
- "base": "",
- "fields": [
- {
- "name": "owner",
- "type": "name"
- },
- {
- "name": "quantity",
- "type": "asset"
- }
- ]
- }
- ],
- "types": [],
- "actions": [
- {
- "name": "close",
- "type": "close"
- },
- {
- "name": "create",
- "type": "create"
- },
- {
- "name": "issue",
- "type": "issue"
- },
- {
- "name": "open",
- "type": "open"
- },
- {
- "name": "retire",
- "type": "retire"
- },
- {
- "name": "bulktransfer",
- "type": "bulktransfer"
- },
- {
- "name": "bulkpayment",
- "type": "bulktransfer"
- },
- {
- "name": "transfer",
- "type": "transfer"
- },
- {
- "name": "payment",
- "type": "transfer"
- },
- {
- "name": "claim",
- "type": "claim"
- }
- ],
- "events": [
- {"name": "currency", "type": "currency_stats"},
- {"name": "balance", "type": "balance_event"}
- ],
- "tables": [
- {
- "name": "accounts",
- "type": "account",
- "indexes": [
- {
- "name": "primary",
- "unique": "true",
- "orders": [
- {"field": "balance._sym", "order": "asc"}
- ]
- }
- ]
- },
- {
- "name": "stat",
- "type": "currency_stats",
- "scope_type": "symbol_code",
- "indexes": [
- {
- "name": "primary",
- "unique": "true",
- "orders": [
- {"field": "supply._sym", "order": "asc"}
- ]
- }
- ]
- }
- ],
- "variants": [],
- "abi_extensions": []
-}
diff --git a/cyber.token/include/cyber.token/cyber.token.hpp b/cyber.token/include/cyber.token/cyber.token.hpp
index d0e367547..8308aa93c 100644
--- a/cyber.token/include/cyber.token/cyber.token.hpp
+++ b/cyber.token/include/cyber.token/cyber.token.hpp
@@ -101,14 +101,14 @@ namespace eosio {
}
private:
- struct [[eosio::table]] account {
+ struct account {
asset balance;
asset payments;
uint64_t primary_key()const { return balance.symbol.code().raw(); }
};
- struct [[eosio::table]] currency_stats {
+ struct [[eosio::event("currency")]] currency_stats {
asset supply;
asset max_supply;
name issuer;
@@ -116,14 +116,16 @@ namespace eosio {
uint64_t primary_key()const { return supply.symbol.code().raw(); }
};
- struct balance_event {
+ struct [[eosio::event]] balance_event {
name account;
asset balance;
asset payments;
};
- typedef eosio::multi_index< "accounts"_n, account > accounts;
- typedef eosio::multi_index< "stat"_n, currency_stats > stats;
+ using accounts [[eosio::order("balance._sym")]] =
+ eosio::multi_index<"accounts"_n, account>;
+ using stats [[using eosio: order("supply._sym"), scope_type("symbol_code")]] =
+ eosio::multi_index<"stat"_n, currency_stats>;
void sub_balance( name owner, asset value );
void add_balance( name owner, asset value, name ram_payer );
diff --git a/cyber.token/src/cyber.token.cpp b/cyber.token/src/cyber.token.cpp
index 694ced156..6fe75e6c9 100644
--- a/cyber.token/src/cyber.token.cpp
+++ b/cyber.token/src/cyber.token.cpp
@@ -281,5 +281,3 @@ void token::bulkpayment(name from, vector recipients)
}
} /// namespace eosio
-
-EOSIO_DISPATCH( eosio::token, (create)(issue)(transfer)(bulktransfer)(payment)(bulkpayment)(claim)(open)(close)(retire) )
diff --git a/docs/en-US/cyber.bios_contract.md b/docs/en-US/cyber.bios_contract.md
new file mode 100644
index 000000000..f1cc3dce1
--- /dev/null
+++ b/docs/en-US/cyber.bios_contract.md
@@ -0,0 +1,225 @@
+
+## Purpose of the cyber.bios smart contract
+
+The `cyber.bios` smart contract is used as a link between operations, executed both directly in smart contracts and the node core.
+
+`cyber.bios` includes the following actions: [newaccount](#newaccount), [setprods](#setprods), [setparams](#setparams), [reqauth](#reqauth), [setabi](#setabi), [setcode](#setcode), [onblock](#onblock), [checkwin](#checkwin), [bidname](#bidname), [bidrefund](#bidrefund), [canceldelay](#canceldelay), [updateauth](#updateauth), [deleteauth](#deleteauth), [linkauth](#linkauth) and [unlinkauth](#unlinkauth).
+
+
+## newaccount
+The `newaccount` action is used when creating new accounts in the system.
+
+```cpp
+void bios::newaccount(
+ name creator,
+ name name,
+ authority owner,
+ authority active
+)
+```
+**Parameters:**
+ * `creator` — new account creator.
+ * `name` — the account name that is created in the system.
+ * `owner` — structure of type `authority` containing `owner public key` for the new account.
+ * `active` — structure of type `authority` containing `active public key` for the new account.
+
+
+**Restrictions:**
+ * The name created (not redeemed at auction) must contain no more than 12 characters. Also, it must not contain the dot symbol.
+ * The name purchased at auction or created through the system account can be expanded by adding characters to the right after the dot symbol (for example, an owner of the name `cyber` can expanded it to the name `cyber.anyname`).
+
+A transaction containing `newaccount` action must be signed by the new account creator.
+
+## setprods
+The action `setprods` is used when creating a block production schedule. This schedule contains a list of validators in accordance with their rating. This action is not accessible to a user and is called by the system. This action takes the following form:
+
+```cpp
+[[eosio::action]] void setprods(eosio::producer_key schedule )
+```
+The `schedule` parameter is a list of validator keys.
+
+ The `cyber` account signature is required to execute a transaction containing the `setprods` action (`cyber` is account of `cyber.bios` smart contract).
+
+
+## setparams
+The `setparams` action is used to configure system parameters. This action is not accessible to a user and is called by the system. This action takes the following form:
+
+```cpp
+[[eosio::action]] void setparams( eosio::blockchain_parameters params )
+```
+The `params` parameter is a structure containing settable system parameters.
+
+ The `cyber` account signature is required to execute a transaction containing the `setparams` action.
+
+## reqauth
+The `reqauth` action is used to verify the user's signature in a transaction. This action takes the following form:
+```cpp
+[[eosio::action]] void reqauth( name from )
+```
+The `from` parameter is the account whose signature is verified.
+
+The `from` account signature is required to execute a transaction containing the `reqauth` operation.
+
+## setabi
+The `setabi` action is used to upload ABI-description to an account. This action takes the following form:
+
+```cpp
+[[eosio::action]] void setabi(
+ name account,
+ std::vector abi
+)
+```
+**Parameters:**
+ * `account` — the account to which ABI-description will be downloaded.
+ * `abi` — an array of bytes containing ABI-description.
+
+## setcode
+The `setcode` action is used to upload smart contract code to an account. The signature of this action is:
+
+```cpp
+[[eosio::action]] void setcode(
+ name account,
+ uint8_t vmtype,
+ uint8_t vmversion,
+ std::vector code
+)
+```
+**Parameters:**
+ * `account` — the account to which smart contract code will be downloaded.
+ * `vmtype` — type of contract (type of virtual machine). In this release, the parameter is set to «0».
+ * `vmversion` — version of contract (version of virtual machine). In this release, the parameter is set to «0».
+ * `code` — an array of bytes containing smart contract code.
+
+## onblock
+The `onblock` action is inaccessible to a user and is called by the system each time after creation of a new block. Inside `onblock`, the `cyber.govern` smart contract code is called. This action takes the following form:
+
+```cpp
+void bios::onblock(block_header header)
+```
+The `header` parameter is a block header.
+
+## checkwin
+Action `checkwin` is used to register a name owner (a winner) at auction. This action has no parameters and is called implicitly.
+```cpp
+void bios::checkwin()
+```
+
+## bidname
+The bidname action allows a user to bid on a specific name at the names auction. This action takes the following form:
+```cpp
+void bios::bidname(
+ name bidder,
+ name newname,
+ eosio::asset bid
+)
+```
+**Parameters:**
+ * `bidder` — an account which bids at the action.
+ * `newname` — betting name (a name on which the bid is done).
+ * `bid` — a bet on the name (a structure value specified the bid). Bets are accepted only in system tokens (CYBER).
+
+The `bidder` account will be announced as an owner (a winner) of `newname`, if:
+ * the `bid` is the highest in the name newname;
+ * the `bid` remains the highest after 24 hours.
+In this case, the bid is not returned to the bidder. If the `bid` is beaten within 24 hours, it will be returned back to the `bidder` (in this release, the bid is not automatically refunded; a call of `bidrefund` is required to refund it).
+
+## bidrefund
+The `bidrefund` action is used to refund a non-winning (not the highest) bid to an auction participant
+```cpp
+void bios::bidrefund( name bidder )
+```
+The `bidder` parameter is an auction participant to whom the bid is returned.
+
+The bid is automatically refunded to `bidder` if the `bidder` does not become a winner. The bidder should create a transaction with the bidrefund operation to refund the bid. One calling `bidrefund` is enough to return all of non-winning bids in case the bidder put several bids on different names and neither of these bids (or part of them) were non-winning.
+
+
+
+## canceldelay
+The `canceldelay` action cancels a deferred transaction.
+```cpp
+void canceldelay(
+ permission_level canceling_auth,
+ eosio::checksum256 trx_id
+)
+```
+**Parameters:**
+ * `canceling_auth` — value containing an account of the transaction creator and its permission (permission name takes the value `active`).
+ * `trx_id ` — a deferred transaction identifier.
+
+The `canceling_auth` account signature is required to execute a transaction containing the `canceldelay` operation.
+
+## updateauth
+The `updateauth` action is used to add or change authorization for an account. This action takes the following form:
+```cpp
+void updateauth(
+ name account,
+ name permission,
+ name parent,
+ authority auth
+)
+```
+**Parameters:**
+ * `account` — an account name whose authorization changes.
+ * `permission` — a permission name (for example: owner, active, posting, etc.) to be changed.
+ * `parent` — a name of parent permission (for example: `owner` is the parent permission to `active`; `active` is the parent permission to `posting`. Child permissions cannot exceed parental permissions). In most cases, this parameter is set to `active'.
+ * `auth` — structure of the form `authority`, the new values of which should match the `permission`.
+
+The `account` signature is required to execute a transaction containing the `updateauth` operation.
+
+
+## deleteauth
+The action `deleteauth` is used to delete authorization of an account. This action takes the following form:
+```cpp
+void deleteauth(
+ name account,
+ name permission
+)
+```
+**Parameters:**
+ * `account` — account name whose authorization is being deleted.
+ * `permission` — permission name that is deleted.
+
+The `account` signature is required to execute a transaction containing the `deleteauth` operation.
+
+## linkauth
+The `linkauth` action allows the `permission` to execute some action in a specific contract.
+```cpp
+void linkauth(
+ name account,
+ name code,
+ name type,
+ name requirement
+)
+
+```
+**Parameters:**
+ * `account ` — account name for which permission is changed.
+ * `code ` — an account name of the contract in which some action will be executed.
+ * `type ` — type of permission. Any action can be performed in the contract if this parameter is empty, otherwise only one operation specified in the parameter can be performed (parameter value matches the name of the action).
+ * `requirement ` — name of `permission`, which is allowed to perform the operation. This parameter cannot be empty.
+
+The parameters `account` and` requirement` specify `permission_level`.
+The parameters `code` and` type` specify the contract account and action, respectively.
+
+> **Notes:**
+> Any set of `account`, `code`, and `type` parameters must correspond to only one `requirement` value. This means that one account cannot have two or more different permissions related to the same contract's account and action.
+>
+> If the same `account`,` code` and `type` values are passed to the action again, but with a different `requirement`, its previous value will be replaced by the last passed.
+
+The `account` signature is required to execute a transaction containing the `linkauth` operation.
+
+## unlinkauth
+The `unlinkauth` action removes the name of `permission` provided by the `linkauth` action in a specific contract
+```cpp
+void unlinkauth(
+ name account,
+ name code,
+ name type
+)
+```
+**Parameters:**
+ * `account ` — a name of the account whose permission is removed.
+ * `code ` — an account name of the contract in which some action was allowed to execute.
+ * `type ` — type of permission..
+
+The `account` signature is required to execute the transaction containing the `unlinkauth` operation.
diff --git a/docs/en-US/cyber.domain_contract.md b/docs/en-US/cyber.domain_contract.md
index ddcc4f958..089d5e474 100644
--- a/docs/en-US/cyber.domain_contract.md
+++ b/docs/en-US/cyber.domain_contract.md
@@ -3,50 +3,56 @@
## Purpose of the cyber.domain smart contract development
The `cyber.domain` smart contract is designed to create and handle domain names, create or delete a link of domain names to accounts, change domain name owners, as well as to handle a purchase of domain names at auction.
-The `cyber.domain` contract includes:
-* a part of actions used to purchase a domain name at auction, including [checkwin](#the-checkwin-action), [biddomain](#the-biddomain-action), [biddmrefund](#the-biddmrefund-action), [newdomain](#the-newdomain-action) and [declarenames](#the-declarenames-declaration);
-* a part of internal domain actions, including [passdomain](#the-passdomain-declaration), [linkdomain](#the-linkdomain-declaration), [unlinkdomain](#the-unlinkdomain-declaration) and [newusername](#the-newusername-declaration). Although a code of these actions is in the blockchain core, they are called up via smart contract;
-* a list of domain names used in transactions.
-
+The `cyber.domain` smart contract includes the following actions:
+ * actions used to acquire a domain name at auction: [checkwin](#checkwin), [biddomain](#biddomain), [biddmrefund](#biddmrefund) and [newdomain](#newdomain).
+ * internal domain actions: [passdomain](#passdomain), [linkdomain](#linkdomain), [unlinkdomain](#unlinkdomain) and [newusername](#newusername). Although a code of these actions is in the blockchain core, they are called up via smart contract.
+ * the action [declarenames](#declarenames) to declare the names used in transactions.
## Requirements for domain names
Domain names in CyberWay are formed in accordance with rules and procedures of the Domain Name System (DNS). The following requirements are imposed on the structure of domain names:
- * a total number of characters in domain name should not exceed 253 pcs;
- * a domain name consists of individual parts, separated by the symbol «dot»;
- * the «dot» characters should not stand side by side in any domain name;
- * a number of characters in a separate part of domain name should not exceed 63 pcs;
- * the valid characters in the domain name are alphanumeric, plus «hyphen»;
- * the capital letters in the domain name are unacceptable;
- * a symbol «hyphen» should not be at the beginning or end of any domain name part;
- * the further right part of domain name must contain at least one alphabetic character. A presence of numeric characters only is not allowed.
-
+ * a total number of characters in domain name should not exceed 253 pcs.
+ * a domain name consists of individual *parts*, separated by the symbol «dot».
+ * the «dot» characters should not stand side by side in any domain name.
+ * a number of characters in a separate *part* of domain name should not exceed 63 pcs.
+ * the valid characters in the domain name are alphanumeric, plus «hyphen».
+ * the capital letters in the domain name are unacceptable.
+ * a symbol «hyphen» should not be at the beginning or end of any domain name *part*.
+ * the further right *part* of domain name must contain at least one alphabetic character. A presence of numeric characters only is not allowed.
+
+## Requirements for user names
+ The following requirements are imposed on a user name structure:
+ * a total number of characters in a user name should not exceed 32 pcs.
+ * a user name can consist of individual *parts* separated by the symbol «dot».
+ * two «dot» symbols near each other are not allowed.
+ * the valid characters in a user name should be alphanumeric, a symbol «hyphen» could be used as well.
+ * the capital letters in a user name are unacceptable.
+ * a symbol «hyphen» should not be at the beginning or end of any user name *part*.
## Domain name auction in the smart contract
The actions `checkwin`, `biddomain`, `biddmrefund` and `newdomain` are used to purchase a domain name at auction. The procedure for buying a domain name at the auction is the same as the procedure of buying an account name. The following rules apply to the procedure:
-* the bids for the purchase of any domain name are accepted at auction at any time;
-* the largest bid is used to determine only one domain name that can be purchased at the moment;
+* the bids for the purchase of any domain name are accepted at auction at any time.
+* the largest bid is used to determine only one domain name that can be purchased at the moment.
* a domain name is considered to be purchased at auction if the following conditions are met:
- * no more than a day is passed after betting on the current domain name;
- * at least one day has passed since previous redemption of any domain name;
-* the number of domain names purchased at auction during a day should be no more than one;
+ * at least a day has passed after betting on the current domain name;
+ * at least one day has passed since previous redemption of any domain name.
* upon completion of auction, a token transfer of the winner is not returned. The winner can take advantage of the following opportunities:
- * to create her/his own domain name using the operation `newdomain` and become its owner;
- * to create subdomain names from it by adding the «dot» symbol and а name to the domain name on the left (for example, an owner of the domain `golos.io` can create subdomains such as `api.golos.io`, `ws.golos.io` and etc). It should be noted, that in `cyberway` the account names are formed by the same way — by adding parts to the left of word (for example, the names `msig.cyber`, `domain.cyber` can be formed from `cyber`). In this case the only direct inheritance of domain names is allowed. It means that if the owner has created a domain for the second level, a domain for the third level can’t be created (for example, the name `a.msig.cyber` can’t be formed from `cyber`).
+ * to create her/his own domain name using the operation `newdomain` and become its owner.
+ * to create subdomain names from it by adding the «dot» symbol and а name to the domain name on the left (for example, an owner of the domain `golos.io` can create subdomains such as `api.golos.io`, `ws.golos.io` and etc). In this case the only direct inheritance of domain names is allowed. It means that if the owner has created a domain for the second level, a domain for the third level can’t be created.
> **Note:**
-> The actions of the `cyber.domain` smart contract were originally intended for the distribution of domain names at auction. Later to this group of actions were added other actions, including such as creating a domain, transferring a domain, linking (or unlinking) a domain, declaring names used in transactions and creating a user name.
+> The *account names* are formed by adding parts **to the right** (for example, `cyber.msig`, `cyber.domain` can be formed from `cyber`).
-### The checkwin action
-The `checkwin` action is used to register a domain name owner (a winner) at auction. This action does not require a special call and is called automatically with a certain periodic.
+### checkwin
+The `checkwin` action is used to register a domain name owner (a winner) at auction. This action does not require a special call and is called automatically when either `biddomain` or `biddmrefund` is performing.
The `checkwin` action has the following form:
```cpp
[[eosio::action]] void checkwin();
```
-This action has no input parameters. One has to have `cyber.domain` smart contract rights to call this action.
+This action has no input parameters and can be performed by any account.
-### The biddomain action
+### biddomain
The `biddomain` action allows an account to bid at the auction. The action has the following form:
```cpp
[[eosio::action]] void biddomain(
@@ -55,14 +61,14 @@ The `biddomain` action allows an account to bid at the auction. The action has t
asset bid
);
```
-Parameters:
-`bidder` — an account name which bids at the action;
-`name` — a domain name (a string value in accordance with the requirements) on which the bid is done;
-`bid` — a structure value that specifies the bid.
+**Parameters:**
+ * `bidder` — an account name which bids at the action.
+ * `name` — a domain name (a string value in accordance with the requirements) on which the bid is done.
+ * `bid` — a bid (in system tokens, `asset` structure).
-To perform this action the `cyber.domain` smart contract account should have the rights of the `bidder` account. If a bid with a bigger value appears at the auction, the bid with a smaller value is returned to the `bidder` account. The refunds are made automatically by internal calling `biddmrefund` from `biddomain`.
+To perform this action the `cyber.domain` contract account should have the rights of the `bidder` account to call `transfer` action in `cyber.token` contract. If a bid with a bigger value appears at the auction, the previous bid is returned to the `bidder` account. The refunds are made automatically by internal calling `biddmrefund` from `biddomain`.
-### The biddmrefund action
+### biddmrefund
The action `biddmrefund` is used to return a bid which was made at the auction to purchase a domain name if a higher bid is made for the same domain name. The action has the following form:
```cpp
[[eosio::action]] void biddmrefund(
@@ -70,13 +76,13 @@ The action `biddmrefund` is used to return a bid which was made at the auction t
domain_name name
);
```
-Parameters:
-`bidder` — the account name to which the funds are returned from the auction;
-`name` — the domain name for which the bid was made.
+**Parameters:**
+ * `bidder` — the account name to which the funds are returned from the auction.
+ * `name` — the domain name for which the bid was made.
In case of exceptional situations (a network failure or an error in the node's operation), the `biddmrefund` action can be called apart from calling `biddomain` (non-standard calling `biddmrefund`).
-### The newdomain action
+### newdomain
The `newdomain` action is used to create a new domain name. The action has the following form:
```cpp
[[eosio::action]] void newdomain(
@@ -84,43 +90,22 @@ The `newdomain` action is used to create a new domain name. The action has the f
domain_name name
);
```
-Parameters:
-`creator` — account name that creates a domain name;
-`name` — domain name being created.
+**Parameters:**
+ * `creator` — account name that creates a domain name.
+ * `name` — domain name being created.
The `newdomain` action can be used:
- * to create domain name by the system account `cyber.domain`;
- * to create domain name by a winner of the domain name auction;
+ * to create domain name by the system account `cyber.domain` (without an auction).
+ * to create domain name by a winner of the domain name auction.
* to create a sub-domain name by the owner of a direct parent domain.
-The smart contract account `cyber.domain` must be privileged or have a permission to transfer funds from `cyber.namesaccount` (in case of a refund). Upon completion of this action, the `creator` account becomes the owner of the created domain name.
+The smart contract account `cyber.domain` must be privileged or have a permission to transfer funds from `cyber.names` (in case of a refund). Upon completion of this action, the `creator` account becomes the owner of the created domain name.
-## Declarations of the names used in transactions
-It is not enough for one domain name to define a user name. The matter is, the user name is linked to both the owner account and the domain account (usually, it is a smart contract) and has a structure of the form `name@domain`. The domain part defines a scope for the `name`. The accounts with the same name can exist in different scopes. There are several variants that support `username` data and allow a textual representation of a user name to match an account name.
-**Restrictions imposed on a user name value**
- The following requirements are imposed on a user name structure:
- * a total number of characters in a user name should not exceed 32 pcs;
- * a user name can consist of individual parts separated by the symbol «dot»;
- * two «dot» symbols near each other are not allowed;
- * the valid characters in a user name should be alphanumeric, a symbol «hyphen» could be used as well;
- * the capital letters in a user name are unacceptable;
- * a symbol «hyphen» should not be at the beginning or end of any user name part.
-
-### The declarenames declaration
-The `declarenames` declaration is used to submit a list of structures with information about the domain names (or user names) used in transactions and their respective accounts. The declaration has the following form:
-```cpp
-[[eosio::action]] void declarenames(
- vector domains
-);
-```
-Parameter:
-`domains` — an array of descriptions, each description of which is a structure in form of `name_info`.
+## Username operations
-The `declarenames` declaration can be created by any account.
-
-### The newusername declaration
-The `newusername` declaration is used to create a user name. When creating the user name, three fields are used — `creator`, `owner`, `name`. The `newusername` declaration has the following form:
+### newusername
+The `newusername` action is used to create a user name. The `newusername` declaration has the following form:
```cpp
[[eosio::action]] void newusername(
name creator,
@@ -128,17 +113,18 @@ The `newusername` declaration is used to create a user name. When creating the u
username name
);
```
-Parameters:
-`creator` — an account name in scope of which the user name is created;
-`owner`— an account name that is the domain name owner;
-`name` —a string representation of the user name to be created.
+**Parameters:**
+ * `creator` — an account name in scope of which the user name is created.
+ * `owner`— an account name that is to be the domain name owner.
+ * `name` — a string representation of the user name to be created.
+
+The transaction containing the operation `newusername` must be signed by `creator` account.
-The lists of structures with information about the names used and the corresponding accounts are passed to the `newusername` declaration. The `newusername` declaration can be created by any account.
-The transaction requires a signature from the account `creator`.
+## Domain operations
-### The passdomain declaration
-The `passdomain` declaration is used to transfer a domain name from one account to another one (to change the domain name owner). The `passdomain` declaration has the following form:
+### passdomain
+The `passdomain` action is used to transfer a domain name from one account to another one (to change the domain name owner). The `passdomain` declaration has the following form:
```cpp
[[eosio::action]] void passdomain(
name from,
@@ -146,16 +132,16 @@ The `passdomain` declaration is used to transfer a domain name from one account
domain_name name
);
```
-Parameters:
-`from` — an account name from which a domain name is transferred and which owns it;
-`to` — an account name to which the domain name is transferred and which will be a new domain name owner;
-`name` — a string representation of the domain name to be transferred.
+**Parameters:**
+ * `from` — an account from which a domain name is transferred and which owns it.
+ * `to` — an account to which the domain name is transferred and which will be a new domain name owner.
+ * `name` — a string representation of the domain name to be transferred.
The transaction requires a signature from the account `from` that is the domain name owner.
-### The linkdomain declaration
-The `linkdomain` declaration is used to link a domain name to account name. The `linkdomain` declaration has the following form:
+### linkdomain
+The `linkdomain` action is used to link a domain name to account name. After linking, the account can be found by domain name instead of account name. The `linkdomain` declaration has the following form:
```cpp
[[eosio::action]] void linkdomain(
@@ -164,30 +150,48 @@ The `linkdomain` declaration is used to link a domain name to account name. The
domain_name name
);
```
-Parameters:
-`owner` — an account name that was a domain name owner;
-`to` — an account name that becomes new a domain name owner;
-`name` — a string representation of the domain name to be linked.
+**Parameters:**
+ * `owner` — an account that is a domain name owner.
+ * `to` — an account account to which the domain name is linked.
+ * `name` — a string representation of the domain name to be linked.
-### The unlinkdomain declaration
-The `unlinkdomain` declaration is used to remove domain name link from account name (unlinking a domain name from the account name). The `unlinkdomain` declaration has the following form:
+### unlinkdomain
+The `unlinkdomain` action is used to remove domain name link from account name (unlinking a domain name from the account name). The `unlinkdomain` declaration has the following form:
```cpp
[[eosio::action]] void unlinkdomain(
name owner,
domain_name name
);
```
-Parameters:
-`owner` — an account name that was domain name owner (the domain name link is removed from the account name);
-`name` — a string representation of the domain name to be unlinked.
+**Parameters:**
+ * `owner` — an account that is a domain name owner.
+ * `name` — a string representation of the domain name to be unlinked.
+
+After performing `unlinkdomain`, the linked domain name will not point to the account.
-**An example of using the declaration linkdomain and unlinkdomain**
-Let account to have a contract and be assigned a hard-to-remembered name `ajhgsd23qw`. To eliminate this drawback, this account uses `linkdomain` to link the domain name `helloworld` to its contract. It is easier for users to remember such domain as `@helloworld` and send transfers to this name. In contrast to sending transfers to the account name `ajhgsd23qw` the domain name `@helloworld` will be converted to `ajhgsd23qw` and the `declarenames` action will be added to the transaction indicating the used domain names.
+### Examples of using the linkdomain, unlinkdomain and newusername
+Let an account on which the contract is installed has a hard-to-remembered name `ajhgsd23qw`. To eliminate this drawback, this account buys the `hello` domain name and links it to its contract using` linkdomain`. It is easier for users to remember such domain as `@hello` and send transfers to this name. In contrast to sending transfers to the account name `ajhgsd23qw` the domain name `@hello` will be converted to `ajhgsd23qw` and the `declarenames` action will be added to the transaction indicating the used domain names.
-To stop using the domain name `@helloworld` the account has to call the `unlinkdomain` action. After that, the domain name `@helloworld` will not be converted to `ajhgsd23qw` and sending a transfer to it will be impossible.
-
-The `ajhgsd23qw` account in its environment can also create a user name for itself, for example,` admin`. In this case, the name `admin@@helloworld` will be converted to the name` ajhgsd23qw`. When adding a domain name, it is possible to use `admin@helloworld`.
+To stop using the domain name `@hello` the account has to call the `unlinkdomain` action. After that, the domain name `@hello` will not be converted to `ajhgsd23qw` and sending a transfer to it will be impossible.
+The `ajhgsd23qw` account in its environment can create usernames. For example, thic account can take the name `admin` for owan use, and can assign the short name `t` for the contract` cyber.token`. In this case, the name `admin@@ajhgsd23qw` (note the double `@@`) will be resolved in the name `ajhgsd23qw`, and `t@@ajhgsd23qw` in `cyber.token`. When adding a domain name, it is possible to use `admin@hello` and ` t@hello` respectively.
+
+## Declarations of the names used in transactions
+It is not enough for one domain name to define a user name. The matter is, the user name is linked to both the owner account and the domain account (usually, it is a smart contract) and has a structure of the form `name@domain`. The domain part defines a scope. The accounts with the same name can exist in different scopes. There are several variants that support `username` data and allow a textual representation of a user name to match an account name.
+
+### declarenames
+The `declarenames` action is used to submit a list of structures with information about the domain names (or user names) used in transactions and their respective accounts. The action has the following form:
+```cpp
+[[eosio::action]] void declarenames(
+ vector domains
+);
+```
+**Parameter:**
+ * `domains` — an array of descriptions, each description of which is a structure in form of `name_info`.
+
+The `declarenames` action can be called by any account.
+
+
### The name_info struct declaration
The `name_info` structure is used to check the presence of all elements at the time of executing the procedure, as well as the existence of linking a domain name with the specified account. The `name_info` structure declaration has the following form:
@@ -198,24 +202,27 @@ struct name_info {
vector users;
};
```
-Parameters:
-`domain` — a domain name;
-`account` — an account name matching to the domain name;
-`users` — a list of domain name users.
+**Parameters:**
+ * `domain` — a domain name.
+ * `account` — an account name matching to the domain name.
+ * `users` — a list of domain name users.
-The input to the `declarenames` declaration is an array of structures. A number of accepted structures should not be equal to zero. An error occurs when attempting to send an empty array.
+The input to the `declarenames` declaration is an array of structures. This array should not be empty.
There are no matches for user names because a user name, unlike domain name, does not change its account for the entire time.
-The following additional restrictions are introduced:
- * the structures must be sorted by domain name, that is, domain names must be arranged in ascending order. The exception is for a domain name that is an empty string. This is possible when the user name is not specified by the domain name, but explicitly in the smart contract. In this case, sorting should be done performed by account name — by field `.account`;
- * domain name must not contain two identical values `.domain`, except for empty values (“ ”);
- * the field `.account` in the domain name must not contain an empty value.
+The `domains` array should satisfy the following requirements:
+ * the list should not contain two structures with the same domain name (field `.domain`), except for empty values (“ ”).
+ * the field `.account` must not contain an empty value.
-### Notes
- * Along with `declarenames` for internal use, there may be short account names as well. Such short names are replaced with the real account names. The conversion of internal short names to real account names is performed at the explorer level.
+### Notes
* This implementation does not verify that the accounts specified in `declarenames` exist in other actions. The implementation of validation is difficult because the actual validation process requires a lot of resources to read the ABI file format and deserialize the action. A lack of such control causes a certain risk. That is a sender may add more information to the transaction, so the superfluous part of it will not be checked. As a result, it will have to pay extra for overused `bandwidth` resources.
- * This implementation does not contain information about positions of the used domain or user names. A transaction may contain an action with several account names as arguments, for example, `someaction(name 1, name 2, name 3, name 4, name 5)`. If user sends an action like `someaction(acc1, one@golos, two@golos, acc1, three@golos)` and it converts to (`someaction(acc1, acc1, acc1, acc1, acc3)` ), then `declarenames` will contain only used user names or domain names. At the same time, it is impossible to determine the positions where user names were used, for example:
+ * This implementation does not contain information about positions of the used domain or user names.
+
+### Example 1
+A transaction may contain an action with several account names as arguments, for example:
+ `someaction(name 1, name 2, name 3, name 4, name 5)`.
+If a user sends an action like `someaction(acc1, one@golos, two@golos, acc1, three@golos)` and it converts to (`someaction(acc1, acc1, acc1, acc1, acc3)` ), then `declarenames` will contain only used user names or domain names. At the same time, it is impossible to determine the positions where user names were used, for example:
```
declarenames([{
domain:"golos",
@@ -223,15 +230,20 @@ The following additional restrictions are introduced:
users:["one","two","three"]
}])
```
- * Implemented support for the names like `username@@account` ( a presence of the symbols «@@» in the name means that the right side is not a domain, but the account name). For example, if a transaction converts view names `alice@@token`, `bob@@token`, `admin@@golos.io`, then the `declareames` declaration will contain the following arguments:
+
+### Example 2
+Implemented support for the names like `username@@account` ( a presence of the symbols «@@» in the name means that the right side is not a domain, but the account name). For example, if a transaction converts view names `alice@@token`, `bob@@token`, `admin@@golos.io`, then the `declareames` declaration will contain the following arguments:
```
declarenames([
{domain:"", account:N(golos.io), users:["admin"]},
{domain:"", account:N(token), users:["alice","bob"]}
])
```
+
+In the above case, the `users` in ` declarenames` indicate the usernames used, and in the `account` - the scope where these users are registered. Although, the final accounts into which names are resolved are not specified, an explorer (or some other application) can always identify required account via using the pair `username` +` scope`.
+
## Long domain names
-When an account is created it is assigned a base32-encoded 8-byte identification name, which is a 12.5-character string. 60 bits are allocated for 12 characters. The remaining 4 bits are reserved for an additional symbol from the set {1,2,3,4, a, b, c, d, e, f, g, h, i, j}.
+When an account is created it is assigned a base32-encoded 8-byte identification name, which is a 12.5-character string. 60 bits are allocated for 12 characters. The remaining 4 bits are reserved for an additional symbol from the set {1, 2, 3, 4, a, b, c, d, e, f, g, h, i, j}.
A user can also assign her/his domain name to an account. The length of each part of the domain name should not exceed 63 characters. The number of domain names assigned to an account can be more than one. Since a fee is charged for storing a domain name, the number of domain names is limited and depends on the user funds.
diff --git a/docs/en-US/cyber.govern_contract.md b/docs/en-US/cyber.govern_contract.md
new file mode 100644
index 000000000..cef8a6ff6
--- /dev/null
+++ b/docs/en-US/cyber.govern_contract.md
@@ -0,0 +1,48 @@
+# Govern Smart Contract
+
+## Purpose of the cyber.govern smart contract development
+
+The `cyber.govern` system smart contract contains the logic for selecting active validators and distributing remuneration for block production in accordance with the DPoS provisions.
+
+The cyber.govern smart contract includes the `onblock` action.
+
+## onblock action
+
+The `onblock` action is a system operation and is not accessible to an external user. It is called by the `cyber.bios` smart contract. The operation contains the logic of block production, including the logic of token issuance and the appointment of validators. This action has the following form:
+```
+[[eosio::action]] void onblock(name producer);
+```
+`producer` parameter is the name of the validator that succeed producing the next block.
+
+The `cyber.bios` smart contract activates the `onblock` action after each block produced by validators. Calling `onblock` requires the authorization of the `cyber.govern` smart contract account.
+
+## Main regulations concerning the validators
+
+### Validator status
+
+ * The status of a validator candidate can be obtained by any user who has signed up, switched to a zero proxy level and has his own stake in the amount of at least 50,000 CYBER as well as a server (node) that meets the requirements specified in [manual](https://cyberway.gitbook.io/en/validators/quick_reference).
+ * The validators are selected from the candidates by users via voting (read more about [voting process](https://cyberway.gitbook.io/en/validators/voting_for_validators)).
+ * The validators are divided into two groups according to the voting results — the main validators who received the biggest number of votes and reserved candidates.
+
+### Active validators
+
+ * Block production-wise, a schedule of active validators is automatically compiled, which includes all the main validators and one reserve candidate.
+ * The selection of a reserve candidate for the schedule of active validators takes place according to the following rules. The priority of the candidate is calculated in accordance with the established formula, taking into account the number of votes cast for him and the time interval since his last appearance in the schedule of active validators.
+ * Active validators produce blocks alternately according to the schedule.
+ * The schedule of active validators is updated periodically every 4×A blocks, where A is the current number of active validators.
+ * The number of active validators changes upwards from 21 to 101 inclusive.
+ * The number of active validators increases by one if two conditions are met:
+ * at least 14 days have passed since the last increase in the number of active validators;
+ * the number of votes cast for the main validators was less than 90 % of the total number of votes cast for all candidates.
+
+
+### Validator rewards
+
+ * The validator is rewarded for blocks produced by him in accordance with the provisions of DPoS and taking into account the annual issue of tokens. Funds generated from the annual issue of tokens are distributed in the following fractions:
+ * 10 % goes to reward pool for validators for block production;
+ * 20 % goes to reward pool for workers;
+ * 70 % goes to the stake pool that is distributed between validators and users that voted for them.
+
+ * Validator rewards are distributed in the following proportions:
+ * 10 % from the validators pool goes to a validator, as well as commissions that he/she sets, which are taken from the stake pool;
+ * the rest of the stake pool is distributed between the proxy and generic accounts that voted for this specific validator.
diff --git a/docs/en-US/cyber.multi-signature_contract.md b/docs/en-US/cyber.multi-signature_contract.md
new file mode 100644
index 000000000..9970a25d1
--- /dev/null
+++ b/docs/en-US/cyber.multi-signature_contract.md
@@ -0,0 +1,179 @@
+# Multi-Signature Smart Contract
+
+## Purpose of the cyber.msig smart contract
+
+The `cyber.msig` system smart contract is used to both sign and send multi-signature transactions to the blockchain, as well as to offer the transaction to another user for signing.
+
+The `cyber.msig` smart contract includes such actions as [propose](#propose), [approve](#approve), [unapprove](#unapprove), [cancel](#cancel), [exec](#exec) and [invalidate](#invalidate).
+
+## Terminology used
+
+**Multi-signature transaction** — a transaction for the performance of which it is necessary to obtain permission (signature) from one or more accounts (actors) whose names are indicated in a separate list during its formation. The transaction can include one or more operations. The transaction is sent to the blockchain only after obtaining the number of permissions (signatures) necessary for its execution.
+
+**Multi-signature transaction actor** — the name of the account which you need to obtain permission from to perform a multi-signature transaction. An actor can be an individual user or a company account. Only users entrusted to it specified during its registration in the system with a certain permission level can give permission on behalf of the company.
+
+**Transaction Permission** — the signature of the actor which has the form of a structure containing two fields — the name of the account and the required level of permission.
+
+**Permission level** — significance of the signature (authorization) which is assigned to an account during registration. Authorization has the form of a structure containing fields that uniquely identify the signature belonging to a particular account, taking into account its weight and the key being used for signing.
+```cpp
+struct authority {
+ uint32_t threshold = 0;
+ std::vector keys;
+ std::vector accounts;
+ std::vector waits;
+}
+```
+Depending on the permission level set in the transaction, the number of signatures required to complete the transaction may vary. For example, permission on behalf of a company representative permission with the required `threshold=3` is considered to be obtained if the signature is affixed either with one account with the same threshold value, or with two accounts with values of «2» and «1», or three with «1».
+
+## Actions
+
+### propose
+The `propose` action is used to create a multi-signature transaction offer (proposed transaction), which requires permission from third-party accounts.
+```cpp
+void multisig::propose(
+ name proposer,
+ name proposal_name,
+ std::vector requested,
+ transaction trx
+)
+```
+**Parameters:**
+ * `proposer` — name of account, author of a multi-signature transaction.
+ * `proposal_nam`e — the unique name assigned to the multi-signature transaction when it is created. This parameter, in conjunction with the proposer parameter, uniquely identifies a multi-signature transaction.
+ * `requested` — the unique name assigned to the multi-signature transaction when it is created. This parameter, in conjunction with the proposer parameter, uniquely identifies a multi-signature transaction.
+ * `trx` — proposed transaction.
+
+For calling the propose action the authorization of proposer user is obligatory.
+
+**For example:**
+The `alice`, `bob`, and `carol` accounts created a common multisig account, for which transactions on behalf of which 2 signatures from 3 are sufficient. To work with this account, `bob` and `alice` decided to use separate keys, adding them to the `msig` permission. Now, to send a transaction to the network, `alice` can offer it for signing, indicating all three participants (with the required permissions) in the requested list:
+```cpp
+cyber.msig::propose(
+ alice,
+ sendtofund,
+ [alice@msig, bob@msig, carol@active],
+ {actions:[{account:token, name:transfer, authorization:[multisig@active], …}], …}
+)
+```
+
+> **Side note**
+> After the multi-subscription transaction is completed, its name `proposal_name` can be reused for other transactions (by the `proposer` account). However, this `proposal_name` cannot be reused unless permission was not obtained to execute a multi-subscription transaction.
+
+### approve
+The `approve` action is used to send permission from an account in the requested list to execute a multi-subscription transaction.
+```cpp
+void multisig::approve(
+ name proposer,
+ name proposal_name,
+ permission_level level,
+ const eosio::binary_extension& proposal_hash
+)
+```
+**Parameters:**
+ * `proposer` — name of account, author of a multi-signature transaction..
+ * `proposal_name` — The unique name assigned to the multi-signature transaction when it is created. This parameter, in conjunction with the proposer parameter, uniquely identifies a multi-signature transaction.
+ * `level` — permission by which the account gives its consent to the implementation of the transaction proposed for signing.
+ * `proposal_hash` — optional parameter, «hash amount» of the multi-signature transaction. The parameter is used to control the occurrence of changes in a multi-signature transaction since the call to propose. Level permission is not issued in the event of a «hash amounts» mismatch specified by proposal_hash and obtained for an incoming transaction.
+
+Authorization of the account specified in the level parameter is required to call the approve action.
+
+**For example:**
+In order to approve the transaction proposed by `alice`, member accounts must approve with the permission specified in propose and sign the transaction with a signature that satisfies this permission. For greater security, the `proposal_hash` parameter can be used:
+```
+cyber.msig::approve(alice, sendtofund, alice@msig, hash)
+cyber.msig::approve(alice, sendtofund, bob@msig)
+cyber.msig::approve(alice, sendtofund, carol@active)
+```
+
+### unapprove
+The `unapprove` action is used by an account in the requested list to revoke permission previously granted by this account to execute a multi-signature transaction in case of a change in decision.
+```cpp
+void multisig::unapprove(
+ name proposer,
+ name proposal_name,
+ permission_level level
+)
+```
+**Parameters:**
+ * `proposer` — name of account, author of a multi-signature transaction.
+ * `proposal_name` — the unique name assigned to the multi-signature transaction when it is created. This parameter, in conjunction with the proposer parameter, uniquely identifies a multi-signature transaction.
+ * `level` — revoked permission
+
+To call `unapprove` action, authorization of the account specified in the level parameter is required.
+
+**For example:**
+To withdraw approval, `alice` should unapprove with the permission specified in propose and sign the transaction with a signature that satisfies this permission:
+```cpp
+cyber.msig::unapprove(alice, sendtofund, alice@msig)
+```
+
+### cancel
+The `cancel` action is used to cancel a multi-signature transaction offer.
+```cpp
+void multisig::cancel(
+ name proposer,
+ name proposal_name,
+ name canceler
+)
+```
+**Parameters:**
+ * `proposer` — name of account, author of a multi-signature transaction.
+ * `proposal_name` — the unique name assigned to the multi-signature transaction when it is created. This parameter, in conjunction with the proposer parameter, uniquely identifies a multi-signature transaction.
+ * `canceler` — the name of the account that cancels its execution.
+
+A certain amount of time is allotted for a multi-signature transaction. If after this time the transaction does not complete, it can be canceled by any user. Only author is authorized to cancel a multi-subscription transaction before the time allotted for its execution expires, for example, after making an adjustment to it. In this case, the canceler parameter will match the proposer parameter.
+
+To call the cancel operation, the authorization of the account specified in the canceler parameter is required.
+
+**For example:**
+At the moment, the transaction has 2 necessary signatures, but `alice` has made a decision not to send it to the network (see examples above). She can cancel the transaction as the creator of the proposed transaction:
+```cpp
+cyber.msig::cancel(alice, sendtofund, alice)
+```
+Any user can cancel an expired transaction, thereby restoring STORAGE himself:
+```cpp
+cyber.msig::cancel(alice, sendtofund, anybody)
+```
+
+### exec
+The `exec` action is called to execute a multi-signature transaction.
+```cpp
+void multisig::exec(
+ name proposer,
+ name proposal_name,
+ name executer
+)
+```
+**Parameters:**
+ * `proposer` — name of account, author of a multi-signature transaction.
+ * `proposal_name` — the unique name assigned to the multi-signature transaction when it is created. This parameter, in conjunction with the proposer parameter, uniquely identifies a multi-signature transaction.
+ * `executer` — the name of the account performing the multi-signature transaction. An executor account can be either an actor or any other user who provides payment for the resources used. A transaction is executed if it contains the required number of permissions.
+
+To invoke the `exec` action, authorization of the account specified in the executer parameter is required.
+
+**For example:**
+If the transaction was not canceled in the previous example, then it has the required 2 of 3 permissions, and can be executed by calling the `exec`:
+```cpp
+cyber.msig::exec(alice, sendtofund, bob)
+```
+
+### invalidate
+The `invalidate` action is used to revoke all permissions previously issued by the account for performing multi-signature transactions. The action applies to all proposed transactions that are at the voting stage.
+```cpp
+void multisig::invalidate(name account)
+```
+**Parameter:**
+ * `account` — the name of the account whose previously issued permissions to perform multi-signature transactions must be invalidated.
+
+Calling invalidate action will invalidate all permissions issued by an account named account for ongoing multi-signature transactions. Invalidate does not apply to permissions of already completed transactions, as well as to permissions that will be issued by this account after calling invalidate.
+
+The proposed transaction can still be sent to the network if it contains the necessary number of remaining permissions, taking into account the canceled one.
+
+To invoke an invalidate action, authorization of an account named account is required.
+
+**For example:**
+`carol` learned that her key was stolen. It's time to cancel all proposed transactions (and change the key):
+```cpp
+cyber.msig::invalidate(carol)
+```
+*****
diff --git a/docs/en-US/cyber.stake_contract.md b/docs/en-US/cyber.stake_contract.md
new file mode 100644
index 000000000..efce5e154
--- /dev/null
+++ b/docs/en-US/cyber.stake_contract.md
@@ -0,0 +1,375 @@
+# The cyber.stake smart contract
+
+The `cyber.stake` system smart contract provides voting methods for validators, which allow users to use their votes, as well as resources on balance sheets, most effectively. `Cyber.stake` also provides the user with the ability to authorize another user to vote.
+
+## Terminology used
+**Agent** — is a potentially active user who has a balance in the `cyber.stake` smart contract.
+
+**Votes for a validator** — a certain number of staked tokens given to the validator as votes during a voting (one token equals to one vote).
+
+**Delegator** — a user who delegates part of his stake (RAM, CPU, NET, Storage resources) or an authorization to vote to another user during the voting process.
+
+**Proxy account** — a user empowered with a specific voting authorization during the voting process. Any user can declare himself a proxy account in order to receive votes from other users and use these votes at his own discretion during the voting for validators. In that case the user must set the proxy account level by calling `setproxylvl`.
+
+A user who does not participate in the voting for validators regardless of reason, can authorize the proxy account to vote instead of him. A proxy account can also empower another proxy account in order to vote on his behalf.
+
+**Proxy account level** — a conditional division of users into categories. Each user category is assigned a specific proxy account level. The highest level of proxy account is zero, which is assigned only to validators. The first and further levels in ascending order are assigned to users who have declared themselves proxy accounts. The number of proxy accounts of the same level is also not limited. The last level of proxy account is assigned to the ordinary user. A proxy account can only be configured at only one of the following levels (**please note: in the current release, the number of proxy account levels is limited to three**):
+ * A validator is considered as a zero-level proxy account.
+ * First level proxy account — a user who has declared himself a proxy account, empowered with the votes of ordinary users at his disposal during the voting process.
+ * The second level proxy account is an ordinary user. The ordinary user does not have to worry about what level of proxy he/she needs to set. The user is automatically assigned to the lowest-rated proxy level.
+
+The proxy accounts’ separation by levels was introduced in order to avoid the isolation when transferring the voting right from one user to another (for example, user A can transfer his vote to user B, and user B can transmit the vote received to user C. If the latter transfers the received votes back to user A, it will lead to the abruption of transmissions. The concept of proxy account levels was introduced to exclude the appearance of isolation during the transfer of votes. Transfer of voting rights is open only to a lower level proxy account.
+
+**Stake** — a share of bandwidth resources (RAM, NET, CPU, and Storage) allocated to a user. The user can manage the share of resources allocated to him both independently and entrust its use to another user (delegate the share of resources).
+
+**Staked tokens** — the tokens allocated for a stake acquisition that can’t be used for anything else in this state. The user can stake active tokens listed on his/her balance or deposit them. Also, the user can perform the reverse operation — withdraw tokens from the staked state to active.
+
+## cyber.stake smart contract actions
+The following actions are assemble the `cyber.stake` smart contract: [create](#create), [enable](#enable), [open](#open), [delegatevote](#delegatevote), [setgrntterms](#setgrntterms), [recallvote](#recallvote), [withdraw](#withdraw), [setproxylvl](#setproxylvl), [setproxyfee](#setproxyfee), [setminstaked](#setminstaked), [setkey](#setkey), [updatefunds](#updatefunds), [reward](#reward), [pick](#pick), [delegateuse](#delegateuse), [recalluse](#recalluse) and [claim](#claim). Additionally, the following inline functions can be found in this smart contract: [get_votes_sum](#get_votes_sum) and [get_top](#get_top).
+
+## create
+The create action is used to create a stake for a separate type (symbol) of tokens. After creating the stake, tokens of this type can be transferred by users to the state of the stake.
+```cpp
+void create(
+ symbol token_symbol,
+ std::vector max_proxies,
+ int64_t depriving_window,
+ int64_t min_own_staked_for_election
+)
+```
+**Parameters:**
+ * `token_symbol` — symbol of the token for which the stake is being created.
+ * `max_proxies` — maximum allowed proxy account values. The zero element of the vector sets the maximum number of users who can be trusted in the voting if the proxy account level is the first. The first element of the vector sets the maximum number of parts into which the stake can be divided if the level of the proxy account is second. If there are fourth or more levels in the proxy system, the vector will contain three or more elements.
+ * `depriving_window` — the length of the period (in seconds) during which funds withdrawn back to the delegate cannot be used by him. Funds can be recalled by `recallvote` operation call. Withdrawn funds during this period cannot be taken into account when calculating the share of resources.
+ * `min_own_staked_for_election` — the minimum number of tokens staked which the user must have in his/her repository to be a candidate for validators.
+
+Generic users can vote for one validator either transfer the authorization for voting to only one proxy account.
+
+A proxy account can simultaneously vote for several validators. The maximum number of validators for which the proxy account can vote is 30.
+
+Calling create requires the rights of the creator of the `token_symbol` token. The token creator, by calling create, allows other users to translate tokens of this type into the state of the stake. When creating a stake for a system token, you must have system rights.
+
+## enable
+The `enable` action allows to deduce a certain type of token from use for a while. This action sets the enable flag to true.
+```cpp
+void enable(symbol token_symbol)
+```
+The `token_symbol` parameter is the symbol of the token affected by the enable flag set to true.
+
+This action is used in testing to disable the use of tokens of a certain symbol. During testing, the enable flag is set to false, that is, the resource limit for a certain type of token is disabled. At the end of testing, the flag should be set back to true. If `token_symbol` is a system one, then when the enable flag is set on the node, the restriction on the use of resources by stake is enabled.
+
+The rights of the `token_symbol` token creator are required to call this action.
+
+## open
+The `open` action is used to create a balance for a user.
+```cpp
+void open(
+ name owner,
+ symbol_code token_code,
+ std::optional ram_payer = std::nullopt
+)
+```
+**Parameters:**
+ * `owner` — username for which balance is being created.
+ * `token_code` — symbol of the token that will be listed on the balance.
+ * `ram_payer` — name of the account paying for opening the balance. If the parameter accepts the default value `std::nullopt`, then the owner himself pays for opening the balance.
+
+A transaction, containing the `open` action should be signed by the user who pays for opening a balance. Owner rights are being requested by default.
+
+## delegatevote
+The `delegatevote` action is used to delegate to another account the right to dispose of staked tokens (in whole or in part) when voting for validators.
+```cpp
+void delegatevote(
+ name grantor_name,
+ name recipient_name,
+ asset quantity
+)
+```
+**Parameters:**
+ * `grantor_name` — an account who delegates the right to dispose of a steak. This is either an ordinary user or a proxy account whose proxy level higher than that of a recipient.
+ * `recipient_name` — name of the proxy account that is trusted with the voting right.
+ * `quantity` — number of staked tokens that the proxy `recipient_name` can use when voting. The parameter must be greater than zero.
+
+If a user for some reason can not vote for validators, he/she can use the services of a proxy account. In this case this user has to delegate to this proxy a certain number of staked tokens. These tokens (stake) can be used only when voting for validators.
+
+Depending on the settings of the stake, the right to vote on the use of stake tokens can be transferred further (by calling `delegatevote`) to the ёrecipient_nameё recipient, whose `proxy_level` proxy level is lower than that of the sender `grantor_name`.
+
+> **Note:** Since in the current release the number of levels of proxy accounts is limited to three (zero is a validator, the first is directly a proxy account, the second is an ordinary user), voice transfer from one proxy account to another is not possible. Having received a vote from an ordinary user, a proxy account can use it only when voting for validators.
+
+The stake is divided into several parts. The number of shares depends on the settings of the stake during the create action and is limited by the value of max_proxies. this value is equal to one for an ordinary user. Therefore the user can vote for one validator only.
+
+A transaction, containing the `delegatevote` action should be signed by the `grantor_name` account.
+
+## recallvote
+The `recallvote` action is used to withdraw the right to use a delegated stake when voting for validators. The steak value is set as a percentage and can be withdrawn either partially or completely.
+```cpp
+void recallvote(
+ name grantor_name,
+ name recipient_name,
+ symbol_code token_code,
+ int16_t pct
+)
+```
+**Parameters:**
+ * `grantor_name` — name of the account that withdraws the right to use the stake.
+ * `recipient_name` — name of the account from which the right to use the stake is withdrawn.
+ * `token_code` — staked token symbol.
+ * `pct` — share of stake (in percent), the right to use of which is revoked.
+
+The recalled share of the stake can be used by the `grantor_name` account immediately after the `recallvote` operation is completed.
+
+A transaction, containing the `recallvote` action should be signed by the `grantor_name` account.
+
+## setgrntterms
+The `setgrntterms` action is used to distribute additionally allocated staked tokens between validators when voting. These tokens are distributed in accordance with the specified proportions..
+```cpp
+void setgrntterms(
+ name grantor_name,
+ name recipient_name,
+ symbol_code token_code,
+ int16_t pct,
+ int16_t break_fee,
+ int64_t break_min_own_staked
+)
+
+```
+**Parameters:**
+ * `grantor_name` — name of the account that delegates the extra share of stake.
+ * `recipient_name` — name of the proxy account, recipient of the extra share of staked tokens.
+ * `token_code` — staked token symbol.
+ * `pc`t — share (in percent) of additional votes received, which will be given for the `recipient_name` candidate.
+ * `break_fee` — the parameter that controls the change in the fee set by the candidate. The value of this parameter is the percentage of commission charged by the `grantor_name` account for the `recipient_name` candidate. If the candidate during the voting process sets the value of fee greater than `break_fee`, the recall operation will be automatically called to recall the votes of this candidate.
+ * `break_min_own_staked` — the parameter that controls the availability of secured tokens in the `recipient_name` candidate. The value of this parameter is set by the `grantor_name` account. By default, this change is set to the current `min_own_staked` value. If the candidate reduces `min_own_staked` (in order to withdraw funds to a liquid state), the next time the balance of the `grantor_name` account is updated, the recall operation will be automatically called to revoke the allocated tokens from this candidate.
+
+When recalling, the condition is checked so that the candidate has funds in the amount of at least `min_own_staked`.
+
+A proxy account can vote simultaneously for several candidates for validators, distributing votes between them in a certain proportion. The proxy account can receive an additional number of votes (staked tokens) and use them until the end of voting process. These additional votes will automatically be given to those candidates for whom the proxy account voted before receiving these votes. Additional votes will be distributed among the candidates taking into account previous proportion .
+
+## withdraw
+The `withdraw` action is used to withdraw staked tokens and make them liquid.
+```cpp
+void withdraw(
+ name account,
+ asset quantity
+)
+```
+**Parameters:**
+ * `account` — name of the account withdrawing staked tokens.
+ * `quantity` — number of tokens withdrawn. This value shpild be positive.
+
+When the `withdraw` action is calling, the `cyber.stake` smart contract sends a notification to token creator, which makes the final decision on the withdrawal of tokens. The withdrawal of tokens is performed immediately without any delay.
+
+In case of withdrawal the system tokens, decision to withdraw these tokens is made by the `cyber.bios` smart contract. The operation is performed if the remainder of the user’s stake covers the costs of the resources used by him taking into account the funds being withdrawn.
+
+A transaction, containing the `withdraw` action should be signed by the `account`.
+
+## setproxylvl
+The `setproxylvl` action sets the proxy account level for a user.
+```cpp
+void setproxylvl(
+ name account,
+ symbol_code token_code,
+ uint8_t level
+)
+```
+**Parameters:**
+ * `account` — name of the account for which a proxy account level is set.
+ * `token_code` — number of staked tokens available to the user.
+ * `level` — level being set.
+
+If a user intends to be a candidate for validators, he/she should set the proxy level to zero.
+If a user intends to register as a proxy account, he/she should set the proxy level to one (in the current release).
+
+A transaction, containing the `setproxylvl` action should be signed by the `account`.
+
+## setproxyfee
+The `setproxyfee` action sets a size of the commission for an agent (a candidate for validators) that users voted for.
+```cpp
+void setproxyfee(
+ name account,
+ symbol_code token_code,
+ int16_t fee
+)
+```
+**Parameters:**
+ * `account` — name of the agent that users have voted for.
+ * `token_code` — symbol of tokens constituting a reward to the agent.
+ * `fee` — amount of commission deductions to be taken from the amount of reward that the agent receives. This parameter is calculated at the time of reward payment. The parameter takes values from «0» to «10000» inclusive («0» corresponds to «0 %», «10000» corresponds to «100 %»).
+
+A fee is deducted from the received reward amount allocated to the agent and transferred to the agent account. The rest of the reward is distributed among all users who voted for this agent.
+
+## setminstaked
+The `setminstaked` action sets the minimum possible size of a steak allocated for voting by an agent (a candidate for validators).
+```cpp
+void setminstaked(
+ name account,
+ symbol_code token_code,
+ int64_t min_own_staked
+)
+```
+**Parameters:**
+ * `account` — name of the agent.
+ * `token_code` — symbol of staked tokens.
+ * `min_own_staked` — minimum possible steak, which is allocated for voting by the agent himself. This value should be positive.
+
+If the candidate for validators sets the value `min_own_staked` less than `min_own_staked_for_election` one during a voting, then the votes cast for him are automatically canceled.
+
+## setkey
+The `setkey` action sets the public key for a validator, which will be used for signing blocks.
+```cpp
+void setkey(
+ name account,
+ symbol_code token_code,
+ public_key signing_key
+)
+```
+**Parameters:**
+ * `account` — name of the validator to which the public key is being set.
+ * `token_code` — symbol of staked tokens.
+ * `signing_key` — the public key (i.e. validator signature).
+
+## updatefunds
+The `updatefunds` action is used to update a user’s stake.
+```cpp
+void updatefunds(
+ name account,
+ symbol_code token_code
+)
+```
+**Parameters:**
+ * `account` — name of the account for which the steak size is being updated.
+ * `token_code` — symbol of staked tokens.
+
+The size of one user's stake may vary. The `updatefunds` action sets the actual value for a user’s stake. This operation simplifies the procedure for calculating user rewards, including share determination for a user. The `updatefunds` action can also be used during other actions, such as revoking tokens or voting. That is, when performing active operations that require updating a stake.
+
+The `updatefunds` action can be called by any user. Authorization is not required.
+
+## reward
+The `reward` action is used to pay rewards to users by crediting the corresponding amounts to their balances.
+```cpp
+void reward(
+ std::vector > rewards,
+ symbol sym
+)
+```
+**Parameters:**
+ * `rewards` — a vector containing account names and the payouts associated to them.
+ * `sym` — symbol of tokens in which the reward is being paid.
+
+A transaction, containing the `reward` action should be signed by the `sym` token creator.
+
+## pick
+The `pick` action updates the entire list of validators, including candidates for validators, and notifies the `cyber.stake` contract about changes in the list, as well as about appearance of a new validator selected from the candidates at a certain time.
+```cpp
+void pick(
+ symbol_code token_code,
+ std::vector accounts
+)
+```
+**Parameters:**
+ * `token_code` — number of tokens held by a candidate, notification of which is sent.
+ * `accounts` — vector of accounts that are current validators, as well as candidates for validators.
+
+Selection of validators from the candidates for validators is carried out in the `cyber.govern` smart contract. The first `n-1` validators from the list (`n` is total number of validators) are selected in accordance with the number of votes cast for them, taking into account the weight of each vote. The last validator from this list is pseudo-random. This validator is selected according to a formula that takes into account the time elapsed since the last validator selection and the number of votes cast for this candidate.
+
+Those candidates who did not become validators, but are listed next to the first `n-1` validators, get the opportunity to appear among these validators after a certain time.
+Candidate priorities are calculated using `get_top` method in the `cyber.govern` smart contract. This method determines the selected candidates who received the most votes and the reserve candidates. The method takes into account the length of stay in the status of a candidate (since when did the candidate remain among the non-elected). Each of the candidates is located in the list of the priority field in accordance with the priority calculated for it.
+
+The `pick` operation is called by the `cyber.govern` smart contract and updates the priority field, moving all candidates in the queue to positions according to their calculated priority, and notifies `cyber.stake` of changes in the candidate list at the current time.
+
+
+A transaction, containing the `pick` action should be signed by the `token_code` token creator
+
+## delegateuse
+The `delegateuse` action is used to delegate resources (RAM, NET, CPU, Storage) to another user, which can be used at user’s discretion (unlike this operation, `delegatevote` delegates resources that can be used only for voting).
+```cpp
+void delegateuse(
+ name grantor_name,
+ name recipient_name,
+ asset quantity
+)
+```
+**Parameters:**
+ * `grantor_name` — name of the account, who is a delegator of a stake.
+ * `recipient_name` — name of the account, who is a recipient of a stake.
+ * `quantity` — amount of delegated stake. This parameter should be positive.
+
+Stake resources such as RAM, NET, CPU, and Storage cannot be delegated in parts as they appear on the system. Therefore, the delegation of stake funds is carried out in value terms. The cost of resources consumed by the delegator is calculated in accordance with current prices and compared with his steak existing.
+
+The delegated steak is not taken into account when votes for the delegator are calculated, but is taken into account in the calculation of votes for the recipient of these funds.
+
+When the `delegateuse` action is calling, the `cyber.stake` smart contract sends a notification to the token creator, who makes the final decision on the stake delegation.
+
+In case of delegating the system tokens, decision to delegate these tokens is made by the `cyber.bios` smart contract. The operation is performed if the remainder of the delegator’s stake covers the costs of the resources used by him taking into account the funds being delegated.
+
+## recalluse
+The `recalluse` action is used to revoke the delegated portion of a stake.
+```cpp
+void recalluse(
+ name grantor_name,
+ name recipient_name,
+ asset quantity
+)
+```
+**Parameters:**
+ * `grantor_name` — name of the account revoking the delegated portion of the stake.
+ * `recipient_name` — name of the account whose delegated portion of the stake is being revoked.
+ * `quantity` — amount of revoked stake. This parameter should be positive.
+
+As soon as the delegated part of the steak is withdrawn from the recipient, it immediately goes into a «frozen» state. The duration of the «frozen» state is determined by the `depriving_window` parameter when the create operation is called. The delegator can use the returned portion of the stake only after this period.
+
+In case, after delegating the funds, only half of these funds are requested back, and then the remaining part is requested, then after the second `recalluse` call, the returned funds can only be used after the `depriving_window` time has passed.
+
+## claim
+The claim action is used to obtain permission to use revoked delegated funds.
+```cpp
+void claim(
+ name grantor_name,
+ name recipient_name,
+ symbol_code token_code
+)
+```
+**Parameters:**
+ * `grantor_name` — name of the account that revokes the delegated portion of the stake
+ * `recipient_name` — name of the account whose delegated portion of the stake is being revoked.
+ * `token_code` — revoked part of the stake. Value should be positive.
+
+After the expiration of the «frozen» state, the withdrawn funds may not be used by the delegator immediately, but only after obtaining the appropriate permission. The claim call requests permission from the cyber.stake smart contract to use funds withdrawn from the «frozen» state.
+
+The operation is tied to resource recovery time. Funds are immediately returned in full.
+
+The delegator can again return the withdrawn funds (or part of the funds) back to the recipient_name account. The funds for redelegation will be debited from the «frozen» funds.
+
+
+To perform the action, authorization of the funds delegator is required.
+
+## get_votes_sum
+The `get_votes_sum` inline function returns the total number of votes available to validator candidates who received the most votes by validator candidates who received the most votes.
+```cpp
+static inline int64_t get_votes_sum(
+ symbol_code token_code,
+ size_t limit = 0
+)
+```
+**Parameters:**
+ * `token_code` — symbol of staked tokens by which votes were determined.
+ * `limit` — the requested number of first candidates from the priority list for which the total number of votes is determined. The first on the list are the candidates who received the most votes. By default, the parameter takes a null value.If `limit` is null, then this action returns total number of votes of all candidates from the `priority` list.
+
+## get_top
+The `get_top` inline function returns a list of the most rated candidates, compiled according to a certain principle.
+```cpp
+static inline std::vector get_top(
+ symbol_code token_code,
+ uint16_t elected_num,
+ uint16_t reserve_num,
+ bool strict = true
+)
+```
+**Parameters:**
+ * `token_code` — symbol of tokens tokens by which the votes were defined.
+ * `elected_num` — the requested number of candidates who received the largest number of votes among those elected as validators.
+ * `reserve_num` — the requested number of reserve candidates who were not included in the list of eligible candidates. The list of reserve candidates includes those candidates who received the most votes among remaining candidates, taking into account the time when each of the candidates did not appear in the list of validators.
+ * `strict` — `true` ignores the votes of candidates who do not have a public key. Default is `true`.
+The `get_top` function returns a list of candidates made up of the requested number of `elected_num` elected to validators and the number of `reserve_num` backups added to it taking into account the strict parameter.
+
+*****
\ No newline at end of file
diff --git a/docs/en-US/cyber.token_contract.md b/docs/en-US/cyber.token_contract.md
index 37d0838cc..9a9697afd 100644
--- a/docs/en-US/cyber.token_contract.md
+++ b/docs/en-US/cyber.token_contract.md
@@ -3,7 +3,7 @@
## Purpose of the cyber.token smart contract development
The system smart contract `cyber.token` provides token management functions, generates new tokens and stores information about created tokens and provides an ability to conduct mutual settlements between accounts as well.
-The `cyber.token` smart contract includes the following actions: [create](#the-create-action), [issue](#the-issue-action), [retire](#the-retire-action), [transfer](#the-transfer-action), [bulktransfer](#the-bulktransfer-action), [payment](#the-payment-action), [bulkpayment](#the-bulkpayment-action), [claim](#the-claim-action), [open](#the-open-action) и [close](#the-close-action).
+The `cyber.token` smart contract includes the following actions: [create](#the-create-action), [issue](#the-issue-action), [retire](#the-retire-action), [transfer](#the-transfer-action), [bulktransfer](#the-bulktransfer-action), [payment](#the-payment-action), [bulkpayment](#the-bulkpayment-action), [claim](#the-claim-action), [open](#the-open-action) and [close](#the-close-action).
## The create action
The `create` action is applied to create a token to supply it into the system. This action has the following form:
diff --git a/docs/ru-RU/cyber.bios_contract.md b/docs/ru-RU/cyber.bios_contract.md
new file mode 100644
index 000000000..5706b4fd1
--- /dev/null
+++ b/docs/ru-RU/cyber.bios_contract.md
@@ -0,0 +1,220 @@
+
+## Назначение смарт-контракта cyber.bios
+
+Системный смарт-контракт `cyber.bios` используется в качестве связующего звена между операциями, часть которых выполняется непосредственно в смарт-контрактах, а часть — в ядре ноды.
+
+В состав смарт-контракта cyber.bios входят операции-действия [newaccount](#newaccount), [setprods](#setprods), [setparams](#setparams), [reqauth](#reqauth), [setabi](#setabi), [setcode](#setcode), [onblock](#onblock), [checkwin](#checkwin), [bidname](#bidname), [bidrefund](#bidrefund), [canceldelay](#canceldelay), [updateauth](#updateauth), [deleteauth](#deleteauth), [linkauth](#linkauth), [unlinkauth](#unlinkauth).
+
+
+## newaccount
+Операция-действие `newaccount` используется для создания новых аккаунтов в сети cyberway.
+
+```cpp
+void bios::newaccount(
+ name creator,
+ name name,
+ authority owner,
+ authority active
+)
+```
+**Параметры:**
+ * `creator` — создатель нового аккаунта.
+ * `name` — имя аккаунта, которое создается в системе.
+ * `owner` — структура вида `authority`, содержащая `owner public key` для нового аккаунта.
+ * `active` — структура вида `authority`, содержащая `active public key` для нового аккаунта.
+
+Созданному аккаунту будут переданы параметры `owner` и `active`.
+
+**Ограничения:**
+ * количество символов в создаваемом (не полученном на аукционе) имени, не должно превышать 12. Также оно не должно содержать символ «точка».
+ * расширение имени (выкупленном на аукционе или созданном через системный аккаунт) допускается добавлением символов справа после символа «точка» (например, владелец имени `cyber` может создать имя `cyber.name`).
+
+Транзакция, содержащая `newaccount`, должна быть подписана создателем нового аккаунта.
+
+## setprods
+Операция-действие `setprods` вызывается системой и используется для составления расписания валидаторов. Расписание содержит список валидаторов в соответствии с их очередностью в производстве блоков.
+
+```cpp
+[[eosio::action]] void setprods(eosio::producer_key schedule )
+```
+Параметр `schedule` — список ключей валидаторов.
+
+Для выполнения транзакции, содержащей операцию `setprods`, требуется подпись аккаунта `cyber`, на котором развернут смарт-контракт `cyber.bios`.
+
+
+## setparams
+Операция-действие `setparams` вызывается системой и используется для настройки параметров системы. Операция имеет вид:
+
+```cpp
+[[eosio::action]] void setparams(eosio::blockchain_parameters params)
+```
+Параметр `params` — структура, содержащая настраиваемые параметры системы.
+
+ Для выполнения транзакции, содержащей операцию `setparams`, требуется подпись аккаунта `cyber`, на котором развернут смарт-контракт `cyber.bios`.
+
+## reqauth
+Операция-действие `reqauth` используется для проверки в транзакции подписи пользователя. Операция имеет вид:
+```cpp
+[[eosio::action]] void reqauth(name from)
+```
+Параметр `from` — имя аккаунта, подписавшего транзакцию, чья подпись проверяется.
+
+Для выполнения транзакции, содержащей операцию `reqauth`, требуется подпись аккаунта `from`.
+
+## setabi
+Операция-действие `setabi` используется для загрузки ABI-описания на аккаунт.
+
+```cpp
+[[eosio::action]] void setabi(
+ name account,
+ std::vector abi
+)
+```
+**Параметры:**
+ * `account` — имя аккаунта, на который загружается ABI-описание.
+ * `abi` — массив байтов, содержащий ABI-описание.
+
+## setcode
+Операция-действие `setcode` используется для загрузки кода смарт-контракта на аккаунт.
+
+```cpp
+[[eosio::action]] void setcode(
+ name account,
+ uint8_t vmtype,
+ uint8_t vmversion,
+ std::vector code
+)
+```
+**Параметры:**
+ * `account` — имя аккаунта, на который загружается код смарт-контракта.
+ * `vmtype` — тип контракта (тип виртуальной машины). В настоящем релизе параметр принимает значение «0».
+ * `vmversion` — версия контракта (версия виртуальной машины). В настоящем релизе параметр принимает значение «0».
+ * `code` — массив байтов, содержащий код смарт-контракта.
+
+## onblock
+Операция-действие `onblock` является недоступной для пользователя и вызывается системой после создания каждого нового блока. Внутри `onblock` реализован вызов кода смарт-контракта `cyber.govern`.
+
+```cpp
+void bios::onblock(block_header header)
+```
+Параметр `header` — заголовок блока.
+
+## checkwin
+Операция-действие `checkwin` контролирует появление победителя на аукционе имён. Операция не имеет параметров и вызывается неявно.
+```cpp
+void bios::checkwin()
+```
+
+## bidname
+Операция-действие `bidname` позволяет сделать ставку на получение определенного имени на аукционе имен.
+```cpp
+void bios::bidname(
+ name bidder,
+ name newname,
+ asset bid
+)
+```
+**Параметры:**
+ * `bidder` — участник аукциона, претендент на получение имени.
+ * `newname` — имя, на получение которого делается ставка.
+ * `bid` — ставка на получаемое имя. Ставки принимаются только в системных токенах (CYBER).
+
+Если ставка `bid` оказывается самой высокой на получение имени `newname` и по истечении 24 часов она не будет перебита, участник аукциона `bidder` будет объявлен победителем и, соответственно, владельцем данного имени. В этом случае ставка `bid` не возвращается обратно участнику `bidder`.
+В случае неуспеха, то есть, если ставка `bid` в течение 24 часов перебивается, она будет возвращена обратно участнику `bidder` (в настоящем релизе автоматического возврата ставки не происходит, для ее возврата требуется вызов `bidrefund`).
+
+## bidrefund
+Операция-действие `bidrefund` используется для возврата невыигрышной (не ставшей самой высокой) ставки участнику аукциона.
+```cpp
+void bios::bidrefund(name bidder)
+```
+Параметр `bidder ` — участник аукциона, которому возвращается ставка.
+
+Если участник аукциона не становится победителем, поставленная им ставка будет ему возвращена. Для возврата ставки участник аукциона должен создать транзакцию с операцией `bidrefund`. Если участником аукциона было поставлено несколько ставок на разные имена и все эти ставки (или часть из них) были невыигрышными, то для возврата всех невыигрышных ставок достаточно однократного вызова `bidrefund`.
+
+## canceldelay
+Операция-действие `canceldelay` отменяет отложенную транзакцию.
+```cpp
+void canceldelay(
+ permission_level canceling_auth,
+ checksum256 trx_id
+)
+```
+**Параметры:**
+ * `canceling_auth` — значение,содержащее аккаунт создателя транзакции и его разрешение (имя разрешения принимает значение `active`).
+ * `trx_id ` — идентификатор отложенной транзакции.
+
+Для выполнения транзакции, содержащей операцию `canceldelay`, требуется подпись создателя транзакции `canceling_auth`.
+
+## updateauth
+Операция-действие `updateauth` используется для добавления или изменения авторизации у аккаунта. Операция имеет вид:
+```cpp
+void updateauth(
+ name account,
+ name permission,
+ name parent,
+ authority auth
+)
+```
+**Параметры:**
+ * `account` — имя аккаунта, у которого изменяется авторизация.
+ * `permission` — имя разрешения (например: owner, active, posting и др.), которое обновляется.
+ * `parent` — имя родительского разрешения (например: `owner` является родительским разрешением для `active`; `active` является родительским разрешением для `posting`. Дочерние разрешения не могут превосходить в правах родительские разрешения). В большинстве случаев этот параметр принимает значение `active`.
+ * `auth` — структура вида `authority`, новые значения полей которой задаются в соответствии с `permission`.
+
+Для выполнения транзакции, содержащей операцию `updateauth`, требуется подпись аккаунта с именем `account`.
+
+## deleteauth
+Операция-действие `deleteauth` используется для удаления авторизации у аккаунта. Операция имеет вид:
+```cpp
+void deleteauth(
+ name account,
+ name permission
+)
+```
+**Параметры:**
+ * `account` — имя аккаунта, авторизация у которого удаляется.
+ * `permission` — имя разрешения, которое удаляется.
+
+Транзакция, содержащая операцию `deleteauth`, должна быть подписана аккаунтом с именем `account`.
+
+## linkauth
+Операция-действие `linkauth` разрешает `permission` выполнять какое-то действие в определенном контракте.
+```cpp
+void linkauth(
+ name account,
+ name code,
+ name type,
+ name requirement
+)
+
+```
+**Параметры:**
+ * `account ` — имя аккаунта, у которого изменяется `permission`.
+ * `code ` — имя аккаунта контракта, в котором разрешается выполнять какое-то действие.
+ * `type ` — тип разрешения. В смарт-контракте разрешается выполнять все операции, если параметр является пустым, или только одну операцию, заданную в параметре. В последнем случае значение параметра совпадает с именем операции-действия.
+ * `requirement ` — имя `permission`, которому разрешается выполнять операцию. Этот параметр не может быть пустым.
+
+Пара параметров `account` и `requirement` задают `permission_level` — аккаунт и имя его разрешения. Пара параметров `code` и `type` задают аккаунт контракта и операцию-действие.
+
+> **Важное:**
+> Любому набора параметров `account `, `code ` и `type ` должно соответствовать только одно значение `requirement `. Это означает, что аккаунт не может иметь два и более разных разрешений, связанных с теми же аккаунтом контракта и операцией-действием.
+>
+> Если в операцию-действие повторно передаются все те же значения `account `, `code ` и `type `, но с другим `requirement `, предыдущее его значение будет заменено на последнее переданное.
+
+Транзакция, содержащая операцию `linkauth`, должна быть подписана аккаунтом с именем `account`.
+
+## unlinkauth
+Операция-действие `unlinkauth` удаляет имя разрешения `permission` предоставленное операцией-действием `linkauth` в определенном контракте.
+```cpp
+void unlinkauth(
+ name account,
+ name code,
+ name type
+)
+```
+**Параметры:**
+ * `account ` — имя аккаунта, у которого удаляется разрешение `permission`.
+ * `code ` — имя аккаунта контракта, в котором разрешалось выполнять какое-то действие.
+ * `type ` — тип разрешения.
+
+Транзакция, содержащая операцию `unlinkauth`, должна быть подписана аккаунтом с именем `account `.
\ No newline at end of file
diff --git a/docs/ru-RU/cyber.domain_contract.md b/docs/ru-RU/cyber.domain_contract.md
index 5ae0f9774..587834410 100644
--- a/docs/ru-RU/cyber.domain_contract.md
+++ b/docs/ru-RU/cyber.domain_contract.md
@@ -3,9 +3,9 @@
Cмарт-контракт `cyber.domain` обеспечивает создание и обработку доменных имен, создание или удаление привязки доменных имен к аккаунтам, создание имен пользователей с привязкой к аккаунту, смену владельцев доменных имен, а также покупку доменных имен на аукционе.
В состав смарт-контракта `cyber.domain` входят:
- * группа операций-действий, используемых для покупки доменного имени на аукционе, в том числе: `checkwin`, `biddomain`, `biddmrefund` и `newdomain`;
- * группа внутренних доменных операций-действий, в том числе: `passdomain`, `linkdomain`, `unlinkdomain` и `newusername`. Код данных операций-действий находится в ядре, но их вызов происходит через смарт-контракт;
- * объявление используемых в транзакциях имен `declarenames`.
+ * группа операций-действий, используемых для покупки доменного имени на аукционе, в том числе: [checkwin](#checkwin), [biddomain](#biddomain), [biddmrefund](#biddmrefund) и [newdomain](#newdomain);
+ * группа внутренних доменных операций-действий, в том числе: [passdomain](#passdomain), [linkdomain](#linkdomain), [unlinkdomain](#unlinkdomain) и [newusername](#newusername). Код данных операций-действий находится в ядре, но их вызов происходит через смарт-контракт;
+ * объявление используемых в транзакциях имен [declarenames](#declarenames).
## Требования, предъявляемые к доменным именам
Доменные имена в CyberWay формируются в соответствии с правилами и процедурами Domain Name System (DNS). К структуре доменных имен предъявляются следующие требования:
@@ -29,7 +29,7 @@ Cмарт-контракт `cyber.domain` обеспечивает создан
## Аукцион доменных имен в смарт-контракте
-Операции-действия [checkwin](#operaciya-deistvie-checkwin), [biddomain](#operaciya-deistvie-biddomain), [biddmrefund](#operaciya-deistvie-biddmrefund) и [newdomain](#operaciya-deistvie-newdomain) используются для покупки доменного имени на аукционе. Процедура покупки доменного имени на аукционе выполняется аналогично процедуре покупки имени аккаунта. В процедуре действуют следующие правила:
+Операции-действия [checkwin](#checkwin), [biddomain](#biddomain), [biddmrefund](#biddmrefund) и [newdomain](#newdomain) используются для покупки доменного имени на аукционе. Процедура покупки доменного имени на аукционе выполняется аналогично процедуре покупки имени аккаунта. В процедуре действуют следующие правила:
* ставки на покупку любого доменного имени принимаются на аукционе в любой момент;
* по самой крупной ставке определяется единственное доменное имя, которое может быть выкуплено в настоящий момент;
* доменное имя считается выкупленным на аукционе, если были выполнены следующие условия:
@@ -37,27 +37,28 @@ Cмарт-контракт `cyber.domain` обеспечивает создан
* с момента предыдущего выкупа любого доменного имени прошло не менее суток;
* по завершении аукциона победителю не возвращается его ставка. Победитель может воспользоваться следующими возможностями:
* создать самостоятельно доменное имя с использованием операции `newdomain` и стать его владельцем;
- * владелец доменного имени может создать от него поддоменные имена добавлением к нему слева символа «точка» и части имени (например, владелец домена `golos.io` может создавать поддомены `api.golos.io`, `ws.golos.io` и т.д. При этом допускается только прямое наследование доменных имен (например, если владелец создал домен для второго уровня, то создание домена для третьего уровня будет недопустимым).
+ * владелец доменного имени может создать от него поддоменные имена добавлением к нему слева символа «точка» и части имени (например, владелец домена `golos.io` может создавать поддомены `api.golos.io`, `ws.golos.io` и т.д. При этом допускается только прямое наследование доменных имен (например, если владелец создал домен для второго уровня, то создание домена для третьего уровня будет недопустимым).
+
Следует заметить, что в `cyberway` *имена аккаунтов* формируются добавлением частей **справа** (например, от `cyber` могут быть образованы `cyber.msig`, `cyber.domain`).
-#### Примечания:
+**Примечание:**
* В CyberWay имеются операции-действия, прописанные непосредственно в ядре. У этой группы операций-действий часть логики выполняется в ядре, а часть — в смарт-контракте (например, у `newaccount` создание объекта `account` и вспомогательных объектов, выделение для него памяти и выделение ресурсов `bandwidth` выполняется в ядре).
-### Операция-действие checkwin
+### checkwin
Операция-действие `checkwin` используется для контроля появления владельца (победителя) доменного имени на аукционе. Данная операция не требует специального вызова и вызывается автоматически при вызове `biddomain` и `biddmrefund`.
Операция-действие `checkwin` имеет следующий вид:
```cpp
[[eosio::action]] void checkwin();
-```
-Операцию может выполнить любой аккаунт.
+```
-#### Параметры:
+**Параметры:**
Данная операция не имеет входных параметров.
+Операцию может выполнить любой аккаунт.
-### Операция-действие biddomain
+### biddomain
Операция-действие `biddomain` позволяет аккаунту делать ставки на аукционе для покупки доменного имени. Данная операция имеет следующий вид:
```cpp
[[eosio::action]] void biddomain(
@@ -66,16 +67,16 @@ Cмарт-контракт `cyber.domain` обеспечивает создан
asset bid
);
```
-#### Параметры:
+**Параметры:**
* `bidder` — имя аккаунта, делающего ставку на аукционе на покупку доменного имени;
* `name` — доменное имя (строковое значение в соответствии с требованиями), на которое делается ставка;
* `bid` — ставка (структура `asset`, токен должен быть системным).
-#### Примечания:
-* Для выполнение данной операции-действия требуется, чтобы аккаунт смарт-контракта `cyber.domain` был наделен правами аккаунта `bidder` на выполнение операции `transfer` в контракте `cyber.token`.
-* В случае, если на аукционе появляется бо́льшая ставка на то же доменное имя, осуществляется возврат предыдущей ставки аккаунту `bidder`. Возврат выполняется автоматически внутренним вызовом `biddmrefund` из `biddomain`.
+> **Примечания:**
+> * Для выполнение данной операции-действия требуется, чтобы аккаунт смарт-контракта `cyber.domain` был наделен правами аккаунта `bidder` на выполнение операции `transfer` в контракте `cyber.token`.
+> * В случае, если на аукционе появляется бо́льшая ставка на то же доменное имя, осуществляется возврат предыдущей ставки аккаунту `bidder`. Возврат выполняется автоматически внутренним вызовом `biddmrefund` из `biddomain`.
-### Операция-действие biddmrefund
+### biddmrefund
Операция-действие `biddmrefund` используется для возврата с аукциона ставки на покупку доменного имени в случае, если для того же доменного имени была сделана ставка с бо́льшим значением. Данная операция имеет вид:
```cpp
[[eosio::action]] void biddmrefund(
@@ -83,14 +84,14 @@ Cмарт-контракт `cyber.domain` обеспечивает создан
domain_name name
);
```
-#### Параметры:
+**Параметры:**
* `bidder` — имя аккаунта, которому делается возврат средств с аукциона;
* `name` — доменное имя, на покупку которого была сделана ставка.
В случае возникновения нестандартной ситуации (появление сбоя в сети или ошибки в работе узла) возможен вызов `biddmrefund` отдельно от вызова `biddomain` (нестандартный вызов `biddmrefund`).
-### Операция-действие newdomain
+### newdomain
Операция-действие `newdomain` используется для создания нового доменного имени. Данная операция имеет следующий вид:
```cpp
[[eosio::action]] void newdomain(
@@ -98,7 +99,7 @@ Cмарт-контракт `cyber.domain` обеспечивает создан
domain_name name
);
```
-#### Параметры:
+**Параметры:**
* `creator` — имя аккаунта, создающего доменное имя;
* `name` — создаваемое доменное имя.
@@ -111,6 +112,7 @@ Cмарт-контракт `cyber.domain` обеспечивает создан
## Операции с именами пользователя (username)
+
### newusername
Операция-действие `newusername` используется для создания имени пользователя и имеет следующий вид:
```cpp
@@ -120,7 +122,7 @@ Cмарт-контракт `cyber.domain` обеспечивает создан
username name
);
```
-#### Параметры:
+**Параметры:**
* `creator` — имя аккаунта, в области (scope) которого создается имя пользователя;
* `owner`— имя аккаунта, который станет владельцем имени пользователя;
* `name` — текстовое представление имени пользователя.
@@ -139,7 +141,7 @@ Cмарт-контракт `cyber.domain` обеспечивает создан
domain_name name
);
```
-#### Параметры:
+**Параметры:**
* `from` — аккаунт, от которого передается доменное имя и который является его владельцем;
* `to` — аккаунт, который получает доменное имя и станет его владельцем;
* `name` — строковое представление передаваемого доменного имени.
@@ -156,7 +158,7 @@ Cмарт-контракт `cyber.domain` обеспечивает создан
domain_name name
);
```
-#### Параметры:
+**Параметры:**
* `owner` — аккаунт, владелец доменного имени;
* `to` — аккаунт, к которому привязывается доменное имя;
* `name` — строковое представление доменного имени.
@@ -171,7 +173,7 @@ Cмарт-контракт `cyber.domain` обеспечивает создан
domain_name name
);
```
-#### Параметры:
+**Параметры:**
* `owner` — аккаунт, владелец доменного имени;
* `name` — строковое представление доменного имени.
@@ -185,8 +187,7 @@ Cмарт-контракт `cyber.domain` обеспечивает создан
Для прекращения использования доменного имени `@hello` аккаунт должен вызвать операцию-действие `unlinkdomain`. После этого доменное имя `@hello` не будет разрешаться в `ajhgsd23qw` и, следовательно, отправка трансфера на домен будет невозможна.
Аккаунт `ajhgsd23qw` в своем окружении может создавать имена пользователей. Например себе он берёт имя `admin`, а для контракта `cyber.token` хочет назначить короткое имя `t`. После этого имя `admin@@ajhgsd23qw` (обратите внимание на двойной `@@`) будет разрешаться в имя `ajhgsd23qw`, а `t@@ajhgsd23qw` в `cyber.token`. При добавлении доменного имени можно использовать `admin@hello` и `t@hello` соответственно.
-
-
+
## Объявление имён, используемых в транзакции
Для определения имени пользователя только одного доменного имени недостаточно. Имя пользователя привязывается к аккаунту-владельцу и аккаунту-области (обычно это смарт-контракт) и имеет вид структуры `имя@домен`. Доменная часть определяет область. В разных областях могут существовать аккаунты с одинаковыми именами.
@@ -200,7 +201,7 @@ Cмарт-контракт `cyber.domain` обеспечивает создан
);
```
-#### Параметры:
+**Параметры:**
* `domains` — массив описаний, каждое описание представляет собой структуру `name_info`.
Операция `declarenames` может быть выполнена любым аккаунтом.
@@ -215,7 +216,7 @@ struct name_info {
vector users;
};
```
-#### Параметры:
+**Параметры:**
* `domain` — доменное имя;
* `account` — имя аккаунта, соответствующее доменному имени;
* `users` — список имен пользователей доменного имени.
@@ -230,10 +231,9 @@ struct name_info {
* поле `.account` не должно содержать пустое значение.
-
-### Примечания
- * В настоящей реализации нет проверки, что указанные в `declarenames` аккаунты существуют в других операциях-действиях транзакции. Реализация проверки затруднена, так как непосредственно процесс проверки требует большие ресурсы, затрачиваемые на чтение файла формата ABI и десериализацию действия. Риск отсутствия такой проверки — добавление отправителем транзакции больше информации, чем ее необходимо для проверки. В результате ему нужно будет дополнительно заплатить за излишне используемые ресурсы `bandwidth`.
- * В настоящей реализации отсутствует информация о позициях используемых доменных или пользовательских имен.
+> **Примечания:**
+> * В настоящей реализации нет проверки, что указанные в `declarenames` аккаунты существуют в других операциях-действиях транзакции. Реализация проверки затруднена, так как непосредственно процесс проверки требует большие ресурсы, затрачиваемые на чтение файла формата ABI и десериализацию действия. Риск отсутствия такой проверки — добавление отправителем транзакции больше информации, чем ее необходимо для проверки. В результате ему нужно будет дополнительно заплатить за излишне используемые ресурсы `bandwidth`.
+> * В настоящей реализации отсутствует информация о позициях используемых доменных или пользовательских имен.
### Пример 1:
Транзакция может содержать действие с несколькими именами аккаунтов в качестве аргументов, например такое:
diff --git a/docs/ru-RU/cyber.msig_contract.md b/docs/ru-RU/cyber.msig_contract.md
index d9c213bb3..ef3224c5f 100644
--- a/docs/ru-RU/cyber.msig_contract.md
+++ b/docs/ru-RU/cyber.msig_contract.md
@@ -34,7 +34,8 @@ void multisig::propose(
name proposer,
name proposal_name,
std::vector requested,
- transaction trx
+ transaction trx,
+ eosio::binary_extension& description
)
```
#### Параметры:
@@ -42,6 +43,7 @@ void multisig::propose(
* `proposal_name` — уникальное имя, присвоенное мультиподписной транзакции при ее создании. Данный параметр, в совокупности с параметром `proposer`, однозначно идентифицируют мультиподписную транзакцию.
* `requested` — список разрешений, которые требуется получить для выполнения мультиподписной транзакции. Здесь могут быть индивидуальные разрешения аккаунтов, входящих в мультиподписную авторити.
* `trx` — предложенная на подписание транзакция.
+ * `description` — необязательный параметр, описание мультиподписной транзакции
Для вызова операции-действия `propose` требуется авторизация пользователя `proposer`.
diff --git a/docs/ru-RU/cyber.token_contract.md b/docs/ru-RU/cyber.token_contract.md
index 3544a5cf4..0708f6f15 100644
--- a/docs/ru-RU/cyber.token_contract.md
+++ b/docs/ru-RU/cyber.token_contract.md
@@ -12,18 +12,18 @@
asset maximum_supply
);
```
-Параметры:
-`issuer` — имя аккаунта, создающего токен для обращения в системе;
-`maximum_supply` — значение, содержащее поля:
- * максимально возможное количество поставляемых токенов;
- * символ токена (тип данных, однозначно определяющий токен):
- * имя токена, состоящее из набора прописных букв;
- * поле, задающее точность стоимости токена в виде количества знаков после запятой.
+#### Параметры:
+* `issuer` — имя аккаунта, который станет владельцем (издателем) создаваемого токена в системе;
+* `maximum_supply` — максимально возможное количество поставляемых токенов, тип `asset`, содержит поля:
+ * целочисленное количество токена;
+ * символ токена (тип данных, однозначно определяющий токен):
+ * код символа — имя токена, состоящее из набора прописных букв;
+ * поле, задающее точность стоимости токена в виде количества знаков после запятой.
-Аккаунт `issuer` наделяется правами по выпуску и изъятию из обращения токенов. Права на выполнение операции-действия `create` имеются только у смарт-контракта `token`. Для выполнения данной операции-действия необходима подпись валидаторов. За использование ресурсов `bandwidth` (RAM) плата взимается с аккаунта `issuer`.
+Аккаунт `issuer` наделяется правами по выпуску и изъятию из обращения токенов. Нельза создать 2 и более токена с одинаковым *кодом символа*. Права на выполнение операции-действия `create` имеются только у смарт-контракта `cyber.token`. Для выполнения данной операции-действия необходима подпись валидаторов.
## Операция-действие issue
-Операция-действие `issue` используется для выпуска в обращение токена в системе.
+Операция-действие `issue` используется для выпуска в обращение заданного количества токенов.
Операция-действие `issue` имеет следующий вид:
```cpp
[[eosio::action]] void issue(
@@ -32,17 +32,12 @@
string memo
);
```
-Параметры:
-`to` — имя аккаунта, на баланс которого поступает токен;
-`quantity` — значение, содержащее поля:
- * количество выпущенных в обращение токенов;
- * символ токена:
- * имя токена, состоящее из набора прописных букв;
- * поле, задающее точность стоимости токена в виде количества знаков после запятой;
+#### Параметры:
+* `to` — имя аккаунта, на баланс которого поступает токен;
+* `quantity` — количество выпускаемых в обращение токенов, тип `asset`:
+* `memo` — примечание, текст которого дополняет смысловое значение выпуска токена (например, эмиссия токена). Количество символов в строке не должно превышать 384 шт.
-`memo` — примечание, текст которого дополняет смысловое значение выпуска токена (например, эмиссия токена). Количество символов в строке не должно превышать 384 шт.
-
-При выполнении операции-действия `create` в таблицу записывается символ токена и имя аккаунта `issuer`. При выполнении операции-действия `issue` из полученного значения `quantity` берется символ токена и по нему, используя табличные данные, определяется аккаунт `issuer`. Правами на выполнение операции-действия обладает аккаунт `issuer`. Количество выпущенных токенов не должно превышать значение `maximum_supply`, заданного в операции-действия `create`. За использование ресурсов `bandwidth` (RAM) взимается плата c аккаунта `issuer`.
+При выполнении операции-действия `create` в таблицу записывается символ токена и имя аккаунта `issuer`. При выполнении операции-действия `issue` из полученного значения `quantity` берется символ токена и по нему, используя табличные данные, определяется аккаунт `issuer`. Правами на выполнение операции-действия обладает аккаунт `issuer`. Общее количество выпущенных токенов не должно превышать значение `maximum_supply`, заданного в операции-действия `create`. За использование ресурсов `bandwidth` (RAM) взимается плата c аккаунта `issuer`.
## Операция-действие retire
Операция-действие `retire` используется для изъятия из обращения определенного количества токенов в системе. Операция-действие `retire` имеет следующий вид:
@@ -52,14 +47,11 @@
string memo
);
```
-Параметры:
-`quantity` — значение, содержащее поля:
- * количество изымаемых из обращения токенов;
- * символ токена:
- * имя токена, состоящее из набора прописных букв;
- * поле, задающее точность стоимости токена в виде количества знаков после запятой.
+#### Параметры:
+* `quantity` — количество изымаемых из обращения токенов, тип `asset`;
+* `memo` — строка, поясняющая изъятие средств. Длина строки не более 384 символов.
-Правами запуска операции-действия обладает аккаунт `issuer`. За использование ресурсов `bandwidth` (RAM) плата взимается c аккаунта `issuer`. Указанное в операции-действия количество изымаемых из обращения токенов также снимается с баланса аккаунта `issuer`, поэтому он не может изъять из обращения токенов больше, чем имеется на его балансе.
+Правами запуска операции-действия обладает аккаунт `issuer`. Указанное в операции-действия количество изымаемых из обращения токенов также снимается с баланса аккаунта `issuer`, поэтому он не может изъять из обращения токенов больше, чем имеется на его балансе.
## Операция-действие transfer
Операция-действие `transfer` используется для передачи токена с баланса одного аккаунта на баланс другого. Операция-действие `transfer` имеет следующий вид:
@@ -71,17 +63,17 @@
string memo
);
```
-Параметры:
-`from` — имя аккаунта-отправителя, с баланса которого снимаются токены;
-`to` — имя аккаунта-получателя, на баланс которого поступают токены;
-`quantity` — значение в виде структуры, задающее количество переводимых токенов. Количество токенов должно быть больше нуля;
-`memo` — примечание, уточняющее цель перевода токенов.
+#### Параметры:
+* `from` — имя аккаунта-отправителя, с баланса которого снимаются токены;
+* `to` — имя аккаунта-получателя, на баланс которого поступают токены;
+* `quantity` — количество переводимых токенов, тип `asset`. Количество токенов должно быть больше нуля;
+`memo` — примечание, уточняющее цель перевода токенов. Длина строки не более 384 символов.
Операция-действие выполняется с отправкой уведомления на смарт-контракты отправителя и получателя токенов. Операция отправки уведомления имеет вид аналогичный операции-действия `transfer`. Отличие состоит в том, что операция отправки уведомления выполняется не на смарт-контракте `cyber.token`, а на контрактах отправителя и получателя уведомления (например, если смарт-контракт `vesting` получает уведомление, он автоматически начисляет токены на баланс `vesting`). Для отправки токенов аккаунт должен быть наделен правами отправителя.
За использование ресурсов `bandwidth` (RAM) плата взимается либо с аккаунта-отправителя, либо с аккаунта-получателя в зависимости от того, кто подписал транзакцию. В случае, если предварительно была выполнена операция-действие `open`, никто из них не оплачивает `bandwidth`, так как используется уже созданная в БД запись.
-**Примечание**
+#### Примечание
Раньше в EOS при пересылке токенов создавалась запись в БД для хранения баланса пользователя для случая, когда у получателя не было в наличии токена. Если аккаунт-отправитель оплачивал используемые ресурсы (RAM) за аккаунта-получателя, то существовала возможность расхода всей выделенной доли памяти аккаунту-получателю.
С целью недопущения подобных случаев при выполнении операции-действия `transfer` было принято решение ввести дополнительные операции-действия `open` и `close`. Функциональное назначение данных операций — предварительное создание записи в БД, чтобы запись создавалась не во время выполнения операции-действия `transfer`, а также ее удаление.
@@ -93,17 +85,17 @@
vector recipients
)
```
-Параметры:
-`from` — имя аккаунта-отправителя, с баланса которого снимаются токены;
-`recipients` — массив (перечень) получателей токенов. Каждый элемент массива имеет форму структуры, содержащей поля:
- * `to` — имя аккаунта, на баланс которого переводятся токены;
- * `quantity` — количество токенов, переводимых на баланс аккаунта `to`. Количество токенов должно быть больше нуля;
- * `memo` — примечание, уточняющее цель перевода токенов. Длина строки `memo` не должна превышать 384 символов.
+#### Параметры:
+* `from` — имя аккаунта-отправителя, с баланса которого снимаются токены;
+* `recipients` — массив (перечень) получателей токенов. Каждый элемент массива является структурой, содержащей поля:
+ * `to` — имя аккаунта, на баланс которого переводятся токены;
+ * `quantity` — количество токенов, переводимых на баланс аккаунта `to`. Количество токенов должно быть больше нуля;
+ * `memo` — примечание, уточняющее цель перевода токенов. Длина строки `memo` не должна превышать 384 символов.
Ограничения на выполнение операции-действия `bulktransfer`:
* трансфер токенов различного вида не допускается;
* трансфер токенов самому себе не допускается, то есть перечень получателей токенов `recipients` не должен содержать имя аккаунта `from`;
- * количество трансфера токенов должно быть больше нуля для каждого аккаунта.
+ * количество переводимых токенов должно быть больше нуля для каждого аккаунта.
Допускается в перечне `recipients` указывать одно и то же имя аккаунта `to` более одного раза. Операция-действие выполняется с отправкой уведомления на смарт-контракты отправителя и получателя токенов аналогично выполнению `transfer`.
@@ -115,7 +107,6 @@
Операция-действие `payment` имеет следующий вид:
-
```cpp
[[eosio::action]] void payment(
name from,
@@ -124,15 +115,15 @@
const string& memo
)
```
-Параметры:
-`from` — имя аккаунта-отправителя, с баланса которого снимаются токены;
-`to` — имя аккаунта-получателя токенов;
-`quantity` — количество перечисляемых токенов аккаунту `to`. Количество токенов должно быть больше нуля;
-`memo` — примечание, уточняющее цель перевода токенов. Длина строки `memo` не должна превышать 384 символов.
+#### Параметры:
+* `from` — имя аккаунта-отправителя, с баланса которого снимаются токены;
+* `to` — имя аккаунта-получателя токенов;
+* `quantity` — количество перечисляемых токенов аккаунту `to`. Количество токенов должно быть больше нуля;
+* `memo` — примечание, уточняющее цель перевода токенов. Длина строки `memo` не должна превышать 384 символов.
-В отличие от `transfer` при выполнении операции-действия `payment` отправка уведомлений не происходит и средства поступают на баланс аккаунта-получателя не напрямую, а через баланс-посредника `payment`, Для пересылки средств с баланса-посредника `payment` на баланс аккаунта-получателя `to` дополнительно требуется выполнить операцию-действие `claim`.
+В отличие от `transfer` при выполнении операции-действия `payment` отправка уведомлений не происходит и средства поступают не на ликвидный баланс аккаунта-получателя, а на отдельный `payment`-баланс. Для пересылки средств с баланса-посредника `payment` на баланс аккаунта-получателя `to` дополнительно требуется выполнить операцию-действие `claim`.
-Операция-действие `payment` является более безопасным вариантом трансфера токенов. Для ее выполнения требуется подпись аккаунта `from`.
+Операция-действие `payment` является более надёжным вариантом трансфера токенов, т.к. не отправляются уведомления, которые могли бы завершиться ошибкой и прервать трансфер. Для ее выполнения требуется подпись аккаунта `from`.
## Операция-действие bulkpayment
@@ -143,24 +134,24 @@
vector recipients
)
```
-Параметры:
-`from` — имя аккаунта-отправителя, с баланса которого снимаются токены;
-`recipients` — массив (перечень) получателей токенов. Каждый элемент массива имеет форму структуры, содержащей поля:
- * `to` — имя аккаунта-получателя токенов;
- * `quantity` — количество перечисляемых токенов аккаунту `to`. Количество токенов должно быть больше нуля;
- * `memo` — примечание, уточняющее цель перевода токенов. Длина строки `memo` не должна превышать 384 символов.
+#### Параметры:
+* `from` — имя аккаунта-отправителя, с баланса которого снимаются токены;
+* `recipients` — массив (перечень) получателей токенов. Каждый элемент массива является структурой, содержащей поля:
+ * `to` — имя аккаунта-получателя токенов;
+ * `quantity` — количество перечисляемых токенов аккаунту `to`. Количество токенов должно быть больше нуля;
+ * `memo` — примечание, уточняющее цель перевода токенов. Длина строки `memo` не должна превышать 384 символов.
Ограничения на выполнение операции-действия `bulkpayment`:
* трансфер токенов различного вида не допускается;
* трансфер токенов самому себе не допускается, то есть перечень получателей токенов `recipients` не должен содержать имя аккаунта `from`.
-В отличие от `bulktransfer` при выполнении операции-действия `bulkpayment` отправка уведомлений не происходит и средства поступают на баланс аккаунта-получателя не напрямую, а через баланс-посредника `payment`, Для пересылки средств с баланса-посредника `payment` на баланс аккаунта-получателя `to` дополнительно требуется выполнить операцию-действие `claim`.
+В отличие от `bulktransfer` при выполнении операции-действия `bulkpayment` отправка уведомлений не происходит, и средства поступают на дополнительный `payment`-баланс аккаунта-получателя. Для пересылки средств с баланса-посредника `payment` на основной (ликвидный) баланс аккаунта-получателя `to` дополнительно требуется выполнить операцию-действие `claim`.
-Операция-действие `bulkpayment` является более безопасным вариантом трансфера токенов. Для ее выполнения требуется подпись аккаунта `from`.
+Операция-действие `bulkpayment` является более надёжным вариантом трансфера токенов. Для ее выполнения требуется подпись аккаунта `from`.
## Операция-действие claim
-Операция-действие `claim` используется для перевода средств с баланса `payment` на баланс аккаунта. Операция-действие имеет вид:
+Операция-действие `claim` используется для перевода средств с баланса `payment` на основной баланс аккаунта. Операция-действие имеет вид:
```cpp
[[eosio::action]] void claim(
@@ -168,15 +159,15 @@
asset quantity
)
```
-Параметры:
-`owner` — имя аккаунта, на баланс которого переводятся средства;
-`quantity` — запрашиваемое количество токенов для перевода. Это количество не должно превышать количество токенов, находящихся на балансе `payment`, собственником которых является аккаунт `owner`.
+#### Параметры:
+* `owner` — имя аккаунта, на балансах которого совершается перевод;
+* `quantity` — запрашиваемое количество токенов для перевода. Это количество должно быть больше 0 и не должно превышать количество токенов, находящихся на балансе `payment`, собственником которых является аккаунт `owner`.
Для выполнения операции-действия `claim` требуется подпись аккаунта `owner`.
## Операция-действие open
-Операция-действие `open` используется для создания записи в БД с указанием имени аккаунта, который должен оплатить используемую память, а также с указанием символа, для которого создается запись. Операция-действие `open` имеет следующий вид:
+Операция-действие `open` создаёт запись в БД для хранения баланса аккаунта для заданного токена. Операция-действие `open` имеет следующий вид:
```cpp
[[eosio::action]] void open(
name owner,
@@ -184,33 +175,36 @@
name ram_payer
);
```
-Параметры:
-`owner` — имя аккаунта, которому выделяется память;
-`symbol` — символ, для которого создается запись;
-`ram_payer` — имя аккаунта, который оплачивает используемую память;
+#### Параметры:
+* `owner` — имя аккаунта, которому выделяется память;
+* `symbol` — символ токена, для которого создается запись;
+* `ram_payer` — имя аккаунта, который оплачивает используемую память.
Выполнение операции-действия `open` требует подписи аккаунта `ram_payer`.
## Операция-действие close
-Операция-действие `close` является обратным действием по отношению к `open` и используется для освобождение выделенной памяти в БД. Для выполнения данного операции-действия требуется наличие нулевого баланса токена (определяемого символом) у аккаунта. Операция-действие `close` имеет следующий вид:
+Операция-действие `close` является обратным действием по отношению к `open` и используется для освобождение выделенной памяти в БД. Для выполнения данной операции-действия требуется наличие нулевого баланса токена (определяемого символом) у аккаунта. Операция-действие `close` имеет следующий вид:
```cpp
[[eosio::action]] void close(
name owner,
symbol symbol
);
```
-Параметры:
-`owner` — имя аккаунта, которому была выделена память;
-`symbol` — символ, для которого удаляется запись.
+#### Параметры:
+* `owner` — имя аккаунта, баланс которого удаляется;
+* `symbol` — символ, для которого удаляется запись.
-Для выполнения `close` требуется наличие нулевых балансов у аккаунта `owner`:
- * нулевой баланс токена (определяется символом);
- * нулевой платежный баланс.
+Для выполнения `close` требуется наличие нулевых балансов указанного токена у аккаунта `owner`:
+ * нулевой основной баланс;
+ * нулевой `payment` баланс.
-### Получение статистической информации по системным токенам
+***
+
+## Получение статистической информации по системным токенам
Для получения статистической информации по токенам в смарт-контракте `cyber.token` используются две таблицы `currency_stats` и `account`.
-Таблица `currency_stats` имеет следующий вид:
+### currency_stats
+Запись в таблице `currency_stats` имеет следующий вид:
```cpp
struct [[eosio::table]] currency_stats {
asset supply;
@@ -218,18 +212,23 @@ struct [[eosio::table]] currency_stats {
name issuer;
};
```
-Параметры:
-`supply` — значение в виде структуры с полями, показывающее количество токенов в обращении определенного вида;
-`max_supply` — значение в виде структуры с полями, показывающее максимально возможное количество в обращении токенов определенного вида;
-`issuer` — имя аккаунта, выпустившего токены.
+#### Параметры:
+* `supply` — количество токенов определенного вида в обращении, тип `asset`;
+* `max_supply` — максимально возможное количество в обращении токенов определенного вида, тип `asset`;
+* `issuer` — имя аккаунта, выпустившего токены.
Первичным ключом для таблицы `currency_stats` является символьное значение в `asset`, по которому определяется токен. По токену определяется значение `supply` — количество выпущенных в обращение токенов, а также имя аккаунта, выпустившего токены.
-Таблица `account` имеет следующий вид:
+### account
+Записи в таблице `account` имеет следующий вид:
```cpp
struct [[eosio::table]] account {
asset balance;
asset payments;
};
```
-Область видимости таблицы определяется именем аккаунта.
+#### Параметры:
+* `balance` — количество токенов на основном (ликвидном) балансе пользователя, тип `asset`;
+* `payments` — количество токенов на `payment`-балансе (переводятся на основной с помощью `claim`), тип `asset`;
+
+Область видимости таблицы определяется именем аккаунта, владеющего соответствующим балансом.
diff --git a/scripts/base-genesis/CMakeLists.txt b/scripts/base-genesis/CMakeLists.txt
new file mode 100644
index 000000000..59ed016c1
--- /dev/null
+++ b/scripts/base-genesis/CMakeLists.txt
@@ -0,0 +1,23 @@
+set(GENESIS_DIR ${CMAKE_CURRENT_BINARY_DIR})
+set(CYBERWAY_CONTRACTS ${CMAKE_BINARY_DIR})
+
+string(TIMESTAMP INITIAL_TIMESTAMP %Y-%m-%dT%H:%M:%S.000 UTC)
+
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/genesis.json.tmpl ${CMAKE_CURRENT_BINARY_DIR}/genesis.json.tmpl @ONLY)
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/genesis-info.json.tmpl ${CMAKE_CURRENT_BINARY_DIR}/genesis-info.json)
+
+file(STRINGS ${CMAKE_CURRENT_BINARY_DIR}/genesis.json.tmpl GENESIS_JSON_CONTENT REGEX ^[^\#].*)
+file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/genesis.json ${GENESIS_JSON_CONTENT})
+
+if(EOSIO_ROOT STREQUAL "" OR NOT EOSIO_ROOT)
+ if(APPLE)
+ set(EOSIO_ROOT "~/opt/eosio")
+ else()
+ set(EOSIO_ROOT "/opt/eosio")
+ endif()
+endif()
+add_custom_target(generate-genesis ALL DEPENDS cyber.bios.wasm cyber.token.wasm cyber.stake.wasm)
+add_custom_command(TARGET generate-genesis POST_BUILD COMMAND ${CMAKE_COMMAND} ARGS -P ${CMAKE_CURRENT_SOURCE_DIR}/create-genesis.cmake ${EOSIO_ROOT})
+
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/genesis.dat DESTINATION ${CMAKE_INSTALL_PREFIX}/base-genesis/)
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/genesis.json DESTINATION ${CMAKE_INSTALL_PREFIX}/base-genesis/)
diff --git a/scripts/base-genesis/create-genesis.cmake b/scripts/base-genesis/create-genesis.cmake
new file mode 100644
index 000000000..da97baace
--- /dev/null
+++ b/scripts/base-genesis/create-genesis.cmake
@@ -0,0 +1,4 @@
+execute_process(COMMAND ${CMAKE_ARGV3}/bin/create-genesis -g ${CMAKE_CURRENT_BINARY_DIR}/genesis-info.json -o ${CMAKE_CURRENT_BINARY_DIR}/genesis.dat OUTPUT_VARIABLE GENERATOR_OUT)
+message(${CMAKE_ARGV3}/bin/create-genesis -g ${CMAKE_CURRENT_BINARY_DIR}/genesis-info.json -o ${CMAKE_CURRENT_BINARY_DIR}/genesis.dat)
+file(SHA256 ${CMAKE_CURRENT_BINARY_DIR}/genesis.dat GENESIS_DATA_HASH)
+configure_file(${CMAKE_CURRENT_BINARY_DIR}/genesis.json ${CMAKE_CURRENT_BINARY_DIR}/genesis.json)
diff --git a/scripts/base-genesis/genesis-info.json.tmpl b/scripts/base-genesis/genesis-info.json.tmpl
new file mode 100644
index 000000000..7c5fe3d96
--- /dev/null
+++ b/scripts/base-genesis/genesis-info.json.tmpl
@@ -0,0 +1,94 @@
+{
+ "state_file": "",
+ "genesis_json": "${GENESIS_DIR}/genesis.json",
+ "params":{
+ "initial_prod_count": 0,
+ "funds": [],
+ },
+ "accounts": [
+ {"name": "cyber",
+ "privileged": true,
+ "permissions": [
+ {"name": "owner", "keys": []},
+ {"name": "active", "keys": ["INITIAL"]},
+ {"name": "prods", "keys": [], "threshold": 2, "accounts": ["cyber.prods@active"], "waits": ["1209600"]}
+ ],
+ "abi": {"path": "${CYBERWAY_CONTRACTS}/cyber.bios/cyber.bios.abi", "hash":""},
+ "code": {"path": "${CYBERWAY_CONTRACTS}/cyber.bios/cyber.bios.wasm", "hash":""}
+ },
+ {"name": "cyber.govern",
+ "privileged": true,
+ "permissions": [
+ {"name": "owner", "keys": [], "accounts": ["cyber@owner"]},
+ {"name": "active", "keys": [], "accounts": ["cyber@active"]}
+ ]
+ },
+ {"name": "cyber.stake",
+ "privileged": true,
+ "permissions": [
+ {"name": "owner", "keys": [], "accounts": ["cyber@owner"]},
+ {"name": "active", "keys": [], "accounts": ["cyber@active"]},
+ ],
+ "abi": {"path": "${CYBERWAY_CONTRACTS}/cyber.stake/cyber.stake.abi", "hash":""},
+ "code": {"path": "${CYBERWAY_CONTRACTS}/cyber.stake/cyber.stake.wasm", "hash":""}
+ },
+ {"name": "cyber.null",
+ "permissions": [
+ {"name": "owner", "keys": [], "accounts": []},
+ {"name": "active", "keys": [], "accounts": []}
+ ]
+ },
+ {"name": "cyber.prods",
+ "permissions": [
+ {"name": "owner", "keys": [], "accounts": []},
+ {"name": "active", "keys": [], "accounts": ["cyber@active"]},
+ {"name": "prod.major", "keys": [], "accounts": ["cyber@active"]},
+ {"name": "prod.minor", "keys": [], "accounts": ["cyber@active"]}
+ ]
+ },
+ {"name": "cyber.token",
+ "permissions": [
+ {"name": "owner", "keys": [], "accounts": ["cyber@owner"]},
+ {"name": "active", "keys": [], "accounts": ["cyber@active"]}
+ ],
+ "abi": {"path": "${CYBERWAY_CONTRACTS}/cyber.token/cyber.token.abi", "hash":""},
+ "code": {"path": "${CYBERWAY_CONTRACTS}/cyber.token/cyber.token.wasm", "hash":""}
+ },
+ {"name": "cyber.msig",
+ "permissions": [
+ {"name": "owner", "keys": [], "accounts": ["cyber@owner"]},
+ {"name": "active", "keys": [], "accounts": ["cyber@active"]}
+ ],
+ "abi": {"path": "${CYBERWAY_CONTRACTS}/cyber.msig/cyber.msig.abi", "hash":""},
+ "code": {"path": "${CYBERWAY_CONTRACTS}/cyber.msig/cyber.msig.wasm", "hash":""}
+ }
+ ],
+ "transit_account_authorities": [],
+ "delegateuse": [],
+ "tables": [
+ {
+ "code": "cyber.token",
+ "table": "stat",
+ "abi_type": "currency_stats",
+ "rows": [
+ {
+ "scope": "CYBER", "payer": "cyber.token", "pk": 353349294403,
+ "data": {
+ "supply" : {
+ "_amount" : 0,
+ "_decs" : 4,
+ "_sym" : "CYBER"
+ },
+ "max_supply" : {
+ "_amount" : 1000000000,
+ "_decs" : 4,
+ "_sym" : "CYBER"
+ },
+ "issuer" : "cyber"
+ }
+ }
+ ]
+ }
+ ],
+ "auth_links": [],
+}
diff --git a/scripts/base-genesis/genesis.json.tmpl b/scripts/base-genesis/genesis.json.tmpl
new file mode 100644
index 000000000..62a15cf09
--- /dev/null
+++ b/scripts/base-genesis/genesis.json.tmpl
@@ -0,0 +1,37 @@
+{
+# NOTE: For automatic set initial_timestamp from GolosChain LIB time
+# "initial_timestamp": "1970-01-01T00:00:00.000",
+ "initial_timestamp": "@INITIAL_TIMESTAMP@",
+ "initial_key": "GLS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
+ "initial_configuration": {
+ "base_per_transaction_net_usage": 12,
+ "context_free_discount_net_usage_num": 20,
+ "context_free_discount_net_usage_den": 100,
+ "min_transaction_cpu_usage": 100,
+ "min_transaction_ram_usage": 1024
+ "max_transaction_lifetime": 3600,
+
+# NOTE: The expiration interval for deferred transactions was increased (for 3 hours)
+# due to the fact that current value (10 minutes) is not enough to close a large
+# number of posts in testnet (about 60,000). In the real system there will not be
+# such a number of posts and this value should be revised.
+ "deferred_trx_expiration_window": 10800,
+ "max_transaction_delay": 3888000,
+ "max_inline_action_size": 32768,
+ "max_inline_action_depth": 4,
+ "max_authority_depth": 6,
+ "ram_size": "8589934592",
+ "reserved_ram_size": "536870912",
+ "max_block_usage": [1400000, 1048576, 134217728, 4194304],
+ "max_transaction_usage": [800000, 524288, 67108864, 2097152],
+ "target_virtual_limits": [140000, 524288, 67108864, 3268],
+ "min_virtual_limits": [1400000, 1048576, 33554432, 1634],
+ "max_virtual_limits": [1400000000, 1048576000, 33554432000, 326800],
+ "usage_windows": [60000, 60000, 60000, 86400000],
+ "virtual_limit_decrease_pct": [100, 100, 100, 100],
+ "virtual_limit_increase_pct": [10, 10, 10, 10],
+ "account_usage_windows": [3600000, 3600000, 3600000, 2592000000]
+ },
+ "initial_chain_id": "000000000000000000000000",
+ "genesis_data_hash": "${GENESIS_DATA_HASH}"
+}
diff --git a/tests/cyber.govern_test_api.hpp b/tests/cyber.govern_test_api.hpp
index 8543e39ac..d5cf5c282 100644
--- a/tests/cyber.govern_test_api.hpp
+++ b/tests/cyber.govern_test_api.hpp
@@ -95,8 +95,8 @@ struct cyber_govern_api: base_contract_api {
wait_irreversible_block(proposed_schedule_block_num, disabled_producers);
wait_irreversible_block(_tester->control->head_block_num(), disabled_producers);
- BOOST_REQUIRE(_tester->control->head_block_header().schedule_version == prev_version);
- BOOST_REQUIRE(get_block_offset() == prev_block_offset);
+ BOOST_REQUIRE_EQUAL(_tester->control->head_block_header().schedule_version, prev_version);
+ BOOST_REQUIRE_EQUAL(get_block_offset(), prev_block_offset);
BOOST_REQUIRE(_tester->control->pending_block_state()->active_schedule.version == prev_version + static_cast(change_version));
auto ret = _tester->control->head_block_num() - prev_block;
diff --git a/tests/cyber.govern_tests.cpp b/tests/cyber.govern_tests.cpp
index ff4387b8a..546ce0b86 100644
--- a/tests/cyber.govern_tests.cpp
+++ b/tests/cyber.govern_tests.cpp
@@ -143,6 +143,7 @@ class cyber_govern_tester : public golos_tester {
const string temp_unavailable = amsg("action is temporarily unavailable");
const string incorrect_shift = amsg("incorrect shift");
const string shift_not_changed = amsg("the shift has not changed");
+ const string cand_is_active = amsg("candidate is active");
} err;
};
@@ -317,8 +318,11 @@ BOOST_FIXTURE_TEST_CASE(recovery_delay, cyber_govern_tester) try {
}
BOOST_CHECK_EQUAL(stake.get_candidate(_bob, token._symbol)["signing_key"].as(), public_key_type());
BOOST_CHECK_EQUAL(err.temp_unavailable, stake.setkey(_bob, token._symbol.to_symbol_code(), false));
- BOOST_CHECK_EQUAL(err.temp_unavailable, stake.setproxylvl(_bob, token._symbol.to_symbol_code(), 1));
- produce_block(fc::seconds(cfg::key_recovery_delay));
+ BOOST_CHECK_EQUAL(success(), stake.setproxylvl(_bob, token._symbol.to_symbol_code(), 1));
+ BOOST_CHECK_EQUAL(success(), stake.setproxylvl(_bob, token._symbol.to_symbol_code(), 0));
+ BOOST_CHECK_EQUAL(err.temp_unavailable, stake.setkey(_bob, token._symbol.to_symbol_code(), false));
+ produce_block();
+ produce_block(fc::seconds(cfg::key_recovery_delay) - fc::milliseconds(config::block_interval_ms));
BOOST_CHECK_EQUAL(success(), stake.setkey(_bob, token._symbol.to_symbol_code(), false));
BOOST_CHECK_EQUAL(stake.get_candidate(_bob, token._symbol)["signing_key"].as(), get_public_key(_bob, "active"));
BOOST_CHECK_EQUAL(success(), stake.setproxylvl(_bob, token._symbol.to_symbol_code(), 1));
@@ -469,6 +473,70 @@ BOOST_FIXTURE_TEST_CASE(unreg_sleeping_producer, cyber_govern_tester) try {
BOOST_AUTO_TEST_SUITE_END() // reset_key
+BOOST_AUTO_TEST_SUITE(reset_level)
+BOOST_FIXTURE_TEST_CASE(suspendcand, cyber_govern_tester) try {
+ BOOST_TEST_MESSAGE("reset_level/suspendcand");
+
+ deploy_sys_contracts();
+ auto cur_producers = reg_candidates(cfg::min_producers_num, 1000);
+ BOOST_CHECK_EQUAL(success(), stake.register_candidate(_alice, token._symbol.to_symbol_code()));
+
+ BOOST_CHECK_EQUAL(govern.get_active_producers(), govern.make_producers_group({cfg::internal_name}));
+ govern.wait_schedule_activation();
+ BOOST_CHECK_EQUAL(govern.get_active_producers(), govern.make_producers_group(cur_producers));
+ BOOST_CHECK_EQUAL(success(), stake.setkey(cur_producers[0], token._symbol.to_symbol_code(), true));
+ produce_block();
+ produce_block(fc::seconds(cfg::max_no_pick_period) - fc::milliseconds(config::block_interval_ms));
+ govern.wait_schedule_activation();
+ auto suspended = cur_producers[0];
+ cur_producers[0] = _alice;
+ BOOST_CHECK_EQUAL(govern.get_active_producers(), govern.make_producers_group(cur_producers));
+
+ BOOST_CHECK_EQUAL(success(), stake.suspendcand(suspended, token._symbol.to_symbol_code()));
+ for (size_t p = 0; p < cur_producers.size(); p++) {
+ BOOST_CHECK_EQUAL(err.cand_is_active, stake.suspendcand(cur_producers[p], token._symbol.to_symbol_code()));
+ }
+ BOOST_CHECK_EQUAL(stake.get_agent(suspended, token._symbol)["proxy_level"], 4);
+
+ BOOST_CHECK_EQUAL(err.temp_unavailable, stake.setproxylvl(suspended, token._symbol.to_symbol_code(), 1));
+ produce_block();
+ produce_block(fc::seconds(cfg::proxylvl_recovery_delay) - fc::milliseconds(config::block_interval_ms));
+ BOOST_CHECK_EQUAL(success(), stake.setproxylvl(suspended, token._symbol.to_symbol_code(), 1));
+} FC_LOG_AND_RETHROW()
+
+BOOST_FIXTURE_TEST_CASE(resets_limit, cyber_govern_tester) try {
+ BOOST_TEST_MESSAGE("reset_level/resets_limit");
+ deploy_sys_contracts();
+ BOOST_CHECK_EQUAL(success(), stake.register_candidate(_alice, token._symbol.to_symbol_code()));
+ BOOST_CHECK_EQUAL(success(), stake.register_candidate(_bob, token._symbol.to_symbol_code()));
+ BOOST_CHECK_EQUAL(success(), stake.register_candidate(_carol, token._symbol.to_symbol_code()));
+ BOOST_CHECK_EQUAL(success(), stake.register_candidate(_whale, token._symbol.to_symbol_code()));
+ govern.wait_schedule_activation();
+ BOOST_CHECK_EQUAL(stake.get_candidate(_alice, token._symbol)["signing_key"].as(), get_public_key(_alice, "active"));
+ BOOST_CHECK_EQUAL(stake.get_candidate(_bob, token._symbol)["signing_key"].as(), get_public_key(_bob, "active"));
+ BOOST_CHECK_EQUAL(stake.get_candidate(_carol, token._symbol)["signing_key"].as(), get_public_key(_carol, "active"));
+ BOOST_CHECK_EQUAL(stake.get_candidate(_whale, token._symbol)["signing_key"].as(), get_public_key(_whale, "active"));
+ for (size_t r = 0; r < cfg::resets_limit; r++) {
+ BOOST_TEST_MESSAGE("--- reset " << r);
+ for (size_t i = 0; i < 2222; i++) {
+ produce_block(fc::milliseconds(config::block_interval_ms), 0, {_bob});
+ }
+ BOOST_CHECK_EQUAL(stake.get_candidate(_bob, token._symbol)["signing_key"].as(), public_key_type());
+ BOOST_CHECK_EQUAL(stake.get_agent(_bob, token._symbol)["proxy_level"], 0);
+ produce_block(fc::milliseconds(config::block_interval_ms), 0, {_bob});
+ produce_block(fc::seconds(cfg::key_recovery_delay) - fc::milliseconds(config::block_interval_ms), 0, {_bob});
+ BOOST_CHECK_EQUAL(success(), stake.setkey(_bob, token._symbol.to_symbol_code(), false));
+ }
+ for (size_t i = 0; i < 2222; i++) {
+ produce_block(fc::milliseconds(config::block_interval_ms), 0, {_bob});
+ }
+ BOOST_CHECK(stake.get_candidate(_bob, token._symbol).is_null());
+ BOOST_CHECK_EQUAL(stake.get_agent(_bob, token._symbol)["proxy_level"], 4);
+
+} FC_LOG_AND_RETHROW()
+
+BOOST_AUTO_TEST_SUITE_END() // reset_level
+
BOOST_AUTO_TEST_SUITE(emission)
BOOST_FIXTURE_TEST_CASE(no_staked_test, cyber_govern_tester) try {
BOOST_TEST_MESSAGE("emission/no_staked_test");
diff --git a/tests/cyber.msig_tests.cpp b/tests/cyber.msig_tests.cpp
index 53dc0e8ac..4209cc7d1 100644
--- a/tests/cyber.msig_tests.cpp
+++ b/tests/cyber.msig_tests.cpp
@@ -125,9 +125,27 @@ class cyber_msig_tester : public tester {
*/
}
+ auto push_action(name code, name signer, name action, const mvo& data, bool add_nested = false) {
+ vector auths{{signer, N(active)}};
+ auto trace = base_tester::push_action(code, action, auths, data, base_tester::DEFAULT_EXPIRATION_DELTA, 0, add_nested);
+ produce_block();
+ BOOST_REQUIRE_EQUAL(true, chain_has_transaction(trace->id));
+ return trace;
+ }
+
transaction reqauth( account_name from, const vector& auths, const fc::microseconds& max_serialization_time );
+ transaction reqauth_delayed(
+ account_name from, const vector& auths, const uint32_t delay, const fc::variants& add_actions = {});
abi_serializer abi_ser;
+
+ // api // can be moved to test_api when decide to refactor tests (leads to manual merge of upstream changes)
+ transaction_trace_ptr propose(
+ name proposer, name proposal, const transaction& trx, const vector& requested,
+ optional description = {});
+ transaction_trace_ptr approve(name proposer, name proposal, const permission_level& level);
+ transaction_trace_ptr schedule(name proposer, name proposal, name actor);
+ transaction_trace_ptr exec(name proposer, name proposal, name executer);
};
transaction cyber_msig_tester::reqauth( account_name from, const vector& auths, const fc::microseconds& max_serialization_time ) {
@@ -160,6 +178,76 @@ transaction cyber_msig_tester::reqauth( account_name from, const vector& auths, const uint32_t delay, const fc::variants& add_actions
+) {
+ fc::variants v;
+ for (auto& level: auths) {
+ v.push_back(mvo("actor", level.actor)("permission", level.permission));
+ }
+ fc::variants actions{add_actions};
+ actions.push_back(mvo
+ ("account", name{config::system_account_name})
+ ("name", "reqauth")
+ ("authorization", v)
+ ("data", mvo("from", from))
+ );
+ variant pretty_trx = mvo
+ ("expiration", "2020-01-01T00:30")
+ ("ref_block_num", 0)
+ ("ref_block_prefix", 0)
+ ("max_net_usage_words", 0)
+ ("max_cpu_usage_ms", 0)
+ ("max_ram_kbytes", 0)
+ ("max_storage_kbytes", 0)
+ ("delay_sec", delay)
+ ("actions", actions);
+ transaction trx;
+ abi_serializer::from_variant(pretty_trx, trx, get_resolver(), abi_serializer_max_time);
+ return trx;
+}
+
+
+transaction_trace_ptr cyber_msig_tester::propose(
+ name proposer, name proposal, const transaction& trx, const vector& requested, optional description
+) {
+ auto args = mvo
+ ("proposer", proposer)
+ ("proposal_name", proposal)
+ ("trx", trx)
+ ("requested", requested);
+ if (description) {
+ args = args("description", *description);
+ }
+ return push_action(proposer, N(propose), args);
+}
+
+transaction_trace_ptr cyber_msig_tester::approve(name proposer, name proposal, const permission_level& level) {
+ return push_action(level.actor, N(approve), mvo
+ ("proposer", proposer)
+ ("proposal_name", proposal)
+ ("level", level)
+ );
+}
+
+transaction_trace_ptr cyber_msig_tester::schedule(name proposer, name proposal, name actor) {
+ return push_action(actor, N(schedule), mvo
+ ("proposer", proposer)
+ ("proposal_name", proposal)
+ ("actor", actor)
+ );
+}
+
+transaction_trace_ptr cyber_msig_tester::exec(name proposer, name proposal, name executer) {
+ return push_action(config::msig_account_name, executer, N(exec), mvo
+ ("proposer", proposer)
+ ("proposal_name", proposal)
+ ("executer", executer),
+ true
+ );
+}
+
+
BOOST_AUTO_TEST_SUITE(cyber_msig_tests)
BOOST_FIXTURE_TEST_CASE( propose_approve_execute, cyber_msig_tester ) try {
@@ -173,10 +261,11 @@ BOOST_FIXTURE_TEST_CASE( propose_approve_execute, cyber_msig_tester ) try {
);
//fail to execute before approval
- BOOST_REQUIRE_EXCEPTION( push_action( N(alice), N(exec), mvo()
+ BOOST_REQUIRE_EXCEPTION(push_action(config::msig_account_name, N(alice), N(exec), mvo()
("proposer", "alice")
("proposal_name", "first")
("executer", "alice")
+ , true
),
eosio_assert_message_exception,
eosio_assert_message_is("transaction authorization failed")
@@ -190,14 +279,16 @@ BOOST_FIXTURE_TEST_CASE( propose_approve_execute, cyber_msig_tester ) try {
);
transaction_trace_ptr trace;
- control->applied_transaction.connect([&]( const transaction_trace_ptr& t) { if (t->scheduled) { trace = t; } } );
- push_action( N(alice), N(exec), mvo()
+ control->applied_transaction.connect([&]( const transaction_trace_ptr& t) { if (t->nested) { trace = t; } } );
+ push_action(config::msig_account_name, N(alice), N(exec), mvo()
("proposer", "alice")
("proposal_name", "first")
("executer", "alice")
+ , true
);
BOOST_REQUIRE( bool(trace) );
+ BOOST_TEST_MESSAGE("TRACE : " << fc::json::to_string(*trace));
BOOST_REQUIRE_EQUAL( 1, trace->action_traces.size() );
BOOST_REQUIRE_EQUAL( transaction_receipt::executed, trace->receipt->status );
} FC_LOG_AND_RETHROW()
@@ -225,10 +316,11 @@ BOOST_FIXTURE_TEST_CASE( propose_approve_unapprove, cyber_msig_tester ) try {
("level", permission_level{ N(alice), config::active_name })
);
- BOOST_REQUIRE_EXCEPTION( push_action( N(alice), N(exec), mvo()
+ BOOST_REQUIRE_EXCEPTION( push_action(config::msig_account_name, N(alice), N(exec), mvo()
("proposer", "alice")
("proposal_name", "first")
("executer", "alice")
+ , true
),
eosio_assert_message_exception,
eosio_assert_message_is("transaction authorization failed")
@@ -255,10 +347,11 @@ BOOST_FIXTURE_TEST_CASE( propose_approve_by_two, cyber_msig_tester ) try {
//fail because approval by bob is missing
- BOOST_REQUIRE_EXCEPTION( push_action( N(alice), N(exec), mvo()
+ BOOST_REQUIRE_EXCEPTION( push_action(config::msig_account_name, N(alice), N(exec), mvo()
("proposer", "alice")
("proposal_name", "first")
("executer", "alice")
+ , true
),
eosio_assert_message_exception,
eosio_assert_message_is("transaction authorization failed")
@@ -272,12 +365,13 @@ BOOST_FIXTURE_TEST_CASE( propose_approve_by_two, cyber_msig_tester ) try {
);
transaction_trace_ptr trace;
- control->applied_transaction.connect([&]( const transaction_trace_ptr& t) { if (t->scheduled) { trace = t; } } );
+ control->applied_transaction.connect([&]( const transaction_trace_ptr& t) { if (t->nested) { trace = t; } } );
- push_action( N(alice), N(exec), mvo()
+ push_action(config::msig_account_name, N(alice), N(exec), mvo()
("proposer", "alice")
("proposal_name", "first")
("executer", "alice")
+ , true
);
BOOST_REQUIRE( bool(trace) );
@@ -353,11 +447,11 @@ BOOST_FIXTURE_TEST_CASE( big_transaction, cyber_msig_tester ) try {
);
transaction_trace_ptr trace;
- control->applied_transaction.connect([&]( const transaction_trace_ptr& t) { if (t->scheduled) { trace = t; } } );
+ control->applied_transaction.connect([&]( const transaction_trace_ptr& t) { if (t->nested) { trace = t; } } );
- BOOST_REQUIRE_THROW(push_action( N(alice), N(exec), mvo("proposer", "alice")
+ BOOST_REQUIRE_THROW(push_action(config::msig_account_name, N(alice), N(exec), mvo("proposer", "alice")
("proposal_name", "first")
- ("executer", "alice")),
+ ("executer", "alice"), true),
fc::exception);
// TODO: Cyberway exchange_wasm is compiled for EOS
@@ -379,10 +473,11 @@ BOOST_FIXTURE_TEST_CASE( propose_approve_invalidate, cyber_msig_tester ) try {
);
//fail to execute before approval
- BOOST_REQUIRE_EXCEPTION( push_action( N(alice), N(exec), mvo()
+ BOOST_REQUIRE_EXCEPTION(push_action(config::msig_account_name, N(alice), N(exec), mvo()
("proposer", "alice")
("proposal_name", "first")
("executer", "alice")
+ , true
),
eosio_assert_message_exception,
eosio_assert_message_is("transaction authorization failed")
@@ -401,10 +496,11 @@ BOOST_FIXTURE_TEST_CASE( propose_approve_invalidate, cyber_msig_tester ) try {
);
//fail to execute after invalidation
- BOOST_REQUIRE_EXCEPTION( push_action( N(alice), N(exec), mvo()
+ BOOST_REQUIRE_EXCEPTION(push_action(config::msig_account_name, N(alice), N(exec), mvo()
("proposer", "alice")
("proposal_name", "first")
("executer", "alice")
+ , true
),
eosio_assert_message_exception,
eosio_assert_message_is("transaction authorization failed")
@@ -422,10 +518,11 @@ BOOST_FIXTURE_TEST_CASE( propose_invalidate_approve, cyber_msig_tester ) try {
);
//fail to execute before approval
- BOOST_REQUIRE_EXCEPTION( push_action( N(alice), N(exec), mvo()
+ BOOST_REQUIRE_EXCEPTION( push_action(config::msig_account_name, N(alice), N(exec), mvo()
("proposer", "alice")
("proposal_name", "first")
("executer", "alice")
+ , true
),
eosio_assert_message_exception,
eosio_assert_message_is("transaction authorization failed")
@@ -445,12 +542,13 @@ BOOST_FIXTURE_TEST_CASE( propose_invalidate_approve, cyber_msig_tester ) try {
//successfully execute
transaction_trace_ptr trace;
- control->applied_transaction.connect([&]( const transaction_trace_ptr& t) { if (t->scheduled) { trace = t; } } );
+ control->applied_transaction.connect([&]( const transaction_trace_ptr& t) { if (t->nested) { trace = t; } } );
- push_action( N(bob), N(exec), mvo()
+ push_action(config::msig_account_name, N(bob), N(exec), mvo()
("proposer", "alice")
("proposal_name", "first")
("executer", "bob")
+ , true
);
BOOST_REQUIRE( bool(trace) );
@@ -491,11 +589,12 @@ BOOST_FIXTURE_TEST_CASE( approve_with_hash, cyber_msig_tester ) try {
);
transaction_trace_ptr trace;
- control->applied_transaction.connect([&]( const transaction_trace_ptr& t) { if (t->scheduled) { trace = t; } } );
- push_action( N(alice), N(exec), mvo()
+ control->applied_transaction.connect([&]( const transaction_trace_ptr& t) { if (t->nested) { trace = t; } } );
+ push_action(config::msig_account_name, N(alice), N(exec), mvo()
("proposer", "alice")
("proposal_name", "first")
("executer", "alice")
+ , true
);
BOOST_REQUIRE( bool(trace) );
@@ -545,4 +644,123 @@ BOOST_FIXTURE_TEST_CASE( switch_proposal_and_fail_approve_with_hash, cyber_msig_
);
} FC_LOG_AND_RETHROW()
+BOOST_FIXTURE_TEST_CASE(propose_with_description, cyber_msig_tester) try {
+ auto trx = reqauth("alice", {permission_level{N(alice), config::active_name}}, abi_serializer_max_time);
+ const name alice = N(alice);
+ const name proposal = N(description);
+ const permission_level alice_perm{N(alice), config::active_name};
+
+ propose(alice, proposal, trx, {alice_perm}, "Propose to Hello world!");
+ approve(alice, proposal, alice_perm);
+
+ transaction_trace_ptr trace;
+ control->applied_transaction.connect([&](const transaction_trace_ptr& t) { if (t->nested) { trace = t; } });
+ exec(alice, proposal, alice);
+
+ BOOST_REQUIRE(bool(trace));
+ BOOST_REQUIRE_EQUAL(1, trace->action_traces.size());
+ BOOST_REQUIRE_EQUAL(transaction_receipt::executed, trace->receipt->status);
+} FC_LOG_AND_RETHROW()
+
+BOOST_FIXTURE_TEST_CASE(auth_wait, cyber_msig_tester) try {
+ BOOST_TEST_MESSAGE("Schedule (auth with waits)");
+ const name proposal = N(wait.auth);
+ const name active = config::active_name;
+ const name bps = N(bps);
+ const name alice = N(alice);
+ const name carol = N(carol);
+ const name bob = N(bob);
+ const permission_level alice_perm{alice, active};
+ const permission_level carol_perm{carol, active};
+ const permission_level bob_perm{bob, active};
+ const permission_level bps_perm{bps, active};
+ const int short_delay = 60;
+ const int long_delay = short_delay * 3;
+
+ signed_transaction trx_acc;
+ authority auth = authority(3, {},
+ {{alice_perm, 1}, {bob_perm, 1}, {carol_perm, 1}},
+ {{short_delay, 1}, {long_delay, 1}} // waits are cumulative
+ );
+ trx_acc.actions.emplace_back(vector{bob_perm}, newaccount{
+ .creator = bob,
+ .name = bps,
+ .active = auth,
+ .owner = authority(get_public_key(bps, "owner"))
+ });
+ set_transaction_headers(trx_acc);
+ trx_acc.sign(get_private_key(bob, "active"), control->get_chain_id());
+ auto trace = push_transaction(trx_acc);
+
+ const auto check_exec = [&](name proposer, name proposal) {
+ transaction_trace_ptr trace;
+ control->applied_transaction.connect([&](const transaction_trace_ptr& t) { if (t->nested) { trace = t; } });
+ exec(proposer, proposal, proposer);
+ BOOST_REQUIRE(bool(trace));
+ BOOST_REQUIRE_EQUAL(1, trace->action_traces.size());
+ BOOST_REQUIRE_EQUAL(transaction_receipt::executed, trace->receipt->status);
+ };
+ auto err_auth = eosio_assert_message_is{"transaction authorization failed"};
+ auto too_early = eosio_assert_message_is{"too early"};
+ auto not_scheduled = eosio_assert_message_is{"proposal was not scheduled"};
+ auto already_scheduled = eosio_assert_message_is{"proposal already scheduled"};
+
+ BOOST_TEST_MESSAGE("--- propose with long delay");
+ const vector all_perms{alice_perm, bob_perm, carol_perm};
+ auto trx = reqauth_delayed(bps, {bps_perm}, long_delay);
+ propose(bob, proposal, trx, all_perms);
+ BOOST_TEST_MESSAGE("----- fail to `schedule` when no approvals");
+ BOOST_REQUIRE_EXCEPTION(check_exec(bob, proposal), eosio_assert_message_exception, not_scheduled);
+ BOOST_REQUIRE_EXCEPTION(schedule(bob, proposal, bob), eosio_assert_message_exception, err_auth);
+ BOOST_TEST_MESSAGE("----- successful `schedule` when one approval + enough delay");
+ approve(bob, proposal, bob_perm);
+ produce_block();
+ schedule(bob, proposal, bob);
+ BOOST_REQUIRE_EXCEPTION(check_exec(bob, proposal), eosio_assert_message_exception, too_early);
+ BOOST_TEST_MESSAGE("----- wait one block before ready and check it's failing to exec");
+ int wait_blocks = long_delay / 3 - 1 - 1; // -1 = 1 block (within `schedule` call)
+ produce_blocks(wait_blocks);
+ BOOST_REQUIRE_EXCEPTION(check_exec(bob, proposal), eosio_assert_message_exception, too_early);
+ BOOST_TEST_MESSAGE("----- successful exec after waiting one more block");
+ produce_block();
+ check_exec(bob, proposal);
+
+ BOOST_TEST_MESSAGE("--- propose with short delay, can't execute earlier even with all approvals");
+ trx = reqauth_delayed(bps, {bps_perm}, short_delay);
+ propose(bob, proposal, trx, all_perms);
+ BOOST_TEST_MESSAGE("----- fail to `schedule` with one approval");
+ approve(bob, proposal, bob_perm);
+ BOOST_REQUIRE_EXCEPTION(schedule(bob, proposal, bob), eosio_assert_message_exception, err_auth);
+ BOOST_TEST_MESSAGE("----- fail to `exec` without `schedule` when have all approvals");
+ approve(bob, proposal, alice_perm);
+ BOOST_REQUIRE_EXCEPTION(check_exec(bob, proposal), eosio_assert_message_exception, not_scheduled);
+ approve(bob, proposal, carol_perm);
+ BOOST_REQUIRE_EXCEPTION(check_exec(bob, proposal), eosio_assert_message_exception, not_scheduled);
+ BOOST_TEST_MESSAGE("----- still fail to `exec` one block earlier than delay");
+ schedule(bob, proposal, bob);
+ BOOST_REQUIRE_EXCEPTION(schedule(bob, proposal, bob), eosio_assert_message_exception, already_scheduled);
+ BOOST_REQUIRE_EXCEPTION(check_exec(bob, proposal), eosio_assert_message_exception, too_early);
+ wait_blocks = short_delay / 3 - 1 - 1; // -1 = 1 block (within `schedule` call)
+ produce_blocks(wait_blocks);
+ BOOST_REQUIRE_EXCEPTION(check_exec(bob, proposal), eosio_assert_message_exception, too_early);
+ BOOST_TEST_MESSAGE("----- successful `exec` after delay");
+ produce_block();
+ check_exec(bob, proposal);
+
+} FC_LOG_AND_RETHROW()
+
+BOOST_FIXTURE_TEST_CASE(no_delay_schedule, cyber_msig_tester) try {
+ BOOST_TEST_MESSAGE("Schedule with 0 delay");
+ const name proposal = N(no.delay);
+ const name bob = N(bob);
+ const permission_level bob_perm{bob, config::active_name};
+
+ auto trx = reqauth(bob, {bob_perm}, abi_serializer_max_time);
+ propose(bob, proposal, trx, {bob_perm});
+ BOOST_REQUIRE_EXCEPTION(schedule(bob, proposal, bob), eosio_assert_message_exception,
+ eosio_assert_message_is("can't schedule transaction with zero delay_sec, call exec"));
+
+} FC_LOG_AND_RETHROW()
+
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/cyber.stake_test_api.hpp b/tests/cyber.stake_test_api.hpp
index e4c9f5f03..d3526341e 100644
--- a/tests/cyber.stake_test_api.hpp
+++ b/tests/cyber.stake_test_api.hpp
@@ -166,6 +166,13 @@ struct cyber_stake_api: base_contract_api {
("token_code", token_code)
);
}
+
+ action_result suspendcand(account_name account, symbol_code token_code) {
+ return push(N(suspendcand), account, args()
+ ("account", account)
+ ("token_code", token_code)
+ );
+ }
action_result reward(account_name issuer, account_name account, asset quantity) {
return push(N(reward), issuer, args()
@@ -203,6 +210,15 @@ struct cyber_stake_api: base_contract_api {
("token_code", token_code)
);
}
+
+ action_result setautorc(account_name account, symbol_code token_code, bool break_fee_enabled, bool break_min_stake_enabled) {
+ return push(N(setautorc), account, args()("account", account)("token_code", token_code)
+ ("break_fee_enabled", break_fee_enabled)("break_min_stake_enabled", break_min_stake_enabled));
+ }
+
+ action_result setautorcmode(account_name issuer, symbol_code token_code, bool enabled) {
+ return push(N(setautorcmode), issuer, args()("enabled", enabled)("token_code", token_code));
+ }
action_result register_candidate(account_name account, symbol_code token_code, bool need_to_open = true) {
if (need_to_open) {
@@ -322,6 +338,10 @@ struct cyber_stake_api: base_contract_api {
("last_reward", last_reward)
("enabled", enabled);
}
+
+ action_result return_losses(account_name caller) {
+ return push(N(returnlosses), caller, args());
+ }
};
}} // eosio::testing
diff --git a/tests/cyber.stake_tests.cpp b/tests/cyber.stake_tests.cpp
index a853b730c..bac44e612 100644
--- a/tests/cyber.stake_tests.cpp
+++ b/tests/cyber.stake_tests.cpp
@@ -116,6 +116,9 @@ class cyber_stake_tester : public golos_tester {
static string no_agent() {
return "agent doesn't exist";
}
+ const string agent_doesnt_exist() {
+ return amsg(no_agent());
+ }
const string agent_exists() {
return amsg(std::string("agent already exists"));
}
@@ -170,6 +173,9 @@ class cyber_stake_tester : public golos_tester {
const string too_many_proxies() {
return amsg(std::string("can't set proxy level, user has too many proxies"));
}
+ const string already_have_been_returned() {
+ return amsg(std::string("losses already have been returned"));
+ }
const string bad_precision = amsg("symbol precision mismatch");
const string quantity_bad_precision = amsg("quantity precision mismatch");
const string quantity_le0 = amsg("quantity must be positive");
@@ -207,6 +213,10 @@ class cyber_stake_tester : public golos_tester {
static bool is_block_res_limit_err_mssg(const std::string& arg) {
return arg.find("Block has insufficient resources") != std::string::npos;
}
+ const string no_params_changed = amsg("no params changed");
+ const string autorc_disabled = amsg("custom auto recall mode is disabled for this token");
+ const string autorc_already_disabled = amsg("custom auto recall mode is already disabled for this token");
+ const string autorc_already_enabled = amsg("custom auto recall mode is already enabled for this token");
} err;
};
@@ -1265,6 +1275,75 @@ BOOST_FIXTURE_TEST_CASE(reset_key_test, cyber_stake_tester) try {
BOOST_CHECK(!stake.get_candidate(_alice, token._symbol)["enabled"].as());
} FC_LOG_AND_RETHROW()
+BOOST_FIXTURE_TEST_CASE(custom_autorc_mode_test, cyber_stake_tester) try {
+ BOOST_TEST_MESSAGE("custom autorc mode test");
+ install_contract(config::system_account_name, contracts::bios_wasm(), contracts::bios_abi());
+ produce_block();
+ int64_t min_for_election = 1000;
+ int64_t stake_amount = 5000;
+ BOOST_CHECK_EQUAL(success(), token.create(_issuer, token.from_amount(100500)));
+ BOOST_CHECK_EQUAL(success(), token.issue(_issuer, _alice, token.from_amount(stake_amount), ""));
+ BOOST_CHECK_EQUAL(success(), token.issue(_issuer, _bob, token.from_amount(stake_amount), ""));
+ BOOST_CHECK_EQUAL(success(), token.issue(_issuer, _carol, token.from_amount(stake_amount), ""));
+ BOOST_CHECK_EQUAL(success(), stake.create(_issuer, token._symbol, {30, 10, 3, 1}, 100500, min_for_election));
+ int64_t alice_stake = min_for_election + 1;
+ BOOST_CHECK_EQUAL(success(), token.transfer(_alice, _code, token.from_amount(alice_stake)));
+ BOOST_CHECK_EQUAL(success(), stake.setminstaked(_alice, token._symbol.to_symbol_code(), alice_stake));
+ BOOST_CHECK_EQUAL(success(), stake.setproxylvl(_alice, token._symbol.to_symbol_code(), 0));
+ BOOST_CHECK_EQUAL(success(), stake.setproxyfee(_alice, token._symbol.to_symbol_code(), 2000));
+
+ BOOST_CHECK_EQUAL(success(), token.transfer(_bob, _code, token.from_amount(stake_amount)));
+ BOOST_CHECK_EQUAL(success(), token.transfer(_carol, _code, token.from_amount(stake_amount)));
+ BOOST_CHECK_EQUAL(success(), stake.delegatevote(_bob, _alice, token.from_amount(stake_amount)));
+ BOOST_CHECK_EQUAL(success(), stake.delegatevote(_carol, _alice, token.from_amount(stake_amount)));
+
+ BOOST_CHECK_EQUAL(stake.get_agent(_bob, token._symbol)["proxied"], stake_amount);
+ BOOST_CHECK_EQUAL(stake.get_agent(_carol, token._symbol)["proxied"], stake_amount);
+
+ BOOST_CHECK_EQUAL(err.autorc_disabled, stake.setautorc(_bob, token._symbol.to_symbol_code(), true, false));
+ BOOST_CHECK_EQUAL(err.autorc_already_disabled, stake.setautorcmode(_issuer, token._symbol.to_symbol_code(), false));
+ BOOST_CHECK_EQUAL(success(), stake.setautorcmode(_issuer, token._symbol.to_symbol_code(), true));
+ produce_block();
+ BOOST_CHECK_EQUAL(err.autorc_already_enabled, stake.setautorcmode(_issuer, token._symbol.to_symbol_code(), true));
+ BOOST_CHECK_EQUAL(success(), stake.setautorcmode(_issuer, token._symbol.to_symbol_code(), false));
+ BOOST_CHECK_EQUAL(err.autorc_disabled, stake.setautorc(_bob, token._symbol.to_symbol_code(), true, true));
+ produce_block();
+ BOOST_CHECK_EQUAL(success(), stake.setautorcmode(_issuer, token._symbol.to_symbol_code(), true));
+ BOOST_CHECK_EQUAL(err.no_params_changed, stake.setautorc(_bob, token._symbol.to_symbol_code(), false, false));
+ BOOST_CHECK_EQUAL(success(), stake.setautorc(_bob, token._symbol.to_symbol_code(), true, false));
+ produce_block();
+ BOOST_CHECK_EQUAL(err.no_params_changed, stake.setautorc(_bob, token._symbol.to_symbol_code(), true, false));
+ BOOST_CHECK_EQUAL(success(), stake.setautorc(_carol, token._symbol.to_symbol_code(), false, true));
+
+ BOOST_CHECK_EQUAL(success(), stake.updatefunds(_bob, token._symbol.to_symbol_code()));
+ BOOST_CHECK_EQUAL(success(), stake.updatefunds(_carol, token._symbol.to_symbol_code()));
+ BOOST_CHECK_EQUAL(stake.get_agent(_bob, token._symbol)["proxied"], stake_amount);
+ BOOST_CHECK_EQUAL(stake.get_agent(_carol, token._symbol)["proxied"], stake_amount);
+ produce_block();
+
+ BOOST_CHECK_EQUAL(success(), stake.setproxyfee(_alice, token._symbol.to_symbol_code(), 2001));
+ BOOST_CHECK_EQUAL(success(), stake.updatefunds(_bob, token._symbol.to_symbol_code()));
+ BOOST_CHECK_EQUAL(success(), stake.updatefunds(_carol, token._symbol.to_symbol_code()));
+
+ BOOST_CHECK_EQUAL(stake.get_agent(_bob, token._symbol)["proxied"], 0);
+ BOOST_CHECK_EQUAL(stake.get_agent(_bob, token._symbol)["balance"], stake_amount);
+ BOOST_CHECK_EQUAL(stake.get_agent(_carol, token._symbol)["proxied"], stake_amount);
+
+ BOOST_CHECK_EQUAL(success(), stake.delegatevote(_bob, _alice, token.from_amount(stake_amount)));
+ BOOST_CHECK_EQUAL(stake.get_agent(_bob, token._symbol)["proxied"], stake_amount);
+ produce_block();
+
+ BOOST_CHECK_EQUAL(err.min_for_election_violated(), stake.setminstaked(_alice, token._symbol.to_symbol_code(), min_for_election - 1));
+ BOOST_CHECK_EQUAL(success(), stake.setminstaked(_alice, token._symbol.to_symbol_code(), min_for_election));
+ BOOST_CHECK_EQUAL(success(), stake.updatefunds(_bob, token._symbol.to_symbol_code()));
+ BOOST_CHECK_EQUAL(success(), stake.updatefunds(_carol, token._symbol.to_symbol_code()));
+
+ BOOST_CHECK_EQUAL(stake.get_agent(_bob, token._symbol)["proxied"], stake_amount);
+ BOOST_CHECK_EQUAL(stake.get_agent(_carol, token._symbol)["proxied"], 0);
+ BOOST_CHECK_EQUAL(stake.get_agent(_carol, token._symbol)["balance"], stake_amount);
+ produce_block();
+} FC_LOG_AND_RETHROW()
+
BOOST_AUTO_TEST_SUITE_END() // agent_updates
BOOST_AUTO_TEST_SUITE(delegation)
@@ -1525,3 +1604,42 @@ BOOST_FIXTURE_TEST_CASE(recursive_update_test, cyber_stake_tester) try {
produce_block();
} FC_LOG_AND_RETHROW()
BOOST_AUTO_TEST_SUITE_END()
+
+BOOST_AUTO_TEST_SUITE(return_losses)
+
+BOOST_FIXTURE_TEST_CASE(returns, cyber_stake_tester) try {
+ std::vector max_proxies = {30, 10, 3, 1};
+
+ const account_name _mike2mike = N(dsqy4k5ym3uk);
+ const account_name _ltrack = N(ym2imjop4l5n);
+ const account_name _drugan7 = N(rzi4tcizzvyw);
+ const account_name _gunblade = N(xt351mrztghr);
+
+ BOOST_TEST_MESSAGE("-- create accounts");
+ create_accounts({_mike2mike, _ltrack, _drugan7, _gunblade});
+
+ BOOST_TEST_MESSAGE("-- open stake accounts");
+ BOOST_CHECK_EQUAL(success(), token.create(_issuer, asset(1000000, token._symbol)));
+ BOOST_CHECK_EQUAL(success(), stake.create(_issuer, token._symbol, max_proxies, 30 * 24 * 60 * 60));
+ BOOST_CHECK_EQUAL(success(), stake.open(_mike2mike, token._symbol.to_symbol_code()));
+ BOOST_CHECK_EQUAL(success(), stake.open(_ltrack, token._symbol.to_symbol_code()));
+ BOOST_CHECK_EQUAL(success(), stake.open(_drugan7, token._symbol.to_symbol_code()));
+
+ BOOST_TEST_MESSAGE("-- try to return losses with closed account for gunblade");
+ BOOST_CHECK_EQUAL(err.agent_doesnt_exist(), stake.return_losses(_issuer));
+
+ produce_block();
+
+ BOOST_TEST_MESSAGE("-- open stake account for gunblade");
+ BOOST_CHECK_EQUAL(success(), stake.open(_gunblade, token._symbol.to_symbol_code()));
+
+ BOOST_TEST_MESSAGE("-- return losses");
+ BOOST_CHECK_EQUAL(success(), stake.return_losses(_issuer));
+
+ produce_block();
+
+ BOOST_TEST_MESSAGE("-- can't return losses again");
+ BOOST_CHECK_EQUAL(err.already_have_been_returned(), stake.return_losses(_issuer));
+} FC_LOG_AND_RETHROW()
+
+BOOST_AUTO_TEST_SUITE_END()
\ No newline at end of file