diff --git a/CMakeLists.txt b/CMakeLists.txt index f47d40a68..ed7de599a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ project(eosio_contracts) set(VERSION_MAJOR 3) set(VERSION_MINOR 0) -set(VERSION_PATCH 0) +set(VERSION_PATCH 1) #set(VERSION_SUFFIX rc3) if (VERSION_SUFFIX) diff --git a/README.md b/README.md index a6cda1871..35c6d35b3 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # bos.contracts -## BOSCore Version : v3.0.0 +## BOSCore Version : v3.0.1 ### EOSIO Contracts Version: v1.6.0 The design of the EOSIO blockchain calls for a number of smart contracts that are run at a privileged permission level in order to support functions such as block producer registration and voting, token staking for CPU and network bandwidth, RAM purchasing, multi-sig, etc. These smart contracts are referred to as the system, token, msig and wrap (formerly known as sudo) contracts. diff --git a/contracts/eosio.msig/include/eosio.msig/eosio.msig.hpp b/contracts/eosio.msig/include/eosio.msig/eosio.msig.hpp index 9589fe52a..8339e5312 100755 --- a/contracts/eosio.msig/include/eosio.msig/eosio.msig.hpp +++ b/contracts/eosio.msig/include/eosio.msig/eosio.msig.hpp @@ -23,6 +23,16 @@ namespace eosio { void exec( name proposer, name proposal_name, name executer ); [[eosio::action]] void invalidate( name account ); + [[eosio::action]] + void oppose( name proposer, name proposal_name, permission_level level, + const eosio::binary_extension& proposal_hash ); + [[eosio::action]] + void unoppose( name proposer, name proposal_name, permission_level level ); + [[eosio::action]] + void abstain( name proposer, name proposal_name, permission_level level, + const eosio::binary_extension& proposal_hash ); + [[eosio::action]] + void unabstain( name proposer, name proposal_name, permission_level level ); using propose_action = eosio::action_wrapper<"propose"_n, &multisig::propose>; using approve_action = eosio::action_wrapper<"approve"_n, &multisig::approve>; @@ -67,6 +77,17 @@ namespace eosio { }; typedef eosio::multi_index< "approvals2"_n, approvals_info > approvals; + //one can only approval, or oppose, or abstain. + //if he/she wants to change opinion of oppose, needs to unoppose first. + //oppose/abstain won't be counted in when trying exec. only approvals are counted. + struct [[eosio::table]] opposes_info { + name proposal_name; + std::vector opposed_approvals; + std::vector abstained_approvals; + uint64_t primary_key() const { return proposal_name.value; } + }; + typedef eosio::multi_index< "opposes"_n, opposes_info > opposes; + struct [[eosio::table]] invalidation { name account; time_point last_invalidation_time; diff --git a/contracts/eosio.msig/src/eosio.msig.cpp b/contracts/eosio.msig/src/eosio.msig.cpp index c0baad73c..01383051f 100755 --- a/contracts/eosio.msig/src/eosio.msig.cpp +++ b/contracts/eosio.msig/src/eosio.msig.cpp @@ -69,6 +69,16 @@ void multisig::approve( name proposer, name proposal_name, permission_level leve assert_sha256( prop.packed_transaction.data(), prop.packed_transaction.size(), *proposal_hash ); } + //check whether opposed or abstained to this proposal. + opposes oppotable(_self, proposer.value ); + auto oppos_it = oppotable.find(proposal_name.value ); + if(oppos_it != oppotable.end() ){ + auto check_itr = std::find( oppos_it->opposed_approvals.begin(), oppos_it->opposed_approvals.end(), level ); + check( check_itr == oppos_it->opposed_approvals.end(), "you already opposed this proposal" ); + check_itr = std::find( oppos_it->abstained_approvals.begin(), oppos_it->abstained_approvals.end(), level ); + check( check_itr == oppos_it->abstained_approvals.end(), "you already abstained this proposal" ); + } + approvals apptable( _self, proposer.value ); auto apps_it = apptable.find( proposal_name.value ); if ( apps_it != apptable.end() ) { @@ -139,6 +149,138 @@ void multisig::cancel( name proposer, name proposal_name, name canceler ) { check( apps_it != old_apptable.end(), "proposal not found" ); old_apptable.erase(apps_it); } + + //remove from oppose table + opposes oppotable(_self, proposer.value ); + auto oppo_it = oppotable.find( proposal_name.value ); + if( oppo_it != oppotable.end() ){ + oppotable.erase(oppo_it); + } +} + +void multisig::oppose( name proposer, name proposal_name, permission_level level, + const eosio::binary_extension& proposal_hash ){ + require_auth( level ); + if( proposal_hash ) { + proposals proptable( _self, proposer.value ); + auto& prop = proptable.get( proposal_name.value, "proposal not found" ); + assert_sha256( prop.packed_transaction.data(), prop.packed_transaction.size(), *proposal_hash ); + } + + //check whether level is in requested approvals or already approved to it + approvals apptable( _self, proposer.value ); + auto app_it = apptable.find(proposal_name.value ); + if( app_it != apptable.end() ){ + auto requested_it = std::find_if( app_it->requested_approvals.begin(), app_it->requested_approvals.end(), [&](const approval& a) { return a.level == level; } ); + bool requested = (requested_it != app_it->requested_approvals.end()); + auto approved_it = std::find_if( app_it->provided_approvals.begin(), app_it->provided_approvals.end(), [&](const approval &a){ return a.level == level;} ); + bool approved = (approved_it != app_it->provided_approvals.end()); + check( requested && !approved , "provided permission not requested, or you have approved to this proposal" ); + }else{ + old_approvals old_apptable( _self, proposer.value ); + auto old_app_it = old_apptable.find( proposal_name.value ); + if( old_app_it != old_apptable.end() ){ + auto requested_it = std::find( old_app_it->requested_approvals.begin(), old_app_it->requested_approvals.end(), level ); + bool requested = (requested_it != old_app_it->requested_approvals.end()); + auto approved_it = std::find( old_app_it->provided_approvals.begin(), old_app_it->provided_approvals.end(), level ); + bool approved = (approved_it != old_app_it->provided_approvals.end()); + check( requested && !approved , "provided permission not requested, or you have approved to this proposal" ); + } + } + + //check whether abstained/opposed to this proposal + //update opposed + opposes oppotable( _self, proposer.value ); + auto oppo_it = oppotable.find( proposal_name.value ); + if ( oppo_it != oppotable.end() ) { + auto abs_check_it = std::find( oppo_it->abstained_approvals.begin(), oppo_it->abstained_approvals.end(), level ); + check( abs_check_it == oppo_it->abstained_approvals.end(), "you have abstained to this proposal" ); + auto oppo_check_it = std::find( oppo_it->opposed_approvals.begin(), oppo_it->opposed_approvals.end(), level); + check( oppo_check_it == oppo_it->opposed_approvals.end(), "you have opposed to this proposal" ); + oppotable.modify( oppo_it, proposer, [&]( auto& o ) { + o.opposed_approvals.push_back( level ); + }); + }else{ + oppotable.emplace( proposer, [&]( auto& o ) { + o.proposal_name = proposal_name; + o.opposed_approvals.push_back( level ); + }); + } +} + +void multisig::unoppose( name proposer, name proposal_name, permission_level level ){ + require_auth( level ); + + opposes oppotable( _self, proposer.value ); + auto& oppo_it = oppotable.get( proposal_name.value, "can't find this proposal in opposes table" ); + auto itr = std::find( oppo_it.opposed_approvals.begin(), oppo_it.opposed_approvals.end(), level ); + check( itr != oppo_it.opposed_approvals.end(), "no oppose found" ); + oppotable.modify( oppo_it, proposer, [&]( auto& o ) { + o.opposed_approvals.erase( itr ); + }); +} + +void multisig::abstain( name proposer, name proposal_name, permission_level level, + const eosio::binary_extension& proposal_hash ){ + require_auth( level ); + + if( proposal_hash ) { + proposals proptable( _self, proposer.value ); + auto& prop = proptable.get( proposal_name.value, "proposal not found" ); + assert_sha256( prop.packed_transaction.data(), prop.packed_transaction.size(), *proposal_hash ); + } + + //check whether level is in requested approvals or already approved to it + approvals apptable( _self, proposer.value ); + auto app_it = apptable.find(proposal_name.value ); + if( app_it != apptable.end() ){ + auto requested_it = std::find_if( app_it->requested_approvals.begin(), app_it->requested_approvals.end(), [&](const approval& a) { return a.level == level; } ); + bool requested = (requested_it != app_it->requested_approvals.end()); + auto approved_it = std::find_if( app_it->provided_approvals.begin(), app_it->provided_approvals.end(), [&](const approval &a){ return a.level == level;} ); + bool approved = (approved_it != app_it->provided_approvals.end()); + check( requested && !approved , "provided permission not requested, or you have approved to this proposal" ); + }else{ + old_approvals old_apptable( _self, proposer.value ); + auto old_app_it = old_apptable.find( proposal_name.value ); + if( old_app_it != old_apptable.end() ){ + auto requested_it = std::find( old_app_it->requested_approvals.begin(), old_app_it->requested_approvals.end(), level ); + bool requested = (requested_it != old_app_it->requested_approvals.end()); + auto approved_it = std::find( old_app_it->provided_approvals.begin(), old_app_it->provided_approvals.end(), level ); + bool approved = (approved_it != old_app_it->provided_approvals.end()); + check( requested && !approved , "provided permission not requested, or you have approved to this proposal" ); + } + } + + //check whether abstained/opposed to this proposal + //update abstained + opposes oppotable( _self, proposer.value ); + auto abs_it = oppotable.find( proposal_name.value ); + if ( abs_it != oppotable.end() ) { + auto abs_check_it = std::find( abs_it->abstained_approvals.begin(), abs_it->abstained_approvals.end(), level ); + check( abs_check_it == abs_it->abstained_approvals.end(), "you have abstained to this proposal" ); + auto oppo_check_it = std::find( abs_it->opposed_approvals.begin(), abs_it->opposed_approvals.end(), level); + check( oppo_check_it == abs_it->opposed_approvals.end(), "you have opposed to this proposal" ); + oppotable.modify( abs_it, proposer, [&]( auto& a ) { + a.abstained_approvals.push_back( level ); + }); + }else{ + oppotable.emplace( proposer, [&]( auto& a ) { + a.proposal_name = proposal_name; + a.abstained_approvals.push_back( level ); + }); + } +} + +void multisig::unabstain( name proposer, name proposal_name, permission_level level ){ + require_auth( level ); + + opposes oppotable( _self, proposer.value ); + auto& abs_it = oppotable.get( proposal_name.value, "can't find this proposal in opposes table" ); + auto itr = std::find( abs_it.abstained_approvals.begin(), abs_it.abstained_approvals.end(), level ); + check( itr != abs_it.abstained_approvals.end(), "no abstain found" ); + oppotable.modify( abs_it, proposer, [&]( auto& a ) { + a.abstained_approvals.erase( itr ); + }); } void multisig::exec( name proposer, name proposal_name, name executer ) { @@ -186,6 +328,13 @@ void multisig::exec( name proposer, name proposal_name, name executer ) { prop.packed_transaction.data(), prop.packed_transaction.size() ); proptable.erase(prop); + + //clear opposes + opposes oppotable(_self, proposer.value); + auto oppos_it = oppotable.find( proposal_name.value ); + if( oppos_it != oppotable.end() ){ + oppotable.erase( oppos_it ); + } } void multisig::invalidate( name account ) { @@ -206,4 +355,4 @@ void multisig::invalidate( name account ) { } /// namespace eosio -EOSIO_DISPATCH( eosio::multisig, (propose)(approve)(unapprove)(cancel)(exec)(invalidate) ) +EOSIO_DISPATCH( eosio::multisig, (propose)(approve)(unapprove)(cancel)(oppose)(unoppose)(abstain)(unabstain)(exec)(invalidate) ) diff --git a/contracts/eosio.system/ricardian/eosio.system.contracts.md b/contracts/eosio.system/ricardian/eosio.system.contracts.md new file mode 100644 index 000000000..815f9cf41 --- /dev/null +++ b/contracts/eosio.system/ricardian/eosio.system.contracts.md @@ -0,0 +1,511 @@ +

+ bidname +

+--- +spec-version: 0.0 +title: Bid on premium account name +summary: The {{ bidname }} action places a bid on a premium account name, in the knowledge that the high bid will purchase the name. +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- + +As an authorized party I {{ signer }} wish to bid on behalf of {{ bidder }} the amount of {{ bid }} toward purchase of the account name {{ newname }}. + +

+ buyram +

+--- +spec-version: 0.0 +title: Buy RAM +summary: This action will attempt to reserve about {{quant}} worth of RAM on behalf of {{receiver}}. +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- + +{{buyer}} authorizes this contract to transfer {{quant}} to buy RAM based upon the current price as determined by the market maker algorithm. + +{{buyer}} accepts that a 0.5% fee will be charged on the amount spent and that the actual RAM received may be slightly less than expected due to the approximations necessary to enable this service. {{buyer}} accepts that a 0.5% fee will be charged if and when they sell the RAM received. {{buyer}} accepts that rounding errors resulting from limits of computational precision may result in less RAM being allocated. {{buyer}} acknowledges that the supply of RAM may be increased at any time up to the limits of off-the-shelf computer equipment and that this may result in RAM selling for less than purchase price. {{buyer}} acknowledges that the price of RAM may increase or decrease over time according to supply and demand. {{buyer}} acknowledges that RAM is non-transferrable. {{buyer}} acknowledges RAM currently in use by their account cannot be sold until it is freed and that freeing RAM may be subject to terms of other contracts. + +

+ buyrambytes +

+--- +spec-version: 0.0 +title: Buy RAM Bytes +summary: This action will attempt to reserve about {{bytes}} bytes of RAM on behalf of {{receiver}}. +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- + +{{buyer}} authorizes this contract to transfer sufficient EOS tokens to buy the RAM based upon the current price as determined by the market maker algorithm. + +{{buyer}} accepts that a 0.5% fee will be charged on the EOS spent and that the actual RAM received may be slightly less than requested due to the approximations necessary to enable this service. {{buyer}} accepts that a 0.5% fee will be charged if and when they sell the RAM received. {{buyer}} accepts that rounding errors resulting from limits of computational precision may result in less RAM being allocated. {{buyer}} acknowledges that the supply of RAM may be increased at any time up to the limits of off-the-shelf computer equipment and that this may result in RAM selling for less than purchase price. {{buyer}} acknowledges that the price of RAM may increase or decrease over time according to supply and demand. {{buyer}} acknowledges that RAM is non-transferable. {{buyer}} acknowledges RAM currently in use by their account cannot be sold until it is freed and that freeing RAM may be subject to terms of other contracts. + +

+ canceldelay +

+--- +spec-version: 0.0 +title: Cancel delayed transaction +summary: The {{ canceldelay }} action cancels an existing delayed transaction. +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- + +As an authorized party I {{ signer }} wish to invoke the authority of {{ canceling_auth }} to cancel the transaction with ID {{ trx_id }}. + +

+ claimrewards +

+--- +spec-version: 0.0 +title: Claim rewards +summary: The {{ claimrewards }} action allows a block producer (active or standby) to claim the system rewards due them for producing blocks and receiving votes. +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- + +As an authorized party I {{ signer }} wish to have the rewards earned by {{ owner }} deposited into the {{ owner }} account. + +

+ delegatebw +

+--- +spec-version: 0.0 +title: Delegate Bandwidth +summary: The intent of the {{ delegatebw }} action is to stake tokens for bandwidth and/or CPU and optionally transfer ownership. +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- + +As an authorized party I {{ signer }} wish to stake {{ stake_cpu_quantity }} for CPU and {{ stake_net_quantity }} for bandwidth from the liquid tokens of {{ from }} for the use of delegatee {{ to }}. + +

+ newaccount +

+--- +spec-version: 0.0 +title: Create a new account +summary: The {{ newaccount }} action creates a new account. +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- + +As an authorized party I {{ signer }} wish to exercise the authority of {{ creator }} to create a new account on this system named {{ name }} such that the new account's owner public key shall be {{ owner }} and the active public key shall be {{ active }}. + +

+ refund +

+--- +spec-version: 0.0 +title: Refund unstaked tokens +summary: The intent of the {{ refund }} action is to return previously unstaked tokens to an account after the unstaking period has elapsed. +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- + +As an authorized party I {{ signer }} wish to have the unstaked tokens of {{ owner }} returned. + +

+ sellram +

+--- +spec-version: 0.0 +title: Sell Ram +summary: The {{ sellram }} action sells unused RAM for tokens. +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- +As an authorized party I {{ signer }} wish to sell {{ bytes }} of unused RAM from account {{ account }}. + +

+ setprods +

+--- +spec-version: 0.0 +title: Set new producer schedule +summary: The {{ setprods }} action creates a new schedule of active producers, who will produce blocks in the order given. +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- +THIS IS A SYSTEM COMMAND NOT AVAILABLE FOR DIRECT ACCESS BY USERS. + +As an authorized party I {{ signer }} wish to set the rotation of producers to be {{ schedule }}. + +

+ undelegatebw +

+--- +spec-version: 0.0 +title: Undelegate bandwidth +summary: The intent of the {{ undelegatebw }} action is to unstake tokens from CPU and/or bandwidth. +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- +As an authorized party I {{ signer }} wish to unstake {{ unstake_cpu_quantity }} from CPU and {{ unstake_net_quantity }} from bandwidth from the tokens owned by {{ from }} previously delegated for the use of delegatee {{ to }}. + +

+ regproducer +

+--- +spec-version: 0.0 +title: register as Block Producer +summary: The {{ regprod }} action registers a new block producer candidate. +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- +As an authorized party I {{ signer }} wish to register the block producer candidate {{ producer }}. + +

+ unregprod +

+--- +spec-version: 0.0 +title: Unregister as Block Producer +summary: The {{ unregprod }} action unregisters a previously registered block producer candidate. +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- +As an authorized party I {{ signer }} wish to unregister the block producer candidate {{ producer }}, rendering that candidate no longer able to receive votes. + +

+ voteproducer +

+--- +spec-version: 0.0 +title: Vote for Block Producer(s) +summary: The intent of the {{ voteproducer }} action is to cast a valid vote for up to 30 BP candidates. +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- + +As an authorized party I {{ signer }} wish to vote on behalf of {{ voter }} in favor of the block producer candidates {{ producers }} with a voting weight equal to all tokens currently owned by {{ voter }} and staked for CPU or bandwidth. + +

+ regproxy +

+--- +spec-version: 0.0 +title: Register account as a proxy (for voting) +summary: The intent of the {{ regproxy }} action is to register an account as a proxy for voting. +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- + +As an authorized party I {{ signer }} wish to register as a {{ proxy }} account to vote on block producer candidates {{ producers }} with a voting weight equal to all tokens currently owned by {{ signer }} and tokens voted through registered {{ proxy }} by others. + +

+ updateauth +

+--- +spec-version: 0.0 +title: Update authorization of account +summary: The intent of {{ updateauth }} action is to update the authorization of an account. +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- + +As an authorized party I {{ signer }} wish to update the {{ authorization }} and {{ weight }} on {{ account }}. + +With `updateauth` you can update the {{ authorization }} on stated {{ account }}. + +

+ deleteauth +

+--- +spec-version: 0.0 +title: delete authorization of account +summary: The intent of {{ deleteauth }} action is to delete the authentication(s) of an account. +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- + +As an authorized party I {{ signer }} wish to delete the stated {{ authorization }} on {{ account }}. + +With `deleteauth` you can delete the {{ authorization }} on stated {{ account }}. + +

+ linkauth +

+--- +spec-version: 0.0 +title: link authorization with account, code, type or requirement. +summary: The intent of {{ linkauth }} action is to authorize a set permission. +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- + +As an authorized party I {{ signer }} wish to link the {{ authorization }} on {{ account }} with stated {{ permission }}. + +The `linkauth` action is used to set an authorization between a stated permission and the provided key or account. + +

+ unlinkauth +

+--- +spec-version: 0.0 +title: unlink authorization with account, code, type or requirement. +summary: The intent of {{ unlinkauth }} action is to remove the authorization of a permission. +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- + +As an authorized party I {{ signer }} wish to remove the linked {{ authorization }} on {{ account }} with stated {{ permission }}. + +The `unlinkauth` action is used to remove an linked authorization between a stated permission and the provided key or account. + +

+ setabi +

+--- +spec-version: 0.0 +title: Set ABI +summary: Set ABI to account. +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- + +As an authorized party I {{ signer }} wish to set ABI to {{ account }}. + +

+ buyrex +

+--- +spec-version: 0.0 +title: Buy REX +summary: + The `buyrex` action allows an account to buy REX in exchange for tokens taken + out of the user's REX fund. +icon: + https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- + +As an authorized party I {{ signer }} wish to buy REX on the account {{ from }} with the amount {{ amount }} EOS. I am aware of, and have fulfilled, all voting requirements needed to participate in the REX marketplace.  + +

+ closerex +

+--- + +spec-version: 0.0 +title: Close REX +summary: The `closerex` action allows an account to delete unused REX-related database entries and frees occupied RAM associated with its storage. +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- + +As an authorized party, I {{ signer }}, wish to delete all unused REX-related database entries from the account {{ owner }}. + +I will not be able to successfully call `closerex` unless all checks for CPU loans, NET loans or refunds pending refunds are still processing on the account {{ owner }}. + +

+ cnclrexorder +

+--- + +spec-version: 0.0 +title: Cancel REX order +summary: The `cnclrexorder` action cancels a queued REX sell order if one exists for an account. + +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- + +As an authorized party I, {{ signer }}, wish to cancel any unfilled and queued REX sell orders that exist for the account {{ owner }}. + +

+ consolidate +

+--- + +spec-version: 0.0 +title: Consolidate REX +summary: The `consolidate` action will consolidate all REX maturity buckets for an account into one that matures 4 days from 00:00 UTC. + +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- + +As an authorized party I, {{ signer }}, wish to consolidate any open REX maturity buckets for the account {{ owner }} into one that matures 4 days from the following 00:00 UTC. + +

+ defcpuloan +

+--- + +spec-version: 0.0 +title: Withdraw CPU loan +summary: The `defcpuloan` action allows an account to withdraw tokens from the fund of a specific CPU loan and adds them to REX fund. + +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- + +As an authorized party I, {{ signer }}, wish to withdraw from the CPU loan fund identified by loan number {{ loan_num }} on the account {{ from }} in the amount of {{ amount }} and have those tokens allocated to the REX fund of {{ from }}. + +

+ defnetloan +

+--- + +spec-version: 0.0 +title: Withdraw NET loan +summary: The `defnetloan` action allows an account to withdraw tokens from the fund of a specific Network loan and adds them to REX fund. + +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- + +As an authorized party I, {{ signer }}, wish to withdraw from the Network loan fund identified by loan number {{ loan_num }} on the account {{ from }} in the amount of {{ amount }} and have those tokens allocated to the REX fund of {{ from }}. + +

+ deposit +

+--- + +spec-version: 0.0 +title: Deposit EOS into REX +summary: The `deposit` action allows an account to deposit EOS tokens into REX fund by transfering from their liquid token balance. + +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- + +As an authorized party I, {{ signer }}, wish to deposit {{ amount }} EOS tokens into the REX fund of the account {{ owner }} from the liquid token balance of {{ owner }}. + +

+ fundcpuloan +

+--- + +spec-version: 0.0 +title: Fund CPU Loan +summary: The `fundcpuloan` action allows an account to transfer tokens from its REX fund to the fund of a specific CPU loan in order for those tokens to be used for loan renewal at the loan's expiry. + +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- + +As an authorized party I, {{ signer }}, wish to transfer the amount of {{ payment }} tokens into the CPU loan fund of the loan identified by loan number {{ loan_num }} from the account {{ from }} to be used for loan renewal at the expiry of {{ loan_num }}. + +

+ fundnetloan +

+--- + +spec-version: 0.0 +title: Fund NET Loan +summary: The `fundnetloan` action allows an account to transfer tokens from its REX fund to the fund of a specific Network loan in order for those tokens to be used for loan renewal at the loan's expiry. + +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- + +As an authorized party I, {{ signer }}, wish to transfer the amount of {{ payment }} tokens into the Network loan fund of the loan identified by loan number {{ loan_num }} from the account {{ from }} to be used for loan renewal at the expiry of {{ loan_num }}. + +

+ mvfrsavings +

+--- + +spec-version: 0.0 +title: Move REX from savings +summary: The `mvfrsavings` action allows an account to move REX tokens from its savings bucket to a bucket with a maturity date that is 4 days after 00:00 UTC. + +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- + +As an authorized party I, {{ signer }}, wish to move {{ rex }} tokens from the savings bucket of the account {{ owner }}. Those tokens shall become available to {{ owner }} 4 days from 00:00 UTC. + +

+ mvtosavings +

+--- + +spec-version: 0.0 +title: Move REX to savings +summary: The `mvtosavings` action allows an account to move REX tokens into a savings bucket. + +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- + +As an authorized party I, {{ signer }}, wish to move {{ rex }} tokens to a savings bucket associated to the account {{ owner }}. I acknowledge that those tokens will then be subject to any maturity restrictions described in the `mvfrsavings` action. + +

+ updaterex +

+--- + +spec-version: 0.0 +title: Update REX +summary: The `updaterex` action updates the vote stake of the account. + +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- + +As an authorized party I, {{ signer }}, wish to update the vote stake of account to the current value of my {{ REX }} balance. + +

+ rentcpu +

+--- + +spec-version: 0.0 +title: Rent CPU +summary: The `rentcpu` action allows an account to rent CPU bandwidth for 30 days at a market-determined price. + +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- + +As an authorized party I, {{ signer }}, wish to rent CPU bandwidth for 30 days for the use of the account {{ receiver }} in exchange for the loan payment of {{ loan_payment }}, which shall be taken from the account {{ from }}. The loan fund amount {{ loan_fund }} is set for automatic renewal of the loan at the expiry of said loan. + +The amount of CPU bandwidth shall be determined by the market at time of loan execution and shall be recalculated at time of renewal, should I wish to automatically renew the loan at that time. I acknowledge that the amount of CPU bandwidth received in exchange of {{ loan_payment }} for the benefit of {{ receiver }} at loan renewal may be different from the current amount of bandwidth received. + +

+ rentnet +

+--- + +spec-version: 0.0 +title: Rent NET +summary: The `rentnet` action allows an account to rent Network bandwidth for 30 days at a market-determined price. + +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- + +As an authorized party I, {{ signer }}, wish to rent Network bandwidth for 30 days for the use of the account {{ receiver }} in exchange for the loan payment of {{ loan_payment }}, which shall be taken from the account {{ from }}. The loan fund amount {{ loan_fund }} is set for automatic renewal of the loan at the expiry of said loan. + +The amount of Network bandwidth shall be determined by the market at time of loan execution and shall be recalculated at time of renewal, should I wish to automatically renew the loan at that time. I acknowledge that the amount of Network bandwidth received in exchange of {{ loan_payment }} for the benefit of {{ receiver }} at loan renewal may be different from the current amount of bandwidth received. + +

+ rexexec +

+--- + +spec-version: 0.0 +title: REX Exec +summary: The `rexexec` action allows any account to perform REX maintenance by processing expired loans and unfilled sell orders. + +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- + +I, {{ signer }}, wish to process up to {{ max }} of any CPU loans, Network loans, and sell orders that may currently be pending. + +

+ sellrex +

+--- + +spec-version: 0.0 +title: Sell REX +summary: The `sellrex` action allows an account to sell REX tokens held by the account. + +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- + +As an authorized party I, {{ signer }}, wish to sell {{ rex }} REX tokens held on the account {{ from }} in exchange for core EOS tokens. If there is an insufficient amount of EOS tokens available at this time, I acknowledge that my order will be placed in a queue to be processed. + +If there is an open `sellrex` order for the account {{ from }}, then this amount of {{ rex }} REX shall be added to the existing order and the order shall move to the back of the queue. + +

+ unstaketorex +

+--- + +spec-version: 0.0 +title: Unstake to REX +summary: The `unstaketorex` action allows an account to buy REX using EOS tokens which are currently staked for either CPU or Network bandwidth. + +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- + +As an authorized party I, {{ signer }}, wish to buy REX tokens by unstaking {{ from_cpu }} EOS from CPU bandwidth and {{ from_net }} EOS from Network bandwidth from account {{ owner }} that are staked to account {{ receiver }}. + +I am aware of, and have fulfilled, all voting requirements needed to participate in the REX marketplace. + +

+ withdraw +

+--- + +spec-version: 0.0 +title: withdraw from REX +summary: The `withdraw` action allows an account to withdraw EOS tokens from their REX fund into their liquid token balance. + + +icon: https://boscore.io/icon_256.png#b264b855c6d3335e5ee213443f679fb87c3633de8bc31cf66a766daac6dc6d7c +--- + +As an authorized party I, {{ signer }}, wish to withdraw {{ amount }} of EOS tokens from the REX fund for the account {{ owner }} into its liquid token balance. \ No newline at end of file diff --git a/contracts/eosio.token/src/eosio.token.cpp b/contracts/eosio.token/src/eosio.token.cpp index 5d6ceebb2..ca494574f 100755 --- a/contracts/eosio.token/src/eosio.token.cpp +++ b/contracts/eosio.token/src/eosio.token.cpp @@ -170,7 +170,7 @@ void token::close( name owner, const symbol& symbol ) ///bos begin void token::addblacklist(const std::vector& accounts) { - require_auth("eosio"_n); + require_auth(_self); eosio_assert(blacklist_limit_size >= accounts.size(), "accounts' size must be less than 100."); static const std::string msg = std::string(" account does not exist"); @@ -193,7 +193,7 @@ void token::addblacklist(const std::vector& accounts) void token::rmblacklist(const std::vector& accounts) { - require_auth("eosio"_n); + require_auth(_self); eosio_assert( blacklist_limit_size>=accounts.size(), "accounts' size must be less than 100." ); bool is_executed = false; diff --git a/tests/eosio.msig_tests.cpp b/tests/eosio.msig_tests.cpp index cdc6dfc2f..5e00a5aff 100644 --- a/tests/eosio.msig_tests.cpp +++ b/tests/eosio.msig_tests.cpp @@ -131,18 +131,17 @@ class eosio_msig_tester : public tester { produce_block(); BOOST_REQUIRE_EQUAL( true, chain_has_transaction(trace->id) ); return trace; + } - /* - string action_type_name = abi_ser.get_action_type(name); + action_result push_action_get_result( const account_name& signer, const action_name &name, const variant_object &data ) { + string action_type_name = abi_ser.get_action_type(name); - action act; - act.account = N(eosio.msig); - act.name = name; - act.data = abi_ser.variant_to_binary( action_type_name, data, abi_serializer_max_time ); - //std::cout << "test:\n" << fc::to_hex(act.data.data(), act.data.size()) << " size = " << act.data.size() << std::endl; + action act; + act.account = N(eosio.msig); + act.name = name; + act.data = abi_ser.variant_to_binary( action_type_name, data,abi_serializer_max_time ); - return base_tester::push_action( std::move(act), auth ? uint64_t(signer) : 0 ); - */ + return base_tester::push_action( std::move(act), uint64_t(signer)); } transaction reqauth( account_name from, const vector& auths, const fc::microseconds& max_serialization_time ); @@ -954,4 +953,241 @@ BOOST_FIXTURE_TEST_CASE( switch_proposal_and_fail_approve_with_hash, eosio_msig_ ); } FC_LOG_AND_RETHROW() +BOOST_FIXTURE_TEST_CASE( propose_approve_oppose_abstain, eosio_msig_tester ) try { + //what should be tested: + //1. if you approved, you can't oppose/abstain. the same with oppose/abstain + //2. if you approved, unoppose/unabstain should fail, the same with oppose/abstain + //3. if you approved, unapprove should success, the same with unoppose and unabstain + //4. after unapprove, you can oppose/abstain, the same with unoppose/unabstain + //5. without all approved, exec will fail + //6. after all approved, exec will success + auto trx = reqauth("alice", + vector{ + { N(alice), config::active_name }, + { N(bob), config::active_name }, + { N(carol), config::active_name }}, + abi_serializer_max_time ); + + //propose should success + BOOST_REQUIRE_EQUAL(success(), + push_action_get_result( N(alice), N(propose), mvo() + ("proposer", "alice") + ("proposal_name", "first") + ("trx", trx) + ("requested", vector{ { N(alice), config::active_name }, { N(bob), config::active_name },{ N(carol), config::active_name } }) + )); + + //1. if you approved, you can't oppose/abstain. the same with oppose/abstain + //approve by alice should success + BOOST_REQUIRE_EQUAL(success(), + push_action_get_result( N(alice), N(approve), mvo() + ("proposer", "alice") + ("proposal_name", "first") + ("level", permission_level{ N(alice), config::active_name }) + )); + + //oppose by alice should fail + BOOST_REQUIRE_EQUAL( wasm_assert_msg("provided permission not requested, or you have approved to this proposal" ), + push_action_get_result( N(alice), N(oppose), mvo() + ("proposer", "alice") + ("proposal_name", "first") + ("level", permission_level{ N(alice), config::active_name }) + )); + + //abstain by alice should fail + BOOST_REQUIRE_EQUAL( wasm_assert_msg("provided permission not requested, or you have approved to this proposal" ), + push_action_get_result( N(alice), N(abstain), mvo() + ("proposer", "alice") + ("proposal_name", "first") + ("level", permission_level{ N(alice), config::active_name }) + )); + + //oppose by bob should success + BOOST_REQUIRE_EQUAL(success(), + push_action_get_result(N(bob), N(oppose), mvo() + ("proposer", "alice") + ("proposal_name", "first") + ("level", permission_level{N(bob), config::active_name }) + )); + + //approve by bob should fail + BOOST_REQUIRE_EQUAL(wasm_assert_msg("you already opposed this proposal" ), + push_action_get_result(N(bob), N(approve), mvo() + ("proposer", "alice") + ("proposal_name", "first") + ("level", permission_level{N(bob), config::active_name }) + )); + + //abstain by bob should fail + BOOST_REQUIRE_EQUAL(wasm_assert_msg("you have opposed to this proposal" ), + push_action_get_result(N(bob), N(abstain), mvo() + ("proposer", "alice") + ("proposal_name", "first") + ("level", permission_level{N(bob), config::active_name }) + )); + + //abstain by carol should success + BOOST_REQUIRE_EQUAL(success(), + push_action_get_result(N(carol), N(abstain), mvo() + ("proposer", "alice") + ("proposal_name", "first") + ("level", permission_level{N(carol), config::active_name }) + )); + + //approve by carol should fail + BOOST_REQUIRE_EQUAL(wasm_assert_msg("you already abstained this proposal"), + push_action_get_result(N(carol), N(approve), mvo() + ("proposer", "alice") + ("proposal_name", "first") + ("level", permission_level{N(carol), config::active_name }) + )); + + //oppose by carol should fail + BOOST_REQUIRE_EQUAL(wasm_assert_msg("you have abstained to this proposal"), + push_action_get_result(N(carol), N(oppose), mvo() + ("proposer", "alice") + ("proposal_name", "first") + ("level", permission_level{N(carol), config::active_name }) + )); + + //2. if you approved, unoppose/unabstain should fail, the same with oppose/abstain + //bob do unapprove will faill + BOOST_REQUIRE_EQUAL(wasm_assert_msg("no approval previously granted"), + push_action_get_result(N(bob), N(unapprove), mvo() + ("proposer", "alice") + ("proposal_name", "first") + ("level", permission_level{N(bob), config::active_name }) + )); + + //carol do unapprove will fail + BOOST_REQUIRE_EQUAL(wasm_assert_msg("no approval previously granted"), + push_action_get_result(N(carol), N(unapprove), mvo() + ("proposer", "alice") + ("proposal_name", "first") + ("level", permission_level{N(carol), config::active_name }) + )); + + //alice do unoppose will fail + BOOST_REQUIRE_EQUAL(wasm_assert_msg("no oppose found"), + push_action_get_result(N(alice), N(unoppose), mvo() + ("proposer", "alice") + ("proposal_name", "first") + ("level", permission_level{N(alice), config::active_name }) + )); + + //carol do unoppose will fail + BOOST_REQUIRE_EQUAL(wasm_assert_msg("no oppose found"), + push_action_get_result(N(carol), N(unoppose), mvo() + ("proposer", "alice") + ("proposal_name", "first") + ("level", permission_level{N(carol), config::active_name }) + )); + + //alice do unabstain will fail + BOOST_REQUIRE_EQUAL(wasm_assert_msg("no abstain found"), + push_action_get_result(N(alice), N(unabstain), mvo() + ("proposer", "alice") + ("proposal_name", "first") + ("level", permission_level{N(alice), config::active_name }) + )); + + //bob do unabstain will fail + BOOST_REQUIRE_EQUAL(wasm_assert_msg("no abstain found"), + push_action_get_result(N(bob), N(unabstain), mvo() + ("proposer", "alice") + ("proposal_name", "first") + ("level", permission_level{N(bob), config::active_name }) + )); + + //3. if you approved, unapprove should success, the same with unoppose and unabstain + //alice unapprove will success + BOOST_REQUIRE_EQUAL(success(), + push_action_get_result(N(alice), N(unapprove), mvo() + ("proposer", "alice") + ("proposal_name", "first") + ("level", permission_level{N(alice), config::active_name }) + )); + + //bob unoppose will success + BOOST_REQUIRE_EQUAL(success(), + push_action_get_result(N(bob), N(unoppose), mvo() + ("proposer", "alice") + ("proposal_name", "first") + ("level", permission_level{N(bob), config::active_name }) + )); + + //carol unoppose will success + BOOST_REQUIRE_EQUAL(success(), + push_action_get_result(N(carol), N(unabstain), mvo() + ("proposer", "alice") + ("proposal_name", "first") + ("level", permission_level{N(carol), config::active_name }) + )); + + //4. after unapprove, you can oppose/abstain, the same with unoppose/unabstain + //alice do abstain will success + BOOST_REQUIRE_EQUAL(success(), + push_action_get_result(N(alice), N(abstain), mvo() + ("proposer", "alice") + ("proposal_name", "first") + ("level", permission_level{N(alice), config::active_name }) + )); + //bob do approve will success + BOOST_REQUIRE_EQUAL(success(), + push_action_get_result(N(bob), N(approve), mvo() + ("proposer", "alice") + ("proposal_name", "first") + ("level", permission_level{N(bob), config::active_name }) + )); + //carol do oppose will success + BOOST_REQUIRE_EQUAL(success(), + push_action_get_result(N(carol), N(oppose), mvo() + ("proposer", "alice") + ("proposal_name", "first") + ("level", permission_level{N(carol), config::active_name }) + )); + + //5. without all approved, exec will fail + BOOST_REQUIRE_EQUAL(wasm_assert_msg("transaction authorization failed"), + push_action_get_result(N(alice), N(exec), mvo() + ("proposer", "alice") + ("proposal_name", "first") + ("executer", "alice"))); + + //6. after all approved, exec will success + //alice unabstain and approve will success + BOOST_REQUIRE_EQUAL(success(), + push_action_get_result(N(alice), N(unabstain), mvo() + ("proposer", "alice") + ("proposal_name", "first") + ("level", permission_level{N(alice), config::active_name }) + )); + BOOST_REQUIRE_EQUAL(success(), + push_action_get_result(N(alice), N(approve), mvo() + ("proposer", "alice") + ("proposal_name", "first") + ("level", permission_level{N(alice), config::active_name }) + )); + //carol unoppose and approve will success + BOOST_REQUIRE_EQUAL(success(), + push_action_get_result(N(carol), N(unoppose), mvo() + ("proposer", "alice") + ("proposal_name", "first") + ("level", permission_level{N(carol), config::active_name }) + )); + BOOST_REQUIRE_EQUAL(success(), + push_action_get_result(N(carol), N(approve), mvo() + ("proposer", "alice") + ("proposal_name", "first") + ("level", permission_level{N(carol), config::active_name }) + )); + + //exec will success + BOOST_REQUIRE_EQUAL(success(), + push_action_get_result(N(alice), N(exec), mvo() + ("proposer", "alice") + ("proposal_name", "first") + ("executer", "alice"))); +}FC_LOG_AND_RETHROW() + BOOST_AUTO_TEST_SUITE_END()