提交 f068ca99 编写于 作者: D Daniel Larimer

Update Documentation and Segment eoslib api

上级 a6cd2e56
......@@ -3,6 +3,12 @@
*.cmake
*.dylib
*.ll
*.bc
*.wast
*.wast.hpp
*.wasm
*.s
*.dot
CMakeCache.txt
CMakeFiles
......
# Eos
Welcome to the EOS source code repository!
Welcome to the EOS.IO source code repository!
## Getting Started
The following instructions overview the process of getting the software, building it, and running a simple test network that produces blocks.
......@@ -134,4 +134,4 @@ git clone https://github.com/eosio/eos --recursive
mkdir -p eos/build && cd eos/build
WASM_LLVM_CONFIG=~/wasm-compiler/llvm/bin/llvm-config cmake ..
make -j4
```
\ No newline at end of file
```
#include <currency/currency.hpp> /// defines transfer struct (abi)
namespace TOKEN_NAME {
using namespace eos;
/// When storing accounts, check for empty balance and remove account
void storeAccount( AccountName account, const Account& a ) {
......
......@@ -15,9 +15,10 @@ const char* currency_wast = R"=====(
(import "env" "store_i64" (func $store_i64 (param i64 i64 i32 i32) (result i32)))
(table 0 anyfunc)
(memory $0 1)
(data (i32.const 4) "p\04\00\00")
(data (i32.const 4) "\90\04\00\00")
(data (i32.const 16) "integer underflow subtracting token balance\00")
(data (i32.const 64) "integer overflow adding token balance\00")
(data (i32.const 112) "message shorter than expected\00")
(export "memory" (memory $0))
(export "_ZN8currency12storeAccountEyRKNS_7AccountE" (func $_ZN8currency12storeAccountEyRKNS_7AccountE))
(export "_ZN8currency23apply_currency_transferERKNS_8TransferE" (func $_ZN8currency23apply_currency_transferERKNS_8TransferE))
......@@ -327,14 +328,18 @@ const char* currency_wast = R"=====(
(get_local $2)
(i64.const 0)
)
(drop
(call $readMessage
(i32.add
(get_local $2)
(i32.const 8)
(call $assert
(i32.gt_u
(call $readMessage
(i32.add
(get_local $2)
(i32.const 8)
)
(i32.const 24)
)
(i32.const 24)
(i32.const 23)
)
(i32.const 112)
)
(call $_ZN8currency23apply_currency_transferERKNS_8TransferE
(i32.add
......
/**
@defgroup contractdev How To Write Contracts
@brief Introduction to writing contracts for EOS.IO
@section background Background
EOS.IO contracts (aka applications) are deployed to a blockchain as pre-compiled Web Assembly (aka WASM). WASM is compiled
from C/C++ using LLVM and clang, which means that you will require knowledge of C/C++ in order to develop your blockchain
applications. While it is possible to develop in C, we strongly recommend that all developers use the EOS.IO C++ API which
provides much stronger type safety and is generally easier to read.
@section programstructure Application Structure
EOS.IO applications are designed around event (aka message) handlers that respond to user actions. For example,
a user might transfer tokens to another user. This event can be processed and potentially rejected by the sender,
the receiver, and the currency application itself.
As an application developer you get to decide what actions users can take and which handlers may or must be called
in response to those events.
@subsection programentry Entry Points
EOS.IO applications have three potential starting points that behave like `main` in traditional applications:
```
extern "C" {
void validate( uint64_t code, uint64_t action );
void precondition( uint64_t code, uint64_t action );
void apply( uint64_t code, uint64_t action );
}
```
Each of these entry points is give the arguments `code` and `action` which uniquely identify every event in
the system. For example, code could be a `currency` contract and action could be `transfer`. This event (code,action)
may be passed to several contracts including the `sender` and `receiver`. It is up to your application to figure
out what to do in response to such an event.
Each handler has a different level of access to blockchain state:
- **validate** can only access the data on the message itself
- **precondition** only has read access to the database and message
- **apply** has read/write access to the database
These three different handlers enable EOS.IO to maximize the amount of potential parallelism possible and it may
make sense to move computationaly expensive validations to either precondition or validate. That said, most developers
can postpone learning about `validate` and `precondition` as anything that can be done in those entry points can also
be performed in `apply`.
### Example Apply Entry Handler
Generally speaking, you should use your entry handler to dispatch events to functions that implement the majority
of your logic and optionally reject events that your contract is unable or unwilling to accept.
```
extern "C" {
void apply( uint64_t code, uint64_t action ) {
if( code == N(currency) ) {
if( action == N(transfer) )
currency::apply_currency_transfer( currentMessage< currency::Transfer >() );
} else {
assert( false, "rejecting unexpected event" );
}
}
}
```
@note When defining your entry points it is required that they are placed in an `extern "C"` code block so that
c++ name mangling does not get applied to the function.
*/
......@@ -6,8 +6,9 @@
#include <eoslib/types.h>
/**
* @defgroup database EOS.IO Database API
* @defgroup database Database API
* @brief APIs that store and retreive data on the blockchain
* @ingroup contractdev
*
* EOS.IO organizes data according to the following broad structure:
*
......@@ -107,13 +108,30 @@ int32_t remove_i64( AccountName scope, TableName table, uint64_t key );
* @brief Interface to a database table with 128 bit primary and secondary keys and arbitary binary data value.
* @ingroup databaseC
*
* These methods expect data to point to a record that is at least 2*sizeof(uint128_t) where the leading
* 32 bytes are the primary and secondary keys. These keys will be interpreted and sorted as unsigned
* 128 bit integers.
* @param scope - the account where table data will be found
* @param code - the code which owns the table
* @param table - the name of the table where record is stored
* @param data - a pointer to memory that is at least 32 bytes long
* @param len - the length of data, must be greater than or equal to 32 bytes
*
* @return the total number of bytes read or -1 for "not found" or "end" where bytes
* read includes 32 bytes of the key
*
* These methods assume a database table with records of the form:
*
* ```
* struct Record {
* uint128 primary;
* uint128 secondary;
* ... arbitrary data ...
* };
*
* ```
*
* You can iterate over these indicies with primary index sorting records by { primary, secondary } and
* the secondary index sorting records by { secondary, primary }. This means that duplicates of the primary or
* secondary values are allowed so long as there are no duplicates of the combination {primary, secondary}.
*
* @see Table class in C++ API
*
* @{
......@@ -143,12 +161,17 @@ int32_t lower_bound_secondary_i128i128( AccountName scope, AccountName code, Tab
const void* key, void* data, uint32_t len );
int32_t load_secondary_i128i128( AccountName scope, AccountName code, TableName table, const void* secondary, void* data, uint32_t len );
///@}
/// data must point to 2*sizeof(uint128) containing primary and secondary key
/**
* @param data - must point to at lest 32 bytes containing {primary,secondary}
*
* @return true if the record was removed, false if no record was found
*/
bool remove_i128i128( AccountName scope, TableName table, const void* data );
/// data must point to at least 2*sizeof(uint128) containing primary and secondary key
/**
* Creates or updates a record and returns true if successful
*/
bool store_i128i128( AccountName scope, TableName table, const void* data, uint32_t len );
///@ }
///@} dbi128i128
#pragma once
#include <eoslib/types.h>
#include <eoslib/types.hpp>
#include <eoslib/message.hpp>
#include <eoslib/print.hpp>
#include <eoslib/math.hpp>
extern "C" {
/**
* Multiplying two 128 bit numbers creates a 256 bit temporary that is then divided by
* a 128 bit number and cast back to 128 bits posing some precision in the process.
*
* This method performs the following operation:
*
* result = uint128( (uint256(a)*mult) / div)
*
* @pre *div != 0
* @post *result contains the result of the above math
void mult_div_i128( const uint128_t* a, const uint128_t* mult, const uint128_t* div, uint128_t* result );
*/
/**
* @param msg - a pointer where up to @ref len bytes of the current message will be coppied
* @return the number of bytes copied to msg
*/
uint32_t readMessage( void* msg, uint32_t len );
/**
* This method is useful for dynamicly sized messages
*
* @return the length of the current message
*/
uint32_t messageSize();
void prints( const char* cstr );
void printi( uint64_t value );
void printi128( const uint128_t* value );
void printn( uint64_t name );
/**
* @return the account which specifes the code that is being run
*/
AccountName currentCode();
void assert( uint32_t test, const char* cstr );
/**
* Verifies that @ref name exists in the set of notified accounts on a message. Throws if not found
*/
void requireNotice( AccountName );
/**
* Verifies that @ref name exists in the set of provided auths on a message. Throws if not found
*/
void requireAuth( AccountName name );
/**
* Gets the notified account at index N
*/
AccountName getNotify( int32_t index );
/**
* Gets the required auth at index N
*/
AccountName getAuth( int32_t index );
/**
* Returns the time of the last block (not the block including this message)
*/
Time now();
/**
* Any message handler can generate an "output" to be returned if it calls the callResult() API, if
* callResult() is not called then result_length will be set to 0.
*
* @param code - the account whose code should execute within the notify account contexts
* @param notify - any account in the notify list must be within the current read scope
* @param message - the message that should be delivered
* @param result - a place to store the result of the call
*
* @param canfail - if true then a new undo context is started and undon on error, else this method throws on error
* @return 0 on success and 1 if the call aborted
*/
uint32_t syncCall( AccountName code, AccountName* authorities, uint32_t numauths,
AccountName* notify, uint32_t numnotice,
const void* message, uint32_t message_length,
void* result, uint32_t* result_length,
bool canfail );
/**
* Used to specify the return value for syncCall
*/
void callResult( const void* data, uint32_t datalen );
/**
* Given the message with CODE.ACTION notifying CONTEXT we normally also allow
* CONTEXT to define its own method handler that can be called: eg context::apply_code_action()
*
* In some cases the code::apply_code_action() may want to prevent context::apply_code_action()
* from being invoked. This is necessary if the context may have incentive to block a particular
* contract from modifying data stored in the context/code section.
*
* For example a social media website that stores votes on accounts and an account owner interested
* in blocking negative votes.
*/
void disableContextCode( uint64_t AccountName );
} /// extern C
template<typename T>
T min( const T& a, const T&b ) {
return a < b ? a : b;
}
static constexpr char char_to_symbol( char c ) {
if( c >= 'a' && c <= 'z' )
return (c - 'a') + 1;
if( c >= '1' && c <= '5' )
return (c - '1') + 26;
return 0;
}
static constexpr uint64_t string_to_name( const char* str ) {
uint32_t len = 0;
while( str[len] ) ++len;
uint64_t value = 0;
for( uint32_t i = 0; i <= 12 && i < len; ++i ) {
value <<= 5;
value |= char_to_symbol( str[ len -1 - i ] );
}
if( len == 13 ) {
value <<= 4;
value |= 0x0f & char_to_symbol( str[ 12 ] );
}
return value;
}
template<uint64_t I>
struct ConstName {
static uint64_t value() { return I; }
operator uint64_t()const { return I; }
};
#define NAME(X) (ConstName<string_to_name(X)>::value())
#define N(X) string_to_name(#X)
struct Name {
Name(){}
// Name( const char* c ) : value( string_to_name(c) ){}
Name( uint64_t v ): value(v) {}
operator uint64_t()const { return value; }
friend bool operator==( const Name& a, const Name& b ) { return a.value == b.value; }
AccountName value = 0;
};
template<typename T> struct remove_reference { typedef T type; };
template<typename T> struct remove_reference<T&> { typedef T type; };
template<typename T> struct remove_reference<const T&> { typedef T type; };
template<typename T>
T currentMessage() {
T value;
readMessage( &value, sizeof(value) );
return value;
}
template<typename... Accounts>
void requireNotice( AccountName name, Accounts... accounts ){
requireNotice( name );
requireNotice( accounts... );
}
struct Ratio {
uint64_t base = 1;
uint64_t quote = 1;
};
static_assert( sizeof(Ratio) == 2*sizeof(uint64_t), "unexpected padding" );
/*
template<uint64_t table, typename Record, typename Primary, typename Secondary>
struct PrimaryIndex {
typedef table_impl<sizeof( Primary ), sizeof( Secondary )> impl;
bool front( Record& r )const {
return impl::front_primary( scope, code, table, &r, sizeof(Record) ) == sizeof(Record);
}
bool back( Record& r )const {
return impl::back_primary( scope, code, table, &r, sizeof(Record) ) == sizeof(Record);
}
bool next( Record& r )const {
return impl::next_primary( scope, code, table, &r, sizeof(Record) ) == sizeof(Record);
}
bool previous( Record& r )const {
return impl::previous_primary( scope, code, table, &r, sizeof(Record) ) == sizeof(Record);
}
bool get( const Primary& p, Record& r )const {
return impl::load_primary( scope, code, table, &p, &r, sizeof(Record) ) == sizeof(Record);
}
bool lower_bound( const Primary& p, Record& r )const {
return impl::lower_bound_primary( scope, code, table, &p &r, sizeof(Record) ) == sizeof(Record);
}
bool upper_bound( const Primary& p, Record& r )const {
return impl::upper_bound_primary( scope, code, table, &p &r, sizeof(Record) ) == sizeof(Record);
}
bool remove( Record& r )const {
impl::remove( scope, table, &r );
}
PrimaryIndex( AccountName _scope, AccountName _code ):scope(_scope),code(_code){}
private:
AccountName scope;
AccountName code;
};
template<uint64_t scope, uint64_t code, uint64_t table, typename Record, typename Primary, typename Secondary>
struct PrimaryIndex {
private:
typedef table_impl<sizeof( Primary ), sizeof( Secondary )> impl;
public:
static bool front( Record& r ) {
return impl::front_primary( scope, code, table, &r, sizeof(Record) ) == sizeof(Record);
}
static bool back( Record& r ) {
return impl::back_primary( scope, code, table, &r, sizeof(Record) ) == sizeof(Record);
}
static bool next( Record& r ) {
return impl::next_primary( scope, code, table, &r, sizeof(Record) ) == sizeof(Record);
}
static bool previous( Record& r ) {
return impl::previous_primary( scope, code, table, &r, sizeof(Record) ) == sizeof(Record);
}
static bool get( const Primary& p, Record& r ) {
return impl::load_primary( scope, code, table, &p, &r, sizeof(Record) ) == sizeof(Record);
}
static bool lower_bound( const Primary& p, Record& r ) {
return impl::lower_bound_primary( scope, code, table, &p &r, sizeof(Record) ) == sizeof(Record);
}
static bool upper_bound( const Primary& p, Record& r ) {
return impl::upper_bound_primary( scope, code, table, &p &r, sizeof(Record) ) == sizeof(Record);
}
static bool remove( Record& r ) {
impl::remove( scope, table, &r );
}
};
template<uint64_t scope, uint64_t code, uint64_t table, typename Record, typename Primary, typename Secondary>
struct SecondaryIndex {
private:
typedef table_impl<sizeof( Primary ), sizeof( Secondary )> impl;
public:
static bool front( Record& r ) {
return impl::front_secondary( scope, code, table, &r, sizeof(Record) ) == sizeof(Record);
}
static bool back( Record& r ) {
return impl::back_secondary( scope, code, table, &r, sizeof(Record) ) == sizeof(Record);
}
static bool next( Record& r ) {
return impl::next_secondary( scope, code, table, &r, sizeof(Record) ) == sizeof(Record);
}
static bool previous( Record& r ) {
return impl::previous_secondary( scope, code, table, &r, sizeof(Record) ) == sizeof(Record);
}
static bool get( const Primary& p, Record& r ) {
return impl::load_secondary( scope, code, table, &p &r, sizeof(Record) ) == sizeof(Record);
}
static bool lower_bound( const Primary& p, Record& r ) {
return impl::lower_bound_secondary( scope, code, table, &p &r, sizeof(Record) ) == sizeof(Record);
}
static bool upper_bound( const Primary& p, Record& r ) {
return impl::upper_bound_secondary( scope, code, table, &p &r, sizeof(Record) ) == sizeof(Record);
}
static bool remove( Record& r ) {
impl::remove( scope, table, &r );
}
};
template<uint64_t scope, uint64_t code, uint64_t table, typename Record, typename Primary, typename Secondary>
struct SecondaryIndex<scope,code,table,Record,Primary,void> {}
template<uint64_t scope, uint64_t code, uint64_t table, typename Record, typename PrimaryType, typename SecondaryType>
struct Table {
private:
typedef table_impl<sizeof( PrimaryType ), sizeof( SecondaryType )> impl;
public:
typedef PrimaryIndex<scope,code,table,Record,PrimaryType,SecondaryType> Primary;
typedef SecondaryIndex<scope,code,table,Record,PrimaryType,SecondaryType> Secondary;
static bool store( Record& r ) {
return impl::store( scope, table, &r, sizeof(r) );
}
static bool remove( Record& r ) {
return impl::remove( scope, table, &r );
}
};
#define TABLE2(SCOPE, CODE, TABLE, TYPE, NAME, PRIMARY_NAME, PRIMARY_TYPE, SECONDARY_NAME, SECONDARY_TYPE) \
using NAME = Table<N(SCOPE),N(CODE),N(TABLE),TYPE,PRIMARY_TYPE,SECONDARY_TYPE>; \
typedef NAME::Primary PRIMARY_NAME; \
typedef NAME::Secondary SECONDARY_NAME;
#define TABLE(SCOPE, CODE, TABLE, TYPE, NAME, PRIMARY_NAME, PRIMARY_TYPE) \
using NAME = Table<N(SCOPE),N(CODE),N(TABLE),TYPE,PRIMARY_TYPE,void>; \
typedef NAME::Primary PRIMARY_NAME;
*/
/**
@defgroup howtobuild How To Build EOS.IO
@brief Describes how to download, compile, and configure an EOS.IO node
The following instructions overview the process of getting the software, building it, and running a simple test network that produces blocks.
### Setting up a build/development environment
This project is written primarily in C++14 and uses CMake as its build system. An up-to-date C++ toolchain (such as Clang or GCC) and the latest version of CMake is recommended. At the time of this writing, Nathan uses clang 4.0.0 and CMake 3.8.0.
### Installing Dependencies
Eos has the following external dependencies, which must be installed on your system:
- Boost 1.64
- OpenSSL
- LLVM 4.0
- [secp256k1-zkp (Cryptonomex branch)](https://github.com/cryptonomex/secp256k1-zkp.git)
```
git clone https://github.com/cryptonomex/secp256k1-zkp.git
cd secp256k1-zkp
./autogen.sh
./configure
make
sudo make install
```
### How to Build LLVM and clang for WASM
By default LLVM and clang do not include the WASM build target, so you will have to build it yourself. Note that following these instructions will create a version of LLVM that can only build WASM targets.
```
mkdir ~/wasm-compiler
cd ~/wasm-compiler
git clone --depth 1 --single-branch --branch release_40 https://github.com/llvm-mirror/llvm.git
cd llvm/tools
git clone --depth 1 --single-branch --branch release_40 https://github.com/llvm-mirror/clang.git
cd ..
mkdir build
cd build
cmake -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX=.. -DLLVM_TARGETS_TO_BUILD= -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=WebAssembly -DCMAKE_BUILD_TYPE=Release ../
make -j4 install
```
### Getting the code
To download all of the code, download Eos and a recursion or two of submodules. The easiest way to get all of this is to do a recursive clone:
`git clone https://github.com/eosio/eos --recursive`
If a repo is cloned without the `--recursive` flag, the submodules can be retrieved after the fact by running this command from within the repo:
`git submodule update --init --recursive`
### Configuring and building
To do an in-source build, simply run `cmake .` from the top level directory. Out-of-source builds are also supported. To override clang's default choice in compiler, add these flags to the CMake command:
`-DCMAKE_CXX_COMPILER=/path/to/c++ -DCMAKE_C_COMPILER=/path/to/cc`
For a debug build, add `-DCMAKE_BUILD_TYPE=Debug`. Other common build types include `Release` and `RelWithDebInfo`.
After successfully running cmake, simply run `make` to build everything. To run the test suite after building, run the `chain_test` executable in the `tests` folder.
### Using the WASM compiler to perform a full build of the project
The WASM_LLVM_CONFIG environment variable is used to find our recently built WASM compiler.
This is needed to compile the example contracts insde eos/contracts folder and their respective tests.
```
git clone https://github.com/eosio/eos --recursive
mkdir -p eos/build && cd eos/build
export WASM_LLVM_CONFIG=~/wasm-compiler/llvm/bin/llvm-config
cmake ..
make -j4
```
If you are doing active development on EOS.IO software you may want to add WASM_LLVM_CONFIG to your `.bash_profile`
### Creating and launching a single-node testnet
After successfully building the project, the `eosd` binary should be present in the `programs/eosd` directory. Go ahead and run `eosd` -- it will probably exit with an error, but if not, close it immediately with Ctrl-C. Note that `eosd` will have created a directory named `data-dir` containing the default configuration (`config.ini`) and some other internals. This default data storage path can be overridden by passing `--data-dir /path/to/data` to `eosd`.
Edit the `config.ini` file, adding the following settings to the defaults already in place:
```
# Load the testnet genesis state, which creates some initial block producers with the default key
genesis-json = /path/to/eos/source/genesis.json
# Enable production on a stale chain, since a single-node test chain is pretty much always stale
enable-stale-production = true
# Enable block production with the testnet producers
producer-name = inita
producer-name = initb
producer-name = initc
producer-name = initd
producer-name = inite
producer-name = initf
producer-name = initg
producer-name = inith
producer-name = initi
producer-name = initj
producer-name = initk
producer-name = initl
producer-name = initm
producer-name = initn
producer-name = inito
producer-name = initp
producer-name = initq
producer-name = initr
producer-name = inits
producer-name = initt
producer-name = initu
# Load the block producer plugin, so we can produce blocks
plugin = eos::producer_plugin
```
Now it should be possible to run `eosd` and see it begin producing blocks. At present, the P2P code is not implemented, so only single-node configurations are possible. When the P2P networking is implemented, these instructions will be updated to show how to create an example multi-node testnet.
*/
#pragma once
extern "C" {
/**
* @defgroup mathcapi Math C API
* @brief defines builtin math functions
* @ingroup mathapi
*/
void multeq_i128( uint128_t* self, const uint128_t* other );
void diveq_i128 ( uint128_t* self, const uint128_t* other );
} // extern "C"
......@@ -2,13 +2,34 @@
#include <eoslib/math.h>
namespace eos {
/**
* @defgroup mathapi Math API
* @brief Defines common math functions
* @ingroup contractdev
*/
/**
* @defgroup mathcppapi Math C++ API
* @brief Defines common math functions and helper types
* @ingroup mathapi
*
* @{
*/
/** @brief wraps multeq_i128 from @ref mathcapi */
inline void multeq( uint128_t& self, const uint128_t& other ) {
multeq_i128( &self, &other );
}
/** @brief wraps diveq_i128 from @ref mathcapi */
inline void diveq( uint128_t& self, const uint128_t& other ) {
diveq_i128( &self, &other );
}
/**
* @brief a struct that wraps uint128 integer and defines common operator overloads
*/
struct uint128 {
public:
uint128( uint128_t i = 0 ):value(i){}
......@@ -55,4 +76,21 @@ namespace eos {
private:
uint128_t value;
};
/**
* Define similar to std::min()
*/
template<typename T>
T min( const T& a, const T&b ) {
return a < b ? a : b;
}
/**
* Define similar to std::max()
*/
template<typename T>
T max( const T& a, const T&b ) {
return a > b ? a : b;
}
/// @} /// mathcppapi
}
#pragma once
#include <eoslib/types.h>
extern "C" {
/**
* @defgroup messageapi Message API
* @ingroup contractdev
* @brief Define API for querying message properties
*
* A EOS.IO message has the following abstract structure:
*
* ```
* struct Message {
* Name code; ///< primary account whose code defines the action
* Name action; ///< the name of the action.
* Name recipients[]; ///< accounts whose code will be notified (in addition to code)
* Name authorization[]; ///< accounts that have approved this message
* char data[];
* };
* ```
*
* This API enables your contract to inspect the fields on the current message and act accordingly.
*
*/
/**
* @defgroup messagecapi Message C API
* @ingroup messageapi
* @brief Define API for querying message properties
*
* @{
*/
/**
* @param msg - a pointer where up to @ref len bytes of the current message will be coppied
* @return the number of bytes copied to msg
*/
uint32_t readMessage( void* msg, uint32_t len );
/**
* This method is useful for dynamicly sized messages
*
* @return the length of the current message's data field
*/
uint32_t messageSize();
/**
* Verifies that @ref name exists in the set of notified accounts on a message. Throws if not found
*/
void requireNotice( AccountName );
/**
* Verifies that @ref name exists in the set of provided auths on a message. Throws if not found
*/
void requireAuth( AccountName name );
/**
* @return the account which specifes the code that is being run
*/
AccountName currentCode();
/**
* Aborts processing of this message and unwinds all pending changes
*
* @param test - 0 to abort, 1 to ignore
* @param cstr - a null terminated message to explain the reason for failure
*/
void assert( uint32_t test, const char* cstr );
/**
* Returns the time in seconds from 1970 of the last accepted block (not the block including this message)
*/
Time now();
///@ } messagecapi
}
#pragma once
#include <eoslib/message.h>
namespace eos {
/**
* @defgroup messagecppapi Message C++ API
* @ingroup messageapi
* @brief Type-safe C++ wrapers for Message C API
*
* @note There are some methods from the @ref messagecapi that can be used directly from C++
*
* @{
*/
/**
* This method attempts to reinterpret the message body as type T. This will only work
* if the message has no dynamic fields and the struct packing on type T is properly defined.
*/
template<typename T>
T currentMessage() {
T value;
auto read = readMessage( &value, sizeof(value) );
assert( read >= sizeof(value), "message shorter than expected" );
return value;
}
using ::requireAuth;
using ::requireNotice;
/**
* All of the listed accounts must be specified on the message notice list or this method will throw
* and end execution of the message.
*
* This helper method enables you to require notice of multiple accounts with a single
* call rather than having to call the similar C API multiple times.
*
* @note message.code is also considered as part of the set of notified accounts
*/
template<typename... Accounts>
void requireNotice( AccountName name, Accounts... accounts ){
requireNotice( name );
requireNotice( accounts... );
}
///@} messagecpp api
} // namespace eos
#pragma once
extern "C" {
/**
* @defgroup consoleapi Console API
* @brief Enables applications to log/print text messages
* @ingroup contractdev
*
*/
/**
* @defgroup consolecapi Console C API
* @ingroup consoleapi
*
* @{
*/
/**
* @param cstr - a null terminated string
*/
void prints( const char* cstr );
/**
* Prints value as an integer
*/
void printi( uint64_t value );
void printi128( const uint128_t* value );
/**
* Prints a 64 bit names as base32 encoded string
*/
void printn( uint64_t name );
/// @}
}
#pragma once
#include <eoslib/eos.hpp>
#include <eoslib/print.h>
#include <eoslib/types.hpp>
#include <eoslib/math.hpp>
namespace eos {
inline void print_native( const char* ptr ) {
inline void print( const char* ptr ) {
prints(ptr);
}
inline void print_native( uint64_t num ) {
inline void print( uint64_t num ) {
printi(num);
}
inline void print_native( uint32_t num ) {
inline void print( uint32_t num ) {
printi(num);
}
inline void print_native( int num ) {
inline void print( int num ) {
printi(num);
}
inline void print_native( uint128 num ) {
inline void print( uint128 num ) {
printi128((uint128_t*)&num);
}
inline void print_native( uint128_t num ) {
inline void print( uint128_t num ) {
printi128((uint128_t*)&num);
}
inline void print_native( Name name ) {
inline void print( Name name ) {
printn(name.value);
}
template<typename T>
inline void print_native( T&& t ) {
inline void print( T&& t ) {
t.print();
}
template<typename Arg>
inline void print( Arg a ) {
print_native(a);
}
/**
* @defgroup consoleCppapi Console C++ API
* @ingroup consoleapi
* @brief C++ wrapper for Console C API
*
* This API uses C++ varidic templates and type detection to
* make it easy to print any native type. You can even overload
* the `print()` method for your own custom types.
*
* ### Example:
* ```
* print( "hello world, this is a number: ", 5 );
* ```
*
* @section override Overriding Print for your Types
*
* There are two ways to overload print:
* 1. implement void print( const T& )
* 2. implement T::print()const
*
* @{
*/
template<typename Arg, typename... Args>
void print( Arg a, Args... args ) {
print(a);
print(args...);
}
/**
* Simulate C++ style streams
*/
class iostream {};
template<typename T>
inline iostream& operator<<( iostream& out, const T& v ) {
print( v );
return out;
}
static iostream cout;
/// @} consoleCppapi
}
......@@ -2,6 +2,11 @@
extern "C" {
/**
* @defgroup types Builtin Types
* @ingroup contractdev
* @brief Specifies typedefs and aliases
*/
typedef long long int64_t;
typedef unsigned long long uint64_t;
typedef unsigned long uint32_t;
......
#pragma once
#include <eoslib/types.h>
namespace eos {
/**
* Converts a base32 symbol into its binary representation, used by string_to_name()
*/
static constexpr char char_to_symbol( char c ) {
if( c >= 'a' && c <= 'z' )
return (c - 'a') + 1;
if( c >= '1' && c <= '5' )
return (c - '1') + 26;
return 0;
}
/**
* Converts a base32 string to a uint64_t. This is a constexpr so that
* this method can be used in template arguments as well.
*
* @ingroup types
*/
static constexpr uint64_t string_to_name( const char* str ) {
uint32_t len = 0;
while( str[len] ) ++len;
uint64_t value = 0;
for( uint32_t i = 0; i <= 12 && i < len; ++i ) {
value <<= 5;
value |= char_to_symbol( str[ len -1 - i ] );
}
if( len == 13 ) {
value <<= 4;
value |= 0x0f & char_to_symbol( str[ 12 ] );
}
return value;
}
/**
* @brief used to generate a compile time uint64_t from the base32 encoded string interpretation of X
* @ingroup types
*/
#define N(X) ::eos::string_to_name(#X)
/**
* @class Name
* @brief wraps a uint64_t to ensure it is only passed to methods that expect a Name and
* that no mathematical operations occur. It also enables specialization of print
* so that it is printed as a base32 string.
*
* @ingroup types
*/
struct Name {
Name( uint64_t v = 0 ): value(v) {}
operator uint64_t()const { return value; }
friend bool operator==( const Name& a, const Name& b ) { return a.value == b.value; }
AccountName value = 0;
};
/**
* @ingroup types
*
* @{
*/
template<typename T> struct remove_reference { typedef T type; };
template<typename T> struct remove_reference<T&> { typedef T type; };
template<typename T> struct remove_reference<const T&> { typedef T type; };
///@}
} // namespace eos
......@@ -31,7 +31,7 @@ const char* exchange_wast = R"=====(
(import "env" "store_i64" (func $store_i64 (param i64 i64 i32 i32) (result i32)))
(table 0 anyfunc)
(memory $0 1)
(data (i32.const 4) "`\08\00\00")
(data (i32.const 4) "\80\08\00\00")
(data (i32.const 16) "integer overflow adding token balance\00")
(data (i32.const 64) "remove\00")
(data (i32.const 80) "store\00")
......@@ -65,7 +65,8 @@ const char* exchange_wast = R"=====(
(data (i32.const 976) "\n No bids found, saving seller account and storing ask\n\00")
(data (i32.const 1040) "\n bids found, lets see what matches\n\00")
(data (i32.const 1088) "saving ask\n\00")
(data (i32.const 1104) "unknown action\00")
(data (i32.const 1104) "message shorter than expected\00")
(data (i32.const 1136) "unknown action\00")
(export "memory" (memory $0))
(export "_ZN8exchange23apply_currency_transferERKN8currency8TransferE" (func $_ZN8exchange23apply_currency_transferERKN8currency8TransferE))
(export "_ZN8exchange18apply_eos_transferERKN3eos8TransferE" (func $_ZN8exchange18apply_eos_transferERKN3eos8TransferE))
......@@ -2886,14 +2887,18 @@ const char* exchange_wast = R"=====(
(get_local $2)
(i32.const 0)
)
(drop
(call $readMessage
(i32.add
(get_local $2)
(i32.const 176)
(call $assert
(i32.gt_u
(call $readMessage
(i32.add
(get_local $2)
(i32.const 176)
)
(i32.const 48)
)
(i32.const 48)
(i32.const 47)
)
(i32.const 1104)
)
(call $_ZN8exchange18apply_exchange_buyENS_8BuyOrderE
(call $memcpy
......@@ -2924,14 +2929,18 @@ const char* exchange_wast = R"=====(
(get_local $2)
(i64.const 0)
)
(drop
(call $readMessage
(i32.add
(get_local $2)
(i32.const 104)
(call $assert
(i32.gt_u
(call $readMessage
(i32.add
(get_local $2)
(i32.const 104)
)
(i32.const 24)
)
(i32.const 24)
(i32.const 23)
)
(i32.const 1104)
)
(call $_ZN8exchange23apply_currency_transferERKN8currency8TransferE
(i32.add
......@@ -2957,14 +2966,18 @@ const char* exchange_wast = R"=====(
(get_local $2)
(i64.const 0)
)
(drop
(call $readMessage
(i32.add
(get_local $2)
(i32.const 104)
(call $assert
(i32.gt_u
(call $readMessage
(i32.add
(get_local $2)
(i32.const 104)
)
(i32.const 24)
)
(i32.const 24)
(i32.const 23)
)
(i32.const 1104)
)
(call $_ZN8exchange18apply_eos_transferERKN3eos8TransferE
(i32.add
......@@ -3001,14 +3014,18 @@ const char* exchange_wast = R"=====(
(get_local $2)
(i32.const 0)
)
(drop
(call $readMessage
(i32.add
(get_local $2)
(i32.const 128)
(call $assert
(i32.gt_u
(call $readMessage
(i32.add
(get_local $2)
(i32.const 128)
)
(i32.const 48)
)
(i32.const 48)
(i32.const 47)
)
(i32.const 1104)
)
(drop
(call $memcpy
......@@ -3033,7 +3050,7 @@ const char* exchange_wast = R"=====(
)
(call $assert
(i32.const 0)
(i32.const 1104)
(i32.const 1136)
)
)
(i32.store offset=4
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册