Você está na página 1de 63

SmartDec

Beam Blockchain Security


Analysis

This report is private.

Published: January 22, 2019.


Abstract . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
Disclaimer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
General recommendations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
Procedure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
Report on the first stage of the Beam project research . . . . . . . . . . . . . . . . . . . . 8
General characteristics of the source code . . . . . . . . . . . . . . . . . . . . . . . . 8
Source code compilation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Source code review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Unused code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
No IPv6 support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Custom implementation of long arithmetic . . . . . . . . . . . . . . . . . . . . . . 10
Unsafe use of memset / memcpy . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
Unjustified templates with variable number of arguments . . . . . . . . . . . . . . 11
Custom definition of procedural macros . . . . . . . . . . . . . . . . . . . . . . . 11
Unsafe assignment operation definition . . . . . . . . . . . . . . . . . . . . . . . 12
Unsigned overflow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
Unsafe pointer manipulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
Report on the second stage of the Beam project research . . . . . . . . . . . . . . . . . . 14
Basics of the Beam blockchain structure . . . . . . . . . . . . . . . . . . . . . . . . . . 14
Cryptographic hash-function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
Asymmetric cryptography . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
Consensus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
Transactions model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
Information hiding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
Beam realization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Report on the third stage of the Beam project research . . . . . . . . . . . . . . . . . . . 20
Node messaging protocol . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
0x00, Config . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
0x01, Bye . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
0x02, Ping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
0x03, Pong . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
0x04, SChannelInitiate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
0x05, SChannelReady . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
0x06, Authentication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
0x07, PeerInfoSelf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

Blockchain Security Analysis


SmartDec https://smartdec.net
1
0x08, PeerInfo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
0x09, GetExternalAddr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
0x0a, ExternalAddr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
0x0b, GetTime . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
0x0c, Time . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
0x0d, DataMissing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
0x0e, Boolean . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
0x10, NewTip . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
0x11, GetHdr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
0x12, Hdr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
0x13, GetHdrPack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
0x14, HdrPack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
0x15, GetBody . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
0x16, Body . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
0x17, GetProofState . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
0x18, ProofState . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
0x19, GetProofKernel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
0x1a, ProofKernel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
0x1b, GetProofUtxo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
0x1c, ProofUtxo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
0x1d, GetProofChainWork . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
0x1e, ProofChainWork . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
0x20, MacroblockGet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
0x21, Macroblock . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
0x22, GetCommonState . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
0x23, ProofCommonState . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
0x24, GetProofKernel2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
0x25, ProofKernel2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
0x28, GetMined . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
0x29, Mined . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
0x2a, Recover . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
0x2b, Recovered . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
0x30, NewTransaction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
0x31, HaveTransaction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
0x32, GetTransaction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
0x38, BbsMsg . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
0x39, BbsHaveMsg . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
0x3a, BbsGetMsg . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
0x3b, BbsSubscribe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34

Blockchain Security Analysis


SmartDec https://smartdec.net
2
0x3c, BbsPickChannel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
0x3d, BbsPickChannelRes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
Class generation for messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
Review of the in-out subsystem (utility/io) . . . . . . . . . . . . . . . . . . . . . . . . 37
Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
class Reactor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
reactor.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
Reactor.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
class PendingWrites . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
class Address . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
address.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
buffer.h and buffer.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
class BufferChain . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
bufferchain.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
class Timer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
timer.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
class MemPool . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
mempool.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
coarsetimer.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
class MultipleTimers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
coarsetimer.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
class FragmentWriter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
fragment_writer.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
class TcpStream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
tcpstream.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
class SSLContext . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
sslio.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
class SSLIO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
sslio.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
class SslStream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
sslstream.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
class TcpServer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
tcpserver.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
class SslServer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
sslserver.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
Overview of the Node subsystem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
db.h / db.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
txpool.h / txpool.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
processor.h / processor.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46

Blockchain Security Analysis


SmartDec https://smartdec.net
3
Validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
NodeProcessor::HandleBlockElement(const Input& v, Height h,
const Height* pHMax, bool bFwd) . . . . . . . . . . . . . . . . . . 48
NodeProcessor::HandleBlockElement(const Output& v, Height h,
const Height* pHMax, bool bFwd) . . . . . . . . . . . . . . . . . . 48
NodeProcessor::OnStateInternal(const Block::SystemState::Full&
s, Block::SystemState::ID& id) . . . . . . . . . . . . . . . . . . 49
Node::Processor::ApproveState(id) . . . . . . . . . . . . . . . . . . . . 50
bool NodeProcessor::VerifyBlock(const Block::BodyBase& block,
TxBase::IReader&& r, const HeightRange& hr) . . . . . . . . 50
Node::Processor::VerifyBlock(const Block::BodyBase&
block, TxBase::IReader&& r, const HeightRange& hr) . . . 50
Verification in Node::Processor::Verifier . . . . . . . . . . . . . . . . . . 50
NodeProcessor::ImportMacroBlockInternal(
Block::BodyBase::IMacroReader& r) . . . . . . . . . . . . . . . . 51
NodeProcessor::ValidateTxWrtHeight(const Transaction& tx,
Height h) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
NodeProcessor::ValidateTxContext(const Transaction& tx) . . . 52
bool NodeProcessor::HandleBlock(const NodeDB::StateID& sid,
bool bFwd) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
Generation of a new block . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
size_t NodeProcessor::GenerateNewBlock(BlockContext&
bc, Block::Body& res, Height h) . . . . . . . . . . . . . . . . . . 54
bool NodeProcessor::GenerateNewBlock(BlockContext&
bc, Block::Body& res, bool bInitiallyEmpty) . . . . . . . . 56
Subsidy mechanism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
ToggleSubsidyOpened() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
Notes and recommendations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
Overview of the implementation of the Bulletproof algorithm in the BeamMV project . . . 59
Comparison of B. Bunz's work with the source code . . . . . . . . . . . . . . . . . . 59
Bulletproof interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61

Blockchain Security Analysis


SmartDec https://smartdec.net
4
Abstract
In this report, we consider the security of the Beam blockchain project. Our task is to find and
describe security issues in the project code.

Disclaimer
The audit does not give any warranties on the security of the code. One audit cannot be
considered enough. We always recommend proceeding with several independent audits and
a public bug bounty program to ensure the security of the project. Besides, security audit is
not an investment advice.

Summary
The audit indicated that the source code of the project is of high quality, which attests to high
proficiency and experience of the developers. The development team was very responsive to
requests and suggestions; the issues were resolved promptly. All the findings were discussed
with the team and either fixed or justified.

General recommendations
The project is still under development, so we recommend performing regular code audit to
ensure correctness and security of newly added code.

Additionally, we recommend creating source code documentation. In some cases the overall
structure and readability of the code can be improved by refactoring.

The text below is for technical use only; it details the statements made
in Summary and General recommendations.

Blockchain Security Analysis


SmartDec https://smartdec.net
5
Introduction
The objective of this document is to describe the procedure and results of the source code
audit of the Beam blockchain project. The audit is focused mainly on its security aspects.

The project employs the latest advances in cryptography to ensure integrity and confidentiality
of the data, contained within. Based on Pedersen commitment idea, MimbleWimble protocol is
used to hide payment amounts as well as senders and receivers identities as the blockchain
stores no wallet addresses. Besides, Beam declares built-in support for time-locked
transactions, escrow transactions, atomic swaps and some other features, that can only be
implemented in other blockchains on the smart-contract level. Additionally, Beam uses a
number of techniques for compact blockchain to achieve a very moderate blockchain size as
compared to other blockchain implementations.

The original plan of the audit was to perform a traditional application security audit manually
and using tools with subsequent code review for the blockchain protocol, data storage and
processing. Actually, several stages of manual code review are done; each audit stage uses
the current version of the source code from GIT. The source code is written using C++ as the
primary programming language with some supporting libs in plain C. The source code has
total 190407 LOC ANSI C that is 71.74% of all the code and 73849 LOC in C++ that is 27.82%
of all the code. Beam in-house code has approximately 40261 (91.59%) LOC written in C++
and 3693 (8.40%) LOC written in ANSI C.

The following libraries are used with the source code:


• Sqlite – embeddable sql database engine
• libuv – async input-output library (http://libuv.org/)
• json.hpp – JSON for Modern C++ (https://github.com/nlohmann/json)
• expected.hpp – an implementation of std::expected
(https://github.com/TartanLlama/expected)
• YAS – yet another serialization (https://github.com/niXman/yas)

In the first stage of the audit the project was compiled. There were some unaccounted
dependencies and hardware-specific options, all of them were revealed. Boost was not
checked by cmake; -mavx gcc compiler option was available for x86_64 only. Compiler
warnings had to be analysed and fixed:

warning: void* memcpy(void*, const void*, size_t) writing to


an object of non-trivially copyable type ‘class
ECC::Scalar::Native’; use copy-assignment or
copy-initialization instead [-Wclass-memaccess]
memcpy(&(out[pubKey].V), p+32, 32);

Blockchain Security Analysis


SmartDec https://smartdec.net
6
The source code review identified some typical programming issues such as bad
implementation of C++ assignment operator (lacking of self-assignment check), non-typical or
non-recommended macro usage, unnecessary low-level coding, and API misuse.

In the next stages analysis of the papers describing math foundations and algorithms was
performed. We searched and identified MimbleWimble and Bulletproof implementations.
Beam has its own Bulletproof implementation by the author (B. Bunz), which is available as a
fork (with a pull request) of libsecp256-zkp. During performing the Beam protocol
analysis we recovered the Beam message types and associated values, which were sent and
received by cross-referencing for each message type.

Procedure
The audit was performed in three stages, each of which was accompanied by checking the
latest version of the code. Repository link and exact commit hash are provided in the
corresponding section of the report.

Blockchain Security Analysis


SmartDec https://smartdec.net
7
Report on the first stage of the Beam
project research
The objective of the first stage was to get familiar with the project and undertake an initial
overview of the source code and architecture.

The source code description was generated automatically with the use of Doxygen tool. The
description contains the inheritance graph and cross-references and is convenient for the
source code exploration.

Further, the project source code was reviewed manually.

General characteristics of the source code


The code is written in C++11 with the use of boost library:
• boost::intrusive – intrusive containers
• boost::program_options
• boost::filesystem
• boost::iostreams
• boost::any
• boost::optional
• boost::msm
• boost::uuid
• boost::functional

It should be underlined that the yas library uses the header files of the boost library.

There is a code of the following third-party projects in the repository:


• BLAKE2 – the family of cryptographic hash-functions (https://blake2.net/) – the
modern generation of the hash-functions (analog to keccak/sha-3, but faster in
software implementation)
• libuv – async input-output library (http://libuv.org/)
• json.hpp – JSON for Modern C++ (https://github.com/nlohmann/json)
• expected.hpp – an implementation of std::expected
(https://github.com/TartanLlama/expected)
• YAS – yet another serialization (https://github.com/niXman/yas)

Utility directory of the project contains both the third-party code and the code of the project itself.

Recommendation: separate the third-party code from the project code by placing it, for
example, in the 3rdparty directory.

Blockchain Security Analysis


SmartDec https://smartdec.net
8
Source code compilation
The source code compilation was performed using Fedora Linux 28 operating system and gcc 8.1
compiler.

Compilation on the x86_64 architecture:


after installation of the cmake, qt5, qt5-devel packages, cmake ran successfully, but the
subsequent compilation finished with an error due to the absence of boost libraries in the
system. After the boost and boost-devel libraries installation the compilation was finished
successfully.

Recommendation: add the checking for the presence of boost library in the
CMakeLists.txt file.

Compilation on the aarch64 architecture:


The compilation was finished with an error as the -mavx option of the compiler is not supported.

Recommendation: use the -mavx option only for the x86_64 architecture.

After removal of the considered option from CMakeLists.txt, the compilation was finished
successfully.

The compiler returned warnings such as:

utility/logger_checkpoints.cpp: In destructor
‘beam::Checkpoint::~Checkpoint()’:
utility/logger_checkpoints.cpp:82:32:
warning: ‘bool std::uncaught_exception()’ is deprecated
[-Wdeprecated-declarations]
if (_maxItems != 0 && std::uncaught_exception()) {

Moreover, the compiler generated warnings about comparison of signed and unsigned
numbers in the file pow/impl/crypto/equihash_impl.cpp.

wallet/keystore.cpp: In function ‘void beam::{anonymous}::re


ad_keystore_file(beam::{anonymous}::KeyPairs&, const string&
, const PasswordHash&)’:
wallet/keystore.cpp:104:42: warning: ‘void* memcpy(void*, co
nst void*, size_t)’ writing to an object of non-trivially co
pyable type ‘class ECC::Scalar::Native’; use copy-assignment
or copy-initialization instead [-Wclass-memaccess]
memcpy(&(out[pubKey].V), p+32, 32);

Recommendation: eliminate compiler warnings.

Blockchain Security Analysis


SmartDec https://smartdec.net
9
Source code review
The first iteration of the code review showed a good quality of the code. However, it revealed
some problems.

The detected problems are listed below.

Unused code
The beam/bss directory contains an obsolete component that is no longer used.

No IPv6 support
There is a definition of the StreamId structure with an anonymous union in the
p2p/types.hheader file :

union {
uint64_t u64;
struct {
uint32_t ip;
uint16_t port;
uint16_t flags;
} fields;
};

The size of the ip field is 32 bits. Thus, only IPv4 is supported.

Recommendation: add the IPv6 support. This can be achieved by reserving 16 bytes for
the address. In the foreseeable future IPv4 will be definitely the major protocol version on
the Internet, but IPV6 support should not be discarded.

Custom implementation of long arithmetic


The uintBig.h file contains a simple implementation of the unlimited precision arithmetic.

Recommendation: use one of the existing implementations, e.g.


boost::multiprecision. The existing implementations are usually optimised for a
particular platform. Moreover, boost::multiprecision may provide optimized types for
128- and 256-bit arithmetic: boost::multiprecision::uint128_t,
boost::multiprecision::int256_t.

Blockchain Security Analysis


SmartDec https://smartdec.net
10
Unsafe use of memset / memcpy
The code uses the ZeroObject function, which resets an object by calling memset.
Moreover, memcpy is used in one place to copy objects.

Recommendation: do not reset and copy objects with the memset function. One of the
possible ways to re-initialize an object is given below:

class A
{
A() { ... }
void reset()
{
*this = A();
}
};

Unjustified templates with a variable number of arguments


In the config.cpp file, the array_values function is defined as a template with a variable
number of arguments, which are not actually needed.

Custom definition of procedural macros


Procedural macros in the Beam code are generally defined in a similar way:

#define PROC(x) { ... }

and used without terminating ’;’:

void func()
{
PROC(10)
}

It is a correct syntax, however, it does not correspond to usual practices of macros usage in
C/C++.

Blockchain Security Analysis


SmartDec https://smartdec.net
11
Recommendation: include macros in the do-while loops:

#define PROC(x) do { ... } while(0)

Syntactically, this macro acts like an operator requiring a semicolon after it, so the code
becomes more uniform:

void func()
{
PROC(10);
}

Unsafe assignment operation definition


As a rule, the assignment operation in C++ should avoid assignment of the object to
itself, as it may lead to the destruction of the object. However, in the Beam code it has not
been performed several times.

Recommendation: add the following checking for identity:

X & operator = (const X &arg)


{
if (this == &arg) return *this;
...
return *this;
}

Besides, the standard idiom of assignment operation realisation in C++ is copy-and-swap.


This allows removing duplication of copy constructors and assignment operations.
Moreover, it automatically supports rvalue assignments.

Unsigned overflow
block_crypt.cpp:

return (uint32_t)duration_cast<milliseconds>(system_clock::n
ow().time_since_epoch()).count();

Blockchain Security Analysis


SmartDec https://smartdec.net
12
Here Unix timestamp is converted into milliseconds. Before converting into the
uint32_t type the values have the form of 1538097338048. The conversion into the
uint32_t type loses the head part. Time in milliseconds overflows the uint32_t type
every 49 days.

Recommendation: use the uint64_t type.

Unsafe pointer manipulation


In the common.h file, the IMPLEMENT_GET_PARENT_OBJ macros returns a pointer to
this for the enclosing object with a pointer to this for the aggregated object
parent_class* p = (parent_class*) (((uint8_t*) this) + 1 -
(uint8_t*) (&((parent_class*) 1)->this_var));

We recommend using the standard offsetof macro


(https://en.cppreference.com/w/cpp/types/offsetof). Moreover, we recommend adding
static_assert checking is_standard_layout.

Blockchain Security Analysis


SmartDec https://smartdec.net
13
Report on the second stage of the Beam
project research
The objective of the second stage of the project analysis was to restore the architecture of the
Beam blockchain software implementation in order to analyze it for compliance with the best
industry practices. It was also necessary to establish the location of the protocols and
algorithms implementation for their subsequent audit.

The following activities were undertaken:


• Study of the source code with the use of reverse engineering methods in order to
restore the architecture of the software system.
• Analysis of the protocols and algorithms specifications in order to establish their
compliance with the source code.
• Analysis of the implementation in order to check the compliance with the best industry
practices.

Basics of the Beam blockchain structure


The kernel of the blockchain responsible for formation and correctness of new blocks (i.e.
components) is based on several cryptographic primitives, which are described below.

Cryptographic hash-function

• Signing transactions and/or messages. First, the hash function of the message is
calculated. This calculated value is used as an input value in the asymmetric
cryptography method for electronic signature.
• Proving that a transaction is stored in the blockchain without downloading the entire
blockchain. In order to accomplish this, transactions in each block are organized into a
Merkle Tree data structure. This is a binary tree with transactions stored in its leaves. In
the nodes of the tree there is a hash function from the sons of each node.
• This means that if the tree height equals to h, then the transaction location in the tree is
defined by a chain of hashes, starting from the leaf, where the transaction is stored, and
ending with the tree root.
• In some blockchains the hash function is used to link blocks into a chain. For example,
in the Bitcoin network each block contains a hash function from the previous block.

The Beam blockchain mainly uses SHA256 hash function. This is a second generation hash
function and at the present moment the use of it is considered to be completely safe, since
no practical collision finding algorithms have been found for it yet.

Blockchain Security Analysis


SmartDec https://smartdec.net
14
Nevertheless, experts recommend switching to the third generation hash functions (e.g.
SHA-3, BLAKE2).

The BLAKE2 hash function is used in the Proof-of-Work scheme. The BLAKE2 version
implemented a 256-bit hash in the Beam outputs as a result.

At the present moment BLAKE2 is considered to be safe for use. There are no working
methods that allow significantly reducing the brute force to find a collision.

Methods for significantly reducing brute force to find the preimage are also unknown.

Asymmetric cryptography
The Beam project uses asymmetric cryptography on elliptic curves provided by the
secp256k1 standard library.
The source code of the project (core/ecc.cpp and others) uses constants (for instance, the
order of a group of points on the elliptic curve) that coincide with the standard values.

Consensus
The Beam project uses the Proof-of-Work concept to obtain consensus. That means the
network nodes must perform computationally complex tasks to add a new block to the
blockchain (mine block).

Software implementations of the Proof-of-Work functions running on central processing units


or video cards have to compete with application-specific integrated circuits (ASIC). Thus, for
Bitcoin the relative simplicity of the Proof-of-Work functions (SHA256 hash calculation) made it
possible to quickly develop specialized schemes that are hundreds or thousands times more
efficient than the software implementations. This leads to the displacement of small miners
and concentration of mining capacities, which undermine decentralization and distribution of
the blockchain.

Therefore, the Proof-of-Work schemes that would be difficult to implement on specialized


chips are required. That means the implementation of Proof-of-Work in the form of a chip
would have a cost comparable to CPUs or GPUs.

The Beam project exploits the Equihash Proof-of-Work scheme. The authors of the scheme
assumed that the schemes with a large number of RAM would be economically unprofitable.
The Equihash scheme is based on the problem of the generalized birthday paradox.
To solve the problem with maximum performance, a large amount (approx. 1GB) of RAM is
required. Performance falls exponentially with the decreasing RAM requirements.
However, recently there have been reports, that Equihash Proof-of-Work scheme is “hacked”
in terms of ASIC-realisation resistance since ASIC chip about 100 times more efficient than
the software implementations has been developed.

Blockchain Security Analysis


SmartDec https://smartdec.net
15
Transactions model
The Beam blockchain uses UTXO transactions model (unspent transaction output). In this
model users’ balances are not stored, but transfers are tracked over the entire lifetime of the
blockchain.

A single transaction consists of:


• One or several Input addresses of a sender
• One or several Output addresses of a receiver (receivers)
• One or several Change addresses, to which the change is sent, that is the difference
between Inputs, Outputs, and Fee
• Fee – transaction commission

Information hiding
Transactional blockchain, by their nature, reveal all the information about transactions: sender
address, receiver address, and amounts. This allows tracking the fund movements. Although
only addresses take part in transactions and the owner of the address does not appear in
transactions explicitly, this is just a quasi-anonymity, since if the owner of the address is
established from the other source, anonymity is lost.

The Beam project implements the MimbleWimble protocol that allows hiding amounts
transferred in transactions. However, as before, anyone can make sure all the transactions
are valid. MimbleWimble is based on a scheme named Pedersen Commitment. In the original
Pedersen Commitment scheme it is possible to specify huge amount causing integer overflow,
effectively producing money out of thin air. To prevent this attack we limit the transferred
amount to some reasonable max value. A Zero Knowledge Proof (ZKP) is attached to each
commitment, so anyone can ensure that the amount is valid without disclosing it. Therefore, a
Zero Knowledge Proof that the amount in the commitment satisfies the constraints is attached
to each commitment (Rangeproof).

There are two sides in Zero Knowledge Proof:

Prover is the side that wants to prove that it has some information without revealing this
information. For example, the prover wants to prove to everybody that the number x lies in the
interval [a;b) without actually revealing x.

Verifier is the other side of the proof. The verifier wants to make sure that the other party
actually has the information.

ZKPs are usually formulated as interactive proofs, that is, the verifier asks some “questions” to
which the prover answers. In considered process we are interested in minimizing the number
of rounds and the amount of information transmitted between the parties, both in one round
and total.

Blockchain Security Analysis


SmartDec https://smartdec.net
16
Early versions of the range proof protocol required a large amount of data to check. For
example, range proof for a transaction with two exits could take up to 3.8 KiB.

The general scheme of Bulletproof Zero Knowledge Proofs, which is used in the Beam
blockchain, has the following advantages:
• Bulletproof requires significantly less memory compared to previous schemes. Proof of
belonging to a 64-bit range requires 675 bytes.
• When transitioning from a single number to a vector, the amount of required memory
grows not linearly, but logarithmically. That is, the proof of belonging of two numbers to a
64-bit range requires 739 bytes, four – 803 bytes.
• This means that different range proofs can be merged into one vector range proof in
order to decrease the amount of required memory for the proof.
• When range proof is used in the blockchain with the UTXO model, it is possible to
remove range proof for transaction outputs wasted in other transactions.
• It is required to store only 32 bytes of range proof in wasted transaction.

The idea of ZKP is in transition from scalar commitments to vector ones and in calculation of
the scalar product of vector commitments.

Interactive proof can be converted to a noninteractive one by using Fiat-Shamir heuristic. For
this heuristic interactive proof should have a public coin property, i.e. requests from the verifier
to the prover must be generated randomly and uniformly from the predefined set of requests.
Instead of the verifier, who makes a request to the prover, the prover refers to the “random
oracle” model. It applies a cryptographic hash function from some state, which possibly
includes the previous response from the prover to the verifier. In this way a request to the
prover is formed. It is computationally difficult for the prover to form a request that is
advantageous for himself, since this would require finding the preimage of some value of
cryptographic hash function.

Beam realization
Most of the cryptographic primitives are implemented in the secp256k1-zkp library
(https://github.com/ElementsProject/secp256k1-zkp). This library is based on secp256k1
from Bitcoin (https://github.com/bitcoin-core/secp256k1). The Beam project exploits
cryptographic primitives on the elliptic curves, SHA256 hashing.

The BLAKE2 hash function used in the Proof-of-Work scheme is located in the utility/crypto/
blake directory. The code in the considered directory is similar to the official BLAKE2 code
(https://github.com/BLAKE2/BLAKE2) in the ref and sse directories.

In the core directory there is a realization of the AES symmetric cipher (aes.h, aes.cpp).
The author of the realization is Christophe Devine.
There is almost identical code in other repositories (for example, in
https://github.com/eddelbuettel/digest). Methods implemented in C++ and written in the Beam
project are provided at the end of the file.

Blockchain Security Analysis


SmartDec https://smartdec.net
17
The file header points out that the considered realization is distributed under GNU GPL
v2 license or older. The main Beam code is distributed under Apache license. Despite
both licenses are “free”, they are compatible only in one way: the code under Apache
license can be included in the code under GPL license, but there is no possibility to do
it vice versa.

https://www.apache.org/licenses/GPL-compatibility.html:

Apache 2 software can therefore be included in GPLv3 project


s, because the GPLv3 license accepts our software into GPLv3
works.

However, GPLv3 software cannot be included in Apache project


s. The licenses are incompatible in one direction only,
and it is a result of ASF's licensing philosophy and the GPL
v3 authors interpretation of copyright law.

The Proof-of-Work Equihash scheme is located in the beam/pow directory. These files are
apparently taken from the ZCash project (https://github.com/zcash/zcash).

The implementation (within the Beam project from scratch) of the range proof according to the
Bulletproof scheme is given in the beam/core/ecc_Bulletproof.cpp file.

The author’s implementation of the Bulletproof and MimbleWimble protocols is available on github
(https://github.com/apoelstra/secp256k1-mw).

Moreover, in the official secp256k1-zkp repository there is a merge request from the author of
Bulletproof.

Other found errors and notes on the source code are presented below:
1. _countof macro is used incorrectly in the ecc.cpp file. _countof("STR") is always
equal to sizeof(void*) since "STR" is casted to the const char * type. The
following usage is the correct one:

static const char str[] = "STR";

_countof(str);

2. In different source files in different namespaces the uintBig type is defined with a
different number of bits in the integer value. In the core/block_crypt.h file in the
AmountBig structure it has the size of 128 bits and in the core/ecc.h file in the
ECC namespace it has the size of 256 bits.

3. In the ecc.cpp file the memory for ECC::Context singletone is allocated in the
following way:

Blockchain Security Analysis


SmartDec https://smartdec.net
18
uint64_t g_pContextBuf[(sizeof(Context) + sizeof(uint64_
t) - 1) / sizeof(uint64_t)];

That means the size of the allocated array is a multiple of eight and sufficient for storing
ECC::Context.

After that the reference is returned:

const Context& Context::get()


{
return *(Context*) g_pContextBuf;
}

The use of low-level memory manipulation appears to be unjustified. The get method
for obtaining an object reference can be implemented in the following way:

const Context& Context::get()


{
static Context cntx;
return &cntx;
}

Thus, according to C++11 standard, the constructor of the cntx object will be called
with the first call of the get method and in thread-safe manner. Accordingly, the
functionality implemented in the InitializeContext should be moved to the
constructor.

Blockchain Security Analysis


SmartDec https://smartdec.net
19
Report on the third stage of the Beam
project research
The objective of the third stage of the project analysis was
• to restore and perform security analysis of the node messaging protocol
• to check compliance of the MimbleWimble (wallet/negotiator.cpp,
core/block_validation.cpp) and Bulletproof (ecc_Bulletproof.cpp)
implementations with the corresponding specifications (algorithms described in
articles)

Node messaging protocol


During this process we’ve produced a protocol description, including the list of commands
and message format, as well as code locations where messages are sent or received.

0x00, Config
Transferred data:

• ECC::Hash::Value CfgChecksum;
• bool SpreadingTransactions;
• bool Bbs;
• bool SendPeers;
Message sent at:
• wallet/wallet_network.cpp
WalletNetworkIO::WalletNodeConnection::OnConnectedSecure()– commented out
• core/proto.cpp
FlyClient::NetworkStd::Connection::OnConnectedSecure()
• node/node.cpp
Node::Peer::OnConnectedSecure()

Message processed at:


• node/node.cpp
Node::Peer::OnMsg(proto::Config&& msg)

0x01, Bye
Transferred data:
• uint8_t Reason;
Message sent at:

Blockchain Security Analysis


SmartDec https://smartdec.net
20
• node/node.cpp
Node::Peer::DeleteSelf
Message processed at:
• node/node.cpp
Node::Peer::OnMsg – does nothing

0x02, Ping
Transferred data: none
Message sent at:
• core/proto.cpp
FlyClient::NetworkStd::Connection::SendRequest
Message processed at:
• node/node.cpp
Node::Peer::OnMsg – just answers with Pong

0x03, Pong
Transferred data: none
Message sent at:
• node/node.cpp
Message processed at: none

0x04, SChannelInitiate
Transferred data:
• ECC::uintBig NoncePub
Message sent at:
• core/proto.cpp
NodeConnection::SecureConnect
Message processed at:
• core/proto.cpp
NodeConnection::OnMsg

0x05, SChannelReady
Transferred data: none
Message sent at:
• core/proto.cpp
NodeConnection::OnMsg

Blockchain Security Analysis


SmartDec https://smartdec.net
21
Message processed at:
• core/proto.cpp
NodeConnection::OnMsg(SChannelReady&& msg)

0x06, Authentication
Transferred data:
• PeerID ID
• uint8_t IDType
• ECC::Signature Sig
Message sent at:
• core/proto.cpp
NodeConnection::ProveID(ECC::Scalar::Native& sk, uint8_t nIDType)
Message processed at:
• wallet/wallet_network.cpp – the whole logic is commented out
• node/node.cpp
Node::Peer::OnMsg(proto::Authentication&& msg)
• core/proto.cpp
NodeConnection::OnMsg(Authentication&& msg)

0x07, PeerInfoSelf
Transferred data:
• uint16_t Port
Message sent at:
• node/node.cpp
Node::Peer::OnConnectedSecure()
Message processed at:
• node/node.cpp
Node::Peer::OnMsg(proto::PeerInfoSelf&& msg)

0x08, PeerInfo
Transferred data:
• PeerID ID
• io::Address LastAddr
Message sent at:
• node/node.cpp
Node::Peer::OnResendPeers()
Obtained from PeerMan::PeerInfo m_This.mPeerMan

Blockchain Security Analysis


SmartDec https://smartdec.net
22
• Messages including its info are sent to all the peers
Message processed at:
• node/node.cpp
Node::Peer::OnMsg(proto::PeerInfo&& msg)
• Comment: structures with the same names in multiple namespaces: struct PeerInfo in
PeerManager is used as PeerMan::PeerInfo

0x09, GetExternalAddr
Transferred data: none
Message sent at: none
• processed at:
• node/node.cpp
Node::Peer::OnMsg(proto::GetExternalAddr&& msg)

0x0a, ExternalAddr
Transferred data:
• uint32_t Value
Message sent at:
• node/node.cpp
Node::Peer::OnMsg(proto::GetExternalAddr&& msg)
Message processed at: none

0x0b, GetTime
Transferred data: none
Message sent at: none
Message processed at:
• node/node.cpp
Node::Peer::OnMsg(proto::GetTime&& msg)

0x0c, Time
Transferred data: none
Message sent at:
• node/node.cpp
Node::Peer::OnMsg(proto::GetTime&& msg)
Message processed at:
• node/unittest/node_test.cpp – test code
MyClient::OnMsg(proto::Time&& msg) – empty function body

Blockchain Security Analysis


SmartDec https://smartdec.net
23
0x0d, DataMissing
Transferred data: none
Message sent at:
• node/node.cpp
Node::Peer::OnMsg(proto::GetHdr&& msg)
Node::Peer:OnMsg(proto::GetHdrPacked&& msg)
Node::Peer::OnMsg(proto::GetBody&& msg)
• node/functionaltests/send_data_missing_test.cpp
TestNodeConnection::GenerateTests()
Message processed at:
• node/node.cpp
Node::Peer::OnMsg(proto::DataMissing&& msg)

0x0e, Boolean
Transferred data:
• bool Value;
Message sent at: none
Message processed at:
• wallet/wallet_network.cpp – commented out

0x10, NewTip
Transferred data:
• Block::SystemState::Full Description
Message sent at:
• node/node.cpp
Node::Peer::OnConnectedSecure()
Node::Processor::OnNewState()
Message processed at:
• wallet/wallet_network.cpp – commented out
• core/proto.cpp
FlyClient::NetworkStd::Connection::OnMsg(proto::NewTip&&
msg)– simple context-free state validation
• node/node.cpp
• Node::Peer::OnMsg(proto::NewTip&& msg) – new state is received and validated in
Node::Processor::OnState. Updates sender’s rating if the state is valid and calls the
Node::RefreshCongestions() function.

Blockchain Security Analysis


SmartDec https://smartdec.net
24
0x11, GetHdr
Transferred data:
• Block::SystemState::ID, ID
Message sent at:
• Node::TryAssignTask(Task& t, Peer& p)
Message processed at:
• node/node.cpp
Node::Peer::OnMsg(proto::GetHdr&& msg)

0x12, Hdr
Transferred data:
• Block::SystemState::Full Description
Message sent at:
• node/node.cpp
Node::Peer::OnMsg(proto::GetHdr&& msg)
Message processed at:
• node/node.cpp
Node::Peer::OnMsg(proto::Hdr&& msg)

0x13, GetHdrPack
Transferred data:
• Block::SystemState::ID Top
• uint32_t Count
Message sent at:
• node/node.cpp
Node::TryAssignTask(Task& t, Peer&, p)
Message processed at:
• node/node.cpp
Node::Peer::OnMsg(proto::GetHdrPack&& msg)

0x14, HdrPack
Transferred data:
• Block::SystemState::Sequence::Prefix, Prefix
std::vector<Block::SystemState::Sequence::Element>,vElements
Message sent at:
• node/node.cpp
Node::Peer::OnMsg(proto::GetHdrPack&& msg)

Blockchain Security Analysis


SmartDec https://smartdec.net
25
Message processed at:
• node/node.cpp
Node::Peer::OnMsg(proto::Hdr&& msg)
Each received element is validated. Error is raised if none are valid. If any of the last
elements is valid, it is saved to the DB and peer’s rating is updated.

0x15, GetBody
Transferred data:
• Block::SystemState::ID ID
Message sent at:
• node/node.cpp
Node::Peer::TryAssignTask(Task& t, Peer& p)
Message processed at:
• node/node.cpp
Node::Peer::OnMsg(proto::GetBody&& msg)

0x16, Body
Transferred data:
• ByteBuffer Perishable;
• ByteBuffer Ethernal;
Message sent at:
• node/node.cpp
Node::Peer::OnMsg(proto::GetBody&& msg)
Message processed at:
• node/node.cpp
Node::Peer::OnMsg(proto::Body&& msg)

0x17, GetProofState
Transferred data:
• Height Height
Message sent at:
• node/unittests/node_test.cpp – test code
MyClient::OnMsg(proto::NewTip&& msg)
Message processed at:
• node/node.cpp
Node::Peer::OnMsg(proto::GetProofState&& msg)

Blockchain Security Analysis


SmartDec https://smartdec.net
26
0x18, ProofState
Transferred data:
• Merkle::HardProof Proof
Message sent at:
• node/node.cpp
Node::Peer::OnMsg(proto::GetProofState&& msg)
Message processed at:
• node/unittests/node_test.cpp – test code
Node::Peer::OnMsg(proto::ProofState&& msg)
• wallet/wallet_network.cpp
WalletNetworkIO::WalletNodeConnection::OnMsg2(proto::ProofState&& m
sg)

0x19, GetProofKernel
Transferred data:
• Merkle::Hash ID;
Message sent at:
• node/node.cpp
Node::Peer::TryAssignTask(Task& t, Peer& p)
Message processed at:
• node/node.cpp
Node::Peer::OnMsg(proto::GetProofKernel&& msg) – one might send empty
proto::ProofKernel

0x1a, ProofKernel
Transferred data:
• TxKernel::LongProof Proof
Message sent at:
• node/node.cpp
Node::Peer::OnMsg(proto::GetProofKernel&& msg)– one might send empty
proto::ProofKernel
• wallet/unittests/wallet_test.cpp – test code
Client::OnMsg(proto::GetProofKernel&& msg)
Message processed at:
• wallet/wallet_network.cpp
WalletNetworkIO::WalletNodeConnection::OnMsg2
(proto::ProofKernel&& msg) – commented out
• node/unittests/node_test.cpp – test code
MyClient::OnMsg(proto::ProofKernel&& msg)

Blockchain Security Analysis


SmartDec https://smartdec.net
27
0x1b, GetProofUtxo
Transferred data:
• ECC::Point Utxo
• Height MaturityMin
Message sent at:
• node/unittests/node_test.cpp
MyClient::OnMsg(proto::NewTip&& msg)– test code
Message processed at:
• node/node.cpp
Node::Peer::OnMsg(proto::GetProofUtxo&& msg)
• wallet/unittests/wallet_test.cpp test code
TestBlockchain::GetProof(const proto::GetProofUtxo& data,
proto::ProofUtxo& msgOut)
Client::OnMsg(proto::GetProofUtxo&& msg)

0x1c, ProofUtxo
Transferred data:
• std::vector<Input::Proof> Proofs
Message sent at:
• node/node.cpp
Node::Peer::OnMsg(proto::GetProofUtxo&& msg)
• wallet/unittests/wallet_test.cpp – test code
TestBlockchain::GetProof(const proto::GetProofUtxo& data,
proto::ProofUtxo& msgOut)
Client::OnMsg(proto::GetProofUtxo&& msg)
Message processed at:
• node/unittests/node_test.cpp – test code
MyClient::OnMsg(proto::ProofUtxo&& msg)

0x1d, GetProofChainWork
Transferred data:
• Difficulty::Raw LowerBound
Message sent at:
• core/proto.cpp
FlyClient::NetworkStd::Connection::RequestChainworkProof()
• node/unittests/node_test.cpp – test code
MyClient::OnMsg(proto::NewTip&& msg)
Message processed at:

Blockchain Security Analysis


SmartDec https://smartdec.net
28
• node/node.cpp
Node::Peer::OnMsg(proto::GetProofChainWork&& msg)
• wallet/unittests/wallet_test.cpp – test code
Client::OnMsg(proto::GetProofChainWork&& msg)

0x1e, ProofChainWork
Transferred data:
• Block::ChainWorkProof Proof
Message sent at:
• node/node.cpp
Node::Peer::OnMsg(proto::GetProofChainWork&& msg)
• wallet/unittests/wallet_test.cpp – test code
Client::OnMsg(proto::GetProofChainWork&& msg)
Message processed at:
• node/node.cpp
Node::Peer::OnMsg(proto::ProofChainWork&& msg)
• core/proto.cpp
FlyClient::NetworkStd::Connection::OnMsg(proto::ProofChainWork&& m
sg)
• node/unittests/node_test.cpp – test code
MyClient::OnMsg(proto::ProofChainWork&& msg)

0x20, MacroblockGet
Transferred data:
• Block::SystemState::ID ID
• uint8_t Data
• uint64_t Offset
Message sent at:
• node/node.cpp
Node::SyncCycle(Peer& p)
Message processed at:
• node/node.cpp
Node::Peer::OnMsg(proto::MacroblockGet&& msg)

0x21, Macroblock
Transferred data:
• Block::SystemState::ID ID

Blockchain Security Analysis


SmartDec https://smartdec.net
29
• ByteBuffer Portion
Message sent at:
• node/node.cpp
Node::Peer::OnMsg(proto::MacroblockGet&& msg)
Message processed at:
• node/node.cpp
Node::Peer::OnMsg(proto::Macroblock&& msg)

0x22, GetCommonState
Transferred data:
• std::vector<Block::SystemState::ID> IDs
Message sent at:
• node/unittests/node_test.cpp – test code
• MyClient::OnMsg(proto::NewTip&& msg)
Message processed at:
• node/node.cpp
Node::Peer::OnMsg(proto::GetCommonState&& msg)
Comment: message processing throws exception when any of the received states has
a lower height than the genesis block. As a result, one can form incorrect message to
crash the node.

0x23, ProofCommonState
Transferred data:
• uint32_t iState
• Merkle::HardProof Proof
Message sent at:
• node/node.cpp
Node::Peer::OnMsg(proto::GetCommonState&& msg)
Message processed at: none

0x24, GetProofKernel2
Transferred data:
• Merkle::Hash ID
• bool Fetch
Message sent at:
• node/unittests/node_test.cpp – test code
MyClient::OnMsg(proto::NewTip&& msg)

Blockchain Security Analysis


SmartDec https://smartdec.net
30
Message processed at:
• node/node.cpp
Node::Peer::OnMsg(proto::GetProofKernel2&& msg)

0x25, ProofKernel2
Transferred data:
• Merkle::Proof Proof
• Height Height
• TxKernel::Ptr Kernel
Message sent at:
• node/node.cpp
Node::Peer::OnMsg(proto::GetProofKernel2&& msg)
Message processed at:
• node/node.cpp
Node::Peer::OnMsg(proto::ProofKernel2&& msg)

0x28, GetMined
Transferred data:
• Height HeightMin
Message sent at:
• node/unittests/node_test.cpp – test code
MyClient::OnMsg(proto::NewTip&& msg)
Message processed at:
• node/node.cpp
Node::Peer::OnMsg(proto::GetMined&& msg)

0x29, Mined
Transferred data:
• std::vector<PerMined> Entries
Message sent at:
• node/node.cpp
Node::Peer::OnMsg(proto::GetMined&& msg)
Message processed at: none

0x2a, Recover
Transferred data:

Blockchain Security Analysis


SmartDec https://smartdec.net
31
• bool Private
• bool Public
Message sent at:
• node/unittests/node_test.cpp – test code
MyClient::OnMsg(proto::NewTip&& msg)
Message processed at:
• node/node.cpp
Node::Peer::OnMsg(proto::Recover&& msg)

0x2b, Recovered
Transferred data:
• std::vector<Key::IDV> Private
• std::vector<Key::IDV> Public
Message sent at:
• node/node.cpp
Node::Peer::OnMsg(proto::Recover&& msg)
Message processed at:
• node/unittests/node_test.cpp – test code
MyClient::OnMsg(proto::Recovered&& msg)

0x30, NewTransaction
Transferred data:
• Transaction::Ptr Transaction
• bool Fluff
Message sent at:
• node/node.cpp
Node::Peer::SendTx(Transaction::Ptr& ptx, bool bFluff)
Message processed at:
• node/node.cpp
Node::Peer::OnMsg(proto::NewTransaction&& msg)

0x31, HaveTransaction
Transferred data:
• Transaction::KeyType ID
Message sent at:
• node/node.cpp
Node::Peer::OnMsg(proto::Config&& msg)

Blockchain Security Analysis


SmartDec https://smartdec.net
32
Node::OnTransactionFluff(Transaction::Ptr&& ptxArg, const Peer*
pPeer, TxPool::Stem::Element* pElem)
Message processed at:
• node/node.cpp
Node::Peer::OnMsg(proto::HaveTransaction&& msg)

0x32, GetTransaction
Transferred data:
• Transaction::KeyType ID
Message sent at:
• node/node.cpp
Node::WantedTx::OnExpired(const KeyType& key)
Node::Peer::OnMsg(proto::HaveTransaction&& msg)
Message processed at:
• node/node.cpp
Node::Peer::OnMsg(proto::GetTransaction&& msg)

0x38, BbsMsg
Transferred data:
• BbsChannel Channel
• Timestamp TimePosted
• ByteBuffer Message
Message sent at:
• node/node.cpp
Node::Peer::SendBbsMsg(const NodeDB::WalkerBbs::Data& d)
Message processed at:
• core/proto.cpp
FlyClient::NetworkStd::Connection::OnMsg(proto::BbsMsg&& msg)
• node/node.cpp
Node::Peer::OnMsg(proto::BbsMsg&& msg)
• wallet/wallet_network.cpp
WalletNetworkViaBbs::OnMsg(const proto::BbsMsg& msg)
WalletNetworkViaBbs::BbsSentEvt::OnMsg(proto::BbsMsg&& msg)– calls
parent’s function OnMsg

0x39, BbsHaveMsg
Transferred data:

Blockchain Security Analysis


SmartDec https://smartdec.net
33
• BbsMsgID Key
Message sent at:
• node/node.cpp
Node::Peer::OnMsg(proto::Config&& msg)
Node::Peer::OnMsg(proto::BbsMsg&& msg)
Message processed at:
• node/node.cpp
Node::Peer::OnMsg(proto::BbsHaveMsg&& msg)

0x3a, BbsGetMsg
Transferred data:
• BbsMsgID Key
Message sent at:
• node/node.cpp
Node::Peer::OnMsg(proto::BbsHaveMsg&& msg)
Node::Bbs::WantedMsg::OnExpired(const KeyType& key)
Message processed at:
• node/node.cpp
Node::Peer::OnMsg(proto::BbsGetMsg&& msg)

0x3b, BbsSubscribe
Transferred data:
• BbsChannel Channel
• Timestamp TimeFrom
• bool On
Message sent at:
• core/proto.cpp
FlyClient::NetworkStd::BbsSubscribe(BbsChannel ch, Timestamp ts,
IBbsReceiver* p)
FlyClient::NetworkStd::Connection::OnMsg(proto::Config&& msg)
Message processed at:
• node/node.cpp
Node::Peer::OnMsg(proto::BbsSubscribe&& msg)

0x3c, BbsPickChannel
Transferred data: none
Message sent at: none
Message processed at:

Blockchain Security Analysis


SmartDec https://smartdec.net
34
• node/node.cpp
Node::Peer::OnMsg(proto::BbsPickChannel&& msg)

0x3d, BbsPickChannelRes
Transferred data:
• BbsChannel Channel
Message sent at:
• node/node.cpp
Node::Peer::OnMsg(proto::BbsPickChannel&& msg)
Message processed at: none

Class generation for messages


Macros are used to generate structs with required fields and methods for every message type,
e.g. the structure below was generated for the BbsSubscribe message.

struct BbsSubscribe
{
static const uint8_t s_Code = 0x3b;

BbsChannel m_Channel;
Timestamp m_TimeFrom;
bool m_On;

template <typename Archive> void serialize(Archive& ar)


{
ar & m_Channel & m_TimeFrom & m_On;
}

BbsSubscribe(Zero_ = Zero)
{
ZeroInit(m_Channel);
ZeroInit(m_TimeFrom);
ZeroInit(m_On);
}

BbsSubscribe(Uninitialized_)
{
}

BbsSubscribe(BeamNodeMsg_BbsSubscribe(

Blockchain Security Analysis


SmartDec https://smartdec.net
35
typename InitArg<BbsChannel>::TArg argChannel,
typename InitArg<Timestamp>::TArg argTimeFrom,
typename InitArg<bool>::TArg argOn,
Unused_ = Unused)
{
InitArg<BbsChannel>::Set(m_Channel, argChannel);
InitArg<Timestamp>::Set(m_Timestamp, argTimestamp);
InitArg<bool>::Set(m_On, argOn);
}

};
struct BbsSubscribe_NoInit :public BbsSubscribe
{
BbsSubscribe_NoInit() : BbsSubscribe(Uninitialized) {}
};

The last argument of the constructor (Unused) is required to bypass syntax limitations of C
macros (one must place comma after each parameter).

Template InitArg method is used to initialize fields. This method is additionally specialized
to correctly process unique_ptr.

template <typename T> struct InitArg {


typedef const T& TArg;
static void Set(T& var, TArg arg) { var = arg; }
};

template <typename T> struct InitArg<std::unique_ptr<T> > {


typedef std::unique_ptr<T>& TArg;
static void Set(std::unique_ptr<T>& var, TArg arg) { var
= std::move(arg); }
};

Blockchain Security Analysis


SmartDec https://smartdec.net
36
Review of the in-out subsystem
(utility/io)
Analysis of this module was performed on commit
65949fa8c9d82abf4d89c04fd69f614bb18c7492 (Nov 12, 2018)

Structure
This module includes the libuv folder with implementation of this library.

Headers:
• address.h
• bufferchain.h
• coarsetimer.h
• fragment_writer.h
• mempool.h
• sslio.h
• sslstream.h
• tcpstream.h
• asyncevent.h
• buffer.h
• errorhandling.h
• libuv.h
• reactor.h
• sslserver.h
• tcpserver.h
• timer.h

Implementation:
• address.cpp
• bufferchain.cpp
• coarsetimer.cpp
• fragment_writer.cpp
• sslio.cpp
• sslstream.cpp
• tcpstream.cpp
• asyncevent.cpp
• buffer.cpp
• errorhandling.cpp

Blockchain Security Analysis


SmartDec https://smartdec.net
37
• reactor.cpp
• sslserver.cpp
• tcpserver.cpp
• timer.cpp

class Reactor

reactor.h
The class wraps the libuv library. It includes private fields:
• _loop – handler for Eventloop in libuv
• _stopEvent – stop event EventLoop
• _handlePool– handlers pool in EventLoop
• bool _creatingInternalObjects = false
• std::unique_ptr<PendingWrites> _pendingWrites
• std::unique_ptr<TcpConnectors> _tcpConnectors
• std::unique_ptr<TcpShutdowns> _tcpShutdowns

reactor.h includes public interface:


• run() – starts EventLoop
• stop() – stops EventLoop
• tcp_connect (Address address, uint64_t tag, const ConnectCallback
&callback, int timeoutMsec=-1, Address bindTo=Address())– initializes
new TCP connection
• create()– static function that uses a private constructor to initialize new Reactor object

reactor.h includes private functions that use internal class Object to wrap commonly used
libuv function. Object has libuv handler and pointer to Reactor. The code that interacts
with Reactor allocates a lot of internal class objects. It stores pointers to these new objects in
EventLoop handlers. Reactor’s destructor calls uv_close() for every handler.

Reactor.cpp
This file includes TcpConnectors, TcpConnectors, and PendingWrites classes.
Reactor uses these classes to store TCP connections and data awaiting for sending.

Blockchain Security Analysis


SmartDec https://smartdec.net
38
class PendingWrites
The most important functions:
• ErrorCode async_write(Reactor::Object* o, BufferChain& unsent,
const Reactor::OnDataWritten& cb)– expects to receive TCPStream pointer as
the first parameter. The function writes to the provided TCPStream asynchronously.
• void cancel_all()– clears output queue

class Address

address.h
The class wraps IP address and port. It can be converted to sockaddr_in.

buffer.h and buffer.cpp


It implements buffers for data transfers and allocates memory, wraps C arrays,
memory, and files. The classes included are:
• struct IOVec
• struct SharedBuffer : IOVec
• struct HeapAllocatedMemory : AllocatedMemory
• struct ReadOnlyMappedFile : AllocatedMemory

Functions:
• SharedBuffer normalize(const SerializedMsg& msg, bool
makeUnique=false)– normalizes data to SharedBuffer. It makes buffer unique if it
is necessary.
• textttSharedBuffer map_file_read_only(const char* fileName) – makes memory map of
the file to the buffer

class BufferChain

bufferchain.h
The class stores a chain of buffers (vectors of the IOVec classes) and the pointer to the
empty IOVec.

Blockchain Security Analysis


SmartDec https://smartdec.net
39
class Timer

timer.h
Functions:
• Create – stores raw pointer to the object in the EventLoop
• Start – sets a callback and starts the timer by calling restart function
• Restart – directly interacts with Reactor and starts timer at the EventLoop level,
setting callback in the same indirect way as AsyncEvent
• Cancel – cancels the timer if it is in the same thread

class MemPool

mempool.h
The class is used for storing handlers in the EventLoop. In the destructor it frees memory
pointed to by the stored pointers. The data field in the structure stores the pointer to the
Reactor::Object.

coarsetimer.h
This class allows setting/resetting several timers with different times, but with the same
callback. Only one timer can work at the same time, since the Timer object is changed by
every call of the set_timer and cancel functions, which are responsible for timers in
EventLoop. However, the class emulates the work of several timers simultaneously. It stores a
queue of timers with the time when they should start. When an internal timer of the
Timer object goes off the internal callback function checks if there are timers in the queue that
should go off. If so, the external callback function is called and the timer id is passed to it.
TIMER_ACCURACY constant allows setting the tolerance for the go off time.

class MultipleTimers

coarsetimer.h
The class implements the logic for multiple timers with different times and callbacks via
CoarseTime class. It stores a set of pointers to callbacks for each timer id. It uses internal
callback for choosing the corresponding external callback. Timer work logic is similar to the
logic in the CoarseTimer class.

Blockchain Security Analysis


SmartDec https://smartdec.net
40
class FragmentWriter

fragment_writer.h
The class writes data to a fragment (instance of the SharedMem class). When the entire
fragment is filled the writer passes it to the external callback via move semantics. The
constructor defines the fragment size, header size, and callback.

Public functions:
• void* write(const void *ptr, size_t size)
• void finalize()

class TcpStream

tcpstream.h
The interface is well documented and its logic is clear. There are functions for reading/writing
from/to stream, retrieving information about connection, turning on/off reading from the
stream, and closing the stream.

The _reactor field is not initialized anywhere, though this field is referenced in the code. The
Reactor::init_tcpstream and uv_tcp_init functions are not used. Stream in the
EventLoop is not initialized.

The close function from the interface is not implemented. It was apparently replaced with
the shutdown function.

No more errors were found.

The class itself does not work as a full TCP stream.

class SSLContext

sslio.h
The class implements context for the SSL connection and context for the server (with the
certificate and private key) and for the client.

The certificate verification function for the client is not implemented.

Blockchain Security Analysis


SmartDec https://smartdec.net
41
class SSLIO

sslio.h
The class implements interface for SSL data encryption/decryption. The
bool SSLIO::new_data_from_stream function is implemented instead of
Result SSLIO::on_encrypted_data_from_stream.

The flush_write_buffer function is not implemented.

The implementation uses the client variable that is not defined (probably, it is defined in
openssl, but it does not appear that there is such a variable). Perhaps, the authors wanted to
use SSL* _ssl.

Obviously, the considered class is incomplete.

class SslStream

sslstream.h
The class inherits from TcpStream which causes its problems with initialization. It is not
initialized in the EventLoop.

It overrides the functions for writing into stream via SSLIO only.

The class does not implement functions for reading encrypted data from the stream.
The on_decrypted_data function calls TcpStream::on_read. However, no functions are
passed to the EventLoop (read_cb is not overridden) for encrypted input data processing.

class TcpServer

tcpserver.h
This class implements SSL server with the callback function that will be called on every new
connection with the server. The considered class is initialized in Reactor in the same way as
the other Reactor::Object inheritors described above.
The only interesting function is the server creation function:

static std::unique_ptr<TcpServer> create(Reactor& reactor,


Address bindAddress, Callback&& callback);

Blockchain Security Analysis


SmartDec https://smartdec.net
42
using Callback = std::function<void(TcpStream::Ptr&&
newStream, ErrorCode status)>;

The implementation is simple and does not contain any errors.

class SslServer

sslserver.h
This class implements SSL server with the callback function that will be called on every new
connection with the server. The considered class inherits TcpServer and is initialized in
Reactor in the same way as the other Reactor::Object inheritors described above.

The only interesting function is the server creation function:

static std::unique_ptr<SslServer> create(Reactor& reactor, A


ddress bindAddress, Callback&& callback);

The implementation is simple and does not contain any errors.

Notes on the code:


• The directory contains files that support SSL. However, their usage is disabled in
CMakeLists.txt.
• SSL implementation is incomplete.
• (code style) mempool.h is a simple class for storing a pool of pointers to objects. It
appears to be an unnecessary layer above the standard calloc and free
• (code style) mixed low-level C-style memory management and use of shared_ptr,
enable_shared_from_this, unique_ptr

Blockchain Security Analysis


SmartDec https://smartdec.net
43
Overview of the Node subsystem
The audit was performed using the master branch as of 2018-11-21.

The system logic was restored by the source code.

db.h / db.cpp
The NodeDB class allows the node to work with the sqlite3 database. The database is
opened via the NodeDB::open function. The DB structure is created by calling the
NodeDB::create function.

Constant variables are defined at the beginning of the db.cpp file. The variables contain the
names of the DB tables/fields that will be used in the queries.

Low-level creating/processing requests to the database is made with the use of the
NodeDB::Recordset class. The NodeDB::Recordset::Reset(NodeDB&,
Query::Enum, const char*) function sends a request to the database. The second
parameter determines the cell of the NodeDB::m_pPrep array in which the request handler,
defined by the sqlite3_prepare_v2, will be saved.

In the Query structure the enum is specified. It is used to separate queries. For example,
setting Recordset for getting all the peers will be done with the following command:

m_Rs.Reset(Query :: PeerEnum, "SELECT" TblPeer_Key ","


TblPeer_Rating "," TblPeer_Addr "," TblPeer_LastSeen "FROM"
TblPeer)

where m_Rs is an instance of the Recordset class.

The query step is executed by calling the NodeDB::Recordset::Step function.

The NodeDB::Recordset::StepStrict function also checks if the query has returned at


least one string.

The NodeDB::Recordset::get and NodeDB::Recordset::set functions get/set values


of the column in the current row. There are functions realisations for the uint32_t and
uint64_t types as well as various bytes data representations.

The NodeDB::Recordset::get_As and NodeDB::Recordset::set_As template


functions read and write various types in bytes using the Blob class defined in
beam/utility/common.h.

Blockchain Security Analysis


SmartDec https://smartdec.net
44
Reading and writing the Merkle::Hash and Block::PoW classes can be done via
functions described above. The read/write functions do not allow checking if the written data
type matches the type of the field in the database table.

In the NodeDB class there are functions for working with various entities necessary for node
operation (states, peers, events, blocks, transaction cores, etc.). The
NodeDB::Transaction class implements the minimal functionality for working with
database transactions.

The NodeDB::WalkerEvent, NodeDB::WalkerMined, NodeDB::WalkerPeer,


NodeDB::WalkerState, NodeDB::WalkerBbs classes are implemented for walking
through the corresponding entities.

txpool.h / txpool.cpp
It implements the TxPool transaction pool class.

The internal class TxPool::Profit describes the profit from the transaction. It stores
transaction fee and its size. Operator < is implemented for the profits comparison.

TxPool contains two classes for storing transaction data:


• TxPool::Fluff that stores transactions in three different multisets:
– Fluff::Element::Tx (stores the transaction key)
– Fluff::Element::Profit
– Fluff::Element::Threshold (stores the height of the transaction block)
• TxPool::Stem that stores transactions in three different multisets:
– Stem::Element::Kernel
– Stem::Element::Profit
– Stem::Element::Time
Both classes can get the parent class by calculating its address in a heap (since multisets
store pointers in the objects in a heap). This can be achieved by using the
IMPLEMENT_GET_PARENT_OBJ macro declared in beam/utility/common.h.

In both classes Element moved the pointer from beam/core to Transaction and does not
allocate free memory for it in a heap. Therefore, the transaction should live more than the
TxPool instance.

Fluff contains the following functions:


• AddValidTx(Transaction::Ptr&& pValue, const Transaction::Context&
ctx, const Transaction::KeyType& key)
• Delete(Element&)
• Clear()

Blockchain Security Analysis


SmartDec https://smartdec.net
45
• DeleteOutOfBound(Height)
• ShrinkUpTo(uint32_t nCount)

Fluff monitors the memory allocation/release for Fluff::Element in the following way: a
new element is created in a heap and initialized in the AddValidTx function. After that
pointers to subclasses are written in multisets, the address of the parent element is calculated
in any function that removes the subclass from the multiset and the corresponding memory in a
heap is released.

Stem contains functions for storing/removing the Stem::Element subclasses.


Stem::Element has the functionality to store “merged” transactions as a vector of the
Element::m_vKrn transactions kernels. Moreover, it has the Stem::TryMerge(Element&
trg, Element& src) function, which “merges” two transactions if possible.

Stem does not create new Stem::Element instances. Pointer to Stem::Element is passed
to the functions that add subclasses. Probably, it is implied that the ownership of the object is
transferred to the Stem instance at that moment, since when the element is deleted, the
memory is released.

Stem has the functionality to control timers. In the callback for the timers (Stem::OnTimer)
only the next timer is activated, with timeout taken from multiset that stores time for Element.
When the timer is created, Element records the response time of the timer. No other use of
the imers or their interaction has been detected.

processor.h / processor.cpp
These files contain realisation of the NodeProcessor class. This class is an interlayer
between the node, which communicates with the network, and the internal database. The class
takes prepared data (states, blocks, transactions, inputs, and outputs) as the input, validates
them with the cryptography from beam/core, if needed, and writes them to the database.

It also gets data from the database and forms the corresponding classes, implements the
NodeProcessor::Cursor class that stores current blockchain processor state.

The ApproveState function always returns true , f a l s e and


OpenMacroblock functions. These functions are overridden in the Node::Processor
child class, which is used in Node.

NodeProcessor leaves event handlers (such as OnNewState) empty. They are also
overridden in Node::Processor. In addition, the VerifyBlock function complementing the
block checking procedure is overridden. NodeProcessor implements only transaction context
validation (assuming that the validation of the transaction itself is done) and height of the block,
where the transaction is written.

Blockchain Security Analysis


SmartDec https://smartdec.net
46
NodeProcessor implements NodeProcessor::RollbackData class for rolling back data.

The NodeProcessor::ProcessKrnMmr function returns uint64_t(-1) if there is no


transaction kernel in the provided instance of TxBase::IReader. The returned value is not
checked in the NodeProcessor::get_ProofKernel function, which calls the original
function.

The NodeProcessor::GoForward(uint64_t row) function tries to raise the blockchain


height by using the block, located at the row row of the DB. The considered function removes
the block from the DB if the block is rejected and calls OnPeerInsane for the peer at the row
row (probably for the peer that created the block).

The NodeProcessor::Rollback() function rolls back one block, invokes block check on
the current cursor state, and calls the OnCorrupted() function if the block is invalid.

The OnState and OnBlock functions write new objects to the database and call the
OnStateData and OnBlockData callbacks respectively.

The IBlockWalker class is implemented for walking the blocks. Child classes must
implement the OnBlock handler.

The IUtxoWalker : IBlockWalker anonymous classs is implemented. It allows walking


through all UTXO. Child classes must implement the OnInput and OnOutput handlers.

The UtxoRecoverSimple : IUtxoWalker and UtxoRecoverEx:UtxoRecoverSimple


classes are implemented. They allow getting all UTXO from the block history.

The NodeProcessor::EnumBlocks function walks through all the blocks till the last
macroblock inclusive and passes them to the IBlockWalker::OnBlock functon. It is used
by the classes described above as well as by the InitializeFromBlocks function.

Validation
Validation is performed in the following functions:
• HandleBlockElement
• OnStateInternal
• VerifyBlock
• ApproveState
• ImportMacroBlockInternal
• ValidateTxContext
• ValidateTxWrtHeight
• HandleBlock

Blockchain Security Analysis


SmartDec https://smartdec.net
47
NodeProcessor::HandleBlockElement(const Input& v,
Height h, const Height* pHMax, bool bFwd)
The function processes the transaction input:
1. If bFwd == true (perhaps this means that Input will be included only in the next block)
a) Traveller for UTXO tree is created and returns false if the required root was found
b) Sets the boundaries of the search by maturity
i. If pHMax == NULL, then UTXO with maturity from 0 to h is searched
ii. Otherwise, it is checked if the input maturity is greater than *pHMax; then only
UTXO with maturity equal to maturity of the input is searched
c) The rest of the walker is configured
d) If UTXO is not found, then the function returns false and does not make any
changes
e) If UTXO is found, then corresponding UTXO is retrieved, commitments and
maturity are compared and its count is decreased
f) If count does not become zero, then UTXO is simply erased from the tree
g) Otherwise, the tree search cursor is invalidated

2. If bFwd == false
a) bool bCreate = true
b) Corresponding UTXO is searched:
p = m_Utxos.Find(cu, key, bCreate)
the key has the same commitment and maturity as the input
c) If bCreate == true
i. Count of the found UTXO is set to 1
d) Otherwise
i. Count of the found UTXO is incremented
ii. The tree search cursor is invalidated

NodeProcessor::HandleBlockElement(const Output& v,
Height h, const Height* pHMax, bool bFwd)
The function processes input :
1. UtxoTree::Key::Data is created
a) UtxoTree::Key::Data = d

2. Commitment d is set to output commitment

3. Maturity of d is set to v.get_MinMaturity(h)

4. When pHMax != NULL, it is checked that maturity d < maturity v; maturity d is set to
maturity

Blockchain Security Analysis


SmartDec https://smartdec.net
48
5. UTXO corresponding to d is searched and bCreate flag is set similar to the previous
item

6. Tree search cursor is invalidated

7. If bFwd == true
a) If bCreate == true
i. count UTXO is set to 1
b) Otherwise
i. Input::Count nCountInc is set to count UTXO plus 1
ii. If nCountInc == 0 (i.e. the overflow is present), then the function returns
false
iii. count UTXO is set to nCountInc

8. If bFwd == false
a) If count UTXO is 1, then the UTXO is removed from the tree
b) Otherwise count is decremented

TODO. The flag logic is unclear. What does the bFwd flag really mean and why are
diametrically opposed actions taken with the found UTXO for its different values?

NodeProcessor::OnStateInternal(const
Block::SystemState::Full& s, Block::SystemState::ID&
id)
The function validates the received state s:
1. It receives state id and writes it by reference id.

2. It calls the Block::SystemState::Full::isValid function from the


beam/core/block_crypt module. Context-free state validation is done. It returns
DataStatus::Invalid if the state does not pass validation

3. The function checks if the state timestamp is higher than the internal one by no more
than Rules::get().TimestampAheadThreshold_s. Otherwise, it returns
DataStatus::Invalid

4. It calls ApproveState(id). It returns DataStatus::Invalid if the function returns


false

5. The function checks if the state height is not lower than the lowest cursor horizon.
Otherwise, it returns DataStatus::Unreachable

6. The function calls DB::StateFindSafe(id). If the state id is not found in DB, it


returns DataStatus::Rejected

7. If all the checks have been successfully passed, it returns DataStatus::Accepted

Blockchain Security Analysis


SmartDec https://smartdec.net
49
Node::Processor::ApproveState(id)
The function checks the height and hash id with the corresponding parameters of the
Node::m_Cfg::m_ControlState state.

bool NodeProcessor::VerifyBlock(const
Block::BodyBase& block, TxBase::IReader&& r, const
HeightRange& hr)
The function calls Block::BodyBase::IsValid function from the
beam/core/block_crypt module.

Node::Processor::VerifyBlock(const Block::BodyBase&
block, TxBase::IReader&& r, const HeightRange& hr)
The function validates the block:
1. It receives the number of verification threads

2. If this number is equal to zero, it returns the NodeProcessor::VerifyBlock value

3. If the number is greater than zero, then the function works with the following object: v =
m_Verifier (Node::Processor::Verifier)

4. It holds its mutex

5. If the vector of the thread handlers is empty, it creates this vector

6. The function creates a block verification task and waits until it finishes

7. It returns !v.m_bFail && v.m_Context.IsValidBlock(block,


m_Extra.m_SubsidyOpen)

Verification in Node::Processor::Verifier
The thread function makes the checks in the thread:
1. It creates new std::unique_ptr<Verifier::MyBatch> p(new
Verifier::MyBatch)

2. It creates new Scope for p

3. The function holds mutex m_Mutex and waits for a new task

4. It releases the mutex

5. It creates TxBase::Context ctx based on the parameters passed with the


task

6. The function clones TxBase::IReader* into pR

7. It executes

Blockchain Security Analysis


SmartDec https://smartdec.net
50
8. bool bValid = ctx.ValidateAndSummarize(*m_pTx, std::move(*pR)) &&
p->Flush()

9. It holds mutex m_Mutex

10. If the check in the item 6 is valid and no other thread sets the value of shared variable
m_bFail to true, then the current context is merged with the general context

11. bValid = m_Context.Merge(ctx)

12. If bValid is false, then it sets the shared variable m_bFail to false. That means the
part of the context is invalid. Thus the whole task is marked as invalid

13. The function decrements the value of checks remained

14. It calls m_TaskFinished.notify_all () of the conditional variable

15. It finishes the task and releases the mutex

NodeProcessor::ImportMacroBlockInternal(
Block::BodyBase::IMacroReader& r)
The function imports and verifies the macroblock. It does not add the macroblock to the DB.
Node class in Node::ImportMacroblock function does it after successfull call of the
current function.
1. The function checks if the height of the macroblock beginning is greater than the height
of the cursor by one

2. It checks if the hash of the state is equal to the hash of the block previous to the current
macroblock

3. It checks if the maturity of the initial state’s kernel is not greater than the height of the
cursor

4. It fills Merkle::CompactMmr cmmr, cmmrKrn

5. If the cursor height is greater than the height of the genesis block, it fills cmmr with the
data from the DB

6. The function traverses all the headers and the prefixes in the macroblock and fills cmmr,
cmmrKrn (currently disordered), increases difficulty, validates every state and adds it
to the DB (it returns false if at least one state is invalid), checks if the received root
ccmrKrn corresponds to the state’s kernel at every step

7. It conducts a context-free validations of the block by the VerifyBlock function

8. It calls the HandleValidatedBlock function

9. The function compares the definition of the resulting state with the one gained from
cmmr

10. If definitions are different, it calls the HandleValidatedBlock function again with the
bFwd flag set to false

Blockchain Security Analysis


SmartDec https://smartdec.net
51
11. It traverses all the headers and the prefixes again

12. It sets the flags in the DB and updates the cursor

13. For each prefix it looks for the corresponding state in the DB

14. It removes block corresponding to the state (apparently, if the usual block was loaded
instead of the macroblock for this state)

15. The function places the kernel of the final state in the DB

16. It recognizes the UTXO of the owner using the RecognizeUtxos function

17. It updates parameters of the horizon and FossilHeight

18. It updates the cursor

NodeProcessor::ValidateTxWrtHeight(const
Transaction& tx,
Height h)
The function checks if the height of each transaction’s kernel is in the allowed range using
the function from beam/core/block_crypt.

NodeProcessor::ValidateTxContext(const Transaction&
tx)
The function validates the context of the transaction:
1. The function checks the transactions height relative to the cursor using the
NodeProcessor::ValidateTxWrtHeight function

2. It traverses all the inputs of the transaction

3. The traveller t is created; it supports the counter that will be used for the search of
every input’s UTXO

4. For the every input it is checked if there is no input with the same commitment in the
transaction

5. t is set in such a way that only UTXO with the commitment, same as the input’s one, is
searched among all the UTXO’s (with maturity set from 0 to the height of the
cursor + 1)

6. Traversing the UTXO’s tree it is determined if this UTXO exists

7. If all the transaction inputs pass all the checks, the function returns true

Blockchain Security Analysis


SmartDec https://smartdec.net
52
bool NodeProcessor::HandleBlock(const
NodeDB::StateID& sid, bool bFwd)
The function processes the block that has already been added to the DB and is currently on
the same line as sid.
1. The block parameters bbP, bbE, rbData are extracted from the DB

2. The state s is extracted from the DB

3. The block is deserialized

4. Hashed of all the kernels are extracted from the block body

5. If bFwd == true and there is no restore data (rbData.m_Buf is empty)


a) bFirstTime = true
b) The next difficulty on the cursor is calculated and compared with the difficulty s
c) The timestamp of s is checked to be not more than moving median
d) A Merkle Tree for the kernel’s hashes is created and its root is compared with the
root of the state's kernels
e) The block context-free validation is conducted by the VerifyBlock function

