Adding and changing a bunch of functionality.
This commit is contained in:
parent
97f3a71011
commit
31818ef434
13 changed files with 237 additions and 172 deletions
|
@ -6,27 +6,26 @@ include_directories (${CMAKE_SOURCE_DIR})
|
|||
find_package (Boost REQUIRED)
|
||||
include_directories (${Boost_INCLUDE_DIR})
|
||||
|
||||
find_package (OpenSSL REQUIRED)
|
||||
include_directories (${OpenSSL_INCLUDE_DIR})
|
||||
|
||||
find_package (GTest REQUIRED)
|
||||
include_directories (${GTEST_INCLUDE_DIR})
|
||||
|
||||
find_package (CryptoPP REQUIRED)
|
||||
include_directories (${CRYPTOPP_INCLUDE_DIRS})
|
||||
|
||||
add_library (mu_coin
|
||||
mu_coin/entry.cpp
|
||||
mu_coin/address.hpp
|
||||
mu_coin/address.cpp
|
||||
mu_coin/block.hpp
|
||||
mu_coin/block.cpp
|
||||
mu_coin/delta.cpp
|
||||
mu_coin/delta.hpp
|
||||
mu_coin/ledger.hpp
|
||||
mu_coin/ledger.cpp
|
||||
mu_coin/balance.hpp
|
||||
mu_coin/balance.cpp)
|
||||
mu_coin/ledger.cpp)
|
||||
|
||||
add_executable (mu_coin_test
|
||||
mu_coin_test/entry.cpp
|
||||
mu_coin_test/ledger.cpp
|
||||
mu_coin_test/balance.cpp
|
||||
mu_coin_test/block.cpp)
|
||||
|
||||
target_link_libraries (mu_coin_test ${GTEST_LIBRARY} ${GTEST_MAIN_LIBRARY} mu_coin)
|
||||
target_link_libraries (mu_coin_test ${GTEST_LIBRARY} ${GTEST_MAIN_LIBRARY} ${CRYPTOPP_LIBRARY} mu_coin)
|
108
FindCryptoPP.cmake
Normal file
108
FindCryptoPP.cmake
Normal file
|
@ -0,0 +1,108 @@
|
|||
# Module for locating the Crypto++ encryption library.
|
||||
#
|
||||
# Customizable variables:
|
||||
# CRYPTOPP_ROOT_DIR
|
||||
# This variable points to the CryptoPP root directory. On Windows the
|
||||
# library location typically will have to be provided explicitly using the
|
||||
# -D command-line option. The directory should include the include/cryptopp,
|
||||
# lib and/or bin sub-directories.
|
||||
#
|
||||
# Read-only variables:
|
||||
# CRYPTOPP_FOUND
|
||||
# Indicates whether the library has been found.
|
||||
#
|
||||
# CRYPTOPP_INCLUDE_DIRS
|
||||
# Points to the CryptoPP include directory.
|
||||
#
|
||||
# CRYPTOPP_LIBRARIES
|
||||
# Points to the CryptoPP libraries that should be passed to
|
||||
# target_link_libararies.
|
||||
#
|
||||
#
|
||||
# Copyright (c) 2012 Sergiu Dotenco
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
INCLUDE (FindPackageHandleStandardArgs)
|
||||
|
||||
FIND_PATH (CRYPTOPP_ROOT_DIR
|
||||
NAMES cryptopp/cryptlib.h include/cryptopp/cryptlib.h
|
||||
PATHS ENV CRYPTOPPROOT
|
||||
DOC "CryptoPP root directory")
|
||||
|
||||
# Re-use the previous path:
|
||||
FIND_PATH (CRYPTOPP_INCLUDE_DIR
|
||||
NAMES cryptopp/cryptlib.h
|
||||
HINTS ${CRYPTOPP_ROOT_DIR}
|
||||
PATH_SUFFIXES include
|
||||
DOC "CryptoPP include directory")
|
||||
|
||||
FIND_LIBRARY (CRYPTOPP_LIBRARY_DEBUG
|
||||
NAMES cryptlibd cryptoppd
|
||||
HINTS ${CRYPTOPP_ROOT_DIR}
|
||||
PATH_SUFFIXES lib
|
||||
DOC "CryptoPP debug library")
|
||||
|
||||
FIND_LIBRARY (CRYPTOPP_LIBRARY_RELEASE
|
||||
NAMES cryptlib cryptopp
|
||||
HINTS ${CRYPTOPP_ROOT_DIR}
|
||||
PATH_SUFFIXES lib
|
||||
DOC "CryptoPP release library")
|
||||
|
||||
IF (CRYPTOPP_LIBRARY_DEBUG AND CRYPTOPP_LIBRARY_RELEASE)
|
||||
SET (CRYPTOPP_LIBRARY
|
||||
optimized ${CRYPTOPP_LIBRARY_RELEASE}
|
||||
debug ${CRYPTOPP_LIBRARY_DEBUG} CACHE DOC "CryptoPP library")
|
||||
ELSEIF (CRYPTOPP_LIBRARY_RELEASE)
|
||||
SET (CRYPTOPP_LIBRARY ${CRYPTOPP_LIBRARY_RELEASE} CACHE DOC
|
||||
"CryptoPP library")
|
||||
ENDIF (CRYPTOPP_LIBRARY_DEBUG AND CRYPTOPP_LIBRARY_RELEASE)
|
||||
|
||||
IF (CRYPTOPP_INCLUDE_DIR)
|
||||
SET (_CRYPTOPP_VERSION_HEADER ${CRYPTOPP_INCLUDE_DIR}/cryptopp/config.h)
|
||||
|
||||
IF (EXISTS ${_CRYPTOPP_VERSION_HEADER})
|
||||
FILE (STRINGS ${_CRYPTOPP_VERSION_HEADER} _CRYPTOPP_VERSION_TMP REGEX
|
||||
"^#define CRYPTOPP_VERSION[ \t]+[0-9]+$")
|
||||
|
||||
STRING (REGEX REPLACE
|
||||
"^#define CRYPTOPP_VERSION[ \t]+([0-9]+)" "\\1" _CRYPTOPP_VERSION_TMP
|
||||
${_CRYPTOPP_VERSION_TMP})
|
||||
|
||||
STRING (REGEX REPLACE "([0-9]+)[0-9][0-9]" "\\1" CRYPTOPP_VERSION_MAJOR
|
||||
${_CRYPTOPP_VERSION_TMP})
|
||||
STRING (REGEX REPLACE "[0-9]([0-9])[0-9]" "\\1" CRYPTOPP_VERSION_MINOR
|
||||
${_CRYPTOPP_VERSION_TMP})
|
||||
STRING (REGEX REPLACE "[0-9][0-9]([0-9])" "\\1" CRYPTOPP_VERSION_PATCH
|
||||
${_CRYPTOPP_VERSION_TMP})
|
||||
|
||||
SET (CRYPTOPP_VERSION_COUNT 3)
|
||||
SET (CRYPTOPP_VERSION
|
||||
${CRYPTOPP_VERSION_MAJOR}.${CRYPTOPP_VERSION_MINOR}.${CRYPTOPP_VERSION_PATCH})
|
||||
ENDIF (EXISTS ${_CRYPTOPP_VERSION_HEADER})
|
||||
ENDIF (CRYPTOPP_INCLUDE_DIR)
|
||||
|
||||
SET (CRYPTOPP_INCLUDE_DIRS ${CRYPTOPP_INCLUDE_DIR})
|
||||
SET (CRYPTOPP_LIBRARIES ${CRYPTOPP_LIBRARY})
|
||||
|
||||
MARK_AS_ADVANCED (CRYPTOPP_INCLUDE_DIR CRYPTOPP_LIBRARY CRYPTOPP_LIBRARY_DEBUG
|
||||
CRYPTOPP_LIBRARY_RELEASE)
|
||||
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS (CryptoPP REQUIRED_VARS CRYPTOPP_ROOT_DIR
|
||||
CRYPTOPP_INCLUDE_DIR CRYPTOPP_LIBRARY VERSION_VAR CRYPTOPP_VERSION)
|
|
@ -1,24 +0,0 @@
|
|||
#include <mu_coin/balance.hpp>
|
||||
#include <mu_coin/block.hpp>
|
||||
|
||||
void mu_coin::balance_memory::operator += (mu_coin::block const & block_a)
|
||||
{
|
||||
coins_m += block_a.coins ();
|
||||
votes_m += block_a.votes ();
|
||||
}
|
||||
|
||||
void mu_coin::balance_memory::operator -= (mu_coin::block const & block_a)
|
||||
{
|
||||
coins_m -= block_a.coins ();
|
||||
votes_m -= block_a.votes ();
|
||||
}
|
||||
|
||||
boost::multiprecision::uint256_t mu_coin::balance_memory::coins () const
|
||||
{
|
||||
return coins_m;
|
||||
}
|
||||
|
||||
boost::multiprecision::uint256_t mu_coin::balance_memory::votes () const
|
||||
{
|
||||
return votes_m;
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
#pragma once
|
||||
#include <boost/multiprecision/cpp_int.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
namespace mu_coin {
|
||||
class block;
|
||||
class balance : boost::noncopyable
|
||||
{
|
||||
public:
|
||||
virtual boost::multiprecision::uint256_t coins () const = 0;
|
||||
virtual boost::multiprecision::uint256_t votes () const = 0;
|
||||
virtual void operator += (mu_coin::block const &) = 0;
|
||||
virtual void operator -= (mu_coin::block const &) = 0;
|
||||
};
|
||||
|
||||
class balance_memory : public balance
|
||||
{
|
||||
public:
|
||||
void operator += (mu_coin::block const &) override;
|
||||
void operator -= (mu_coin::block const &) override;
|
||||
boost::multiprecision::uint256_t coins () const override;
|
||||
boost::multiprecision::uint256_t votes () const override;
|
||||
boost::multiprecision::uint256_t coins_m;
|
||||
boost::multiprecision::uint256_t votes_m;
|
||||
};
|
||||
}
|
|
@ -1,27 +1,77 @@
|
|||
#include <mu_coin/block.hpp>
|
||||
#include <cryptopp/sha.h>
|
||||
|
||||
mu_coin::address mu_coin::block_memory::address () const
|
||||
mu_coin::entry::entry (boost::multiprecision::uint256_t const & coins_a, boost::multiprecision::uint256_t const & previous_a) :
|
||||
previous (previous_a),
|
||||
coins (coins_a)
|
||||
{
|
||||
return address_m;
|
||||
}
|
||||
|
||||
mu_coin::block_hash mu_coin::block_memory::previous () const
|
||||
void hash_number (CryptoPP::SHA256 & hash_a, boost::multiprecision::uint256_t const & number_a)
|
||||
{
|
||||
return previous_m;
|
||||
uint64_t bytes;
|
||||
uint8_t * first (reinterpret_cast <uint8_t *> (&bytes));
|
||||
uint8_t * last (reinterpret_cast <uint8_t *> (&bytes + 1));
|
||||
bytes = (number_a >> 192).convert_to <uint64_t> ();
|
||||
std::reverse (first, last);
|
||||
hash_a.Update (first, sizeof (bytes));
|
||||
bytes = (number_a >> 128).convert_to <uint64_t> ();
|
||||
std::reverse (first, last);
|
||||
hash_a.Update (first, sizeof (bytes));
|
||||
bytes = (number_a >> 64).convert_to <uint64_t> ();
|
||||
std::reverse (first, last);
|
||||
hash_a.Update (first, sizeof (bytes));
|
||||
bytes = number_a.convert_to <uint64_t> ();
|
||||
std::reverse (first, last);
|
||||
hash_a.Update (first, sizeof (bytes));
|
||||
}
|
||||
|
||||
boost::multiprecision::uint256_t mu_coin::block_memory::coins () const
|
||||
boost::multiprecision::uint256_t mu_coin::block::hash () const
|
||||
{
|
||||
return coins_m;
|
||||
CryptoPP::SHA256 hash;
|
||||
uint8_t digest [32];
|
||||
for (std::vector <mu_coin::entry>::const_iterator i (inputs.begin ()), j (inputs.end ()); i != j; ++i)
|
||||
{
|
||||
hash_number (hash, i->address.number);
|
||||
hash_number (hash, i->previous);
|
||||
hash_number (hash, i->coins);
|
||||
}
|
||||
for (std::vector <mu_coin::entry>::const_iterator i (outputs.begin ()), j (outputs.end ()); i != j; ++i)
|
||||
{
|
||||
hash_number (hash, i->address.number);
|
||||
hash_number (hash, i->previous);
|
||||
hash_number (hash, i->coins);
|
||||
}
|
||||
hash.Final (digest);
|
||||
uint64_t bytes;
|
||||
uint8_t * first (reinterpret_cast <uint8_t *> (&bytes));
|
||||
uint8_t * last (reinterpret_cast <uint8_t *> (&bytes + 1));
|
||||
bytes = *reinterpret_cast <uint64_t *> (digest);
|
||||
std::reverse (first, last);
|
||||
boost::multiprecision::uint256_t result (bytes);
|
||||
bytes = *reinterpret_cast <uint64_t *> (digest + 8);
|
||||
std::reverse (first, last);
|
||||
result = (result << 64) | bytes;
|
||||
bytes = *reinterpret_cast <uint64_t *> (digest + 16);
|
||||
std::reverse (first, last);
|
||||
result = (result << 64) | bytes;
|
||||
bytes = *reinterpret_cast <uint64_t *> (digest + 24);
|
||||
std::reverse (first, last);
|
||||
result = (result << 64) | bytes;
|
||||
return result;
|
||||
}
|
||||
|
||||
boost::multiprecision::uint256_t mu_coin::block_memory::votes () const
|
||||
{
|
||||
return votes_m;
|
||||
}
|
||||
|
||||
mu_coin::block_memory::block_memory (boost::multiprecision::uint256_t const & coins_a, boost::multiprecision::uint256_t const & votes_a) :
|
||||
coins_m (coins_a),
|
||||
votes_m (votes_a)
|
||||
bool mu_coin::block::balanced () const
|
||||
{
|
||||
boost::multiprecision::uint256_t input_sum;
|
||||
for (std::vector <mu_coin::entry>::const_iterator i (inputs.begin ()), j (inputs.end ()); i != j; ++i)
|
||||
{
|
||||
input_sum += i->coins;
|
||||
}
|
||||
boost::multiprecision::uint256_t output_sum;
|
||||
for (std::vector <mu_coin::entry>::const_iterator i (outputs.begin ()), j (outputs.end ()); i != j; ++i)
|
||||
{
|
||||
output_sum += i->coins;
|
||||
}
|
||||
return input_sum == output_sum;
|
||||
}
|
|
@ -1,35 +1,25 @@
|
|||
#pragma once
|
||||
#include <boost/multiprecision/cpp_int.hpp>
|
||||
#include <mu_coin/address.hpp>
|
||||
#include <mu_coin/delta.hpp>
|
||||
|
||||
namespace mu_coin {
|
||||
class block_hash
|
||||
class entry
|
||||
{
|
||||
public:
|
||||
boost::multiprecision::uint256_t number;
|
||||
entry () = default;
|
||||
entry (boost::multiprecision::uint256_t const &, boost::multiprecision::uint256_t const &);
|
||||
mu_coin::address address;
|
||||
boost::multiprecision::uint256_t previous;
|
||||
boost::multiprecision::uint256_t coins;
|
||||
};
|
||||
|
||||
class block
|
||||
{
|
||||
public:
|
||||
virtual mu_coin::address address () const = 0;
|
||||
virtual mu_coin::block_hash previous () const = 0;
|
||||
virtual boost::multiprecision::uint256_t coins () const = 0;
|
||||
virtual boost::multiprecision::uint256_t votes () const = 0;
|
||||
};
|
||||
|
||||
class block_memory : public block
|
||||
{
|
||||
public:
|
||||
block_memory () = default;
|
||||
block_memory (boost::multiprecision::uint256_t const &, boost::multiprecision::uint256_t const &);
|
||||
mu_coin::address address () const override;
|
||||
mu_coin::block_hash previous () const override;
|
||||
boost::multiprecision::uint256_t coins () const override;
|
||||
boost::multiprecision::uint256_t votes () const override;
|
||||
mu_coin::address address_m;
|
||||
mu_coin::block_hash previous_m;
|
||||
boost::multiprecision::uint256_t coins_m;
|
||||
boost::multiprecision::uint256_t votes_m;
|
||||
std::vector <entry> inputs;
|
||||
std::vector <entry> outputs;
|
||||
bool balanced () const;
|
||||
boost::multiprecision::uint256_t fee () const;
|
||||
boost::multiprecision::uint256_t hash () const;
|
||||
};
|
||||
}
|
7
mu_coin/delta.cpp
Normal file
7
mu_coin/delta.cpp
Normal file
|
@ -0,0 +1,7 @@
|
|||
#include <mu_coin/delta.hpp>
|
||||
|
||||
mu_coin::delta::delta (boost::multiprecision::uint256_t const & coins_a, boost::multiprecision::uint256_t const & votes_a) :
|
||||
coins (coins_a),
|
||||
votes (votes_a)
|
||||
{
|
||||
}
|
12
mu_coin/delta.hpp
Normal file
12
mu_coin/delta.hpp
Normal file
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
#include <boost/multiprecision/cpp_int.hpp>
|
||||
|
||||
namespace mu_coin {
|
||||
class delta
|
||||
{
|
||||
public:
|
||||
delta (boost::multiprecision::uint256_t const &, boost::multiprecision::uint256_t const &);
|
||||
boost::multiprecision::uint256_t coins;
|
||||
boost::multiprecision::uint256_t votes;
|
||||
};
|
||||
}
|
|
@ -1,14 +1,14 @@
|
|||
#include <mu_coin/ledger.hpp>
|
||||
#include <mu_coin/block.hpp>
|
||||
|
||||
mu_coin::balance & mu_coin::ledger_memory::balance (mu_coin::address const & address_a)
|
||||
mu_coin::block * mu_coin::ledger::previous (mu_coin::address const & address_a)
|
||||
{
|
||||
assert (has_balance (address_a));
|
||||
auto existing (entries.find (address_a));
|
||||
if (existing != entries.end ())
|
||||
{
|
||||
return *existing->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
return empty_balance;
|
||||
}
|
||||
return existing->second;
|
||||
}
|
||||
|
||||
bool mu_coin::ledger::has_balance (mu_coin::address const & address_a)
|
||||
{
|
||||
return entries.find (address_a) != entries.end ();
|
||||
}
|
|
@ -1,20 +1,14 @@
|
|||
#pragma once
|
||||
#include <mu_coin/balance.hpp>
|
||||
#include <mu_coin/address.hpp>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace mu_coin {
|
||||
class ledger
|
||||
{
|
||||
public:
|
||||
virtual mu_coin::balance & balance (mu_coin::address const &) = 0;
|
||||
};
|
||||
|
||||
class ledger_memory : public ledger
|
||||
{
|
||||
public:
|
||||
mu_coin::balance & balance (mu_coin::address const &) override;
|
||||
mu_coin::balance_memory empty_balance;
|
||||
std::unordered_map <mu_coin::address, mu_coin::balance_memory *> entries;
|
||||
};
|
||||
class block;
|
||||
class ledger
|
||||
{
|
||||
public:
|
||||
mu_coin::block * previous (mu_coin::address const &);
|
||||
bool has_balance (mu_coin::address const &);
|
||||
std::unordered_map <mu_coin::address, mu_coin::block *> entries;
|
||||
};
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
#include <gtest/gtest.h>
|
||||
#include <mu_coin/balance.hpp>
|
||||
#include <mu_coin/block.hpp>
|
||||
|
||||
TEST (balance, balance_memory)
|
||||
{
|
||||
mu_coin::balance_memory balance;
|
||||
ASSERT_EQ (0, balance.coins ());
|
||||
ASSERT_EQ (0, balance.votes ());
|
||||
}
|
||||
|
||||
TEST (balance, balance_add)
|
||||
{
|
||||
mu_coin::balance_memory balance;
|
||||
mu_coin::block_memory block (100, 200);
|
||||
balance += block;
|
||||
ASSERT_EQ (100, balance.coins ());
|
||||
ASSERT_EQ (200, balance.votes ());
|
||||
}
|
||||
|
||||
TEST (balance, balance_subtract)
|
||||
{
|
||||
mu_coin::balance_memory balance;
|
||||
mu_coin::block_memory block1 (400, 500);
|
||||
balance += block1;
|
||||
mu_coin::block_memory block2 (200, 100);
|
||||
balance -= block2;
|
||||
ASSERT_EQ (200, balance.coins ());
|
||||
ASSERT_EQ (400, balance.votes ());
|
||||
}
|
|
@ -1,9 +1,13 @@
|
|||
#include <gtest/gtest.h>
|
||||
#include <mu_coin/block.hpp>
|
||||
|
||||
TEST (block, block_memory)
|
||||
TEST (block, empty)
|
||||
{
|
||||
mu_coin::block_memory block;
|
||||
ASSERT_EQ (0, block.coins ());
|
||||
ASSERT_EQ (0, block.votes ());
|
||||
mu_coin::block block;
|
||||
ASSERT_EQ (0, block.inputs.size ());
|
||||
ASSERT_EQ (0, block.outputs.size ());
|
||||
ASSERT_TRUE (block.balanced ());
|
||||
boost::multiprecision::uint256_t hash (block.hash ());
|
||||
std::string str (hash.convert_to <std::string> ());
|
||||
ASSERT_EQ (boost::multiprecision::uint256_t ("0xE3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855"), hash);
|
||||
}
|
|
@ -5,25 +5,6 @@
|
|||
|
||||
TEST (ledger, empty)
|
||||
{
|
||||
mu_coin::ledger_memory ledger;
|
||||
mu_coin::balance & balance (ledger.balance (mu_coin::address (0)));
|
||||
ASSERT_EQ (0, balance.coins ());
|
||||
ASSERT_EQ (0, balance.votes ());
|
||||
}
|
||||
|
||||
TEST (ledger, separate_empty)
|
||||
{
|
||||
mu_coin::ledger_memory ledger;
|
||||
mu_coin::balance & balance1 (ledger.balance (mu_coin::address (0)));
|
||||
ASSERT_EQ (0, balance1.coins ());
|
||||
ASSERT_EQ (0, balance1.votes ());
|
||||
mu_coin::balance & balance2 (ledger.balance (mu_coin::address (0)));
|
||||
ASSERT_EQ (0, balance2.coins ());
|
||||
ASSERT_EQ (0, balance2.votes ());
|
||||
mu_coin::block_memory block1 (100, 200);
|
||||
balance1 += block1;
|
||||
ASSERT_EQ (100, balance1.coins ());
|
||||
ASSERT_EQ (200, balance1.votes ());
|
||||
ASSERT_EQ (0, balance2.coins ());
|
||||
ASSERT_EQ (0, balance2.votes ());
|
||||
mu_coin::ledger ledger;
|
||||
ASSERT_FALSE (ledger.has_balance (mu_coin::address (0)));
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue