Skip to content

Commit

Permalink
Cleanup token migration on attributes (#2356)
Browse files Browse the repository at this point in the history
* Cleanup token migration on attributes

* Cleanup token migration on attributes

* Cleanup token symbol errors

* Update version

* Heuristic based grouping

* Add helpful note

* Fix indentation

* Remove extra indentation

* Improve logs

* Remove unused enumerate

* Revert "EVM: Discard dst20_contract error (#2355)"

This reverts commit 04ac649.

* Use genesis block flag

* Cleanup genesis block

* Restore comments

* Fix Queue error logging

* Test correct deployed bytecode

* Fix evmQueueId type

---------

Co-authored-by: Peter John Bushnell <[email protected]>
Co-authored-by: Niven <[email protected]>
Co-authored-by: jouzo <[email protected]>
Co-authored-by: Jouzo <[email protected]>
  • Loading branch information
5 people authored Aug 21, 2023
1 parent 407ddf2 commit 07c6d0f
Show file tree
Hide file tree
Showing 14 changed files with 152 additions and 130 deletions.
31 changes: 18 additions & 13 deletions lib/ain-evm/src/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,12 +134,6 @@ impl EVMServices {
let tx_queue = self.core.tx_queues.get(queue_id)?;
let mut queue = tx_queue.data.lock().unwrap();

let is_evm_genesis_block = queue.target_block == U256::zero();
if is_evm_genesis_block {
let migration_txs = get_dst20_migration_txs(mnview_ptr)?;
queue.transactions.extend(migration_txs.into_iter())
}

let queue_txs_len = queue.transactions.len();
let mut all_transactions = Vec::with_capacity(queue_txs_len);
let mut failed_transactions = Vec::with_capacity(queue_txs_len);
Expand Down Expand Up @@ -194,25 +188,30 @@ impl EVMServices {

let mut executor = AinExecutor::new(&mut backend);

// Ensure that state root changes by updating counter contract storage
if current_block_number == U256::zero() {
let is_evm_genesis_block = queue.target_block == U256::zero();
if is_evm_genesis_block {
// reserve DST20 namespace
self.reserve_dst20_namespace(&mut executor)?;

// Deploy contract on the first block
let migration_txs = get_dst20_migration_txs(mnview_ptr)?;
queue.transactions.extend(migration_txs.into_iter());

// Deploy counter contract on the first block
let DeployContractInfo {
address,
storage,
bytecode,
} = EVMServices::counter_contract(dvm_block_number, current_block_number)?;
executor.deploy_contract(address, bytecode, storage)?;
} else {
// Ensure that state root changes by updating counter contract storage
let DeployContractInfo {
address, storage, ..
} = EVMServices::counter_contract(dvm_block_number, current_block_number)?;
executor.update_storage(address, storage)?;
}
for (_idx, queue_item) in queue.transactions.clone().into_iter().enumerate() {

for queue_item in queue.transactions.clone() {
match queue_item.tx {
QueueTx::SignedTx(signed_tx) => {
let nonce = executor.get_nonce(&signed_tx.sender);
Expand Down Expand Up @@ -505,7 +504,7 @@ impl EVMServices {
None => {}
Some(account) => {
if account.code_hash != ain_contracts::get_system_reserved_codehash()? {
debug!("Token address is already in use for {name} {symbol}");
return Err(format_err!("Token address is already in use").into());
}
}
}
Expand Down Expand Up @@ -633,7 +632,10 @@ impl EVMServices {
.collect::<Vec<H160>>();

for address in addresses {
debug!("Deploying address to {:#?}", address);
debug!(
"[reserve_dst20_namespace] Deploying address to {:#?}",
address
);
executor.deploy_contract(address, bytecode.clone().into(), Vec::new())?;
}

Expand Down Expand Up @@ -668,7 +670,10 @@ fn get_dst20_migration_txs(mnview_ptr: usize) -> Result<Vec<QueueTxItem>> {
let mut txs = Vec::new();
for token in ain_cpp_imports::get_dst20_tokens(mnview_ptr) {
let address = ain_contracts::dst20_address_from_token_id(token.id)?;
debug!("Deploying to address {:#?}", address);
debug!(
"[get_dst20_migration_txs] Deploying to address {:#?}",
address
);

let tx = QueueTx::SystemTx(SystemTx::DeployContract(DeployContractData {
name: token.name,
Expand Down
2 changes: 1 addition & 1 deletion lib/ain-evm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub type MaybeTransactionV2 = Option<ethereum::TransactionV2>;
pub enum EVMError {
#[error("EVM: Backend error: {0:?}")]
TrieCreationFailed(#[from] BackendError),
#[error("EVM: Queue error")]
#[error("EVM: Queue error {0:?}")]
QueueError(#[from] QueueError),
#[error("EVM: Queue invalid nonce error {0:?}")]
QueueInvalidNonce((Box<transaction::SignedTx>, ethereum_types::U256)),
Expand Down
10 changes: 9 additions & 1 deletion make.sh
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ setup_vars() {
MAKE_DEPS_ARGS=${MAKE_DEPS_ARGS:-}
TESTS_FAILFAST=${TESTS_FAILFAST:-"0"}
TESTS_COMBINED_LOGS=${TESTS_COMBINED_LOGS:-"0"}
CI_GROUP_LOGS=${CI_GROUP_LOGS:-"1"}
CI_GROUP_LOGS=${CI_GROUP_LOGS:-"$(get_default_ci_group_logs)"}
}

main() {
Expand Down Expand Up @@ -950,6 +950,14 @@ get_default_use_clang() {
return
}

get_default_ci_group_logs() {
if [[ -n "${GITHUB_ACTIONS-}" ]]; then
echo 1
else
echo 0
fi
}

# Dev tools
# ---

Expand Down
25 changes: 5 additions & 20 deletions src/masternodes/govvariables/attributes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

#include <masternodes/govvariables/attributes.h>
#include <masternodes/mn_rpc.h>
#include <ain_rs_exports.h>

#include <masternodes/accountshistory.h> /// CAccountsHistoryWriter
#include <masternodes/errors.h> /// DeFiErrors
Expand All @@ -16,6 +15,7 @@
#include <amount.h> /// GetDecimaleString
#include <core_io.h> /// ValueFromAmount
#include <util/strencodings.h>
#include <ffi/ffihelpers.h>

enum class EVMAttributesTypes : uint32_t {
Finalized = 1,
Expand Down Expand Up @@ -1810,25 +1810,6 @@ Res ATTRIBUTES::Validate(const CCustomCSView &view) const {
if (GetValue(intervalPriceKey, CTokenCurrencyPair{}) == CTokenCurrencyPair{}) {
return DeFiErrors::GovVarValidateCurrencyPair();
}

const CDataStructureV0 enabledKey{AttributeTypes::Param, ParamIDs::Feature,
DFIPKeys::EVMEnabled};

CrossBoundaryResult result;
if (view.GetLastHeight() >= Params().GetConsensus().NextNetworkUpgradeHeight &&
GetValue(enabledKey, false) &&
evmQueueId &&
!evm_try_is_dst20_deployed_or_queued(result, evmQueueId, token->name, token->symbol,
tokenID.v)) {
evm_try_create_dst20(result, evmQueueId, token->creationTx.GetHex(),
token->name,
token->symbol,
tokenID.v);

if (!result.ok) {
return DeFiErrors::GovVarErrorCreatingDST20(result.reason.c_str());
}
}
break;
}
case TokenKeys::FixedIntervalPriceId:
Expand Down Expand Up @@ -2313,6 +2294,7 @@ Res ATTRIBUTES::Apply(CCustomCSView &mnview, const uint32_t height) {
return DeFiErrors::GovVarUnsupportedValue();
}

// TODO: Cut this out.
CrossBoundaryResult result;
if (!evm_try_set_attribute(result, evmQueueId, attributeType, *number)) {
return DeFiErrors::SettingEVMAttributeFailure();
Expand All @@ -2322,6 +2304,9 @@ Res ATTRIBUTES::Apply(CCustomCSView &mnview, const uint32_t height) {
}
}
}

// TODO: evm_try_handle_attribute_apply here.
// Pass the whole apply chain. On the rust side, pick and choose what needs to be handled
return Res::Ok();
}

Expand Down
23 changes: 4 additions & 19 deletions src/masternodes/mn_checks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1057,32 +1057,17 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor {
token.creationTx = tx.GetHash();
token.creationHeight = height;

// check foundation auth
if (token.IsDAT() && !HasFoundationAuth()) {
return Res::Err("tx not from foundation member");
}

if (static_cast<int>(height) >= consensus.BayfrontHeight) { // formal compatibility if someone cheat and create
// LPS token on the pre-bayfront node
if (static_cast<int>(height) >= consensus.BayfrontHeight) {
if (token.IsPoolShare()) {
return Res::Err("Can't manually create 'Liquidity Pool Share' token; use poolpair creation");
}
}

auto tokenId = mnview.CreateToken(token, static_cast<int>(height) < consensus.BayfrontHeight);

if (tokenId && token.IsDAT() && isEvmEnabledForBlock) {
CrossBoundaryResult result;
evm_try_create_dst20(result, evmQueueId, tx.GetHash().GetHex(),
rust::string(tokenName.c_str()),
rust::string(tokenSymbol.c_str()),
tokenId->v);

if (!result.ok) {
return Res::Err("Error creating DST20 token: %s", result.reason);
}
}

auto tokenId = mnview.CreateToken(token, static_cast<int>(height) < consensus.BayfrontHeight, isEvmEnabledForBlock, evmQueueId);
return tokenId;
}

Expand Down Expand Up @@ -1435,7 +1420,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor {
token.creationTx = tx.GetHash();
token.creationHeight = height;

auto tokenId = mnview.CreateToken(token);
auto tokenId = mnview.CreateToken(token, false, false, evmQueueId);
Require(tokenId);

rewards = obj.rewards;
Expand Down Expand Up @@ -2651,7 +2636,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor {
token.flags |=
static_cast<uint8_t>(CToken::TokenFlags::LoanToken) | static_cast<uint8_t>(CToken::TokenFlags::DAT);

auto tokenId = mnview.CreateToken(token);
auto tokenId = mnview.CreateToken(token, false, isEvmEnabledForBlock, evmQueueId);
Require(tokenId);

if (height >= static_cast<uint32_t>(consensus.FortCanningCrunchHeight) && IsTokensMigratedToGovVar()) {
Expand Down
59 changes: 41 additions & 18 deletions src/masternodes/tokens.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
#include <core_io.h>
#include <primitives/transaction.h>
#include <util/strencodings.h>
#include <ffi/cxx.h>
#include <ain_rs_exports.h>
#include <ffi/ffihelpers.h>

#include <univalue.h>

Expand Down Expand Up @@ -61,17 +64,19 @@ Res CTokensView::CreateDFIToken() {
return Res::Ok();
}

ResVal<DCT_ID> CTokensView::CreateToken(const CTokensView::CTokenImpl &token, bool isPreBayfront) {
// this should not happen, but for sure
Require(!GetTokenByCreationTx(token.creationTx),
[=]{ return strprintf("token with creation tx %s already exists!",
token.creationTx.ToString()); });

Require(token.IsValidSymbol());
ResVal<DCT_ID> CTokensView::CreateToken(const CTokensView::CTokenImpl &token, bool isPreBayfront, bool shouldCreateDst20, uint64_t evmQueueId) {
if (GetTokenByCreationTx(token.creationTx)) {
return Res::Err("token with creation tx %s already exists!", token.creationTx.ToString());
}
if (auto r = token.IsValidSymbol(); !r) {
return r;
}

DCT_ID id{0};
if (token.IsDAT()) {
Require(!GetToken(token.symbol), [=]{ return strprintf("token '%s' already exists!", token.symbol); });
if (GetToken(token.symbol)) {
return Res::Err("token '%s' already exists!", token.symbol);
}

ForEachToken(
[&](DCT_ID const &currentId, CLazySerialize<CTokenImplementation>) {
Expand All @@ -81,21 +86,30 @@ ResVal<DCT_ID> CTokensView::CreateToken(const CTokensView::CTokenImpl &token, bo
},
id);
if (id == DCT_ID_START) {
// asserted before BayfrontHeight, keep it for strict sameness
Require(
!isPreBayfront,
[]{ return "Critical fault: trying to create DCT_ID same as DCT_ID_START for Foundation owner\n"; });
if (isPreBayfront) {
return Res::Err("Critical fault: trying to create DCT_ID same as DCT_ID_START for Foundation owner\n");
}

id = IncrementLastDctId();
LogPrintf("Warning! Range <DCT_ID_START already filled. Using \"common\" id=%s for new token\n",
id.ToString().c_str());
}

if (shouldCreateDst20) {
CrossBoundaryResult result;
evm_try_create_dst20(result, evmQueueId, token.creationTx.GetHex(),
rust::string(token.name.c_str()),
rust::string(token.symbol.c_str()),
id.v);
if (!result.ok) {
return Res::Err("Error creating DST20 token: %s", result.reason);
}
}
} else {
id = IncrementLastDctId();
}

std::string symbolKey = token.CreateSymbolKey(id);

const auto symbolKey = token.CreateSymbolKey(id);
WriteBy<ID>(id, token);
WriteBy<Symbol>(symbolKey, id);
WriteBy<CreationTx>(token.creationTx, id);
Expand Down Expand Up @@ -231,15 +245,24 @@ std::optional<DCT_ID> CTokensView::ReadLastDctId() const {
if (Read(LastDctId::prefix(), lastDctId)) {
return {lastDctId};
}

return {};
}

inline Res CTokenImplementation::IsValidSymbol() const {
Require(!symbol.empty() && !IsDigit(symbol[0]), []{ return "token symbol should be non-empty and starts with a letter"; });
Require(symbol.find('#') == std::string::npos, []{ return "token symbol should not contain '#'"; });
auto invalidTokenSymbol = []() {
return Res::Err("Invalid token symbol. Valid: Start with an alphabet, non-empty, not contain # or /");
};

if (symbol.empty() || IsDigit(symbol[0])) {
return invalidTokenSymbol();
}
if (symbol.find('#') != std::string::npos) {
return invalidTokenSymbol();
}
if (creationHeight >= Params().GetConsensus().FortCanningCrunchHeight) {
Require(symbol.find('/') == std::string::npos, []{ return "token symbol should not contain '/'"; });
if (symbol.find('/') != std::string::npos) {
return invalidTokenSymbol();
};
}
return Res::Ok();
}
4 changes: 2 additions & 2 deletions src/masternodes/tokens.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,8 @@ class CTokensView : public virtual CStorageView {
DCT_ID const &start = DCT_ID{0});

Res CreateDFIToken();
ResVal<DCT_ID> CreateToken(const CTokenImpl &token, bool isPreBayfront = false);
Res UpdateToken(const CTokenImpl &newToken, bool isPreBayfront = false, const bool tokenSplitUpdatea = false);
ResVal<DCT_ID> CreateToken(const CTokenImpl &token, bool isPreBayfront = false, bool shouldCreateDst20 = false, uint64_t evmQueueId = 0);
Res UpdateToken(const CTokenImpl &newToken, bool isPreBayfront = false, const bool tokenSplitUpdate = false);

Res BayfrontFlagsCleanup();
Res AddMintedTokens(DCT_ID const &id, const CAmount &amount);
Expand Down
5 changes: 3 additions & 2 deletions src/masternodes/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1270,7 +1270,7 @@ static Res PoolSplits(CCustomCSView& view, CAmount& totalBalance, ATTRIBUTES& at
throw std::runtime_error(res.msg);
}

auto resVal = view.CreateToken(newPoolToken);
auto resVal = view.CreateToken(newPoolToken, false, false);
if (!resVal) {
throw std::runtime_error(resVal.msg);
}
Expand Down Expand Up @@ -1809,7 +1809,8 @@ static void ProcessTokenSplits(const CBlock& block, const CBlockIndex* pindex, C
continue;
}

auto resVal = view.CreateToken(newToken);
// TODO: Pass this on, once we add support for EVM splits
auto resVal = view.CreateToken(newToken, false, false, 0);
if (!resVal) {
LogPrintf("Token split failed on CreateToken %s\n", resVal.msg);
continue;
Expand Down
Loading

0 comments on commit 07c6d0f

Please sign in to comment.