6. Otherwise, the block body is exported from rbData

7. The block is validated by the HandleValidatedBlock function and the result is written
to bOk

8. If bOk and bFirstTime == true


a) The current definition is taken and compared with the one from the state, otherwise,
bOk = false
b) If bOk == true
i. RollBackData based on the block is written to the DB
ii. The horizon is updated if it is larger than the current height by more than
Rules::get().MaxRollbackHeight

9. if bOk == true
a) If bFwd == true all the block kernels are added to the DB, otherwise, all the
kernels are removed from the DB
b) If bFwd == true all the block UTXO are recognised by the RecognizeUtxos
function, otherwise, all the events below the current height are removed

10. bOk is returned

Blockchain Security Analysis


SmartDec https://smartdec.net
53
Generation of a new block
The following functions are used for new blocks generation. The blocks are generated, but
not mined. This means that PoW is not calculated for them.
• NodeProcessor::get_NextDifficulty
• NodeProcessor::get_MovingMedian
• NodeProcessor::GenerateNewBlock

The NodeProcessor::get_NextDifficulty function is clear and does not require any


additional questions.

NodeProcessor::get_MovingMedian returns the median of timestamps among the last


states (the number of states is defined by the Rules class). For the calculation of the median
the elements are sorted and the median element is taken.

All the functions generating blocks imply that the block context with a transaction pool and
some block body will be passed to them (except for the one that creates a block with an empty
body). The data will be added to the block body and context. It is assumed that the class
calling this function will take the block header, filled block body and, thus, get an entire block.

size_t NodeProcessor::GenerateNewBlock(BlockContext&
bc, Block::Body& res, Height h)
The function fills the required block fields:

1. The function adds coinbase emission to res.subsidy (the total amount generated
by the block)

2. If the subsidy is open in the block context, the res.m_SubsidyClosing flag is set
to false

3. The scalar ECC::Scalar::Native sk is initialised

4. offset = res.m_Offset

5. UTXO from coinbase is generated and added with the key from bc (sk is rewritten)

6. sk = -sk

7. offset += sk

8. The transaction kernel is generated and added (sk is rewritten)

9. sk = -sk

10. offset += sk

11. The block is serialised and its size is checked to be consistent with the Rules; later
the block will be updated with UTXO fees and its size will be checked again

Blockchain Security Analysis


SmartDec https://smartdec.net
54
12. The function traverses through all the transactions from the pool and checks
a) If the fee is “gigantic”
b) If the fee overflows the bc.m_Fees variable
c) If the block size increased by the fee does not exceed the maximum
d) If the transaction can be placed in an empty block (the block size does not exceed
the maximum and includes only one input and only one kernel)
e) The transaction is validated and its height is calculated

13. If the transaction does not pass at least one check, it is eliminated from the transactions
pool

14. The transaction is written in the block body

15. The transaction and the fee are written in the block (only UTXO, without kernel)

16. The state of the cursor is set to the hash of previous block or 0 if the cursor is in
null position

17. If the subsidy closing flag is true then the ToggleSubsidyOpened function is called

18. The definition of block is set

19. The block body is normalised (its kernels are sorted) the fmmr object – the instance of
the Merkle::FlyMmr class – is filled by the kernels

20. The kernels’ hash in the block header is gained from the fmmr object

21. Item 17 is repeated (Why? The block body is only modified by normalization which does
not affect the subsidy flag)

22. The height of block header is set

23. The new difficulty taken from the cursor is set in the block header

24. The timestamp is set in the block header

25. The function increasing the difficulty based on ChainWork from the context and the
cursor are called (Why is the NodeProcessor::get_NextDifficulty function
required? Should it be called before generation of the block?)

26. The block timestamp is increased as it is required for timestamp to be not less than the
moving median ( the NodeProcessor::get_NextDifficulty function)

27. The block offset is set to offset

28. The serialized block size is returned

Blockchain Security Analysis


SmartDec https://smartdec.net
55
bool NodeProcessor::GenerateNewBlock(BlockContext&
bc, Block::Body& res, bool bInitiallyEmpty)
The function prepares the block for the previous function depending on whether or not res
contains any data:
1. The block height is calculated as the cursor height + 1

2. If bInitiallyEmpty == false, it checks whether the block body is valid or not

3. If bInitiallyEmpty == false, it checks all the transactions by calling the


HandleValidatedTx function

4. The previous function generating the block is called and the returned block size is saved

5. All the transactions are validated again by the HandleValidatedTx function

6. If the generation function returned 0, the function returns false

7. Maturity of all the inputs is reset to 0

8. P normalisation of the block is conducted


a) all inputs and outputs are sorted
b) inputs and outputs are in pairs compared by the TxBase::CmpInOut function
c) if it returns a negative result, the input and output are reset
d) vectors of inputs and outputs are rearranged without reset elements
e) the number of reset elements is returned

9. Cast::Down<Block::BodyBase>(res) and
Cast::Down<TxVectors::Perishable>(res) are serialized and the size is written
to bc.m_BodyP

10. Cast::Down<TxVectors::Ethernal>(res) is serialized and the size is written to


bc.m_BodyE

11. bc.m_BodyP.size() + bc.m_BodyE.size() < Rules::get().MaxBodySize


is returned

Subsidy mechanism
The enabled subsidy flag allows one to issue more than stated in coinbase emission. Initially,
this flag is set to true in the NodeProcessor::Extra function. If the block with the
m_SubsidyClosing flag is processed, then the ToggleSubsidyOpened function is called,
which sets the subsidy flag to the correct value in the NodeProcessor::Extra function.

Blockchain Security Analysis


SmartDec https://smartdec.net
56
ToggleSubsidyOpened()
The function switches the subsidy flag, which is initially set to true:
1. It searches for the UTXO with the invalid commitment

2. The m_Utxos.Find function sets the bCreate flag (probably it shows if the
corresponding UTXO was created)

3. It is checked if this flag’s value is equal to the current Extra::m_SubsidyOpen flag’s


value via assert and the value of the Extra::m_SubsidyOpen flag is set to the
opposite

4. If the subsidy is closed, the count of found UTXO is increased by 1, otherwise, this
UTXO is eliminated from the tree

In the HadleValidatedBlock function it is checked if the current flag is consistent with the
block and the passed argument. The function does not process the block if
body.m_SubsidyClosing && (m_Extra.m_SubsidyOpen != bFwd) (bFwd is false
only for the block with zero height).

According to the code calling the block handlers the open subsidy is available only for a
number of the initial blocks. As soon as a block with the m_SubsidyClosing flag is
generated, the subsidy must be switched off and not be able to become switched on again.

The switching of subsidy (i.e. the call of the ToggleSubsidyOpened function) is present
only in the HandleValidatedBlock and GenerateNewBlock functions.

Notes and recommendations


• Tests in the functionaltests directory do not compile.
• There is a TODO in the NodeDB::Transaction::Rollback function. Exceptions from
the database are not handled.
• In the the NodeProcessor::ProcessKrnMmr function if there is no transaction kernel
in the passed instance of TxBase::IReader, the value uint64_t (-1) is returned.
However, this value is not checked in the NodeProcessor::get_ProofKernel
function.
• In the NodeProcessor::SquashOnce function, which makes the convolution of the
vector of blocks, the trg value is used several times after the call of the
std::move(trg) function. The Block::Body class contains vectors that will be
empty after applying move to them.
• In the NodeProcessor::HandleBlockElement function, cu – cursor on the tree
elements – is used after the cu.Invalidate() call. In fact, the element pointed by the
cursor, and not the cursor itself, is invalidated. Perhaps, the developers should use
another method name, for example, Erase , in order to avoid confusion.

