提交 dd089d1e 编写于 作者: S Serg Metelin

Merge remote-tracking branch 'origin/environment-setup-update' into environment-setup-update

# Conflicts:
#	README.md
......@@ -3,5 +3,5 @@ image: docker:latest
build:
script:
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
- docker build --rm -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA -f Docker/Dockerfile .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
......@@ -23,7 +23,7 @@ before_install:
- cd $TRAVIS_BUILD_DIR/ext && git clone --depth=1 --single-branch git://github.com/oci-labs/clang-WebAssembly wasm-compiler
- cd $TRAVIS_BUILD_DIR
script:
- WASM_LLVM_CONFIG=$TRAVIS_BUILD_DIR/ext/wasm-compiler/bin/llvm-config ext/cmake-3.9.0-Linux-x86_64/bin/cmake -G Ninja -DCMAKE_CXX_COMPILER=clang++-4.0 -DCMAKE_C_COMPILER=clang-4.0 -DBOOST_ROOT=$TRAVIS_BUILD_DIR/ext -DSecp256k1_ROOT_DIR=$TRAVIS_BUILD_DIR/ext -DBINARYEN_ROOT=$TRAVIS_BUILD_DIR/ext/wasm-compiler/
- ext/cmake-3.9.0-Linux-x86_64/bin/cmake -G Ninja -DWASM_LLVM_CONFIG=$TRAVIS_BUILD_DIR/ext/wasm-compiler/bin/llvm-config -DCMAKE_CXX_COMPILER=clang++-4.0 -DCMAKE_C_COMPILER=clang-4.0 -DBOOST_ROOT=$TRAVIS_BUILD_DIR/ext -DSecp256k1_ROOT_DIR=$TRAVIS_BUILD_DIR/ext -DBINARYEN_ROOT=$TRAVIS_BUILD_DIR/ext/wasm-compiler/
- ninja -j4
- tests/eosd_run_test.sh
- tests/chain_test
......
FROM ubuntu:16.04
LABEL authors="xiaobo (peterwillcn@gmail.com), toonsevrin (toonsevrin@gmail.com)"
LABEL maintainer="xiaobo <peterwillcn@gmail.com>" version="0.1.1" \
description="This is eosio/eos image" website="https://eos.io" \
reviewers="toonsevrin (toonsevrin@gmail.com), etc..."
RUN echo 'APT::Install-Recommends 0;' >> /etc/apt/apt.conf.d/01norecommends \
&& echo 'APT::Install-Suggests 0;' >> /etc/apt/apt.conf.d/01norecommends \
&& apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y sudo wget net-tools ca-certificates unzip
&& echo 'APT::Install-Suggests 0;' >> /etc/apt/apt.conf.d/01norecommends \
&& apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y sudo wget net-tools ca-certificates unzip
RUN echo "deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-4.0 main" >> /etc/apt/sources.list.d/llvm.list \
&& wget -O - http://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add - \
&& apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y git-core automake autoconf libtool build-essential pkg-config libtool \
mpi-default-dev libicu-dev python-dev python3-dev libbz2-dev zlib1g-dev libssl-dev libgmp-dev \
clang-4.0 lldb-4.0 lld-4.0 \
&& rm -rf /var/lib/apt/lists/*
&& wget -O - http://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add - \
&& apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y git-core automake autoconf libtool build-essential pkg-config libtool \
mpi-default-dev libicu-dev python-dev python3-dev libbz2-dev zlib1g-dev libssl-dev libgmp-dev \
clang-4.0 lldb-4.0 lld-4.0 \
&& rm -rf /var/lib/apt/lists/*
RUN update-alternatives --install /usr/bin/clang clang /usr/lib/llvm-4.0/bin/clang 400 \
&& update-alternatives --install /usr/bin/clang++ clang++ /usr/lib/llvm-4.0/bin/clang++ 400
RUN cd /tmp && wget https://cmake.org/files/v3.9/cmake-3.9.0-Linux-x86_64.sh \
&& mkdir /opt/cmake && chmod +x /tmp/cmake-3.9.0-Linux-x86_64.sh \
&& sh /tmp/cmake-3.9.0-Linux-x86_64.sh --prefix=/opt/cmake --skip-license \
&& ln -s /opt/cmake/bin/cmake /usr/local/bin
&& mkdir /opt/cmake && chmod +x /tmp/cmake-3.9.0-Linux-x86_64.sh \
&& sh /tmp/cmake-3.9.0-Linux-x86_64.sh --prefix=/opt/cmake --skip-license \
&& ln -s /opt/cmake/bin/cmake /usr/local/bin
RUN cd /tmp && wget https://dl.bintray.com/boostorg/release/1.64.0/source/boost_1_64_0.tar.gz \
&& tar zxf boost_1_64_0.tar.gz \
......@@ -42,10 +44,24 @@ RUN cd /tmp && mkdir wasm-compiler && cd wasm-compiler \
&& cmake -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX=/opt/wasm -DLLVM_TARGETS_TO_BUILD= -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=WebAssembly -DCMAKE_BUILD_TYPE=Release ../ \
&& make -j$(nproc) install && rm -rf /tmp/wasm-compiler
RUN cd /tmp && wget https://github.com/WebAssembly/binaryen/archive/1.37.21.tar.gz && tar zxf 1.37.21.tar.gz \
&& cd binaryen-1.37.21 && cmake . && make && mkdir /opt/binaryen && mv /tmp/binaryen-1.37.21/bin /opt/binaryen \
&& ln -s /opt/binaryen/bin/* /usr/local && rm -rf /tmp/*
# ** Following the official master branch code takes a long time to download, depending on the network speed.
#RUN cd /tmp && git clone https://github.com/EOSIO/eos.git --recursive \
# && mkdir -p /opt/eos/bin/data-dir && cd eos && mkdir build && cd build \
# && cmake -DWASM_LLVM_CONFIG=/opt/wasm/bin/llvm-config -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang -DCMAKE_INSTALL_PREFIX=/opt/eos ../ \
# && make -j$(nproc) && make install && mv ../contracts / \
# && ln -s /opt/eos/bin/eos* /usr/local/bin \
# && rm -rf /tmp/eos*
RUN mkdir -p /opt/eos/bin/data-dir && mkdir -p /tmp/eos/build/
COPY . /tmp/eos/
# ** Using local code saves considerable time, but does't guarantee that your code stays up-to-date
COPY . /tmp/eos/
RUN cd /tmp/eos/build && cmake -DWASM_LLVM_CONFIG=/opt/wasm/bin/llvm-config -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang -DCMAKE_INSTALL_PREFIX=/opt/eos ../ \
&& make -j$(nproc) && make install && mv ../contracts / \
&& ln -s /opt/eos/bin/eos* /usr/local/bin \
......
### Run in docker
So simple and fast operation EOS:
Simple and fast setup of EOS on Docker is also available. Firstly, install dependencies:
- [Docker](https://docs.docker.com)
- [Docker-compose](https://github.com/docker/compose)
- [Docker-volumes](https://github.com/cpuguy83/docker-volumes)
Build eos images
Build eos image
```
cd eos/Docker
cp ../genesis.json .
docker build --rm -t eosio/eos .
git clone https://github.com/EOSIO/eos.git --recursive
cd eos
cp genesis.json Docker
docker build -t eosio/eos -f Docker/Dockerfile .
```
Start docker
......@@ -18,17 +19,21 @@ Start docker
```
sudo rm -rf /data/store/eos # options
sudo mkdir -p /data/store/eos
docker-compose -f docker-compose.yml up
docker-compose -f Docker/docker-compose.yml up
```
Get chain info
```
curl http://127.0.0.1:8888/v1/chain/get_info
```
Run example contracts
You can run the `eosc` commands via `docker exec` command. For example:
```
cd /data/store/eos/contracts/exchange
docker exec docker_eos_1 eosc setcode exchange contracts/exchange/exchange.wast contracts/exchange/exchange.abi
cd /data/store/eos/contracts/currency
docker exec docker_eos_1 eosc setcode currency contracts/currency/currency.wast contracts/currency/currency.abi
docker exec docker_eos_1 eosc contract exchange contracts/exchange/exchange.wast contracts/exchange/exchange.abi
```
......
......@@ -18,10 +18,10 @@ shared-file-dir = "blockchain"
shared-file-size = 8192
# The local IP and port to listen for incoming http connections.
http-server-endpoint = 127.0.0.1:8888
http-server-endpoint = 0.0.0.0:8888
# The local IP address and port to listen for incoming connections.
listen-endpoint = 127.0.0.1:9876
listen-endpoint = 0.0.0.0:9876
# The IP address and port of a remote peer to sync with.
# remote-endpoint =
......
#!/bin/bash
# Usage:
# Go into cmd loop: sudo ./eosc.sh
# Run single cmd: sudo ./eosc.sh <eosc paramers>
PREFIX="docker exec docker_eos_1 eosc"
if [ -z $1 ] ; then
while :
do
read -e -p "eosc " cmd
history -s "$cmd"
$PREFIX $cmd
done
else
$PREFIX $@
fi
#Doxygen Guide for EOS
# Doxygen Guide for EOS
<br/>
## Index
......
......@@ -338,7 +338,7 @@ sudo mkdir -p /data/store/eos
docker-compose -f Docker/docker-compose.yml up
```
Get chain info
Get chain info:
```
curl http://127.0.0.1:8888/v1/chain/get_info
......
......@@ -4,7 +4,7 @@ machine:
dependencies:
override:
- cd ~/eos/Docker && docker build -t eosio/eos .
- cd ~/eos && docker build -t eosio/eos -f Docker/Dockerfile .
test:
pre:
......
此差异已折叠。
此差异已折叠。
......@@ -3,7 +3,7 @@ Welcome to the eos.io Documentation
@note this documentation is under development and may be incorrect.
This documentaiton describes the APIs available to eos.io application developers. This is the best place to
This documentation describes the APIs available to eos.io application developers. This is the best place to
start your journey into smart contract development.
## Environment
......
......@@ -5,21 +5,180 @@
extern "C" {
/**
* @defgroup mathcapi Math C API
* @brief defines builtin math functions
* @brief Defines basic mathematical operations for higher abstractions to use.
* @ingroup mathapi
*
* @{
*/
/**
* Multiply two 128 bit unsigned integers and assign the value to the first parameter.
* @brief Multiply two 128 unsigned bit integers. Throws exception if pointers are invalid.
* @param self Pointer to the value to be multiplied. It will be replaced with the result.
* @param other Pointer to the Value to be multiplied.
*
* Example:
* @code
* uint128_t self(100);
* uint128_t other(100);
* multeq_i128(&self, &other);
* printi128(self); // Output: 10000
* @endcode
*/
void multeq_i128( uint128_t* self, const uint128_t* other );
/**
* Divide two 128 bit unsigned integers and assign the value to the first parameter.
* It will throw an exception if the value of other is zero.
* @brief Divide two 128 unsigned bit integers and throws an exception in case of invalid pointers
* @param self Pointer to numerator. It will be replaced with the result
* @param other Pointer to denominator
* Example:
* @code
* uint128_t self(100);
* uint128_t other(100);
* diveq_i128(&self, &other);
* printi128(self); // Output: 1
* @endcode
*/
void diveq_i128 ( uint128_t* self, const uint128_t* other );
/**
* Get the result of addition between two double interpreted as 64 bit unsigned integer
* This function will first reinterpret_cast both inputs to double (50 decimal digit precision), add them together, and reinterpret_cast the result back to 64 bit unsigned integer.
* @brief Addition between two double
* @param a Value in double interpreted as 64 bit unsigned integer
* @param b Value in double interpreted as 64 bit unsigned integer
* @return Result of addition reinterpret_cast to 64 bit unsigned integers
*
* Example:
* @code
* uint64_t a = double_div( i64_to_double(5), i64_to_double(10) );
* uint64_t b = double_div( i64_to_double(5), i64_to_double(2) );
* uint64_t res = double_add( a, b );
* printd(res); // Output: 3
* @endcode
*/
uint64_t double_add(uint64_t a, uint64_t b);
/**
* Get the result of multiplication between two double interpreted as 64 bit unsigned integer
* This function will first reinterpret_cast both inputs to double (50 decimal digit precision), multiply them together, and reinterpret_cast the result back to 64 bit unsigned integer.
* @brief Multiplication between two double
* @param a Value in double interpreted as 64 bit unsigned integer
* @param b Value in double interpreted as 64 bit unsigned integer
* @return Result of multiplication reinterpret_cast to 64 bit unsigned integers
*
* Example:
* @code
* uint64_t a = double_div( i64_to_double(10), i64_to_double(10) );
* uint64_t b = double_div( i64_to_double(5), i64_to_double(2) );
* uint64_t res = double_mult( a, b );
* printd(res); // Output: 2.5
* @endcode
*/
uint64_t double_mult(uint64_t a, uint64_t b);
/**
* Get the result of division between two double interpreted as 64 bit unsigned integer
* This function will first reinterpret_cast both inputs to double (50 decimal digit precision), divide numerator with denominator, and reinterpret_cast the result back to 64 bit unsigned integer.
* Throws an error if b is zero (after it is reinterpret_cast to double)
* @brief Division between two double
* @param a Numerator in double interpreted as 64 bit unsigned integer
* @param b Denominator in double interpreted as 64 bit unsigned integer
* @return Result of division reinterpret_cast to 64 bit unsigned integers
*
* Example:
* @code
* uint64_t a = double_div( i64_to_double(10), i64_to_double(100) );
* printd(a); // Output: 0.1
* @endcode
*/
uint64_t double_div(uint64_t a, uint64_t b);
/**
* Get the result of less than comparison between two double
* This function will first reinterpret_cast both inputs to double (50 decimal digit precision) before doing the less than comparison.
* @brief Less than comparison between two double
* @param a Value in double interpreted as 64 bit unsigned integer
* @param b Value in double interpreted as 64 bit unsigned integer
* @return 1 if first input is smaller than second input, 0 otherwise
*
* Example:
* @code
* uint64_t a = double_div( i64_to_double(10), i64_to_double(10) );
* uint64_t b = double_div( i64_to_double(5), i64_to_double(2) );
* uint64_t res = double_lt( a, b );
* printi(res); // Output: 1
* @endcode
*/
uint32_t double_lt(uint64_t a, uint64_t b);
/**
* Get the result of equality check between two double
* This function will first reinterpret_cast both inputs to double (50 decimal digit precision) before doing equality check.
* @brief Equality check between two double
* @param a Value in double interpreted as 64 bit unsigned integer
* @param b Value in double interpreted as 64 bit unsigned integer
* @return 1 if first input is equal to second input, 0 otherwise
*
* Example:
* @code
* uint64_t a = double_div( i64_to_double(10), i64_to_double(10) );
* uint64_t b = double_div( i64_to_double(5), i64_to_double(2) );
* uint64_t res = double_eq( a, b );
* printi(res); // Output: 0
* @endcode
*/
uint32_t double_eq(uint64_t a, uint64_t b);
/**
* Get the result of greater than comparison between two double
* This function will first reinterpret_cast both inputs to double (50 decimal digit precision) before doing the greater than comparison.
* @brief Greater than comparison between two double
* @param a Value in double interpreted as 64 bit unsigned integer
* @param b Value in double interpreted as 64 bit unsigned integer
* @return 1 if first input is greater than second input, 0 otherwise
*
* Example:
* @code
* uint64_t a = double_div( i64_to_double(10), i64_to_double(10) );
* uint64_t b = double_div( i64_to_double(5), i64_to_double(2) );
* uint64_t res = double_gt( a, b );
* printi(res); // Output: 0
* @endcode
*/
uint32_t double_gt(uint64_t a, uint64_t b);
/**
* Convert double (interpreted as 64 bit unsigned integer) to 64 bit unsigned integer.
* This function will first reinterpret_cast the input to double (50 decimal digit precision) then convert it to double, then reinterpret_cast it to 64 bit unsigned integer.
* @brief Convert double to 64 bit unsigned integer
* @param self Value in double interpreted as 64 bit unsigned integer
* @return Result of conversion in 64 bit unsigned integer
*
* Example:
* @code
* uint64_t a = double_div( i64_to_double(5), i64_to_double(2) );
* uint64_t res = double_to_i64( a );
* printi(res); // Output: 2
* @endcode
*/
uint64_t double_to_i64(uint64_t a);
/**
* Convert 64 bit unsigned integer to double (interpreted as 64 bit unsigned integer).
* This function will convert the input to double (50 decimal digit precision) then reinterpret_cast it to 64 bit unsigned integer.
* @brief Convert 64 bit unsigned integer to double (interpreted as 64 bit unsigned integer)
* @param self Value to be converted
* @return Result of conversion in double (interpreted as 64 bit unsigned integer)
*
* Example:
* @code
* uint64_t res = i64_to_double( 3 );
* printd(res); // Output: 3
* @endcode
*/
uint64_t i64_to_double(uint64_t a);
/// @}
} // extern "C"
......@@ -5,7 +5,7 @@ namespace eos {
/**
* @defgroup mathapi Math API
* @brief Defines common math functions
* @brief Defines common math functions
* @ingroup contractdev
*/
......@@ -17,18 +17,47 @@ namespace eos {
* @{
*/
/** @brief wraps multeq_i128 from @ref mathcapi */
/**
* Multiply two 128 bit unsigned integers and assign the value to the first parameter.
* This wraps multeq_i128 from @ref mathcapi.
* @brief wraps multeq_i128 from @ref mathcapi
* @param self Value to be multiplied. It will be replaced with the result
* @param other Value integer to be multiplied.
*
* Example:
* @code
* uint128_t self(100);
* uint128_t other(100);
* multeq(self, other);
* std::cout << self; // Output: 10000
* @endcode
*/
inline void multeq( uint128_t& self, const uint128_t& other ) {
multeq_i128( &self, &other );
}
/** @brief wraps diveq_i128 from @ref mathcapi */
/**
* Divide two 128 bit unsigned integers and assign the value to the first parameter.
* It will throw an exception if other is zero.
* This wraps diveq_i128 from @ref mathcapi
* @brief wraps diveq_i128 from @ref mathcapi
* @param self Numerator. It will be replaced with the result
* @param other Denominator
*
* Example:
* @code
* uint128_t self(100);
* uint128_t other(100);
* diveq(self, other);
* std::cout << self; // Output: 1
* @endcode
*/
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
* @brief A struct that wraps uint128 integer and defines common operator overloads
*/
struct uint128 {
public:
......@@ -50,20 +79,20 @@ namespace eos {
return a.value >= b.value;
}
uint128& operator *= ( const uint128_t& other ) {
uint128& operator *= ( const uint128_t& other ) {
multeq( value, other );
return *this;
}
uint128& operator *= ( const uint128& other ) {
uint128& operator *= ( const uint128& other ) {
multeq( value, other.value );
return *this;
}
uint128& operator /= ( const uint128_t& other ) {
uint128& operator /= ( const uint128_t& other ) {
diveq( value, other );
return *this;
}
uint128& operator /= ( const uint128& other ) {
uint128& operator /= ( const uint128& other ) {
diveq( value, other.value );
return *this;
}
......@@ -78,15 +107,38 @@ namespace eos {
};
/**
* Define similar to std::min()
* Get the smaller of the given values
* @brief Defined similar to std::min()
* @param a Value to compare
* @param b Value to compare
* @return The smaller of a and b. If they are equivalent, returns a
*
* Example:
* @code
* uint128_t a(1);
* uint128_t b(2);
* std::cout << min(a, b); // Output: 1
* @endcode
*/
template<typename T>
T min( const T& a, const T&b ) {
return a < b ? a : b;
}
/**
* Define similar to std::max()
*/
* Get the greater of the given values.
* @brief Define similar to std::max()
* @param a Value to compare
* @param b Value to compare
* @return The greater of a and b. If they are equivalent, returns a
*
* Example:
* @code
* uint128_t a(1);
* uint128_t b(2);
* std::cout << max(a, b); // Output: 2
* @endcode
*/
template<typename T>
T max( const T& a, const T&b ) {
return a > b ? a : b;
......
......@@ -7,69 +7,119 @@ extern "C" {
* @ingroup contractdev
* @brief Define API for querying message properties
*
*/
/**
* @defgroup messagecapi Message C API
* @ingroup messageapi
* @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[];
* AccountName code; // the contract defining the primary code to execute for code/type
* FuncName type; // the action to be taken
* AccountPermission[] authorization; // the accounts and permission levels provided
* Bytes data; // opaque data processed by code
* };
* ```
*
*
* 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
*
* Example:
* @code
* // Assume this message is used for the following examples:
* // {
* // "code": "eos",
* // "type": "transfer",
* // "authorization": [{ "account": "inita", "permission": "active" }],
* // "data": {
* // "from": "inita",
* // "to": "initb",
* // "amount": 1000
* // }
* // }
*
* char buffer[128];
* uint32_t total = readMessage(buffer, 5); // buffer contains the content of the message up to 5 bytes
* print(total); // Output: 5
*
* uint32_t msgsize = messageSize();
* print(msgsize); // Output: size of the above message's data field
*
* requireNotice(N(initc)); // initc account will be notified for this message
*
* requireAuth(N(inita)); // Do nothing since inita exists in the auth list
* requireAuth(N(initb)); // Throws an exception
*
* AccountName code = currentCode();
* print(Name(code)); // Output: eos
*
* assert(Name(currentCode()) === "eos", "This message expects to be received by eos"); // Do nothing
* assert(Name(currentCode()) === "inita", "This message expects to be received by inita"); // Throws exception and roll back transfer transaction
*
* print(now()); // Output: timestamp of last accepted block
*
* @endcode
*
*
* @{
*/
/**
* @param msg - a pointer where up to @ref len bytes of the current message will be coppied
* Copy up to @ref len bytes of current message to the specified location
* @brief Copy current message to the specified location
* @param msg - a pointer where up to @ref len bytes of the current message will be copied
* @param len - len of the current message to be copied
* @return the number of bytes copied to msg
*/
uint32_t readMessage( void* msg, uint32_t len );
/**
* This method is useful for dynamicly sized messages
*
* Get the length of the current message's data field
* This method is useful for dynamically sized messages
* @brief Get the length of current message's data field
* @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
* Add the specified account to set of accounts to be notified
* @brief Add the specified account to set of accounts to be notified
* @param name - name of the account to be verified
*/
void requireNotice( AccountName );
void requireNotice( AccountName name );
/**
* Verifies that @ref name exists in the set of provided auths on a message. Throws if not found
* @brief Verify specified account exists in the set of provided auths
* @param name - name of the account to be verified
*/
void requireAuth( AccountName name );
/**
* @return the account which specifes the code that is being run
* Get the account which specifies the code that is being run
* @brief Get the account which specifies the code that is being run
* @return the account which specifies the code that is being run
*/
AccountName currentCode();
/**
* Aborts processing of this message and unwinds all pending changes
*
* Aborts processing of this message and unwinds all pending changes if the test condition is true
* @brief 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)
* @brief Get time of the last accepted block
* @return time in seconds from 1970 of the last accepted block
*/
Time now();
......
......@@ -11,12 +11,25 @@ namespace eos {
*
* @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.
*
* @brief Interpret the message body as type T
*
* Example:
* @code
* struct dummy_message {
* char a; //1
* unsigned long long b; //8
* int c; //4
* };
* dummy_message msg = currentMessage<dummy_message>();
* @endcode
*/
template<typename T>
T currentMessage() {
......@@ -30,13 +43,19 @@ namespace eos {
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
* All of the listed accounts will be added to the set of accounts to be notified
*
* This helper method enables you to add multiple accounts to accounts to be notified list 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
*
* @brief Verify specified accounts exist in the set of notified accounts
*
* Example:
* @code
* requireNotice(N(Account1), N(Account2), N(Account3)); // throws exception if any of them not in set.
* @endcode
*/
template<typename... Accounts>
void requireNotice( AccountName name, Accounts... accounts ){
......
......@@ -16,18 +16,64 @@ extern "C" {
*/
/**
* Prints string
* @brief Prints string
* @param cstr - a null terminated string
*
* Example:
* @code
* prints("Hello World!"); // Output: Hello World!
* @endcode
*/
void prints( const char* cstr );
/**
* Prints value as an integer
* Prints value as a 64 bit unsigned integer
* @brief Prints value as a 64 bit unsigned integer
* @param Value of 64 bit unsigned integer to be printed
*
* Example:
* @code
* printi(1e+18); // Output: 1000000000000000000
* @endcode
*/
void printi( uint64_t value );
/**
* Prints value as a 128 bit unsigned integer
* @brief Prints value as a 128 bit unsigned integer
* @param value 128 bit integer to be printed
*
* Example:
* @code
* uint128_t large_int(87654323456);
* printi128(large_int); // Output: 87654323456
* @endcode
*/
void printi128( const uint128_t* value );
/**
* Prints value as double
* @brief Prints value as double
* @param Value of double (interpreted as 64 bit unsigned integer) to be printed
*
* Example:
* @code
* uint64_t double_value = double_div( i64_to_double(5), i64_to_double(10) );
* printd(double_value); // Output: 0.5
* @endcode
*/
void printd(uint64_t value);
/**
* Prints a 64 bit names as base32 encoded string
* @brief Prints a 64 bit names as base32 encoded string
* @param Value of 64 bit names to be printed
*
* Example:
* @code
* printn(N(abcde)); // Output: abcde
* @endcode
*/
void printn( uint64_t name );
/// @}
......
......@@ -7,30 +7,74 @@ namespace eos {
static_assert( sizeof(long) == sizeof(int), "unexpected size difference" );
/**
* Prints string
* @brief Prints string
* @param cstr - a null terminated string
*/
inline void print( const char* ptr ) {
prints(ptr);
}
/**
* Prints 64 bit unsigned integer as a 64 bit unsigned integer
* @brief Prints integer 64 bit unsigned integer
* @param Value to be printed
*/
inline void print( uint64_t num ) {
printi(num);
}
/**
* Prints 32 bit unsigned integer as a 64 bit unsigned integer
* @brief Prints integer 32 bit unsigned integer
* @param Value to be printed
*/
inline void print( uint32_t num ) {
printi(num);
}
/**
* Prints integer as a 64 bit unsigned integer
* @brief Prints integer
* @param Value to be printed
*/
inline void print( int num ) {
printi(num);
}
/**
* Prints unsigned integer as a 64 bit unsigned integer
* @brief Prints unsigned integer
* @param Value to be printed
*/
inline void print( unsigned int num ) {
printi(num);
}
/**
* Prints uint128 struct as 128 bit unsigned integer
* @brief Prints uint128 struct
* @param Value to be printed
*/
inline void print( uint128 num ) {
printi128((uint128_t*)&num);
}
/**
* Prints 128 bit unsigned integer
* @brief Prints 128 bit unsigned integer
* @param Value to be printed
*/
inline void print( uint128_t num ) {
printi128((uint128_t*)&num);
}
/**
* Prints a 64 bit names as base32 encoded string
* @brief Prints a 64 bit names as base32 encoded string
* @param Value of 64 bit names to be printed
*/
inline void print( Name name ) {
printn(name.value);
}
......@@ -63,6 +107,23 @@ namespace eos {
*
* @{
*/
/**
* Print out value / list of values (except double)
* @brief Print out value / list of values
* @param a Value to be printed
* @param args Other values to be printed
*
* Example:
* @code
* char *s = "Hello World!";
* uint64_t unsigned_64_bit_int = 1e+18;
* uint128_t unsigned_128_bit_int (87654323456);
* uint64_t string_as_unsigned_64_bit = N(abcde);
* print(s , unsigned_64_bit_int, unsigned_128_bit_int, string_as_unsigned_64_bit);
* // Ouput: Hello World!100000000000000000087654323456abcde
* @endcode
*/
template<typename Arg, typename... Args>
void print( Arg a, Args... args ) {
print(a);
......@@ -74,6 +135,22 @@ namespace eos {
*/
class iostream {};
/**
* Overload c++ iostream
* @brief Overload c++ iostream
* @param out Output strem
* @param v Value to be printed
*
* Example:
* @code
* char *s = "Hello World!";
* uint64_t unsigned_64_bit_int = 1e+18;
* uint128_t unsigned_128_bit_int (87654323456);
* uint64_t string_as_unsigned_64_bit = N(abcde);
* std::out << s << " " << unsigned_64_bit_int << " " << unsigned_128_bit_int << " " << string_as_unsigned_64_bit);
* // Ouput: Hello World! 1000000000000000000 87654323456 abcde
* @endcode
*/
template<typename T>
inline iostream& operator<<( iostream& out, const T& v ) {
print( v );
......
......@@ -12,7 +12,7 @@
* @brief Defines the ABI for interfacing with standard-compatible token messages and database tables.
* @ingroup contractdev
*
*
* @{
*/
......@@ -20,62 +20,218 @@
namespace eos {
/**
* Base token structure with checks for proper types and over/underflows.
* It supports the following operator: +=, -=, +, -, <=, <, ==, !=, >=, >, bool and also print functionality
* @brief a uint64_t wrapper with checks for proper types and over/underflows.
*
* @tparam NumberType - numeric type of the token
* @tparam CurrencyType - type of the currency (e.g. eos) represented as an unsigned 64 bit integer
* @ingroup tokens
* @code
* typedef eos::token<uint32_t, N(MyToken)> MyToken;
* MyToken a(128);
* a.print(); // Output: 128 MyToken
* MyToken b(64);
* a += b;
* a.print(); // Output: 192 MyToken
* b.print(); // Output: 64 MyToken
* a -= b;
* a.print(); // Output: 128 MyToken
* b.print(); // Output: 64 MyToken
* b -= a; // Throws integer underflow exception
* MyToken c = a + b;
* c.print(); // Output: 192 MyToken
* MyToken d = a - b;
* d.print(); // Output: 64 MyToken
* MyToken maxToken(std::numeric_limits<uint32_t>::max());
* maxToken += b; // Throws integer overflow exception
* std::cout << (maxToken > b); // Output: true
* std::cout << (b > maxToken); // Output: false
* std::cout << (bool)maxToken; // Output: true
* std::cout << (a == b); // Output: false
* std::cout << (a != b); // Output: true
* @endcode
*/
template<typename NumberType, uint64_t CurrencyType = N(eos) >
struct token {
/**
* Type of the currency (e.g. eos) represented as an unsigned 64 bit integer
* @brief Type of the currency
*/
static const uint64_t currency_type = CurrencyType;
/**
* Default constructor
* @brief Default constructor
*/
token(){}
/**
* Constructor for token given quantity of tokens available
* @brief Constructor for token given quantity of tokens available
* @param v - quantity of tokens available
*/
explicit token( NumberType v ):quantity(v){};
/**
* Quantity of tokens available
* @brief Quantity of tokens available
*/
NumberType quantity = 0;
/**
* Subtracts quantity of token from this object
* Throws an exception if underflow
* @brief Subtracts quantity of token from this object
* @param a token to be subtracted
* @return this token after subtraction
*/
token& operator-=( const token& a ) {
assert( quantity >= a.quantity, "integer underflow subtracting token balance" );
quantity -= a.quantity;
return *this;
}
/**
* Adds quantity of token to this object
* Throws an exception if overflow
* @brief Adds quantity of token to this object
* @param a token to be added
* @return this token after addition
*/
token& operator+=( const token& a ) {
assert( quantity + a.quantity >= a.quantity, "integer overflow adding token balance" );
quantity += a.quantity;
return *this;
}
/**
* Adds quantity of two tokens and return a new token
* Throws an exception if overflow
* @brief Adds quantity of two tokens and return a new token
* @param a token to be added
* @param b token to be added
* @return result of addition as a new token
*/
inline friend token operator+( const token& a, const token& b ) {
token result = a;
result += b;
return result;
}
/**
* Subtracts quantity of two tokens and return a new token
* Throws an exception if underflow
* @brief Subtracts quantity of two tokens and return a new token
* @param a token to be subtracted
* @param b token to be subtracted
* @return result of subtraction as a new token
*/
inline friend token operator-( const token& a, const token& b ) {
token result = a;
result -= b;
return result;
}
/**
* Less than or equal to comparison operator
* @brief Less than or equal to comparison operator
* @param a token to be compared
* @param b token to be compared
* @return true if quantity of a is less than or equal to quantity of b
*/
friend bool operator <= ( const token& a, const token& b ) { return a.quantity <= b.quantity; }
/**
* Less than comparison operator
* @brief Less than comparison operator
* @param a token to be compared
* @param b token to be compared
* @return true if quantity of a is less than quantity of b
*/
friend bool operator < ( const token& a, const token& b ) { return a.quantity < b.quantity; }
/**
* Greater than or equal to comparison operator
* @brief Greater than or equal to comparison operator
* @param a token to be compared
* @param b token to be compared
* @return true if quantity of a is greater than or equal to quantity of b
*/
friend bool operator >= ( const token& a, const token& b ) { return a.quantity >= b.quantity; }
/**
* Greater than comparison operator
* @brief Greater than comparison operator
* @param a token to be compared
* @param b token to be compared
* @return true if quantity of a is greater than quantity of b
*/
friend bool operator > ( const token& a, const token& b ) { return a.quantity > b.quantity; }
/**
* Equality comparison operator
* @brief Equality comparison operator
* @param a token to be compared
* @param b token to be compared
* @return true if quantity of a is equal to quantity of b
*/
friend bool operator == ( const token& a, const token& b ) { return a.quantity == b.quantity; }
/**
* Inequality comparison operator
* @brief Inequality comparison operator
* @param a token to be compared
* @param b token to be compared
* @return true if quantity of a is not equal to quantity of b
*/
friend bool operator != ( const token& a, const token& b ) { return a.quantity != b.quantity; }
/**
* Boolean conversion operator
* @brief Boolean conversion operator
* @return true if quantity is not zero
*/
explicit operator bool()const { return quantity != 0; }
inline void print() {
/**
* Print as string representation of the token (e.g. 1 EOS)
* @brief Print as string
*/
inline void print() {
eos::print( quantity, " ", Name(CurrencyType) );
}
}
};
/**
* @brief defines a fixed precision price between two tokens.
*
* A price is written as X Base/Quote
*
* Defines a fixed precision price between two tokens.
* A price is written as X Base/Quote. Where X is a power of 10 which makes it simpler to just shift the decimal.
* It supports the following operator: /, \, <=, <, ==, !=, >=, > and also print functionality
* @brief Defines a fixed precision price between two tokens.
* @tparam BaseToken - represents the type of the base token
* @tparam QuoteToken - represents the type of the quote token
* @ingroup tokens
* @code
* typedef eos::token<uint64_t, N(MyBaseToken)> MyBaseToken;
* typedef eos::token<uint64_t, N(MyQuoteToken)> MyQuoteToken;
* typedef price<MyBaseToken, MyQuoteToken> MyBaseToQuotePrice;
* MyBaseToken zeroBaseToken;
* MyQuoteToken zeroQuoteToken;
* MyBaseToQuotePrice zeroBaseToQuote(zeroBaseToken, zeroQuoteToken); // throws invalid price exception
* MyBaseToken baseToken(128);
* MyQuoteToken quoteToken(128);
* MyBaseToQuotePrice aPrice(baseToken, quoteToken);
* aPrice.print(); // Output: 1e+15. MyBaseToken / MyQuoteToken
* MyQuoteToken anotherQuote = baseToken / price;
* std::cout << (anotherQuote == quoteToken); // Output: true
* MyBaseToken anotherBase = quoteToken * price;
* std::cout << (anotherBase == baseToken); // Output: true
* MyBaseToQuotePrice anotherPrice(baseToken, quoteToken);
* std::cout << (aPrice == anotherPrice); // Output: true
* std::cout << (aPrice != anotherPrice); // Output: false
* MyBaseToken base256(256);
* MyBaseToQuotePrice price2(base256, quoteToken);
* std::cout << (price2 > aPrice); // Output: true
* std::cout << (aPrice < price2); // Output: true
* @endcode
*/
template<typename BaseToken, typename QuoteToken>
struct price
......@@ -90,8 +246,19 @@ namespace eos {
*/
static const uint64_t precision = 1000ll*1000ll*1000ll*1000ll*1000ll;
/**
* Default constructor.
* Initialize base per quote to be 1.
* @brief Default constructor.
*/
price():base_per_quote(1ul){}
/**
* Construction for price given the base token and quote token
* @brief Construction for price given the base token and quote token
* @param base - base token
* @param quote - quote token
*/
price( BaseToken base, QuoteToken quote ) {
assert( base >= BaseToken(1ul), "invalid price" );
assert( quote >= QuoteToken(1ul), "invalid price" );
......@@ -101,11 +268,25 @@ namespace eos {
base_per_quote /= quote.quantity;
}
/**
* Operator returns a quote token given a base token and the conversion price
* @brief Operator returns a quote token given a base token and the conversion price
* @param b - base token
* @param q - price
* @return quote token
*/
friend QuoteToken operator / ( BaseToken b, const price& q ) {
eos::print( "operator/ ", uint128(b.quantity), " * ", uint128( precision ), " / ", q.base_per_quote, "\n" );
return QuoteToken( uint64_t((uint128(b.quantity) * uint128(precision) / q.base_per_quote)) );
}
/**
* Operator returns a base token given a quote token and the conversion price
* @brief Operator returns a base token given a quote token and the conversion price
* @param b - quote token
* @param q - price
* @return base token
*/
friend BaseToken operator * ( const QuoteToken& b, const price& q ) {
eos::print( "b: ", b, " \n" );
eos::print( "operator* ", uint128(b.quantity), " * ", uint128( q.base_per_quote ), " / ", precision, "\n" );
......@@ -113,36 +294,114 @@ namespace eos {
return BaseToken( uint64_t((b.quantity * q.base_per_quote) / precision) );
}
/**
* Less than or equal to comparison operator
* @brief Less than or equal to comparison operator
* @param a price to be compared
* @param b price to be compared
* @return true if base per quote of a is less than or equal to base per quote of b
*/
friend bool operator <= ( const price& a, const price& b ) { return a.base_per_quote <= b.base_per_quote; }
/**
* Less than comparison operator
* @brief Less than comparison operator
* @param a price to be compared
* @param b price to be compared
* @return true if base per quote of a is less than base per quote of b
*/
friend bool operator < ( const price& a, const price& b ) { return a.base_per_quote < b.base_per_quote; }
/**
* Greater than or equal to comparison operator
* @brief Greater than or equal to comparison operator
* @param a price to be compared
* @param b price to be compared
* @return true if base per quote of a is greater than or equal to base per quote of b
*/
friend bool operator >= ( const price& a, const price& b ) { return a.base_per_quote >= b.base_per_quote; }
/**
* Greater than comparison operator
* @brief Greater than comparison operator
* @param a price to be compared
* @param b price to be compared
* @return true if base per quote of a is greater than base per quote of b
*/
friend bool operator > ( const price& a, const price& b ) { return a.base_per_quote > b.base_per_quote; }
/**
* Equality comparison operator
* @brief Equality comparison operator
* @param a price to be compared
* @param b price to be compared
* @return true if base per quote of a is equal to base per quote of b
*/
friend bool operator == ( const price& a, const price& b ) { return a.base_per_quote == b.base_per_quote; }
/**
* Inequality comparison operator
* @brief Inequality comparison operator
* @param a price to be compared
* @param b price to be compared
* @return true if base per quote of a is not equal to base per quote of b
*/
friend bool operator != ( const price& a, const price& b ) { return a.base_per_quote != b.base_per_quote; }
/**
* Print as string representing the conversion
* @brief Print as string
*/
inline void print() {
eos::print( base_per_quote, ".", " ", Name(base_token_type::currency_type), "/", Name(quote_token_type::currency_type) );
}
private:
/**
* represented as number of base tokens to purchase 1 quote token
* Represented as number of base tokens to purchase 1 quote token
* @brief Represented as number of base tokens to purchase 1 quote token
*/
eos::uint128 base_per_quote;
eos::uint128 base_per_quote;
};
/**
* Defines an eos tokens
* @brief Defines an eos tokens
*/
typedef eos::token<uint64_t,N(eos)> Tokens;
/**
* @brief the binary structure of the `transfer` message type for the `eos` contract.
*
* The binary structure of the `transfer` message type for the `eos` contract.
* @brief The binary structure of the `transfer` message type for the `eos` contract.
* @ingroup tokens
* @code
* Transfer MeToYou;
* MeToYou.from = N(Me);
* MeToYou.to = N(You);
* MeToYou.quantity = Tokens(100);
* @endcode
*/
struct Transfer {
/**
* Defines transfer action type
* @brief Defines transfer action type
*/
static const uint64_t action_type = N(transfer);
/**
* Name of the account who sends the token
* @brief Name of the account who sends the token
*/
AccountName from;
/**
* Name of the account who receives the token
* @brief Name of the account who receives the token
*/
AccountName to;
/**
* Quantity of token to be transferred
* @brief Quantity of token to be transferred
*/
Tokens quantity;
};
/// @} tokenhppapi
} // namespace eos
......@@ -8,4 +8,3 @@ add_subdirectory( egenesis )
add_subdirectory( utilities )
add_subdirectory( appbase )
add_subdirectory( native_contract )
add_subdirectory( wallet )
......@@ -718,23 +718,51 @@ void chain_controller::_apply_block(const signed_block& next_block)
} FC_CAPTURE_AND_RETHROW( (next_block.block_num()) ) }
namespace {
auto make_get_permission(const chainbase::database& db) {
return [&db](const types::AccountPermission& permission) {
auto key = boost::make_tuple(permission.account, permission.permission);
return db.get<permission_object, by_owner>(key);
};
}
auto make_authority_checker(const chainbase::database& db, const flat_set<public_key_type>& signingKeys) {
auto getPermission = make_get_permission(db);
auto getAuthority = [getPermission](const types::AccountPermission& permission) {
return getPermission(permission).auth;
};
auto depthLimit = db.get<global_property_object>().configuration.authDepthLimit;
return MakeAuthorityChecker(std::move(getAuthority), depthLimit, signingKeys);
}
}
flat_set<public_key_type> chain_controller::get_required_keys(const SignedTransaction& trx, const flat_set<public_key_type>& candidateKeys)const {
auto checker = make_authority_checker(_db, candidateKeys);
for (const auto& message : trx.messages) {
for (const auto& declaredAuthority : message.authorization) {
if (!checker.satisfied(declaredAuthority)) {
EOS_ASSERT(checker.satisfied(declaredAuthority), tx_missing_sigs,
"Transaction declares authority '${auth}', but does not have signatures for it.",
("auth", declaredAuthority));
}
}
}
return checker.used_keys();
}
void chain_controller::check_transaction_authorization(const SignedTransaction& trx, bool allow_unused_signatures)const {
if ((_skip_flags & skip_transaction_signatures) && (_skip_flags & skip_authority_check)) {
//ilog("Skipping auth and sigs checks");
return;
}
auto getPermission = [&db=_db](const types::AccountPermission& permission) {
auto key = boost::make_tuple(permission.account, permission.permission);
return db.get<permission_object, by_owner>(key);
};
auto getAuthority = [&getPermission](const types::AccountPermission& permission) {
return getPermission(permission).auth;
};
auto depthLimit = get_global_properties().configuration.authDepthLimit;
auto getPermission = make_get_permission(_db);
#warning TODO: Use a real chain_id here (where is this stored? Do we still need it?)
auto checker = MakeAuthorityChecker(std::move(getAuthority), depthLimit, trx.get_signature_keys(chain_id_type{}));
auto checker = make_authority_checker(_db, trx.get_signature_keys(chain_id_type{}));
for (const auto& message : trx.messages)
for (const auto& declaredAuthority : message.authorization) {
......
......@@ -157,6 +157,15 @@ namespace eos { namespace chain {
ProcessedTransaction push_transaction( const SignedTransaction& trx, uint32_t skip = skip_nothing );
ProcessedTransaction _push_transaction( const SignedTransaction& trx );
/**
* Determine which public keys are needed to sign the given transaction.
* @param trx Transaction that requires signature
* @param candidateKeys Set of public keys to examine for applicability
* @return Subset of candidateKeys whose private keys should be used to sign transaction
* @throws fc::exception if candidateKeys does not contain all required keys
*/
flat_set<public_key_type> get_required_keys(const SignedTransaction& trx, const flat_set<public_key_type>& candidateKeys)const;
bool _push_block( const signed_block& b );
......
#include <fc/thread/thread.hpp>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/mem_fun.hpp>
#if defined(__linux__) && !defined(NDEBUG)
# include <pthread.h>
static void set_thread_name(const char* threadName)
{ pthread_setname_np(pthread_self(), threadName); }
#elif defined(__APPLE__) && !defined(NDEBUG)
# include <pthread.h>
static void set_thread_name(const char* threadName)
{ pthread_setname_np(threadName); }
#else// do nothing in release mode
static void set_thread_name(const char* threadName)
{}
#endif
namespace fc {
using namespace boost::multi_index;
struct by_time;
typedef multi_index_container <
std::shared_ptr<scheduled_task>,
indexed_by<
ordered_non_unique< tag<by_time>, const_mem_fun< scheduled_task, time_point, &scheduled_task::get_scheduled_time > >
>
> scheduled_task_index;
thread*& current_thread() {
static __thread thread* t = nullptr;
return t;
}
class thread_detail {
public:
thread_detail( fc::thread& t ):fc_thread(t) {
this_id = std::this_thread::get_id();
current_thread() = &t;
task_in_queue = nullptr;
}
~thread_detail(){ }
fc::thread& fc_thread;
std::thread::id this_id;
std::thread* std_thread = nullptr;
boost::fibers::promise<void> exit_promise;
std::string name;
boost::fibers::condition_variable task_ready;
boost::fibers::mutex task_ready_mutex;
std::atomic<detail::task*> task_in_queue;
detail::task* _queue = nullptr;
detail::task* _queue_end = nullptr;
bool _running = false;
bool _done = false;
scheduled_task_index _scheduled;
void async_task( detail::task* t ) {
idump((name));
if( _done ) {
delete t;
throw std::runtime_error( "attempt to async task on thread that has quit" );
}
auto stale_head = task_in_queue.load(std::memory_order_relaxed);
do { t->next = stale_head;
}while( !task_in_queue.compare_exchange_weak( stale_head, t, std::memory_order_release ) );
if( !stale_head )
{
dlog( "----grabbing ready mutex" );
std::unique_lock<boost::fibers::mutex> lock(task_ready_mutex);
dlog("--- got ready mutex, notify one" );
task_ready.notify_one();
}
}
bool exec_next_task() {
auto itr = _scheduled.begin();
if( _scheduled.end() != itr && (*itr)->get_scheduled_time() < fc::time_point::now() ) {
idump(((*itr)->get_scheduled_time()));
auto tsk = *itr;
_scheduled.erase(itr);
tsk->exec();
return true;
}
if( !_queue )
{
move_newly_scheduled_tasks_to_task_queue();
if( !_queue ) return false;
}
auto tmp = _queue;
_queue = _queue->next;
if( !_queue )
_queue_end = nullptr;
tmp->exec();
delete tmp;
return true;
}
/**
* Start a new fiber which will process tasks until it
* blocks on some event, at that time it should resume
* the exec() loop which will look for new tasks. If there
* are no new tasks then it will block on a wait condition
* until a new task comes in.
*
*/
void exec() {
_running = true;
while( !_done ) {
move_newly_scheduled_tasks_to_task_queue();
if( _queue || _scheduled.size() ) {
#if 1
boost::fibers::async( boost::fibers::launch::dispatch, [this](){ while( exec_next_task() ){} } );
#else
// elog( "creating new fiber... " );
static int tmp = 0;
++tmp;
/**
* First we execute the task, then delete it, and
* finally look for other tasks to execute, and
* exit when there are no more tasks in the queue
*/
boost::fibers::fiber fib( boost::fibers::launch::dispatch, [this,t=tmp](){
// wlog( "starting new fiber... ${d}", ("d",int64_t(t)) );
while( exec_next_task() ){}
// dlog( "exit fiber... ${d}", ("d",int64_t(t)) );
});
fib.detach();
#endif
} else {
//ilog( "grabbing task_read_mutex..." );
std::unique_lock<boost::fibers::mutex> lock(task_ready_mutex);
move_newly_scheduled_tasks_to_task_queue();
if( !(_queue || _scheduled.size()) ) {
if( !_scheduled.size() ) {
// wlog( "waiting until next event" );
task_ready.wait( lock );
// ilog( "wake up..." );
} else {
// wlog( "waiting for ${t} or until next event", ("t", (*_scheduled.begin())->get_scheduled_time() - fc::time_point::now() ));
task_ready.wait_until( lock, std::chrono::system_clock::time_point(std::chrono::microseconds( (*_scheduled.begin())->get_scheduled_time().time_since_epoch().count())) );
// wlog( "done waiting... " );
}
}
}
}
// ilog( "exec done" );
_running = false;
}
void move_newly_scheduled_tasks_to_task_queue()
{
// first, if there are any new tasks on 'task_in_queue', which is tasks that
// have been just been async or scheduled, but we haven't processed them.
// move them into the task_sch_queue or task_pqueue, as appropriate
//DLN: changed from memory_order_consume for boost 1.55.
//This appears to be safest replacement for now, maybe
//can be changed to relaxed later, but needs analysis.
auto pending_list = task_in_queue.exchange(0, std::memory_order_seq_cst);
if( !pending_list ) return;
/** reverse the list */
detail::task* cur = pending_list;
detail::task* prev = nullptr;
detail::task* next = nullptr;
detail::task* new_end = cur;
while( cur != nullptr ) {
next = cur->next;
cur->next = prev;
prev = cur;
cur = next;
}
/** insert the list to the current queue */
if( !_queue ) {
_queue = prev;
} else {
_queue_end->next = prev;
}
_queue_end = new_end;
}
};
namespace detail {
void thread_detail_cancel( thread& t, scheduled_task* stask ) {
t.async( [tptr=&t,stask](){
for( auto itr = tptr->my->_scheduled.begin(); itr != tptr->my->_scheduled.end(); ++itr ) {
if( itr->get() == stask ) {
tptr->my->_scheduled.erase(itr);
return;
}
}
});
}
}
thread::thread( const std::string& name ) {
boost::fibers::promise<void> prom;
auto stdt = new std::thread( [this,name,&prom]() {
my = new thread_detail( *this );
my->name = name;
prom.set_value();
set_thread_name( name.c_str() );
this->exec();
//elog( "exit thread" );
my->exit_promise.set_value();
});
prom.get_future().wait();
my->std_thread = stdt;
}
thread::thread( thread&& mv )
:my(mv.my){
mv.my = nullptr;
}
thread::thread( thread_detail* d ) {
my = new thread_detail(*this);
}
thread::~thread() {
delete my->std_thread;
delete my;
}
const string& thread::name()const {
return my->name;
}
thread& thread::current() {
auto cur = current_thread();
if( cur ) return *cur;
return *(new thread( (thread_detail*)nullptr ));
}
void thread::quit() {
if( !my->_done && my->_running )
async( [&](){ my->_done = true; }, "thread::quit" ).wait();
}
bool thread::is_running()const {
return !my->_done;
}
bool thread::is_current()const {
return this == &current();
}
void thread::set_name( const string& n ) {
//this->async( [=]() {
my->name = n;
set_thread_name( my->name.c_str() );
//}).wait();
}
void thread::exec() {
if( this != &current() ) elog( "exec called from wrong thread" );
else my->exec();
}
void thread::async_task( detail::task* t ) {
my->async_task(t);
if( !my->_running /*&& this == &current()*/ ) {
my->_running = true;
boost::fibers::async( boost::fibers::launch::post, [this](){ my->exec(); } );
/*
my->exec();
boost::fibers::fiber fib( boost::fibers::launch::post, [&](){
elog( "STARTING FIBER to call exec()" );
exec();
elog( "EXITING FIBER CALLING EXEC" );
} );
fib.detach();
*/
}
}
void thread::schedule( const std::shared_ptr<scheduled_task>& stask ) {
async( [=]() {
my->_scheduled.insert( stask );
});
}
void thread::join() {
quit();
if( my->std_thread ) {
my->exit_promise.get_future().wait();
my->std_thread->join();
}
}
} // namespace fc
file(GLOB HEADERS "include/eos/net/*.hpp")
set(SOURCES node.cpp
stcp_socket.cpp
core_messages.cpp
peer_database.cpp
peer_connection.cpp
message_oriented_connection.cpp)
add_library( eos_net ${SOURCES} ${HEADERS} )
target_link_libraries( eos_net
PUBLIC fc chainbase appbase eos_types)
target_include_directories( eos_net
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include"
PRIVATE "${CMAKE_SOURCE_DIR}/libraries/chain/include"
)
if(MSVC)
set_source_files_properties( node.cpp PROPERTIES COMPILE_FLAGS "/bigobj" )
endif(MSVC)
if (USE_PCH)
set_target_properties(eos_net PROPERTIES COTIRE_ADD_UNITY_BUILD FALSE)
cotire(eos_net)
endif(USE_PCH)
install( TARGETS
eos_net
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
install( FILES ${HEADERS} DESTINATION "include/eos/net" )
/*
* Copyright (c) 2017, Respective Authors.
*
* The MIT License
*
* 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 <eos/net/core_messages.hpp>
namespace eos { namespace net {
const core_message_type_enum trx_message::type = core_message_type_enum::trx_message_type;
const core_message_type_enum block_message::type = core_message_type_enum::block_message_type;
const core_message_type_enum item_ids_inventory_message::type = core_message_type_enum::item_ids_inventory_message_type;
const core_message_type_enum blockchain_item_ids_inventory_message::type = core_message_type_enum::blockchain_item_ids_inventory_message_type;
const core_message_type_enum fetch_blockchain_item_ids_message::type = core_message_type_enum::fetch_blockchain_item_ids_message_type;
const core_message_type_enum fetch_items_message::type = core_message_type_enum::fetch_items_message_type;
const core_message_type_enum item_not_available_message::type = core_message_type_enum::item_not_available_message_type;
const core_message_type_enum hello_message::type = core_message_type_enum::hello_message_type;
const core_message_type_enum connection_accepted_message::type = core_message_type_enum::connection_accepted_message_type;
const core_message_type_enum connection_rejected_message::type = core_message_type_enum::connection_rejected_message_type;
const core_message_type_enum address_request_message::type = core_message_type_enum::address_request_message_type;
const core_message_type_enum address_message::type = core_message_type_enum::address_message_type;
const core_message_type_enum closing_connection_message::type = core_message_type_enum::closing_connection_message_type;
const core_message_type_enum current_time_request_message::type = core_message_type_enum::current_time_request_message_type;
const core_message_type_enum current_time_reply_message::type = core_message_type_enum::current_time_reply_message_type;
const core_message_type_enum check_firewall_message::type = core_message_type_enum::check_firewall_message_type;
const core_message_type_enum check_firewall_reply_message::type = core_message_type_enum::check_firewall_reply_message_type;
const core_message_type_enum get_current_connections_request_message::type = core_message_type_enum::get_current_connections_request_message_type;
const core_message_type_enum get_current_connections_reply_message::type = core_message_type_enum::get_current_connections_reply_message_type;
} } // eos::net
/*
* Copyright (c) 2017, Respective Authors.
*
* The MIT License
*
* 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.
*/
#pragma once
#define EOS_NET_PROTOCOL_VERSION 106
/**
* Define this to enable debugging code in the p2p network interface.
* This is code that would never be executed in normal operation, but is
* used for automated testing (creating artificial net splits,
* tracking where messages came from and when)
*/
#define ENABLE_P2P_DEBUGGING_API 1
/**
* 2MiB
*/
#define MAX_MESSAGE_SIZE 1024*1024*2
#define EOS_NET_DEFAULT_PEER_CONNECTION_RETRY_TIME 30 // seconds
/**
* AFter trying all peers, how long to wait before we check to
* see if there are peers we can try again.
*/
#define EOS_PEER_DATABASE_RETRY_DELAY 15 // seconds
#define EOS_NET_PEER_HANDSHAKE_INACTIVITY_TIMEOUT 5
#define EOS_NET_PEER_DISCONNECT_TIMEOUT 20
#define EOS_NET_TEST_SEED_IP "104.236.44.210" // autogenerated
#define EOS_NET_TEST_P2P_PORT 1700
#define EOS_NET_DEFAULT_P2P_PORT 1776
#define EOS_NET_DEFAULT_DESIRED_CONNECTIONS 20
#define EOS_NET_DEFAULT_MAX_CONNECTIONS 200
#define EOS_NET_MAXIMUM_QUEUED_MESSAGES_IN_BYTES (1024 * 1024)
/**
* When we receive a message from the network, we advertise it to
* our peers and save a copy in a cache were we will find it if
* a peer requests it. We expire out old items out of the cache
* after this number of blocks go by.
*
* Recently lowered from 30 to match the default expiration time
* the web wallet imposes on transactions.
*/
#define EOS_NET_MESSAGE_CACHE_DURATION_IN_BLOCKS 5
/**
* We prevent a peer from offering us a list of blocks which, if we fetched them
* all, would result in a blockchain that extended into the future.
* This parameter gives us some wiggle room, allowing a peer to give us blocks
* that would put our blockchain up to an hour in the future, just in case
* our clock is a bit off.
*/
#define EOS_NET_FUTURE_SYNC_BLOCKS_GRACE_PERIOD_SEC (60 * 60)
#define EOS_NET_MAX_INVENTORY_SIZE_IN_MINUTES 2
#define EOS_NET_MAX_BLOCKS_PER_PEER_DURING_SYNCING 200
/**
* During normal operation, how many items will be fetched from each
* peer at a time. This will only come into play when the network
* is being flooded -- typically transactions will be fetched as soon
* as we find out about them, so only one item will be requested
* at a time.
*
* No tests have been done to find the optimal value for this
* parameter, so consider increasing or decreasing it if performance
* during flooding is lacking.
*/
#define EOS_NET_MAX_ITEMS_PER_PEER_DURING_NORMAL_OPERATION 1
/**
* Instead of fetching all item IDs from a peer, then fetching all blocks
* from a peer, we will interleave them. Fetch at least this many block IDs,
* then switch into block-fetching mode until the number of blocks we know about
* but haven't yet fetched drops below this
*/
#define EOS_NET_MIN_BLOCK_IDS_TO_PREFETCH 10000
#define EOS_NET_MAX_TRX_PER_SECOND 1000
/*
* Copyright (c) 2017, Respective Authors.
*
* The MIT License
*
* 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.
*/
#pragma once
#include <eos/net/config.hpp>
#include <eos/chain/block.hpp>
#include <fc/crypto/ripemd160.hpp>
#include <fc/crypto/elliptic.hpp>
#include <fc/crypto/sha256.hpp>
#include <fc/network/ip.hpp>
#include <fc/reflect/reflect.hpp>
#include <fc/time.hpp>
#include <fc/variant_object.hpp>
#include <fc/exception/exception.hpp>
#include <fc/io/enum_type.hpp>
#include <vector>
namespace eos { namespace net {
using eos::chain::SignedTransaction;
using eos::chain::block_id_type;
using eos::chain::transaction_id_type;
using eos::chain::signed_block;
typedef fc::ecc::public_key_data node_id_t;
typedef fc::sha256 item_hash_t;
struct item_id
{
uint32_t item_type;
item_hash_t item_hash;
item_id() {}
item_id(uint32_t type, const item_hash_t& hash) :
item_type(type),
item_hash(hash)
{}
bool operator==(const item_id& other) const
{
return item_type == other.item_type &&
item_hash == other.item_hash;
}
};
enum core_message_type_enum
{
trx_message_type = 1000,
block_message_type = 1001,
core_message_type_first = 5000,
item_ids_inventory_message_type = 5001,
blockchain_item_ids_inventory_message_type = 5002,
fetch_blockchain_item_ids_message_type = 5003,
fetch_items_message_type = 5004,
item_not_available_message_type = 5005,
hello_message_type = 5006,
connection_accepted_message_type = 5007,
connection_rejected_message_type = 5008,
address_request_message_type = 5009,
address_message_type = 5010,
closing_connection_message_type = 5011,
current_time_request_message_type = 5012,
current_time_reply_message_type = 5013,
check_firewall_message_type = 5014,
check_firewall_reply_message_type = 5015,
get_current_connections_request_message_type = 5016,
get_current_connections_reply_message_type = 5017,
core_message_type_last = 5099
};
const uint32_t core_protocol_version = EOS_NET_PROTOCOL_VERSION;
struct trx_message
{
static const core_message_type_enum type;
SignedTransaction trx;
trx_message() {}
trx_message(SignedTransaction transaction) :
trx(std::move(transaction))
{}
};
struct block_message
{
static const core_message_type_enum type;
block_message(){}
block_message(const signed_block& blk )
:block(blk),block_id(blk.id()){}
signed_block block;
block_id_type block_id;
};
struct item_ids_inventory_message
{
static const core_message_type_enum type;
uint32_t item_type;
std::vector<item_hash_t> item_hashes_available;
item_ids_inventory_message() {}
item_ids_inventory_message(uint32_t item_type, const std::vector<item_hash_t>& item_hashes_available) :
item_type(item_type),
item_hashes_available(item_hashes_available)
{}
};
struct blockchain_item_ids_inventory_message
{
static const core_message_type_enum type;
uint32_t total_remaining_item_count;
uint32_t item_type;
std::vector<item_hash_t> item_hashes_available;
blockchain_item_ids_inventory_message() {}
blockchain_item_ids_inventory_message(uint32_t total_remaining_item_count,
uint32_t item_type,
const std::vector<item_hash_t>& item_hashes_available) :
total_remaining_item_count(total_remaining_item_count),
item_type(item_type),
item_hashes_available(item_hashes_available)
{}
};
struct fetch_blockchain_item_ids_message
{
static const core_message_type_enum type;
uint32_t item_type;
std::vector<item_hash_t> blockchain_synopsis;
fetch_blockchain_item_ids_message() {}
fetch_blockchain_item_ids_message(uint32_t item_type, const std::vector<item_hash_t>& blockchain_synopsis) :
item_type(item_type),
blockchain_synopsis(blockchain_synopsis)
{}
};
struct fetch_items_message
{
static const core_message_type_enum type;
uint32_t item_type;
std::vector<item_hash_t> items_to_fetch;
fetch_items_message() {}
fetch_items_message(uint32_t item_type, const std::vector<item_hash_t>& items_to_fetch) :
item_type(item_type),
items_to_fetch(items_to_fetch)
{}
};
struct item_not_available_message
{
static const core_message_type_enum type;
item_id requested_item;
item_not_available_message() {}
item_not_available_message(const item_id& requested_item) :
requested_item(requested_item)
{}
};
struct hello_message
{
static const core_message_type_enum type;
std::string user_agent;
uint32_t core_protocol_version;
fc::ip::address inbound_address;
uint16_t inbound_port;
uint16_t outbound_port;
node_id_t node_public_key;
fc::ecc::compact_signature signed_shared_secret;
fc::sha256 chain_id;
fc::variant_object user_data;
hello_message() {}
hello_message(const std::string& user_agent,
uint32_t core_protocol_version,
const fc::ip::address& inbound_address,
uint16_t inbound_port,
uint16_t outbound_port,
const node_id_t& node_public_key,
const fc::ecc::compact_signature& signed_shared_secret,
const fc::sha256& chain_id_arg,
const fc::variant_object& user_data ) :
user_agent(user_agent),
core_protocol_version(core_protocol_version),
inbound_address(inbound_address),
inbound_port(inbound_port),
outbound_port(outbound_port),
node_public_key(node_public_key),
signed_shared_secret(signed_shared_secret),
chain_id(chain_id_arg),
user_data(user_data)
{}
};
struct connection_accepted_message
{
static const core_message_type_enum type;
connection_accepted_message() {}
};
enum class rejection_reason_code { unspecified,
different_chain,
already_connected,
connected_to_self,
not_accepting_connections,
blocked,
invalid_hello_message,
client_too_old };
struct connection_rejected_message
{
static const core_message_type_enum type;
std::string user_agent;
uint32_t core_protocol_version;
fc::ip::endpoint remote_endpoint;
std::string reason_string;
fc::enum_type<uint8_t, rejection_reason_code> reason_code;
connection_rejected_message() {}
connection_rejected_message(const std::string& user_agent, uint32_t core_protocol_version,
const fc::ip::endpoint& remote_endpoint, rejection_reason_code reason_code,
const std::string& reason_string) :
user_agent(user_agent),
core_protocol_version(core_protocol_version),
remote_endpoint(remote_endpoint),
reason_string(reason_string),
reason_code(reason_code)
{}
};
struct address_request_message
{
static const core_message_type_enum type;
address_request_message() {}
};
enum class peer_connection_direction { unknown, inbound, outbound };
enum class firewalled_state { unknown, firewalled, not_firewalled };
struct address_info
{
fc::ip::endpoint remote_endpoint;
fc::time_point_sec last_seen_time;
fc::microseconds latency;
node_id_t node_id;
fc::enum_type<uint8_t, peer_connection_direction> direction;
fc::enum_type<uint8_t, firewalled_state> firewalled;
address_info() {}
address_info(const fc::ip::endpoint& remote_endpoint,
const fc::time_point_sec last_seen_time,
const fc::microseconds latency,
const node_id_t& node_id,
peer_connection_direction direction,
firewalled_state firewalled) :
remote_endpoint(remote_endpoint),
last_seen_time(last_seen_time),
latency(latency),
node_id(node_id),
direction(direction),
firewalled(firewalled)
{}
};
struct address_message
{
static const core_message_type_enum type;
std::vector<address_info> addresses;
};
struct closing_connection_message
{
static const core_message_type_enum type;
std::string reason_for_closing;
bool closing_due_to_error;
fc::oexception error;
closing_connection_message() : closing_due_to_error(false) {}
closing_connection_message(const std::string& reason_for_closing,
bool closing_due_to_error = false,
const fc::oexception& error = fc::oexception()) :
reason_for_closing(reason_for_closing),
closing_due_to_error(closing_due_to_error),
error(error)
{}
};
struct current_time_request_message
{
static const core_message_type_enum type;
fc::time_point request_sent_time;
current_time_request_message(){}
current_time_request_message(const fc::time_point request_sent_time) :
request_sent_time(request_sent_time)
{}
};
struct current_time_reply_message
{
static const core_message_type_enum type;
fc::time_point request_sent_time;
fc::time_point request_received_time;
fc::time_point reply_transmitted_time;
current_time_reply_message(){}
current_time_reply_message(const fc::time_point request_sent_time,
const fc::time_point request_received_time,
const fc::time_point reply_transmitted_time = fc::time_point()) :
request_sent_time(request_sent_time),
request_received_time(request_received_time),
reply_transmitted_time(reply_transmitted_time)
{}
};
struct check_firewall_message
{
static const core_message_type_enum type;
node_id_t node_id;
fc::ip::endpoint endpoint_to_check;
};
enum class firewall_check_result
{
unable_to_check,
unable_to_connect,
connection_successful
};
struct check_firewall_reply_message
{
static const core_message_type_enum type;
node_id_t node_id;
fc::ip::endpoint endpoint_checked;
fc::enum_type<uint8_t, firewall_check_result> result;
};
struct get_current_connections_request_message
{
static const core_message_type_enum type;
};
struct current_connection_data
{
uint32_t connection_duration; // in seconds
fc::ip::endpoint remote_endpoint;
node_id_t node_id;
fc::microseconds clock_offset;
fc::microseconds round_trip_delay;
fc::enum_type<uint8_t, peer_connection_direction> connection_direction;
fc::enum_type<uint8_t, firewalled_state> firewalled;
fc::variant_object user_data;
};
struct get_current_connections_reply_message
{
static const core_message_type_enum type;
uint32_t upload_rate_one_minute;
uint32_t download_rate_one_minute;
uint32_t upload_rate_fifteen_minutes;
uint32_t download_rate_fifteen_minutes;
uint32_t upload_rate_one_hour;
uint32_t download_rate_one_hour;
std::vector<current_connection_data> current_connections;
};
} } // eos::net
FC_REFLECT_ENUM( eos::net::core_message_type_enum,
(trx_message_type)
(block_message_type)
(core_message_type_first)
(item_ids_inventory_message_type)
(blockchain_item_ids_inventory_message_type)
(fetch_blockchain_item_ids_message_type)
(fetch_items_message_type)
(item_not_available_message_type)
(hello_message_type)
(connection_accepted_message_type)
(connection_rejected_message_type)
(address_request_message_type)
(address_message_type)
(closing_connection_message_type)
(current_time_request_message_type)
(current_time_reply_message_type)
(check_firewall_message_type)
(check_firewall_reply_message_type)
(get_current_connections_request_message_type)
(get_current_connections_reply_message_type)
(core_message_type_last) )
FC_REFLECT( eos::net::trx_message, (trx) )
FC_REFLECT( eos::net::block_message, (block)(block_id) )
FC_REFLECT( eos::net::item_id, (item_type)
(item_hash) )
FC_REFLECT( eos::net::item_ids_inventory_message, (item_type)
(item_hashes_available) )
FC_REFLECT( eos::net::blockchain_item_ids_inventory_message, (total_remaining_item_count)
(item_type)
(item_hashes_available) )
FC_REFLECT( eos::net::fetch_blockchain_item_ids_message, (item_type)
(blockchain_synopsis) )
FC_REFLECT( eos::net::fetch_items_message, (item_type)
(items_to_fetch) )
FC_REFLECT( eos::net::item_not_available_message, (requested_item) )
FC_REFLECT( eos::net::hello_message, (user_agent)
(core_protocol_version)
(inbound_address)
(inbound_port)
(outbound_port)
(node_public_key)
(signed_shared_secret)
(chain_id)
(user_data) )
FC_REFLECT_EMPTY( eos::net::connection_accepted_message )
FC_REFLECT_ENUM(eos::net::rejection_reason_code, (unspecified)
(different_chain)
(already_connected)
(connected_to_self)
(not_accepting_connections)
(blocked)
(invalid_hello_message)
(client_too_old))
FC_REFLECT( eos::net::connection_rejected_message, (user_agent)
(core_protocol_version)
(remote_endpoint)
(reason_code)
(reason_string))
FC_REFLECT_EMPTY( eos::net::address_request_message )
FC_REFLECT( eos::net::address_info, (remote_endpoint)
(last_seen_time)
(latency)
(node_id)
(direction)
(firewalled) )
FC_REFLECT( eos::net::address_message, (addresses) )
FC_REFLECT( eos::net::closing_connection_message, (reason_for_closing)
(closing_due_to_error)
(error) )
FC_REFLECT_ENUM(eos::net::peer_connection_direction, (unknown)
(inbound)
(outbound))
FC_REFLECT_ENUM(eos::net::firewalled_state, (unknown)
(firewalled)
(not_firewalled))
FC_REFLECT(eos::net::current_time_request_message, (request_sent_time))
FC_REFLECT(eos::net::current_time_reply_message, (request_sent_time)
(request_received_time)
(reply_transmitted_time))
FC_REFLECT_ENUM(eos::net::firewall_check_result, (unable_to_check)
(unable_to_connect)
(connection_successful))
FC_REFLECT(eos::net::check_firewall_message, (node_id)(endpoint_to_check))
FC_REFLECT(eos::net::check_firewall_reply_message, (node_id)(endpoint_checked)(result))
FC_REFLECT_EMPTY(eos::net::get_current_connections_request_message)
FC_REFLECT(eos::net::current_connection_data, (connection_duration)
(remote_endpoint)
(node_id)
(clock_offset)
(round_trip_delay)
(connection_direction)
(firewalled)
(user_data))
FC_REFLECT(eos::net::get_current_connections_reply_message, (upload_rate_one_minute)
(download_rate_one_minute)
(upload_rate_fifteen_minutes)
(download_rate_fifteen_minutes)
(upload_rate_one_hour)
(download_rate_one_hour)
(current_connections))
#include <unordered_map>
#include <fc/crypto/city.hpp>
#include <fc/crypto/sha224.hpp>
namespace std
{
template<>
struct hash<eos::net::item_id>
{
size_t operator()(const eos::net::item_id& item_to_hash) const
{
return fc::city_hash_size_t((char*)&item_to_hash, sizeof(item_to_hash));
}
};
}
/*
* Copyright (c) 2017, Respective Authors.
*
* The MIT License
*
* 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.
*/
#pragma once
#include <fc/exception/exception.hpp>
namespace eos { namespace net {
// registered in node.cpp
FC_DECLARE_EXCEPTION( net_exception, 90000, "P2P Networking Exception" );
FC_DECLARE_DERIVED_EXCEPTION( send_queue_overflow, eos::net::net_exception, 90001, "send queue for this peer exceeded maximum size" );
FC_DECLARE_DERIVED_EXCEPTION( insufficient_relay_fee, eos::net::net_exception, 90002, "insufficient relay fee" );
FC_DECLARE_DERIVED_EXCEPTION( already_connected_to_requested_peer, eos::net::net_exception, 90003, "already connected to requested peer" );
FC_DECLARE_DERIVED_EXCEPTION( block_older_than_undo_history, eos::net::net_exception, 90004, "block is older than our undo history allows us to process" );
FC_DECLARE_DERIVED_EXCEPTION( peer_is_on_an_unreachable_fork, eos::net::net_exception, 90005, "peer is on another fork" );
FC_DECLARE_DERIVED_EXCEPTION( unlinkable_block_exception, eos::net::net_exception, 90006, "unlinkable block" )
} }
/*
* Copyright (c) 2017, Respective Authors.
*
* The MIT License
*
* 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.
*/
#pragma once
#include <fc/array.hpp>
#include <fc/io/varint.hpp>
#include <fc/network/ip.hpp>
#include <fc/io/raw.hpp>
#include <fc/crypto/sha512.hpp>
#include <fc/crypto/sha256.hpp>
#include <fc/reflect/variant.hpp>
namespace eos { namespace net {
/**
* Defines an 8 byte header that is always present because the minimum encrypted packet
* size is 8 bytes (blowfish). The maximum message size is defined in config.hpp. The channel,
* and message type is also included because almost every channel will have a message type
* field and we might as well include it in the 8 byte header to save space.
*/
struct message_header
{
uint32_t size; // number of bytes in message, capped at MAX_MESSAGE_SIZE
uint32_t msg_type; // every channel gets a 16 bit message type specifier
};
typedef fc::sha256 message_hash_type;
/**
* Abstracts the process of packing/unpacking a message for a
* particular channel.
*/
struct message : public message_header
{
std::vector<char> data;
message(){}
message( message&& m )
:message_header(m),data( std::move(m.data) ){}
message( const message& m )
:message_header(m),data( m.data ){}
/**
* Assumes that T::type specifies the message type
*/
template<typename T>
message( const T& m )
{
msg_type = T::type;
data = fc::raw::pack(m);
size = (uint32_t)data.size();
}
fc::sha256 id()const
{
return fc::sha256::hash( data.data(), (uint32_t)data.size() );
}
/**
* Automatically checks the type and deserializes T in the
* opposite process from the constructor.
*/
template<typename T>
T as()const
{
try {
FC_ASSERT( msg_type == T::type );
T tmp;
if( data.size() )
{
fc::datastream<const char*> ds( data.data(), data.size() );
fc::raw::unpack( ds, tmp );
}
else
{
// just to make sure that tmp shouldn't have any data
fc::datastream<const char*> ds( nullptr, 0 );
fc::raw::unpack( ds, tmp );
}
return tmp;
} FC_RETHROW_EXCEPTIONS( warn,
"error unpacking network message as a '${type}' ${x} !=? ${msg_type}",
("type", fc::get_typename<T>::name() )
("x", T::type)
("msg_type", msg_type)
);
}
};
} } // eos::net
FC_REFLECT( eos::net::message_header, (size)(msg_type) )
FC_REFLECT_DERIVED( eos::net::message, (eos::net::message_header), (data) )
/*
* Copyright (c) 2017, Respective Authors.
*
* The MIT License
*
* 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.
*/
#pragma once
#include <fc/network/tcp_socket.hpp>
#include <eos/net/message.hpp>
namespace eos { namespace net {
namespace detail { class message_oriented_connection_impl; }
class message_oriented_connection;
/** receives incoming messages from a message_oriented_connection object */
class message_oriented_connection_delegate
{
public:
virtual void on_message(message_oriented_connection* originating_connection, const message& received_message) = 0;
virtual void on_connection_closed(message_oriented_connection* originating_connection) = 0;
};
/** uses a secure socket to create a connection that reads and writes a stream of `fc::net::message` objects */
class message_oriented_connection
{
public:
message_oriented_connection(message_oriented_connection_delegate* delegate = nullptr);
~message_oriented_connection();
fc::tcp_socket& get_socket();
void accept();
void bind(const fc::ip::endpoint& local_endpoint);
void connect_to(const fc::ip::endpoint& remote_endpoint);
void send_message(const message& message_to_send);
void close_connection();
void destroy_connection();
uint64_t get_total_bytes_sent() const;
uint64_t get_total_bytes_received() const;
fc::time_point get_last_message_sent_time() const;
fc::time_point get_last_message_received_time() const;
fc::time_point get_connection_time() const;
fc::sha512 get_shared_secret() const;
private:
std::unique_ptr<detail::message_oriented_connection_impl> my;
};
typedef std::shared_ptr<message_oriented_connection> message_oriented_connection_ptr;
} } // eos::net
/*
* Copyright (c) 2017, Respective Authors.
*
* The MIT License
*
* 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.
*/
#pragma once
#include <eos/net/core_messages.hpp>
#include <eos/net/message.hpp>
#include <eos/net/peer_database.hpp>
#include <eos/chain/types.hpp>
#include <list>
namespace eos { namespace net {
using fc::variant_object;
using eos::chain::chain_id_type;
namespace detail
{
class node_impl;
struct node_impl_deleter
{
void operator()(node_impl*);
};
}
// during network development, we need to track message propagation across the network
// using a structure like this:
struct message_propagation_data
{
fc::time_point received_time;
fc::time_point validated_time;
node_id_t originating_peer;
};
/**
* @class node_delegate
* @brief used by node reports status to client or fetch data from client
*/
class node_delegate
{
public:
virtual ~node_delegate(){}
/**
* If delegate has the item, the network has no need to fetch it.
*/
virtual bool has_item( const net::item_id& id ) = 0;
/**
* @brief Called when a new block comes in from the network
*
* @param sync_mode true if the message was fetched through the sync process, false during normal operation
* @returns true if this message caused the blockchain to switch forks, false if it did not
*
* @throws exception if error validating the item, otherwise the item is
* safe to broadcast on.
*/
virtual bool handle_block( const eos::net::block_message& blk_msg, bool sync_mode,
std::vector<fc::sha256>& contained_transaction_message_ids ) = 0;
/**
* @brief Called when a new transaction comes in from the network
*
* @throws exception if error validating the item, otherwise the item is
* safe to broadcast on.
*/
virtual void handle_transaction( const eos::net::trx_message& trx_msg ) = 0;
/**
* @brief Called when a new message comes in from the network other than a
* block or a transaction. Currently there are no other possible
* messages, so this should never be called.
*
* @throws exception if error validating the item, otherwise the item is
* safe to broadcast on.
*/
virtual void handle_message( const message& message_to_process ) = 0;
/**
* Assuming all data elements are ordered in some way, this method should
* return up to limit ids that occur *after* from_id.
* On return, remaining_item_count will be set to the number of items
* in our blockchain after the last item returned in the result,
* or 0 if the result contains the last item in the blockchain
*/
virtual std::vector<item_hash_t> get_block_ids(const std::vector<item_hash_t>& blockchain_synopsis,
uint32_t& remaining_item_count,
uint32_t limit = 2000) = 0;
/**
* Given the hash of the requested data, fetch the body.
*/
virtual message get_item( const item_id& id ) = 0;
virtual chain_id_type get_chain_id()const = 0;
/**
* Returns a synopsis of the blockchain used for syncing.
* This consists of a list of selected item hashes from our current preferred
* blockchain, exponentially falling off into the past. Horrible explanation.
*
* If the blockchain is empty, it will return the empty list.
* If the blockchain has one block, it will return a list containing just that block.
* If it contains more than one block:
* the first element in the list will be the hash of the highest numbered block that
* we cannot undo
* the second element will be the hash of an item at the half way point in the undoable
* segment of the blockchain
* the third will be ~3/4 of the way through the undoable segment of the block chain
* the fourth will be at ~7/8...
* &c.
* the last item in the list will be the hash of the most recent block on our preferred chain
*/
virtual std::vector<item_hash_t> get_blockchain_synopsis(const item_hash_t& reference_point,
uint32_t number_of_blocks_after_reference_point) = 0;
/**
* Call this after the call to handle_message succeeds.
*
* @param item_type the type of the item we're synchronizing, will be the same as item passed to the sync_from() call
* @param item_count the number of items known to the node that haven't been sent to handle_item() yet.
* After `item_count` more calls to handle_item(), the node will be in sync
*/
virtual void sync_status( uint32_t item_type, uint32_t item_count ) = 0;
/**
* Call any time the number of connected peers changes.
*/
virtual void connection_count_changed( uint32_t c ) = 0;
virtual uint32_t get_block_number(const item_hash_t& block_id) = 0;
/**
* Returns the time a block was produced (if block_id = 0, returns genesis time).
* If we don't know about the block, returns time_point_sec::min()
*/
virtual fc::time_point_sec get_block_time(const item_hash_t& block_id) = 0;
virtual item_hash_t get_head_block_id() const = 0;
virtual uint32_t estimate_last_known_fork_from_git_revision_timestamp(uint32_t unix_timestamp) const = 0;
virtual void error_encountered(const std::string& message, const fc::oexception& error) = 0;
virtual uint8_t get_current_block_interval_in_seconds() const = 0;
};
/**
* Information about connected peers that the client may want to make
* available to the user.
*/
struct peer_status
{
uint32_t version;
fc::ip::endpoint host;
/** info contains the fields required by bitcoin-rpc's getpeerinfo call, we will likely
extend it with our own fields. */
fc::variant_object info;
};
/**
* @class node
* @brief provides application independent P2P broadcast and data synchronization
*
* Unanswered questions:
* when does the node start establishing network connections and accepting peers?
* we don't have enough info to start synchronizing until sync_from() is called,
* would we have any reason to connect before that?
*/
class node : public std::enable_shared_from_this<node>
{
public:
node(const std::string& user_agent);
~node();
void close();
void set_node_delegate( node_delegate* del );
void load_configuration( const fc::path& configuration_directory );
virtual void listen_to_p2p_network();
virtual void connect_to_p2p_network();
/**
* Add endpoint to internal level_map database of potential nodes
* to attempt to connect to. This database is consulted any time
* the number connected peers falls below the target.
*/
void add_node( const fc::ip::endpoint& ep );
/**
* Attempt to connect to the specified endpoint immediately.
*/
virtual void connect_to_endpoint( const fc::ip::endpoint& ep );
/**
* Specifies the network interface and port upon which incoming
* connections should be accepted.
*/
void listen_on_endpoint( const fc::ip::endpoint& ep, bool wait_if_not_available );
/**
* Call with true to enable listening for incoming connections
*/
void accept_incoming_connections(bool accept);
/**
* Specifies the port upon which incoming connections should be accepted.
* @param port the port to listen on
* @param wait_if_not_available if true and the port is not available, enter a
* sleep and retry loop to wait for it to become
* available. If false and the port is not available,
* just choose a random available port
*/
void listen_on_port(uint16_t port, bool wait_if_not_available);
/**
* Returns the endpoint the node is listening on. This is usually the same
* as the value previously passed in to listen_on_endpoint, unless we
* were unable to bind to that port.
*/
virtual fc::ip::endpoint get_actual_listening_endpoint() const;
/**
* @return a list of peers that are currently connected.
*/
std::vector<peer_status> get_connected_peers() const;
/** return the number of peers we're actively connected to */
virtual uint32_t get_connection_count() const;
/**
* Add message to outgoing inventory list, notify peers that
* I have a message ready.
*/
virtual void broadcast( const message& item_to_broadcast );
virtual void broadcast_transaction( const SignedTransaction& trx )
{
broadcast( trx_message(trx) );
}
/**
* Node starts the process of fetching all items after item_id of the
* given item_type. During this process messages are not broadcast.
*/
virtual void sync_from(const item_id& current_head_block, const std::vector<uint32_t>& hard_fork_block_numbers);
bool is_connected() const;
void set_advanced_node_parameters(const fc::variant_object& params);
fc::variant_object get_advanced_node_parameters();
message_propagation_data get_transaction_propagation_data(const eos::chain::transaction_id_type& transaction_id);
message_propagation_data get_block_propagation_data(const eos::chain::block_id_type& block_id);
node_id_t get_node_id() const;
void set_allowed_peers(const std::vector<node_id_t>& allowed_peers);
/**
* Instructs the node to forget everything in its peer database, mostly for debugging
* problems where nodes are failing to connect to the network
*/
void clear_peer_database();
void set_total_bandwidth_limit(uint32_t upload_bytes_per_second, uint32_t download_bytes_per_second);
fc::variant_object network_get_info() const;
fc::variant_object network_get_usage_stats() const;
std::vector<potential_peer_record> get_potential_peers() const;
void disable_peer_advertising();
fc::variant_object get_call_statistics() const;
private:
std::unique_ptr<detail::node_impl, detail::node_impl_deleter> my;
};
class simulated_network : public node
{
public:
~simulated_network();
simulated_network(const std::string& user_agent) : node(user_agent) {}
void listen_to_p2p_network() override {}
void connect_to_p2p_network() override {}
void connect_to_endpoint(const fc::ip::endpoint& ep) override {}
fc::ip::endpoint get_actual_listening_endpoint() const override { return fc::ip::endpoint(); }
void sync_from(const item_id& current_head_block, const std::vector<uint32_t>& hard_fork_block_numbers) override {}
void broadcast(const message& item_to_broadcast) override;
void add_node_delegate(node_delegate* node_delegate_to_add);
virtual uint32_t get_connection_count() const override { return 8; }
private:
struct node_info;
void message_sender(node_info* destination_node);
std::list<node_info*> network_nodes;
};
typedef std::shared_ptr<node> node_ptr;
typedef std::shared_ptr<simulated_network> simulated_network_ptr;
} } // eos::net
FC_REFLECT(eos::net::message_propagation_data, (received_time)(validated_time)(originating_peer));
FC_REFLECT( eos::net::peer_status, (version)(host)(info) );
/*
* Copyright (c) 2017, Respective Authors.
*
* The MIT License
*
* 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.
*/
#pragma once
#include <eos/net/node.hpp>
#include <eos/net/peer_database.hpp>
#include <eos/net/message_oriented_connection.hpp>
#include <eos/net/stcp_socket.hpp>
#include <eos/net/config.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/mem_fun.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/random_access_index.hpp>
#include <boost/multi_index/tag.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <queue>
#include <boost/container/deque.hpp>
#include <fc/thread/future.hpp>
namespace eos { namespace net
{
struct firewall_check_state_data
{
node_id_t expected_node_id;
fc::ip::endpoint endpoint_to_test;
// if we're coordinating a firewall check for another node, these are the helper
// nodes we've already had do the test (if this structure is still relevant, that
// that means they have all had indeterminate results
std::set<node_id_t> nodes_already_tested;
// If we're a just a helper node, this is the node we report back to
// when we have a result
node_id_t requesting_peer;
};
class peer_connection;
class peer_connection_delegate
{
public:
virtual void on_message(peer_connection* originating_peer,
const message& received_message) = 0;
virtual void on_connection_closed(peer_connection* originating_peer) = 0;
virtual message get_message_for_item(const item_id& item) = 0;
};
class peer_connection;
typedef std::shared_ptr<peer_connection> peer_connection_ptr;
class peer_connection : public message_oriented_connection_delegate,
public std::enable_shared_from_this<peer_connection>
{
public:
enum class our_connection_state
{
disconnected,
just_connected, // if in this state, we have sent a hello_message
connection_accepted, // remote side has sent us a connection_accepted, we're operating normally with them
connection_rejected // remote side has sent us a connection_rejected, we may be exchanging address with them or may just be waiting for them to close
};
enum class their_connection_state
{
disconnected,
just_connected, // we have not yet received a hello_message
connection_accepted, // we have sent them a connection_accepted
connection_rejected // we have sent them a connection_rejected
};
enum class connection_negotiation_status
{
disconnected,
connecting,
connected,
accepting,
accepted,
hello_sent,
peer_connection_accepted,
peer_connection_rejected,
negotiation_complete,
closing,
closed
};
private:
peer_connection_delegate* _node;
fc::optional<fc::ip::endpoint> _remote_endpoint;
message_oriented_connection _message_connection;
/* a base class for messages on the queue, to hide the fact that some
* messages are complete messages and some are only hashes of messages.
*/
struct queued_message
{
fc::time_point enqueue_time;
fc::time_point transmission_start_time;
fc::time_point transmission_finish_time;
queued_message(fc::time_point enqueue_time = fc::time_point::now()) :
enqueue_time(enqueue_time)
{}
virtual message get_message(peer_connection_delegate* node) = 0;
/** returns roughly the number of bytes of memory the message is consuming while
* it is sitting on the queue
*/
virtual size_t get_size_in_queue() = 0;
virtual ~queued_message() {}
};
/* when you queue up a 'real_queued_message', a full copy of the message is
* stored on the heap until it is sent
*/
struct real_queued_message : queued_message
{
message message_to_send;
size_t message_send_time_field_offset;
real_queued_message(message message_to_send,
size_t message_send_time_field_offset = (size_t)-1) :
message_to_send(std::move(message_to_send)),
message_send_time_field_offset(message_send_time_field_offset)
{}
message get_message(peer_connection_delegate* node) override;
size_t get_size_in_queue() override;
};
/* when you queue up a 'virtual_queued_message', we just queue up the hash of the
* item we want to send. When it reaches the top of the queue, we make a callback
* to the node to generate the message.
*/
struct virtual_queued_message : queued_message
{
item_id item_to_send;
virtual_queued_message(item_id item_to_send) :
item_to_send(std::move(item_to_send))
{}
message get_message(peer_connection_delegate* node) override;
size_t get_size_in_queue() override;
};
size_t _total_queued_messages_size;
std::queue<std::unique_ptr<queued_message>, std::list<std::unique_ptr<queued_message> > > _queued_messages;
fc::future<void> _send_queued_messages_done;
public:
fc::time_point connection_initiation_time;
fc::time_point connection_closed_time;
fc::time_point connection_terminated_time;
peer_connection_direction direction;
//connection_state state;
firewalled_state is_firewalled;
fc::microseconds clock_offset;
fc::microseconds round_trip_delay;
our_connection_state our_state;
bool they_have_requested_close;
their_connection_state their_state;
bool we_have_requested_close;
connection_negotiation_status negotiation_status;
fc::oexception connection_closed_error;
fc::time_point get_connection_time()const { return _message_connection.get_connection_time(); }
fc::time_point get_connection_terminated_time()const { return connection_terminated_time; }
/// data about the peer node
/// @{
/** node_public_key from the hello message, zero-initialized before we get the hello */
node_id_t node_public_key;
/** the unique identifier we'll use to refer to the node with. zero-initialized before
* we receive the hello message, at which time it will be filled with either the "node_id"
* from the user_data field of the hello, or if none is present it will be filled with a
* copy of node_public_key */
node_id_t node_id;
uint32_t core_protocol_version;
std::string user_agent;
fc::optional<std::string> eos_git_revision_sha;
fc::optional<fc::time_point_sec> eos_git_revision_unix_timestamp;
fc::optional<std::string> fc_git_revision_sha;
fc::optional<fc::time_point_sec> fc_git_revision_unix_timestamp;
fc::optional<std::string> platform;
fc::optional<uint32_t> bitness;
// for inbound connections, these fields record what the peer sent us in
// its hello message. For outbound, they record what we sent the peer
// in our hello message
fc::ip::address inbound_address;
uint16_t inbound_port;
uint16_t outbound_port;
/// @}
typedef std::unordered_map<item_id, fc::time_point> item_to_time_map_type;
/// blockchain synchronization state data
/// @{
boost::container::deque<item_hash_t> ids_of_items_to_get; /// id of items in the blockchain that this peer has told us about
std::set<item_hash_t> ids_of_items_being_processed; /// list of all items this peer has offered use that we've already handed to the client but the client hasn't finished processing
uint32_t number_of_unfetched_item_ids; /// number of items in the blockchain that follow ids_of_items_to_get but the peer hasn't yet told us their ids
bool peer_needs_sync_items_from_us;
bool we_need_sync_items_from_peer;
fc::optional<boost::tuple<std::vector<item_hash_t>, fc::time_point> > item_ids_requested_from_peer; /// we check this to detect a timed-out request and in busy()
item_to_time_map_type sync_items_requested_from_peer; /// ids of blocks we've requested from this peer during sync. fetch from another peer if this peer disconnects
item_hash_t last_block_delegate_has_seen; /// the hash of the last block this peer has told us about that the peer knows
fc::time_point_sec last_block_time_delegate_has_seen;
bool inhibit_fetching_sync_blocks;
/// @}
/// non-synchronization state data
/// @{
struct timestamped_item_id
{
item_id item;
fc::time_point_sec timestamp;
timestamped_item_id(const item_id& item, const fc::time_point_sec timestamp) :
item(item),
timestamp(timestamp)
{}
};
struct timestamp_index{};
typedef boost::multi_index_container<timestamped_item_id,
boost::multi_index::indexed_by<boost::multi_index::hashed_unique<boost::multi_index::member<timestamped_item_id, item_id, &timestamped_item_id::item>,
std::hash<item_id> >,
boost::multi_index::ordered_non_unique<boost::multi_index::tag<timestamp_index>,
boost::multi_index::member<timestamped_item_id, fc::time_point_sec, &timestamped_item_id::timestamp> > > > timestamped_items_set_type;
timestamped_items_set_type inventory_peer_advertised_to_us;
timestamped_items_set_type inventory_advertised_to_peer;
item_to_time_map_type items_requested_from_peer; /// items we've requested from this peer during normal operation. fetch from another peer if this peer disconnects
/// @}
// if they're flooding us with transactions, we set this to avoid fetching for a few seconds to let the
// blockchain catch up
fc::time_point transaction_fetching_inhibited_until;
uint32_t last_known_fork_block_number;
fc::future<void> accept_or_connect_task_done;
firewall_check_state_data *firewall_check_state;
#ifndef NDEBUG
private:
fc::thread* _thread;
unsigned _send_message_queue_tasks_running; // temporary debugging
#endif
private:
peer_connection(peer_connection_delegate* delegate);
void destroy();
public:
static peer_connection_ptr make_shared(peer_connection_delegate* delegate); // use this instead of the constructor
virtual ~peer_connection();
fc::tcp_socket& get_socket();
void accept_connection();
void connect_to(const fc::ip::endpoint& remote_endpoint, fc::optional<fc::ip::endpoint> local_endpoint = fc::optional<fc::ip::endpoint>());
void on_message(message_oriented_connection* originating_connection, const message& received_message) override;
void on_connection_closed(message_oriented_connection* originating_connection) override;
void send_queueable_message(std::unique_ptr<queued_message>&& message_to_send);
void send_message(const message& message_to_send, size_t message_send_time_field_offset = (size_t)-1);
void send_item(const item_id& item_to_send);
void close_connection();
void destroy_connection();
uint64_t get_total_bytes_sent() const;
uint64_t get_total_bytes_received() const;
fc::time_point get_last_message_sent_time() const;
fc::time_point get_last_message_received_time() const;
fc::optional<fc::ip::endpoint> get_remote_endpoint();
fc::ip::endpoint get_local_endpoint();
void set_remote_endpoint(fc::optional<fc::ip::endpoint> new_remote_endpoint);
bool busy();
bool idle();
bool is_transaction_fetching_inhibited() const;
fc::sha512 get_shared_secret() const;
void clear_old_inventory();
bool is_inventory_advertised_to_us_list_full_for_transactions() const;
bool is_inventory_advertised_to_us_list_full() const;
bool performing_firewall_check() const;
fc::optional<fc::ip::endpoint> get_endpoint_for_connecting() const;
private:
void send_queued_messages_task();
void accept_connection_task();
void connect_to_task(const fc::ip::endpoint& remote_endpoint);
};
typedef std::shared_ptr<peer_connection> peer_connection_ptr;
} } // end namespace eos::net
// not sent over the wire, just reflected for logging
FC_REFLECT_ENUM(eos::net::peer_connection::our_connection_state, (disconnected)
(just_connected)
(connection_accepted)
(connection_rejected))
FC_REFLECT_ENUM(eos::net::peer_connection::their_connection_state, (disconnected)
(just_connected)
(connection_accepted)
(connection_rejected))
FC_REFLECT_ENUM(eos::net::peer_connection::connection_negotiation_status, (disconnected)
(connecting)
(connected)
(accepting)
(accepted)
(hello_sent)
(peer_connection_accepted)
(peer_connection_rejected)
(negotiation_complete)
(closing)
(closed) )
FC_REFLECT( eos::net::peer_connection::timestamped_item_id, (item)(timestamp));
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
......@@ -6,3 +6,5 @@ add_subdirectory(chain_api_plugin)
add_subdirectory(producer_plugin)
add_subdirectory(account_history_plugin)
add_subdirectory(account_history_api_plugin)
add_subdirectory(wallet_plugin)
add_subdirectory(wallet_api_plugin)
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册