Blockchain Security Analysis


SmartDec https://smartdec.net
57
• The profit in TxPool::Profit is computed as m_nSize / m_Fee. It is counter-
intuitive, however, the developers prefer to maintain the current definition of the profit.
• In the NodeProcessor class the semantics of the bFwd flag, which is passed as an
argument to some methods, is not entirely clear.
• The GenerateNewBlock ToggleSubsidyOpen function is called twice. It is necessary
to check if it is needed.
• In the GenerateNewBlock function a specific function is called. It increases difficulty
based on ChainWork from the context and cursor. It is necessary to check if the
NodeProcessor::get_NextDifficulty function should be called prior to block
generation.
• The ImportMacroBlockInternal function invokes the
HandleValidatedBlock function twice.
• NodeProcessor::HandleBlockElement(const Input& v, Height h, const
Height* pHMax, bool bFwd) function:
It is necessary to compare the role of the count in UTXO with the protocol in order to
check if the double spending occurs. Moreover, it is necessary to compare the behavior
at differing bCreate and bFwd == false with the protocol. In general, manipulations
in the considered function are not fully clear, especially the values of the bFwd and
bCreate flags.

Blockchain Security Analysis


SmartDec https://smartdec.net
58
Overview of the implementation of the
Bulletproof algorithm in the BeamMV
project
The logic of the program was restored in order to verify if the implementation of the Bulletproof
method completes theoretical description.

The Bulletproof description was taken from the following article:


B. Bunz, et al, Bulletproofs: Short Proofs for Confidential Transactions and More
(https://eprint.iacr.org/2017/1066.pdf) .

Master branch as of 2018-11-21 of the BeamMW project was reviewed.

Next, a comparison between the restored Bulletproof and the Bulletproof described in the
article was made. It has been concluded that its implementation in the Beam source code
conforms to the algorithm as specified in the paper.

Comparison of B. Bunz's work with the source code

page 20 (61) lines 895 - 909

// m_TauX = tau2*x^2 + tau1*x


τx = τ2 ¨ x2 + τ1 ¨ x + z 2 ¨ γ P Zp
+ sk*z ^2

sk = gamma, random

page 20 (62) lines 789 - 805

µ = α + ρ ¨ x P Zp // m_Mu = alpha + ro*x

page 20 (65) lines 1046 - 1072

// H_Big * m_tDot + G * m_TauX


? z2 2
g t̂ hτx = V ¨ g δ(y,z) ¨ T1x ¨ T2x =?= commitment * z^2 + H_Big *
delta(y,z) + m_T1*x + m_T2*x^2

Blockchain Security Analysis


SmartDec https://smartdec.net
59
page 20 (66-67) lines 1073 - 1124

n +z 2 ¨2n // (P - m_Mu*G) + m_Mu*G =?=


P = A ¨ S x ¨ g ´z ¨ (h1 )z¨y PG
m_A + m_S*x - vec(G)*vec(z)

? + vec(H)*( vec(z) +
P = hµ ¨ g l ¨ (h1 )r
vec(z2̂*2n̂*y-̂n) )

Notes:
1. The second equality check was not found during the audit

2. The representation of h1 through h was not found

page 18 line 1022-1044

// calculate delta(y,z) = (z -
δ(y, z) = (z´z 2 )¨x1n , y n y´z 3 x1n , 2n y P Zp
z^2) * sumY - z^3 * sum2

page 19 lines 809 - 838

l(X) = (aL ´ z ¨ 1n ) + sL ¨ X // construct vectors l,r, use

r(X) = y n ˝ (aR + z ¨ 1n + sR ¨ X) + z 2 ¨ 2n buffers pS

page 19 lines 706 - 741

// calculate t1, t2 - parts of


t(X) = xl(X), r(X)y = t0 + t1 ¨ X + t2 ¨ X2 vec(L)*vec(R) which depend on
(future) x and x^2

page 20 (68) lines 1115 - 1123

?
t̂ = x1, ry P Zp

page 19 (47) lines 677 - 701

// S = G*ro + vec(sL)*vec(G) +
S = hρ g SL hSR P G
vec(sR)*vec(H)

Blockchain Security Analysis


SmartDec https://smartdec.net
60
page 19 (44) lines 642 - 675

$
α ÐÝ Zp // A = G*alpha

+ vec(aL)*vec(G) +
A = hα g al haR P G
vec(aR)*vec(H)

page 20 lines 746-768

Ti = g ti hτi P G T_i = g ^ (t_i) + p

Note: the code does not match the article.

page 20 lines 538-558

// The expression we're cal-


´n
h1 = hy culating is: the transformed
generator

Note: the purpose of bool RangeProof::Confidential::Recover is unclear.

Bulletproof interface
The prover calls the following function:

Create(const Scalar::Native& sk, const CreatorParams& cp, Or


acle& oracle)

sk is the secret previously chosen for Pedersen Commitment by the prover.

CoSign is called inside of Create (the variables are calculated and commits are made here).

Commits are saved in the RangeProof::Confidential structure that has the following
fields:
• structure m_Part1

1. m_A //A
2. m_S //S

• structure m_Part2

1. m_T1 //T1
2. m_T2 //T2

Blockchain Security Analysis


SmartDec https://smartdec.net
61
• structure m_Part3

1. m_TauX //τx
2. m_Mu //μ
3. m_tDot //t̂

The following values are public:


1. Scalar::Native pS[0] // l(x)

2. Scalar::Native pS[1] // r(x)

The verifier calls the following function:

RangeProof::Confidential::IsValid(const Point::Native& commi


tment, Oracle& oracle, InnerProduct::BatchContext& bc)

This analysis was performed by SmartDec.

January 22, 2019

Blockchain Security Analysis


SmartDec https://smartdec.net
62

Você também pode gostar