mirror of
https://github.com/Xahau/xahaud.git
synced 2026-07-27 09:00:09 +00:00
Merge branch 'master' of github.com:jedmccaleb/NewCoin
Conflicts: ripple2010.vcxproj.filters rippled-example.cfg
This commit is contained in:
10
.gitignore
vendored
10
.gitignore
vendored
@@ -9,6 +9,9 @@
|
||||
# Ignore python compiled files.
|
||||
*.pyc
|
||||
|
||||
# Ignore Macintosh Desktop Services Store files.
|
||||
.DS_Store
|
||||
|
||||
# Ignore backup/temps
|
||||
*~
|
||||
|
||||
@@ -25,8 +28,13 @@ tmp
|
||||
|
||||
# Ignore database directory.
|
||||
db/*.db
|
||||
db/*.db-journal
|
||||
db/*.db-*
|
||||
|
||||
# Ignore obj files
|
||||
Debug/*.*
|
||||
Release/*.*
|
||||
|
||||
# Ignore customized configs
|
||||
rippled.cfg
|
||||
validators.txt
|
||||
test/config.js
|
||||
|
||||
10
README
10
README
@@ -1,10 +0,0 @@
|
||||
Dependancies:
|
||||
- boost 1.47
|
||||
- Google protocol buffers 2.4.1
|
||||
- openssl
|
||||
|
||||
Sub modules:
|
||||
- websocketpp: https://github.com/zaphoyd/websocketpp
|
||||
- sjcl: https://github.com/bitwiseshiftleft/sjcl
|
||||
- cryptojs: https://github.com/gwjjeff/cryptojs
|
||||
aka http://code.google.com/p/crypto-js/
|
||||
19
README.md
Normal file
19
README.md
Normal file
@@ -0,0 +1,19 @@
|
||||
Ripple - P2P Payment Network
|
||||
============================
|
||||
|
||||
Some portions of this source code are currently closed source.
|
||||
|
||||
This is the repository for Ripple's:
|
||||
* rippled - Reference P2P network server
|
||||
* ripple.js - Reference JavaScript client libraries for node.js and browsers.
|
||||
|
||||
Build instructions:
|
||||
* https://ripple.com/wiki/Rippled_build_instructions
|
||||
* https://ripple.com/wiki/Ripple_JavaScript_library
|
||||
|
||||
Setup instructions:
|
||||
* https://ripple.com/wiki/Rippled_setup_instructions
|
||||
|
||||
For more information:
|
||||
* https://ripple.com
|
||||
* https://ripple.com/wiki
|
||||
19
SConstruct
19
SConstruct
@@ -75,10 +75,21 @@ else:
|
||||
]
|
||||
)
|
||||
|
||||
# Apparently, only linux uses -ldl
|
||||
if not FreeBSD:
|
||||
env.Append(
|
||||
LIBS = [
|
||||
'dl', # dynamic linking for linux
|
||||
]
|
||||
)
|
||||
|
||||
# Apparently, pkg-config --libs protobuf on bsd fails to provide this necessary include dir.
|
||||
if FreeBSD:
|
||||
env.Append(LINKFLAGS = ['-I/usr/local/include'])
|
||||
|
||||
env.Append(
|
||||
LIBS = [
|
||||
'protobuf',
|
||||
'dl', # dynamic linking
|
||||
'z'
|
||||
]
|
||||
)
|
||||
@@ -87,12 +98,12 @@ DEBUGFLAGS = ['-g', '-DDEBUG']
|
||||
BOOSTFLAGS = ['-DBOOST_TEST_DYN_LINK', '-DBOOST_FILESYSTEM_NO_DEPRECATED']
|
||||
|
||||
env.Append(LINKFLAGS = ['-rdynamic', '-pthread'])
|
||||
env.Append(CCFLAGS = ['-pthread', '-Wall', '-Wno-sign-compare', '-Wno-char-subscripts', '-DSQLITE_THREADSAFE'])
|
||||
env.Append(CCFLAGS = ['-pthread', '-Wall', '-Wno-sign-compare', '-Wno-char-subscripts', '-DSQLITE_THREADSAFE=1'])
|
||||
env.Append(CXXFLAGS = ['-O0', '-pthread', '-Wno-invalid-offsetof', '-Wformat']+BOOSTFLAGS+DEBUGFLAGS)
|
||||
|
||||
if OSX:
|
||||
env.Append(LINKFLAGS = ['-L/usr/local/Cellar/openssl/1.0.1c/lib'])
|
||||
env.Append(CXXFLAGS = ['-I/usr/local/Cellar/openssl/1.0.1c/include'])
|
||||
env.Append(LINKFLAGS = ['-L/usr/local/opt/openssl/lib'])
|
||||
env.Append(CXXFLAGS = ['-I/usr/local/opt/openssl/include'])
|
||||
|
||||
DB_SRCS = glob.glob('src/cpp/database/*.c') + glob.glob('src/cpp/database/*.cpp')
|
||||
JSON_SRCS = glob.glob('src/cpp/json/*.cpp')
|
||||
|
||||
18
bin/email_hash.js
Executable file
18
bin/email_hash.js
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/node
|
||||
//
|
||||
// Returns a Gravatar style hash as per: http://en.gravatar.com/site/implement/hash/
|
||||
//
|
||||
|
||||
if (3 != process.argv.length) {
|
||||
process.stderr.write("Usage: " + process.argv[1] + " email_address\n\nReturns gravatar style hash.\n");
|
||||
process.exit(1);
|
||||
|
||||
} else {
|
||||
var md5 = require('crypto').createHash('md5');
|
||||
|
||||
md5.update(process.argv[2].trim().toLowerCase());
|
||||
|
||||
process.stdout.write(md5.digest('hex') + "\n");
|
||||
}
|
||||
|
||||
// vim:sw=2:sts=2:ts=8:et
|
||||
31
bin/flash_policy.js
Executable file
31
bin/flash_policy.js
Executable file
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/node
|
||||
//
|
||||
// This program allows IE 9 ripple-clients to make websocket connections to
|
||||
// rippled using flash. As IE 9 does not have websocket support, this required
|
||||
// if you wish to support IE 9 ripple-clients.
|
||||
//
|
||||
// http://www.lightsphere.com/dev/articles/flash_socket_policy.html
|
||||
//
|
||||
// For better security, be sure to set the Port below to the port of your
|
||||
// [websocket_public_port].
|
||||
//
|
||||
|
||||
var net = require("net"),
|
||||
port = "*",
|
||||
domains = ["*:"+port]; // Domain:Port
|
||||
|
||||
net.createServer(
|
||||
function(socket) {
|
||||
socket.write("<?xml version='1.0' ?>\n");
|
||||
socket.write("<!DOCTYPE cross-domain-policy SYSTEM 'http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd'>\n");
|
||||
socket.write("<cross-domain-policy>\n");
|
||||
domains.forEach(
|
||||
function(domain) {
|
||||
var parts = domain.split(':');
|
||||
socket.write("\t<allow-access-from domain='" + parts[0] + "' to-ports='" + parts[1] + "' />\n");
|
||||
}
|
||||
);
|
||||
socket.write("</cross-domain-policy>\n");
|
||||
socket.end();
|
||||
}
|
||||
).listen(843);
|
||||
23
bin/hexify.js
Executable file
23
bin/hexify.js
Executable file
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/node
|
||||
//
|
||||
// Returns hex of lowercasing a string.
|
||||
//
|
||||
|
||||
var stringToHex = function (s) {
|
||||
return Array.prototype.map.call(s, function (c) {
|
||||
var b = c.charCodeAt(0);
|
||||
|
||||
return b < 16 ? "0" + b.toString(16) : b.toString(16);
|
||||
}).join("");
|
||||
};
|
||||
|
||||
if (3 != process.argv.length) {
|
||||
process.stderr.write("Usage: " + process.argv[1] + " string\n\nReturns hex of lowercasing string.\n");
|
||||
process.exit(1);
|
||||
|
||||
} else {
|
||||
|
||||
process.stdout.write(stringToHex(process.argv[2].toLowerCase()) + "\n");
|
||||
}
|
||||
|
||||
// vim:sw=2:sts=2:ts=8:et
|
||||
@@ -1,149 +0,0 @@
|
||||
#
|
||||
# Sample newcoind.cfg
|
||||
#
|
||||
# This file should be named newcoind.cfg. This file is UTF-8 with Dos, UNIX,
|
||||
# or Mac style end of lines. Blank lines and lines beginning with '#' are
|
||||
# ignored. Undefined sections are reserved. No escapes are currently defined.
|
||||
#
|
||||
# When you launch newcoind, it will attempt to find this file.
|
||||
#
|
||||
# --conf=<path>:
|
||||
# You may specify the location of this file with --conf=<path>. The config
|
||||
# directory is the directory containing this file. The data directory is a
|
||||
# the subdirectory named "dbs".
|
||||
#
|
||||
# Windows and no --conf:
|
||||
# The config directory is the same directory as the newcoind program. The
|
||||
# data directory is a the subdirectory named "dbs".
|
||||
#
|
||||
# Other OSes and no --conf:
|
||||
# This file will be looked for in these places in the following order:
|
||||
# ./newcoind.cfg
|
||||
# $XDG_CONFIG_HOME/newcoin/newcoind.cfg
|
||||
#
|
||||
# If newcoind.cfg, is found in the current working directory, the directory
|
||||
# will be used as the config directory. The data directory is a the
|
||||
# subdirectory named "dbs".
|
||||
#
|
||||
# Otherwise, the data directory data is:
|
||||
# $XDG_DATA_HOME/newcoin/
|
||||
#
|
||||
# Note: $XDG_CONFIG_HOME defaults to $HOME/.config
|
||||
# $XDG_DATA_HOME defaults to $HOME/.local/share
|
||||
#
|
||||
# [debug_logfile]
|
||||
# Specifies were a debug logfile is kept. By default, no debug log is kept
|
||||
#
|
||||
# Example: debug.log
|
||||
#
|
||||
# [validators_site]:
|
||||
# Specifies where to find validators.txt for UNL boostrapping and RPC command unl_network.
|
||||
# During alpha testing, this defaults to: redstem.com
|
||||
#
|
||||
# Example: newcoin.org
|
||||
#
|
||||
# [unl_default]:
|
||||
# XXX This should be called: [validators_file]
|
||||
# Specifies how to bootstrap the UNL list. The UNL list is based on a
|
||||
# validators.txt file and is maintained in the databases. When newcoind
|
||||
# starts up, if the databases are missing or are obsolete due to an upgrade
|
||||
# of newcoind, newcoind will reconstruct the UNL list as specified here.
|
||||
#
|
||||
# If this entry is not present or empty, newcoind will look for a validators.txt in the
|
||||
# config directory. If not found there, it will attempt to retrieve the file
|
||||
# from the newcoin foundation's web site.
|
||||
#
|
||||
# This entry is also used by the RPC command unl_load.
|
||||
#
|
||||
# Specify the file by specifying its full path.
|
||||
#
|
||||
# Examples:
|
||||
# C:/home/johndoe/newcoin/validators.txt
|
||||
# /home/johndoe/newcoin/validators.txt
|
||||
#
|
||||
# [validators]:
|
||||
# Only valid in "newcoind.cfg", "newcoin.txt", and the referered [validators_url].
|
||||
# List of nodes to accept as validators speficied by public key or domain.
|
||||
#
|
||||
# For domains, newcoind will probe for https web servers at the specied
|
||||
# domain in the following order: newcoin.DOMAIN, www.DOMAIN, DOMAIN
|
||||
#
|
||||
# Examples:
|
||||
# redstem.com
|
||||
# n9KorY8QtTdRx7TVDpwnG9NvyxsDwHUKUEeDLY3AkiGncVaSXZi5
|
||||
# n9MqiExBcoG19UXwoLjBJnhsxEhAZMuWwJDRdkyDz1EkEkwzQTNt John Doe
|
||||
#
|
||||
# [ips]:
|
||||
# Only valid in "newcoind.cfg", "newcoin.txt", and the referered [ips_url].
|
||||
# List of ips where the Newcoin protocol is avialable.
|
||||
# One ipv4 or ipv6 address per line.
|
||||
# A port may optionally be specified after adding a space to the address.
|
||||
# By convention, if known, IPs are listed in from most to least trusted.
|
||||
#
|
||||
# Examples:
|
||||
# 192.168.0.1
|
||||
# 192.168.0.1 3939
|
||||
# 2001:0db8:0100:f101:0210:a4ff:fee3:9566
|
||||
#
|
||||
# [peer_ip]:
|
||||
# IP address or domain to bind to allow external connections from peers.
|
||||
# Defaults to not allow external connections from peers.
|
||||
#
|
||||
# Examples: 0.0.0.0 - Bind on all interfaces.
|
||||
#
|
||||
# [peer_port]:
|
||||
# Port to bind to allow external connections from peers.
|
||||
#
|
||||
# [rpc_ip]:
|
||||
# IP address or domain to bind to allow insecure RPC connections.
|
||||
# Defaults to not allow RPC connections.
|
||||
#
|
||||
# [rpc_port]:
|
||||
# Port to bind to if allowing insecure RPC connections.
|
||||
#
|
||||
# [rpc_allow_remote]:
|
||||
# 0 or 1. 0 only allows RPC connections from 127.0.0.1. [default 0]
|
||||
#
|
||||
# [websocket_ip]:
|
||||
# IP address or domain to bind to allow client connections.
|
||||
#
|
||||
# Examples: 0.0.0.0 - Bind on all interfaces.
|
||||
# 127.0.0.1 - Bind on localhost interface. Only local programs may connect.
|
||||
#
|
||||
# [websocket_port]:
|
||||
# Port to bind to allow client connections.
|
||||
#
|
||||
# [validation_seed]:
|
||||
# To perform validation, this section should contain either a validation seed or key.
|
||||
# The validation seed is used to generate the validation public/private key pair.
|
||||
# To obtain a validation seed, use the validation_create command.
|
||||
#
|
||||
# Examples: RASH BUSH MILK LOOK BAD BRIM AVID GAFF BAIT ROT POD LOVE
|
||||
# shfArahZT9Q9ckTf3s1psJ7C7qzVN
|
||||
#
|
||||
|
||||
[peer_ip]
|
||||
0.0.0.0
|
||||
|
||||
[peer_port]
|
||||
51235
|
||||
|
||||
[rpc_ip]
|
||||
127.0.0.1
|
||||
|
||||
[rpc_port]
|
||||
5005
|
||||
|
||||
[rpc_allow_remote]
|
||||
1
|
||||
|
||||
[debug_logfile]
|
||||
debug.log
|
||||
|
||||
[unl_default]
|
||||
validators.txt
|
||||
|
||||
[ips]
|
||||
23.21.167.100 51235
|
||||
23.23.201.55 51235
|
||||
107.21.116.214 51235
|
||||
@@ -1,10 +1,10 @@
|
||||
Name "CoinToss"
|
||||
Name "Rippled"
|
||||
|
||||
; The file to write
|
||||
OutFile "toss install.exe"
|
||||
OutFile "ripple install.exe"
|
||||
|
||||
; The default installation directory
|
||||
InstallDir "$PROGRAMFILES\CoinToss"
|
||||
InstallDir "$PROGRAMFILES\Rippled"
|
||||
|
||||
; Request application privileges for Windows Vista
|
||||
RequestExecutionLevel user
|
||||
@@ -25,12 +25,12 @@ Section "" ;No components page, name is not important
|
||||
SetOutPath $INSTDIR
|
||||
|
||||
; Put file there
|
||||
File ..\Release\newcoin.exe
|
||||
File ..\Release\rippled.exe
|
||||
File ..\*.dll
|
||||
File "start CoinToss.bat"
|
||||
File newcoind.cfg
|
||||
;File "start rippled.bat"
|
||||
File rippled.cfg
|
||||
File validators.txt
|
||||
File /r /x .git ..\..\nc-client\*.*
|
||||
;File /r /x .git ..\..\nc-client\*.*
|
||||
|
||||
CreateDirectory $INSTDIR\db
|
||||
|
||||
76
grunt.js
Normal file
76
grunt.js
Normal file
@@ -0,0 +1,76 @@
|
||||
module.exports = function(grunt) {
|
||||
grunt.loadNpmTasks('grunt-webpack');
|
||||
|
||||
grunt.initConfig({
|
||||
pkg: '<json:package.json>',
|
||||
meta: {
|
||||
banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' +
|
||||
'<%= grunt.template.today("yyyy-mm-dd") %>\n' +
|
||||
'<%= pkg.homepage ? "* " + pkg.homepage + "\n" : "" %>' +
|
||||
'* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' +
|
||||
' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */'
|
||||
},
|
||||
concat: {
|
||||
sjcl: {
|
||||
src: [
|
||||
"src/js/sjcl/core/sjcl.js",
|
||||
"src/js/sjcl/core/aes.js",
|
||||
"src/js/sjcl/core/bitArray.js",
|
||||
"src/js/sjcl/core/codecString.js",
|
||||
"src/js/sjcl/core/codecHex.js",
|
||||
"src/js/sjcl/core/codecBase64.js",
|
||||
"src/js/sjcl/core/codecBytes.js",
|
||||
"src/js/sjcl/core/sha256.js",
|
||||
"src/js/sjcl/core/sha512.js",
|
||||
"src/js/sjcl/core/sha1.js",
|
||||
"src/js/sjcl/core/ccm.js",
|
||||
// "src/js/sjcl/core/cbc.js",
|
||||
// "src/js/sjcl/core/ocb2.js",
|
||||
"src/js/sjcl/core/hmac.js",
|
||||
"src/js/sjcl/core/pbkdf2.js",
|
||||
"src/js/sjcl/core/random.js",
|
||||
"src/js/sjcl/core/convenience.js",
|
||||
"src/js/sjcl/core/bn.js",
|
||||
"src/js/sjcl/core/ecc.js",
|
||||
"src/js/sjcl/core/srp.js"
|
||||
],
|
||||
dest: 'build/sjcl.js'
|
||||
}
|
||||
},
|
||||
webpack: {
|
||||
lib: {
|
||||
src: "src/js/index.js",
|
||||
dest: "build/ripple-<%= pkg.version %>.js",
|
||||
libary: "ripple", // misspelling fixed in later versions of webpack
|
||||
library: "ripple"
|
||||
},
|
||||
lib_debug: {
|
||||
src: "src/js/index.js",
|
||||
dest: "build/ripple-<%= pkg.version %>-debug.js",
|
||||
libary: "ripple", // misspelling fixed in later versions of webpack
|
||||
library: "ripple",
|
||||
debug: true
|
||||
},
|
||||
lib_min: {
|
||||
src: "src/js/index.js",
|
||||
dest: "build/ripple-<%= pkg.version %>-min.js",
|
||||
libary: "ripple", // misspelling fixed in later versions of webpack
|
||||
library: "ripple",
|
||||
minimize: true
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
sjcl: {
|
||||
files: ['<config:concat.sjcl.src>'],
|
||||
tasks: 'concat:sjcl'
|
||||
},
|
||||
lib: {
|
||||
files: 'src/js/*.js',
|
||||
tasks: 'webpack'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Tasks
|
||||
grunt.registerTask('default', 'concat:sjcl webpack');
|
||||
};
|
||||
@@ -41,9 +41,11 @@
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<TargetName>rippled</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<TargetName>rippled</TargetName>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
@@ -76,14 +78,14 @@
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>BOOST_TEST_ALTERNATIVE_INIT_API;BOOST_TEST_NO_MAIN;_CRT_SECURE_NO_WARNINGS;_WIN32_WINNT=0x0501;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>..\OpenSSL\include;..\boost_1_47_0;..\protobuf-2.4.1\src</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>.\;..\OpenSSL\include;..\boost_1_52_0;..\protobuf\src</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalLibraryDirectories>..\OpenSSL\lib\VC;..\boost_1_47_0\stage\lib;..\protobuf-2.4.1\vsprojects\Release</AdditionalLibraryDirectories>
|
||||
<AdditionalLibraryDirectories>..\OpenSSL\lib\VC;..\boost_1_52_0\stage\lib;..\protobuf\vsprojects\Release</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>libprotobuf.lib;ssleay32MD.lib;libeay32MD.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
@@ -91,7 +93,6 @@
|
||||
<ClCompile Include="src\cpp\database\database.cpp" />
|
||||
<ClCompile Include="src\cpp\database\sqlite3.c" />
|
||||
<ClCompile Include="src\cpp\database\SqliteDatabase.cpp" />
|
||||
<ClCompile Include="src\cpp\database\win\windatabase.cpp" />
|
||||
<ClCompile Include="src\cpp\json\json_reader.cpp" />
|
||||
<ClCompile Include="src\cpp\json\json_value.cpp" />
|
||||
<ClCompile Include="src\cpp\json\json_writer.cpp" />
|
||||
@@ -145,7 +146,6 @@
|
||||
<ClCompile Include="src\cpp\ripple\PeerDoor.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\PlatRand.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\ProofOfWork.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\PubKeyCache.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\RangeSet.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\RegularKeySetTransactor.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\rfc1751.cpp" />
|
||||
@@ -158,6 +158,7 @@
|
||||
<ClCompile Include="src\cpp\ripple\RPCErr.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\RPCHandler.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\RPCServer.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\RPCSub.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\ScriptData.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\SerializedLedger.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\SerializedObject.cpp" />
|
||||
@@ -177,6 +178,7 @@
|
||||
<ClCompile Include="src\cpp\ripple\TransactionFormats.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\TransactionMaster.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\TransactionMeta.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\TransactionQueue.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\Transactor.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\TrustSetTransactor.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\UniqueNodeList.cpp" />
|
||||
@@ -250,7 +252,6 @@
|
||||
<ClInclude Include="src\cpp\ripple\Peer.h" />
|
||||
<ClInclude Include="src\cpp\ripple\PeerDoor.h" />
|
||||
<ClInclude Include="src\cpp\ripple\ProofOfWork.h" />
|
||||
<ClInclude Include="src\cpp\ripple\PubKeyCache.h" />
|
||||
<ClInclude Include="src\cpp\ripple\RangeSet.h" />
|
||||
<ClInclude Include="src\cpp\ripple\RegularKeySetTransactor.h" />
|
||||
<ClInclude Include="src\cpp\ripple\rfc1751.h" />
|
||||
@@ -284,6 +285,7 @@
|
||||
<ClInclude Include="src\cpp\ripple\TransactionFormats.h" />
|
||||
<ClInclude Include="src\cpp\ripple\TransactionMaster.h" />
|
||||
<ClInclude Include="src\cpp\ripple\TransactionMeta.h" />
|
||||
<ClInclude Include="src\cpp\ripple\TransactionQueue.h" />
|
||||
<ClInclude Include="src\cpp\ripple\Transactor.h" />
|
||||
<ClInclude Include="src\cpp\ripple\TrustSetTransactor.h" />
|
||||
<ClInclude Include="src\cpp\ripple\types.h" />
|
||||
@@ -320,8 +322,10 @@
|
||||
<None Include="SConstruct" />
|
||||
<CustomBuild Include="src\cpp\ripple\ripple.proto">
|
||||
<FileType>Document</FileType>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">/code/protobuf/protoc -I=..\newcoin --cpp_out=\code\newcoin\ ..\newcoin/src/cpp/ripple/ripple.proto</Command>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"../protobuf/protoc" -I=..\newcoin --cpp_out=..\newcoin\ ..\newcoin/src/cpp/ripple/ripple.proto</Command>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">\code\newcoin\src\ripple.pb.h</Outputs>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">/code/protobuf/protoc -I=..\newcoin --cpp_out=\code\newcoin\ ..\newcoin/src/cpp/ripple/ripple.proto</Command>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">\code\newcoin\src\ripple.pb.h</Outputs>
|
||||
</CustomBuild>
|
||||
<None Include="test\buster.js" />
|
||||
<None Include="test\server.js" />
|
||||
|
||||
@@ -42,9 +42,6 @@
|
||||
<ClCompile Include="src\cpp\database\SqliteDatabase.cpp">
|
||||
<Filter>Source Files\database</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\cpp\database\win\windatabase.cpp">
|
||||
<Filter>Source Files\database</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\cpp\json\json_reader.cpp">
|
||||
<Filter>Source Files\json</Filter>
|
||||
</ClCompile>
|
||||
@@ -177,9 +174,6 @@
|
||||
<ClCompile Include="src\cpp\ripple\PlatRand.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\cpp\ripple\PubKeyCache.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\cpp\ripple\RangeSet.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
@@ -264,6 +258,9 @@
|
||||
<ClCompile Include="src\cpp\ripple\TransactionMeta.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\cpp\ripple\TransactionQueue.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\cpp\ripple\UniqueNodeList.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
@@ -360,6 +357,9 @@
|
||||
<ClCompile Include="src\cpp\ripple\LoadManager.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\cpp\ripple\RPCSub.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="util\pugiconfig.hpp">
|
||||
@@ -506,9 +506,6 @@
|
||||
<ClInclude Include="src\cpp\ripple\ProofOfWork.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\cpp\ripple\PubKeyCache.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\cpp\ripple\RangeSet.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
@@ -605,6 +602,9 @@
|
||||
<ClInclude Include="src\cpp\ripple\TransactionMeta.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\cpp\ripple\TransactionQueue.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\cpp\ripple\types.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
||||
51
newcoin2012.sln
Normal file
51
newcoin2012.sln
Normal file
@@ -0,0 +1,51 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 2012
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "newcoin", "newcoin.vcxproj", "{19465545-42EE-42FA-9CC8-F8975F8F1CC7}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{3E283F37-A4ED-41B7-A3E6-A2D89D131A30} = {3E283F37-A4ED-41B7-A3E6-A2D89D131A30}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libprotobuf", "..\protobuf\vsprojects\libprotobuf.vcxproj", "{3E283F37-A4ED-41B7-A3E6-A2D89D131A30}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Debug|x64 = Debug|x64
|
||||
FlashDebug|Win32 = FlashDebug|Win32
|
||||
FlashDebug|x64 = FlashDebug|x64
|
||||
Release|Win32 = Release|Win32
|
||||
Release|x64 = Release|x64
|
||||
ReleaseD|Win32 = ReleaseD|Win32
|
||||
ReleaseD|x64 = ReleaseD|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{19465545-42EE-42FA-9CC8-F8975F8F1CC7}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{19465545-42EE-42FA-9CC8-F8975F8F1CC7}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{19465545-42EE-42FA-9CC8-F8975F8F1CC7}.Debug|x64.ActiveCfg = Debug|Win32
|
||||
{19465545-42EE-42FA-9CC8-F8975F8F1CC7}.FlashDebug|Win32.ActiveCfg = Debug|Win32
|
||||
{19465545-42EE-42FA-9CC8-F8975F8F1CC7}.FlashDebug|Win32.Build.0 = Debug|Win32
|
||||
{19465545-42EE-42FA-9CC8-F8975F8F1CC7}.FlashDebug|x64.ActiveCfg = Debug|Win32
|
||||
{19465545-42EE-42FA-9CC8-F8975F8F1CC7}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{19465545-42EE-42FA-9CC8-F8975F8F1CC7}.Release|Win32.Build.0 = Release|Win32
|
||||
{19465545-42EE-42FA-9CC8-F8975F8F1CC7}.Release|x64.ActiveCfg = Release|Win32
|
||||
{19465545-42EE-42FA-9CC8-F8975F8F1CC7}.ReleaseD|Win32.ActiveCfg = Release|Win32
|
||||
{19465545-42EE-42FA-9CC8-F8975F8F1CC7}.ReleaseD|Win32.Build.0 = Release|Win32
|
||||
{19465545-42EE-42FA-9CC8-F8975F8F1CC7}.ReleaseD|x64.ActiveCfg = Release|Win32
|
||||
{3E283F37-A4ED-41B7-A3E6-A2D89D131A30}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{3E283F37-A4ED-41B7-A3E6-A2D89D131A30}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{3E283F37-A4ED-41B7-A3E6-A2D89D131A30}.Debug|x64.ActiveCfg = Debug|Win32
|
||||
{3E283F37-A4ED-41B7-A3E6-A2D89D131A30}.FlashDebug|Win32.ActiveCfg = Debug|Win32
|
||||
{3E283F37-A4ED-41B7-A3E6-A2D89D131A30}.FlashDebug|Win32.Build.0 = Debug|Win32
|
||||
{3E283F37-A4ED-41B7-A3E6-A2D89D131A30}.FlashDebug|x64.ActiveCfg = Debug|Win32
|
||||
{3E283F37-A4ED-41B7-A3E6-A2D89D131A30}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{3E283F37-A4ED-41B7-A3E6-A2D89D131A30}.Release|Win32.Build.0 = Release|Win32
|
||||
{3E283F37-A4ED-41B7-A3E6-A2D89D131A30}.Release|x64.ActiveCfg = Release|Win32
|
||||
{3E283F37-A4ED-41B7-A3E6-A2D89D131A30}.ReleaseD|Win32.ActiveCfg = Release|Win32
|
||||
{3E283F37-A4ED-41B7-A3E6-A2D89D131A30}.ReleaseD|Win32.Build.0 = Release|Win32
|
||||
{3E283F37-A4ED-41B7-A3E6-A2D89D131A30}.ReleaseD|x64.ActiveCfg = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -8,11 +8,12 @@
|
||||
"dependencies": {
|
||||
"async": "~0.1.22",
|
||||
"ws": "~0.4.22",
|
||||
"extend": "~1.1.1"
|
||||
"extend": "~1.1.1",
|
||||
"simple-jsonrpc": "~0.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"buster": "~0.6.2",
|
||||
"webpack": "~0.7.17"
|
||||
"grunt-webpack": "~0.4.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "buster test"
|
||||
|
||||
@@ -96,13 +96,6 @@
|
||||
# [currencies]:
|
||||
# This section allows a site to declare currencies it currently issues.
|
||||
#
|
||||
# [ledger_history]:
|
||||
# To serve clients, servers need historical ledger data. This sets the number of
|
||||
# past ledgers to acquire on server startup and the minimum to maintain while
|
||||
# running. Servers that don't need to serve clients can set this to "none" or "off".
|
||||
# Servers that want complete history can set this to "full" or "on".
|
||||
# The default is 256 ledgers.
|
||||
|
||||
|
||||
[validation_public_key]
|
||||
n9MZTnHe5D5Q2cgE8oV2usFwRqhUvEA8MwP5Mu1XVD6TxmssPRev
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalLibraryDirectories>..\OpenSSL\lib\VC;..\boost_1_52_0\stage\lib;..\protobuf\vsprojects\Debug</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>ssleay32MDd.lib;libeay32MTd.lib;libprotobuf.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<OutputFile>$(OutDir)rippled.exe</OutputFile>
|
||||
</Link>
|
||||
<PreBuildEvent>
|
||||
<Command>
|
||||
@@ -81,15 +82,15 @@
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalLibraryDirectories>..\OpenSSL\lib\VC;..\boost_1_47_0\stage\lib;..\protobuf-2.4.1\vsprojects\Release</AdditionalLibraryDirectories>
|
||||
<AdditionalLibraryDirectories>..\OpenSSL\lib\VC;..\boost_1_52_0\stage\lib;..\protobuf\vsprojects\Release</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>libprotobuf.lib;ssleay32MD.lib;libeay32MD.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<OutputFile>$(OutDir)rippled.exe</OutputFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src\cpp\database\database.cpp" />
|
||||
<ClCompile Include="src\cpp\database\sqlite3.c" />
|
||||
<ClCompile Include="src\cpp\database\SqliteDatabase.cpp" />
|
||||
<ClCompile Include="src\cpp\database\win\windatabase.cpp" />
|
||||
<ClCompile Include="src\cpp\json\json_reader.cpp" />
|
||||
<ClCompile Include="src\cpp\json\json_value.cpp" />
|
||||
<ClCompile Include="src\cpp\json\json_writer.cpp" />
|
||||
@@ -144,7 +145,6 @@
|
||||
<ClCompile Include="src\cpp\ripple\PeerDoor.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\PlatRand.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\ProofOfWork.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\PubKeyCache.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\RangeSet.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\RegularKeySetTransactor.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\rfc1751.cpp" />
|
||||
@@ -157,6 +157,7 @@
|
||||
<ClCompile Include="src\cpp\ripple\RPCErr.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\RPCHandler.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\RPCServer.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\RPCSub.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\ScriptData.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\SerializedLedger.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\SerializedObject.cpp" />
|
||||
@@ -176,6 +177,7 @@
|
||||
<ClCompile Include="src\cpp\ripple\TransactionFormats.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\TransactionMaster.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\TransactionMeta.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\TransactionQueue.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\Transactor.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\TrustSetTransactor.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\UniqueNodeList.cpp" />
|
||||
@@ -242,7 +244,6 @@
|
||||
<ClInclude Include="src\cpp\ripple\Peer.h" />
|
||||
<ClInclude Include="src\cpp\ripple\PeerDoor.h" />
|
||||
<ClInclude Include="src\cpp\ripple\ProofOfWork.h" />
|
||||
<ClInclude Include="src\cpp\ripple\PubKeyCache.h" />
|
||||
<ClInclude Include="src\cpp\ripple\RangeSet.h" />
|
||||
<ClInclude Include="src\cpp\ripple\rfc1751.h" />
|
||||
<ClInclude Include="src\cpp\ripple\ripple.pb.h" />
|
||||
@@ -276,6 +277,7 @@
|
||||
<ClInclude Include="src\cpp\ripple\TransactionFormats.h" />
|
||||
<ClInclude Include="src\cpp\ripple\TransactionMaster.h" />
|
||||
<ClInclude Include="src\cpp\ripple\TransactionMeta.h" />
|
||||
<ClInclude Include="src\cpp\ripple\TransactionQueue.h" />
|
||||
<ClInclude Include="src\cpp\ripple\types.h" />
|
||||
<ClInclude Include="src\cpp\ripple\uint256.h" />
|
||||
<ClInclude Include="src\cpp\ripple\UniqueNodeList.h" />
|
||||
@@ -299,8 +301,8 @@
|
||||
<None Include="SConstruct" />
|
||||
<CustomBuild Include="src\cpp\ripple\ripple.proto">
|
||||
<FileType>Document</FileType>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">/code/protobuf/protoc -I=..\newcoin --cpp_out=\code\newcoin\ ..\newcoin/src/cpp/ripple/ripple.proto</Command>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">\code\newcoin\src\ripple.pb.h</Outputs>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"../protobuf/protoc" -I=..\newcoin --cpp_out=..\newcoin\ ..\newcoin/src/cpp/ripple/ripple.proto</Command>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\newcoin\src\ripple.pb.h</Outputs>
|
||||
</CustomBuild>
|
||||
<None Include="test\buster.js" />
|
||||
<None Include="test\server.js" />
|
||||
|
||||
@@ -39,9 +39,6 @@
|
||||
<ClCompile Include="src\cpp\database\SqliteDatabase.cpp">
|
||||
<Filter>Source Files\database</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\cpp\database\win\windatabase.cpp">
|
||||
<Filter>Source Files\database</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\cpp\json\json_reader.cpp">
|
||||
<Filter>Source Files\json</Filter>
|
||||
</ClCompile>
|
||||
@@ -174,9 +171,6 @@
|
||||
<ClCompile Include="src\cpp\ripple\PlatRand.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\cpp\ripple\PubKeyCache.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\cpp\ripple\RangeSet.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
@@ -261,6 +255,9 @@
|
||||
<ClCompile Include="src\cpp\ripple\TransactionMeta.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\cpp\ripple\TransactionQueue.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\cpp\ripple\UniqueNodeList.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
@@ -345,16 +342,16 @@
|
||||
<ClCompile Include="src\cpp\ripple\RPCErr.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\cpp\ripple\LoadManager.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\cpp\ripple\AccountItems.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\cpp\ripple\Offer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\cpp\ripple\FeatureTable.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\cpp\ripple\LoadManager.cpp">
|
||||
<ClCompile Include="src\cpp\ripple\RPCSub.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
@@ -503,9 +500,6 @@
|
||||
<ClInclude Include="src\cpp\ripple\ProofOfWork.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\cpp\ripple\PubKeyCache.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\cpp\ripple\RangeSet.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
@@ -605,6 +599,9 @@
|
||||
<ClInclude Include="src\cpp\ripple\TransactionMeta.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\cpp\ripple\TransactionQueue.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\cpp\ripple\types.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
@@ -642,14 +639,13 @@
|
||||
<Filter>html</Filter>
|
||||
</None>
|
||||
<None Include="SConstruct" />
|
||||
<None Include="newcoind.cfg" />
|
||||
<None Include="validators.txt" />
|
||||
<None Include="README" />
|
||||
<None Include="test\buster.js" />
|
||||
<None Include="test\server.js" />
|
||||
<None Include="test\standalone-test.js" />
|
||||
<None Include="test\utils.js" />
|
||||
<None Include="rippled-example.cfg" />
|
||||
<None Include="ripple-example.txt" />
|
||||
<None Include="validators-example.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CustomBuild Include="src\cpp\ripple\ripple.proto" />
|
||||
|
||||
@@ -3,35 +3,47 @@
|
||||
#
|
||||
# This file contains configuration information for rippled.
|
||||
#
|
||||
# This file should be named rippled.cfg. This file is UTF-8 with Dos, UNIX,
|
||||
# or Mac style end of lines. Blank lines and lines beginning with '#' are
|
||||
# Rippled when launched attempts to find this file. For details, refer to the
|
||||
# wiki page for --conf command line option:
|
||||
# https://ripple.com/wiki/Rippled#--conf.3Dpath
|
||||
#
|
||||
# This file should be named rippled.cfg. This file is UTF-8 with Dos, UNIX, or
|
||||
# Mac style end of lines. Blank lines and lines beginning with '#' are
|
||||
# ignored. Undefined sections are reserved. No escapes are currently defined.
|
||||
#
|
||||
# When you launch rippled, it will attempt to find this file. For details,
|
||||
# refer to the manual page for --conf command line option.
|
||||
#
|
||||
# [debug_logfile]
|
||||
# Specifies were a debug logfile is kept. By default, no debug log is kept
|
||||
# Specifies were a debug logfile is kept. By default, no debug log is kept.
|
||||
# Unless absolute, the path is relative the directory containing this file.
|
||||
#
|
||||
# Example: debug.log
|
||||
#
|
||||
# [validators_site]:
|
||||
# Specifies where to find validators.txt for UNL boostrapping and RPC command unl_network.
|
||||
# During alpha testing, this defaults to: redstem.com
|
||||
# [validators]:
|
||||
# List of nodes to always accept as validators. Nodes are specified by domain
|
||||
# or public key.
|
||||
#
|
||||
# Example: ripple.com
|
||||
# For domains, rippled will probe for https web servers at the specified
|
||||
# domain in the following order: ripple.DOMAIN, www.DOMAIN, DOMAIN
|
||||
#
|
||||
# For public key entries, a comment may optionally be spcified after adding a
|
||||
# space to the pulic key.
|
||||
#
|
||||
# Examples:
|
||||
# ripple.com
|
||||
# n9KorY8QtTdRx7TVDpwnG9NvyxsDwHUKUEeDLY3AkiGncVaSXZi5
|
||||
# n9MqiExBcoG19UXwoLjBJnhsxEhAZMuWwJDRdkyDz1EkEkwzQTNt John Doe
|
||||
#
|
||||
# [validators_file]:
|
||||
# Specifies how to bootstrap the UNL list. The UNL list is based on a
|
||||
# validators.txt file and is maintained in the databases. When rippled
|
||||
# starts up, if the databases are missing or are obsolete due to an upgrade
|
||||
# of rippled, rippled will reconstruct the UNL list as specified here.
|
||||
# Path to file contain a list of nodes to always accept as validators. Use
|
||||
# this to specify a file other than this file to manage your validators list.
|
||||
#
|
||||
# If this entry is not present or empty, rippled will look for a validators.txt in the
|
||||
# config directory. If not found there, it will attempt to retrieve the file
|
||||
# from the Ripple foundation's web site.
|
||||
# If this entry is not present or empty and no nodes from previous runs were
|
||||
# found in the database, rippled will look for a validators.txt in the config
|
||||
# directory. If not found there, it will attempt to retrieve the file from
|
||||
# the [validators_site] web site.
|
||||
#
|
||||
# This entry is also used by the RPC command unl_load.
|
||||
# After specifying a different [validators_file] or changing the contents of
|
||||
# the validators file, issue a RPC unl_load command to have rippled load the
|
||||
# file.
|
||||
#
|
||||
# Specify the file by specifying its full path.
|
||||
#
|
||||
@@ -39,24 +51,19 @@
|
||||
# C:/home/johndoe/ripple/validators.txt
|
||||
# /home/johndoe/ripple/validators.txt
|
||||
#
|
||||
# [validators]:
|
||||
# Only valid in "rippled.cfg", "ripple.txt", and the referered [validators_url].
|
||||
# List of nodes to accept as validators speficied by public key or domain.
|
||||
# [validators_site]:
|
||||
# Specifies where to find validators.txt for UNL boostrapping and RPC
|
||||
# unl_network command.
|
||||
#
|
||||
# For domains, rippled will probe for https web servers at the specied
|
||||
# domain in the following order: ripple.DOMAIN, www.DOMAIN, DOMAIN
|
||||
#
|
||||
# Examples:
|
||||
# redstem.com
|
||||
# n9KorY8QtTdRx7TVDpwnG9NvyxsDwHUKUEeDLY3AkiGncVaSXZi5
|
||||
# n9MqiExBcoG19UXwoLjBJnhsxEhAZMuWwJDRdkyDz1EkEkwzQTNt John Doe
|
||||
# Example: ripple.com
|
||||
#
|
||||
# [ips]:
|
||||
# Only valid in "rippled.cfg", "ripple.txt", and the referered [ips_url].
|
||||
# List of ips where the Newcoin protocol is avialable.
|
||||
# One ipv4 or ipv6 address per line.
|
||||
# A port may optionally be specified after adding a space to the address.
|
||||
# By convention, if known, IPs are listed in from most to least trusted.
|
||||
# List of ips where the Ripple protocol is served. For a starter list, you
|
||||
# can copy entries from: https://ripple.com/ripple.txt
|
||||
#
|
||||
# Domain names are not allowed. One ipv4 or ipv6 address per line. A port
|
||||
# may optionally be specified after adding a space to the address. By
|
||||
# convention, if known, IPs are listed in from most to least trusted.
|
||||
#
|
||||
# Examples:
|
||||
# 192.168.0.1
|
||||
@@ -64,46 +71,117 @@
|
||||
# 2001:0db8:0100:f101:0210:a4ff:fee3:9566
|
||||
#
|
||||
# [sntp_servers]
|
||||
# IP address or domain of servers to use for time synchronization.
|
||||
# The default time servers are suitable for servers located in the United States
|
||||
# IP address or domain of NTP servers to use for time synchronization.
|
||||
#
|
||||
# These NTP servers are suitable for rippled servers located in the United
|
||||
# States:
|
||||
# time.windows.com
|
||||
# time.apple.com
|
||||
# time.nist.gov
|
||||
# pool.ntp.org
|
||||
#
|
||||
# [peer_ip]:
|
||||
# IP address or domain to bind to allow external connections from peers.
|
||||
# Defaults to not allow external connections from peers.
|
||||
# Defaults to not binding, which disallows external connections from peers.
|
||||
#
|
||||
# Examples: 0.0.0.0 - Bind on all interfaces.
|
||||
#
|
||||
# [peer_port]:
|
||||
# Port to bind to allow external connections from peers.
|
||||
# If peer_ip is supplied, corresponding port to bind to for peer connections.
|
||||
#
|
||||
# [peer_private]:
|
||||
# 0 or 1.
|
||||
# 0: allow peers to broadcast your address. [default]
|
||||
# 0: request peers to broadcast your address. [default]
|
||||
# 1: request peers not broadcast your address.
|
||||
#
|
||||
# [rpc_ip]:
|
||||
# IP address or domain to bind to allow insecure RPC connections.
|
||||
# Defaults to not allow RPC connections.
|
||||
# Defaults to not binding, which disallows RPC connections.
|
||||
#
|
||||
# [rpc_port]:
|
||||
# Port to bind to if allowing insecure RPC connections.
|
||||
# If rpc_ip is supplied, corresponding port to bind to for peer connections.
|
||||
#
|
||||
# [rpc_allow_remote]:
|
||||
# 0 or 1.
|
||||
# 0: only allows RPC connections from 127.0.0.1. [default]
|
||||
# 0: Allow RPC connections only from 127.0.0.1. [default]
|
||||
# 1: Allow RPC connections from any IP.
|
||||
#
|
||||
# [rpc_admin_allow]:
|
||||
# Specify an list of IP addresses allowed to have admin access. One per line.
|
||||
#
|
||||
# Defaults to 127.0.0.1.
|
||||
#
|
||||
# [rpc_user]:
|
||||
# As a server, require a this user to specified and require rpc_password to
|
||||
# be checked for RPC access via the rpc_ip and rpc_port. The user and password
|
||||
# must be specified via HTTP's basic authentication method.
|
||||
#
|
||||
# As a client, supply this to the server via HTTP's basic authentication
|
||||
# method.
|
||||
#
|
||||
# [rpc_password]:
|
||||
# As a server, require a this password to specified and require rpc_user to
|
||||
# be checked for RPC access via the rpc_ip and rpc_port. The user and password
|
||||
# must be specified via HTTP's basic authentication method.
|
||||
#
|
||||
# As a client, supply this to the server via HTTP's basic authentication
|
||||
# method.
|
||||
#
|
||||
# [rpc_admin_user]:
|
||||
# As a server, require this as the admin user to be specified. Also, require
|
||||
# rpc_admin_user and rpc_admin_password to be checked for RPC admin functions.
|
||||
# The request must specify these as the admin_user and admin_password in the
|
||||
# request object.
|
||||
#
|
||||
# As a client, supply this to the server in the request object.
|
||||
#
|
||||
# [rpc_admin_password]:
|
||||
# As a server, require this as the admin pasword to be specified. Also,
|
||||
# require rpc_admin_user and rpc_admin_password to be checked for RPC admin
|
||||
# functions. The request must specify these as the admin_user and
|
||||
# admin_password in the request object.
|
||||
#
|
||||
# As a client, supply this to the server in the request object.
|
||||
#
|
||||
# [websocket_public_ip]:
|
||||
# IP address or domain to bind to allow untrusted connections from clients.
|
||||
# In the future, this option will go away and the peer_ip will accept
|
||||
# websocket client connections.
|
||||
#
|
||||
# Examples: 0.0.0.0 - Bind on all interfaces.
|
||||
# 127.0.0.1 - Bind on localhost interface. Only local programs may connect.
|
||||
#
|
||||
# [websocket_public_port]:
|
||||
# Port to bind to allow untrusted connections from clients. In the future,
|
||||
# this option will go away and the peer_ip will accept websocket client
|
||||
# connections.
|
||||
#
|
||||
# [websocket_public_secure]
|
||||
# 0, 1 or 2.
|
||||
# 0: Provide ws service for websocket_public_ip/websocket_public_port.
|
||||
# 1: Provide both ws and wss service for websocket_public_ip/websocket_public_port. [default]
|
||||
# 2: Provide wss service only for websocket_public_ip/websocket_public_port.
|
||||
#
|
||||
# Browser pages like the Ripple client will not be able to connect to a secure
|
||||
# websocket connection if a self-signed certificate is used. As the Ripple
|
||||
# reference client currently shares secrets with its server, this should be
|
||||
# enabled.
|
||||
#
|
||||
# [websocket_ip]:
|
||||
# IP address or domain to bind to allow client connections.
|
||||
# IP address or domain to bind to allow trusted ADMIN connections from backend
|
||||
# applications.
|
||||
#
|
||||
# Examples: 0.0.0.0 - Bind on all interfaces.
|
||||
# 127.0.0.1 - Bind on localhost interface. Only local programs may connect.
|
||||
#
|
||||
# [websocket_port]:
|
||||
# Port to bind to allow client connections.
|
||||
# Port to bind to allow trusted ADMIN connections from backend applications.
|
||||
#
|
||||
# [websocket_ssl]:
|
||||
# 0 or 1.
|
||||
# Enable websocket SSL
|
||||
# [websocket_secure]
|
||||
# 0, 1, or 2.
|
||||
# 0: Provide ws service only for websocket_ip/websocket_port. [default]
|
||||
# 1: Provide ws and wss service for websocket_ip/websocket_port
|
||||
# 2: Provide wss service for websocket_ip/websocket_port.
|
||||
#
|
||||
# [websocket_ssl_key]:
|
||||
# Specify the filename holding the SSL key in PEM format.
|
||||
@@ -113,30 +191,72 @@
|
||||
# This is not needed if the chain includes it.
|
||||
#
|
||||
# [websocket_ssl_chain]:
|
||||
# If you need a certificate chain, specify the path to the certificate chain here.
|
||||
# The chain may include the end certificate.
|
||||
# If you need a certificate chain, specify the path to the certificate chain
|
||||
# here. The chain may include the end certificate.
|
||||
#
|
||||
# [validation_seed]:
|
||||
# To perform validation, this section should contain either a validation seed or key.
|
||||
# The validation seed is used to generate the validation public/private key pair.
|
||||
# To perform validation, this section should contain either a validation seed
|
||||
# or key. The validation seed is used to generate the validation
|
||||
# public/private key pair. To obtain a validation seed, use the
|
||||
# validation_create command.
|
||||
#
|
||||
# Examples: RASH BUSH MILK LOOK BAD BRIM AVID GAFF BAIT ROT POD LOVE
|
||||
# shfArahZT9Q9ckTf3s1psJ7C7qzVN
|
||||
#
|
||||
# [node_seed]:
|
||||
# This is used for clustering. To force a particular node seed or key, the
|
||||
# key can be set here. The format is the same as the validation_seed field.
|
||||
# To obtain a validation seed, use the validation_create command.
|
||||
#
|
||||
# Examples: RASH BUSH MILK LOOK BAD BRIM AVID GAFF BAIT ROT POD LOVE
|
||||
# shfArahZT9Q9ckTf3s1psJ7C7qzVN
|
||||
#
|
||||
# [cluster_nodes]:
|
||||
# To extend full trust to other nodes, place their node public keys here.
|
||||
# Generally, you should only do this for nodes under common administration.
|
||||
# Node public keys start with an 'n'.
|
||||
#
|
||||
# [ledger_history]:
|
||||
# To serve clients, servers need historical ledger data. This sets the number of
|
||||
# past ledgers to acquire on server startup and the minimum to maintain while
|
||||
# running. Servers that don't need to serve clients can set this to "none".
|
||||
# Servers that want complete history can set this to "full".
|
||||
# The default is 256 ledgers
|
||||
# The number of past ledgers to acquire on server startup and the minimum to
|
||||
# maintain while running.
|
||||
#
|
||||
# To serve clients, servers need historical ledger data. Servers that don't
|
||||
# need to serve clients can set this to "none". Servers that want complete
|
||||
# history can set this to "full".
|
||||
#
|
||||
# The default is: 256
|
||||
#
|
||||
# [database_path]:
|
||||
# Full path of database directory.
|
||||
#
|
||||
# [rpc_startup]:
|
||||
# Specify a list of RPC commands to run at startup.
|
||||
#
|
||||
# Example: { "command" : "server_info" }
|
||||
#
|
||||
|
||||
# Allow other peers to connect to this server.
|
||||
[peer_ip]
|
||||
0.0.0.0
|
||||
|
||||
[peer_port]
|
||||
51235
|
||||
|
||||
# Allow untrusted clients to connect to this server.
|
||||
[websocket_public_ip]
|
||||
0.0.0.0
|
||||
|
||||
[websocket_public_port]
|
||||
5006
|
||||
|
||||
# Provide trusted websocket ADMIN access.
|
||||
[websocket_ip]
|
||||
127.0.0.1
|
||||
|
||||
[websocket_port]
|
||||
6006
|
||||
|
||||
# Provide trusted json-rpc ADMIN access.
|
||||
[rpc_ip]
|
||||
127.0.0.1
|
||||
|
||||
@@ -144,13 +264,7 @@
|
||||
5005
|
||||
|
||||
[rpc_allow_remote]
|
||||
1
|
||||
|
||||
[websocket_ip]
|
||||
0.0.0.0
|
||||
|
||||
[websocket_port]
|
||||
5006
|
||||
0
|
||||
|
||||
[debug_logfile]
|
||||
log/debug.log
|
||||
@@ -161,10 +275,7 @@ time.apple.com
|
||||
time.nist.gov
|
||||
pool.ntp.org
|
||||
|
||||
|
||||
[unl_default]
|
||||
validators.txt
|
||||
|
||||
# Where to find some other servers speaking the Ripple protocol.
|
||||
[ips]
|
||||
23.21.167.100 51235
|
||||
23.23.201.55 51235
|
||||
|
||||
2757
rippled.xcodeproj/project.pbxproj
Normal file
2757
rippled.xcodeproj/project.pbxproj
Normal file
File diff suppressed because it is too large
Load Diff
7
rippled.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
7
rippled.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:rippled.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
BIN
rippled.xcodeproj/project.xcworkspace/xcuserdata/jcar.xcuserdatad/UserInterfaceState.xcuserstate
generated
Normal file
BIN
rippled.xcodeproj/project.xcworkspace/xcuserdata/jcar.xcuserdatad/UserInterfaceState.xcuserstate
generated
Normal file
Binary file not shown.
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Bucket
|
||||
type = "1"
|
||||
version = "1.0">
|
||||
</Bucket>
|
||||
@@ -0,0 +1,86 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0450"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C14F81261681323400AAB80A"
|
||||
BuildableName = "rippled"
|
||||
BlueprintName = "rippled"
|
||||
ReferencedContainer = "container:rippled.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
buildConfiguration = "Debug">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C14F81261681323400AAB80A"
|
||||
BuildableName = "rippled"
|
||||
BlueprintName = "rippled"
|
||||
ReferencedContainer = "container:rippled.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Debug"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C14F81261681323400AAB80A"
|
||||
BuildableName = "rippled"
|
||||
BlueprintName = "rippled"
|
||||
ReferencedContainer = "container:rippled.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Release"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C14F81261681323400AAB80A"
|
||||
BuildableName = "rippled"
|
||||
BlueprintName = "rippled"
|
||||
ReferencedContainer = "container:rippled.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>SchemeUserState</key>
|
||||
<dict>
|
||||
<key>rippled.xcscheme</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
<dict>
|
||||
<key>C14F81261681323400AAB80A</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,24 +1,30 @@
|
||||
#include "SqliteDatabase.h"
|
||||
#include "sqlite3.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/thread.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
SqliteDatabase::SqliteDatabase(const char* host) : Database(host,"","")
|
||||
SqliteDatabase::SqliteDatabase(const char* host) : Database(host,"",""), walRunning(false)
|
||||
{
|
||||
mConnection=NULL;
|
||||
mCurrentStmt=NULL;
|
||||
mConnection = NULL;
|
||||
mCurrentStmt = NULL;
|
||||
}
|
||||
|
||||
void SqliteDatabase::connect()
|
||||
{
|
||||
int rc = sqlite3_open(mHost.c_str(), &mConnection);
|
||||
if( rc )
|
||||
if (rc)
|
||||
{
|
||||
cout << "Can't open database: " << mHost << " " << rc << endl;
|
||||
sqlite3_close(mConnection);
|
||||
assert((rc != SQLITE_BUSY) && (rc != SQLITE_LOCKED));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,8 +38,10 @@ void SqliteDatabase::disconnect()
|
||||
bool SqliteDatabase::executeSQL(const char* sql, bool fail_ok)
|
||||
{
|
||||
sqlite3_finalize(mCurrentStmt);
|
||||
|
||||
int rc = sqlite3_prepare_v2(mConnection, sql, -1, &mCurrentStmt, NULL);
|
||||
if (rc != SQLITE_OK )
|
||||
|
||||
if (SQLITE_OK != rc)
|
||||
{
|
||||
if (!fail_ok)
|
||||
{
|
||||
@@ -56,7 +64,9 @@ bool SqliteDatabase::executeSQL(const char* sql, bool fail_ok)
|
||||
}
|
||||
else
|
||||
{
|
||||
assert((rc != SQLITE_BUSY) && (rc != SQLITE_LOCKED));
|
||||
mMoreRows = false;
|
||||
|
||||
if (!fail_ok)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
@@ -106,17 +116,20 @@ void SqliteDatabase::endIterRows()
|
||||
// will return false if there are no more rows
|
||||
bool SqliteDatabase::getNextRow()
|
||||
{
|
||||
if(!mMoreRows) return(false);
|
||||
if (!mMoreRows) return(false);
|
||||
|
||||
int rc=sqlite3_step(mCurrentStmt);
|
||||
if(rc==SQLITE_ROW)
|
||||
if (rc==SQLITE_ROW)
|
||||
{
|
||||
return(true);
|
||||
}else if(rc==SQLITE_DONE)
|
||||
}
|
||||
else if (rc==SQLITE_DONE)
|
||||
{
|
||||
return(false);
|
||||
}else
|
||||
}
|
||||
else
|
||||
{
|
||||
assert((rc != SQLITE_BUSY) && (rc != SQLITE_LOCKED));
|
||||
cout << "SQL Rerror:" << rc << endl;
|
||||
return(false);
|
||||
}
|
||||
@@ -175,27 +188,54 @@ uint64 SqliteDatabase::getBigInt(int colIndex)
|
||||
}
|
||||
|
||||
|
||||
/* http://www.sqlite.org/lang_expr.html
|
||||
BLOB literals are string literals containing hexadecimal data and preceded by a single "x" or "X" character. For example:
|
||||
X'53514C697465'
|
||||
*/
|
||||
void SqliteDatabase::escape(const unsigned char* start, int size, std::string& retStr)
|
||||
static int SqliteWALHook(void *s, sqlite3* dbCon, const char *dbName, int walSize)
|
||||
{
|
||||
static const char toHex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'A', 'B', 'C', 'D', 'E', 'F' };
|
||||
(reinterpret_cast<SqliteDatabase*>(s))->doHook(dbName, walSize);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
retStr.resize(3 + (size * 2));
|
||||
bool SqliteDatabase::setupCheckpointing()
|
||||
{
|
||||
sqlite3_wal_hook(mConnection, SqliteWALHook, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
int pos = 0;
|
||||
retStr[pos++] = 'X';
|
||||
retStr[pos++] = '\'';
|
||||
|
||||
for (int n = 0; n < size; ++n)
|
||||
void SqliteDatabase::doHook(const char *db, int pages)
|
||||
{
|
||||
if (pages < 256)
|
||||
return;
|
||||
boost::mutex::scoped_lock sl(walMutex);
|
||||
if (walDBs.insert(db).second && !walRunning)
|
||||
{
|
||||
retStr[pos++] = toHex[start[n] >> 4];
|
||||
retStr[pos++] = toHex[start[n] & 0x0f];
|
||||
walRunning = true;
|
||||
boost::thread(boost::bind(&SqliteDatabase::runWal, this)).detach();
|
||||
}
|
||||
}
|
||||
|
||||
void SqliteDatabase::runWal()
|
||||
{
|
||||
std::set<std::string> walSet;
|
||||
|
||||
while (1)
|
||||
{
|
||||
{
|
||||
boost::mutex::scoped_lock sl(walMutex);
|
||||
walDBs.swap(walSet);
|
||||
if (walSet.empty())
|
||||
{
|
||||
walRunning = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_FOREACH(const std::string& db, walSet)
|
||||
{
|
||||
int log, ckpt;
|
||||
sqlite3_wal_checkpoint_v2(mConnection, db.c_str(), SQLITE_CHECKPOINT_PASSIVE, &log, &ckpt);
|
||||
}
|
||||
walSet.clear();
|
||||
|
||||
}
|
||||
retStr[pos] = '\'';
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
|
||||
@@ -1,13 +1,24 @@
|
||||
#include "database.h"
|
||||
|
||||
#include <string>
|
||||
#include <set>
|
||||
|
||||
#include <boost/thread/mutex.hpp>
|
||||
|
||||
struct sqlite3;
|
||||
struct sqlite3_stmt;
|
||||
|
||||
|
||||
class SqliteDatabase : public Database
|
||||
{
|
||||
sqlite3* mConnection;
|
||||
sqlite3_stmt* mCurrentStmt;
|
||||
bool mMoreRows;
|
||||
|
||||
boost::mutex walMutex;
|
||||
std::set<std::string> walDBs;
|
||||
bool walRunning;
|
||||
|
||||
public:
|
||||
SqliteDatabase(const char* host);
|
||||
|
||||
@@ -39,7 +50,11 @@ public:
|
||||
std::vector<unsigned char> getBinary(int colIndex);
|
||||
uint64 getBigInt(int colIndex);
|
||||
|
||||
void escape(const unsigned char* start,int size,std::string& retStr);
|
||||
sqlite3* peekConnection() { return mConnection; }
|
||||
virtual bool setupCheckpointing();
|
||||
|
||||
void runWal();
|
||||
void doHook(const char *db, int walSize);
|
||||
};
|
||||
|
||||
// vim:ts=4
|
||||
|
||||
@@ -185,12 +185,4 @@ char* Database::getSingleDBValueStr(const char* sql,std::string& retStr)
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string Database::escape(const std::string strValue)
|
||||
{
|
||||
std::string strReturn;
|
||||
|
||||
escape(reinterpret_cast<const unsigned char*>(strValue.c_str()), strValue.size(), strReturn);
|
||||
|
||||
return strReturn;
|
||||
}
|
||||
// vim:ts=4
|
||||
|
||||
@@ -37,9 +37,6 @@ public:
|
||||
|
||||
std::string& getPass(){ return(mDBPass); }
|
||||
|
||||
virtual void escape(const unsigned char* start,int size,std::string& retStr)=0;
|
||||
std::string escape(const std::string strValue);
|
||||
|
||||
// returns true if the query went ok
|
||||
virtual bool executeSQL(const char* sql, bool fail_okay=false)=0;
|
||||
|
||||
@@ -85,6 +82,8 @@ public:
|
||||
// int getSingleDBValueInt(const char* sql);
|
||||
// float getSingleDBValueFloat(const char* sql);
|
||||
// char* getSingleDBValueStr(const char* sql, std::string& retStr);
|
||||
|
||||
virtual bool setupCheckpointing() { return false; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,136 +0,0 @@
|
||||
#include "mysqldatabase.h"
|
||||
#include "reportingmechanism.h"
|
||||
#include "string/i4string.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
Database* Database::newDatabase(const char* host,const char* user,const char* pass)
|
||||
{
|
||||
return(new MySqlDatabase(host,user,pass));
|
||||
}
|
||||
|
||||
MySqlDatabase::MySqlDatabase(const char* host,const char* user,const char* pass) : Database(host,user,pass)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
MySqlDatabase::~MySqlDatabase()
|
||||
{
|
||||
}
|
||||
|
||||
void MySqlDatabase::connect()
|
||||
{
|
||||
mysql_init(&mMysql);
|
||||
mysql_options(&mMysql,MYSQL_READ_DEFAULT_GROUP,"i4min");
|
||||
if(!mysql_real_connect(&mMysql,mHost,mUser,mDBPass,NULL,0,NULL,0))
|
||||
{
|
||||
theUI->statusMsg("Failed to connect to database: Error: %s\n", mysql_error(&mMysql));
|
||||
}else theUI->statusMsg("Connection Established to DB");
|
||||
}
|
||||
|
||||
void MySqlDatabase::disconnect()
|
||||
{
|
||||
mysql_close(&mMysql);
|
||||
}
|
||||
|
||||
int MySqlDatabase::getNumRowsAffected()
|
||||
{
|
||||
return( mysql_affected_rows(&mMysql));
|
||||
}
|
||||
|
||||
// returns true if the query went ok
|
||||
bool MySqlDatabase::executeSQL(const char* sql)
|
||||
{
|
||||
int ret=mysql_query(&mMysql, sql);
|
||||
if(ret)
|
||||
{
|
||||
connect();
|
||||
int ret=mysql_query(&mMysql, sql);
|
||||
if(ret)
|
||||
{
|
||||
theUI->statusMsg("ERROR with executeSQL: %d %s",ret,sql);
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
|
||||
bool MySqlDatabase::startIterRows()
|
||||
{
|
||||
mResult=mysql_store_result(&mMysql);
|
||||
// get total number of columns from the resultset
|
||||
mNumCol = mysql_num_fields(mResult);
|
||||
if(mNumCol)
|
||||
{
|
||||
delete[](mColNameTable);
|
||||
mColNameTable=new i4_str[mNumCol];
|
||||
|
||||
// fill out the column name table
|
||||
for(int n = 0; n < mNumCol; n++)
|
||||
{
|
||||
MYSQL_FIELD* field=mysql_fetch_field(mResult);
|
||||
mColNameTable[n]= field->name;
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
|
||||
// call this after you executeSQL
|
||||
// will return false if there are no more rows
|
||||
bool MySqlDatabase::getNextRow()
|
||||
{
|
||||
mCurRow=mysql_fetch_row(mResult);
|
||||
|
||||
return(mCurRow!=NULL);
|
||||
}
|
||||
|
||||
char* MySqlDatabase::getStr(int colIndex,i4_str* retStr)
|
||||
{
|
||||
if(mCurRow[colIndex])
|
||||
{
|
||||
(*retStr)=mCurRow[colIndex];
|
||||
}else (*retStr)="";
|
||||
|
||||
return(*retStr);
|
||||
}
|
||||
|
||||
w32 MySqlDatabase::getInt(int colIndex)
|
||||
{
|
||||
|
||||
if(mCurRow[colIndex])
|
||||
{
|
||||
w32 ret=atoll(mCurRow[colIndex]);
|
||||
//theUI->statusMsg("getInt: %s,%c,%u",mCurRow[colIndex],mCurRow[colIndex][0],ret);
|
||||
return(ret);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
float MySqlDatabase::getFloat(int colIndex)
|
||||
{
|
||||
if(mCurRow[colIndex])
|
||||
{
|
||||
float ret=atof(mCurRow[colIndex]);
|
||||
return(ret);
|
||||
}
|
||||
return(0.0);
|
||||
}
|
||||
|
||||
bool MySqlDatabase::getBool(int colIndex)
|
||||
{
|
||||
if(mCurRow[colIndex])
|
||||
{
|
||||
int ret=atoi(mCurRow[colIndex]);
|
||||
return(ret);
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
|
||||
|
||||
void MySqlDatabase::endIterRows()
|
||||
{
|
||||
mysql_free_result(mResult);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
#ifndef __MYSQLDATABASE__
|
||||
#define __MYSQLDATABASE__
|
||||
|
||||
#include "../database.h"
|
||||
#include "string/i4string.h"
|
||||
#include "mysql/mysql.h"
|
||||
|
||||
/*
|
||||
this maintains the connection to the database
|
||||
*/
|
||||
class MySqlDatabase : public Database
|
||||
{
|
||||
MYSQL mMysql;
|
||||
MYSQL_RES* mResult;
|
||||
MYSQL_ROW mCurRow;
|
||||
|
||||
public:
|
||||
MySqlDatabase(const char* host,const char* user,const char* pass);
|
||||
~MySqlDatabase();
|
||||
|
||||
void connect();
|
||||
void disconnect();
|
||||
|
||||
// returns true if the query went ok
|
||||
bool executeSQL(const char* sql);
|
||||
|
||||
int getNumRowsAffected();
|
||||
|
||||
// returns false if there are no results
|
||||
bool startIterRows();
|
||||
void endIterRows();
|
||||
|
||||
// call this after you executeSQL
|
||||
// will return false if there are no more rows
|
||||
bool getNextRow();
|
||||
|
||||
// get Data from the current row
|
||||
|
||||
char* getStr(int colIndex,i4_str* retStr);
|
||||
w32 getInt(int colIndex);
|
||||
float getFloat(int colIndex);
|
||||
bool getBool(int colIndex);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
#ifndef __TMYODBC_UTILITY_H__
|
||||
#define __TMYODBC_UTILITY_H__
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <myconf.h>
|
||||
#endif
|
||||
|
||||
|
||||
/* STANDARD C HEADERS */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* ODBC HEADERS */
|
||||
#include <sqlext.h>
|
||||
|
||||
#define MAX_NAME_LEN 95
|
||||
#define MAX_COLUMNS 255
|
||||
#define MAX_ROW_DATA_LEN 255
|
||||
|
||||
|
||||
/* PROTOTYPE */
|
||||
void myerror(SQLRETURN rc,SQLSMALLINT htype, SQLHANDLE handle);
|
||||
|
||||
/* UTILITY MACROS */
|
||||
#define myenv(henv,r) \
|
||||
if ( ((r) != SQL_SUCCESS) ) \
|
||||
myerror(r, 1,henv); \
|
||||
assert( ((r) == SQL_SUCCESS) || ((r) == SQL_SUCCESS_WITH_INFO) )
|
||||
|
||||
#define myenv_err(henv,r,rc) \
|
||||
if ( rc == SQL_ERROR || rc == SQL_SUCCESS_WITH_INFO ) \
|
||||
myerror(rc, 1, henv); \
|
||||
assert( r )
|
||||
|
||||
#define mycon(hdbc,r) \
|
||||
if ( ((r) != SQL_SUCCESS) ) \
|
||||
myerror(r, 2, hdbc); \
|
||||
assert( ((r) == SQL_SUCCESS) || ((r) == SQL_SUCCESS_WITH_INFO) )
|
||||
|
||||
#define mycon_err(hdbc,r,rc) \
|
||||
if ( rc == SQL_ERROR || rc == SQL_SUCCESS_WITH_INFO ) \
|
||||
myerror(rc, 2, hdbc); \
|
||||
assert( r )
|
||||
|
||||
#define mystmt(hstmt,r) \
|
||||
if ( ((r) != SQL_SUCCESS) ) \
|
||||
myerror(r, 3, hstmt); \
|
||||
assert( ((r) == SQL_SUCCESS) || ((r) == SQL_SUCCESS_WITH_INFO) )
|
||||
|
||||
#define mystmt_err(hstmt,r,rc) \
|
||||
if ( rc == SQL_ERROR || rc == SQL_SUCCESS_WITH_INFO ) \
|
||||
myerror(rc, 3, hstmt); \
|
||||
assert( r )
|
||||
|
||||
/********************************************************
|
||||
* MyODBC 3.51 error handler *
|
||||
*********************************************************/
|
||||
void myerror(SQLRETURN rc, SQLSMALLINT htype, SQLHANDLE handle)
|
||||
{
|
||||
SQLRETURN lrc;
|
||||
|
||||
if( rc == SQL_ERROR || rc == SQL_SUCCESS_WITH_INFO )
|
||||
{
|
||||
SQLCHAR szSqlState[6],szErrorMsg[SQL_MAX_MESSAGE_LENGTH];
|
||||
SQLINTEGER pfNativeError;
|
||||
SQLSMALLINT pcbErrorMsg;
|
||||
|
||||
lrc = SQLGetDiagRec(htype, handle,1,
|
||||
(SQLCHAR *)&szSqlState,
|
||||
(SQLINTEGER *)&pfNativeError,
|
||||
(SQLCHAR *)&szErrorMsg,
|
||||
SQL_MAX_MESSAGE_LENGTH-1,
|
||||
(SQLSMALLINT *)&pcbErrorMsg);
|
||||
if(lrc == SQL_SUCCESS || lrc == SQL_SUCCESS_WITH_INFO)
|
||||
printf("\n [%s][%d:%s]\n",szSqlState,pfNativeError,szErrorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif /* __TMYODBC_UTILITY_H__ */
|
||||
|
||||
@@ -1,246 +0,0 @@
|
||||
#include "windatabase.h"
|
||||
#include "dbutility.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
Database* Database::newMysqlDatabase(const char* host,const char* user,const char* pass)
|
||||
{
|
||||
return(new WinDatabase(host,user,pass));
|
||||
}
|
||||
|
||||
WinDatabase::WinDatabase(const char* host,const char* user,const char* pass) : Database(host,user,pass)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
WinDatabase::~WinDatabase()
|
||||
{
|
||||
disconnect();
|
||||
}
|
||||
|
||||
|
||||
void WinDatabase::connect()
|
||||
{
|
||||
SQLRETURN rc;
|
||||
|
||||
rc = SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&henv);
|
||||
myenv(henv, rc);
|
||||
|
||||
rc = SQLSetEnvAttr(henv,SQL_ATTR_ODBC_VERSION,(SQLPOINTER)SQL_OV_ODBC3,0);
|
||||
myenv(henv, rc);
|
||||
|
||||
rc = SQLAllocHandle(SQL_HANDLE_DBC,henv, &hdbc);
|
||||
myenv(henv, rc);
|
||||
|
||||
rc = SQLConnect(hdbc, (unsigned char*)(char*) mHost.c_str(), SQL_NTS, (unsigned char*)(char*) mUser.c_str(), SQL_NTS, (unsigned char*)(char*) mDBPass.c_str(), SQL_NTS);
|
||||
mycon(hdbc, rc);
|
||||
|
||||
rc = SQLSetConnectAttr(hdbc,SQL_ATTR_AUTOCOMMIT,(SQLPOINTER)SQL_AUTOCOMMIT_ON,0);
|
||||
mycon(hdbc,rc);
|
||||
|
||||
rc = SQLAllocHandle(SQL_HANDLE_STMT,hdbc,&hstmt);
|
||||
mycon(hdbc,rc);
|
||||
|
||||
//rc = SQLGetInfo(hdbc,SQL_DBMS_NAME,&server_name,40,NULL);
|
||||
//mycon(hdbc, rc);
|
||||
|
||||
//theUI->statusMsg("Connection Established to DB");
|
||||
}
|
||||
|
||||
void WinDatabase::disconnect()
|
||||
{
|
||||
SQLRETURN rc;
|
||||
|
||||
rc = SQLFreeStmt(hstmt, SQL_DROP);
|
||||
mystmt(hstmt,rc);
|
||||
|
||||
rc = SQLDisconnect(hdbc);
|
||||
mycon(hdbc, rc);
|
||||
|
||||
rc = SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
|
||||
mycon(hdbc, rc);
|
||||
|
||||
rc = SQLFreeHandle(SQL_HANDLE_ENV, henv);
|
||||
myenv(henv, rc);
|
||||
}
|
||||
|
||||
int WinDatabase::getNumRowsAffected()
|
||||
{
|
||||
// theUI->statusMsg("getNumRowsAffected()");
|
||||
SQLINTEGER ret;
|
||||
SQLRowCount(hstmt,&ret);
|
||||
return(ret);
|
||||
}
|
||||
|
||||
// returns true if the query went ok
|
||||
bool WinDatabase::executeSQL(const char* sql, bool fail_okay)
|
||||
{
|
||||
SQLRETURN rc = SQLExecDirect(hstmt,(unsigned char*) sql,SQL_NTS);
|
||||
if(rc==SQL_ERROR)
|
||||
{
|
||||
//theUI->errorMsg("Trying to recover from DB error");
|
||||
rc = SQLExecDirect(hstmt,(unsigned char*) sql,SQL_NTS);
|
||||
if(rc==SQL_ERROR)
|
||||
{
|
||||
SQLCHAR SqlState[6], /*SQLStmt[100],*/ Msg[SQL_MAX_MESSAGE_LENGTH];
|
||||
SQLINTEGER NativeError;
|
||||
SQLSMALLINT i, MsgLen;
|
||||
SQLRETURN /*rc1,*/ rc2;
|
||||
|
||||
i = 1;
|
||||
while ((rc2 = SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, i, SqlState, &NativeError, Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA)
|
||||
{
|
||||
//theUI->errorMsg("DB ERROR: %s,%d,%s",SqlState,NativeError,Msg);
|
||||
i++;
|
||||
}
|
||||
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
|
||||
mystmt(hstmt,rc);
|
||||
|
||||
// commit the transaction
|
||||
rc = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT);
|
||||
mycon(hdbc,rc);
|
||||
|
||||
return(true);
|
||||
|
||||
}
|
||||
|
||||
bool WinDatabase::startIterRows()
|
||||
{
|
||||
SQLUINTEGER pcColDef;
|
||||
SQLCHAR szColName[MAX_NAME_LEN];
|
||||
SQLSMALLINT pfSqlType, pcbScale, pfNullable;
|
||||
SQLSMALLINT numCol;
|
||||
|
||||
/* get total number of columns from the resultset */
|
||||
SQLRETURN rc = SQLNumResultCols(hstmt,&numCol);
|
||||
mystmt(hstmt,rc);
|
||||
mNumCol=(int)numCol;
|
||||
|
||||
if(mNumCol)
|
||||
{
|
||||
mColNameTable.resize(mNumCol);
|
||||
|
||||
// fill out the column name table
|
||||
for(int n = 1; n <= mNumCol; n++)
|
||||
{
|
||||
rc = SQLDescribeCol(hstmt,n,szColName, MAX_NAME_LEN, NULL, &pfSqlType,&pcColDef,&pcbScale,&pfNullable);
|
||||
mystmt(hstmt,rc);
|
||||
|
||||
mColNameTable[n-1]= (char*)szColName;
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
|
||||
// call this after you executeSQL
|
||||
// will return false if there are no more rows
|
||||
bool WinDatabase::getNextRow()
|
||||
{
|
||||
SQLRETURN rc = SQLFetch(hstmt);
|
||||
return((rc==SQL_SUCCESS) || (rc==SQL_SUCCESS_WITH_INFO));
|
||||
}
|
||||
|
||||
|
||||
|
||||
char* WinDatabase::getStr(int colIndex,string& retStr)
|
||||
{
|
||||
colIndex++;
|
||||
retStr="";
|
||||
char buf[1000];
|
||||
// SQLINTEGER len;
|
||||
buf[0]=0;
|
||||
|
||||
while(SQLGetData(hstmt, colIndex, SQL_C_CHAR, &buf, 1000,NULL)!= SQL_NO_DATA)
|
||||
{
|
||||
retStr += buf;
|
||||
// theUI->statusMsg("Win: %s",buf);
|
||||
}
|
||||
|
||||
//SQLRETURN rc = SQLGetData(hstmt,colIndex,SQL_C_CHAR,&buf,30000,&len);
|
||||
//mystmt(hstmt,rc);
|
||||
//*retStr=buf;
|
||||
|
||||
//theUI->statusMsg("Win: %s",buf);
|
||||
|
||||
return((char*)retStr.c_str());
|
||||
}
|
||||
|
||||
int32 WinDatabase::getInt(int colIndex)
|
||||
{
|
||||
colIndex++;
|
||||
int ret=0;
|
||||
SQLRETURN rc = SQLGetData(hstmt,colIndex,SQL_INTEGER,&ret,sizeof(int),NULL);
|
||||
mystmt(hstmt,rc);
|
||||
return(ret);
|
||||
}
|
||||
|
||||
float WinDatabase::getFloat(int colIndex)
|
||||
{
|
||||
colIndex++;
|
||||
float ret=0;
|
||||
SQLRETURN rc = SQLGetData(hstmt,colIndex,SQL_C_FLOAT,&ret,sizeof(float),NULL);
|
||||
mystmt(hstmt,rc);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
bool WinDatabase::getBool(int colIndex)
|
||||
{
|
||||
colIndex++;
|
||||
char buf[1];
|
||||
buf[0]=0;
|
||||
SQLRETURN rc = SQLGetData(hstmt,colIndex,SQL_C_CHAR,&buf,1,NULL);
|
||||
mystmt(hstmt,rc);
|
||||
|
||||
return(buf[0] != 0);
|
||||
}
|
||||
|
||||
|
||||
void WinDatabase::endIterRows()
|
||||
{
|
||||
// free the statement row bind resources
|
||||
SQLRETURN rc = SQLFreeStmt(hstmt, SQL_UNBIND);
|
||||
mystmt(hstmt,rc);
|
||||
|
||||
// free the statement cursor
|
||||
rc = SQLFreeStmt(hstmt, SQL_CLOSE);
|
||||
mystmt(hstmt,rc);
|
||||
}
|
||||
|
||||
// TODO
|
||||
void WinDatabase::escape(const unsigned char* start,int size,std::string& retStr)
|
||||
{
|
||||
retStr=(char*)start;
|
||||
}
|
||||
|
||||
// TODO
|
||||
int WinDatabase::getLastInsertID()
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
uint64 WinDatabase::getBigInt(int colIndex)
|
||||
{
|
||||
colIndex++;
|
||||
uint64 ret=0;
|
||||
SQLRETURN rc = SQLGetData(hstmt,colIndex,SQL_INTEGER,&ret,sizeof(uint64),NULL);
|
||||
mystmt(hstmt,rc);
|
||||
return(ret);
|
||||
}
|
||||
// TODO:
|
||||
int WinDatabase::getBinary(int colIndex,unsigned char* buf,int maxSize)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
std::vector<unsigned char> WinDatabase::getBinary(int colIndex)
|
||||
{
|
||||
// TODO:
|
||||
std::vector<unsigned char> vucResult;
|
||||
return vucResult;
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
#ifndef __WINDATABASE__
|
||||
#define __WINDATABASE__
|
||||
|
||||
#include "../database.h"
|
||||
|
||||
|
||||
#ifdef WIN32
|
||||
#define _WINSOCKAPI_ // prevent inclusion of winsock.h in windows.h
|
||||
#include <windows.h>
|
||||
#include "sql.h"
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
this maintains the connection to the database
|
||||
*/
|
||||
class WinDatabase : public Database
|
||||
{
|
||||
SQLHENV henv;
|
||||
SQLHDBC hdbc;
|
||||
SQLHSTMT hstmt;
|
||||
|
||||
public:
|
||||
WinDatabase(const char* host,const char* user,const char* pass);
|
||||
virtual ~WinDatabase();
|
||||
|
||||
void connect();
|
||||
void disconnect();
|
||||
|
||||
//char* getPass(){ return((char*)mDBPass.c_str()); }
|
||||
|
||||
// returns true if the query went ok
|
||||
bool executeSQL(const char* sql, bool fail_okay=false);
|
||||
|
||||
int getNumRowsAffected();
|
||||
int getLastInsertID();
|
||||
|
||||
// returns false if there are no results
|
||||
bool startIterRows();
|
||||
void endIterRows();
|
||||
|
||||
// call this after you executeSQL
|
||||
// will return false if there are no more rows
|
||||
bool getNextRow();
|
||||
|
||||
// get Data from the current row
|
||||
char* getStr(int colIndex,std::string& retStr);
|
||||
int32 getInt(int colIndex);
|
||||
float getFloat(int colIndex);
|
||||
bool getBool(int colIndex);
|
||||
uint64 getBigInt(int colIndex);
|
||||
int getBinary(int colIndex,unsigned char* buf,int maxSize);
|
||||
std::vector<unsigned char> getBinary(int colIndex);
|
||||
bool getNull(int colIndex){ return(true); }
|
||||
|
||||
void escape(const unsigned char* start,int size,std::string& retStr);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -67,9 +67,9 @@ std::string valueToString( double value )
|
||||
{
|
||||
char buffer[32];
|
||||
#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005 to avoid warning.
|
||||
sprintf_s(buffer, sizeof(buffer), "%#.16g", value);
|
||||
sprintf_s(buffer, sizeof(buffer), "%#f", value);
|
||||
#else
|
||||
sprintf(buffer, "%#.16g", value);
|
||||
sprintf(buffer, "%#f", value);
|
||||
#endif
|
||||
char* ch = buffer + strlen(buffer) - 1;
|
||||
if (*ch != '0') return buffer; // nothing to truncate, so save time
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
#include "Ledger.h"
|
||||
#include "SerializedLedger.h"
|
||||
|
||||
/*
|
||||
Way to fetch ledger entries from an account's owner dir
|
||||
*/
|
||||
//
|
||||
// Fetch ledger entries from an account's owner dir.
|
||||
//
|
||||
class AccountItem
|
||||
{
|
||||
protected:
|
||||
@@ -42,3 +42,5 @@ public:
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
|
||||
@@ -1,8 +1,83 @@
|
||||
#include "AccountSetTransactor.h"
|
||||
#include "Config.h"
|
||||
|
||||
SETUP_LOG();
|
||||
|
||||
TER AccountSetTransactor::doApply()
|
||||
{
|
||||
Log(lsINFO) << "doAccountSet>";
|
||||
cLog(lsINFO) << "AccountSet>";
|
||||
|
||||
const uint32 uTxFlags = mTxn.getFlags();
|
||||
|
||||
const uint32 uFlagsIn = mTxnAccount->getFieldU32(sfFlags);
|
||||
uint32 uFlagsOut = uFlagsIn;
|
||||
|
||||
if (uTxFlags & tfAccountSetMask)
|
||||
{
|
||||
cLog(lsINFO) << "AccountSet: Malformed transaction: Invalid flags set.";
|
||||
|
||||
return temINVALID_FLAG;
|
||||
}
|
||||
|
||||
//
|
||||
// RequireAuth
|
||||
//
|
||||
|
||||
if ((tfRequireAuth|tfOptionalAuth) == (uTxFlags & (tfRequireAuth|tfOptionalAuth)))
|
||||
{
|
||||
cLog(lsINFO) << "AccountSet: Malformed transaction: Contradictory flags set.";
|
||||
|
||||
return temINVALID_FLAG;
|
||||
}
|
||||
|
||||
if ((uTxFlags & tfRequireAuth) && !isSetBit(uFlagsIn, lsfRequireAuth))
|
||||
{
|
||||
if (mTxn.getFieldU32(sfOwnerCount))
|
||||
{
|
||||
cLog(lsINFO) << "AccountSet: Retry: OwnerCount not zero.";
|
||||
|
||||
return terOWNERS;
|
||||
}
|
||||
|
||||
cLog(lsINFO) << "AccountSet: Set RequireAuth.";
|
||||
|
||||
uFlagsOut |= lsfRequireAuth;
|
||||
}
|
||||
|
||||
if (uTxFlags & tfOptionalAuth)
|
||||
{
|
||||
cLog(lsINFO) << "AccountSet: Clear RequireAuth.";
|
||||
|
||||
uFlagsOut &= ~lsfRequireAuth;
|
||||
}
|
||||
|
||||
//
|
||||
// RequireDestTag
|
||||
//
|
||||
|
||||
if ((tfRequireDestTag|tfOptionalDestTag) == (uTxFlags & (tfRequireDestTag|tfOptionalDestTag)))
|
||||
{
|
||||
cLog(lsINFO) << "AccountSet: Malformed transaction: Contradictory flags set.";
|
||||
|
||||
return temINVALID_FLAG;
|
||||
}
|
||||
|
||||
if (uTxFlags & tfRequireDestTag)
|
||||
{
|
||||
cLog(lsINFO) << "AccountSet: Set RequireDestTag.";
|
||||
|
||||
uFlagsOut |= lsfRequireDestTag;
|
||||
}
|
||||
|
||||
if (uTxFlags & tfOptionalDestTag)
|
||||
{
|
||||
cLog(lsINFO) << "AccountSet: Clear RequireDestTag.";
|
||||
|
||||
uFlagsOut &= ~lsfRequireDestTag;
|
||||
}
|
||||
|
||||
if (uFlagsIn != uFlagsOut)
|
||||
mTxnAccount->setFieldU32(sfFlags, uFlagsOut);
|
||||
|
||||
//
|
||||
// EmailHash
|
||||
@@ -14,13 +89,13 @@ TER AccountSetTransactor::doApply()
|
||||
|
||||
if (!uHash)
|
||||
{
|
||||
Log(lsINFO) << "doAccountSet: unset email hash";
|
||||
cLog(lsINFO) << "AccountSet: unset email hash";
|
||||
|
||||
mTxnAccount->makeFieldAbsent(sfEmailHash);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log(lsINFO) << "doAccountSet: set email hash";
|
||||
cLog(lsINFO) << "AccountSet: set email hash";
|
||||
|
||||
mTxnAccount->setFieldH128(sfEmailHash, uHash);
|
||||
}
|
||||
@@ -36,13 +111,13 @@ TER AccountSetTransactor::doApply()
|
||||
|
||||
if (!uHash)
|
||||
{
|
||||
Log(lsINFO) << "doAccountSet: unset wallet locator";
|
||||
cLog(lsINFO) << "AccountSet: unset wallet locator";
|
||||
|
||||
mTxnAccount->makeFieldAbsent(sfEmailHash);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log(lsINFO) << "doAccountSet: set wallet locator";
|
||||
cLog(lsINFO) << "AccountSet: set wallet locator";
|
||||
|
||||
mTxnAccount->setFieldH256(sfWalletLocator, uHash);
|
||||
}
|
||||
@@ -52,15 +127,22 @@ TER AccountSetTransactor::doApply()
|
||||
// MessageKey
|
||||
//
|
||||
|
||||
if (!mTxn.isFieldPresent(sfMessageKey))
|
||||
if (mTxn.isFieldPresent(sfMessageKey))
|
||||
{
|
||||
nothing();
|
||||
}
|
||||
else
|
||||
{
|
||||
Log(lsINFO) << "doAccountSet: set message key";
|
||||
std::vector<unsigned char> vucPublic = mTxn.getFieldVL(sfMessageKey);
|
||||
|
||||
mTxnAccount->setFieldVL(sfMessageKey, mTxn.getFieldVL(sfMessageKey));
|
||||
if (vucPublic.size() > PUBLIC_BYTES_MAX)
|
||||
{
|
||||
cLog(lsINFO) << "AccountSet: message key too long";
|
||||
|
||||
return telBAD_PUBLIC_KEY;
|
||||
}
|
||||
else
|
||||
{
|
||||
cLog(lsINFO) << "AccountSet: set message key";
|
||||
|
||||
mTxnAccount->setFieldVL(sfMessageKey, vucPublic);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
@@ -73,13 +155,19 @@ TER AccountSetTransactor::doApply()
|
||||
|
||||
if (vucDomain.empty())
|
||||
{
|
||||
Log(lsINFO) << "doAccountSet: unset domain";
|
||||
cLog(lsINFO) << "AccountSet: unset domain";
|
||||
|
||||
mTxnAccount->makeFieldAbsent(sfDomain);
|
||||
}
|
||||
else if (vucDomain.size() > DOMAIN_BYTES_MAX)
|
||||
{
|
||||
cLog(lsINFO) << "AccountSet: domain too long";
|
||||
|
||||
return telBAD_DOMAIN;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log(lsINFO) << "doAccountSet: set domain";
|
||||
cLog(lsINFO) << "AccountSet: set domain";
|
||||
|
||||
mTxnAccount->setFieldVL(sfDomain, vucDomain);
|
||||
}
|
||||
@@ -95,25 +183,27 @@ TER AccountSetTransactor::doApply()
|
||||
|
||||
if (!uRate || uRate == QUALITY_ONE)
|
||||
{
|
||||
Log(lsINFO) << "doAccountSet: unset transfer rate";
|
||||
cLog(lsINFO) << "AccountSet: unset transfer rate";
|
||||
|
||||
mTxnAccount->makeFieldAbsent(sfTransferRate);
|
||||
}
|
||||
else if (uRate > QUALITY_ONE)
|
||||
{
|
||||
Log(lsINFO) << "doAccountSet: set transfer rate";
|
||||
cLog(lsINFO) << "AccountSet: set transfer rate";
|
||||
|
||||
mTxnAccount->setFieldU32(sfTransferRate, uRate);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log(lsINFO) << "doAccountSet: bad transfer rate";
|
||||
cLog(lsINFO) << "AccountSet: bad transfer rate";
|
||||
|
||||
return temBAD_TRANSFER_RATE;
|
||||
}
|
||||
}
|
||||
|
||||
Log(lsINFO) << "doAccountSet<";
|
||||
cLog(lsINFO) << "AccountSet<";
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
|
||||
@@ -6,4 +6,6 @@ public:
|
||||
AccountSetTransactor(const SerializedTransaction& txn,TransactionEngineParams params, TransactionEngine* engine) : Transactor(txn,params,engine) {}
|
||||
|
||||
TER doApply();
|
||||
};
|
||||
};
|
||||
|
||||
// vim:ts=4
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
#include <algorithm>
|
||||
#include <limits.h>
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
@@ -15,8 +16,16 @@
|
||||
SETUP_LOG();
|
||||
|
||||
uint64 STAmount::uRateOne = STAmount::getRate(STAmount(1), STAmount(1));
|
||||
static const uint64_t tenTo14 = 100000000000000ul;
|
||||
static const uint64_t tenTo17 = 100000000000000000ul;
|
||||
static const uint64 tenTo14 = 100000000000000ull;
|
||||
static const uint64 tenTo17 = tenTo14 * 1000;
|
||||
|
||||
#if (ULONG_MAX > UINT_MAX)
|
||||
#define BN_add_word64(bn, word) BN_add_word(bn, word)
|
||||
#define BN_mul_word64(bn, word) BN_mul_word(bn, word)
|
||||
#define BN_div_word64(bn, word) BN_div_word(bn, word)
|
||||
#else
|
||||
#include "BigNum64.h"
|
||||
#endif
|
||||
|
||||
bool STAmount::issuerFromString(uint160& uDstIssuer, const std::string& sIssuer)
|
||||
{
|
||||
@@ -277,7 +286,7 @@ bool STAmount::setValue(const std::string& sAmount)
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Log(lsINFO) << "Bad integer amount: " << sAmount;
|
||||
cLog(lsINFO) << "Bad integer amount: " << sAmount;
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -304,7 +313,7 @@ bool STAmount::setValue(const std::string& sAmount)
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Log(lsINFO) << "Bad e amount: " << sAmount;
|
||||
cLog(lsINFO) << "Bad e amount: " << sAmount;
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -346,7 +355,7 @@ bool STAmount::setValue(const std::string& sAmount)
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Log(lsINFO) << "Bad float amount: " << sAmount;
|
||||
cLog(lsINFO) << "Bad float amount: " << sAmount;
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -391,7 +400,7 @@ bool STAmount::setFullValue(const std::string& sAmount, const std::string& sCurr
|
||||
//
|
||||
if (!currencyFromString(mCurrency, sCurrency))
|
||||
{
|
||||
Log(lsINFO) << "Currency malformed: " << sCurrency;
|
||||
cLog(lsINFO) << "Currency malformed: " << sCurrency;
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -406,7 +415,7 @@ bool STAmount::setFullValue(const std::string& sAmount, const std::string& sCurr
|
||||
// Issuer must be "" or a valid account string.
|
||||
if (!naIssuerID.setAccountID(sIssuer))
|
||||
{
|
||||
Log(lsINFO) << "Issuer malformed: " << sIssuer;
|
||||
cLog(lsINFO) << "Issuer malformed: " << sIssuer;
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -416,7 +425,7 @@ bool STAmount::setFullValue(const std::string& sAmount, const std::string& sCurr
|
||||
// Stamps not must have an issuer.
|
||||
if (mIsNative && !mIssuer.isZero())
|
||||
{
|
||||
Log(lsINFO) << "Issuer specified for XRP: " << sIssuer;
|
||||
cLog(lsINFO) << "Issuer specified for XRP: " << sIssuer;
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -901,9 +910,9 @@ STAmount STAmount::divide(const STAmount& num, const STAmount& den, const uint16
|
||||
|
||||
// Compute (numerator * 10^17) / denominator
|
||||
CBigNum v;
|
||||
if ((BN_add_word(&v, numVal) != 1) ||
|
||||
(BN_mul_word(&v, tenTo17) != 1) ||
|
||||
(BN_div_word(&v, denVal) == ((BN_ULONG) -1)))
|
||||
if ((BN_add_word64(&v, numVal) != 1) ||
|
||||
(BN_mul_word64(&v, tenTo17) != 1) ||
|
||||
(BN_div_word64(&v, denVal) == ((uint64) -1)))
|
||||
{
|
||||
throw std::runtime_error("internal bn error");
|
||||
}
|
||||
@@ -911,7 +920,7 @@ STAmount STAmount::divide(const STAmount& num, const STAmount& den, const uint16
|
||||
// 10^16 <= quotient <= 10^18
|
||||
assert(BN_num_bytes(&v) <= 64);
|
||||
|
||||
return STAmount(uCurrencyID, uIssuerID, v.getulong() + 5,
|
||||
return STAmount(uCurrencyID, uIssuerID, v.getuint64() + 5,
|
||||
numOffset - denOffset - 17, num.mIsNegative != den.mIsNegative);
|
||||
}
|
||||
|
||||
@@ -924,9 +933,9 @@ STAmount STAmount::multiply(const STAmount& v1, const STAmount& v2, const uint16
|
||||
{
|
||||
uint64 minV = (v1.getSNValue() < v2.getSNValue()) ? v1.getSNValue() : v2.getSNValue();
|
||||
uint64 maxV = (v1.getSNValue() < v2.getSNValue()) ? v2.getSNValue() : v1.getSNValue();
|
||||
if (minV > 3000000000) // sqrt(cMaxNative)
|
||||
if (minV > 3000000000ull) // sqrt(cMaxNative)
|
||||
throw std::runtime_error("Native value overflow");
|
||||
if (((maxV >> 32) * minV) > 2095475792) // cMaxNative / 2^32
|
||||
if (((maxV >> 32) * minV) > 2095475792ull) // cMaxNative / 2^32
|
||||
throw std::runtime_error("Native value overflow");
|
||||
return STAmount(v1.getFName(), minV * maxV);
|
||||
}
|
||||
@@ -955,9 +964,9 @@ STAmount STAmount::multiply(const STAmount& v1, const STAmount& v2, const uint16
|
||||
// Compute (numerator * denominator) / 10^14 with rounding
|
||||
// 10^16 <= result <= 10^18
|
||||
CBigNum v;
|
||||
if ((BN_add_word(&v, value1) != 1) ||
|
||||
(BN_mul_word(&v, value2) != 1) ||
|
||||
(BN_div_word(&v, tenTo14) == ((BN_ULONG) -1)))
|
||||
if ((BN_add_word64(&v, value1) != 1) ||
|
||||
(BN_mul_word64(&v, value2) != 1) ||
|
||||
(BN_div_word64(&v, tenTo14) == ((uint64) -1)))
|
||||
{
|
||||
throw std::runtime_error("internal bn error");
|
||||
}
|
||||
@@ -965,7 +974,7 @@ STAmount STAmount::multiply(const STAmount& v1, const STAmount& v2, const uint16
|
||||
// 10^16 <= product <= 10^18
|
||||
assert(BN_num_bytes(&v) <= 64);
|
||||
|
||||
return STAmount(uCurrencyID, uIssuerID, v.getulong() + 7, offset1 + offset2 + 14,
|
||||
return STAmount(uCurrencyID, uIssuerID, v.getuint64() + 7, offset1 + offset2 + 14,
|
||||
v1.mIsNegative != v2.mIsNegative);
|
||||
}
|
||||
|
||||
@@ -998,15 +1007,26 @@ STAmount STAmount::setRate(uint64 rate)
|
||||
return STAmount(CURRENCY_ONE, ACCOUNT_ONE, mantissa, exponent);
|
||||
}
|
||||
|
||||
// Taker gets all taker can pay for with saTakerFunds, limited by saOfferPays and saOfferFunds.
|
||||
// --> uTakerPaysRate: >= QUALITY_ONE
|
||||
// --> uOfferPaysRate: >= QUALITY_ONE
|
||||
// --> saOfferFunds: Limit for saOfferPays
|
||||
// Taker gets all taker can pay for with saTakerFunds/uTakerPaysRate, limited by saOfferPays and saOfferFunds/uOfferPaysRate.
|
||||
//
|
||||
// Existing offer is on the books. Offer owner get's their rate.
|
||||
//
|
||||
// Taker pays what they can. If taker is an offer, doesn't matter what rate taker is. Taker is spending at same or better rate
|
||||
// than they wanted. Taker should consider themselves as wanting to buy X amount. Taker is willing to pay at most the rate of Y/X
|
||||
// each. Therefore, after having some part of their offer fulfilled at a better rate their offer should be reduced accordingly.
|
||||
//
|
||||
// YYY Could have a flag for spend up to behaviour vs current limit spend rate.
|
||||
//
|
||||
// There are no quality costs for offer vs offer taking.
|
||||
//
|
||||
// --> uTakerPaysRate: >= QUALITY_ONE | TransferRate for third party IOUs paid by taker.
|
||||
// --> uOfferPaysRate: >= QUALITY_ONE | TransferRate for third party IOUs paid by offer owner.
|
||||
// --> saOfferRate: Original saOfferGets/saOfferPays, when offer was made.
|
||||
// --> saOfferFunds: Limit for saOfferPays : How much can pay including fees.
|
||||
// --> saTakerFunds: Limit for saOfferGets : How much taker really wants. : Driver
|
||||
// --> saOfferPays: Request : this should be reduced as the offer is fullfilled.
|
||||
// --> saOfferGets: Request : this should be reduced as the offer is fullfilled.
|
||||
// --> saTakerPays: Total : Used to know the approximate ratio of the exchange.
|
||||
// --> saTakerGets: Total : Used to know the approximate ratio of the exchange.
|
||||
// --> saTakerGets: Limit for taker to get.
|
||||
// <-- saTakerPaid: Actual
|
||||
// <-- saTakerGot: Actual
|
||||
// <-- saTakerIssuerFee: Actual
|
||||
@@ -1014,62 +1034,63 @@ STAmount STAmount::setRate(uint64 rate)
|
||||
// <-- bRemove: remove offer it is either fullfilled or unfunded
|
||||
bool STAmount::applyOffer(
|
||||
const uint32 uTakerPaysRate, const uint32 uOfferPaysRate,
|
||||
const STAmount& saOfferRate,
|
||||
const STAmount& saOfferFunds, const STAmount& saTakerFunds,
|
||||
const STAmount& saOfferPays, const STAmount& saOfferGets,
|
||||
const STAmount& saTakerPays, const STAmount& saTakerGets,
|
||||
const STAmount& saTakerGets,
|
||||
STAmount& saTakerPaid, STAmount& saTakerGot,
|
||||
STAmount& saTakerIssuerFee, STAmount& saOfferIssuerFee)
|
||||
{
|
||||
saOfferGets.throwComparable(saTakerPays);
|
||||
saOfferGets.throwComparable(saTakerFunds);
|
||||
|
||||
assert(!saOfferFunds.isZero() && !saTakerFunds.isZero()); // Must have funds.
|
||||
assert(!saOfferGets.isZero() && !saOfferPays.isZero()); // Must not be a null offer.
|
||||
|
||||
// Amount offer can pay out, limited by funds and fees.
|
||||
// Limit offerer funds available, by transfer fees.
|
||||
STAmount saOfferFundsAvailable = QUALITY_ONE == uOfferPaysRate
|
||||
? saOfferFunds
|
||||
: STAmount::divide(saOfferFunds, STAmount(CURRENCY_ONE, uOfferPaysRate, -9));
|
||||
|
||||
// Amount offer can pay out, limited by offer and funds.
|
||||
STAmount saOfferPaysAvailable = std::min(saOfferFundsAvailable, saOfferPays);
|
||||
cLog(lsINFO) << "applyOffer: uOfferPaysRate=" << uOfferPaysRate;
|
||||
cLog(lsINFO) << "applyOffer: saOfferFundsAvailable=" << saOfferFundsAvailable.getFullText();
|
||||
|
||||
// Amount offer can get in proportion, limited by offer funds.
|
||||
STAmount saOfferGetsAvailable =
|
||||
saOfferFundsAvailable == saOfferPays
|
||||
? saOfferGets // Offer was fully funded, avoid shenanigans.
|
||||
: divide(multiply(saTakerPays, saOfferPaysAvailable, CURRENCY_ONE, ACCOUNT_ONE), saTakerGets, saOfferGets.getCurrency(), saOfferGets.getIssuer());
|
||||
// Limit taker funds available, by transfer fees.
|
||||
STAmount saTakerFundsAvailable = QUALITY_ONE == uTakerPaysRate
|
||||
? saTakerFunds
|
||||
: STAmount::divide(saTakerFunds, STAmount(CURRENCY_ONE, uTakerPaysRate, -9));
|
||||
|
||||
// Amount taker can spend, limited by funds and fees.
|
||||
STAmount saTakerFundsAvailable = QUALITY_ONE == uTakerPaysRate
|
||||
? saTakerFunds
|
||||
: STAmount::divide(saTakerFunds, STAmount(CURRENCY_ONE, uTakerPaysRate, -9));
|
||||
cLog(lsINFO) << "applyOffer: uTakerPaysRate=" << uTakerPaysRate;
|
||||
cLog(lsINFO) << "applyOffer: saTakerFundsAvailable=" << saTakerFundsAvailable.getFullText();
|
||||
|
||||
if (saOfferGets == saOfferGetsAvailable && saTakerFundsAvailable >= saOfferGets)
|
||||
STAmount saOfferPaysAvailable; // Amount offer can pay out, limited by offer and offerer funds.
|
||||
STAmount saOfferGetsAvailable; // Amount offer would get, limited by offer funds.
|
||||
|
||||
if (saOfferFundsAvailable >= saOfferPays)
|
||||
{
|
||||
// Taker gets all of offer available.
|
||||
saTakerPaid = saOfferGets; // Taker paid what offer could get.
|
||||
saTakerGot = saOfferPays; // Taker got what offer could pay.
|
||||
// Offer was fully funded, avoid math shenanigans.
|
||||
|
||||
Log(lsINFO) << "applyOffer: took all outright";
|
||||
}
|
||||
else if (saTakerFunds >= saOfferGetsAvailable)
|
||||
{
|
||||
// Taker gets all of offer available.
|
||||
saTakerPaid = saOfferGetsAvailable; // Taker paid what offer could get.
|
||||
saTakerGot = saOfferPaysAvailable; // Taker got what offer could pay.
|
||||
|
||||
Log(lsINFO) << "applyOffer: took all available";
|
||||
saOfferPaysAvailable = saOfferPays;
|
||||
saOfferGetsAvailable = saOfferGets;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Taker only get's a portion of offer.
|
||||
saTakerPaid = saTakerFunds; // Taker paid all he had.
|
||||
saTakerGot = divide(multiply(saTakerFunds, saOfferPaysAvailable, CURRENCY_ONE, ACCOUNT_ONE), saOfferGetsAvailable, saOfferPays.getCurrency(), saOfferPays.getIssuer());
|
||||
|
||||
Log(lsINFO) << "applyOffer: saTakerGot=" << saTakerGot.getFullText();
|
||||
Log(lsINFO) << "applyOffer: saOfferPaysAvailable=" << saOfferPaysAvailable.getFullText();
|
||||
// Offer has limited funding, limit offer gets and pays by funds available.
|
||||
saOfferPaysAvailable = saOfferFundsAvailable;
|
||||
saOfferGetsAvailable = multiply(saOfferFundsAvailable, saOfferRate, saOfferGets);
|
||||
}
|
||||
|
||||
cLog(lsINFO) << "applyOffer: saOfferPaysAvailable=" << saOfferFundsAvailable.getFullText();
|
||||
cLog(lsINFO) << "applyOffer: saOfferGetsAvailable=" << saOfferGetsAvailable.getFullText();
|
||||
|
||||
STAmount saTakerGetsAvailable = saTakerFunds >= saOfferGetsAvailable
|
||||
? saTakerGets
|
||||
: multiply(saTakerFunds, saOfferRate, saTakerGets); // Amount can afford.
|
||||
|
||||
saTakerGot = std::min(saTakerGets, saOfferPaysAvailable); // Limit by wanted and available.
|
||||
saTakerPaid = saTakerGot == saOfferPaysAvailable
|
||||
? saOfferGetsAvailable
|
||||
: multiply(saTakerGot, saOfferRate, saTakerFunds);
|
||||
|
||||
if (uTakerPaysRate == QUALITY_ONE)
|
||||
{
|
||||
saTakerIssuerFee = STAmount(saTakerPaid.getCurrency(), saTakerPaid.getIssuer());
|
||||
@@ -1079,7 +1100,7 @@ bool STAmount::applyOffer(
|
||||
// Compute fees in a rounding safe way.
|
||||
STAmount saTotal = STAmount::multiply(saTakerPaid, STAmount(CURRENCY_ONE, uTakerPaysRate, -9));
|
||||
|
||||
saTakerIssuerFee = (saTotal > saTakerFunds) ? saTakerFunds-saTakerPaid : saTotal - saTakerPaid;
|
||||
saTakerIssuerFee = (saTotal > saTakerFunds) ? saTakerFunds-saTakerPaid : saTotal-saTakerPaid;
|
||||
}
|
||||
|
||||
if (uOfferPaysRate == QUALITY_ONE)
|
||||
@@ -1089,11 +1110,13 @@ bool STAmount::applyOffer(
|
||||
else
|
||||
{
|
||||
// Compute fees in a rounding safe way.
|
||||
STAmount saTotal = STAmount::multiply(saTakerPaid, STAmount(CURRENCY_ONE, uTakerPaysRate, -9));
|
||||
STAmount saTotal = STAmount::multiply(saTakerGot, STAmount(CURRENCY_ONE, uOfferPaysRate, -9));
|
||||
|
||||
saOfferIssuerFee = (saTotal > saOfferFunds) ? saOfferFunds-saTakerGot : saTotal - saTakerGot;
|
||||
saOfferIssuerFee = (saTotal > saOfferFunds) ? saOfferFunds-saTakerGot : saTotal-saTakerGot;
|
||||
}
|
||||
|
||||
cLog(lsINFO) << "applyOffer: saTakerGot=" << saTakerGot.getFullText();
|
||||
|
||||
return saTakerGot >= saOfferPays;
|
||||
}
|
||||
|
||||
@@ -1119,13 +1142,13 @@ uint64 STAmount::muldiv(uint64 a, uint64 b, uint64 c)
|
||||
if ((a == 0) || (b == 0)) return 0;
|
||||
|
||||
CBigNum v;
|
||||
if ((BN_add_word(&v, a * 10 + 5) != 1) ||
|
||||
(BN_mul_word(&v, b * 10 + 5) != 1) ||
|
||||
(BN_div_word(&v, c) == ((BN_ULONG) -1)) ||
|
||||
(BN_div_word(&v, 100) == ((BN_ULONG) -1)))
|
||||
if ((BN_add_word64(&v, a * 10 + 5) != 1) ||
|
||||
(BN_mul_word64(&v, b * 10 + 5) != 1) ||
|
||||
(BN_div_word64(&v, c) == ((uint64) -1)) ||
|
||||
(BN_div_word64(&v, 100) == ((uint64) -1)))
|
||||
throw std::runtime_error("muldiv error");
|
||||
|
||||
return v.getulong();
|
||||
return v.getuint64();
|
||||
}
|
||||
|
||||
uint64 STAmount::convertToDisplayAmount(const STAmount& internalAmount, uint64 totalNow, uint64 totalInit)
|
||||
@@ -1239,7 +1262,6 @@ BOOST_AUTO_TEST_CASE( NativeCurrency_test )
|
||||
{
|
||||
STAmount zero, one(1), hundred(100);
|
||||
|
||||
if (sizeof(BN_ULONG) < (64 / 8)) BOOST_FAIL("BN too small");
|
||||
if (serdes(zero) != zero) BOOST_FAIL("STAmount fail");
|
||||
if (serdes(one) != one) BOOST_FAIL("STAmount fail");
|
||||
if (serdes(hundred) != hundred) BOOST_FAIL("STAmount fail");
|
||||
@@ -1385,16 +1407,16 @@ BOOST_AUTO_TEST_CASE( CustomCurrency_test )
|
||||
if (STAmount(CURRENCY_ONE, ACCOUNT_ONE, 31,-2).getText() != "0.31") BOOST_FAIL("STAmount fail");
|
||||
|
||||
if (STAmount::multiply(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 20), STAmount(3), CURRENCY_ONE, ACCOUNT_ONE).getText() != "60")
|
||||
BOOST_FAIL("STAmount multiply fail");
|
||||
BOOST_FAIL("STAmount multiply fail 1");
|
||||
if (STAmount::multiply(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 20), STAmount(3), uint160(), ACCOUNT_XRP).getText() != "60")
|
||||
BOOST_FAIL("STAmount multiply fail");
|
||||
BOOST_FAIL("STAmount multiply fail 2");
|
||||
if (STAmount::multiply(STAmount(20), STAmount(3), CURRENCY_ONE, ACCOUNT_ONE).getText() != "60")
|
||||
BOOST_FAIL("STAmount multiply fail");
|
||||
BOOST_FAIL("STAmount multiply fail 3");
|
||||
if (STAmount::multiply(STAmount(20), STAmount(3), uint160(), ACCOUNT_XRP).getText() != "60")
|
||||
BOOST_FAIL("STAmount multiply fail");
|
||||
BOOST_FAIL("STAmount multiply fail 4");
|
||||
if (STAmount::divide(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 60), STAmount(3), CURRENCY_ONE, ACCOUNT_ONE).getText() != "20")
|
||||
{
|
||||
Log(lsFATAL) << "60/3 = " <<
|
||||
cLog(lsFATAL) << "60/3 = " <<
|
||||
STAmount::divide(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 60),
|
||||
STAmount(3), CURRENCY_ONE, ACCOUNT_ONE).getText();
|
||||
BOOST_FAIL("STAmount divide fail");
|
||||
@@ -1449,7 +1471,7 @@ static void mulTest(int a, int b)
|
||||
STAmount prod2(CURRENCY_ONE, ACCOUNT_ONE, static_cast<uint64>(a) * static_cast<uint64>(b));
|
||||
if (prod1 != prod2)
|
||||
{
|
||||
Log(lsWARNING) << "nn(" << aa.getFullText() << " * " << bb.getFullText() << ") = " << prod1.getFullText()
|
||||
cLog(lsWARNING) << "nn(" << aa.getFullText() << " * " << bb.getFullText() << ") = " << prod1.getFullText()
|
||||
<< " not " << prod2.getFullText();
|
||||
BOOST_WARN("Multiplication result is not exact");
|
||||
}
|
||||
@@ -1457,7 +1479,7 @@ static void mulTest(int a, int b)
|
||||
prod1 = STAmount::multiply(aa, bb, CURRENCY_ONE, ACCOUNT_ONE);
|
||||
if (prod1 != prod2)
|
||||
{
|
||||
Log(lsWARNING) << "n(" << aa.getFullText() << " * " << bb.getFullText() << ") = " << prod1.getFullText()
|
||||
cLog(lsWARNING) << "n(" << aa.getFullText() << " * " << bb.getFullText() << ") = " << prod1.getFullText()
|
||||
<< " not " << prod2.getFullText();
|
||||
BOOST_WARN("Multiplication result is not exact");
|
||||
}
|
||||
@@ -1466,25 +1488,39 @@ static void mulTest(int a, int b)
|
||||
|
||||
BOOST_AUTO_TEST_CASE( CurrencyMulDivTests )
|
||||
{
|
||||
CBigNum b;
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
uint64 r = rand();
|
||||
r <<= 32;
|
||||
r |= rand();
|
||||
b.setuint64(r);
|
||||
if (b.getuint64() != r)
|
||||
{
|
||||
cLog(lsFATAL) << r << " != " << b.getuint64() << " " << b.ToString(16);
|
||||
BOOST_FAIL("setull64/getull64 failure");
|
||||
}
|
||||
}
|
||||
|
||||
// Test currency multiplication and division operations such as
|
||||
// convertToDisplayAmount, convertToInternalAmount, getRate, getClaimed, and getNeeded
|
||||
|
||||
if (STAmount::getRate(STAmount(1), STAmount(10)) != (((100ul-14)<<(64-8))|1000000000000000ul))
|
||||
BOOST_FAIL("STAmount getRate fail");
|
||||
if (STAmount::getRate(STAmount(10), STAmount(1)) != (((100ul-16)<<(64-8))|1000000000000000ul))
|
||||
BOOST_FAIL("STAmount getRate fail");
|
||||
if (STAmount::getRate(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 1), STAmount(CURRENCY_ONE, ACCOUNT_ONE, 10)) != (((100ul-14)<<(64-8))|1000000000000000ul))
|
||||
BOOST_FAIL("STAmount getRate fail");
|
||||
if (STAmount::getRate(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 10), STAmount(CURRENCY_ONE, ACCOUNT_ONE, 1)) != (((100ul-16)<<(64-8))|1000000000000000ul))
|
||||
BOOST_FAIL("STAmount getRate fail");
|
||||
if (STAmount::getRate(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 1), STAmount(10)) != (((100ul-14)<<(64-8))|1000000000000000ul))
|
||||
BOOST_FAIL("STAmount getRate fail");
|
||||
if (STAmount::getRate(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 10), STAmount(1)) != (((100ul-16)<<(64-8))|1000000000000000ul))
|
||||
BOOST_FAIL("STAmount getRate fail");
|
||||
if (STAmount::getRate(STAmount(1), STAmount(CURRENCY_ONE, ACCOUNT_ONE, 10)) != (((100ul-14)<<(64-8))|1000000000000000ul))
|
||||
BOOST_FAIL("STAmount getRate fail");
|
||||
if (STAmount::getRate(STAmount(10), STAmount(CURRENCY_ONE, ACCOUNT_ONE, 1)) != (((100ul-16)<<(64-8))|1000000000000000ul))
|
||||
BOOST_FAIL("STAmount getRate fail");
|
||||
if (STAmount::getRate(STAmount(1), STAmount(10)) != (((100ull-14)<<(64-8))|1000000000000000ull))
|
||||
BOOST_FAIL("STAmount getRate fail 1");
|
||||
if (STAmount::getRate(STAmount(10), STAmount(1)) != (((100ull-16)<<(64-8))|1000000000000000ull))
|
||||
BOOST_FAIL("STAmount getRate fail 2");
|
||||
if (STAmount::getRate(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 1), STAmount(CURRENCY_ONE, ACCOUNT_ONE, 10)) != (((100ull-14)<<(64-8))|1000000000000000ull))
|
||||
BOOST_FAIL("STAmount getRate fail 3");
|
||||
if (STAmount::getRate(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 10), STAmount(CURRENCY_ONE, ACCOUNT_ONE, 1)) != (((100ull-16)<<(64-8))|1000000000000000ull))
|
||||
BOOST_FAIL("STAmount getRate fail 4");
|
||||
if (STAmount::getRate(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 1), STAmount(10)) != (((100ull-14)<<(64-8))|1000000000000000ull))
|
||||
BOOST_FAIL("STAmount getRate fail 5");
|
||||
if (STAmount::getRate(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 10), STAmount(1)) != (((100ull-16)<<(64-8))|1000000000000000ull))
|
||||
BOOST_FAIL("STAmount getRate fail 6");
|
||||
if (STAmount::getRate(STAmount(1), STAmount(CURRENCY_ONE, ACCOUNT_ONE, 10)) != (((100ull-14)<<(64-8))|1000000000000000ull))
|
||||
BOOST_FAIL("STAmount getRate fail 7");
|
||||
if (STAmount::getRate(STAmount(10), STAmount(CURRENCY_ONE, ACCOUNT_ONE, 1)) != (((100ull-16)<<(64-8))|1000000000000000ull))
|
||||
BOOST_FAIL("STAmount getRate fail 8");
|
||||
|
||||
roundTest(1, 3, 3); roundTest(2, 3, 9); roundTest(1, 7, 21); roundTest(1, 2, 4);
|
||||
roundTest(3, 9, 18); roundTest(7, 11, 44);
|
||||
|
||||
@@ -18,7 +18,9 @@
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
SETUP_LOG();
|
||||
|
||||
LogPartition TaggedCachePartition("TaggedCache");
|
||||
LogPartition AutoSocketPartition("AutoSocket");
|
||||
Application* theApp = NULL;
|
||||
|
||||
DatabaseCon::DatabaseCon(const std::string& strName, const char *initStrings[], int initCount)
|
||||
@@ -40,7 +42,7 @@ DatabaseCon::~DatabaseCon()
|
||||
Application::Application() :
|
||||
mIOWork(mIOService), mAuxWork(mAuxService), mUNL(mIOService), mNetOps(mIOService, &mLedgerMaster),
|
||||
mTempNodeCache("NodeCache", 16384, 90), mHashedObjectStore(16384, 300),
|
||||
mSNTPClient(mAuxService), mRPCHandler(&mNetOps),
|
||||
mSNTPClient(mAuxService), mRPCHandler(&mNetOps), mFeeTrack(),
|
||||
mRpcDB(NULL), mTxnDB(NULL), mLedgerDB(NULL), mWalletDB(NULL), mHashNodeDB(NULL), mNetNodeDB(NULL),
|
||||
mConnectionPool(mIOService), mPeerDoor(NULL), mRPCDoor(NULL), mWSPublicDoor(NULL), mWSPrivateDoor(NULL),
|
||||
mSweepTimer(mAuxService)
|
||||
@@ -54,9 +56,11 @@ Application::Application() :
|
||||
|
||||
extern const char *RpcDBInit[], *TxnDBInit[], *LedgerDBInit[], *WalletDBInit[], *HashNodeDBInit[], *NetNodeDBInit[];
|
||||
extern int RpcDBCount, TxnDBCount, LedgerDBCount, WalletDBCount, HashNodeDBCount, NetNodeDBCount;
|
||||
bool Instance::running = true;
|
||||
|
||||
void Application::stop()
|
||||
{
|
||||
cLog(lsINFO) << "Received shutdown request";
|
||||
mIOService.stop();
|
||||
mJobQueue.shutdown();
|
||||
mHashedObjectStore.bulkWrite();
|
||||
@@ -64,6 +68,7 @@ void Application::stop()
|
||||
mAuxService.stop();
|
||||
|
||||
cLog(lsINFO) << "Stopped: " << mIOService.stopped();
|
||||
Instance::shutdown();
|
||||
}
|
||||
|
||||
static void InitDB(DatabaseCon** dbCon, const char *fileName, const char *dbInit[], int dbCount)
|
||||
@@ -80,7 +85,7 @@ void sigIntHandler(int)
|
||||
}
|
||||
#endif
|
||||
|
||||
void Application::run()
|
||||
void Application::setup()
|
||||
{
|
||||
#ifndef WIN32
|
||||
#ifdef SIGINT
|
||||
@@ -102,9 +107,7 @@ void Application::run()
|
||||
LogPartition::setSeverity(lsDEBUG);
|
||||
}
|
||||
|
||||
boost::thread auxThread(boost::bind(&boost::asio::io_service::run, &mAuxService));
|
||||
auxThread.detach();
|
||||
|
||||
boost::thread(boost::bind(&boost::asio::io_service::run, &mAuxService)).detach();
|
||||
|
||||
if (!theConfig.RUN_STANDALONE)
|
||||
mSNTPClient.init(theConfig.SNTP_SERVERS);
|
||||
@@ -119,16 +122,21 @@ void Application::run()
|
||||
boost::thread t5(boost::bind(&InitDB, &mHashNodeDB, "hashnode.db", HashNodeDBInit, HashNodeDBCount));
|
||||
boost::thread t6(boost::bind(&InitDB, &mNetNodeDB, "netnode.db", NetNodeDBInit, NetNodeDBCount));
|
||||
t1.join(); t2.join(); t3.join(); t4.join(); t5.join(); t6.join();
|
||||
mTxnDB->getDB()->setupCheckpointing();
|
||||
mLedgerDB->getDB()->setupCheckpointing();
|
||||
mHashNodeDB->getDB()->setupCheckpointing();
|
||||
|
||||
if (theConfig.START_UP == Config::FRESH)
|
||||
{
|
||||
cLog(lsINFO) << "Starting new Ledger";
|
||||
|
||||
startNewLedger();
|
||||
}
|
||||
else if (theConfig.START_UP == Config::LOAD)
|
||||
{
|
||||
cLog(lsINFO) << "Loading Old Ledger";
|
||||
loadOldLedger();
|
||||
cLog(lsINFO) << "Loading specified Ledger";
|
||||
|
||||
loadOldLedger(theConfig.START_LEDGER);
|
||||
}
|
||||
else if (theConfig.START_UP == Config::NETWORK)
|
||||
{ // This should probably become the default once we have a stable network
|
||||
@@ -157,7 +165,17 @@ void Application::run()
|
||||
//
|
||||
if (!theConfig.RUN_STANDALONE && !theConfig.PEER_IP.empty() && theConfig.PEER_PORT)
|
||||
{
|
||||
mPeerDoor = new PeerDoor(mIOService);
|
||||
try
|
||||
{
|
||||
mPeerDoor = new PeerDoor(mIOService);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
// Must run as directed or exit.
|
||||
cLog(lsFATAL) << boost::str(boost::format("Can not open peer service: %s") % e.what());
|
||||
|
||||
exit(3);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -169,7 +187,17 @@ void Application::run()
|
||||
//
|
||||
if (!theConfig.RPC_IP.empty() && theConfig.RPC_PORT)
|
||||
{
|
||||
mRPCDoor = new RPCDoor(mIOService);
|
||||
try
|
||||
{
|
||||
mRPCDoor = new RPCDoor(mIOService);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
// Must run as directed or exit.
|
||||
cLog(lsFATAL) << boost::str(boost::format("Can not open RPC service: %s") % e.what());
|
||||
|
||||
exit(3);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -181,7 +209,17 @@ void Application::run()
|
||||
//
|
||||
if (!theConfig.WEBSOCKET_IP.empty() && theConfig.WEBSOCKET_PORT)
|
||||
{
|
||||
mWSPrivateDoor = WSDoor::createWSDoor(theConfig.WEBSOCKET_IP, theConfig.WEBSOCKET_PORT, false);
|
||||
try
|
||||
{
|
||||
mWSPrivateDoor = WSDoor::createWSDoor(theConfig.WEBSOCKET_IP, theConfig.WEBSOCKET_PORT, false);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
// Must run as directed or exit.
|
||||
cLog(lsFATAL) << boost::str(boost::format("Can not open private websocket service: %s") % e.what());
|
||||
|
||||
exit(3);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -193,7 +231,17 @@ void Application::run()
|
||||
//
|
||||
if (!theConfig.WEBSOCKET_PUBLIC_IP.empty() && theConfig.WEBSOCKET_PUBLIC_PORT)
|
||||
{
|
||||
mWSPublicDoor = WSDoor::createWSDoor(theConfig.WEBSOCKET_PUBLIC_IP, theConfig.WEBSOCKET_PUBLIC_PORT, true);
|
||||
try
|
||||
{
|
||||
mWSPublicDoor = WSDoor::createWSDoor(theConfig.WEBSOCKET_PUBLIC_IP, theConfig.WEBSOCKET_PUBLIC_PORT, true);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
// Must run as directed or exit.
|
||||
cLog(lsFATAL) << boost::str(boost::format("Can not open public websocket service: %s") % e.what());
|
||||
|
||||
exit(3);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -210,11 +258,15 @@ void Application::run()
|
||||
if (theConfig.RUN_STANDALONE)
|
||||
{
|
||||
cLog(lsWARNING) << "Running in standalone mode";
|
||||
|
||||
mNetOps.setStandAlone();
|
||||
}
|
||||
else
|
||||
mNetOps.setStateTimer();
|
||||
}
|
||||
|
||||
void Application::run()
|
||||
{
|
||||
mIOService.run(); // This blocks
|
||||
|
||||
if (mWSPublicDoor)
|
||||
@@ -228,11 +280,20 @@ void Application::run()
|
||||
|
||||
void Application::sweep()
|
||||
{
|
||||
|
||||
boost::filesystem::space_info space = boost::filesystem::space(theConfig.DATA_DIR);
|
||||
if (space.available < (128 * 1024 * 1024))
|
||||
{
|
||||
cLog(lsFATAL) << "Remaining free disk space is less than 128MB";
|
||||
theApp->stop();
|
||||
}
|
||||
|
||||
mMasterTransaction.sweep();
|
||||
mHashedObjectStore.sweep();
|
||||
mLedgerMaster.sweep();
|
||||
mTempNodeCache.sweep();
|
||||
mValidations.sweep();
|
||||
getMasterLedgerAcquire().sweep();
|
||||
mSweepTimer.expires_from_now(boost::posix_time::seconds(60));
|
||||
mSweepTimer.async_wait(boost::bind(&Application::sweep, this));
|
||||
}
|
||||
@@ -274,49 +335,65 @@ void Application::startNewLedger()
|
||||
}
|
||||
}
|
||||
|
||||
void Application::loadOldLedger()
|
||||
void Application::loadOldLedger(const std::string& l)
|
||||
{
|
||||
try
|
||||
{
|
||||
Ledger::pointer lastLedger = Ledger::getLastFullLedger();
|
||||
Ledger::pointer loadLedger;
|
||||
if (l.empty() || (l == "latest"))
|
||||
loadLedger = Ledger::getLastFullLedger();
|
||||
else if (l.length() == 64)
|
||||
{ // by hash
|
||||
uint256 hash;
|
||||
hash.SetHex(l);
|
||||
loadLedger = Ledger::loadByHash(hash);
|
||||
}
|
||||
else // assume by sequence
|
||||
loadLedger = Ledger::loadByIndex(boost::lexical_cast<uint32>(l));
|
||||
|
||||
if (!lastLedger)
|
||||
if (!loadLedger)
|
||||
{
|
||||
cLog(lsFATAL) << "No Ledger found?" << std::endl;
|
||||
exit(-1);
|
||||
}
|
||||
lastLedger->setClosed();
|
||||
loadLedger->setClosed();
|
||||
|
||||
cLog(lsINFO) << "Loading ledger " << lastLedger->getHash() << " seq:" << lastLedger->getLedgerSeq();
|
||||
cLog(lsINFO) << "Loading ledger " << loadLedger->getHash() << " seq:" << loadLedger->getLedgerSeq();
|
||||
|
||||
if (lastLedger->getAccountHash().isZero())
|
||||
if (loadLedger->getAccountHash().isZero())
|
||||
{
|
||||
cLog(lsFATAL) << "Ledger is empty.";
|
||||
assert(false);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (!lastLedger->walkLedger())
|
||||
if (!loadLedger->walkLedger())
|
||||
{
|
||||
cLog(lsFATAL) << "Ledger is missing nodes.";
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (!lastLedger->assertSane())
|
||||
if (!loadLedger->assertSane())
|
||||
{
|
||||
cLog(lsFATAL) << "Ledger is not sane.";
|
||||
exit(-1);
|
||||
}
|
||||
mLedgerMaster.setLedgerRangePresent(0, lastLedger->getLedgerSeq());
|
||||
mLedgerMaster.setLedgerRangePresent(loadLedger->getLedgerSeq(), loadLedger->getLedgerSeq());
|
||||
|
||||
Ledger::pointer openLedger = boost::make_shared<Ledger>(false, boost::ref(*lastLedger));
|
||||
mLedgerMaster.switchLedgers(lastLedger, openLedger);
|
||||
mNetOps.setLastCloseTime(lastLedger->getCloseTimeNC());
|
||||
Ledger::pointer openLedger = boost::make_shared<Ledger>(false, boost::ref(*loadLedger));
|
||||
mLedgerMaster.switchLedgers(loadLedger, openLedger);
|
||||
mNetOps.setLastCloseTime(loadLedger->getCloseTimeNC());
|
||||
}
|
||||
catch (SHAMapMissingNode& mn)
|
||||
{
|
||||
cLog(lsFATAL) << "Cannot load ledger. " << mn;
|
||||
cLog(lsFATAL) << "Data is missing for selected ledger";
|
||||
exit(-1);
|
||||
}
|
||||
catch (boost::bad_lexical_cast& blc)
|
||||
{
|
||||
cLog(lsFATAL) << "Ledger specified '" << l << "' is not valid";
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "RPCHandler.h"
|
||||
#include "ProofOfWork.h"
|
||||
#include "LoadManager.h"
|
||||
#include "TransactionQueue.h"
|
||||
|
||||
class RPCDoor;
|
||||
class PeerDoor;
|
||||
@@ -63,6 +64,8 @@ class Application
|
||||
RPCHandler mRPCHandler;
|
||||
ProofOfWorkGenerator mPOWGen;
|
||||
LoadManager mLoadMgr;
|
||||
LoadFeeTrack mFeeTrack;
|
||||
TXQueue mTxnQueue;
|
||||
|
||||
DatabaseCon *mRpcDB, *mTxnDB, *mLedgerDB, *mWalletDB, *mHashNodeDB, *mNetNodeDB;
|
||||
|
||||
@@ -81,7 +84,7 @@ class Application
|
||||
boost::recursive_mutex mPeerMapLock;
|
||||
|
||||
void startNewLedger();
|
||||
void loadOldLedger();
|
||||
void loadOldLedger(const std::string&);
|
||||
|
||||
public:
|
||||
Application();
|
||||
@@ -109,6 +112,9 @@ public:
|
||||
boost::recursive_mutex& getMasterLock() { return mMasterLock; }
|
||||
ProofOfWorkGenerator& getPowGen() { return mPOWGen; }
|
||||
LoadManager& getLoadManager() { return mLoadMgr; }
|
||||
LoadFeeTrack& getFeeTrack() { return mFeeTrack; }
|
||||
TXQueue& getTxnQueue() { return mTxnQueue; }
|
||||
PeerDoor& getPeerDoor() { return *mPeerDoor; }
|
||||
|
||||
|
||||
bool isNew(const uint256& s) { return mSuppressions.addSuppression(s); }
|
||||
@@ -128,6 +134,7 @@ public:
|
||||
uint256 getNonce256() { return mNonce256; }
|
||||
std::size_t getNonceST() { return mNonceST; }
|
||||
|
||||
void setup();
|
||||
void run();
|
||||
void stop();
|
||||
void sweep();
|
||||
|
||||
204
src/cpp/ripple/AutoSocket.h
Normal file
204
src/cpp/ripple/AutoSocket.h
Normal file
@@ -0,0 +1,204 @@
|
||||
#ifndef __AUTOSOCKET_H_
|
||||
#define __AUTOSOCKET_H_
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/smart_ptr.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <boost/asio/read_until.hpp>
|
||||
|
||||
#include "Log.h"
|
||||
extern LogPartition AutoSocketPartition;
|
||||
|
||||
// Socket wrapper that supports both SSL and non-SSL connections.
|
||||
// Generally, handle it as you would an SSL connection.
|
||||
// To force a non-SSL connection, just don't call async_handshake.
|
||||
// To force SSL only inbound, call setSSLOnly.
|
||||
|
||||
namespace basio = boost::asio;
|
||||
namespace bassl = basio::ssl;
|
||||
|
||||
class AutoSocket
|
||||
{
|
||||
public:
|
||||
typedef bassl::stream<basio::ip::tcp::socket> ssl_socket;
|
||||
typedef boost::shared_ptr<ssl_socket> socket_ptr;
|
||||
typedef ssl_socket::next_layer_type plain_socket;
|
||||
typedef ssl_socket::lowest_layer_type lowest_layer_type;
|
||||
typedef ssl_socket::handshake_type handshake_type;
|
||||
typedef boost::system::error_code error_code;
|
||||
typedef boost::function<void(error_code)> callback;
|
||||
|
||||
protected:
|
||||
socket_ptr mSocket;
|
||||
bool mSecure;
|
||||
|
||||
std::vector<char> mBuffer;
|
||||
|
||||
public:
|
||||
AutoSocket(basio::io_service& s, bassl::context& c) : mSecure(false), mBuffer(4)
|
||||
{
|
||||
mSocket = boost::make_shared<ssl_socket>(boost::ref(s), boost::ref(c));
|
||||
}
|
||||
|
||||
AutoSocket(basio::io_service& s, bassl::context& c, bool secureOnly, bool plainOnly)
|
||||
: mSecure(secureOnly), mBuffer((plainOnly || secureOnly) ? 0 : 4)
|
||||
{
|
||||
mSocket = boost::make_shared<ssl_socket>(boost::ref(s), boost::ref(c));
|
||||
}
|
||||
|
||||
bool isSecure() { return mSecure; }
|
||||
ssl_socket& SSLSocket() { return *mSocket; }
|
||||
plain_socket& PlainSocket() { return mSocket->next_layer(); }
|
||||
void setSSLOnly() { mSecure = true;}
|
||||
void setPlainOnly() { mBuffer.clear(); }
|
||||
|
||||
lowest_layer_type& lowest_layer() { return mSocket->lowest_layer(); }
|
||||
|
||||
void swap(AutoSocket& s)
|
||||
{
|
||||
mBuffer.swap(s.mBuffer);
|
||||
mSocket.swap(s.mSocket);
|
||||
std::swap(mSecure, s.mSecure);
|
||||
}
|
||||
|
||||
void async_handshake(handshake_type type, callback cbFunc)
|
||||
{
|
||||
if ((type == ssl_socket::client) || (mSecure))
|
||||
{ // must be ssl
|
||||
mSecure = true;
|
||||
mSocket->async_handshake(type, cbFunc);
|
||||
}
|
||||
else if (mBuffer.empty())
|
||||
{ // must be plain
|
||||
mSecure = false;
|
||||
mSocket->get_io_service().post(boost::bind(cbFunc, error_code()));
|
||||
}
|
||||
else
|
||||
{ // autodetect
|
||||
mSocket->next_layer().async_receive(basio::buffer(mBuffer), basio::socket_base::message_peek,
|
||||
boost::bind(&AutoSocket::handle_autodetect, this, cbFunc, basio::placeholders::error));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ShutdownHandler> void async_shutdown(ShutdownHandler handler)
|
||||
{
|
||||
if (isSecure())
|
||||
mSocket->async_shutdown(handler);
|
||||
else
|
||||
{
|
||||
lowest_layer().shutdown(plain_socket::shutdown_both);
|
||||
mSocket->get_io_service().post(boost::bind(handler, error_code()));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Seq, typename Handler> void async_read_some(const Seq& buffers, Handler handler)
|
||||
{
|
||||
if (isSecure())
|
||||
mSocket->async_read_some(buffers, handler);
|
||||
else
|
||||
PlainSocket().async_read_some(buffers, handler);
|
||||
}
|
||||
|
||||
template <typename Seq, typename Condition, typename Handler>
|
||||
void async_read_until(const Seq& buffers, Condition condition, Handler handler)
|
||||
{
|
||||
if (isSecure())
|
||||
basio::async_read_until(*mSocket, buffers, condition, handler);
|
||||
else
|
||||
basio::async_read_until(PlainSocket(), buffers, condition, handler);
|
||||
}
|
||||
|
||||
template <typename Allocator, typename Handler>
|
||||
void async_read_until(basio::basic_streambuf<Allocator>& buffers, const std::string& delim, Handler handler)
|
||||
{
|
||||
if (isSecure())
|
||||
basio::async_read_until(*mSocket, buffers, delim, handler);
|
||||
else
|
||||
basio::async_read_until(PlainSocket(), buffers, delim, handler);
|
||||
}
|
||||
|
||||
template <typename Allocator, typename MatchCondition, typename Handler>
|
||||
void async_read_until(basio::basic_streambuf<Allocator>& buffers, MatchCondition cond, Handler handler)
|
||||
{
|
||||
if (isSecure())
|
||||
basio::async_read_until(*mSocket, buffers, cond, handler);
|
||||
else
|
||||
basio::async_read_until(PlainSocket(), buffers, cond, handler);
|
||||
}
|
||||
|
||||
template <typename Buf, typename Handler> void async_write(const Buf& buffers, Handler handler)
|
||||
{
|
||||
if (isSecure())
|
||||
boost::asio::async_write(*mSocket, buffers, handler);
|
||||
else
|
||||
boost::asio::async_write(PlainSocket(), buffers, handler);
|
||||
}
|
||||
|
||||
|
||||
template <typename Buf, typename Condition, typename Handler>
|
||||
void async_read(const Buf& buffers, Condition cond, Handler handler)
|
||||
{
|
||||
if (isSecure())
|
||||
boost::asio::async_read(*mSocket, buffers, cond, handler);
|
||||
else
|
||||
boost::asio::async_read(PlainSocket(), buffers, cond, handler);
|
||||
}
|
||||
|
||||
template <typename Allocator, typename Condition, typename Handler>
|
||||
void async_read(basio::basic_streambuf<Allocator>& buffers, Condition cond, Handler handler)
|
||||
{
|
||||
if (isSecure())
|
||||
boost::asio::async_read(*mSocket, buffers, cond, handler);
|
||||
else
|
||||
boost::asio::async_read(PlainSocket(), buffers, cond, handler);
|
||||
}
|
||||
|
||||
template <typename Buf, typename Handler> void async_read(const Buf& buffers, Handler handler)
|
||||
{
|
||||
if (isSecure())
|
||||
boost::asio::async_read(*mSocket, buffers, handler);
|
||||
else
|
||||
boost::asio::async_read(PlainSocket(), buffers, handler);
|
||||
}
|
||||
|
||||
template <typename Seq, typename Handler> void async_write_some(const Seq& buffers, Handler handler)
|
||||
{
|
||||
if (isSecure())
|
||||
mSocket->async_write_some(buffers, handler);
|
||||
else
|
||||
PlainSocket().async_write_some(buffers, handler);
|
||||
}
|
||||
|
||||
protected:
|
||||
void handle_autodetect(callback cbFunc, const error_code& ec)
|
||||
{
|
||||
if (ec)
|
||||
{
|
||||
Log(lsWARNING, AutoSocketPartition) << "Handle autodetect error: " << ec;
|
||||
cbFunc(ec);
|
||||
}
|
||||
else if ((mBuffer[0] < 127) && (mBuffer[0] > 31) &&
|
||||
(mBuffer[1] < 127) && (mBuffer[1] > 31) &&
|
||||
(mBuffer[2] < 127) && (mBuffer[2] > 31) &&
|
||||
(mBuffer[3] < 127) && (mBuffer[3] > 31))
|
||||
{ // not ssl
|
||||
mSecure = false;
|
||||
cbFunc(ec);
|
||||
}
|
||||
else
|
||||
{ // ssl
|
||||
mSecure = true;
|
||||
mSocket->async_handshake(ssl_socket::server, cbFunc);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
22
src/cpp/ripple/BigNum64.h
Normal file
22
src/cpp/ripple/BigNum64.h
Normal file
@@ -0,0 +1,22 @@
|
||||
|
||||
// Support 64-bit word operations on 32-bit platforms
|
||||
|
||||
static int BN_add_word64(BIGNUM *a, uint64 w)
|
||||
{
|
||||
CBigNum bn(w);
|
||||
return BN_add(a, &bn, a);
|
||||
}
|
||||
|
||||
static int BN_mul_word64(BIGNUM *a, uint64 w)
|
||||
{
|
||||
CBigNum bn(w);
|
||||
CAutoBN_CTX ctx;
|
||||
return BN_mul(a, &bn, a, ctx);
|
||||
}
|
||||
|
||||
static uint64 BN_div_word64(BIGNUM *a, uint64 w)
|
||||
{
|
||||
CBigNum bn(w);
|
||||
CAutoBN_CTX ctx;
|
||||
return (BN_div(a, NULL, a, &bn, ctx) == 1) ? 0 : ((uint64)-1);
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <boost/iostreams/concepts.hpp>
|
||||
#include <boost/iostreams/stream.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include <openssl/buffer.h>
|
||||
#include <openssl/evp.h>
|
||||
@@ -13,6 +14,7 @@
|
||||
#include "../json/value.h"
|
||||
#include "../json/reader.h"
|
||||
|
||||
#include "Application.h"
|
||||
#include "RPC.h"
|
||||
#include "Log.h"
|
||||
#include "RPCErr.h"
|
||||
@@ -53,7 +55,25 @@ std::string EncodeBase64(const std::string& s)
|
||||
|
||||
Json::Value RPCParser::parseAsIs(const Json::Value& jvParams)
|
||||
{
|
||||
return Json::Value(Json::objectValue);
|
||||
Json::Value v(Json::objectValue);
|
||||
|
||||
if (jvParams.isArray() && (jvParams.size() > 0))
|
||||
v["params"] = jvParams;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
Json::Value RPCParser::parseInternal(const Json::Value& jvParams)
|
||||
{
|
||||
Json::Value v(Json::objectValue);
|
||||
v["internal_command"] = jvParams[0u];
|
||||
|
||||
Json::Value params(Json::arrayValue);
|
||||
for (unsigned i = 1; i < jvParams.size(); ++i)
|
||||
params.append(jvParams[i]);
|
||||
v["params"] = params;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
// account_info <account>|<nickname>|<account_public_key>
|
||||
@@ -126,6 +146,7 @@ Json::Value RPCParser::parseConnect(const Json::Value& jvParams)
|
||||
return jvRequest;
|
||||
}
|
||||
|
||||
#if ENABLE_INSECURE
|
||||
// data_delete <key>
|
||||
Json::Value RPCParser::parseDataDelete(const Json::Value& jvParams)
|
||||
{
|
||||
@@ -135,7 +156,9 @@ Json::Value RPCParser::parseDataDelete(const Json::Value& jvParams)
|
||||
|
||||
return jvRequest;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENABLE_INSECURE
|
||||
// data_fetch <key>
|
||||
Json::Value RPCParser::parseDataFetch(const Json::Value& jvParams)
|
||||
{
|
||||
@@ -145,7 +168,9 @@ Json::Value RPCParser::parseDataFetch(const Json::Value& jvParams)
|
||||
|
||||
return jvRequest;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENABLE_INSECURE
|
||||
// data_store <key> <value>
|
||||
Json::Value RPCParser::parseDataStore(const Json::Value& jvParams)
|
||||
{
|
||||
@@ -156,6 +181,7 @@ Json::Value RPCParser::parseDataStore(const Json::Value& jvParams)
|
||||
|
||||
return jvRequest;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Return an error for attemping to subscribe/unsubscribe via RPC.
|
||||
Json::Value RPCParser::parseEvented(const Json::Value& jvParams)
|
||||
@@ -203,6 +229,7 @@ Json::Value RPCParser::parseLedger(const Json::Value& jvParams)
|
||||
return jvRequest;
|
||||
}
|
||||
|
||||
#if ENABLE_INSECURE
|
||||
// login <username> <password>
|
||||
Json::Value RPCParser::parseLogin(const Json::Value& jvParams)
|
||||
{
|
||||
@@ -213,6 +240,7 @@ Json::Value RPCParser::parseLogin(const Json::Value& jvParams)
|
||||
|
||||
return jvRequest;
|
||||
}
|
||||
#endif
|
||||
|
||||
// log_level: Get log levels
|
||||
// log_level <severity>: Set master log level to the specified severity
|
||||
@@ -262,15 +290,43 @@ Json::Value RPCParser::parseAccountItems(const Json::Value& jvParams)
|
||||
return jvRequest;
|
||||
}
|
||||
|
||||
|
||||
// submit any transaction to the network
|
||||
// submit private_key json
|
||||
Json::Value RPCParser::parseSubmit(const Json::Value& jvParams)
|
||||
// ripple_path_find json
|
||||
Json::Value RPCParser::parseRipplePathFind(const Json::Value& jvParams)
|
||||
{
|
||||
Json::Value txJSON;
|
||||
Json::Reader reader;
|
||||
|
||||
if (reader.parse(jvParams[1u].asString(), txJSON))
|
||||
cLog(lsTRACE) << "RPC json:" << jvParams[0u];
|
||||
if (reader.parse(jvParams[0u].asString(), txJSON))
|
||||
{
|
||||
return txJSON;
|
||||
}
|
||||
|
||||
return rpcError(rpcINVALID_PARAMS);
|
||||
}
|
||||
|
||||
// sign/submit any transaction to the network
|
||||
//
|
||||
// sign private_key json
|
||||
// submit private_key json
|
||||
// submit tx_blob
|
||||
Json::Value RPCParser::parseSignSubmit(const Json::Value& jvParams)
|
||||
{
|
||||
Json::Value txJSON;
|
||||
Json::Reader reader;
|
||||
|
||||
if (1 == jvParams.size())
|
||||
{
|
||||
// Submitting tx_blob
|
||||
|
||||
Json::Value jvRequest;
|
||||
|
||||
jvRequest["tx_blob"] = jvParams[0u].asString();
|
||||
|
||||
return jvRequest;
|
||||
}
|
||||
// Submitting tx_json.
|
||||
else if (reader.parse(jvParams[1u].asString(), txJSON))
|
||||
{
|
||||
Json::Value jvRequest;
|
||||
|
||||
@@ -436,9 +492,11 @@ Json::Value RPCParser::parseCommand(std::string strMethod, Json::Value jvParams)
|
||||
{ "peers", &RPCParser::parseAsIs, 0, 0 },
|
||||
// { "profile", &RPCParser::parseProfile, 1, 9 },
|
||||
{ "random", &RPCParser::parseAsIs, 0, 0 },
|
||||
// { "ripple_path_find", &RPCParser::parseRipplePathFind, -1, -1 },
|
||||
{ "submit", &RPCParser::parseSubmit, 2, 2 },
|
||||
{ "ripple_path_find", &RPCParser::parseRipplePathFind, 1, 1 },
|
||||
{ "sign", &RPCParser::parseSignSubmit, 2, 2 },
|
||||
{ "submit", &RPCParser::parseSignSubmit, 1, 2 },
|
||||
{ "server_info", &RPCParser::parseAsIs, 0, 0 },
|
||||
{ "server_state", &RPCParser::parseAsIs, 0, 0 },
|
||||
{ "stop", &RPCParser::parseAsIs, 0, 0 },
|
||||
// { "transaction_entry", &RPCParser::parseTransactionEntry, -1, -1 },
|
||||
{ "tx", &RPCParser::parseTx, 1, 1 },
|
||||
@@ -459,11 +517,15 @@ Json::Value RPCParser::parseCommand(std::string strMethod, Json::Value jvParams)
|
||||
{ "wallet_propose", &RPCParser::parseWalletPropose, 0, 1 },
|
||||
{ "wallet_seed", &RPCParser::parseWalletSeed, 0, 1 },
|
||||
|
||||
{ "internal", &RPCParser::parseInternal, 1, -1 },
|
||||
|
||||
#if ENABLE_INSECURE
|
||||
// XXX Unnecessary commands which should be removed.
|
||||
{ "login", &RPCParser::parseLogin, 2, 2 },
|
||||
{ "data_delete", &RPCParser::parseDataDelete, 1, 1 },
|
||||
{ "data_fetch", &RPCParser::parseDataFetch, 1, 1 },
|
||||
{ "data_store", &RPCParser::parseDataStore, 2, 2 },
|
||||
#endif
|
||||
|
||||
// Evented methods
|
||||
{ "subscribe", &RPCParser::parseEvented, -1, -1 },
|
||||
@@ -477,7 +539,7 @@ Json::Value RPCParser::parseCommand(std::string strMethod, Json::Value jvParams)
|
||||
|
||||
if (i < 0)
|
||||
{
|
||||
return rpcError(rpcBAD_SYNTAX);
|
||||
return rpcError(rpcUNKNOWN_COMMAND);
|
||||
}
|
||||
else if ((commandsA[i].iMinParams >= 0 && jvParams.size() < commandsA[i].iMinParams)
|
||||
|| (commandsA[i].iMaxParams >= 0 && jvParams.size() > commandsA[i].iMaxParams))
|
||||
@@ -528,7 +590,18 @@ int commandLineRPC(const std::vector<std::string>& vCmd)
|
||||
|
||||
jvParams.append(jvRequest);
|
||||
|
||||
if (!theConfig.RPC_ADMIN_USER.empty())
|
||||
jvRequest["admin_user"] = theConfig.RPC_ADMIN_USER;
|
||||
|
||||
if (!theConfig.RPC_ADMIN_PASSWORD.empty())
|
||||
jvRequest["admin_password"] = theConfig.RPC_ADMIN_PASSWORD;
|
||||
|
||||
jvOutput = callRPC(
|
||||
theConfig.RPC_IP,
|
||||
theConfig.RPC_PORT,
|
||||
theConfig.RPC_USER,
|
||||
theConfig.RPC_PASSWORD,
|
||||
"",
|
||||
jvRequest.isMember("method") // Allow parser to rewrite method.
|
||||
? jvRequest["method"].asString()
|
||||
: vCmd[0],
|
||||
@@ -589,32 +662,38 @@ int commandLineRPC(const std::vector<std::string>& vCmd)
|
||||
return nRet;
|
||||
}
|
||||
|
||||
Json::Value callRPC(const std::string& strMethod, const Json::Value& params)
|
||||
Json::Value callRPC(const std::string& strIp, const int iPort, const std::string& strUsername, const std::string& strPassword, const std::string& strPath, const std::string& strMethod, const Json::Value& jvParams)
|
||||
{
|
||||
if (theConfig.RPC_USER.empty() && theConfig.RPC_PASSWORD.empty())
|
||||
throw std::runtime_error("You must set rpcpassword=<password> in the configuration file. "
|
||||
"If the file does not exist, create it with owner-readable-only file permissions.");
|
||||
|
||||
// Connect to localhost
|
||||
if (!theConfig.QUIET)
|
||||
std::cerr << "Connecting to: " << theConfig.RPC_IP << ":" << theConfig.RPC_PORT << std::endl;
|
||||
{
|
||||
std::cerr << "Connecting to: " << strIp << ":" << iPort << std::endl;
|
||||
// std::cerr << "Username: " << strUsername << ":" << strPassword << std::endl;
|
||||
// std::cerr << "Path: " << strPath << std::endl;
|
||||
// std::cerr << "Method: " << strMethod << std::endl;
|
||||
}
|
||||
|
||||
boost::asio::ip::tcp::endpoint
|
||||
endpoint(boost::asio::ip::address::from_string(theConfig.RPC_IP), theConfig.RPC_PORT);
|
||||
endpoint(boost::asio::ip::address::from_string(strIp), iPort);
|
||||
boost::asio::ip::tcp::iostream stream;
|
||||
stream.connect(endpoint);
|
||||
if (stream.fail())
|
||||
throw std::runtime_error("couldn't connect to server");
|
||||
|
||||
// cLog(lsDEBUG) << "connected" << std::endl;
|
||||
|
||||
// HTTP basic authentication
|
||||
std::string strUserPass64 = EncodeBase64(theConfig.RPC_USER + ":" + theConfig.RPC_PASSWORD);
|
||||
std::string strUserPass64 = EncodeBase64(strUsername + ":" + strPassword);
|
||||
std::map<std::string, std::string> mapRequestHeaders;
|
||||
mapRequestHeaders["Authorization"] = std::string("Basic ") + strUserPass64;
|
||||
|
||||
// Log(lsDEBUG) << "requesting" << std::endl;
|
||||
|
||||
// Send request
|
||||
std::string strRequest = JSONRPCRequest(strMethod, params, Json::Value(1));
|
||||
cLog(lsDEBUG) << "send request " << strMethod << " : " << strRequest << std::endl;
|
||||
std::string strPost = createHTTPPost(strRequest, mapRequestHeaders);
|
||||
std::string strRequest = JSONRPCRequest(strMethod, jvParams, Json::Value(1));
|
||||
// cLog(lsDEBUG) << "send request " << strMethod << " : " << strRequest << std::endl;
|
||||
|
||||
std::string strPost = createHTTPPost(strPath, strRequest, mapRequestHeaders);
|
||||
stream << strPost << std::flush;
|
||||
|
||||
// std::cerr << "post " << strPost << std::endl;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#ifndef __CALLRPC__
|
||||
#define __CALLRPC__
|
||||
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "../json/value.h"
|
||||
@@ -16,17 +15,23 @@ protected:
|
||||
Json::Value parseAccountTransactions(const Json::Value& jvParams);
|
||||
Json::Value parseAsIs(const Json::Value& jvParams);
|
||||
Json::Value parseConnect(const Json::Value& jvParams);
|
||||
#if ENABLE_INSECURE
|
||||
Json::Value parseDataDelete(const Json::Value& jvParams);
|
||||
Json::Value parseDataFetch(const Json::Value& jvParams);
|
||||
Json::Value parseDataStore(const Json::Value& jvParams);
|
||||
#endif
|
||||
Json::Value parseEvented(const Json::Value& jvParams);
|
||||
Json::Value parseGetCounts(const Json::Value& jvParams);
|
||||
Json::Value parseLedger(const Json::Value& jvParams);
|
||||
Json::Value parseInternal(const Json::Value& jvParams);
|
||||
#if ENABLE_INSECURE
|
||||
Json::Value parseLogin(const Json::Value& jvParams);
|
||||
#endif
|
||||
Json::Value parseLogLevel(const Json::Value& jvParams);
|
||||
Json::Value parseOwnerInfo(const Json::Value& jvParams);
|
||||
Json::Value parseRandom(const Json::Value& jvParams);
|
||||
Json::Value parseSubmit(const Json::Value& jvParams);
|
||||
Json::Value parseRipplePathFind(const Json::Value& jvParams);
|
||||
Json::Value parseSignSubmit(const Json::Value& jvParams);
|
||||
Json::Value parseTx(const Json::Value& jvParams);
|
||||
Json::Value parseTxHistory(const Json::Value& jvParams);
|
||||
Json::Value parseUnlAdd(const Json::Value& jvParams);
|
||||
@@ -42,7 +47,7 @@ public:
|
||||
};
|
||||
|
||||
extern int commandLineRPC(const std::vector<std::string>& vCmd);
|
||||
extern Json::Value callRPC(const std::string& strMethod, const Json::Value& params);
|
||||
extern Json::Value callRPC(const std::string& strIp, const int iPort, const std::string& strUsername, const std::string& strPassword, const std::string& strPath, const std::string& strMethod, const Json::Value& params);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
//
|
||||
// TODO: Check permissions on config file before using it.
|
||||
//
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include "HashPrefixes.h"
|
||||
|
||||
#define SECTION_ACCOUNT_PROBE_MAX "account_probe_max"
|
||||
#define SECTION_CLUSTER_NODES "cluster_nodes"
|
||||
#define SECTION_DATABASE_PATH "database_path"
|
||||
#define SECTION_DEBUG_LOGFILE "debug_logfile"
|
||||
#define SECTION_FEE_DEFAULT "fee_default"
|
||||
#define SECTION_FEE_NICKNAME_CREATE "fee_nickname_create"
|
||||
@@ -22,6 +26,7 @@
|
||||
#define SECTION_LEDGER_HISTORY "ledger_history"
|
||||
#define SECTION_IPS "ips"
|
||||
#define SECTION_NETWORK_QUORUM "network_quorum"
|
||||
#define SECTION_NODE_SEED "node_seed"
|
||||
#define SECTION_PEER_CONNECT_LOW_WATER "peer_connect_low_water"
|
||||
#define SECTION_PEER_IP "peer_ip"
|
||||
#define SECTION_PEER_PORT "peer_port"
|
||||
@@ -30,14 +35,21 @@
|
||||
#define SECTION_PEER_SSL_CIPHER_LIST "peer_ssl_cipher_list"
|
||||
#define SECTION_PEER_START_MAX "peer_start_max"
|
||||
#define SECTION_RPC_ALLOW_REMOTE "rpc_allow_remote"
|
||||
#define SECTION_RPC_ADMIN_ALLOW "rpc_admin_allow"
|
||||
#define SECTION_RPC_ADMIN_USER "rpc_admin_user"
|
||||
#define SECTION_RPC_ADMIN_PASSWORD "rpc_admin_password"
|
||||
#define SECTION_RPC_IP "rpc_ip"
|
||||
#define SECTION_RPC_PORT "rpc_port"
|
||||
#define SECTION_RPC_USER "rpc_user"
|
||||
#define SECTION_RPC_PASSWORD "rpc_password"
|
||||
#define SECTION_RPC_STARTUP "rpc_startup"
|
||||
#define SECTION_SNTP "sntp_servers"
|
||||
#define SECTION_VALIDATORS_FILE "validators_file"
|
||||
#define SECTION_VALIDATION_QUORUM "validation_quorum"
|
||||
#define SECTION_VALIDATION_SEED "validation_seed"
|
||||
#define SECTION_WEBSOCKET_PUBLIC_IP "websocket_public_ip"
|
||||
#define SECTION_WEBSOCKET_PUBLIC_PORT "websocket_public_port"
|
||||
#define SECTION_WEBSOCKET_PUBLIC_SECURE "websocket_public_secure"
|
||||
#define SECTION_WEBSOCKET_IP "websocket_ip"
|
||||
#define SECTION_WEBSOCKET_PORT "websocket_port"
|
||||
#define SECTION_WEBSOCKET_SECURE "websocket_secure"
|
||||
@@ -56,10 +68,12 @@
|
||||
#define DEFAULT_FEE_OPERATION 1
|
||||
|
||||
Config theConfig;
|
||||
const char* ALPHABET = "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz";
|
||||
|
||||
void Config::setup(const std::string& strConf, bool bQuiet)
|
||||
void Config::setup(const std::string& strConf, bool bTestNet, bool bQuiet)
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
std::string strDbPath, strConfFile;
|
||||
|
||||
//
|
||||
// Determine the config and data directories.
|
||||
@@ -67,21 +81,38 @@ void Config::setup(const std::string& strConf, bool bQuiet)
|
||||
// that with "db" as the data directory.
|
||||
//
|
||||
|
||||
TESTNET = bTestNet;
|
||||
QUIET = bQuiet;
|
||||
|
||||
// TESTNET forces a "testnet-" prefix on the conf file and db directory.
|
||||
strDbPath = TESTNET ? "testnet-db" : "db";
|
||||
strConfFile = boost::str(boost::format(TESTNET ? "testnet-%s" : "%s")
|
||||
% (strConf.empty() ? CONFIG_FILE_NAME : strConf));
|
||||
|
||||
VALIDATORS_BASE = boost::str(boost::format(TESTNET ? "testnet-%s" : "%s")
|
||||
% VALIDATORS_FILE_NAME);
|
||||
VALIDATORS_URI = boost::str(boost::format("/%s") % VALIDATORS_BASE);
|
||||
|
||||
SIGN_TRANSACTION = TESTNET ? sHP_TestNetTransactionSign : sHP_TransactionSign;
|
||||
SIGN_VALIDATION = TESTNET ? sHP_TestNetValidation : sHP_Validation;
|
||||
SIGN_PROPOSAL = TESTNET ? sHP_TestNetProposal : sHP_Proposal;
|
||||
|
||||
if (TESTNET)
|
||||
ALPHABET = "RPShNAF39wBUDnEGHJKLM4pQrsT7VWXYZ2bcdeCg65jkm8ofqi1tuvaxyz";
|
||||
|
||||
if (!strConf.empty())
|
||||
{
|
||||
// --conf=<path> : everything is relative that file.
|
||||
CONFIG_FILE = strConf;
|
||||
CONFIG_DIR = CONFIG_FILE;
|
||||
CONFIG_FILE = strConfFile;
|
||||
CONFIG_DIR = boost::filesystem::absolute(CONFIG_FILE);
|
||||
CONFIG_DIR.remove_filename();
|
||||
DATA_DIR = CONFIG_DIR / "db";
|
||||
DATA_DIR = CONFIG_DIR / strDbPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
CONFIG_DIR = boost::filesystem::current_path();
|
||||
CONFIG_FILE = CONFIG_DIR / CONFIG_FILE_NAME;
|
||||
DATA_DIR = CONFIG_DIR / "db";
|
||||
CONFIG_FILE = CONFIG_DIR / strConfFile;
|
||||
DATA_DIR = CONFIG_DIR / strDbPath;
|
||||
|
||||
if (exists(CONFIG_FILE)
|
||||
// Can we figure out XDG dirs?
|
||||
@@ -111,7 +142,7 @@ void Config::setup(const std::string& strConf, bool bQuiet)
|
||||
}
|
||||
|
||||
CONFIG_DIR = str(boost::format("%s/" SYSTEM_NAME) % strXdgConfigHome);
|
||||
CONFIG_FILE = CONFIG_DIR / CONFIG_FILE_NAME;
|
||||
CONFIG_FILE = CONFIG_DIR / strConfFile;
|
||||
DATA_DIR = str(boost::format("%s/" SYSTEM_NAME) % strXdgDataHome);
|
||||
|
||||
boost::filesystem::create_directories(CONFIG_DIR, ec);
|
||||
@@ -121,35 +152,42 @@ void Config::setup(const std::string& strConf, bool bQuiet)
|
||||
}
|
||||
}
|
||||
|
||||
boost::filesystem::create_directories(DATA_DIR, ec);
|
||||
|
||||
if (ec)
|
||||
throw std::runtime_error(str(boost::format("Can not create %s") % DATA_DIR));
|
||||
// Update default values
|
||||
load();
|
||||
|
||||
// std::cerr << "CONFIG FILE: " << CONFIG_FILE << std::endl;
|
||||
// std::cerr << "CONFIG DIR: " << CONFIG_DIR << std::endl;
|
||||
// std::cerr << "DATA DIR: " << DATA_DIR << std::endl;
|
||||
|
||||
boost::filesystem::create_directories(DATA_DIR, ec);
|
||||
|
||||
if (ec)
|
||||
throw std::runtime_error(str(boost::format("Can not create %s") % DATA_DIR));
|
||||
}
|
||||
|
||||
Config::Config()
|
||||
{
|
||||
//
|
||||
// Defaults
|
||||
//
|
||||
|
||||
TESTNET = false;
|
||||
NETWORK_START_TIME = 1319844908;
|
||||
|
||||
PEER_PORT = SYSTEM_PEER_PORT;
|
||||
RPC_PORT = 5001;
|
||||
WEBSOCKET_PORT = SYSTEM_WEBSOCKET_PORT;
|
||||
WEBSOCKET_PUBLIC_PORT = SYSTEM_WEBSOCKET_PUBLIC_PORT;
|
||||
WEBSOCKET_SECURE = false;
|
||||
WEBSOCKET_PUBLIC_SECURE = 1;
|
||||
WEBSOCKET_SECURE = 0;
|
||||
NUMBER_CONNECTIONS = 30;
|
||||
|
||||
// a new ledger every minute
|
||||
LEDGER_SECONDS = 60;
|
||||
LEDGER_CREATOR = false;
|
||||
|
||||
RPC_USER = "admin";
|
||||
RPC_PASSWORD = "pass";
|
||||
RPC_ALLOW_REMOTE = false;
|
||||
RPC_ADMIN_ALLOW.push_back("127.0.0.1");
|
||||
|
||||
PEER_SSL_CIPHER_LIST = DEFAULT_PEER_SSL_CIPHER_LIST;
|
||||
PEER_SCAN_INTERVAL_MIN = DEFAULT_PEER_SCAN_INTERVAL_MIN;
|
||||
@@ -159,7 +197,7 @@ void Config::setup(const std::string& strConf, bool bQuiet)
|
||||
|
||||
PEER_PRIVATE = false;
|
||||
|
||||
TRANSACTION_FEE_BASE = 1000;
|
||||
TRANSACTION_FEE_BASE = DEFAULT_FEE_DEFAULT;
|
||||
|
||||
NETWORK_QUORUM = 0; // Don't need to see other nodes
|
||||
VALIDATION_QUORUM = 1; // Only need one node to vouch
|
||||
@@ -179,8 +217,6 @@ void Config::setup(const std::string& strConf, bool bQuiet)
|
||||
|
||||
RUN_STANDALONE = false;
|
||||
START_UP = NORMAL;
|
||||
|
||||
load();
|
||||
}
|
||||
|
||||
void Config::load()
|
||||
@@ -220,6 +256,13 @@ void Config::load()
|
||||
// sectionEntriesPrint(&VALIDATORS, SECTION_VALIDATORS);
|
||||
}
|
||||
|
||||
smtTmp = sectionEntries(secConfig, SECTION_CLUSTER_NODES);
|
||||
if (smtTmp)
|
||||
{
|
||||
CLUSTER_NODES = *smtTmp;
|
||||
// sectionEntriesPrint(&CLUSTER_NODES, SECTION_CLUSTER_NODES);
|
||||
}
|
||||
|
||||
smtTmp = sectionEntries(secConfig, SECTION_IPS);
|
||||
if (smtTmp)
|
||||
{
|
||||
@@ -233,6 +276,28 @@ void Config::load()
|
||||
SNTP_SERVERS = *smtTmp;
|
||||
}
|
||||
|
||||
smtTmp = sectionEntries(secConfig, SECTION_RPC_STARTUP);
|
||||
if (smtTmp)
|
||||
{
|
||||
Json::Value jvArray(Json::arrayValue);
|
||||
|
||||
BOOST_FOREACH(const std::string& strJson, *smtTmp)
|
||||
{
|
||||
Json::Reader jrReader;
|
||||
Json::Value jvCommand;
|
||||
|
||||
if (!jrReader.parse(strJson, jvCommand))
|
||||
throw std::runtime_error(boost::str(boost::format("Couldn't parse ["SECTION_RPC_STARTUP"] command: %s") % strJson));
|
||||
|
||||
jvArray.append(jvCommand);
|
||||
}
|
||||
|
||||
RPC_STARTUP = jvArray;
|
||||
}
|
||||
|
||||
if (sectionSingleB(secConfig, SECTION_DATABASE_PATH, DATABASE_PATH))
|
||||
DATA_DIR = DATABASE_PATH;
|
||||
|
||||
(void) sectionSingleB(secConfig, SECTION_VALIDATORS_SITE, VALIDATORS_SITE);
|
||||
|
||||
(void) sectionSingleB(secConfig, SECTION_PEER_IP, PEER_IP);
|
||||
@@ -243,7 +308,17 @@ void Config::load()
|
||||
if (sectionSingleB(secConfig, SECTION_PEER_PRIVATE, strTemp))
|
||||
PEER_PRIVATE = boost::lexical_cast<bool>(strTemp);
|
||||
|
||||
smtTmp = sectionEntries(secConfig, SECTION_RPC_ADMIN_ALLOW);
|
||||
if (smtTmp)
|
||||
{
|
||||
RPC_ADMIN_ALLOW = *smtTmp;
|
||||
}
|
||||
|
||||
(void) sectionSingleB(secConfig, SECTION_RPC_ADMIN_PASSWORD, RPC_ADMIN_PASSWORD);
|
||||
(void) sectionSingleB(secConfig, SECTION_RPC_ADMIN_USER, RPC_ADMIN_USER);
|
||||
(void) sectionSingleB(secConfig, SECTION_RPC_IP, RPC_IP);
|
||||
(void) sectionSingleB(secConfig, SECTION_RPC_PASSWORD, RPC_PASSWORD);
|
||||
(void) sectionSingleB(secConfig, SECTION_RPC_USER, RPC_USER);
|
||||
|
||||
if (sectionSingleB(secConfig, SECTION_RPC_PORT, strTemp))
|
||||
RPC_PORT = boost::lexical_cast<int>(strTemp);
|
||||
@@ -265,12 +340,14 @@ void Config::load()
|
||||
WEBSOCKET_PUBLIC_PORT = boost::lexical_cast<int>(strTemp);
|
||||
|
||||
if (sectionSingleB(secConfig, SECTION_WEBSOCKET_SECURE, strTemp))
|
||||
WEBSOCKET_SECURE = boost::lexical_cast<bool>(strTemp);
|
||||
WEBSOCKET_SECURE = boost::lexical_cast<int>(strTemp);
|
||||
|
||||
if (sectionSingleB(secConfig, SECTION_WEBSOCKET_PUBLIC_SECURE, strTemp))
|
||||
WEBSOCKET_PUBLIC_SECURE = boost::lexical_cast<int>(strTemp);
|
||||
|
||||
sectionSingleB(secConfig, SECTION_WEBSOCKET_SSL_CERT, WEBSOCKET_SSL_CERT);
|
||||
sectionSingleB(secConfig, SECTION_WEBSOCKET_SSL_CHAIN, WEBSOCKET_SSL_CHAIN);
|
||||
sectionSingleB(secConfig, SECTION_WEBSOCKET_SSL_KEY, WEBSOCKET_SSL_KEY);
|
||||
|
||||
|
||||
if (sectionSingleB(secConfig, SECTION_VALIDATION_SEED, strTemp))
|
||||
{
|
||||
@@ -281,6 +358,15 @@ void Config::load()
|
||||
VALIDATION_PRIV = RippleAddress::createNodePrivate(VALIDATION_SEED);
|
||||
}
|
||||
}
|
||||
if (sectionSingleB(secConfig, SECTION_NODE_SEED, strTemp))
|
||||
{
|
||||
NODE_SEED.setSeedGeneric(strTemp);
|
||||
if (NODE_SEED.isValid())
|
||||
{
|
||||
NODE_PUB = RippleAddress::createNodePublic(NODE_SEED);
|
||||
NODE_PRIV = RippleAddress::createNodePrivate(NODE_SEED);
|
||||
}
|
||||
}
|
||||
|
||||
(void) sectionSingleB(secConfig, SECTION_PEER_SSL_CIPHER_LIST, PEER_SSL_CIPHER_LIST);
|
||||
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
#ifndef __CONFIG__
|
||||
#define __CONFIG__
|
||||
|
||||
#include <string>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include "types.h"
|
||||
#include "RippleAddress.h"
|
||||
#include "ParseSection.h"
|
||||
#include "SerializedTypes.h"
|
||||
|
||||
#include <string>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include "../json/value.h"
|
||||
|
||||
#define ENABLE_INSECURE 0 // 1, to enable unnecessary features.
|
||||
|
||||
#define SYSTEM_NAME "ripple"
|
||||
#define SYSTEM_CURRENCY_CODE "XRP"
|
||||
@@ -19,11 +23,14 @@
|
||||
#define SYSTEM_CURRENCY_PARTS 1000000ull // 10^SYSTEM_CURRENCY_PRECISION
|
||||
#define SYSTEM_CURRENCY_START (SYSTEM_CURRENCY_GIFT*SYSTEM_CURRENCY_USERS*SYSTEM_CURRENCY_PARTS)
|
||||
|
||||
#define CONFIG_FILE_NAME SYSTEM_NAME "d.cfg" // rippled.cfg
|
||||
#define CONFIG_FILE_NAME SYSTEM_NAME "d.cfg" // rippled.cfg
|
||||
|
||||
#define DEFAULT_VALIDATORS_SITE ""
|
||||
#define VALIDATORS_FILE_NAME "validators.txt"
|
||||
|
||||
const int DOMAIN_BYTES_MAX = 256;
|
||||
const int PUBLIC_BYTES_MAX = 2048; // Maximum bytes for an account public key.
|
||||
|
||||
const int SYSTEM_PEER_PORT = 6561;
|
||||
const int SYSTEM_WEBSOCKET_PORT = 6562;
|
||||
const int SYSTEM_WEBSOCKET_PUBLIC_PORT = 6563; // XXX Going away.
|
||||
@@ -31,10 +38,9 @@ const int SYSTEM_WEBSOCKET_PUBLIC_PORT = 6563; // XXX Going away.
|
||||
// Allow anonymous DH.
|
||||
#define DEFAULT_PEER_SSL_CIPHER_LIST "ALL:!LOW:!EXP:!MD5:@STRENGTH"
|
||||
|
||||
// Normal, recommend 1 hour.
|
||||
// #define DEFAULT_PEER_SCAN_INTERVAL_MIN (60*60)
|
||||
// Testing, recommend 1 minute.
|
||||
#define DEFAULT_PEER_SCAN_INTERVAL_MIN (60)
|
||||
// Normal, recommend 1 hour: 60*60
|
||||
// Testing, recommend 1 minute: 60
|
||||
#define DEFAULT_PEER_SCAN_INTERVAL_MIN (60*60) // Seconds
|
||||
|
||||
// Maximum number of peers to try to connect to as client at once.
|
||||
#define DEFAULT_PEER_START_MAX 5
|
||||
@@ -47,24 +53,31 @@ class Config
|
||||
public:
|
||||
// Configuration parameters
|
||||
bool QUIET;
|
||||
bool TESTNET;
|
||||
|
||||
boost::filesystem::path CONFIG_FILE;
|
||||
boost::filesystem::path CONFIG_DIR;
|
||||
boost::filesystem::path DATA_DIR;
|
||||
boost::filesystem::path DEBUG_LOGFILE;
|
||||
boost::filesystem::path VALIDATORS_FILE;
|
||||
boost::filesystem::path VALIDATORS_FILE; // As specifed in rippled.cfg.
|
||||
|
||||
std::string VALIDATORS_SITE; // Where to find validators.txt on the Internet.
|
||||
std::string VALIDATORS_URI; // URI of validators.txt.
|
||||
std::string VALIDATORS_BASE; // Name with testnet-, if needed.
|
||||
std::vector<std::string> VALIDATORS; // Validators from rippled.cfg.
|
||||
std::vector<std::string> IPS; // Peer IPs from rippled.cfg.
|
||||
std::vector<std::string> SNTP_SERVERS; // SNTP servers from rippled.cfg.
|
||||
|
||||
enum StartUpType { FRESH, NORMAL, LOAD, NETWORK };
|
||||
StartUpType START_UP;
|
||||
std::string START_LEDGER;
|
||||
|
||||
// Database
|
||||
std::string DATABASE_PATH;
|
||||
|
||||
// Network parameters
|
||||
int NETWORK_START_TIME; // The Unix time we start ledger 0.
|
||||
int TRANSACTION_FEE_BASE;
|
||||
int TRANSACTION_FEE_BASE; // The number of fee units a reference transaction costs
|
||||
int LEDGER_SECONDS;
|
||||
int LEDGER_PROPOSAL_DELAY_SECONDS;
|
||||
int LEDGER_AVALANCHE_SECONDS;
|
||||
@@ -88,10 +101,12 @@ public:
|
||||
// Websocket networking parameters
|
||||
std::string WEBSOCKET_PUBLIC_IP; // XXX Going away. Merge with the inbound peer connction.
|
||||
int WEBSOCKET_PUBLIC_PORT;
|
||||
int WEBSOCKET_PUBLIC_SECURE;
|
||||
|
||||
std::string WEBSOCKET_IP;
|
||||
int WEBSOCKET_PORT;
|
||||
bool WEBSOCKET_SECURE;
|
||||
int WEBSOCKET_SECURE;
|
||||
|
||||
std::string WEBSOCKET_SSL_CERT;
|
||||
std::string WEBSOCKET_SSL_CHAIN;
|
||||
std::string WEBSOCKET_SSL_KEY;
|
||||
@@ -99,17 +114,25 @@ public:
|
||||
// RPC parameters
|
||||
std::string RPC_IP;
|
||||
int RPC_PORT;
|
||||
std::string RPC_USER;
|
||||
std::vector<std::string> RPC_ADMIN_ALLOW;
|
||||
std::string RPC_ADMIN_PASSWORD;
|
||||
std::string RPC_ADMIN_USER;
|
||||
std::string RPC_PASSWORD;
|
||||
std::string RPC_USER;
|
||||
bool RPC_ALLOW_REMOTE;
|
||||
Json::Value RPC_STARTUP;
|
||||
|
||||
// Validation
|
||||
RippleAddress VALIDATION_SEED, VALIDATION_PUB, VALIDATION_PRIV;
|
||||
|
||||
// Fee schedule
|
||||
// Node/Cluster
|
||||
std::vector<std::string> CLUSTER_NODES;
|
||||
RippleAddress NODE_SEED, NODE_PUB, NODE_PRIV;
|
||||
|
||||
// Fee schedule (All below values are in fee units)
|
||||
uint64 FEE_DEFAULT; // Default fee.
|
||||
uint64 FEE_ACCOUNT_RESERVE; // Amount of XRP not allowed to send.
|
||||
uint64 FEE_OWNER_RESERVE; // Amount of XRP not allowed to send per owner entry.
|
||||
uint64 FEE_ACCOUNT_RESERVE; // Amount of units not allowed to send.
|
||||
uint64 FEE_OWNER_RESERVE; // Amount of units not allowed to send per owner entry.
|
||||
uint64 FEE_NICKNAME_CREATE; // Fee to create a nickname.
|
||||
uint64 FEE_OFFER; // Rate per day.
|
||||
int FEE_CONTRACT_OPERATION; // fee for each contract operation
|
||||
@@ -120,11 +143,19 @@ public:
|
||||
// Client behavior
|
||||
int ACCOUNT_PROBE_MAX; // How far to scan for accounts.
|
||||
|
||||
void setup(const std::string& strConf, bool bQuiet);
|
||||
// Signing signatures.
|
||||
uint32 SIGN_TRANSACTION;
|
||||
uint32 SIGN_VALIDATION;
|
||||
uint32 SIGN_PROPOSAL;
|
||||
|
||||
Config();
|
||||
|
||||
void setup(const std::string& strConf, bool bTestNet, bool bQuiet);
|
||||
void load();
|
||||
};
|
||||
|
||||
extern Config theConfig;
|
||||
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "Config.h"
|
||||
#include "Peer.h"
|
||||
#include "PeerDoor.h"
|
||||
#include "Application.h"
|
||||
#include "utils.h"
|
||||
#include "Log.h"
|
||||
@@ -28,21 +29,6 @@ void splitIpPort(const std::string& strIpPort, std::string& strIp, int& iPort)
|
||||
iPort = boost::lexical_cast<int>(vIpPort[1]);
|
||||
}
|
||||
|
||||
ConnectionPool::ConnectionPool(boost::asio::io_service& io_service) :
|
||||
mLastPeer(0),
|
||||
mCtx(boost::asio::ssl::context::sslv23),
|
||||
mScanTimer(io_service),
|
||||
mPolicyTimer(io_service)
|
||||
{
|
||||
mCtx.set_options(
|
||||
boost::asio::ssl::context::default_workarounds
|
||||
| boost::asio::ssl::context::no_sslv2
|
||||
| boost::asio::ssl::context::single_dh_use);
|
||||
|
||||
if (1 != SSL_CTX_set_cipher_list(mCtx.native_handle(), theConfig.PEER_SSL_CIPHER_LIST.c_str()))
|
||||
std::runtime_error("Error setting cipher list (no valid ciphers).");
|
||||
}
|
||||
|
||||
void ConnectionPool::start()
|
||||
{
|
||||
if (theConfig.RUN_STANDALONE)
|
||||
@@ -140,7 +126,7 @@ bool ConnectionPool::peerAvailable(std::string& strIp, int& iPort)
|
||||
|
||||
vstrIpPort.reserve(mIpMap.size());
|
||||
|
||||
BOOST_FOREACH(pipPeer ipPeer, mIpMap)
|
||||
BOOST_FOREACH(const vtPeer& ipPeer, mIpMap)
|
||||
{
|
||||
const std::string& strIp = ipPeer.first.first;
|
||||
int iPort = ipPeer.first.second;
|
||||
@@ -178,7 +164,7 @@ void ConnectionPool::policyLowWater()
|
||||
int iPort;
|
||||
|
||||
// Find an entry to connect to.
|
||||
if (mConnectedMap.size() > theConfig.PEER_CONNECT_LOW_WATER)
|
||||
if (getPeerCount() > theConfig.PEER_CONNECT_LOW_WATER)
|
||||
{
|
||||
// Above low water mark, don't need more connections.
|
||||
cLog(lsTRACE) << "Pool: Low water: sufficient connections: " << mConnectedMap.size() << "/" << theConfig.PEER_CONNECT_LOW_WATER;
|
||||
@@ -251,7 +237,7 @@ int ConnectionPool::relayMessage(Peer* fromPeer, const PackedMessage::pointer& m
|
||||
int sentTo = 0;
|
||||
boost::mutex::scoped_lock sl(mPeerLock);
|
||||
|
||||
BOOST_FOREACH(naPeer pair, mConnectedMap)
|
||||
BOOST_FOREACH(const vtConMap& pair, mConnectedMap)
|
||||
{
|
||||
Peer::ref peer = pair.second;
|
||||
if (!peer)
|
||||
@@ -270,7 +256,7 @@ void ConnectionPool::relayMessageBut(const std::set<uint64>& fromPeers, const Pa
|
||||
{ // Relay message to all but the specified peers
|
||||
boost::mutex::scoped_lock sl(mPeerLock);
|
||||
|
||||
BOOST_FOREACH(naPeer pair, mConnectedMap)
|
||||
BOOST_FOREACH(const vtConMap& pair, mConnectedMap)
|
||||
{
|
||||
Peer::ref peer = pair.second;
|
||||
if (peer->isConnected() && (fromPeers.count(peer->getPeerId()) == 0))
|
||||
@@ -298,9 +284,6 @@ void ConnectionPool::relayMessageTo(const std::set<uint64>& fromPeers, const Pac
|
||||
// Requires sane IP and port.
|
||||
void ConnectionPool::connectTo(const std::string& strIp, int iPort)
|
||||
{
|
||||
if (theConfig.RUN_STANDALONE)
|
||||
return;
|
||||
|
||||
{
|
||||
Database* db = theApp->getWalletDB()->getDB();
|
||||
ScopedLock sl(theApp->getWalletDB()->getDBLock());
|
||||
@@ -329,7 +312,8 @@ Peer::pointer ConnectionPool::peerConnect(const std::string& strIp, int iPort)
|
||||
|
||||
if ((it = mIpMap.find(pipPeer)) == mIpMap.end())
|
||||
{
|
||||
Peer::pointer ppNew(Peer::create(theApp->getIOService(), mCtx, ++mLastPeer));
|
||||
Peer::pointer ppNew(Peer::create(theApp->getIOService(), theApp->getPeerDoor().getSSLContext(),
|
||||
++mLastPeer, false));
|
||||
|
||||
// Did not find it. Not already connecting or connected.
|
||||
ppNew->connect(strIp, iPort);
|
||||
@@ -365,7 +349,7 @@ Json::Value ConnectionPool::getPeersJson()
|
||||
Json::Value ret(Json::arrayValue);
|
||||
std::vector<Peer::pointer> vppPeers = getPeerVector();
|
||||
|
||||
BOOST_FOREACH(Peer::pointer peer, vppPeers)
|
||||
BOOST_FOREACH(Peer::ref peer, vppPeers)
|
||||
{
|
||||
ret.append(peer->getJson());
|
||||
}
|
||||
@@ -388,7 +372,7 @@ std::vector<Peer::pointer> ConnectionPool::getPeerVector()
|
||||
|
||||
ret.reserve(mConnectedMap.size());
|
||||
|
||||
BOOST_FOREACH(naPeer pair, mConnectedMap)
|
||||
BOOST_FOREACH(const vtConMap& pair, mConnectedMap)
|
||||
{
|
||||
assert(!!pair.second);
|
||||
ret.push_back(pair.second);
|
||||
@@ -532,7 +516,7 @@ bool ConnectionPool::peerScanSet(const std::string& strIp, int iPort)
|
||||
db->executeSQL(str(boost::format("UPDATE PeerIps SET ScanNext=%d,ScanInterval=%d WHERE IpPort=%s;")
|
||||
% iToSeconds(tpNext)
|
||||
% iInterval
|
||||
% db->escape(strIpPort)));
|
||||
% sqlEscape(strIpPort)));
|
||||
|
||||
bScanDirty = true;
|
||||
}
|
||||
@@ -632,8 +616,8 @@ void ConnectionPool::peerVerified(Peer::ref peer)
|
||||
ScopedLock sl(theApp->getWalletDB()->getDBLock());
|
||||
Database *db=theApp->getWalletDB()->getDB();
|
||||
|
||||
db->executeSQL(str(boost::format("UPDATE PeerIps SET ScanNext=NULL,ScanInterval=0 WHERE IpPort=%s;")
|
||||
% db->escape(strIpPort)));
|
||||
db->executeSQL(boost::str(boost::format("UPDATE PeerIps SET ScanNext=NULL,ScanInterval=0 WHERE IpPort=%s;")
|
||||
% sqlEscape(strIpPort)));
|
||||
// XXX Check error.
|
||||
}
|
||||
|
||||
@@ -662,7 +646,11 @@ void ConnectionPool::scanHandler(const boost::system::error_code& ecResult)
|
||||
// Scan ips as per db entries.
|
||||
void ConnectionPool::scanRefresh()
|
||||
{
|
||||
if (mScanning)
|
||||
if (theConfig.RUN_STANDALONE)
|
||||
{
|
||||
nothing();
|
||||
}
|
||||
else if (mScanning)
|
||||
{
|
||||
// Currently scanning, will scan again after completion.
|
||||
cLog(lsTRACE) << "Pool: Scan: already scanning";
|
||||
@@ -726,10 +714,10 @@ void ConnectionPool::scanRefresh()
|
||||
ScopedLock sl(theApp->getWalletDB()->getDBLock());
|
||||
Database *db=theApp->getWalletDB()->getDB();
|
||||
|
||||
db->executeSQL(str(boost::format("UPDATE PeerIps SET ScanNext=%d,ScanInterval=%d WHERE IpPort=%s;")
|
||||
db->executeSQL(boost::str(boost::format("UPDATE PeerIps SET ScanNext=%d,ScanInterval=%d WHERE IpPort=%s;")
|
||||
% iToSeconds(tpNext)
|
||||
% iInterval
|
||||
% db->escape(strIpPort)));
|
||||
% sqlEscape(strIpPort)));
|
||||
// XXX Check error.
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ private:
|
||||
|
||||
typedef std::pair<RippleAddress, Peer::pointer> naPeer;
|
||||
typedef std::pair<ipPort, Peer::pointer> pipPeer;
|
||||
typedef std::map<ipPort, Peer::pointer>::value_type vtPeer;
|
||||
|
||||
// Peers we are connecting with and non-thin peers we are connected to.
|
||||
// Only peers we know the connection ip for are listed.
|
||||
@@ -31,13 +32,12 @@ private:
|
||||
|
||||
// Non-thin peers which we are connected to.
|
||||
// Peers we have the public key for.
|
||||
typedef boost::unordered_map<RippleAddress, Peer::pointer>::value_type vtConMap;
|
||||
boost::unordered_map<RippleAddress, Peer::pointer> mConnectedMap;
|
||||
|
||||
// Connections with have a 64-bit identifier
|
||||
boost::unordered_map<uint64, Peer::pointer> mPeerIdMap;
|
||||
|
||||
boost::asio::ssl::context mCtx;
|
||||
|
||||
Peer::pointer mScanning;
|
||||
boost::asio::deadline_timer mScanTimer;
|
||||
std::string mScanIp;
|
||||
@@ -58,7 +58,9 @@ private:
|
||||
Peer::pointer peerConnect(const std::string& strIp, int iPort);
|
||||
|
||||
public:
|
||||
ConnectionPool(boost::asio::io_service& io_service);
|
||||
ConnectionPool(boost::asio::io_service& io_service) :
|
||||
mLastPeer(0), mScanTimer(io_service), mPolicyTimer(io_service)
|
||||
{ ; }
|
||||
|
||||
// Begin enforcing connection policy.
|
||||
void start();
|
||||
|
||||
@@ -32,4 +32,4 @@ void Contract::executeAccept()
|
||||
//interpreter.interpret(this,code);
|
||||
}
|
||||
|
||||
|
||||
// vim:ts=4
|
||||
|
||||
@@ -27,4 +27,6 @@ public:
|
||||
void executeAccept();
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
|
||||
// Transaction database holds transactions and public keys
|
||||
const char *TxnDBInit[] = {
|
||||
"PRAGMA synchronous=NORMAL;",
|
||||
"PRAGMA journal_mode=WAL;",
|
||||
|
||||
"BEGIN TRANSACTION;",
|
||||
|
||||
"CREATE TABLE Transactions ( \
|
||||
@@ -16,10 +19,6 @@ const char *TxnDBInit[] = {
|
||||
RawTxn BLOB, \
|
||||
TxnMeta BLOB \
|
||||
);",
|
||||
"CREATE TABLE PubKeys ( \
|
||||
ID CHARACTER(35) PRIMARY KEY, \
|
||||
PubKey BLOB \
|
||||
);",
|
||||
"CREATE TABLE AccountTransactions ( \
|
||||
TransID CHARACTER(64), \
|
||||
Account CHARACTER(64), \
|
||||
@@ -37,6 +36,9 @@ int TxnDBCount = NUMBER(TxnDBInit);
|
||||
|
||||
// Ledger database holds ledgers and ledger confirmations
|
||||
const char *LedgerDBInit[] = {
|
||||
"PRAGMA synchronous=NORMAL;",
|
||||
"PRAGMA journal_mode=WAL;",
|
||||
|
||||
"BEGIN TRANSACTION;",
|
||||
|
||||
"CREATE TABLE Ledgers ( \
|
||||
@@ -254,6 +256,9 @@ int WalletDBCount = NUMBER(WalletDBInit);
|
||||
|
||||
// Hash node database holds nodes indexed by hash
|
||||
const char *HashNodeDBInit[] = {
|
||||
"PRAGMA synchronous=NORMAL;",
|
||||
"PRAGMA journal_mode=WAL;",
|
||||
|
||||
"BEGIN TRANSACTION;",
|
||||
|
||||
"CREATE TABLE CommittedObjects ( \
|
||||
|
||||
@@ -128,7 +128,7 @@ void FeatureTable::reportValidations(const FeatureSet& set)
|
||||
return;
|
||||
int threshold = (set.mTrustedValidations * mMajorityFraction) / 256;
|
||||
|
||||
typedef std::pair<const uint256, int> u256_int_pair;
|
||||
typedef std::map<uint256, int>::value_type u256_int_pair;
|
||||
|
||||
boost::mutex::scoped_lock sl(mMutex);
|
||||
|
||||
@@ -195,17 +195,17 @@ Json::Value FeatureTable::getJson(int)
|
||||
{
|
||||
Json::Value v(Json::objectValue);
|
||||
|
||||
v["supported"] = it.second.mSupported ? "true" : "false";
|
||||
v["supported"] = it.second.mSupported;
|
||||
|
||||
if (it.second.mEnabled)
|
||||
v["enabled"] = "true";
|
||||
v["enabled"] = true;
|
||||
else
|
||||
{
|
||||
v["enabled"] = "false";
|
||||
v["enabled"] = false;
|
||||
if (mLastReport != 0)
|
||||
{
|
||||
if (it.second.mLastMajority == 0)
|
||||
v["majority"] = "no";
|
||||
v["majority"] = false;
|
||||
else
|
||||
{
|
||||
if (it.second.mFirstMajority != 0)
|
||||
@@ -227,7 +227,7 @@ Json::Value FeatureTable::getJson(int)
|
||||
}
|
||||
|
||||
if (it.second.mVetoed)
|
||||
v["veto"] = "true";
|
||||
v["veto"] = true;
|
||||
|
||||
ret[it.first.GetHex()] = v;
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ std::string SField::getName() const
|
||||
SField::ref SField::getField(const std::string& fieldName)
|
||||
{ // OPTIMIZEME me with a map. CHECKME this is case sensitive
|
||||
boost::mutex::scoped_lock sl(mapMutex);
|
||||
typedef std::pair<const int, SField::ptr> int_sfref_pair;
|
||||
typedef std::map<int, SField::ptr>::value_type int_sfref_pair;
|
||||
BOOST_FOREACH(const int_sfref_pair& fieldPair, codeToField)
|
||||
{
|
||||
if (fieldPair.second->fieldName == fieldName)
|
||||
|
||||
@@ -7,9 +7,11 @@
|
||||
#include "Log.h"
|
||||
SETUP_LOG();
|
||||
|
||||
// Logic to handle incoming HTTP reqests
|
||||
|
||||
void HTTPRequest::reset()
|
||||
{
|
||||
vHeaders.clear();
|
||||
mHeaders.clear();
|
||||
sRequestBody.clear();
|
||||
sAuthorization.clear();
|
||||
iDataSize = 0;
|
||||
@@ -22,7 +24,7 @@ HTTPRequestAction HTTPRequest::requestDone(bool forceClose)
|
||||
if (forceClose || bShouldClose)
|
||||
return haCLOSE_CONN;
|
||||
reset();
|
||||
return haREAD_LINE;
|
||||
return haREAD_LINE;
|
||||
}
|
||||
|
||||
std::string HTTPRequest::getReplyHeaders(bool forceClose)
|
||||
@@ -65,8 +67,6 @@ HTTPRequestAction HTTPRequest::consume(boost::asio::streambuf& buf)
|
||||
eState = getting_body;
|
||||
return haREAD_RAW;
|
||||
}
|
||||
vHeaders.push_back(line);
|
||||
|
||||
size_t colon = line.find(':');
|
||||
if (colon != std::string::npos)
|
||||
{
|
||||
@@ -77,6 +77,8 @@ HTTPRequestAction HTTPRequest::consume(boost::asio::streambuf& buf)
|
||||
std::string headerValue = line.substr(colon+1);
|
||||
boost::trim(headerValue);
|
||||
|
||||
mHeaders[headerName] += headerValue;
|
||||
|
||||
if (headerName == "connection")
|
||||
{
|
||||
boost::to_lower(headerValue);
|
||||
@@ -91,7 +93,6 @@ HTTPRequestAction HTTPRequest::consume(boost::asio::streambuf& buf)
|
||||
|
||||
if (headerName == "authorization")
|
||||
sAuthorization = headerValue;
|
||||
|
||||
}
|
||||
|
||||
return haREAD_LINE;
|
||||
@@ -100,3 +101,5 @@ HTTPRequestAction HTTPRequest::consume(boost::asio::streambuf& buf)
|
||||
assert(false);
|
||||
return haERROR;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#define HTTPREQUEST__HPP
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include <boost/asio/streambuf.hpp>
|
||||
|
||||
@@ -16,7 +16,7 @@ enum HTTPRequestAction
|
||||
};
|
||||
|
||||
class HTTPRequest
|
||||
{ // an HTTP request in progress
|
||||
{ // an HTTP request we are handling from a client
|
||||
protected:
|
||||
|
||||
enum state
|
||||
@@ -32,7 +32,7 @@ protected:
|
||||
std::string sRequestBody;
|
||||
std::string sAuthorization;
|
||||
|
||||
std::vector<std::string> vHeaders;
|
||||
std::map<std::string, std::string> mHeaders;
|
||||
|
||||
int iDataSize;
|
||||
bool bShouldClose;
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
std::string& peekAuth() { return sAuthorization; }
|
||||
std::string getAuth() { return sAuthorization; }
|
||||
|
||||
std::vector<std::string>& peekHeaders() { return vHeaders; }
|
||||
std::map<std::string, std::string>& peekHeaders() { return mHeaders; }
|
||||
std::string getReplyHeaders(bool forceClose);
|
||||
|
||||
HTTPRequestAction consume(boost::asio::streambuf&);
|
||||
@@ -58,4 +58,4 @@ public:
|
||||
int getDataSize() { return iDataSize; }
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -6,9 +6,6 @@
|
||||
// TXN - Hash of transaction plus signature to give transaction ID
|
||||
const uint32 sHP_TransactionID = 0x54584E00;
|
||||
|
||||
// STX - Hash of inner transaction to sign
|
||||
const uint32 sHP_TransactionSign = 0x53545800;
|
||||
|
||||
// TND - Hash of transaction plus metadata
|
||||
const uint32 sHP_TransactionNode = 0x534E4400;
|
||||
|
||||
@@ -21,12 +18,24 @@ const uint32 sHP_InnerNode = 0x4D494E00;
|
||||
// LGR - Hash of ledger master data for signing
|
||||
const uint32 sHP_Ledger = 0x4C575200;
|
||||
|
||||
// STX - Hash of inner transaction to sign
|
||||
const uint32 sHP_TransactionSign = 0x53545800;
|
||||
|
||||
// VAL - Hash of validation for signing
|
||||
const uint32 sHP_Validation = 0x56414C00;
|
||||
|
||||
// PRP - Hash of proposal for signing
|
||||
const uint32 sHP_Proposal = 0x50525000;
|
||||
|
||||
// stx - TESTNET Hash of inner transaction to sign
|
||||
const uint32 sHP_TestNetTransactionSign = 0x73747800;
|
||||
|
||||
// val - TESTNET Hash of validation for signing
|
||||
const uint32 sHP_TestNetValidation = 0x76616C00;
|
||||
|
||||
// prp - TESTNET Hash of proposal for signing
|
||||
const uint32 sHP_TestNetProposal = 0x70727000;
|
||||
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include "../database/SqliteDatabase.h"
|
||||
|
||||
#include "Serializer.h"
|
||||
#include "Application.h"
|
||||
#include "Log.h"
|
||||
@@ -12,7 +14,8 @@ SETUP_LOG();
|
||||
DECLARE_INSTANCE(HashedObject);
|
||||
|
||||
HashedObjectStore::HashedObjectStore(int cacheSize, int cacheAge) :
|
||||
mCache("HashedObjectStore", cacheSize, cacheAge), mWriteGeneration(0), mWritePending(false)
|
||||
mCache("HashedObjectStore", cacheSize, cacheAge), mNegativeCache("HashedObjectNegativeCache", 0, 120),
|
||||
mWriteGeneration(0), mWritePending(false)
|
||||
{
|
||||
mWriteSet.reserve(128);
|
||||
}
|
||||
@@ -42,12 +45,12 @@ bool HashedObjectStore::store(HashedObjectType type, uint32 index,
|
||||
if (!mWritePending)
|
||||
{
|
||||
mWritePending = true;
|
||||
boost::thread t(boost::bind(&HashedObjectStore::bulkWrite, this));
|
||||
t.detach();
|
||||
boost::thread(boost::bind(&HashedObjectStore::bulkWrite, this)).detach();
|
||||
}
|
||||
}
|
||||
// else
|
||||
// cLog(lsTRACE) << "HOS: already had " << hash;
|
||||
mNegativeCache.del(hash);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -83,31 +86,28 @@ void HashedObjectStore::bulkWrite()
|
||||
|
||||
static boost::format fExists("SELECT ObjType FROM CommittedObjects WHERE Hash = '%s';");
|
||||
static boost::format
|
||||
fAdd("INSERT INTO CommittedObjects (Hash,ObjType,LedgerIndex,Object) VALUES ('%s','%c','%u',%s);");
|
||||
fAdd("INSERT OR IGNORE INTO CommittedObjects "
|
||||
"(Hash,ObjType,LedgerIndex,Object) VALUES ('%s','%c','%u',%s);");
|
||||
|
||||
Database* db = theApp->getHashNodeDB()->getDB();
|
||||
{
|
||||
ScopedLock sl( theApp->getHashNodeDB()->getDBLock());
|
||||
ScopedLock sl(theApp->getHashNodeDB()->getDBLock());
|
||||
|
||||
db->executeSQL("BEGIN TRANSACTION;");
|
||||
|
||||
BOOST_FOREACH(const boost::shared_ptr<HashedObject>& it, set)
|
||||
{
|
||||
if (!SQL_EXISTS(db, boost::str(fExists % it->getHash().GetHex())))
|
||||
char type;
|
||||
|
||||
switch (it->getType())
|
||||
{
|
||||
char type;
|
||||
switch(it->getType())
|
||||
{
|
||||
case hotLEDGER: type = 'L'; break;
|
||||
case hotTRANSACTION: type = 'T'; break;
|
||||
case hotACCOUNT_NODE: type = 'A'; break;
|
||||
case hotTRANSACTION_NODE: type = 'N'; break;
|
||||
default: type = 'U';
|
||||
}
|
||||
std::string rawData;
|
||||
db->escape(&(it->getData().front()), it->getData().size(), rawData);
|
||||
db->executeSQL(boost::str(fAdd % it->getHash().GetHex() % type % it->getIndex() % rawData ));
|
||||
case hotLEDGER: type = 'L'; break;
|
||||
case hotTRANSACTION: type = 'T'; break;
|
||||
case hotACCOUNT_NODE: type = 'A'; break;
|
||||
case hotTRANSACTION_NODE: type = 'N'; break;
|
||||
default: type = 'U';
|
||||
}
|
||||
db->executeSQL(boost::str(fAdd % it->getHash().GetHex() % type % it->getIndex() % sqlEscape(it->getData())));
|
||||
}
|
||||
|
||||
db->executeSQL("END TRANSACTION;");
|
||||
@@ -117,16 +117,20 @@ void HashedObjectStore::bulkWrite()
|
||||
|
||||
HashedObject::pointer HashedObjectStore::retrieve(const uint256& hash)
|
||||
{
|
||||
|
||||
HashedObject::pointer obj;
|
||||
{
|
||||
obj = mCache.fetch(hash);
|
||||
if (obj)
|
||||
{
|
||||
cLog(lsTRACE) << "HOS: " << hash << " fetch: incache";
|
||||
// cLog(lsTRACE) << "HOS: " << hash << " fetch: incache";
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
if (mNegativeCache.isPresent(hash))
|
||||
return HashedObject::pointer();
|
||||
|
||||
if (!theApp || !theApp->getHashNodeDB())
|
||||
return HashedObject::pointer();
|
||||
std::string sql = "SELECT * FROM CommittedObjects WHERE Hash='";
|
||||
@@ -134,6 +138,9 @@ HashedObject::pointer HashedObjectStore::retrieve(const uint256& hash)
|
||||
sql.append("';");
|
||||
|
||||
std::vector<unsigned char> data;
|
||||
std::string type;
|
||||
uint32 index;
|
||||
|
||||
{
|
||||
ScopedLock sl(theApp->getHashNodeDB()->getDBLock());
|
||||
Database* db = theApp->getHashNodeDB()->getDB();
|
||||
@@ -141,39 +148,111 @@ HashedObject::pointer HashedObjectStore::retrieve(const uint256& hash)
|
||||
if (!db->executeSQL(sql) || !db->startIterRows())
|
||||
{
|
||||
// cLog(lsTRACE) << "HOS: " << hash << " fetch: not in db";
|
||||
sl.unlock();
|
||||
mNegativeCache.add(hash);
|
||||
return HashedObject::pointer();
|
||||
}
|
||||
|
||||
std::string type;
|
||||
db->getStr("ObjType", type);
|
||||
if (type.size() == 0) return HashedObject::pointer();
|
||||
|
||||
uint32 index = db->getBigInt("LedgerIndex");
|
||||
index = db->getBigInt("LedgerIndex");
|
||||
|
||||
int size = db->getBinary("Object", NULL, 0);
|
||||
data.resize(size);
|
||||
db->getBinary("Object", &(data.front()), size);
|
||||
db->endIterRows();
|
||||
|
||||
assert(Serializer::getSHA512Half(data) == hash);
|
||||
|
||||
HashedObjectType htype = hotUNKNOWN;
|
||||
switch (type[0])
|
||||
{
|
||||
case 'L': htype = hotLEDGER; break;
|
||||
case 'T': htype = hotTRANSACTION; break;
|
||||
case 'A': htype = hotACCOUNT_NODE; break;
|
||||
case 'N': htype = hotTRANSACTION_NODE; break;
|
||||
default:
|
||||
cLog(lsERROR) << "Invalid hashed object";
|
||||
return HashedObject::pointer();
|
||||
}
|
||||
|
||||
obj = boost::make_shared<HashedObject>(htype, index, data, hash);
|
||||
mCache.canonicalize(hash, obj);
|
||||
}
|
||||
|
||||
assert(Serializer::getSHA512Half(data) == hash);
|
||||
|
||||
HashedObjectType htype = hotUNKNOWN;
|
||||
switch (type[0])
|
||||
{
|
||||
case 'L': htype = hotLEDGER; break;
|
||||
case 'T': htype = hotTRANSACTION; break;
|
||||
case 'A': htype = hotACCOUNT_NODE; break;
|
||||
case 'N': htype = hotTRANSACTION_NODE; break;
|
||||
default:
|
||||
assert(false);
|
||||
cLog(lsERROR) << "Invalid hashed object";
|
||||
mNegativeCache.add(hash);
|
||||
return HashedObject::pointer();
|
||||
}
|
||||
|
||||
obj = boost::make_shared<HashedObject>(htype, index, data, hash);
|
||||
mCache.canonicalize(hash, obj);
|
||||
|
||||
cLog(lsTRACE) << "HOS: " << hash << " fetch: in db";
|
||||
return obj;
|
||||
}
|
||||
|
||||
int HashedObjectStore::import(const std::string& file)
|
||||
{
|
||||
cLog(lsWARNING) << "Hash import from \"" << file << "\".";
|
||||
std::auto_ptr<Database> importDB(new SqliteDatabase(file.c_str()));
|
||||
importDB->connect();
|
||||
|
||||
int countYes = 0, countNo = 0;
|
||||
|
||||
SQL_FOREACH(importDB, "SELECT * FROM CommittedObjects;")
|
||||
{
|
||||
uint256 hash;
|
||||
std::string hashStr;
|
||||
importDB->getStr("Hash", hashStr);
|
||||
hash.SetHex(hashStr);
|
||||
if (hash.isZero())
|
||||
{
|
||||
cLog(lsWARNING) << "zero hash found in import table";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (retrieve(hash) != HashedObject::pointer())
|
||||
++countNo;
|
||||
else
|
||||
{ // we don't have this object
|
||||
std::vector<unsigned char> data;
|
||||
std::string type;
|
||||
importDB->getStr("ObjType", type);
|
||||
uint32 index = importDB->getBigInt("LedgerIndex");
|
||||
|
||||
int size = importDB->getBinary("Object", NULL, 0);
|
||||
data.resize(size);
|
||||
importDB->getBinary("Object", &(data.front()), size);
|
||||
|
||||
assert(Serializer::getSHA512Half(data) == hash);
|
||||
|
||||
HashedObjectType htype = hotUNKNOWN;
|
||||
switch (type[0])
|
||||
{
|
||||
case 'L': htype = hotLEDGER; break;
|
||||
case 'T': htype = hotTRANSACTION; break;
|
||||
case 'A': htype = hotACCOUNT_NODE; break;
|
||||
case 'N': htype = hotTRANSACTION_NODE; break;
|
||||
default:
|
||||
assert(false);
|
||||
cLog(lsERROR) << "Invalid hashed object";
|
||||
}
|
||||
|
||||
if (Serializer::getSHA512Half(data) != hash)
|
||||
{
|
||||
cLog(lsWARNING) << "Hash mismatch in import table " << hash
|
||||
<< " " << Serializer::getSHA512Half(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
store(htype, index, data, hash);
|
||||
++countYes;
|
||||
}
|
||||
}
|
||||
if (((countYes + countNo) % 100) == 99)
|
||||
{
|
||||
cLog(lsINFO) << "Import in progress: yes=" << countYes << ", no=" << countNo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cLog(lsWARNING) << "Imported " << countYes << " nodes, had " << countNo << " nodes";
|
||||
waitWrite();
|
||||
return countYes;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "uint256.h"
|
||||
#include "ScopedLock.h"
|
||||
#include "TaggedCache.h"
|
||||
#include "KeyCache.h"
|
||||
#include "InstanceCounter.h"
|
||||
|
||||
DEFINE_INSTANCE(HashedObject);
|
||||
@@ -28,7 +29,7 @@ class HashedObject : private IS_INSTANCE(HashedObject)
|
||||
public:
|
||||
typedef boost::shared_ptr<HashedObject> pointer;
|
||||
|
||||
HashedObjectType mType;
|
||||
HashedObjectType mType;
|
||||
uint256 mHash;
|
||||
uint32 mLedgerIndex;
|
||||
std::vector<unsigned char> mData;
|
||||
@@ -45,7 +46,8 @@ public:
|
||||
class HashedObjectStore
|
||||
{
|
||||
protected:
|
||||
TaggedCache<uint256, HashedObject> mCache;
|
||||
TaggedCache<uint256, HashedObject> mCache;
|
||||
KeyCache<uint256> mNegativeCache;
|
||||
|
||||
boost::mutex mWriteMutex;
|
||||
boost::condition_variable mWriteCondition;
|
||||
@@ -65,7 +67,10 @@ public:
|
||||
|
||||
void bulkWrite();
|
||||
void waitWrite();
|
||||
void sweep() { mCache.sweep(); }
|
||||
void sweep() { mCache.sweep(); mNegativeCache.sweep(); }
|
||||
|
||||
int import(const std::string&);
|
||||
};
|
||||
|
||||
#endif
|
||||
// vim:ts=4
|
||||
|
||||
@@ -362,22 +362,4 @@ void HttpsClient::httpsGet(
|
||||
client->httpsGet(deqSites, timeout, complete);
|
||||
}
|
||||
|
||||
bool HttpsClient::httpsParseUrl(const std::string& strUrl, std::string& strDomain, std::string& strPath)
|
||||
{
|
||||
static boost::regex reUrl("(?i)\\`\\s*https://([^/]+)(/.*)\\s*\\'"); // https://DOMAINPATH
|
||||
|
||||
boost::smatch smMatch;
|
||||
|
||||
bool bMatch = boost::regex_match(strUrl, smMatch, reUrl); // Match status code.
|
||||
|
||||
if (bMatch)
|
||||
{
|
||||
strDomain = smMatch[1];
|
||||
strPath = smMatch[2];
|
||||
}
|
||||
// std::cerr << strUrl << " : " << bMatch << " : '" << strDomain << "' : '" << strPath << "'" << std::endl;
|
||||
|
||||
return bMatch;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
|
||||
@@ -99,8 +99,6 @@ public:
|
||||
std::size_t responseMax,
|
||||
boost::posix_time::time_duration timeout,
|
||||
boost::function<void(const boost::system::error_code& ecResult, std::string& strData)> complete);
|
||||
|
||||
static bool httpsParseUrl(const std::string& strUrl, std::string& strDomain, std::string& strPath);
|
||||
};
|
||||
#endif
|
||||
// vim:ts=4
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "InstanceCounter.h"
|
||||
|
||||
InstanceType* InstanceType::sHeadInstance = NULL;
|
||||
bool InstanceType::sMultiThreaded = false;
|
||||
|
||||
std::vector<InstanceType::InstanceCount> InstanceType::getInstanceCounts(int min)
|
||||
{
|
||||
|
||||
@@ -32,6 +32,7 @@ protected:
|
||||
|
||||
InstanceType* mNextInstance;
|
||||
static InstanceType* sHeadInstance;
|
||||
static bool sMultiThreaded;
|
||||
|
||||
public:
|
||||
typedef std::pair<std::string, int> InstanceCount;
|
||||
@@ -42,17 +43,32 @@ public:
|
||||
sHeadInstance = this;
|
||||
}
|
||||
|
||||
static void multiThread()
|
||||
{
|
||||
// We can support global objects and multi-threaded code, but not both
|
||||
// at the same time. Switch to multi-threaded.
|
||||
sMultiThreaded = true;
|
||||
}
|
||||
|
||||
void addInstance()
|
||||
{
|
||||
mLock.lock();
|
||||
++mInstances;
|
||||
mLock.unlock();
|
||||
if (sMultiThreaded)
|
||||
{
|
||||
mLock.lock();
|
||||
++mInstances;
|
||||
mLock.unlock();
|
||||
}
|
||||
else ++mInstances;
|
||||
}
|
||||
void decInstance()
|
||||
{
|
||||
mLock.lock();
|
||||
--mInstances;
|
||||
mLock.unlock();
|
||||
if (sMultiThreaded)
|
||||
{
|
||||
mLock.lock();
|
||||
--mInstances;
|
||||
mLock.unlock();
|
||||
}
|
||||
else --mInstances;
|
||||
}
|
||||
int getCount()
|
||||
{
|
||||
@@ -70,11 +86,13 @@ public:
|
||||
class Instance
|
||||
{
|
||||
protected:
|
||||
static bool running;
|
||||
InstanceType& mType;
|
||||
|
||||
public:
|
||||
Instance(InstanceType& t) : mType(t) { mType.addInstance(); }
|
||||
~Instance() { mType.decInstance(); }
|
||||
~Instance() { if (running) mType.decInstance(); }
|
||||
static void shutdown() { running = false; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -129,7 +129,7 @@ TER Interpreter::interpret(Contract* contract,const SerializedTransaction& txn,s
|
||||
return(temMALFORMED); // TODO: is this actually what we want to do?
|
||||
}
|
||||
|
||||
mTotalFee += mFunctionTable[ fun ]->getFee();
|
||||
mTotalFee += mFunctionTable[ fun ]->getFee(); // FIXME: You can't use fees this way, there's no consensus
|
||||
if(mTotalFee>txn.getTransactionFee().getNValue())
|
||||
{
|
||||
// TODO: log
|
||||
|
||||
@@ -50,7 +50,7 @@ public:
|
||||
|
||||
Interpreter();
|
||||
|
||||
// returns a TransactionEngineResult
|
||||
// returns a TransactionEngineResult
|
||||
TER interpret(Contract* contract,const SerializedTransaction& txn,std::vector<unsigned char>& code);
|
||||
|
||||
void stop();
|
||||
@@ -67,16 +67,13 @@ public:
|
||||
bool startBlock(int offset);
|
||||
bool endBlock();
|
||||
|
||||
|
||||
Data::pointer getIntData();
|
||||
Data::pointer getFloatData();
|
||||
Data::pointer getUint160Data();
|
||||
Data::pointer getAcceptData(int index);
|
||||
Data::pointer getContractData(int index);
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // end namespace
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
#include "Log.h"
|
||||
#include "Config.h"
|
||||
|
||||
SETUP_LOG();
|
||||
|
||||
@@ -14,6 +15,7 @@ JobQueue::JobQueue() : mLastJob(0), mThreadCount(0), mShuttingDown(false)
|
||||
mJobLoads[jtPROOFWORK].setTargetLatency(2000, 5000);
|
||||
mJobLoads[jtTRANSACTION].setTargetLatency(250, 1000);
|
||||
mJobLoads[jtPROPOSAL_ut].setTargetLatency(500, 1250);
|
||||
mJobLoads[jtPUBLEDGER].setTargetLatency(1000, 2500);
|
||||
mJobLoads[jtVALIDATION_t].setTargetLatency(500, 1500);
|
||||
mJobLoads[jtTRANSACTION_l].setTargetLatency(100, 500);
|
||||
mJobLoads[jtPROPOSAL_t].setTargetLatency(100, 500);
|
||||
@@ -23,7 +25,6 @@ JobQueue::JobQueue() : mLastJob(0), mThreadCount(0), mShuttingDown(false)
|
||||
mJobLoads[jtDISK].setTargetLatency(500, 1000);
|
||||
mJobLoads[jtRPC].setTargetLatency(250, 750);
|
||||
mJobLoads[jtACCEPTLEDGER].setTargetLatency(1000, 2500);
|
||||
mJobLoads[jtPUBLEDGER].setTargetLatency(1000, 2500);
|
||||
}
|
||||
|
||||
|
||||
@@ -33,56 +34,60 @@ const char* Job::toString(JobType t)
|
||||
{
|
||||
case jtINVALID: return "invalid";
|
||||
case jtVALIDATION_ut: return "untrustedValidation";
|
||||
case jtTRANSACTION: return "transaction";
|
||||
case jtPROOFWORK: return "proofOfWork";
|
||||
case jtPROPOSAL_ut: return "untrustedProposal";
|
||||
case jtCLIENT: return "clientCommand";
|
||||
case jtTRANSACTION: return "transaction";
|
||||
case jtPUBLEDGER: return "publishLedger";
|
||||
case jtVALIDATION_t: return "trustedValidation";
|
||||
case jtTRANSACTION_l: return "localTransaction";
|
||||
case jtPROPOSAL_t: return "trustedProposal";
|
||||
case jtADMIN: return "administration";
|
||||
case jtDEATH: return "jobOfDeath";
|
||||
case jtCLIENT: return "clientCommand";
|
||||
|
||||
case jtPEER: return "peerCommand";
|
||||
case jtDISK: return "diskAccess";
|
||||
case jtRPC: return "rpc";
|
||||
case jtACCEPTLEDGER: return "acceptLedger";
|
||||
case jtPUBLEDGER: return "pubLedger";
|
||||
case jtTXN_PROC: return "processTransaction";
|
||||
default: assert(false); return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
bool Job::operator<(const Job& j) const
|
||||
bool Job::operator>(const Job& j) const
|
||||
{ // These comparison operators make the jobs sort in priority order in the job set
|
||||
if (mType < j.mType)
|
||||
return true;
|
||||
if (mType > j.mType)
|
||||
return false;
|
||||
return mJobIndex < j.mJobIndex;
|
||||
}
|
||||
|
||||
bool Job::operator<=(const Job& j) const
|
||||
{
|
||||
if (mType < j.mType)
|
||||
return true;
|
||||
if (mType > j.mType)
|
||||
return false;
|
||||
return mJobIndex <= j.mJobIndex;
|
||||
}
|
||||
|
||||
bool Job::operator>(const Job& j) const
|
||||
{
|
||||
if (mType < j.mType)
|
||||
return false;
|
||||
if (mType > j.mType)
|
||||
return true;
|
||||
return mJobIndex > j.mJobIndex;
|
||||
}
|
||||
|
||||
bool Job::operator>=(const Job& j) const
|
||||
{
|
||||
if (mType < j.mType)
|
||||
return true;
|
||||
if (mType > j.mType)
|
||||
return false;
|
||||
return mJobIndex >= j.mJobIndex;
|
||||
}
|
||||
|
||||
bool Job::operator<(const Job& j) const
|
||||
{
|
||||
if (mType < j.mType)
|
||||
return false;
|
||||
if (mType > j.mType)
|
||||
return true;
|
||||
return mJobIndex >= j.mJobIndex;
|
||||
return mJobIndex < j.mJobIndex;
|
||||
}
|
||||
|
||||
bool Job::operator<=(const Job& j) const
|
||||
{
|
||||
if (mType < j.mType)
|
||||
return false;
|
||||
if (mType > j.mType)
|
||||
return true;
|
||||
return mJobIndex <= j.mJobIndex;
|
||||
}
|
||||
|
||||
void JobQueue::addJob(JobType type, const boost::function<void(Job&)>& jobFunc)
|
||||
@@ -111,7 +116,7 @@ int JobQueue::getJobCountGE(JobType t)
|
||||
|
||||
boost::mutex::scoped_lock sl(mJobLock);
|
||||
|
||||
typedef std::pair<JobType, int> jt_int_pair;
|
||||
typedef std::map<JobType, int>::value_type jt_int_pair;
|
||||
BOOST_FOREACH(const jt_int_pair& it, mJobCounts)
|
||||
if (it.first >= t)
|
||||
ret += it.second;
|
||||
@@ -125,7 +130,7 @@ std::vector< std::pair<JobType, int> > JobQueue::getJobCounts()
|
||||
boost::mutex::scoped_lock sl(mJobLock);
|
||||
ret.reserve(mJobCounts.size());
|
||||
|
||||
typedef std::pair<JobType, int> jt_int_pair;
|
||||
typedef std::map<JobType, int>::value_type jt_int_pair;
|
||||
BOOST_FOREACH(const jt_int_pair& it, mJobCounts)
|
||||
ret.push_back(it);
|
||||
|
||||
@@ -154,7 +159,7 @@ Json::Value JobQueue::getJson(int)
|
||||
{
|
||||
Json::Value pri(Json::objectValue);
|
||||
if (isOver)
|
||||
pri["over_target"] = "true";
|
||||
pri["over_target"] = true;
|
||||
pri["job_type"] = Job::toString(static_cast<JobType>(i));
|
||||
if (jobCount != 0)
|
||||
pri["waiting"] = static_cast<int>(jobCount);
|
||||
@@ -180,15 +185,19 @@ void JobQueue::shutdown()
|
||||
mJobCond.notify_all();
|
||||
while (mThreadCount != 0)
|
||||
mJobCond.wait(sl);
|
||||
cLog(lsDEBUG) << "Job queue has shut down";
|
||||
}
|
||||
|
||||
void JobQueue::setThreadCount(int c)
|
||||
{ // set the number of thread serving the job queue to precisely this number
|
||||
if (c == 0)
|
||||
if (theConfig.RUN_STANDALONE)
|
||||
c = 1;
|
||||
else if (c == 0)
|
||||
{
|
||||
c = boost::thread::hardware_concurrency();
|
||||
if (c < 2)
|
||||
c = 2;
|
||||
if (c < 0)
|
||||
c = 0;
|
||||
c += 2;
|
||||
cLog(lsINFO) << "Auto-tuning to " << c << " validation/transaction/proposal threads";
|
||||
}
|
||||
|
||||
@@ -200,8 +209,7 @@ void JobQueue::setThreadCount(int c)
|
||||
while (mThreadCount < c)
|
||||
{
|
||||
++mThreadCount;
|
||||
boost::thread t(boost::bind(&JobQueue::threadEntry, this));
|
||||
t.detach();
|
||||
boost::thread(boost::bind(&JobQueue::threadEntry, this)).detach();
|
||||
}
|
||||
while (mThreadCount > c)
|
||||
{
|
||||
@@ -243,3 +251,5 @@ void JobQueue::threadEntry()
|
||||
--mThreadCount;
|
||||
mJobCond.notify_all();
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
|
||||
@@ -26,20 +26,20 @@ enum JobType
|
||||
jtPROPOSAL_ut = 3, // A proposal from an untrusted source
|
||||
jtCLIENT = 4, // A websocket command from the client
|
||||
jtTRANSACTION = 5, // A transaction received from the network
|
||||
jtVALIDATION_t = 6, // A validation from a trusted source
|
||||
jtTRANSACTION_l = 7, // A local transaction
|
||||
jtPROPOSAL_t = 8, // A proposal from a trusted source
|
||||
jtADMIN = 9, // An administrative operation
|
||||
jtDEATH = 10, // job of death, used internally
|
||||
jtPUBLEDGER = 6, // Publish a fully-accepted ledger
|
||||
jtVALIDATION_t = 7, // A validation from a trusted source
|
||||
jtTRANSACTION_l = 8, // A local transaction
|
||||
jtPROPOSAL_t = 9, // A proposal from a trusted source
|
||||
jtADMIN = 10, // An administrative operation
|
||||
jtDEATH = 11, // job of death, used internally
|
||||
|
||||
// special types not dispatched by the job pool
|
||||
jtPEER = 17,
|
||||
jtDISK = 18,
|
||||
jtRPC = 19,
|
||||
jtACCEPTLEDGER = 20,
|
||||
jtPUBLEDGER = 21,
|
||||
jtTXN_PROC = 22,
|
||||
};
|
||||
jtTXN_PROC = 21,
|
||||
}; // CAUTION: If you add new types, add them to JobType.cpp too
|
||||
#define NUM_JOB_TYPES 24
|
||||
|
||||
class Job
|
||||
|
||||
129
src/cpp/ripple/KeyCache.h
Normal file
129
src/cpp/ripple/KeyCache.h
Normal file
@@ -0,0 +1,129 @@
|
||||
#ifndef KEY_CACHE__H
|
||||
#define KEY_CACHE__H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
|
||||
template <typename c_Key> class KeyCache
|
||||
{ // Maintains a cache of keys with no associated data
|
||||
public:
|
||||
typedef c_Key key_type;
|
||||
typedef boost::unordered_map<key_type, time_t> map_type;
|
||||
typedef typename map_type::iterator map_iterator;
|
||||
|
||||
protected:
|
||||
const std::string mName;
|
||||
boost::mutex mNCLock;
|
||||
map_type mCache;
|
||||
unsigned int mTargetSize, mTargetAge;
|
||||
|
||||
public:
|
||||
|
||||
KeyCache(const std::string& name, int size = 0, int age = 120) : mName(name), mTargetSize(size), mTargetAge(age)
|
||||
{
|
||||
assert((mTargetSize >= 0) && (mTargetAge > 2));
|
||||
}
|
||||
|
||||
void getSize()
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mNCLock);
|
||||
return mCache.size();
|
||||
}
|
||||
|
||||
void getTargetSize()
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mNCLock);
|
||||
return mTargetSize;
|
||||
}
|
||||
|
||||
void getTargetAge()
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mNCLock);
|
||||
return mTargetAge;
|
||||
}
|
||||
|
||||
void setTargets(int size, int age)
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mNCLock);
|
||||
mTargetSize = size;
|
||||
mTargetAge = age;
|
||||
assert((mTargetSize >= 0) && (mTargetAge > 2));
|
||||
}
|
||||
|
||||
const std::string& getName()
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
|
||||
bool isPresent(const key_type& key, bool refresh = true)
|
||||
{ // Check if an entry is cached, refresh it if so
|
||||
boost::mutex::scoped_lock sl(mNCLock);
|
||||
|
||||
map_iterator it = mCache.find(key);
|
||||
if (it == mCache.end())
|
||||
return false;
|
||||
if (refresh)
|
||||
it->second = time(NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool del(const key_type& key)
|
||||
{ // Remove an entry from the cache, return false if not-present
|
||||
boost::mutex::scoped_lock sl(mNCLock);
|
||||
|
||||
map_iterator it = mCache.find(key);
|
||||
if (it == mCache.end())
|
||||
return false;
|
||||
|
||||
mCache.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool add(const key_type& key)
|
||||
{ // Add an entry to the cache, return true if it is new
|
||||
boost::mutex::scoped_lock sl(mNCLock);
|
||||
|
||||
map_iterator it = mCache.find(key);
|
||||
if (it != mCache.end())
|
||||
{
|
||||
it->second = time(NULL);
|
||||
return false;
|
||||
}
|
||||
mCache.insert(std::make_pair(key, time(NULL)));
|
||||
return true;
|
||||
}
|
||||
|
||||
void sweep()
|
||||
{ // Remove stale entries from the cache
|
||||
time_t now = time(NULL);
|
||||
boost::mutex::scoped_lock sl(mNCLock);
|
||||
|
||||
time_t target;
|
||||
if ((mTargetSize == 0) || (mCache.size() <= mTargetSize))
|
||||
target = now - mTargetAge;
|
||||
else
|
||||
{
|
||||
target = now - (mTargetAge * mTargetSize / mCache.size());
|
||||
if (target > (now - 2))
|
||||
target = now - 2;
|
||||
}
|
||||
|
||||
map_iterator it = mCache.begin();
|
||||
while (it != mCache.end())
|
||||
{
|
||||
if (it->second > now)
|
||||
{
|
||||
it->second = now;
|
||||
++it;
|
||||
}
|
||||
else if (it->second < target)
|
||||
it = mCache.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -37,6 +37,7 @@ Ledger::Ledger(const RippleAddress& masterID, uint64 startAmount) : mTotCoins(st
|
||||
mAccountStateMap->armDirty();
|
||||
writeBack(lepCREATE, startAccount->getSLE());
|
||||
SHAMap::flushDirty(*mAccountStateMap->disarmDirty(), 256, hotACCOUNT_NODE, mLedgerSeq);
|
||||
zeroFees();
|
||||
}
|
||||
|
||||
Ledger::Ledger(const uint256 &parentHash, const uint256 &transHash, const uint256 &accountHash,
|
||||
@@ -55,6 +56,7 @@ Ledger::Ledger(const uint256 &parentHash, const uint256 &transHash, const uint25
|
||||
mAccountStateMap->fetchRoot(mAccountHash);
|
||||
mTransactionMap->setImmutable();
|
||||
mAccountStateMap->setImmutable();
|
||||
zeroFees();
|
||||
}
|
||||
|
||||
Ledger::Ledger(Ledger& ledger, bool isMutable) : mTotCoins(ledger.mTotCoins), mLedgerSeq(ledger.mLedgerSeq),
|
||||
@@ -65,6 +67,7 @@ Ledger::Ledger(Ledger& ledger, bool isMutable) : mTotCoins(ledger.mTotCoins), mL
|
||||
mAccountStateMap(ledger.mAccountStateMap->snapShot(isMutable))
|
||||
{ // Create a new ledger that's a snapshot of this one
|
||||
updateHash();
|
||||
zeroFees();
|
||||
}
|
||||
|
||||
|
||||
@@ -88,20 +91,23 @@ Ledger::Ledger(bool /* dummy */, Ledger& prevLedger) :
|
||||
}
|
||||
else
|
||||
mCloseTime = prevLedger.mCloseTime + mCloseResolution;
|
||||
zeroFees();
|
||||
}
|
||||
|
||||
Ledger::Ledger(const std::vector<unsigned char>& rawLedger) :
|
||||
Ledger::Ledger(const std::vector<unsigned char>& rawLedger, bool hasPrefix) :
|
||||
mClosed(false), mValidHash(false), mAccepted(false), mImmutable(true)
|
||||
{
|
||||
Serializer s(rawLedger);
|
||||
setRaw(s);
|
||||
setRaw(s, hasPrefix);
|
||||
zeroFees();
|
||||
}
|
||||
|
||||
Ledger::Ledger(const std::string& rawLedger) :
|
||||
Ledger::Ledger(const std::string& rawLedger, bool hasPrefix) :
|
||||
mClosed(false), mValidHash(false), mAccepted(false), mImmutable(true)
|
||||
{
|
||||
Serializer s(rawLedger);
|
||||
setRaw(s);
|
||||
setRaw(s, hasPrefix);
|
||||
zeroFees();
|
||||
}
|
||||
|
||||
void Ledger::updateHash()
|
||||
@@ -121,9 +127,10 @@ void Ledger::updateHash()
|
||||
mValidHash = true;
|
||||
}
|
||||
|
||||
void Ledger::setRaw(Serializer &s)
|
||||
void Ledger::setRaw(Serializer &s, bool hasPrefix)
|
||||
{
|
||||
SerializerIterator sit(s);
|
||||
if (hasPrefix) sit.get32();
|
||||
mLedgerSeq = sit.get32();
|
||||
mTotCoins = sit.get64();
|
||||
mParentHash = sit.get256();
|
||||
@@ -157,7 +164,7 @@ void Ledger::addRaw(Serializer &s) const
|
||||
void Ledger::setAccepted(uint32 closeTime, int closeResolution, bool correctCloseTime)
|
||||
{ // used when we witnessed the consensus
|
||||
assert(mClosed && !mAccepted);
|
||||
mCloseTime = closeTime - (closeTime % closeResolution);
|
||||
mCloseTime = correctCloseTime ? (closeTime - (closeTime % closeResolution)) : closeTime;
|
||||
mCloseResolution = closeResolution;
|
||||
mCloseFlags = correctCloseTime ? 0 : sLCF_NoConsensusTime;
|
||||
updateHash();
|
||||
@@ -179,18 +186,22 @@ AccountState::pointer Ledger::getAccountState(const RippleAddress& accountID)
|
||||
#ifdef DEBUG
|
||||
// std::cerr << "Ledger:getAccountState(" << accountID.humanAccountID() << ")" << std::endl;
|
||||
#endif
|
||||
|
||||
SHAMapItem::pointer item = mAccountStateMap->peekItem(Ledger::getAccountRootIndex(accountID));
|
||||
if (!item)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
// std::cerr << " notfound" << std::endl;
|
||||
#endif
|
||||
cLog(lsDEBUG) << boost::str(boost::format("Ledger:getAccountState: not found: %s: %s")
|
||||
% accountID.humanAccountID()
|
||||
% Ledger::getAccountRootIndex(accountID).GetHex());
|
||||
|
||||
return AccountState::pointer();
|
||||
}
|
||||
|
||||
SerializedLedgerEntry::pointer sle =
|
||||
boost::make_shared<SerializedLedgerEntry>(item->peekSerializer(), item->getTag());
|
||||
if (sle->getType() != ltACCOUNT_ROOT)
|
||||
return AccountState::pointer();
|
||||
|
||||
return boost::make_shared<AccountState>(sle,accountID);
|
||||
}
|
||||
|
||||
@@ -367,6 +378,12 @@ void Ledger::saveAcceptedLedger(bool fromConsensus, LoadEvent::pointer event)
|
||||
assert (getAccountHash() == mAccountStateMap->getHash());
|
||||
assert (getTransHash() == mTransactionMap->getHash());
|
||||
|
||||
// Save the ledger header in the hashed object store
|
||||
Serializer s(128);
|
||||
s.add32(sHP_Ledger);
|
||||
addRaw(s);
|
||||
theApp->getHashedObjectStore().store(hotLEDGER, mLedgerSeq, s.peekData(), mHash);
|
||||
|
||||
{
|
||||
{
|
||||
ScopedLock sl(theApp->getLedgerDB()->getDBLock());
|
||||
@@ -386,39 +403,46 @@ void Ledger::saveAcceptedLedger(bool fromConsensus, LoadEvent::pointer event)
|
||||
assert(type == SHAMapTreeNode::tnTRANSACTION_MD);
|
||||
SerializerIterator sit(item->peekSerializer());
|
||||
Serializer rawTxn(sit.getVL());
|
||||
std::string escMeta(sqlEscape(sit.getVL()));
|
||||
Serializer rawMeta(sit.getVL());
|
||||
std::string escMeta(sqlEscape(rawMeta.peekData()));
|
||||
|
||||
SerializerIterator txnIt(rawTxn);
|
||||
SerializedTransaction txn(txnIt);
|
||||
assert(txn.getTransactionID() == item->getTag());
|
||||
TransactionMetaSet meta(item->getTag(), mLedgerSeq, rawMeta.peekData());
|
||||
|
||||
// Make sure transaction is in AccountTransactions.
|
||||
if (!SQL_EXISTS(db, boost::str(AcctTransExists % item->getTag().GetHex())))
|
||||
{
|
||||
// Transaction not in AccountTransactions
|
||||
std::vector<RippleAddress> accts = txn.getAffectedAccounts();
|
||||
|
||||
std::string sql = "INSERT INTO AccountTransactions (TransID, Account, LedgerSeq) VALUES ";
|
||||
bool first = true;
|
||||
for (std::vector<RippleAddress>::iterator it = accts.begin(), end = accts.end(); it != end; ++it)
|
||||
const std::vector<RippleAddress> accts = meta.getAffectedAccounts();
|
||||
if (!accts.empty())
|
||||
{
|
||||
if (!first)
|
||||
sql += ", ('";
|
||||
else
|
||||
|
||||
std::string sql = "INSERT INTO AccountTransactions (TransID, Account, LedgerSeq) VALUES ";
|
||||
bool first = true;
|
||||
for (std::vector<RippleAddress>::const_iterator it = accts.begin(), end = accts.end(); it != end; ++it)
|
||||
{
|
||||
sql += "('";
|
||||
first = false;
|
||||
if (!first)
|
||||
sql += ", ('";
|
||||
else
|
||||
{
|
||||
sql += "('";
|
||||
first = false;
|
||||
}
|
||||
sql += txn.getTransactionID().GetHex();
|
||||
sql += "','";
|
||||
sql += it->humanAccountID();
|
||||
sql += "',";
|
||||
sql += boost::lexical_cast<std::string>(getLedgerSeq());
|
||||
sql += ")";
|
||||
}
|
||||
sql += txn.getTransactionID().GetHex();
|
||||
sql += "','";
|
||||
sql += it->humanAccountID();
|
||||
sql += "',";
|
||||
sql += boost::lexical_cast<std::string>(getLedgerSeq());
|
||||
sql += ")";
|
||||
sql += ";";
|
||||
Log(lsTRACE) << "ActTx: " << sql;
|
||||
db->executeSQL(sql); // may already be in there
|
||||
}
|
||||
sql += ";";
|
||||
Log(lsTRACE) << "ActTx: " << sql;
|
||||
db->executeSQL(sql); // may already be in there
|
||||
else
|
||||
cLog(lsWARNING) << "Transaction in ledger " << mLedgerSeq << " affects no accounts";
|
||||
}
|
||||
|
||||
if (SQL_EXISTS(db, boost::str(transExists % txn.getTransactionID().GetHex())))
|
||||
@@ -458,11 +482,8 @@ void Ledger::saveAcceptedLedger(bool fromConsensus, LoadEvent::pointer event)
|
||||
return;
|
||||
}
|
||||
|
||||
theApp->getLedgerMaster().setFullLedger(shared_from_this());
|
||||
event->stop();
|
||||
|
||||
theApp->getOPs().pubLedger(shared_from_this());
|
||||
|
||||
decPendingSaves();
|
||||
}
|
||||
|
||||
@@ -502,7 +523,7 @@ Ledger::pointer Ledger::getSQL(const std::string& sql)
|
||||
db->endIterRows();
|
||||
}
|
||||
|
||||
Log(lsTRACE) << "Constructing ledger " << ledgerSeq << " from SQL";
|
||||
// Log(lsTRACE) << "Constructing ledger " << ledgerSeq << " from SQL";
|
||||
Ledger::pointer ret = boost::make_shared<Ledger>(prevHash, transHash, accountHash, totCoins,
|
||||
closingTime, prevClosingTime, closeFlags, closeResolution, ledgerSeq);
|
||||
if (ret->getHash() != ledgerHash)
|
||||
@@ -517,12 +538,59 @@ Ledger::pointer Ledger::getSQL(const std::string& sql)
|
||||
assert(false);
|
||||
return Ledger::pointer();
|
||||
}
|
||||
Log(lsDEBUG) << "Loaded ledger: " << ledgerHash;
|
||||
cLog(lsTRACE) << "Loaded ledger: " << ledgerHash;
|
||||
return ret;
|
||||
}
|
||||
|
||||
Ledger::pointer Ledger::loadByIndex(uint32 ledgerIndex)
|
||||
uint256 Ledger::getHashByIndex(uint32 ledgerIndex)
|
||||
{
|
||||
uint256 ret;
|
||||
|
||||
std::string sql="SELECT LedgerHash FROM Ledgers WHERE LedgerSeq='";
|
||||
sql.append(boost::lexical_cast<std::string>(ledgerIndex));
|
||||
sql.append("';");
|
||||
|
||||
std::string hash;
|
||||
{
|
||||
Database *db = theApp->getLedgerDB()->getDB();
|
||||
ScopedLock sl(theApp->getLedgerDB()->getDBLock());
|
||||
if (!db->executeSQL(sql) || !db->startIterRows())
|
||||
return ret;
|
||||
db->getStr("LedgerHash", hash);
|
||||
db->endIterRows();
|
||||
}
|
||||
|
||||
ret.SetHex(hash);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Ledger::getHashesByIndex(uint32 ledgerIndex, uint256& ledgerHash, uint256& parentHash)
|
||||
{
|
||||
std::string sql="SELECT LedgerHash,PrevHash FROM Ledgers WHERE LedgerSeq='";
|
||||
sql.append(boost::lexical_cast<std::string>(ledgerIndex));
|
||||
sql.append("';");
|
||||
|
||||
std::string hash, prevHash;
|
||||
{
|
||||
Database *db = theApp->getLedgerDB()->getDB();
|
||||
ScopedLock sl(theApp->getLedgerDB()->getDBLock());
|
||||
if (!db->executeSQL(sql) || !db->startIterRows())
|
||||
return false;
|
||||
db->getStr("LedgerHash", hash);
|
||||
db->getStr("PrevHash", prevHash);
|
||||
db->endIterRows();
|
||||
}
|
||||
|
||||
ledgerHash.SetHex(hash);
|
||||
parentHash.SetHex(prevHash);
|
||||
|
||||
assert(ledgerHash.isNonZero() && ((ledgerIndex == 0) || parentHash.isNonZero()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Ledger::pointer Ledger::loadByIndex(uint32 ledgerIndex)
|
||||
{ // This is a low-level function with no caching
|
||||
std::string sql="SELECT * from Ledgers WHERE LedgerSeq='";
|
||||
sql.append(boost::lexical_cast<std::string>(ledgerIndex));
|
||||
sql.append("';");
|
||||
@@ -530,7 +598,7 @@ Ledger::pointer Ledger::loadByIndex(uint32 ledgerIndex)
|
||||
}
|
||||
|
||||
Ledger::pointer Ledger::loadByHash(const uint256& ledgerHash)
|
||||
{
|
||||
{ // This is a low-level function with no caching and only gets accepted ledgers
|
||||
std::string sql="SELECT * from Ledgers WHERE LedgerHash='";
|
||||
sql.append(ledgerHash.GetHex());
|
||||
sql.append("';");
|
||||
@@ -559,10 +627,13 @@ Json::Value Ledger::getJson(int options)
|
||||
{
|
||||
Json::Value ledger(Json::objectValue);
|
||||
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
ledger["parentHash"] = mParentHash.GetHex();
|
||||
|
||||
bool full = (options & LEDGER_JSON_FULL) != 0;
|
||||
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
|
||||
ledger["parentHash"] = mParentHash.GetHex();
|
||||
ledger["seqNum"] = boost::lexical_cast<std::string>(mLedgerSeq);
|
||||
|
||||
if(mClosed || full)
|
||||
{
|
||||
if (mClosed)
|
||||
@@ -585,6 +656,7 @@ Json::Value Ledger::getJson(int options)
|
||||
}
|
||||
else
|
||||
ledger["closed"] = false;
|
||||
|
||||
if (mTransactionMap && (full || ((options & LEDGER_JSON_DUMP_TXRP) != 0)))
|
||||
{
|
||||
Json::Value txns(Json::arrayValue);
|
||||
@@ -624,6 +696,7 @@ Json::Value Ledger::getJson(int options)
|
||||
}
|
||||
ledger["transactions"] = txns;
|
||||
}
|
||||
|
||||
if (mAccountStateMap && (full || ((options & LEDGER_JSON_DUMP_STATE) != 0)))
|
||||
{
|
||||
Json::Value state(Json::arrayValue);
|
||||
@@ -641,7 +714,6 @@ Json::Value Ledger::getJson(int options)
|
||||
}
|
||||
ledger["accountState"] = state;
|
||||
}
|
||||
ledger["seqNum"] = boost::lexical_cast<std::string>(mLedgerSeq);
|
||||
return ledger;
|
||||
}
|
||||
|
||||
@@ -899,6 +971,13 @@ uint256 Ledger::getAccountRootIndex(const uint160& uAccountID)
|
||||
return s.getSHA512Half();
|
||||
}
|
||||
|
||||
uint256 Ledger::getLedgerFeeIndex()
|
||||
{ // get the index of the node that holds the fee schedul
|
||||
Serializer s(2);
|
||||
s.add16(spaceFee);
|
||||
return s.getSHA512Half();
|
||||
}
|
||||
|
||||
uint256 Ledger::getLedgerFeatureIndex()
|
||||
{ // get the index of the node that holds the last 256 ledgers
|
||||
Serializer s(2);
|
||||
@@ -922,23 +1001,77 @@ uint256 Ledger::getLedgerHashIndex(uint32 desiredLedgerIndex)
|
||||
return s.getSHA512Half();
|
||||
}
|
||||
|
||||
int Ledger::getLedgerHashOffset(uint32 ledgerIndex)
|
||||
{ // get the offset for this ledger's hash (or the first one after it) in the every-256-ledger table
|
||||
return (ledgerIndex >> 8) % 256;
|
||||
uint256 Ledger::getLedgerHash(uint32 ledgerIndex)
|
||||
{ // return the hash of the specified ledger, 0 if not available
|
||||
|
||||
// easy cases
|
||||
if (ledgerIndex > mLedgerSeq)
|
||||
{
|
||||
cLog(lsWARNING) << "Can't get seq " << ledgerIndex << " from " << mLedgerSeq << " future";
|
||||
return uint256();
|
||||
}
|
||||
|
||||
if (ledgerIndex == mLedgerSeq)
|
||||
return getHash();
|
||||
|
||||
if (ledgerIndex == (mLedgerSeq - 1))
|
||||
return mParentHash;
|
||||
|
||||
// within 256
|
||||
int diff = mLedgerSeq - ledgerIndex;
|
||||
if (diff <= 256)
|
||||
{
|
||||
SLE::pointer hashIndex = getSLE(getLedgerHashIndex());
|
||||
if (hashIndex)
|
||||
{
|
||||
assert(hashIndex->getFieldU32(sfLastLedgerSequence) == (mLedgerSeq - 1));
|
||||
STVector256 vec = hashIndex->getFieldV256(sfHashes);
|
||||
if (vec.size() >= diff)
|
||||
return vec.at(vec.size() - diff);
|
||||
cLog(lsWARNING) << "Ledger " << mLedgerSeq << " missing hash for " << ledgerIndex
|
||||
<< " (" << vec.size() << "," << diff << ")";
|
||||
}
|
||||
else cLog(lsWARNING) << "Ledger " << mLedgerSeq << ":" << getHash() << " missing normal list";
|
||||
}
|
||||
|
||||
if ((ledgerIndex & 0xff) != 0)
|
||||
{
|
||||
cLog(lsWARNING) << "Can't get seq " << ledgerIndex << " from " << mLedgerSeq << " past";
|
||||
return uint256();
|
||||
}
|
||||
|
||||
// in skiplist
|
||||
SLE::pointer hashIndex = getSLE(getLedgerHashIndex(ledgerIndex));
|
||||
if (hashIndex)
|
||||
{
|
||||
int lastSeq = hashIndex->getFieldU32(sfLastLedgerSequence);
|
||||
assert(lastSeq >= ledgerIndex);
|
||||
assert((lastSeq & 0xff) == 0);
|
||||
int sDiff = (lastSeq - ledgerIndex) >> 8;
|
||||
|
||||
STVector256 vec = hashIndex->getFieldV256(sfHashes);
|
||||
if (vec.size() > sDiff)
|
||||
return vec.at(vec.size() - sDiff - 1);
|
||||
}
|
||||
|
||||
cLog(lsWARNING) << "Can't get seq " << ledgerIndex << " from " << mLedgerSeq << " error";
|
||||
return uint256();
|
||||
}
|
||||
|
||||
int Ledger::getLedgerHashOffset(uint32 desiredLedgerIndex, uint32 currentLedgerIndex)
|
||||
{ // get the offset for this ledger's hash in the every-ledger table, -1 if not in it
|
||||
if (desiredLedgerIndex >= currentLedgerIndex)
|
||||
return -1;
|
||||
|
||||
if (currentLedgerIndex < 256)
|
||||
return desiredLedgerIndex;
|
||||
|
||||
if (desiredLedgerIndex < (currentLedgerIndex - 256))
|
||||
return -1;
|
||||
|
||||
return currentLedgerIndex - desiredLedgerIndex - 1;
|
||||
std::vector< std::pair<uint32, uint256> > Ledger::getLedgerHashes()
|
||||
{
|
||||
std::vector< std::pair<uint32, uint256> > ret;
|
||||
SLE::pointer hashIndex = getSLE(getLedgerHashIndex());
|
||||
if (hashIndex)
|
||||
{
|
||||
STVector256 vec = hashIndex->getFieldV256(sfHashes);
|
||||
int size = vec.size();
|
||||
ret.reserve(size);
|
||||
uint32 seq = hashIndex->getFieldU32(sfLastLedgerSequence) - size;
|
||||
for (int i = 0; i < size; ++i)
|
||||
ret.push_back(std::make_pair(++seq, vec.at(i)));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint256 Ledger::getBookBase(const uint160& uTakerPaysCurrency, const uint160& uTakerPaysIssuerID,
|
||||
@@ -1097,10 +1230,7 @@ void Ledger::updateSkipList()
|
||||
std::vector<uint256> hashes;
|
||||
|
||||
if (!skipList)
|
||||
{
|
||||
skipList = boost::make_shared<SLE>(ltLEDGER_HASHES, hash);
|
||||
skipList->setFieldU32(sfFirstLedgerSequence, prevIndex);
|
||||
}
|
||||
else
|
||||
hashes = skipList->getFieldV256(sfHashes).peekValue();
|
||||
|
||||
@@ -1122,7 +1252,6 @@ void Ledger::updateSkipList()
|
||||
if (!skipList)
|
||||
{
|
||||
skipList = boost::make_shared<SLE>(ltLEDGER_HASHES, hash);
|
||||
skipList->setFieldU32(sfFirstLedgerSequence, prevIndex);
|
||||
}
|
||||
else
|
||||
hashes = skipList->getFieldV256(sfHashes).peekValue();
|
||||
@@ -1154,18 +1283,25 @@ void Ledger::pendSave(bool fromConsensus)
|
||||
if (!fromConsensus && !theApp->isNewFlag(getHash(), SF_SAVED))
|
||||
return;
|
||||
|
||||
boost::thread thread(boost::bind(&Ledger::saveAcceptedLedger, shared_from_this(),
|
||||
fromConsensus, theApp->getJobQueue().getLoadEvent(jtDISK)));
|
||||
thread.detach();
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(sPendingSaveLock);
|
||||
++sPendingSaves;
|
||||
}
|
||||
|
||||
boost::thread(boost::bind(&Ledger::saveAcceptedLedger, shared_from_this(),
|
||||
fromConsensus, theApp->getJobQueue().getLoadEvent(jtDISK))).detach();
|
||||
|
||||
boost::recursive_mutex::scoped_lock sl(sPendingSaveLock);
|
||||
++sPendingSaves;
|
||||
}
|
||||
|
||||
void Ledger::decPendingSaves()
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(sPendingSaveLock);
|
||||
--sPendingSaves;
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(sPendingSaveLock);
|
||||
--sPendingSaves;
|
||||
if (sPendingSaves != 0)
|
||||
return;
|
||||
}
|
||||
theApp->getLedgerMaster().resumeAcquiring();
|
||||
}
|
||||
|
||||
void Ledger::ownerDirDescriber(SLE::ref sle, const uint160& owner)
|
||||
@@ -1185,5 +1321,51 @@ void Ledger::qualityDirDescriber(SLE::ref sle,
|
||||
sle->setFieldU64(sfExchangeRate, uRate);
|
||||
}
|
||||
|
||||
void Ledger::zeroFees()
|
||||
{
|
||||
mBaseFee = 0;
|
||||
mReferenceFeeUnits = 0;
|
||||
mReserveBase = 0;
|
||||
mReserveIncrement = 0;
|
||||
}
|
||||
|
||||
void Ledger::updateFees()
|
||||
{
|
||||
mBaseFee = theConfig.FEE_DEFAULT;
|
||||
mReferenceFeeUnits = 10;
|
||||
mReserveBase = theConfig.FEE_ACCOUNT_RESERVE;
|
||||
mReserveIncrement = theConfig.FEE_OWNER_RESERVE;
|
||||
|
||||
LedgerStateParms p = lepNONE;
|
||||
SLE::pointer sle = getASNode(p, Ledger::getLedgerFeeIndex(), ltFEE_SETTINGS);
|
||||
if (!sle)
|
||||
return;
|
||||
|
||||
if (sle->getFieldIndex(sfBaseFee) != -1)
|
||||
mBaseFee = sle->getFieldU64(sfBaseFee);
|
||||
|
||||
if (sle->getFieldIndex(sfReferenceFeeUnits) != -1)
|
||||
mReferenceFeeUnits = sle->getFieldU32(sfReferenceFeeUnits);
|
||||
|
||||
if (sle->getFieldIndex(sfReserveBase) != -1)
|
||||
mReserveBase = sle->getFieldU32(sfReserveBase);
|
||||
|
||||
if (sle->getFieldIndex(sfReserveIncrement) != -1)
|
||||
mReserveIncrement = sle->getFieldU32(sfReserveIncrement);
|
||||
}
|
||||
|
||||
uint64 Ledger::scaleFeeBase(uint64 fee)
|
||||
{
|
||||
if (!mBaseFee)
|
||||
updateFees();
|
||||
return theApp->getFeeTrack().scaleFeeBase(fee, mBaseFee, mReferenceFeeUnits);
|
||||
}
|
||||
|
||||
uint64 Ledger::scaleFeeLoad(uint64 fee)
|
||||
{
|
||||
if (!mBaseFee)
|
||||
updateFees();
|
||||
return theApp->getFeeTrack().scaleFeeLoad(fee, mBaseFee, mReferenceFeeUnits);
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
|
||||
@@ -78,6 +78,10 @@ private:
|
||||
uint32 mCloseFlags; // flags indicating how this ledger close took place
|
||||
bool mClosed, mValidHash, mAccepted, mImmutable;
|
||||
|
||||
uint32 mReferenceFeeUnits; // Fee units for the reference transaction
|
||||
uint32 mReserveBase, mReserveIncrement; // Reserve basse and increment in fee units
|
||||
uint64 mBaseFee; // Ripple cost of the reference transaction
|
||||
|
||||
SHAMap::pointer mTransactionMap, mAccountStateMap;
|
||||
|
||||
mutable boost::recursive_mutex mLock;
|
||||
@@ -95,6 +99,9 @@ protected:
|
||||
static void decPendingSaves();
|
||||
void saveAcceptedLedger(bool fromConsensus, LoadEvent::pointer);
|
||||
|
||||
void updateFees();
|
||||
void zeroFees();
|
||||
|
||||
public:
|
||||
Ledger(const RippleAddress& masterID, uint64 startAmount); // used for the starting bootstrap ledger
|
||||
|
||||
@@ -102,9 +109,8 @@ public:
|
||||
uint64 totCoins, uint32 closeTime, uint32 parentCloseTime, int closeFlags, int closeResolution,
|
||||
uint32 ledgerSeq); // used for database ledgers
|
||||
|
||||
Ledger(const std::vector<unsigned char>& rawLedger);
|
||||
|
||||
Ledger(const std::string& rawLedger);
|
||||
Ledger(const std::vector<unsigned char>& rawLedger, bool hasPrefix);
|
||||
Ledger(const std::string& rawLedger, bool hasPrefix);
|
||||
|
||||
Ledger(bool dummy, Ledger& previous); // ledger after this one
|
||||
|
||||
@@ -125,7 +131,7 @@ public:
|
||||
|
||||
// ledger signature operations
|
||||
void addRaw(Serializer &s) const;
|
||||
void setRaw(Serializer& s);
|
||||
void setRaw(Serializer& s, bool hasPrefix);
|
||||
|
||||
uint256 getHash();
|
||||
const uint256& getParentHash() const { return mParentHash; }
|
||||
@@ -172,9 +178,11 @@ public:
|
||||
SLE::pointer getAccountRoot(const RippleAddress& naAccountID);
|
||||
void updateSkipList();
|
||||
|
||||
// database functions
|
||||
// database functions (low-level)
|
||||
static Ledger::pointer loadByIndex(uint32 ledgerIndex);
|
||||
static Ledger::pointer loadByHash(const uint256& ledgerHash);
|
||||
static uint256 getHashByIndex(uint32 index);
|
||||
static bool getHashesByIndex(uint32 index, uint256& ledgerHash, uint256& parentHash);
|
||||
void pendSave(bool fromConsensus);
|
||||
|
||||
// next/prev function
|
||||
@@ -191,8 +199,11 @@ public:
|
||||
static uint256 getLedgerHashIndex(uint32 desiredLedgerIndex);
|
||||
static int getLedgerHashOffset(uint32 desiredLedgerIndex);
|
||||
static int getLedgerHashOffset(uint32 desiredLedgerIndex, uint32 currentLedgerIndex);
|
||||
uint256 getLedgerHash(uint32 ledgerIndex);
|
||||
std::vector< std::pair<uint32, uint256> > getLedgerHashes();
|
||||
|
||||
static uint256 getLedgerFeatureIndex();
|
||||
static uint256 getLedgerFeeIndex();
|
||||
|
||||
// index calculation functions
|
||||
static uint256 getAccountRootIndex(const uint160& uAccountID);
|
||||
@@ -293,10 +304,10 @@ public:
|
||||
SLE::pointer getRippleState(LedgerStateParms& parms, const uint256& uNode);
|
||||
|
||||
SLE::pointer getRippleState(const uint256& uNode)
|
||||
{
|
||||
LedgerStateParms qry = lepNONE;
|
||||
return getRippleState(qry, uNode);
|
||||
}
|
||||
{
|
||||
LedgerStateParms qry = lepNONE;
|
||||
return getRippleState(qry, uNode);
|
||||
}
|
||||
|
||||
SLE::pointer getRippleState(const RippleAddress& naA, const RippleAddress& naB, const uint160& uCurrency)
|
||||
{ return getRippleState(getRippleStateIndex(naA, naB, uCurrency)); }
|
||||
@@ -304,6 +315,34 @@ public:
|
||||
SLE::pointer getRippleState(const uint160& uiA, const uint160& uiB, const uint160& uCurrency)
|
||||
{ return getRippleState(getRippleStateIndex(RippleAddress::createAccountID(uiA), RippleAddress::createAccountID(uiB), uCurrency)); }
|
||||
|
||||
uint32 getReferenceFeeUnits()
|
||||
{
|
||||
if (!mBaseFee) updateFees();
|
||||
return mReferenceFeeUnits;
|
||||
}
|
||||
|
||||
uint64 getBaseFee()
|
||||
{
|
||||
if (!mBaseFee) updateFees();
|
||||
return mBaseFee;
|
||||
}
|
||||
|
||||
uint64 getReserve(int increments)
|
||||
{
|
||||
if (!mBaseFee) updateFees();
|
||||
return scaleFeeBase(static_cast<uint64>(increments) * mReserveIncrement + mReserveBase);
|
||||
}
|
||||
|
||||
uint64 getReserveInc()
|
||||
{
|
||||
if (!mBaseFee) updateFees();
|
||||
return mReserveIncrement;
|
||||
}
|
||||
|
||||
uint64 scaleFeeBase(uint64 fee);
|
||||
uint64 scaleFeeLoad(uint64 fee);
|
||||
|
||||
|
||||
Json::Value getJson(int options);
|
||||
void addJson(Json::Value&, int options);
|
||||
|
||||
|
||||
@@ -11,15 +11,16 @@
|
||||
#include "HashPrefixes.h"
|
||||
|
||||
SETUP_LOG();
|
||||
DECLARE_INSTANCE(PeerSet);
|
||||
DECLARE_INSTANCE(LedgerAcquire);
|
||||
|
||||
#define LA_DEBUG
|
||||
#define LEDGER_ACQUIRE_TIMEOUT 750
|
||||
#define TRUST_NETWORK
|
||||
|
||||
PeerSet::PeerSet(const uint256& hash, int interval) : mHash(hash), mTimerInterval(interval), mTimeouts(0),
|
||||
mComplete(false), mFailed(false), mProgress(true), mTimer(theApp->getIOService())
|
||||
mComplete(false), mFailed(false), mProgress(true), mAggressive(true), mTimer(theApp->getIOService())
|
||||
{
|
||||
mLastAction = time(NULL);
|
||||
assert((mTimerInterval > 10) && (mTimerInterval < 30000));
|
||||
}
|
||||
|
||||
@@ -37,7 +38,7 @@ void PeerSet::badPeer(Peer::ref ptr)
|
||||
mPeers.erase(ptr->getPeerId());
|
||||
}
|
||||
|
||||
void PeerSet::resetTimer()
|
||||
void PeerSet::setTimer()
|
||||
{
|
||||
mTimer.expires_from_now(boost::posix_time::milliseconds(mTimerInterval));
|
||||
mTimer.async_wait(boost::bind(&PeerSet::TimerEntry, pmDowncast(), boost::asio::placeholders::error));
|
||||
@@ -45,6 +46,9 @@ void PeerSet::resetTimer()
|
||||
|
||||
void PeerSet::invokeOnTimer()
|
||||
{
|
||||
if (isDone())
|
||||
return;
|
||||
|
||||
if (!mProgress)
|
||||
{
|
||||
++mTimeouts;
|
||||
@@ -56,6 +60,9 @@ void PeerSet::invokeOnTimer()
|
||||
mProgress = false;
|
||||
onTimer(true);
|
||||
}
|
||||
|
||||
if (!isDone())
|
||||
setTimer();
|
||||
}
|
||||
|
||||
void PeerSet::TimerEntry(boost::weak_ptr<PeerSet> wptr, const boost::system::error_code& result)
|
||||
@@ -67,76 +74,136 @@ void PeerSet::TimerEntry(boost::weak_ptr<PeerSet> wptr, const boost::system::err
|
||||
ptr->invokeOnTimer();
|
||||
}
|
||||
|
||||
LedgerAcquire::LedgerAcquire(const uint256& hash) : PeerSet(hash, LEDGER_ACQUIRE_TIMEOUT),
|
||||
mHaveBase(false), mHaveState(false), mHaveTransactions(false), mAborted(false), mSignaled(false), mAccept(false)
|
||||
bool PeerSet::isActive()
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
return !isDone();
|
||||
}
|
||||
|
||||
LedgerAcquire::LedgerAcquire(const uint256& hash) : PeerSet(hash, LEDGER_ACQUIRE_TIMEOUT),
|
||||
mHaveBase(false), mHaveState(false), mHaveTransactions(false), mAborted(false), mSignaled(false), mAccept(false),
|
||||
mByHash(true)
|
||||
{
|
||||
#ifdef LA_DEBUG
|
||||
cLog(lsTRACE) << "Acquiring ledger " << mHash;
|
||||
#endif
|
||||
tryLocal();
|
||||
}
|
||||
|
||||
bool LedgerAcquire::tryLocal()
|
||||
{ // return value: true = no more work to do
|
||||
HashedObject::pointer node = theApp->getHashedObjectStore().retrieve(mHash);
|
||||
if (!node)
|
||||
return false;
|
||||
{
|
||||
mLedger = theApp->getLedgerMaster().getLedgerByHash(mHash);
|
||||
if (!mLedger)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
mLedger = boost::make_shared<Ledger>(strCopy(node->getData()), true);
|
||||
|
||||
if (mLedger->getHash() != mHash)
|
||||
{ // We know for a fact the ledger can never be acquired
|
||||
cLog(lsWARNING) << mHash << " cannot be a ledger";
|
||||
mFailed = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
mLedger = boost::make_shared<Ledger>(strCopy(node->getData()));
|
||||
assert(mLedger->getHash() == mHash);
|
||||
mHaveBase = true;
|
||||
|
||||
if (!mLedger->getTransHash())
|
||||
if (mLedger->getTransHash().isZero())
|
||||
{
|
||||
cLog(lsDEBUG) << "No TXNs to fetch";
|
||||
mHaveTransactions = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
mLedger->peekTransactionMap()->fetchRoot(mLedger->getTransHash());
|
||||
cLog(lsDEBUG) << "Got root txn map locally";
|
||||
std::vector<uint256> h = mLedger->peekTransactionMap()->getNeededHashes(1);
|
||||
if (h.empty())
|
||||
{
|
||||
cLog(lsDEBUG) << "Had full txn map locally";
|
||||
mHaveTransactions = true;
|
||||
}
|
||||
}
|
||||
catch (SHAMapMissingNode&)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
if (!mLedger->getAccountHash())
|
||||
if (mLedger->getAccountHash().isZero())
|
||||
mHaveState = true;
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
mLedger->peekAccountStateMap()->fetchRoot(mLedger->getAccountHash());
|
||||
cLog(lsDEBUG) << "Got root AS map locally";
|
||||
std::vector<uint256> h = mLedger->peekAccountStateMap()->getNeededHashes(1);
|
||||
if (h.empty())
|
||||
{
|
||||
cLog(lsDEBUG) << "Had full AS map locally";
|
||||
mHaveState = true;
|
||||
}
|
||||
}
|
||||
catch (SHAMapMissingNode&)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
return mHaveTransactions && mHaveState;
|
||||
if (mHaveTransactions && mHaveState)
|
||||
{
|
||||
cLog(lsDEBUG) << "Had everything locally";
|
||||
mComplete = true;
|
||||
}
|
||||
|
||||
return mComplete;
|
||||
}
|
||||
|
||||
void LedgerAcquire::onTimer(bool progress)
|
||||
{
|
||||
if (getTimeouts() > 6)
|
||||
{
|
||||
cLog(lsWARNING) << "Six timeouts for ledger " << mHash;
|
||||
setFailed();
|
||||
done();
|
||||
return;
|
||||
}
|
||||
else if (!progress)
|
||||
|
||||
mRecentTXNodes.clear();
|
||||
mRecentASNodes.clear();
|
||||
|
||||
if (!progress)
|
||||
{
|
||||
mAggressive = true;
|
||||
cLog(lsDEBUG) << "No progress for ledger " << mHash;
|
||||
if (!getPeerCount())
|
||||
addPeers();
|
||||
else
|
||||
trigger(Peer::pointer(), true);
|
||||
trigger(Peer::pointer());
|
||||
}
|
||||
else
|
||||
mByHash = true;
|
||||
}
|
||||
|
||||
void LedgerAcquire::addPeers()
|
||||
{
|
||||
std::vector<Peer::pointer> peerList = theApp->getConnectionPool().getPeerVector();
|
||||
|
||||
int vSize = peerList.size();
|
||||
if (vSize == 0)
|
||||
return;
|
||||
|
||||
// We traverse the peer list in random order so as not to favor any particular peer
|
||||
int firstPeer = rand() & vSize;
|
||||
|
||||
bool found = false;
|
||||
BOOST_FOREACH(Peer::ref peer, peerList)
|
||||
for (int i = 0; i < vSize; ++i)
|
||||
{
|
||||
Peer::ref peer = peerList[(i + firstPeer) % vSize];
|
||||
if (peer->hasLedger(getHash()))
|
||||
{
|
||||
found = true;
|
||||
@@ -145,10 +212,8 @@ void LedgerAcquire::addPeers()
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
BOOST_FOREACH(Peer::ref peer, peerList)
|
||||
peerHas(peer);
|
||||
}
|
||||
for (int i = 0; i < vSize; ++i)
|
||||
peerHas(peerList[(i + firstPeer) % vSize]);
|
||||
}
|
||||
|
||||
boost::weak_ptr<PeerSet> LedgerAcquire::pmDowncast()
|
||||
@@ -161,36 +226,46 @@ void LedgerAcquire::done()
|
||||
if (mSignaled)
|
||||
return;
|
||||
mSignaled = true;
|
||||
touch();
|
||||
|
||||
#ifdef LA_DEBUG
|
||||
cLog(lsTRACE) << "Done acquiring ledger " << mHash;
|
||||
#endif
|
||||
|
||||
assert(isComplete() || isFailed());
|
||||
|
||||
std::vector< boost::function<void (LedgerAcquire::pointer)> > triggers;
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
triggers.swap(mOnComplete);
|
||||
}
|
||||
|
||||
setComplete();
|
||||
mLock.lock();
|
||||
triggers = mOnComplete;
|
||||
mOnComplete.clear();
|
||||
mLock.unlock();
|
||||
|
||||
if (mLedger)
|
||||
if (isComplete() && !isFailed() && mLedger)
|
||||
{
|
||||
if (mAccept)
|
||||
{
|
||||
mLedger->setClosed();
|
||||
mLedger->setAccepted();
|
||||
}
|
||||
theApp->getLedgerMaster().storeLedger(mLedger);
|
||||
}
|
||||
else
|
||||
theApp->getMasterLedgerAcquire().logFailure(mHash);
|
||||
|
||||
for (unsigned int i = 0; i < triggers.size(); ++i)
|
||||
triggers[i](shared_from_this());
|
||||
}
|
||||
|
||||
void LedgerAcquire::addOnComplete(boost::function<void (LedgerAcquire::pointer)> trigger)
|
||||
bool LedgerAcquire::addOnComplete(boost::function<void (LedgerAcquire::pointer)> trigger)
|
||||
{
|
||||
mLock.lock();
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
if (isDone())
|
||||
return false;
|
||||
mOnComplete.push_back(trigger);
|
||||
mLock.unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
void LedgerAcquire::trigger(Peer::ref peer, bool timer)
|
||||
void LedgerAcquire::trigger(Peer::ref peer)
|
||||
{
|
||||
if (mAborted || mComplete || mFailed)
|
||||
{
|
||||
@@ -211,12 +286,75 @@ void LedgerAcquire::trigger(Peer::ref peer, bool timer)
|
||||
cLog(lsTRACE) << "base=" << mHaveBase << " tx=" << mHaveTransactions << " as=" << mHaveState;
|
||||
}
|
||||
|
||||
if (!mHaveBase)
|
||||
{
|
||||
tryLocal();
|
||||
if (mFailed)
|
||||
{
|
||||
cLog(lsWARNING) << " failed local for " << mHash;
|
||||
}
|
||||
}
|
||||
|
||||
ripple::TMGetLedger tmGL;
|
||||
tmGL.set_ledgerhash(mHash.begin(), mHash.size());
|
||||
if (getTimeouts() != 0)
|
||||
{
|
||||
tmGL.set_querytype(ripple::qtINDIRECT);
|
||||
|
||||
if (!mHaveBase)
|
||||
if (!isProgress() && !mFailed && mByHash && (getTimeouts() > 2))
|
||||
{
|
||||
std::vector<neededHash_t> need = getNeededHashes();
|
||||
if (!need.empty())
|
||||
{
|
||||
ripple::TMGetObjectByHash tmBH;
|
||||
tmBH.set_query(true);
|
||||
tmBH.set_ledgerhash(mHash.begin(), mHash.size());
|
||||
if (mHaveBase)
|
||||
tmBH.set_seq(mLedger->getLedgerSeq());
|
||||
bool typeSet = false;
|
||||
BOOST_FOREACH(neededHash_t& p, need)
|
||||
{
|
||||
cLog(lsWARNING) << "Want: " << p.second;
|
||||
theApp->getOPs().addWantedHash(p.second);
|
||||
if (!typeSet)
|
||||
{
|
||||
tmBH.set_type(p.first);
|
||||
typeSet = true;
|
||||
}
|
||||
if (p.first == tmBH.type())
|
||||
{
|
||||
ripple::TMIndexedObject *io = tmBH.add_objects();
|
||||
io->set_hash(p.second.begin(), p.second.size());
|
||||
}
|
||||
}
|
||||
PackedMessage::pointer packet = boost::make_shared<PackedMessage>(tmBH, ripple::mtGET_OBJECTS);
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
for (boost::unordered_map<uint64, int>::iterator it = mPeers.begin(), end = mPeers.end();
|
||||
it != end; ++it)
|
||||
{
|
||||
Peer::pointer iPeer = theApp->getConnectionPool().getPeerById(it->first);
|
||||
if (iPeer)
|
||||
{
|
||||
mByHash = false;
|
||||
iPeer->sendPacket(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
cLog(lsINFO) << "Attempting by hash fetch for ledger " << mHash;
|
||||
}
|
||||
else
|
||||
{
|
||||
cLog(lsINFO) << "getNeededHashes says acquire is complete";
|
||||
mHaveBase = true;
|
||||
mHaveTransactions = true;
|
||||
mHaveState = true;
|
||||
mComplete = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!mHaveBase && !mFailed)
|
||||
{
|
||||
tmGL.set_itype(ripple::liBASE);
|
||||
cLog(lsTRACE) << "Sending base request to " << (peer ? "selected peer" : "all peers");
|
||||
@@ -224,11 +362,10 @@ void LedgerAcquire::trigger(Peer::ref peer, bool timer)
|
||||
return;
|
||||
}
|
||||
|
||||
assert(mLedger);
|
||||
if (mLedger)
|
||||
tmGL.set_ledgerseq(mLedger->getLedgerSeq());
|
||||
|
||||
if (mHaveBase && !mHaveTransactions)
|
||||
if (mHaveBase && !mHaveTransactions && !mFailed)
|
||||
{
|
||||
assert(mLedger);
|
||||
if (mLedger->peekTransactionMap()->getHash().isZero())
|
||||
@@ -242,11 +379,13 @@ void LedgerAcquire::trigger(Peer::ref peer, bool timer)
|
||||
{
|
||||
std::vector<SHAMapNode> nodeIDs;
|
||||
std::vector<uint256> nodeHashes;
|
||||
TransactionStateSF tFilter(mLedger->getHash(), mLedger->getLedgerSeq());
|
||||
mLedger->peekTransactionMap()->getMissingNodes(nodeIDs, nodeHashes, 128, &tFilter);
|
||||
nodeIDs.reserve(256);
|
||||
nodeHashes.reserve(256);
|
||||
mLedger->peekTransactionMap()->getMissingNodes(nodeIDs, nodeHashes, 256, NULL);
|
||||
if (nodeIDs.empty())
|
||||
{
|
||||
if (!mLedger->peekTransactionMap()->isValid()) mFailed = true;
|
||||
if (!mLedger->peekTransactionMap()->isValid())
|
||||
mFailed = true;
|
||||
else
|
||||
{
|
||||
mHaveTransactions = true;
|
||||
@@ -256,19 +395,24 @@ void LedgerAcquire::trigger(Peer::ref peer, bool timer)
|
||||
}
|
||||
else
|
||||
{
|
||||
tmGL.set_itype(ripple::liTX_NODE);
|
||||
BOOST_FOREACH(SHAMapNode& it, nodeIDs)
|
||||
if (!mAggressive)
|
||||
filterNodes(nodeIDs, nodeHashes, mRecentTXNodes, 128, !isProgress());
|
||||
if (!nodeIDs.empty())
|
||||
{
|
||||
*(tmGL.add_nodeids()) = it.getRawString();
|
||||
tmGL.set_itype(ripple::liTX_NODE);
|
||||
BOOST_FOREACH(SHAMapNode& it, nodeIDs)
|
||||
{
|
||||
*(tmGL.add_nodeids()) = it.getRawString();
|
||||
}
|
||||
cLog(lsTRACE) << "Sending TX node " << nodeIDs.size()
|
||||
<< " request to " << (peer ? "selected peer" : "all peers");
|
||||
sendRequest(tmGL, peer);
|
||||
}
|
||||
cLog(lsTRACE) << "Sending TX node " << nodeIDs.size()
|
||||
<< " request to " << (peer ? "selected peer" : "all peers");
|
||||
sendRequest(tmGL, peer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mHaveBase && !mHaveState)
|
||||
if (mHaveBase && !mHaveState && !mFailed)
|
||||
{
|
||||
assert(mLedger);
|
||||
if (mLedger->peekAccountStateMap()->getHash().isZero())
|
||||
@@ -282,11 +426,13 @@ void LedgerAcquire::trigger(Peer::ref peer, bool timer)
|
||||
{
|
||||
std::vector<SHAMapNode> nodeIDs;
|
||||
std::vector<uint256> nodeHashes;
|
||||
AccountStateSF aFilter(mLedger->getHash(), mLedger->getLedgerSeq());
|
||||
mLedger->peekAccountStateMap()->getMissingNodes(nodeIDs, nodeHashes, 128, &aFilter);
|
||||
nodeIDs.reserve(256);
|
||||
nodeHashes.reserve(256);
|
||||
mLedger->peekAccountStateMap()->getMissingNodes(nodeIDs, nodeHashes, 256, NULL);
|
||||
if (nodeIDs.empty())
|
||||
{
|
||||
if (!mLedger->peekAccountStateMap()->isValid()) mFailed = true;
|
||||
if (!mLedger->peekAccountStateMap()->isValid())
|
||||
mFailed = true;
|
||||
else
|
||||
{
|
||||
mHaveState = true;
|
||||
@@ -296,24 +442,28 @@ void LedgerAcquire::trigger(Peer::ref peer, bool timer)
|
||||
}
|
||||
else
|
||||
{
|
||||
tmGL.set_itype(ripple::liAS_NODE);
|
||||
BOOST_FOREACH(SHAMapNode& it, nodeIDs)
|
||||
*(tmGL.add_nodeids()) = it.getRawString();
|
||||
cLog(lsTRACE) << "Sending AS node " << nodeIDs.size()
|
||||
<< " request to " << (peer ? "selected peer" : "all peers");
|
||||
tLog(nodeIDs.size() == 1, lsTRACE) << "AS node: " << nodeIDs[0];
|
||||
sendRequest(tmGL, peer);
|
||||
if (!mAggressive)
|
||||
filterNodes(nodeIDs, nodeHashes, mRecentASNodes, 128, !isProgress());
|
||||
if (!nodeIDs.empty())
|
||||
{
|
||||
tmGL.set_itype(ripple::liAS_NODE);
|
||||
BOOST_FOREACH(SHAMapNode& it, nodeIDs)
|
||||
*(tmGL.add_nodeids()) = it.getRawString();
|
||||
cLog(lsTRACE) << "Sending AS node " << nodeIDs.size()
|
||||
<< " request to " << (peer ? "selected peer" : "all peers");
|
||||
tLog(nodeIDs.size() == 1, lsTRACE) << "AS node: " << nodeIDs[0];
|
||||
sendRequest(tmGL, peer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mComplete || mFailed)
|
||||
{
|
||||
cLog(lsDEBUG) << "Done:" << (mComplete ? " complete" : "") << (mFailed ? " failed" : "");
|
||||
cLog(lsDEBUG) << "Done:" << (mComplete ? " complete" : "") << (mFailed ? " failed " : " ")
|
||||
<< mLedger->getLedgerSeq();
|
||||
done();
|
||||
}
|
||||
else if (timer)
|
||||
resetTimer();
|
||||
}
|
||||
|
||||
void PeerSet::sendRequest(const ripple::TMGetLedger& tmGL, Peer::ref peer)
|
||||
@@ -361,14 +511,67 @@ int PeerSet::getPeerCount() const
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool LedgerAcquire::takeBase(const std::string& data)
|
||||
void LedgerAcquire::filterNodes(std::vector<SHAMapNode>& nodeIDs, std::vector<uint256>& nodeHashes,
|
||||
std::set<SHAMapNode>& recentNodes, int max, bool aggressive)
|
||||
{ // ask for new nodes in preference to ones we've already asked for
|
||||
std::vector<bool> duplicates;
|
||||
duplicates.reserve(nodeIDs.size());
|
||||
|
||||
int dupCount=0;
|
||||
|
||||
for (unsigned int i = 0; i < nodeIDs.size(); ++i)
|
||||
{
|
||||
bool isDup = recentNodes.count(nodeIDs[i]) != 0;
|
||||
duplicates.push_back(isDup);
|
||||
if (isDup)
|
||||
++dupCount;
|
||||
}
|
||||
|
||||
if (dupCount == nodeIDs.size())
|
||||
{ // all duplicates
|
||||
if (!aggressive)
|
||||
{
|
||||
nodeIDs.clear();
|
||||
nodeHashes.clear();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (dupCount > 0)
|
||||
{ // some, but not all, duplicates
|
||||
int insertPoint = 0;
|
||||
for (unsigned int i = 0; i < nodeIDs.size(); ++i)
|
||||
if (!duplicates[i])
|
||||
{ // Keep this node
|
||||
if (insertPoint != i)
|
||||
{
|
||||
nodeIDs[insertPoint] = nodeIDs[i];
|
||||
nodeHashes[insertPoint] = nodeHashes[i];
|
||||
}
|
||||
++insertPoint;
|
||||
}
|
||||
cLog(lsDEBUG) << "filterNodes " << nodeIDs.size() << " to " << insertPoint;
|
||||
nodeIDs.resize(insertPoint);
|
||||
nodeHashes.resize(insertPoint);
|
||||
}
|
||||
|
||||
if (nodeIDs.size() > max)
|
||||
{
|
||||
nodeIDs.resize(max);
|
||||
nodeHashes.resize(max);
|
||||
}
|
||||
|
||||
BOOST_FOREACH(const SHAMapNode& n, nodeIDs)
|
||||
recentNodes.insert(n);
|
||||
}
|
||||
|
||||
bool LedgerAcquire::takeBase(const std::string& data) // data must not have hash prefix
|
||||
{ // Return value: true=normal, false=bad data
|
||||
#ifdef LA_DEBUG
|
||||
cLog(lsTRACE) << "got base acquiring ledger " << mHash;
|
||||
#endif
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
if (mHaveBase) return true;
|
||||
mLedger = boost::make_shared<Ledger>(data);
|
||||
mLedger = boost::make_shared<Ledger>(data, false);
|
||||
if (mLedger->getHash() != mHash)
|
||||
{
|
||||
cLog(lsWARNING) << "Acquire hash mismatch";
|
||||
@@ -398,10 +601,12 @@ bool LedgerAcquire::takeBase(const std::string& data)
|
||||
bool LedgerAcquire::takeTxNode(const std::list<SHAMapNode>& nodeIDs,
|
||||
const std::list< std::vector<unsigned char> >& data, SMAddNode& san)
|
||||
{
|
||||
if (!mHaveBase) return false;
|
||||
if (!mHaveBase)
|
||||
return false;
|
||||
|
||||
std::list<SHAMapNode>::const_iterator nodeIDit = nodeIDs.begin();
|
||||
std::list< std::vector<unsigned char> >::const_iterator nodeDatait = data.begin();
|
||||
TransactionStateSF tFilter(mLedger->getHash(), mLedger->getLedgerSeq());
|
||||
TransactionStateSF tFilter(mLedger->getLedgerSeq());
|
||||
while (nodeIDit != nodeIDs.end())
|
||||
{
|
||||
if (nodeIDit->isRoot())
|
||||
@@ -445,7 +650,7 @@ bool LedgerAcquire::takeAsNode(const std::list<SHAMapNode>& nodeIDs,
|
||||
|
||||
std::list<SHAMapNode>::const_iterator nodeIDit = nodeIDs.begin();
|
||||
std::list< std::vector<unsigned char> >::const_iterator nodeDatait = data.begin();
|
||||
AccountStateSF tFilter(mLedger->getHash(), mLedger->getLedgerSeq());
|
||||
AccountStateSF tFilter(mLedger->getLedgerSeq());
|
||||
while (nodeIDit != nodeIDs.end())
|
||||
{
|
||||
if (nodeIDit->isRoot())
|
||||
@@ -482,7 +687,7 @@ bool LedgerAcquire::takeAsRootNode(const std::vector<unsigned char>& data, SMAdd
|
||||
{
|
||||
if (!mHaveBase)
|
||||
return false;
|
||||
AccountStateSF tFilter(mLedger->getHash(), mLedger->getLedgerSeq());
|
||||
AccountStateSF tFilter(mLedger->getLedgerSeq());
|
||||
return san.combine(
|
||||
mLedger->peekAccountStateMap()->addRootNode(mLedger->getAccountHash(), data, snfWIRE, &tFilter));
|
||||
}
|
||||
@@ -491,7 +696,7 @@ bool LedgerAcquire::takeTxRootNode(const std::vector<unsigned char>& data, SMAdd
|
||||
{
|
||||
if (!mHaveBase)
|
||||
return false;
|
||||
TransactionStateSF tFilter(mLedger->getHash(), mLedger->getLedgerSeq());
|
||||
TransactionStateSF tFilter(mLedger->getLedgerSeq());
|
||||
return san.combine(
|
||||
mLedger->peekTransactionMap()->addRootNode(mLedger->getTransHash(), data, snfWIRE, &tFilter));
|
||||
}
|
||||
@@ -502,11 +707,18 @@ LedgerAcquire::pointer LedgerAcquireMaster::findCreate(const uint256& hash)
|
||||
boost::mutex::scoped_lock sl(mLock);
|
||||
LedgerAcquire::pointer& ptr = mLedgers[hash];
|
||||
if (ptr)
|
||||
{
|
||||
ptr->touch();
|
||||
return ptr;
|
||||
}
|
||||
ptr = boost::make_shared<LedgerAcquire>(hash);
|
||||
assert(mLedgers[hash] == ptr);
|
||||
ptr->addPeers();
|
||||
ptr->resetTimer(); // Cannot call in constructor
|
||||
if (!ptr->isDone())
|
||||
{
|
||||
ptr->addPeers();
|
||||
ptr->setTimer(); // Cannot call in constructor
|
||||
}
|
||||
else
|
||||
cLog(lsDEBUG) << "LedgerAcquireMaster acquiring ledger we already have";
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@@ -516,10 +728,69 @@ LedgerAcquire::pointer LedgerAcquireMaster::find(const uint256& hash)
|
||||
boost::mutex::scoped_lock sl(mLock);
|
||||
std::map<uint256, LedgerAcquire::pointer>::iterator it = mLedgers.find(hash);
|
||||
if (it != mLedgers.end())
|
||||
{
|
||||
it->second->touch();
|
||||
return it->second;
|
||||
}
|
||||
return LedgerAcquire::pointer();
|
||||
}
|
||||
|
||||
std::vector<LedgerAcquire::neededHash_t> LedgerAcquire::getNeededHashes()
|
||||
{
|
||||
std::vector<neededHash_t> ret;
|
||||
if (!mHaveBase)
|
||||
{
|
||||
ret.push_back(std::make_pair(ripple::TMGetObjectByHash::otLEDGER, mHash));
|
||||
return ret;
|
||||
}
|
||||
if (!mHaveState)
|
||||
{
|
||||
std::vector<uint256> v = mLedger->peekAccountStateMap()->getNeededHashes(16);
|
||||
BOOST_FOREACH(const uint256& h, v)
|
||||
ret.push_back(std::make_pair(ripple::TMGetObjectByHash::otSTATE_NODE, h));
|
||||
}
|
||||
if (!mHaveTransactions)
|
||||
{
|
||||
std::vector<uint256> v = mLedger->peekTransactionMap()->getNeededHashes(16);
|
||||
BOOST_FOREACH(const uint256& h, v)
|
||||
ret.push_back(std::make_pair(ripple::TMGetObjectByHash::otTRANSACTION_NODE, h));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Json::Value LedgerAcquire::getJson(int)
|
||||
{
|
||||
Json::Value ret(Json::objectValue);
|
||||
ret["hash"] = mHash.GetHex();
|
||||
if (mComplete)
|
||||
ret["complete"] = true;
|
||||
if (mFailed)
|
||||
ret["failed"] = true;
|
||||
ret["have_base"] = mHaveBase;
|
||||
ret["have_state"] = mHaveState;
|
||||
ret["have_transactions"] = mHaveTransactions;
|
||||
if (mAborted)
|
||||
ret["aborted"] = true;
|
||||
ret["timeouts"] = getTimeouts();
|
||||
if (mHaveBase && !mHaveState)
|
||||
{
|
||||
Json::Value hv(Json::arrayValue);
|
||||
std::vector<uint256> v = mLedger->peekAccountStateMap()->getNeededHashes(16);
|
||||
BOOST_FOREACH(const uint256& h, v)
|
||||
hv.append(h.GetHex());
|
||||
ret["needed_state_hashes"] = hv;
|
||||
}
|
||||
if (mHaveBase && !mHaveTransactions)
|
||||
{
|
||||
Json::Value hv(Json::arrayValue);
|
||||
std::vector<uint256> v = mLedger->peekTransactionMap()->getNeededHashes(16);
|
||||
BOOST_FOREACH(const uint256& h, v)
|
||||
hv.append(h.GetHex());
|
||||
ret["needed_transaction_hashes"] = hv;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool LedgerAcquireMaster::hasLedger(const uint256& hash)
|
||||
{
|
||||
assert(hash.isNonZero());
|
||||
@@ -573,7 +844,8 @@ SMAddNode LedgerAcquireMaster::gotLedgerData(ripple::TMLedgerData& packet, Peer:
|
||||
{
|
||||
cLog(lsWARNING) << "Included TXbase invalid";
|
||||
}
|
||||
ledger->trigger(peer, false);
|
||||
if (!san.isInvalid())
|
||||
ledger->trigger(peer);
|
||||
return san;
|
||||
}
|
||||
|
||||
@@ -605,7 +877,7 @@ SMAddNode LedgerAcquireMaster::gotLedgerData(ripple::TMLedgerData& packet, Peer:
|
||||
else
|
||||
ledger->takeAsNode(nodeIDs, nodeData, ret);
|
||||
if (!ret.isInvalid())
|
||||
ledger->trigger(peer, false);
|
||||
ledger->trigger(peer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -613,4 +885,39 @@ SMAddNode LedgerAcquireMaster::gotLedgerData(ripple::TMLedgerData& packet, Peer:
|
||||
return SMAddNode::invalid();
|
||||
}
|
||||
|
||||
void LedgerAcquireMaster::sweep()
|
||||
{
|
||||
mRecentFailures.sweep();
|
||||
|
||||
time_t now = time(NULL);
|
||||
boost::mutex::scoped_lock sl(mLock);
|
||||
|
||||
std::map<uint256, LedgerAcquire::pointer>::iterator it = mLedgers.begin();
|
||||
while (it != mLedgers.end())
|
||||
{
|
||||
if (it->second->getLastAction() > now)
|
||||
{
|
||||
it->second->touch();
|
||||
++it;
|
||||
}
|
||||
else if ((it->second->getLastAction() + 60) < now)
|
||||
mLedgers.erase(it++);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
int LedgerAcquireMaster::getFetchCount()
|
||||
{
|
||||
int ret = 0;
|
||||
{
|
||||
typedef std::pair<uint256, LedgerAcquire::pointer> u256_acq_pair;
|
||||
boost::mutex::scoped_lock sl(mLock);
|
||||
BOOST_FOREACH(const u256_acq_pair& it, mLedgers)
|
||||
if (it.second->isActive())
|
||||
++ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <list>
|
||||
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
@@ -18,14 +19,20 @@
|
||||
#include "InstanceCounter.h"
|
||||
#include "ripple.pb.h"
|
||||
|
||||
DEFINE_INSTANCE(PeerSet);
|
||||
// How long before we try again to acquire the same ledger
|
||||
#ifndef LEDGER_REACQUIRE_INTERVAL
|
||||
#define LEDGER_REACQUIRE_INTERVAL 600
|
||||
#endif
|
||||
|
||||
class PeerSet : private IS_INSTANCE(PeerSet)
|
||||
DEFINE_INSTANCE(LedgerAcquire);
|
||||
|
||||
class PeerSet
|
||||
{
|
||||
protected:
|
||||
uint256 mHash;
|
||||
int mTimerInterval, mTimeouts;
|
||||
bool mComplete, mFailed, mProgress;
|
||||
bool mComplete, mFailed, mProgress, mAggressive;
|
||||
time_t mLastAction;
|
||||
|
||||
boost::recursive_mutex mLock;
|
||||
boost::asio::deadline_timer mTimer;
|
||||
@@ -43,14 +50,19 @@ public:
|
||||
bool isFailed() const { return mFailed; }
|
||||
int getTimeouts() const { return mTimeouts; }
|
||||
|
||||
void progress() { mProgress = true; }
|
||||
bool isActive();
|
||||
void progress() { mProgress = true; mAggressive = false; }
|
||||
bool isProgress() { return mProgress; }
|
||||
void touch() { mLastAction = time(NULL); }
|
||||
time_t getLastAction() { return mLastAction; }
|
||||
|
||||
void peerHas(Peer::ref);
|
||||
void badPeer(Peer::ref);
|
||||
void resetTimer();
|
||||
void setTimer();
|
||||
|
||||
int takePeerSetFrom(const PeerSet& s);
|
||||
int getPeerCount() const;
|
||||
virtual bool isDone() const { return mComplete || mFailed; }
|
||||
|
||||
protected:
|
||||
virtual void newPeer(Peer::ref) = 0;
|
||||
@@ -65,21 +77,25 @@ private:
|
||||
static void TimerEntry(boost::weak_ptr<PeerSet>, const boost::system::error_code& result);
|
||||
};
|
||||
|
||||
class LedgerAcquire : public PeerSet, public boost::enable_shared_from_this<LedgerAcquire>
|
||||
class LedgerAcquire :
|
||||
private IS_INSTANCE(LedgerAcquire), public PeerSet, public boost::enable_shared_from_this<LedgerAcquire>
|
||||
{ // A ledger we are trying to acquire
|
||||
public:
|
||||
typedef boost::shared_ptr<LedgerAcquire> pointer;
|
||||
|
||||
protected:
|
||||
Ledger::pointer mLedger;
|
||||
bool mHaveBase, mHaveState, mHaveTransactions, mAborted, mSignaled, mAccept;
|
||||
bool mHaveBase, mHaveState, mHaveTransactions, mAborted, mSignaled, mAccept, mByHash;
|
||||
|
||||
std::set<SHAMapNode> mRecentTXNodes;
|
||||
std::set<SHAMapNode> mRecentASNodes;
|
||||
|
||||
std::vector< boost::function<void (LedgerAcquire::pointer)> > mOnComplete;
|
||||
|
||||
void done();
|
||||
void onTimer(bool progress);
|
||||
|
||||
void newPeer(Peer::ref peer) { trigger(peer, false); }
|
||||
void newPeer(Peer::ref peer) { trigger(peer); }
|
||||
|
||||
boost::weak_ptr<PeerSet> pmDowncast();
|
||||
|
||||
@@ -90,11 +106,12 @@ public:
|
||||
bool isBase() const { return mHaveBase; }
|
||||
bool isAcctStComplete() const { return mHaveState; }
|
||||
bool isTransComplete() const { return mHaveTransactions; }
|
||||
Ledger::pointer getLedger() { return mLedger; }
|
||||
bool isDone() const { return mAborted || isComplete() || isFailed(); }
|
||||
Ledger::ref getLedger() { return mLedger; }
|
||||
void abort() { mAborted = true; }
|
||||
bool setAccept() { if (mAccept) return false; mAccept = true; return true; }
|
||||
|
||||
void addOnComplete(boost::function<void (LedgerAcquire::pointer)>);
|
||||
bool addOnComplete(boost::function<void (LedgerAcquire::pointer)>);
|
||||
|
||||
bool takeBase(const std::string& data);
|
||||
bool takeTxNode(const std::list<SHAMapNode>& IDs, const std::list<std::vector<unsigned char> >& data,
|
||||
@@ -103,9 +120,17 @@ public:
|
||||
bool takeAsNode(const std::list<SHAMapNode>& IDs, const std::list<std::vector<unsigned char> >& data,
|
||||
SMAddNode&);
|
||||
bool takeAsRootNode(const std::vector<unsigned char>& data, SMAddNode&);
|
||||
void trigger(Peer::ref, bool timer);
|
||||
void trigger(Peer::ref);
|
||||
bool tryLocal();
|
||||
void addPeers();
|
||||
|
||||
typedef std::pair<ripple::TMGetObjectByHash::ObjectType, uint256> neededHash_t;
|
||||
std::vector<neededHash_t> getNeededHashes();
|
||||
|
||||
static void filterNodes(std::vector<SHAMapNode>& nodeIDs, std::vector<uint256>& nodeHashes,
|
||||
std::set<SHAMapNode>& recentNodes, int max, bool aggressive);
|
||||
|
||||
Json::Value getJson(int);
|
||||
};
|
||||
|
||||
class LedgerAcquireMaster
|
||||
@@ -113,15 +138,22 @@ class LedgerAcquireMaster
|
||||
protected:
|
||||
boost::mutex mLock;
|
||||
std::map<uint256, LedgerAcquire::pointer> mLedgers;
|
||||
KeyCache<uint256> mRecentFailures;
|
||||
|
||||
public:
|
||||
LedgerAcquireMaster() { ; }
|
||||
LedgerAcquireMaster() : mRecentFailures("LedgerAcquireRecentFailures", 0, LEDGER_REACQUIRE_INTERVAL) { ; }
|
||||
|
||||
LedgerAcquire::pointer findCreate(const uint256& hash);
|
||||
LedgerAcquire::pointer find(const uint256& hash);
|
||||
bool hasLedger(const uint256& ledgerHash);
|
||||
void dropLedger(const uint256& ledgerHash);
|
||||
SMAddNode gotLedgerData(ripple::TMLedgerData& packet, Peer::ref);
|
||||
|
||||
int getFetchCount();
|
||||
void logFailure(const uint256& h) { mRecentFailures.add(h); }
|
||||
bool isFailure(const uint256& h) { return mRecentFailures.isPresent(h, false); }
|
||||
|
||||
void sweep();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -16,15 +16,19 @@
|
||||
|
||||
#define TX_ACQUIRE_TIMEOUT 250
|
||||
|
||||
#define LEDGER_TOTAL_PASSES 8
|
||||
#define LEDGER_RETRY_PASSES 5
|
||||
|
||||
#define TRUST_NETWORK
|
||||
|
||||
#define LC_DEBUG
|
||||
|
||||
typedef std::pair<const uint160, LedgerProposal::pointer> u160_prop_pair;
|
||||
typedef std::pair<const uint256, LCTransaction::pointer> u256_lct_pair;
|
||||
typedef std::map<uint160, LedgerProposal::pointer>::value_type u160_prop_pair;
|
||||
typedef std::map<uint256, LCTransaction::pointer>::value_type u256_lct_pair;
|
||||
|
||||
SETUP_LOG();
|
||||
DECLARE_INSTANCE(LedgerConsensus);
|
||||
DECLARE_INSTANCE(TransactionAcquire);
|
||||
|
||||
TransactionAcquire::TransactionAcquire(const uint256& hash) : PeerSet(hash, TX_ACQUIRE_TIMEOUT), mHaveRoot(false)
|
||||
{
|
||||
@@ -44,6 +48,7 @@ void TransactionAcquire::done()
|
||||
mMap->setImmutable();
|
||||
theApp->getOPs().mapComplete(mHash, mMap);
|
||||
}
|
||||
theApp->getMasterLedgerAcquire().dropLedger(mHash);
|
||||
}
|
||||
|
||||
void TransactionAcquire::onTimer(bool progress)
|
||||
@@ -69,7 +74,7 @@ void TransactionAcquire::onTimer(bool progress)
|
||||
}
|
||||
}
|
||||
else if (!progress)
|
||||
trigger(Peer::pointer(), true);
|
||||
trigger(Peer::pointer());
|
||||
}
|
||||
|
||||
boost::weak_ptr<PeerSet> TransactionAcquire::pmDowncast()
|
||||
@@ -77,7 +82,7 @@ boost::weak_ptr<PeerSet> TransactionAcquire::pmDowncast()
|
||||
return boost::shared_polymorphic_downcast<PeerSet>(shared_from_this());
|
||||
}
|
||||
|
||||
void TransactionAcquire::trigger(Peer::ref peer, bool timer)
|
||||
void TransactionAcquire::trigger(Peer::ref peer)
|
||||
{
|
||||
if (mComplete || mFailed)
|
||||
{
|
||||
@@ -110,20 +115,15 @@ void TransactionAcquire::trigger(Peer::ref peer, bool timer)
|
||||
done();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
ripple::TMGetLedger tmGL;
|
||||
tmGL.set_ledgerhash(mHash.begin(), mHash.size());
|
||||
tmGL.set_itype(ripple::liTS_CANDIDATE);
|
||||
if (getTimeouts() != 0)
|
||||
tmGL.set_querytype(ripple::qtINDIRECT);
|
||||
BOOST_FOREACH(SHAMapNode& it, nodeIDs)
|
||||
*(tmGL.add_nodeids()) = it.getRawString();
|
||||
sendRequest(tmGL, peer);
|
||||
}
|
||||
ripple::TMGetLedger tmGL;
|
||||
tmGL.set_ledgerhash(mHash.begin(), mHash.size());
|
||||
tmGL.set_itype(ripple::liTS_CANDIDATE);
|
||||
if (getTimeouts() != 0)
|
||||
tmGL.set_querytype(ripple::qtINDIRECT);
|
||||
BOOST_FOREACH(SHAMapNode& it, nodeIDs)
|
||||
*(tmGL.add_nodeids()) = it.getRawString();
|
||||
sendRequest(tmGL, peer);
|
||||
}
|
||||
if (timer)
|
||||
resetTimer();
|
||||
}
|
||||
|
||||
SMAddNode TransactionAcquire::takeNodes(const std::list<SHAMapNode>& nodeIDs,
|
||||
@@ -171,7 +171,7 @@ SMAddNode TransactionAcquire::takeNodes(const std::list<SHAMapNode>& nodeIDs,
|
||||
++nodeIDit;
|
||||
++nodeDatait;
|
||||
}
|
||||
trigger(peer, false);
|
||||
trigger(peer);
|
||||
progress();
|
||||
return SMAddNode::useful();
|
||||
}
|
||||
@@ -282,7 +282,7 @@ LedgerConsensus::LedgerConsensus(const uint256& prevLCLHash, Ledger::ref previou
|
||||
mCloseResolution = ContinuousLedgerTiming::getNextLedgerTimeResolution(
|
||||
mPreviousLedger->getCloseResolution(), mPreviousLedger->getCloseAgree(), previousLedger->getLedgerSeq() + 1);
|
||||
|
||||
if (mValPublic.isValid() && mValPrivate.isValid())
|
||||
if (mValPublic.isValid() && mValPrivate.isValid() && !theApp->getOPs().isNeedNetworkLedger())
|
||||
{
|
||||
cLog(lsINFO) << "Entering consensus process, validating";
|
||||
mValidating = true;
|
||||
@@ -297,6 +297,7 @@ LedgerConsensus::LedgerConsensus(const uint256& prevLCLHash, Ledger::ref previou
|
||||
mHaveCorrectLCL = (mPreviousLedger->getHash() == mPrevLedgerHash);
|
||||
if (!mHaveCorrectLCL)
|
||||
{
|
||||
theApp->getOPs().setProposing(false, false);
|
||||
handleLCL(mPrevLedgerHash);
|
||||
if (!mHaveCorrectLCL)
|
||||
{
|
||||
@@ -305,11 +306,13 @@ LedgerConsensus::LedgerConsensus(const uint256& prevLCLHash, Ledger::ref previou
|
||||
cLog(lsINFO) << "Correct LCL is: " << prevLCLHash;
|
||||
}
|
||||
}
|
||||
else
|
||||
theApp->getOPs().setProposing(mProposing, mValidating);
|
||||
}
|
||||
|
||||
void LedgerConsensus::checkOurValidation()
|
||||
{ // This only covers some cases - Fix for the case where we can't ever acquire the consensus ledger
|
||||
if (!mHaveCorrectLCL || !mValPublic.isValid() || !mValPrivate.isValid())
|
||||
if (!mHaveCorrectLCL || !mValPublic.isValid() || !mValPrivate.isValid() || theApp->getOPs().isNeedNetworkLedger())
|
||||
return;
|
||||
|
||||
SerializedValidation::pointer lastVal = theApp->getOPs().getLastValidation();
|
||||
@@ -348,7 +351,7 @@ void LedgerConsensus::checkLCL()
|
||||
boost::unordered_map<uint256, currentValidationCount> vals =
|
||||
theApp->getValidations().getCurrentValidations(favoredLedger);
|
||||
|
||||
typedef std::pair<const uint256, currentValidationCount> u256_cvc_pair;
|
||||
typedef std::map<uint256, currentValidationCount>::value_type u256_cvc_pair;
|
||||
BOOST_FOREACH(u256_cvc_pair& it, vals)
|
||||
if (it.second.first > netLgrCount)
|
||||
{
|
||||
@@ -424,8 +427,10 @@ void LedgerConsensus::handleLCL(const uint256& lclHash)
|
||||
|
||||
cLog(lsINFO) << "Have the consensus ledger " << mPrevLedgerHash;
|
||||
mHaveCorrectLCL = true;
|
||||
mAcquiringLedger.reset();
|
||||
theApp->getOPs().clearNeedNetworkLedger();
|
||||
#if 0 // FIXME: can trigger early
|
||||
if (mAcquiringLedger && mAcquiringLedger->isComplete())
|
||||
theApp->getOPs().clearNeedNetworkLedger();
|
||||
#endif
|
||||
mCloseResolution = ContinuousLedgerTiming::getNextLedgerTimeResolution(
|
||||
mPreviousLedger->getCloseResolution(), mPreviousLedger->getCloseAgree(),
|
||||
mPreviousLedger->getLedgerSeq() + 1);
|
||||
@@ -471,7 +476,7 @@ void LedgerConsensus::createDisputes(SHAMap::ref m1, SHAMap::ref m2)
|
||||
SHAMap::SHAMapDiff differences;
|
||||
m1->compare(m2, differences, 16384);
|
||||
|
||||
typedef std::pair<const uint256, SHAMap::SHAMapDiffItem> u256_diff_pair;
|
||||
typedef std::map<uint256, SHAMap::SHAMapDiffItem>::value_type u256_diff_pair;
|
||||
BOOST_FOREACH (u256_diff_pair& pos, differences)
|
||||
{ // create disputed transactions (from the ledger that has them)
|
||||
if (pos.second.first)
|
||||
@@ -570,7 +575,7 @@ void LedgerConsensus::statusChange(ripple::NodeEvent event, Ledger& ledger)
|
||||
s.set_ledgerhash(hash.begin(), hash.size());
|
||||
PackedMessage::pointer packet = boost::make_shared<PackedMessage>(s, ripple::mtSTATUS_CHANGE);
|
||||
theApp->getConnectionPool().relayMessage(NULL, packet);
|
||||
cLog(lsINFO) << "send status change to peer";
|
||||
cLog(lsTRACE) << "send status change to peer";
|
||||
}
|
||||
|
||||
int LedgerConsensus::startup()
|
||||
@@ -757,10 +762,10 @@ void LedgerConsensus::updateOurPositions()
|
||||
|
||||
for (std::map<uint32, int>::iterator it = closeTimes.begin(), end = closeTimes.end(); it != end; ++it)
|
||||
{
|
||||
cLog(lsINFO) << "CCTime: " << it->first << " has " << it->second << ", " << thresh << " required";
|
||||
cLog(lsTRACE) << "CCTime: " << it->first << " has " << it->second << ", " << thresh << " required";
|
||||
if (it->second >= thresh)
|
||||
{
|
||||
cLog(lsINFO) << "Close time consensus reached: " << it->first;
|
||||
cLog(lsDEBUG) << "Close time consensus reached: " << it->first;
|
||||
mHaveCloseTimeConsensus = true;
|
||||
closeTime = it->first;
|
||||
thresh = it->second;
|
||||
@@ -793,8 +798,7 @@ void LedgerConsensus::updateOurPositions()
|
||||
}
|
||||
|
||||
bool LedgerConsensus::haveConsensus(bool forReal)
|
||||
{ // FIXME: Should check for a supermajority on each disputed transaction
|
||||
// counting unacquired TX sets as disagreeing
|
||||
{ // CHECKME: should possibly count unacquired TX sets as disagreeing
|
||||
int agree = 0, disagree = 0;
|
||||
uint256 ourPosition = mOurPosition->getCurrentHash();
|
||||
BOOST_FOREACH(u160_prop_pair& it, mPeerPositions)
|
||||
@@ -826,7 +830,6 @@ SHAMap::pointer LedgerConsensus::getTransactionTree(const uint256& hash, bool do
|
||||
SHAMap::pointer currentMap = theApp->getLedgerMaster().getCurrentLedger()->peekTransactionMap();
|
||||
if (currentMap->getHash() == hash)
|
||||
{
|
||||
cLog(lsINFO) << "node proposes our open transaction set";
|
||||
currentMap = currentMap->snapShot(false);
|
||||
mapComplete(hash, currentMap, false);
|
||||
return currentMap;
|
||||
@@ -882,7 +885,7 @@ void LedgerConsensus::startAcquiring(const TransactionAcquire::pointer& acquire)
|
||||
acquire->peerHas(peer);
|
||||
}
|
||||
|
||||
acquire->resetTimer();
|
||||
acquire->setTimer();
|
||||
}
|
||||
|
||||
void LedgerConsensus::propose()
|
||||
@@ -976,7 +979,7 @@ bool LedgerConsensus::peerPosition(const LedgerProposal::pointer& newPosition)
|
||||
}
|
||||
|
||||
|
||||
cLog(lsINFO) << "Processing peer proposal " << newPosition->getProposeSeq() << "/" << newPosition->getCurrentHash();
|
||||
cLog(lsTRACE) << "Processing peer proposal " << newPosition->getProposeSeq() << "/" << newPosition->getCurrentHash();
|
||||
currentPosition = newPosition;
|
||||
|
||||
SHAMap::pointer set = getTransactionTree(newPosition->getCurrentHash(), true);
|
||||
@@ -1088,37 +1091,51 @@ void LedgerConsensus::playbackProposals()
|
||||
}
|
||||
}
|
||||
|
||||
void LedgerConsensus::applyTransaction(TransactionEngine& engine, SerializedTransaction::ref txn,
|
||||
Ledger::ref ledger, CanonicalTXSet& failedTransactions, bool openLedger)
|
||||
{
|
||||
#define LCAT_SUCCESS 0
|
||||
#define LCAT_FAIL 1
|
||||
#define LCAT_RETRY 2
|
||||
|
||||
int LedgerConsensus::applyTransaction(TransactionEngine& engine, SerializedTransaction::ref txn, Ledger::ref ledger,
|
||||
bool openLedger, bool retryAssured)
|
||||
{ // Returns false if the transaction has need not be retried.
|
||||
TransactionEngineParams parms = openLedger ? tapOPEN_LEDGER : tapNONE;
|
||||
if (retryAssured)
|
||||
parms = static_cast<TransactionEngineParams>(parms | tapRETRY);
|
||||
|
||||
cLog(lsDEBUG) << "TXN " << txn->getTransactionID()
|
||||
<< (openLedger ? " open" : " closed")
|
||||
<< (retryAssured ? "/retry" : "/final");
|
||||
cLog(lsTRACE) << txn->getJson(0);
|
||||
|
||||
#ifndef TRUST_NETWORK
|
||||
try
|
||||
{
|
||||
#endif
|
||||
TER result = engine.applyTransaction(*txn, parms);
|
||||
if (isTerRetry(result))
|
||||
|
||||
bool didApply;
|
||||
TER result = engine.applyTransaction(*txn, parms, didApply);
|
||||
if (didApply)
|
||||
{
|
||||
cLog(lsINFO) << " retry";
|
||||
assert(!ledger->hasTransaction(txn->getTransactionID()));
|
||||
failedTransactions.push_back(txn);
|
||||
cLog(lsDEBUG) << "Transaction success: " << transHuman(result);
|
||||
return LCAT_SUCCESS;
|
||||
}
|
||||
else if (isTepSuccess(result)) // FIXME: Need to do partial success
|
||||
{
|
||||
cLog(lsTRACE) << " success";
|
||||
assert(ledger->hasTransaction(txn->getTransactionID()));
|
||||
|
||||
if (isTefFailure(result) || isTemMalformed(result))
|
||||
{ // failure
|
||||
cLog(lsDEBUG) << "Transaction failure: " << transHuman(result);
|
||||
return LCAT_FAIL;
|
||||
}
|
||||
else if (isTemMalformed(result) || isTefFailure(result))
|
||||
{
|
||||
cLog(lsINFO) << " hard fail";
|
||||
}
|
||||
else
|
||||
assert(false);
|
||||
|
||||
cLog(lsDEBUG) << "Transaction retry: " << transHuman(result);
|
||||
assert(!ledger->hasTransaction(txn->getTransactionID()));
|
||||
return LCAT_RETRY;
|
||||
|
||||
#ifndef TRUST_NETWORK
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
cLog(lsWARNING) << " Throws";
|
||||
cLog(lsWARNING) << "Throws";
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1126,11 +1143,9 @@ void LedgerConsensus::applyTransaction(TransactionEngine& engine, SerializedTran
|
||||
void LedgerConsensus::applyTransactions(SHAMap::ref set, Ledger::ref applyLedger,
|
||||
Ledger::ref checkLedger, CanonicalTXSet& failedTransactions, bool openLgr)
|
||||
{
|
||||
TransactionEngineParams parms = openLgr ? tapOPEN_LEDGER : tapNONE;
|
||||
TransactionEngine engine(applyLedger);
|
||||
|
||||
for (SHAMapItem::pointer item = set->peekFirstItem(); !!item; item = set->peekNextItem(item->getTag()))
|
||||
{
|
||||
if (!checkLedger->hasTransaction(item->getTag()))
|
||||
{
|
||||
cLog(lsINFO) << "Processing candidate transaction: " << item->getTag();
|
||||
@@ -1140,7 +1155,8 @@ void LedgerConsensus::applyTransactions(SHAMap::ref set, Ledger::ref applyLedger
|
||||
#endif
|
||||
SerializerIterator sit(item->peekSerializer());
|
||||
SerializedTransaction::pointer txn = boost::make_shared<SerializedTransaction>(boost::ref(sit));
|
||||
applyTransaction(engine, txn, applyLedger, failedTransactions, openLgr);
|
||||
if (applyTransaction(engine, txn, applyLedger, openLgr, true) == LCAT_RETRY)
|
||||
failedTransactions.push_back(txn);
|
||||
#ifndef TRUST_NETWORK
|
||||
}
|
||||
catch (...)
|
||||
@@ -1149,35 +1165,52 @@ void LedgerConsensus::applyTransactions(SHAMap::ref set, Ledger::ref applyLedger
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
int successes;
|
||||
do
|
||||
int changes;
|
||||
bool certainRetry = true;
|
||||
|
||||
for (int pass = 0; pass < LEDGER_TOTAL_PASSES; ++pass)
|
||||
{
|
||||
successes = 0;
|
||||
cLog(lsDEBUG) << "Pass: " << pass << " Txns: " << failedTransactions.size()
|
||||
<< (certainRetry ? " retriable" : " final");
|
||||
changes = 0;
|
||||
|
||||
CanonicalTXSet::iterator it = failedTransactions.begin();
|
||||
while (it != failedTransactions.end())
|
||||
{
|
||||
try
|
||||
{
|
||||
TER result = engine.applyTransaction(*it->second, parms);
|
||||
if (result <= 0)
|
||||
switch (applyTransaction(engine, it->second, applyLedger, openLgr, certainRetry))
|
||||
{
|
||||
if (result == 0) ++successes;
|
||||
it = failedTransactions.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
case LCAT_SUCCESS:
|
||||
it = failedTransactions.erase(it);
|
||||
++changes;
|
||||
break;
|
||||
|
||||
case LCAT_FAIL:
|
||||
it = failedTransactions.erase(it);
|
||||
break;
|
||||
|
||||
case LCAT_RETRY:
|
||||
++it;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
cLog(lsWARNING) << " Throws";
|
||||
cLog(lsWARNING) << "Transaction throws";
|
||||
it = failedTransactions.erase(it);
|
||||
}
|
||||
}
|
||||
} while (successes > 0);
|
||||
cLog(lsDEBUG) << "Pass: " << pass << " finished " << changes << " changes";
|
||||
|
||||
// A non-retry pass made no changes
|
||||
if (!changes && !certainRetry)
|
||||
return;
|
||||
|
||||
// Stop retriable passes
|
||||
if ((!changes) || (pass >= LEDGER_RETRY_PASSES))
|
||||
certainRetry = false;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 LedgerConsensus::roundCloseTime(uint32 closeTime)
|
||||
@@ -1187,24 +1220,32 @@ uint32 LedgerConsensus::roundCloseTime(uint32 closeTime)
|
||||
|
||||
void LedgerConsensus::accept(SHAMap::ref set, LoadEvent::pointer)
|
||||
{
|
||||
if (set->getHash().isNonZero()) // put our set where others can get it later
|
||||
theApp->getOPs().takePosition(mPreviousLedger->getLedgerSeq(), set);
|
||||
|
||||
boost::recursive_mutex::scoped_lock masterLock(theApp->getMasterLock());
|
||||
assert(set->getHash() == mOurPosition->getCurrentHash());
|
||||
|
||||
uint32 closeTime = roundCloseTime(mOurPosition->getCloseTime());
|
||||
|
||||
cLog(lsINFO) << "Computing new LCL based on network consensus";
|
||||
if (mHaveCorrectLCL)
|
||||
{
|
||||
cLog(lsINFO) << "CNF tx " << mOurPosition->getCurrentHash() << ", close " << closeTime;
|
||||
cLog(lsINFO) << "CNF mode " << theApp->getOPs().getOperatingMode() << ", oldLCL " << mPrevLedgerHash;
|
||||
bool closeTimeCorrect = true;
|
||||
if (closeTime == 0)
|
||||
{ // we agreed to disagree
|
||||
closeTimeCorrect = false;
|
||||
closeTime = mPreviousLedger->getCloseTimeNC() + 1;
|
||||
}
|
||||
|
||||
cLog(lsDEBUG) << "Report: Prop=" << (mProposing ? "yes" : "no") << " val=" << (mValidating ? "yes" : "no") <<
|
||||
" corLCL=" << (mHaveCorrectLCL ? "yes" : "no") << " fail="<< (mConsensusFail ? "yes" : "no");
|
||||
cLog(lsDEBUG) << "Report: Prev = " << mPrevLedgerHash << ":" << mPreviousLedger->getLedgerSeq();
|
||||
cLog(lsDEBUG) << "Report: TxSt = " << set->getHash() << ", close " << closeTime << (closeTimeCorrect ? "" : "X");
|
||||
|
||||
CanonicalTXSet failedTransactions(set->getHash());
|
||||
|
||||
Ledger::pointer newLCL = boost::make_shared<Ledger>(false, boost::ref(*mPreviousLedger));
|
||||
|
||||
newLCL->peekTransactionMap()->armDirty();
|
||||
newLCL->peekAccountStateMap()->armDirty();
|
||||
cLog(lsDEBUG) << "Applying consensus set transactions to the last closed ledger";
|
||||
applyTransactions(set, newLCL, newLCL, failedTransactions, false);
|
||||
newLCL->updateSkipList();
|
||||
newLCL->setClosed();
|
||||
@@ -1214,17 +1255,11 @@ void LedgerConsensus::accept(SHAMap::ref set, LoadEvent::pointer)
|
||||
// write out dirty nodes (temporarily done here) Most come before setAccepted
|
||||
int fc;
|
||||
while ((fc = SHAMap::flushDirty(*acctNodes, 256, hotACCOUNT_NODE, newLCL->getLedgerSeq())) > 0)
|
||||
{ cLog(lsINFO) << "Flushed " << fc << " dirty state nodes"; }
|
||||
{ cLog(lsTRACE) << "Flushed " << fc << " dirty state nodes"; }
|
||||
while ((fc = SHAMap::flushDirty(*txnNodes, 256, hotTRANSACTION_NODE, newLCL->getLedgerSeq())) > 0)
|
||||
{ cLog(lsINFO) << "Flushed " << fc << " dirty transaction nodes"; }
|
||||
{ cLog(lsTRACE) << "Flushed " << fc << " dirty transaction nodes"; }
|
||||
|
||||
bool closeTimeCorrect = true;
|
||||
if (closeTime == 0)
|
||||
{ // we agreed to disagree
|
||||
closeTimeCorrect = false;
|
||||
closeTime = mPreviousLedger->getCloseTimeNC() + 1;
|
||||
cLog(lsINFO) << "CNF badclose " << closeTime;
|
||||
}
|
||||
cLog(lsDEBUG) << "Report: NewL = " << newLCL->getHash() << ":" << newLCL->getLedgerSeq();
|
||||
|
||||
newLCL->setAccepted(closeTime, mCloseResolution, closeTimeCorrect);
|
||||
newLCL->updateHash();
|
||||
@@ -1232,10 +1267,10 @@ void LedgerConsensus::accept(SHAMap::ref set, LoadEvent::pointer)
|
||||
|
||||
if (sLog(lsTRACE))
|
||||
{
|
||||
Log(lsTRACE) << "newLCL";
|
||||
cLog(lsTRACE) << "newLCL";
|
||||
Json::Value p;
|
||||
newLCL->addJson(p, LEDGER_JSON_DUMP_TXRP | LEDGER_JSON_DUMP_STATE);
|
||||
Log(lsTRACE) << p;
|
||||
cLog(lsTRACE) << p;
|
||||
}
|
||||
|
||||
statusChange(ripple::neACCEPTED_LEDGER, *newLCL);
|
||||
@@ -1271,19 +1306,20 @@ void LedgerConsensus::accept(SHAMap::ref set, LoadEvent::pointer)
|
||||
{ // we voted NO
|
||||
try
|
||||
{
|
||||
cLog(lsINFO) << "Test applying disputed transaction that did not get in";
|
||||
cLog(lsDEBUG) << "Test applying disputed transaction that did not get in";
|
||||
SerializerIterator sit(it.second->peekTransaction());
|
||||
SerializedTransaction::pointer txn = boost::make_shared<SerializedTransaction>(boost::ref(sit));
|
||||
applyTransaction(engine, txn, newOL, failedTransactions, true);
|
||||
if (applyTransaction(engine, txn, newOL, true, false))
|
||||
failedTransactions.push_back(txn);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
cLog(lsINFO) << "Failed to apply transaction we voted NO on";
|
||||
cLog(lsDEBUG) << "Failed to apply transaction we voted NO on";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cLog(lsINFO) << "Applying transactions from current ledger";
|
||||
cLog(lsDEBUG) << "Applying transactions from current open ledger";
|
||||
applyTransactions(theApp->getLedgerMaster().getCurrentLedger()->peekTransactionMap(), newOL, newLCL,
|
||||
failedTransactions, true);
|
||||
theApp->getLedgerMaster().pushLedger(newLCL, newOL, !mConsensusFail);
|
||||
@@ -1330,18 +1366,18 @@ void LedgerConsensus::simulate()
|
||||
Json::Value LedgerConsensus::getJson()
|
||||
{
|
||||
Json::Value ret(Json::objectValue);
|
||||
ret["proposing"] = mProposing ? "yes" : "no";
|
||||
ret["validating"] = mValidating ? "yes" : "no";
|
||||
ret["proposing"] = mProposing;
|
||||
ret["validating"] = mValidating;
|
||||
ret["proposers"] = static_cast<int>(mPeerPositions.size());
|
||||
|
||||
if (mHaveCorrectLCL)
|
||||
{
|
||||
ret["synched"] = "yes";
|
||||
ret["synched"] = true;
|
||||
ret["ledger_seq"] = mPreviousLedger->getLedgerSeq() + 1;
|
||||
ret["close_granularity"] = mCloseResolution;
|
||||
}
|
||||
else
|
||||
ret["synched"] = "no";
|
||||
ret["synched"] = false;
|
||||
|
||||
switch (mState)
|
||||
{
|
||||
|
||||
@@ -21,8 +21,10 @@
|
||||
#include "LoadMonitor.h"
|
||||
|
||||
DEFINE_INSTANCE(LedgerConsensus);
|
||||
DEFINE_INSTANCE(TransactionAcquire);
|
||||
|
||||
class TransactionAcquire : public PeerSet, public boost::enable_shared_from_this<TransactionAcquire>
|
||||
class TransactionAcquire :
|
||||
private IS_INSTANCE(TransactionAcquire), public PeerSet, public boost::enable_shared_from_this<TransactionAcquire>
|
||||
{ // A transaction set we are trying to acquire
|
||||
public:
|
||||
typedef boost::shared_ptr<TransactionAcquire> pointer;
|
||||
@@ -32,10 +34,10 @@ protected:
|
||||
bool mHaveRoot;
|
||||
|
||||
void onTimer(bool progress);
|
||||
void newPeer(Peer::ref peer) { trigger(peer, false); }
|
||||
void newPeer(Peer::ref peer) { trigger(peer); }
|
||||
|
||||
void done();
|
||||
void trigger(Peer::ref, bool timer);
|
||||
void trigger(Peer::ref);
|
||||
boost::weak_ptr<PeerSet> pmDowncast();
|
||||
|
||||
public:
|
||||
@@ -43,7 +45,7 @@ public:
|
||||
TransactionAcquire(const uint256& hash);
|
||||
virtual ~TransactionAcquire() { ; }
|
||||
|
||||
SHAMap::pointer getMap() { return mMap; }
|
||||
SHAMap::ref getMap() { return mMap; }
|
||||
|
||||
SMAddNode takeNodes(const std::list<SHAMapNode>& IDs,
|
||||
const std::list< std::vector<unsigned char> >& data, Peer::ref);
|
||||
@@ -138,8 +140,8 @@ protected:
|
||||
void sendHaveTxSet(const uint256& set, bool direct);
|
||||
void applyTransactions(SHAMap::ref transactionSet, Ledger::ref targetLedger,
|
||||
Ledger::ref checkLedger, CanonicalTXSet& failedTransactions, bool openLgr);
|
||||
void applyTransaction(TransactionEngine& engine, SerializedTransaction::ref txn,
|
||||
Ledger::ref targetLedger, CanonicalTXSet& failedTransactions, bool openLgr);
|
||||
int applyTransaction(TransactionEngine& engine, SerializedTransaction::ref txn, Ledger::ref targetLedger,
|
||||
bool openLgr, bool retryAssured);
|
||||
|
||||
uint32 roundCloseTime(uint32 closeTime);
|
||||
|
||||
@@ -161,8 +163,8 @@ public:
|
||||
int startup();
|
||||
Json::Value getJson();
|
||||
|
||||
Ledger::pointer peekPreviousLedger() { return mPreviousLedger; }
|
||||
uint256 getLCL() { return mPrevLedgerHash; }
|
||||
Ledger::ref peekPreviousLedger() { return mPreviousLedger; }
|
||||
uint256 getLCL() { return mPrevLedgerHash; }
|
||||
|
||||
SHAMap::pointer getTransactionTree(const uint256& hash, bool doAcquire);
|
||||
TransactionAcquire::pointer getAcquiring(const uint256& hash);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
#include "LedgerEntrySet.h"
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
@@ -13,15 +14,14 @@ DECLARE_INSTANCE(LedgerEntrySet)
|
||||
|
||||
// #define META_DEBUG
|
||||
|
||||
// Small for testing, should likely be 32 or 64.
|
||||
#define DIR_NODE_MAX 2
|
||||
#define DIR_NODE_MAX 32
|
||||
|
||||
void LedgerEntrySet::init(Ledger::ref ledger, const uint256& transactionID, uint32 ledgerID)
|
||||
{
|
||||
mEntries.clear();
|
||||
mLedger = ledger;
|
||||
mLedger = ledger;
|
||||
mSet.init(transactionID, ledgerID);
|
||||
mSeq = 0;
|
||||
mSeq = 0;
|
||||
}
|
||||
|
||||
void LedgerEntrySet::clear()
|
||||
@@ -92,9 +92,7 @@ SLE::pointer LedgerEntrySet::entryCache(LedgerEntryType letType, const uint256&
|
||||
entryCache(sleEntry);
|
||||
}
|
||||
else if (action == taaDELETE)
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
sleEntry.reset();
|
||||
}
|
||||
return sleEntry;
|
||||
}
|
||||
@@ -137,16 +135,17 @@ void LedgerEntrySet::entryCreate(SLE::ref sle)
|
||||
return;
|
||||
}
|
||||
|
||||
assert(it->second.mSeq == mSeq);
|
||||
|
||||
switch (it->second.mAction)
|
||||
{
|
||||
case taaMODIFY:
|
||||
throw std::runtime_error("Create after modify");
|
||||
|
||||
case taaDELETE:
|
||||
throw std::runtime_error("Create after delete"); // We could make this a modify
|
||||
it->second.mEntry = sle;
|
||||
it->second.mAction = taaMODIFY;
|
||||
it->second.mSeq = mSeq;
|
||||
break;
|
||||
|
||||
case taaMODIFY:
|
||||
throw std::runtime_error("Create after modify");
|
||||
case taaCREATE:
|
||||
throw std::runtime_error("Create after create"); // We could make this work
|
||||
|
||||
@@ -156,6 +155,8 @@ void LedgerEntrySet::entryCreate(SLE::ref sle)
|
||||
default:
|
||||
throw std::runtime_error("Unknown taa");
|
||||
}
|
||||
|
||||
assert(it->second.mSeq == mSeq);
|
||||
}
|
||||
|
||||
void LedgerEntrySet::entryModify(SLE::ref sle)
|
||||
@@ -175,6 +176,8 @@ void LedgerEntrySet::entryModify(SLE::ref sle)
|
||||
case taaCACHED:
|
||||
it->second.mAction = taaMODIFY;
|
||||
fallthru();
|
||||
|
||||
case taaCREATE:
|
||||
case taaMODIFY:
|
||||
it->second.mSeq = mSeq;
|
||||
it->second.mEntry = sle;
|
||||
@@ -183,11 +186,6 @@ void LedgerEntrySet::entryModify(SLE::ref sle)
|
||||
case taaDELETE:
|
||||
throw std::runtime_error("Modify after delete");
|
||||
|
||||
case taaCREATE:
|
||||
it->second.mSeq = mSeq;
|
||||
it->second.mEntry = sle;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw std::runtime_error("Unknown taa");
|
||||
}
|
||||
@@ -366,7 +364,7 @@ void LedgerEntrySet::calcRawMeta(Serializer& s, TER result, uint32 index)
|
||||
// Entries modified only as a result of building the transaction metadata
|
||||
boost::unordered_map<uint256, SLE::pointer> newMod;
|
||||
|
||||
typedef std::pair<const uint256, LedgerEntrySetEntry> u256_LES_pair;
|
||||
typedef std::map<uint256, LedgerEntrySetEntry>::value_type u256_LES_pair;
|
||||
BOOST_FOREACH(u256_LES_pair& it, mEntries)
|
||||
{
|
||||
SField::ptr type = &sfGeneric;
|
||||
@@ -479,7 +477,7 @@ void LedgerEntrySet::calcRawMeta(Serializer& s, TER result, uint32 index)
|
||||
}
|
||||
|
||||
// add any new modified nodes to the modification set
|
||||
typedef std::pair<const uint256, SLE::pointer> u256_sle_pair;
|
||||
typedef std::map<uint256, SLE::pointer>::value_type u256_sle_pair;
|
||||
BOOST_FOREACH(u256_sle_pair& it, newMod)
|
||||
entryModify(it.second);
|
||||
|
||||
@@ -527,6 +525,11 @@ TER LedgerEntrySet::dirAdd(
|
||||
const uint256& uLedgerIndex,
|
||||
boost::function<void (SLE::ref)> fDescriber)
|
||||
{
|
||||
cLog(lsDEBUG)
|
||||
<< boost::str(boost::format("dirAdd: uRootIndex=%s uLedgerIndex=%s")
|
||||
% uRootIndex.ToString()
|
||||
% uLedgerIndex.ToString());
|
||||
|
||||
SLE::pointer sleNode;
|
||||
STVector256 svIndexes;
|
||||
SLE::pointer sleRoot = entryCache(ltDIR_NODE, uRootIndex);
|
||||
@@ -568,7 +571,7 @@ TER LedgerEntrySet::dirAdd(
|
||||
// Add to new node.
|
||||
else if (!++uNodeDir)
|
||||
{
|
||||
return terDIR_FULL;
|
||||
return tecDIR_FULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -620,19 +623,36 @@ TER LedgerEntrySet::dirDelete(
|
||||
const bool bKeepRoot, // --> True, if we never completely clean up, after we overflow the root node.
|
||||
const uint64& uNodeDir, // --> Node containing entry.
|
||||
const uint256& uRootIndex, // --> The index of the base of the directory. Nodes are based off of this.
|
||||
const uint256& uLedgerIndex, // --> Value to add to directory.
|
||||
const bool bStable) // --> True, not to change relative order of entries.
|
||||
const uint256& uLedgerIndex, // --> Value to remove from directory.
|
||||
const bool bStable, // --> True, not to change relative order of entries.
|
||||
const bool bSoft) // --> True, uNodeDir is not hard and fast (pass uNodeDir=0).
|
||||
{
|
||||
uint64 uNodeCur = uNodeDir;
|
||||
SLE::pointer sleNode = entryCache(ltDIR_NODE, uNodeCur ? Ledger::getDirNodeIndex(uRootIndex, uNodeCur) : uRootIndex);
|
||||
|
||||
assert(sleNode);
|
||||
|
||||
if (!sleNode)
|
||||
{
|
||||
cLog(lsWARNING) << "dirDelete: no such node";
|
||||
cLog(lsWARNING)
|
||||
<< boost::str(boost::format("dirDelete: no such node: uRootIndex=%s uNodeDir=%s uLedgerIndex=%s")
|
||||
% uRootIndex.ToString()
|
||||
% strHex(uNodeDir)
|
||||
% uLedgerIndex.ToString());
|
||||
|
||||
return tefBAD_LEDGER;
|
||||
if (!bSoft)
|
||||
{
|
||||
assert(false);
|
||||
return tefBAD_LEDGER;
|
||||
}
|
||||
else if (uNodeDir < 20)
|
||||
{
|
||||
// Go the extra mile. Even if node doesn't exist, try the next node.
|
||||
|
||||
return dirDelete(bKeepRoot, uNodeDir+1, uRootIndex, uLedgerIndex, bStable, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
return tefBAD_LEDGER;
|
||||
}
|
||||
}
|
||||
|
||||
STVector256 svIndexes = sleNode->getFieldV256(sfIndexes);
|
||||
@@ -641,14 +661,26 @@ TER LedgerEntrySet::dirDelete(
|
||||
|
||||
it = std::find(vuiIndexes.begin(), vuiIndexes.end(), uLedgerIndex);
|
||||
|
||||
assert(vuiIndexes.end() != it);
|
||||
if (vuiIndexes.end() == it)
|
||||
{
|
||||
assert(false);
|
||||
if (!bSoft)
|
||||
{
|
||||
assert(false);
|
||||
|
||||
cLog(lsWARNING) << "dirDelete: no such entry";
|
||||
cLog(lsWARNING) << "dirDelete: no such entry";
|
||||
|
||||
return tefBAD_LEDGER;
|
||||
return tefBAD_LEDGER;
|
||||
}
|
||||
else if (uNodeDir < 20)
|
||||
{
|
||||
// Go the extra mile. Even if entry not in node, try the next node.
|
||||
|
||||
return dirDelete(bKeepRoot, uNodeDir+1, uRootIndex, uLedgerIndex, bStable, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
return tefBAD_LEDGER;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the element.
|
||||
@@ -845,7 +877,7 @@ bool LedgerEntrySet::dirNext(
|
||||
}
|
||||
|
||||
// If there is a count, adjust the owner count by iAmount. Otherwise, compute the owner count and store it.
|
||||
TER LedgerEntrySet::ownerCountAdjust(const uint160& uOwnerID, int iAmount, SLE::ref sleAccountRoot)
|
||||
void LedgerEntrySet::ownerCountAdjust(const uint160& uOwnerID, int iAmount, SLE::ref sleAccountRoot)
|
||||
{
|
||||
SLE::pointer sleHold = sleAccountRoot
|
||||
? SLE::pointer()
|
||||
@@ -855,49 +887,27 @@ TER LedgerEntrySet::ownerCountAdjust(const uint160& uOwnerID, int iAmount, SLE::
|
||||
? sleAccountRoot
|
||||
: sleHold;
|
||||
|
||||
const bool bHaveOwnerCount = sleRoot->isFieldPresent(sfOwnerCount);
|
||||
TER terResult;
|
||||
const uint32 uOwnerCount = sleRoot->getFieldU32(sfOwnerCount);
|
||||
|
||||
if (bHaveOwnerCount)
|
||||
{
|
||||
const uint32 uOwnerCount = sleRoot->getFieldU32(sfOwnerCount);
|
||||
|
||||
if (iAmount + int(uOwnerCount) >= 0)
|
||||
sleRoot->setFieldU32(sfOwnerCount, uOwnerCount+iAmount);
|
||||
|
||||
terResult = tesSUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32 uActualCount;
|
||||
|
||||
terResult = dirCount(Ledger::getOwnerDirIndex(uOwnerID), uActualCount);
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
{
|
||||
sleRoot->setFieldU32(sfOwnerCount, uActualCount);
|
||||
}
|
||||
}
|
||||
|
||||
return terResult;
|
||||
if (iAmount + int(uOwnerCount) >= 0)
|
||||
sleRoot->setFieldU32(sfOwnerCount, uOwnerCount+iAmount);
|
||||
}
|
||||
|
||||
TER LedgerEntrySet::offerDelete(const SLE::pointer& sleOffer, const uint256& uOfferIndex, const uint160& uOwnerID)
|
||||
{
|
||||
bool bOwnerNode = sleOffer->isFieldPresent(sfOwnerNode); // Detect legacy dirs.
|
||||
uint64 uOwnerNode = sleOffer->getFieldU64(sfOwnerNode);
|
||||
TER terResult = dirDelete(false, uOwnerNode, Ledger::getOwnerDirIndex(uOwnerID), uOfferIndex, false);
|
||||
TER terResult = dirDelete(false, uOwnerNode, Ledger::getOwnerDirIndex(uOwnerID), uOfferIndex, false, !bOwnerNode);
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
{
|
||||
terResult = ownerCountAdjust(uOwnerID, -1);
|
||||
}
|
||||
ownerCountAdjust(uOwnerID, -1);
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
{
|
||||
uint256 uDirectory = sleOffer->getFieldH256(sfBookDirectory);
|
||||
uint64 uBookNode = sleOffer->getFieldU64(sfBookNode);
|
||||
|
||||
terResult = dirDelete(false, uBookNode, uDirectory, uOfferIndex, true);
|
||||
// Offer delete is always hard. Always have hints.
|
||||
terResult = dirDelete(false, uBookNode, uDirectory, uOfferIndex, true, true);
|
||||
}
|
||||
|
||||
entryDelete(sleOffer);
|
||||
@@ -1026,7 +1036,7 @@ uint32 LedgerEntrySet::rippleQualityIn(const uint160& uToAccountID, const uint16
|
||||
|
||||
// Return how much of uIssuerID's uCurrencyID IOUs that uAccountID holds. May be negative.
|
||||
// <-- IOU's uAccountID has of uIssuerID.
|
||||
STAmount LedgerEntrySet::rippleHolds(const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID, bool bAvail)
|
||||
STAmount LedgerEntrySet::rippleHolds(const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID)
|
||||
{
|
||||
STAmount saBalance;
|
||||
SLE::pointer sleRippleState = entryCache(ltRIPPLE_STATE, Ledger::getRippleStateIndex(uAccountID, uIssuerID, uCurrencyID));
|
||||
@@ -1037,30 +1047,14 @@ STAmount LedgerEntrySet::rippleHolds(const uint160& uAccountID, const uint160& u
|
||||
}
|
||||
else if (uAccountID > uIssuerID)
|
||||
{
|
||||
if (bAvail)
|
||||
{
|
||||
saBalance = sleRippleState->getFieldAmount(sfLowLimit);
|
||||
saBalance -= sleRippleState->getFieldAmount(sfBalance);
|
||||
}
|
||||
else
|
||||
{
|
||||
saBalance = sleRippleState->getFieldAmount(sfBalance);
|
||||
saBalance.negate(); // Put balance in uAccountID terms.
|
||||
}
|
||||
saBalance = sleRippleState->getFieldAmount(sfBalance);
|
||||
saBalance.negate(); // Put balance in uAccountID terms.
|
||||
|
||||
saBalance.setIssuer(uIssuerID);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bAvail)
|
||||
{
|
||||
saBalance = sleRippleState->getFieldAmount(sfHighLimit);
|
||||
saBalance += sleRippleState->getFieldAmount(sfBalance);
|
||||
}
|
||||
else
|
||||
{
|
||||
saBalance = sleRippleState->getFieldAmount(sfBalance);
|
||||
}
|
||||
saBalance = sleRippleState->getFieldAmount(sfBalance);
|
||||
|
||||
saBalance.setIssuer(uIssuerID);
|
||||
}
|
||||
@@ -1069,35 +1063,45 @@ STAmount LedgerEntrySet::rippleHolds(const uint160& uAccountID, const uint160& u
|
||||
}
|
||||
|
||||
// <-- saAmount: amount of uCurrencyID held by uAccountID. May be negative.
|
||||
STAmount LedgerEntrySet::accountHolds(const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID, bool bAvail)
|
||||
STAmount LedgerEntrySet::accountHolds(const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID)
|
||||
{
|
||||
STAmount saAmount;
|
||||
|
||||
if (!uCurrencyID)
|
||||
{
|
||||
SLE::pointer sleAccount = entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(uAccountID));
|
||||
uint64 uReserve = mLedger->getReserve(sleAccount->getFieldU32(sfOwnerCount));
|
||||
|
||||
saAmount = sleAccount->getFieldAmount(sfBalance);
|
||||
saAmount = sleAccount->getFieldAmount(sfBalance)-uReserve;
|
||||
|
||||
if (saAmount < uReserve)
|
||||
{
|
||||
saAmount.zero();
|
||||
}
|
||||
else
|
||||
{
|
||||
saAmount -= uReserve;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
saAmount = rippleHolds(uAccountID, uCurrencyID, uIssuerID, bAvail);
|
||||
saAmount = rippleHolds(uAccountID, uCurrencyID, uIssuerID);
|
||||
}
|
||||
|
||||
cLog(lsINFO) << boost::str(boost::format("accountHolds: uAccountID=%s saAmount=%s bAvail=%d")
|
||||
cLog(lsINFO) << boost::str(boost::format("accountHolds: uAccountID=%s saAmount=%s")
|
||||
% RippleAddress::createHumanAccountID(uAccountID)
|
||||
% saAmount.getFullText()
|
||||
% bAvail);
|
||||
% saAmount.getFullText());
|
||||
|
||||
return saAmount;
|
||||
}
|
||||
|
||||
// Returns the funds available for uAccountID for a currency/issuer.
|
||||
// Use when you need a default for rippling uAccountID's currency.
|
||||
// XXX Should take into account quality?
|
||||
// --> saDefault/currency/issuer
|
||||
// <-- saFunds: Funds available. May be negative.
|
||||
// If the issuer is the same as uAccountID, funds are unlimited, use result is saDefault.
|
||||
STAmount LedgerEntrySet::accountFunds(const uint160& uAccountID, const STAmount& saDefault, bool bAvail)
|
||||
STAmount LedgerEntrySet::accountFunds(const uint160& uAccountID, const STAmount& saDefault)
|
||||
{
|
||||
STAmount saFunds;
|
||||
|
||||
@@ -1111,13 +1115,12 @@ STAmount LedgerEntrySet::accountFunds(const uint160& uAccountID, const STAmount&
|
||||
}
|
||||
else
|
||||
{
|
||||
saFunds = accountHolds(uAccountID, saDefault.getCurrency(), saDefault.getIssuer(), bAvail);
|
||||
saFunds = accountHolds(uAccountID, saDefault.getCurrency(), saDefault.getIssuer());
|
||||
|
||||
cLog(lsINFO) << boost::str(boost::format("accountFunds: uAccountID=%s saDefault=%s saFunds=%s bAvail=%d")
|
||||
cLog(lsINFO) << boost::str(boost::format("accountFunds: uAccountID=%s saDefault=%s saFunds=%s")
|
||||
% RippleAddress::createHumanAccountID(uAccountID)
|
||||
% saDefault.getFullText()
|
||||
% saFunds.getFullText()
|
||||
% bAvail);
|
||||
% saFunds.getFullText());
|
||||
}
|
||||
|
||||
return saFunds;
|
||||
@@ -1143,47 +1146,119 @@ STAmount LedgerEntrySet::rippleTransferFee(const uint160& uSenderID, const uint1
|
||||
return saTransitFee;
|
||||
}
|
||||
|
||||
TER LedgerEntrySet::trustCreate(
|
||||
const bool bSrcHigh,
|
||||
const uint160& uSrcAccountID,
|
||||
const uint160& uDstAccountID,
|
||||
const uint256& uIndex, // --> ripple state entry
|
||||
SLE::ref sleAccount, // --> the account being set.
|
||||
const bool bAuth, // --> authorize account.
|
||||
const STAmount& saBalance, // --> balance of account being set. Issuer should be ACCOUNT_ONE
|
||||
const STAmount& saLimit, // --> limit for account being set. Issuer should be the account being set.
|
||||
const uint32 uQualityIn,
|
||||
const uint32 uQualityOut)
|
||||
{
|
||||
const uint160& uLowAccountID = !bSrcHigh ? uSrcAccountID : uDstAccountID;
|
||||
const uint160& uHighAccountID = bSrcHigh ? uSrcAccountID : uDstAccountID;
|
||||
|
||||
SLE::pointer sleRippleState = entryCreate(ltRIPPLE_STATE, uIndex);
|
||||
|
||||
uint64 uLowNode;
|
||||
uint64 uHighNode;
|
||||
|
||||
TER terResult = dirAdd(
|
||||
uLowNode,
|
||||
Ledger::getOwnerDirIndex(uLowAccountID),
|
||||
sleRippleState->getIndex(),
|
||||
boost::bind(&Ledger::ownerDirDescriber, _1, uLowAccountID));
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
{
|
||||
terResult = dirAdd(
|
||||
uHighNode,
|
||||
Ledger::getOwnerDirIndex(uHighAccountID),
|
||||
sleRippleState->getIndex(),
|
||||
boost::bind(&Ledger::ownerDirDescriber, _1, uHighAccountID));
|
||||
}
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
{
|
||||
const bool bSetDst = saLimit.getIssuer() == uDstAccountID;
|
||||
const bool bSetHigh = bSrcHigh ^ bSetDst;
|
||||
|
||||
sleRippleState->setFieldU64(sfLowNode, uLowNode); // Remember deletion hints.
|
||||
sleRippleState->setFieldU64(sfHighNode, uHighNode);
|
||||
|
||||
sleRippleState->setFieldAmount(!bSetHigh ? sfLowLimit : sfHighLimit, saLimit);
|
||||
sleRippleState->setFieldAmount( bSetHigh ? sfLowLimit : sfHighLimit, STAmount(saBalance.getCurrency(), bSetDst ? uSrcAccountID : uDstAccountID));
|
||||
|
||||
if (uQualityIn)
|
||||
sleRippleState->setFieldU32(!bSetHigh ? sfLowQualityIn : sfHighQualityIn, uQualityIn);
|
||||
|
||||
if (uQualityOut)
|
||||
sleRippleState->setFieldU32(!bSetHigh ? sfLowQualityOut : sfHighQualityOut, uQualityOut);
|
||||
|
||||
uint32 uFlags = !bSetHigh ? lsfLowReserve : lsfHighReserve;
|
||||
|
||||
if (bAuth)
|
||||
{
|
||||
uFlags |= (!bSetHigh ? lsfLowAuth : lsfHighAuth);
|
||||
}
|
||||
|
||||
sleRippleState->setFieldU32(sfFlags, uFlags);
|
||||
|
||||
ownerCountAdjust(!bSetDst ? uSrcAccountID : uDstAccountID, 1, sleAccount);
|
||||
|
||||
sleRippleState->setFieldAmount(sfBalance, bSetHigh ? -saBalance : saBalance);
|
||||
}
|
||||
|
||||
return terResult;
|
||||
}
|
||||
|
||||
// Direct send w/o fees: redeeming IOUs and/or sending own IOUs.
|
||||
void LedgerEntrySet::rippleCredit(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount, bool bCheckIssuer)
|
||||
TER LedgerEntrySet::rippleCredit(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount, bool bCheckIssuer)
|
||||
{
|
||||
uint160 uIssuerID = saAmount.getIssuer();
|
||||
uint160 uCurrencyID = saAmount.getCurrency();
|
||||
|
||||
assert(!bCheckIssuer || uSenderID == uIssuerID || uReceiverID == uIssuerID);
|
||||
|
||||
bool bFlipped = uSenderID > uReceiverID;
|
||||
bool bSenderHigh = uSenderID > uReceiverID;
|
||||
uint256 uIndex = Ledger::getRippleStateIndex(uSenderID, uReceiverID, saAmount.getCurrency());
|
||||
SLE::pointer sleRippleState = entryCache(ltRIPPLE_STATE, uIndex);
|
||||
|
||||
TER terResult;
|
||||
|
||||
assert(!!uSenderID && uSenderID != ACCOUNT_ONE);
|
||||
assert(!!uReceiverID && uReceiverID != ACCOUNT_ONE);
|
||||
|
||||
if (!sleRippleState)
|
||||
{
|
||||
STAmount saBalance = saAmount;
|
||||
STAmount saReceiverLimit = STAmount(uCurrencyID, uReceiverID);
|
||||
STAmount saBalance = saAmount;
|
||||
|
||||
saBalance.setIssuer(ACCOUNT_ONE);
|
||||
|
||||
cLog(lsDEBUG) << boost::str(boost::format("rippleCredit: create line: %s (%s) -> %s : %s")
|
||||
cLog(lsDEBUG) << boost::str(boost::format("rippleCredit: create line: %s (0) -> %s : %s")
|
||||
% RippleAddress::createHumanAccountID(uSenderID)
|
||||
% saBalance.getFullText()
|
||||
% RippleAddress::createHumanAccountID(uReceiverID)
|
||||
% saAmount.getFullText());
|
||||
|
||||
sleRippleState = entryCreate(ltRIPPLE_STATE, uIndex);
|
||||
|
||||
if (!bFlipped)
|
||||
saBalance.negate();
|
||||
|
||||
sleRippleState->setFieldAmount(sfBalance, saBalance);
|
||||
sleRippleState->setFieldAmount(bFlipped ? sfHighLimit : sfLowLimit, STAmount(uCurrencyID, uSenderID));
|
||||
sleRippleState->setFieldAmount(bFlipped ? sfLowLimit : sfHighLimit, STAmount(uCurrencyID, uReceiverID));
|
||||
terResult = trustCreate(
|
||||
bSenderHigh,
|
||||
uSenderID,
|
||||
uReceiverID,
|
||||
uIndex,
|
||||
entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(uReceiverID)),
|
||||
false,
|
||||
saBalance,
|
||||
saReceiverLimit);
|
||||
}
|
||||
else
|
||||
{
|
||||
STAmount saBalance = sleRippleState->getFieldAmount(sfBalance);
|
||||
|
||||
if (!bFlipped)
|
||||
if (!bSenderHigh)
|
||||
saBalance.negate(); // Put balance in low terms.
|
||||
|
||||
cLog(lsDEBUG) << boost::str(boost::format("rippleCredit> %s (%s) -> %s : %s")
|
||||
@@ -1194,31 +1269,37 @@ void LedgerEntrySet::rippleCredit(const uint160& uSenderID, const uint160& uRece
|
||||
|
||||
saBalance += saAmount;
|
||||
|
||||
if (!bFlipped)
|
||||
if (!bSenderHigh)
|
||||
saBalance.negate();
|
||||
|
||||
sleRippleState->setFieldAmount(sfBalance, saBalance);
|
||||
|
||||
entryModify(sleRippleState);
|
||||
|
||||
terResult = tesSUCCESS;
|
||||
}
|
||||
|
||||
return terResult;
|
||||
}
|
||||
|
||||
// Send regardless of limits.
|
||||
// --> saAmount: Amount/currency/issuer for receiver to get.
|
||||
// <-- saActual: Amount actually sent. Sender pay's fees.
|
||||
STAmount LedgerEntrySet::rippleSend(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount)
|
||||
TER LedgerEntrySet::rippleSend(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount, STAmount& saActual)
|
||||
{
|
||||
STAmount saActual;
|
||||
const uint160 uIssuerID = saAmount.getIssuer();
|
||||
TER terResult;
|
||||
|
||||
assert(!!uSenderID && !!uReceiverID);
|
||||
|
||||
if (uSenderID == uIssuerID || uReceiverID == uIssuerID || uIssuerID == ACCOUNT_ONE)
|
||||
{
|
||||
// Direct send: redeeming IOUs and/or sending own IOUs.
|
||||
rippleCredit(uSenderID, uReceiverID, saAmount, false);
|
||||
terResult = rippleCredit(uSenderID, uReceiverID, saAmount, false);
|
||||
|
||||
saActual = saAmount;
|
||||
|
||||
terResult = tesSUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1230,16 +1311,19 @@ STAmount LedgerEntrySet::rippleSend(const uint160& uSenderID, const uint160& uRe
|
||||
|
||||
saActual.setIssuer(uIssuerID); // XXX Make sure this done in + above.
|
||||
|
||||
rippleCredit(uIssuerID, uReceiverID, saAmount);
|
||||
rippleCredit(uSenderID, uIssuerID, saActual);
|
||||
terResult = rippleCredit(uIssuerID, uReceiverID, saAmount);
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
terResult = rippleCredit(uSenderID, uIssuerID, saActual);
|
||||
}
|
||||
|
||||
return saActual;
|
||||
return terResult;
|
||||
}
|
||||
|
||||
void LedgerEntrySet::accountSend(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount)
|
||||
TER LedgerEntrySet::accountSend(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount)
|
||||
{
|
||||
assert(!saAmount.isNegative());
|
||||
TER terResult = tesSUCCESS;
|
||||
|
||||
if (!saAmount)
|
||||
{
|
||||
@@ -1282,8 +1366,12 @@ void LedgerEntrySet::accountSend(const uint160& uSenderID, const uint160& uRecei
|
||||
}
|
||||
else
|
||||
{
|
||||
rippleSend(uSenderID, uReceiverID, saAmount);
|
||||
STAmount saActual;
|
||||
|
||||
terResult = rippleSend(uSenderID, uReceiverID, saAmount, saActual);
|
||||
}
|
||||
|
||||
return terResult;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
|
||||
@@ -97,13 +97,14 @@ public:
|
||||
const uint64& uNodeDir, // Node item is mentioned in.
|
||||
const uint256& uRootIndex,
|
||||
const uint256& uLedgerIndex, // Item being deleted
|
||||
const bool bStable);
|
||||
const bool bStable,
|
||||
const bool bSoft);
|
||||
|
||||
bool dirFirst(const uint256& uRootIndex, SLE::pointer& sleNode, unsigned int& uDirEntry, uint256& uEntryIndex);
|
||||
bool dirNext(const uint256& uRootIndex, SLE::pointer& sleNode, unsigned int& uDirEntry, uint256& uEntryIndex);
|
||||
TER dirCount(const uint256& uDirIndex, uint32& uCount);
|
||||
|
||||
TER ownerCountAdjust(const uint160& uOwnerID, int iAmount, SLE::ref sleAccountRoot=SLE::pointer());
|
||||
void ownerCountAdjust(const uint160& uOwnerID, int iAmount, SLE::ref sleAccountRoot=SLE::pointer());
|
||||
|
||||
// Offer functions.
|
||||
TER offerDelete(const uint256& uOfferIndex);
|
||||
@@ -119,14 +120,26 @@ public:
|
||||
uint32 rippleQualityOut(const uint160& uToAccountID, const uint160& uFromAccountID, const uint160& uCurrencyID)
|
||||
{ return rippleQualityIn(uToAccountID, uFromAccountID, uCurrencyID, sfLowQualityOut, sfHighQualityOut); }
|
||||
|
||||
STAmount rippleHolds(const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID, bool bAvail=false);
|
||||
STAmount rippleHolds(const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID);
|
||||
STAmount rippleTransferFee(const uint160& uSenderID, const uint160& uReceiverID, const uint160& uIssuerID, const STAmount& saAmount);
|
||||
void rippleCredit(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount, bool bCheckIssuer=true);
|
||||
STAmount rippleSend(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount);
|
||||
TER rippleCredit(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount, bool bCheckIssuer=true);
|
||||
TER rippleSend(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount, STAmount& saActual);
|
||||
|
||||
STAmount accountHolds(const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID, bool bAvail=false);
|
||||
STAmount accountFunds(const uint160& uAccountID, const STAmount& saDefault, bool bAvail=false);
|
||||
void accountSend(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount);
|
||||
STAmount accountHolds(const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID);
|
||||
STAmount accountFunds(const uint160& uAccountID, const STAmount& saDefault);
|
||||
TER accountSend(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount);
|
||||
|
||||
TER trustCreate(
|
||||
const bool bSrcHigh,
|
||||
const uint160& uSrcAccountID,
|
||||
const uint160& uDstAccountID,
|
||||
const uint256& uIndex,
|
||||
SLE::ref sleAccount,
|
||||
const bool bAuth,
|
||||
const STAmount& saSrcBalance,
|
||||
const STAmount& saSrcLimit,
|
||||
const uint32 uSrcQualityIn = 0,
|
||||
const uint32 uSrcQualityOut = 0);
|
||||
|
||||
Json::Value getJson(int) const;
|
||||
void calcRawMeta(Serializer&, TER result, uint32 index);
|
||||
|
||||
@@ -19,6 +19,7 @@ static bool LEFInit()
|
||||
<< SOElement(sfAccount, SOE_REQUIRED)
|
||||
<< SOElement(sfSequence, SOE_REQUIRED)
|
||||
<< SOElement(sfBalance, SOE_REQUIRED)
|
||||
<< SOElement(sfOwnerCount, SOE_REQUIRED)
|
||||
<< SOElement(sfPreviousTxnID, SOE_REQUIRED)
|
||||
<< SOElement(sfPreviousTxnLgrSeq, SOE_REQUIRED)
|
||||
<< SOElement(sfRegularKey, SOE_OPTIONAL)
|
||||
@@ -28,7 +29,6 @@ static bool LEFInit()
|
||||
<< SOElement(sfMessageKey, SOE_OPTIONAL)
|
||||
<< SOElement(sfTransferRate, SOE_OPTIONAL)
|
||||
<< SOElement(sfDomain, SOE_OPTIONAL)
|
||||
<< SOElement(sfOwnerCount, SOE_OPTIONAL)
|
||||
;
|
||||
|
||||
DECLARE_LEF(Contract, ltCONTRACT)
|
||||
@@ -87,14 +87,16 @@ static bool LEFInit()
|
||||
<< SOElement(sfHighLimit, SOE_REQUIRED)
|
||||
<< SOElement(sfPreviousTxnID, SOE_REQUIRED)
|
||||
<< SOElement(sfPreviousTxnLgrSeq, SOE_REQUIRED)
|
||||
<< SOElement(sfLowNode, SOE_OPTIONAL)
|
||||
<< SOElement(sfLowQualityIn, SOE_OPTIONAL)
|
||||
<< SOElement(sfLowQualityOut, SOE_OPTIONAL)
|
||||
<< SOElement(sfHighNode, SOE_OPTIONAL)
|
||||
<< SOElement(sfHighQualityIn, SOE_OPTIONAL)
|
||||
<< SOElement(sfHighQualityOut, SOE_OPTIONAL)
|
||||
;
|
||||
|
||||
DECLARE_LEF(LedgerHashes, ltLEDGER_HASHES)
|
||||
<< SOElement(sfFirstLedgerSequence, SOE_OPTIONAL)
|
||||
<< SOElement(sfFirstLedgerSequence, SOE_OPTIONAL) // Remove if we do a ledger restart
|
||||
<< SOElement(sfLastLedgerSequence, SOE_OPTIONAL)
|
||||
<< SOElement(sfHashes, SOE_REQUIRED)
|
||||
;
|
||||
@@ -103,6 +105,13 @@ static bool LEFInit()
|
||||
<< SOElement(sfFeatures, SOE_REQUIRED)
|
||||
;
|
||||
|
||||
DECLARE_LEF(FeeSettings, ltFEE_SETTINGS)
|
||||
<< SOElement(sfBaseFee, SOE_REQUIRED)
|
||||
<< SOElement(sfReferenceFeeUnits, SOE_REQUIRED)
|
||||
<< SOElement(sfReserveBase, SOE_REQUIRED)
|
||||
<< SOElement(sfReserveIncrement, SOE_REQUIRED)
|
||||
;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ enum LedgerEntryType
|
||||
ltCONTRACT = 'c',
|
||||
ltLEDGER_HASHES = 'h',
|
||||
ltFEATURES = 'f',
|
||||
ltFEE_SETTINGS = 's',
|
||||
};
|
||||
|
||||
// Used as a prefix for computing ledger indexes (keys).
|
||||
@@ -32,15 +33,24 @@ enum LedgerNameSpace
|
||||
spaceContract = 'c',
|
||||
spaceSkipList = 's',
|
||||
spaceFeature = 'f',
|
||||
spaceFee = 's',
|
||||
};
|
||||
|
||||
enum LedgerSpecificFlags
|
||||
{
|
||||
// ltACCOUNT_ROOT
|
||||
lsfPasswordSpent = 0x00010000, // True if password set fee is spent.
|
||||
lsfPasswordSpent = 0x00010000, // True, if password set fee is spent.
|
||||
lsfRequireDestTag = 0x00020000, // True, to require a DestinationTag for payments.
|
||||
lsfRequireAuth = 0x00040000, // True, to require a authorization to hold IOUs.
|
||||
|
||||
// ltOFFER
|
||||
lsfPassive = 0x00010000,
|
||||
|
||||
// ltRIPPLE_STATE
|
||||
lsfLowReserve = 0x00010000, // True, if entry counts toward reserve.
|
||||
lsfHighReserve = 0x00020000,
|
||||
lsfLowAuth = 0x00040000,
|
||||
lsfHighAuth = 0x00080000,
|
||||
};
|
||||
|
||||
class LedgerEntryFormat
|
||||
@@ -48,7 +58,7 @@ class LedgerEntryFormat
|
||||
public:
|
||||
std::string t_name;
|
||||
LedgerEntryType t_type;
|
||||
std::vector<SOElement::ptr> elements;
|
||||
std::vector<SOElement::ref> elements;
|
||||
|
||||
static std::map<int, LedgerEntryFormat*> byType;
|
||||
static std::map<std::string, LedgerEntryFormat*> byName;
|
||||
|
||||
@@ -10,11 +10,11 @@
|
||||
#include "Application.h"
|
||||
|
||||
#ifndef CACHED_LEDGER_NUM
|
||||
#define CACHED_LEDGER_NUM 128
|
||||
#define CACHED_LEDGER_NUM 96
|
||||
#endif
|
||||
|
||||
#ifndef CACHED_LEDGER_AGE
|
||||
#define CACHED_LEDGER_AGE 900
|
||||
#define CACHED_LEDGER_AGE 120
|
||||
#endif
|
||||
|
||||
// FIXME: Need to clean up ledgers by index at some point
|
||||
@@ -24,6 +24,7 @@ LedgerHistory::LedgerHistory() : mLedgersByHash("LedgerCache", CACHED_LEDGER_NUM
|
||||
|
||||
void LedgerHistory::addLedger(Ledger::pointer ledger)
|
||||
{
|
||||
assert(ledger->isImmutable());
|
||||
mLedgersByHash.canonicalize(ledger->getHash(), ledger, true);
|
||||
}
|
||||
|
||||
@@ -41,6 +42,16 @@ void LedgerHistory::addAcceptedLedger(Ledger::pointer ledger, bool fromConsensus
|
||||
ledger->pendSave(fromConsensus);
|
||||
}
|
||||
|
||||
uint256 LedgerHistory::getLedgerHash(uint32 index)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLedgersByHash.peekMutex());
|
||||
std::map<uint32, uint256>::iterator it(mLedgersByIndex.find(index));
|
||||
if (it != mLedgersByIndex.end())
|
||||
return it->second;
|
||||
sl.unlock();
|
||||
return uint256();
|
||||
}
|
||||
|
||||
Ledger::pointer LedgerHistory::getLedgerBySeq(uint32 index)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLedgersByHash.peekMutex());
|
||||
@@ -68,12 +79,17 @@ Ledger::pointer LedgerHistory::getLedgerByHash(const uint256& hash)
|
||||
{
|
||||
Ledger::pointer ret = mLedgersByHash.fetch(hash);
|
||||
if (ret)
|
||||
{
|
||||
assert(ret->getHash() == hash);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = Ledger::loadByHash(hash);
|
||||
if (!ret)
|
||||
return ret;
|
||||
assert(ret->getHash() == hash);
|
||||
mLedgersByHash.canonicalize(ret->getHash(), ret);
|
||||
assert(ret->getHash() == hash);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ public:
|
||||
void addLedger(Ledger::pointer ledger);
|
||||
void addAcceptedLedger(Ledger::pointer ledger, bool fromConsensus);
|
||||
|
||||
uint256 getLedgerHash(uint32 index);
|
||||
Ledger::pointer getLedgerBySeq(uint32 index);
|
||||
Ledger::pointer getLedgerByHash(const uint256& hash);
|
||||
Ledger::pointer canonicalizeLedger(Ledger::pointer, bool cache);
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
|
||||
SETUP_LOG();
|
||||
|
||||
#define MIN_VALIDATION_RATIO 150 // 150/256ths of validations of previous ledger
|
||||
#define MAX_LEDGER_GAP 100 // Don't catch up more than 100 ledgers (cannot exceed 256)
|
||||
|
||||
uint32 LedgerMaster::getCurrentLedgerIndex()
|
||||
{
|
||||
return mCurrentLedger->getLedgerSeq();
|
||||
@@ -49,10 +52,13 @@ void LedgerMaster::pushLedger(Ledger::ref newLCL, Ledger::ref newOL, bool fromCo
|
||||
cLog(lsINFO) << "StashAccepted: " << newLCL->getHash();
|
||||
}
|
||||
|
||||
boost::recursive_mutex::scoped_lock ml(mLock);
|
||||
mFinalizedLedger = newLCL;
|
||||
mCurrentLedger = newOL;
|
||||
mEngine.setLedger(newOL);
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock ml(mLock);
|
||||
mFinalizedLedger = newLCL;
|
||||
mCurrentLedger = newOL;
|
||||
mEngine.setLedger(newOL);
|
||||
}
|
||||
checkAccept(newLCL->getHash(), newLCL->getLedgerSeq());
|
||||
}
|
||||
|
||||
void LedgerMaster::switchLedgers(Ledger::ref lastClosed, Ledger::ref current)
|
||||
@@ -69,6 +75,7 @@ void LedgerMaster::switchLedgers(Ledger::ref lastClosed, Ledger::ref current)
|
||||
|
||||
assert(!mCurrentLedger->isClosed());
|
||||
mEngine.setLedger(mCurrentLedger);
|
||||
checkAccept(lastClosed->getHash(), lastClosed->getLedgerSeq());
|
||||
}
|
||||
|
||||
void LedgerMaster::storeLedger(Ledger::ref ledger)
|
||||
@@ -78,7 +85,6 @@ void LedgerMaster::storeLedger(Ledger::ref ledger)
|
||||
mLedgerHistory.addAcceptedLedger(ledger, false);
|
||||
}
|
||||
|
||||
|
||||
Ledger::pointer LedgerMaster::closeLedger(bool recover)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
@@ -91,8 +97,9 @@ Ledger::pointer LedgerMaster::closeLedger(bool recover)
|
||||
{
|
||||
try
|
||||
{
|
||||
TER result = mEngine.applyTransaction(*it->second, tapOPEN_LEDGER);
|
||||
if (isTepSuccess(result))
|
||||
bool didApply;
|
||||
mEngine.applyTransaction(*it->second, tapOPEN_LEDGER, didApply);
|
||||
if (didApply)
|
||||
++recovers;
|
||||
}
|
||||
catch (...)
|
||||
@@ -112,19 +119,61 @@ Ledger::pointer LedgerMaster::closeLedger(bool recover)
|
||||
|
||||
TER LedgerMaster::doTransaction(const SerializedTransaction& txn, TransactionEngineParams params)
|
||||
{
|
||||
TER result = mEngine.applyTransaction(txn, params);
|
||||
bool didApply;
|
||||
TER result = mEngine.applyTransaction(txn, params, didApply);
|
||||
// CHECKME: Should we call this even on gross failures?
|
||||
theApp->getOPs().pubProposedTransaction(mEngine.getLedger(), txn, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool LedgerMaster::haveLedgerRange(uint32 from, uint32 to)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
uint32 prevMissing = mCompleteLedgers.prevMissing(to + 1);
|
||||
return (prevMissing == RangeSet::RangeSetAbsent) || (prevMissing < from);
|
||||
}
|
||||
|
||||
void LedgerMaster::acquireMissingLedger(const uint256& ledgerHash, uint32 ledgerSeq)
|
||||
void LedgerMaster::asyncAccept(Ledger::pointer ledger)
|
||||
{
|
||||
uint32 seq = ledger->getLedgerSeq();
|
||||
uint256 prevHash = ledger->getParentHash();
|
||||
|
||||
while (seq > 0)
|
||||
{
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock ml(mLock);
|
||||
mCompleteLedgers.setValue(seq);
|
||||
--seq;
|
||||
if (mCompleteLedgers.hasValue(seq))
|
||||
break;
|
||||
}
|
||||
|
||||
uint256 tHash, pHash;
|
||||
if (!Ledger::getHashesByIndex(seq, tHash, pHash) || (tHash != prevHash))
|
||||
break;
|
||||
prevHash = pHash;
|
||||
}
|
||||
|
||||
resumeAcquiring();
|
||||
}
|
||||
|
||||
bool LedgerMaster::acquireMissingLedger(Ledger::ref origLedger, const uint256& ledgerHash, uint32 ledgerSeq)
|
||||
{ // return: false = already gave up recently
|
||||
if (mTooFast)
|
||||
return true;
|
||||
|
||||
Ledger::pointer ledger = mLedgerHistory.getLedgerBySeq(ledgerSeq);
|
||||
if (Ledger::getHashByIndex(ledgerSeq) == ledgerHash)
|
||||
{
|
||||
cLog(lsDEBUG) << "Ledger hash found in database";
|
||||
mTooFast = true;
|
||||
theApp->getJobQueue().addJob(jtPUBLEDGER, boost::bind(&LedgerMaster::asyncAccept, this, ledger));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (theApp->getMasterLedgerAcquire().isFailure(ledgerHash))
|
||||
return false;
|
||||
|
||||
mMissingLedger = theApp->getMasterLedgerAcquire().findCreate(ledgerHash);
|
||||
if (mMissingLedger->isComplete())
|
||||
{
|
||||
@@ -132,11 +181,38 @@ void LedgerMaster::acquireMissingLedger(const uint256& ledgerHash, uint32 ledger
|
||||
if (lgr && (lgr->getLedgerSeq() == ledgerSeq))
|
||||
missingAcquireComplete(mMissingLedger);
|
||||
mMissingLedger.reset();
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
else if (mMissingLedger->isDone())
|
||||
{
|
||||
mMissingLedger.reset();
|
||||
return false;
|
||||
}
|
||||
mMissingSeq = ledgerSeq;
|
||||
if (mMissingLedger->setAccept())
|
||||
mMissingLedger->addOnComplete(boost::bind(&LedgerMaster::missingAcquireComplete, this, _1));
|
||||
{
|
||||
if (!mMissingLedger->addOnComplete(boost::bind(&LedgerMaster::missingAcquireComplete, this, _1)))
|
||||
theApp->getIOService().post(boost::bind(&LedgerMaster::missingAcquireComplete, this, mMissingLedger));
|
||||
}
|
||||
|
||||
if (theApp->getMasterLedgerAcquire().getFetchCount() < 4)
|
||||
{
|
||||
int count = 0;
|
||||
typedef std::pair<uint32, uint256> u_pair;
|
||||
|
||||
std::vector<u_pair> vec = origLedger->getLedgerHashes();
|
||||
BOOST_REVERSE_FOREACH(const u_pair& it, vec)
|
||||
{
|
||||
if ((count < 3) && (it.first < ledgerSeq) &&
|
||||
!mCompleteLedgers.hasValue(it.first) && !theApp->getMasterLedgerAcquire().find(it.second))
|
||||
{
|
||||
++count;
|
||||
theApp->getMasterLedgerAcquire().findCreate(it.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void LedgerMaster::missingAcquireComplete(LedgerAcquire::pointer acq)
|
||||
@@ -151,14 +227,14 @@ void LedgerMaster::missingAcquireComplete(LedgerAcquire::pointer acq)
|
||||
mMissingLedger.reset();
|
||||
mMissingSeq = 0;
|
||||
|
||||
if (!acq->isFailed())
|
||||
if (acq->isComplete())
|
||||
{
|
||||
setFullLedger(acq->getLedger());
|
||||
acq->getLedger()->pendSave(false);
|
||||
}
|
||||
}
|
||||
|
||||
static bool shouldAcquire(uint32 currentLedger, uint32 ledgerHistory, uint32 candidateLedger)
|
||||
bool LedgerMaster::shouldAcquire(uint32 currentLedger, uint32 ledgerHistory, uint32 candidateLedger)
|
||||
{
|
||||
bool ret;
|
||||
if (candidateLedger >= currentLedger)
|
||||
@@ -168,8 +244,75 @@ static bool shouldAcquire(uint32 currentLedger, uint32 ledgerHistory, uint32 can
|
||||
return ret;
|
||||
}
|
||||
|
||||
void LedgerMaster::resumeAcquiring()
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock ml(mLock);
|
||||
if (!mTooFast)
|
||||
return;
|
||||
mTooFast = false;
|
||||
|
||||
if (mMissingLedger && mMissingLedger->isDone())
|
||||
mMissingLedger.reset();
|
||||
|
||||
if (mMissingLedger || !theConfig.LEDGER_HISTORY)
|
||||
{
|
||||
tLog(mMissingLedger, lsDEBUG) << "Fetch already in progress, not resuming";
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 prevMissing = mCompleteLedgers.prevMissing(mFinalizedLedger->getLedgerSeq());
|
||||
if (prevMissing == RangeSet::RangeSetAbsent)
|
||||
{
|
||||
cLog(lsDEBUG) << "no prior missing ledger, not resuming";
|
||||
return;
|
||||
}
|
||||
if (shouldAcquire(mCurrentLedger->getLedgerSeq(), theConfig.LEDGER_HISTORY, prevMissing))
|
||||
{
|
||||
cLog(lsTRACE) << "Resuming at " << prevMissing;
|
||||
assert(!mCompleteLedgers.hasValue(prevMissing));
|
||||
Ledger::pointer nextLedger = getLedgerBySeq(prevMissing + 1);
|
||||
if (nextLedger)
|
||||
acquireMissingLedger(nextLedger, nextLedger->getParentHash(), nextLedger->getLedgerSeq() - 1);
|
||||
else
|
||||
{
|
||||
mCompleteLedgers.clearValue(prevMissing);
|
||||
cLog(lsWARNING) << "We have a gap at: " << prevMissing + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LedgerMaster::fixMismatch(Ledger::ref ledger)
|
||||
{
|
||||
int invalidate = 0;
|
||||
|
||||
mMissingLedger.reset();
|
||||
mMissingSeq = 0;
|
||||
|
||||
for (uint32 lSeq = ledger->getLedgerSeq() - 1; lSeq > 0; --lSeq)
|
||||
if (mCompleteLedgers.hasValue(lSeq))
|
||||
{
|
||||
uint256 hash = ledger->getLedgerHash(lSeq);
|
||||
if (hash.isNonZero())
|
||||
{ // try to close the seam
|
||||
Ledger::pointer otherLedger = getLedgerBySeq(lSeq);
|
||||
if (otherLedger && (otherLedger->getHash() == hash))
|
||||
{ // we closed the seam
|
||||
tLog(invalidate != 0, lsWARNING) << "Match at " << lSeq << ", " <<
|
||||
invalidate << " prior ledgers invalidated";
|
||||
return;
|
||||
}
|
||||
}
|
||||
mCompleteLedgers.clearValue(lSeq);
|
||||
++invalidate;
|
||||
}
|
||||
|
||||
// all prior ledgers invalidated
|
||||
tLog(invalidate != 0, lsWARNING) << "All " << invalidate << " prior ledgers invalidated";
|
||||
}
|
||||
|
||||
void LedgerMaster::setFullLedger(Ledger::ref ledger)
|
||||
{ // A new ledger has been accepted as part of the trusted chain
|
||||
cLog(lsDEBUG) << "Ledger " << ledger->getLedgerSeq() << " accepted :" << ledger->getHash();
|
||||
|
||||
boost::recursive_mutex::scoped_lock ml(mLock);
|
||||
|
||||
@@ -178,26 +321,30 @@ void LedgerMaster::setFullLedger(Ledger::ref ledger)
|
||||
if ((ledger->getLedgerSeq() != 0) && mCompleteLedgers.hasValue(ledger->getLedgerSeq() - 1))
|
||||
{ // we think we have the previous ledger, double check
|
||||
Ledger::pointer prevLedger = getLedgerBySeq(ledger->getLedgerSeq() - 1);
|
||||
if (!prevLedger)
|
||||
if (!prevLedger || (prevLedger->getHash() != ledger->getParentHash()))
|
||||
{
|
||||
cLog(lsWARNING) << "Ledger " << ledger->getLedgerSeq() - 1 << " missing";
|
||||
mCompleteLedgers.clearValue(ledger->getLedgerSeq() - 1);
|
||||
}
|
||||
else if (prevLedger->getHash() != ledger->getParentHash())
|
||||
{
|
||||
cLog(lsWARNING) << "Ledger " << ledger->getLedgerSeq() << " invalidates prior ledger";
|
||||
mCompleteLedgers.clearValue(prevLedger->getLedgerSeq());
|
||||
cLog(lsWARNING) << "Acquired ledger invalidates previous ledger: " <<
|
||||
(prevLedger ? "hashMismatch" : "missingLedger");
|
||||
fixMismatch(ledger);
|
||||
}
|
||||
}
|
||||
|
||||
if (mMissingLedger && mMissingLedger->isComplete())
|
||||
if (mMissingLedger && mMissingLedger->isDone())
|
||||
{
|
||||
if (mMissingLedger->isFailed())
|
||||
theApp->getMasterLedgerAcquire().dropLedger(mMissingLedger->getHash());
|
||||
mMissingLedger.reset();
|
||||
}
|
||||
|
||||
if (mMissingLedger || !theConfig.LEDGER_HISTORY)
|
||||
return;
|
||||
|
||||
if (Ledger::getPendingSaves() > 3)
|
||||
{
|
||||
tLog(mMissingLedger, lsDEBUG) << "Fetch already in progress, " << mMissingLedger->getTimeouts() << " timeouts";
|
||||
return;
|
||||
}
|
||||
|
||||
if (Ledger::getPendingSaves() > 2)
|
||||
{
|
||||
mTooFast = true;
|
||||
cLog(lsINFO) << "Too many pending ledger saves";
|
||||
return;
|
||||
}
|
||||
@@ -207,21 +354,24 @@ void LedgerMaster::setFullLedger(Ledger::ref ledger)
|
||||
{
|
||||
if (!shouldAcquire(mCurrentLedger->getLedgerSeq(), theConfig.LEDGER_HISTORY, ledger->getLedgerSeq() - 1))
|
||||
return;
|
||||
cLog(lsINFO) << "We need the ledger before the ledger we just accepted";
|
||||
acquireMissingLedger(ledger->getParentHash(), ledger->getLedgerSeq() - 1);
|
||||
cLog(lsDEBUG) << "We need the ledger before the ledger we just accepted: " << ledger->getLedgerSeq() - 1;
|
||||
acquireMissingLedger(ledger, ledger->getParentHash(), ledger->getLedgerSeq() - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32 prevMissing = mCompleteLedgers.prevMissing(ledger->getLedgerSeq());
|
||||
if (prevMissing == RangeSet::RangeSetAbsent)
|
||||
{
|
||||
cLog(lsDEBUG) << "no prior missing ledger";
|
||||
return;
|
||||
}
|
||||
if (shouldAcquire(mCurrentLedger->getLedgerSeq(), theConfig.LEDGER_HISTORY, prevMissing))
|
||||
{
|
||||
cLog(lsINFO) << "Ledger " << prevMissing << " is missing";
|
||||
cLog(lsDEBUG) << "Ledger " << prevMissing << " is needed";
|
||||
assert(!mCompleteLedgers.hasValue(prevMissing));
|
||||
Ledger::pointer nextLedger = getLedgerBySeq(prevMissing + 1);
|
||||
if (nextLedger)
|
||||
acquireMissingLedger(nextLedger->getParentHash(), nextLedger->getLedgerSeq() - 1);
|
||||
acquireMissingLedger(ledger, nextLedger->getParentHash(), nextLedger->getLedgerSeq() - 1);
|
||||
else
|
||||
{
|
||||
mCompleteLedgers.clearValue(prevMissing);
|
||||
@@ -231,4 +381,158 @@ void LedgerMaster::setFullLedger(Ledger::ref ledger)
|
||||
}
|
||||
}
|
||||
|
||||
void LedgerMaster::checkAccept(const uint256& hash)
|
||||
{
|
||||
Ledger::pointer ledger = mLedgerHistory.getLedgerByHash(hash);
|
||||
if (ledger)
|
||||
checkAccept(hash, ledger->getLedgerSeq());
|
||||
}
|
||||
|
||||
void LedgerMaster::checkAccept(const uint256& hash, uint32 seq)
|
||||
{ // Can we advance the last fully accepted ledger? If so, can we publish?
|
||||
boost::recursive_mutex::scoped_lock ml(mLock);
|
||||
|
||||
if (mValidLedger && (seq <= mValidLedger->getLedgerSeq()))
|
||||
return;
|
||||
|
||||
int minVal = mMinValidations;
|
||||
|
||||
if (mLastValidateHash.isNonZero())
|
||||
{
|
||||
int val = theApp->getValidations().getTrustedValidationCount(mLastValidateHash);
|
||||
val *= MIN_VALIDATION_RATIO;
|
||||
val /= 256;
|
||||
if (val > minVal)
|
||||
minVal = val;
|
||||
}
|
||||
|
||||
if (theConfig.RUN_STANDALONE)
|
||||
minVal = 0;
|
||||
else if (theApp->getOPs().isNeedNetworkLedger())
|
||||
minVal = 1;
|
||||
|
||||
if (theApp->getValidations().getTrustedValidationCount(hash) < minVal) // nothing we can do
|
||||
return;
|
||||
|
||||
cLog(lsINFO) << "Advancing accepted ledger to " << seq << " with >= " << minVal << " validations";
|
||||
|
||||
mLastValidateHash = hash;
|
||||
mLastValidateSeq = seq;
|
||||
|
||||
Ledger::pointer ledger = mLedgerHistory.getLedgerByHash(hash);
|
||||
if (!ledger)
|
||||
return;
|
||||
mValidLedger = ledger;
|
||||
|
||||
tryPublish();
|
||||
}
|
||||
|
||||
void LedgerMaster::tryPublish()
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock ml(mLock);
|
||||
assert(mValidLedger);
|
||||
|
||||
if (!mPubLedger)
|
||||
{
|
||||
mPubLedger = mValidLedger;
|
||||
mPubLedgers.push_back(mValidLedger);
|
||||
}
|
||||
else if (mValidLedger->getLedgerSeq() > (mPubLedger->getLedgerSeq() + MAX_LEDGER_GAP))
|
||||
{
|
||||
mPubLedger = mValidLedger;
|
||||
mPubLedgers.push_back(mValidLedger);
|
||||
}
|
||||
else if (mValidLedger->getLedgerSeq() > mPubLedger->getLedgerSeq())
|
||||
{
|
||||
for (uint32 seq = mPubLedger->getLedgerSeq() + 1; seq <= mValidLedger->getLedgerSeq(); ++seq)
|
||||
{
|
||||
cLog(lsDEBUG) << "Trying to publish ledger " << seq;
|
||||
|
||||
Ledger::pointer ledger;
|
||||
uint256 hash;
|
||||
|
||||
if (seq == mValidLedger->getLedgerSeq())
|
||||
{
|
||||
ledger = mValidLedger;
|
||||
hash = ledger->getHash();
|
||||
}
|
||||
else
|
||||
{
|
||||
hash = mValidLedger->getLedgerHash(seq);
|
||||
if (hash.isZero())
|
||||
{
|
||||
cLog(lsFATAL) << "Ledger: " << mValidLedger->getLedgerSeq() << " does not have hash for " <<
|
||||
seq;
|
||||
assert(false);
|
||||
}
|
||||
ledger = mLedgerHistory.getLedgerByHash(hash);
|
||||
}
|
||||
|
||||
if (ledger)
|
||||
{
|
||||
mPubLedger = ledger;
|
||||
mPubLedgers.push_back(ledger);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (theApp->getMasterLedgerAcquire().isFailure(hash))
|
||||
{
|
||||
cLog(lsFATAL) << "Unable to acquire a recent validated ledger";
|
||||
}
|
||||
else
|
||||
{
|
||||
LedgerAcquire::pointer acq = theApp->getMasterLedgerAcquire().findCreate(hash);
|
||||
if (!acq->isDone())
|
||||
{
|
||||
acq->setAccept();
|
||||
break;
|
||||
}
|
||||
else if (acq->isComplete() && !acq->isFailed())
|
||||
{
|
||||
mPubLedger = acq->getLedger();
|
||||
mPubLedgers.push_back(mPubLedger);
|
||||
}
|
||||
else
|
||||
cLog(lsWARNING) << "Failed to acquire a published ledger";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!mPubLedgers.empty() && !mPubThread)
|
||||
{
|
||||
theApp->getOPs().clearNeedNetworkLedger();
|
||||
mPubThread = true;
|
||||
mTooFast = false;
|
||||
theApp->getJobQueue().addJob(jtPUBLEDGER, boost::bind(&LedgerMaster::pubThread, this));
|
||||
}
|
||||
}
|
||||
|
||||
void LedgerMaster::pubThread()
|
||||
{
|
||||
std::list<Ledger::pointer> ledgers;
|
||||
|
||||
while (1)
|
||||
{
|
||||
ledgers.clear();
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock ml(mLock);
|
||||
mPubLedgers.swap(ledgers);
|
||||
if (ledgers.empty())
|
||||
{
|
||||
mPubThread = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_FOREACH(Ledger::ref l, ledgers)
|
||||
{
|
||||
cLog(lsDEBUG) << "Publishing ledger " << l->getLedgerSeq();
|
||||
setFullLedger(l); // OPTIMIZEME: This is actually more work than we need to do
|
||||
theApp->getOPs().pubLedger(l);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
|
||||
@@ -17,12 +17,18 @@
|
||||
|
||||
class LedgerMaster
|
||||
{
|
||||
public:
|
||||
typedef boost::function<void(Ledger::ref)> callback;
|
||||
|
||||
protected:
|
||||
boost::recursive_mutex mLock;
|
||||
|
||||
TransactionEngine mEngine;
|
||||
|
||||
Ledger::pointer mCurrentLedger; // The ledger we are currently processiong
|
||||
Ledger::pointer mFinalizedLedger; // The ledger that most recently closed
|
||||
Ledger::pointer mValidLedger; // The highest-sequence ledger we have fully accepted
|
||||
Ledger::pointer mPubLedger; // The last ledger we have published
|
||||
|
||||
LedgerHistory mLedgerHistory;
|
||||
|
||||
@@ -31,27 +37,43 @@ class LedgerMaster
|
||||
RangeSet mCompleteLedgers;
|
||||
LedgerAcquire::pointer mMissingLedger;
|
||||
uint32 mMissingSeq;
|
||||
bool mTooFast; // We are acquiring faster than we're writing
|
||||
|
||||
int mMinValidations; // The minimum validations to publish a ledger
|
||||
uint256 mLastValidateHash;
|
||||
uint32 mLastValidateSeq;
|
||||
std::list<callback> mOnValidate; // Called when a ledger has enough validations
|
||||
|
||||
std::list<Ledger::pointer> mPubLedgers; // List of ledgers to publish
|
||||
bool mPubThread; // Publish thread is running
|
||||
|
||||
void applyFutureTransactions(uint32 ledgerIndex);
|
||||
bool isValidTransaction(const Transaction::pointer& trans);
|
||||
bool isTransactionOnFutureList(const Transaction::pointer& trans);
|
||||
|
||||
void acquireMissingLedger(const uint256& ledgerHash, uint32 ledgerSeq);
|
||||
bool acquireMissingLedger(Ledger::ref from, const uint256& ledgerHash, uint32 ledgerSeq);
|
||||
void asyncAccept(Ledger::pointer);
|
||||
void missingAcquireComplete(LedgerAcquire::pointer);
|
||||
void pubThread();
|
||||
|
||||
public:
|
||||
|
||||
LedgerMaster() : mHeldTransactions(uint256()), mMissingSeq(0) { ; }
|
||||
LedgerMaster() : mHeldTransactions(uint256()), mMissingSeq(0), mTooFast(false),
|
||||
mMinValidations(0), mLastValidateSeq(0), mPubThread(false)
|
||||
{ ; }
|
||||
|
||||
uint32 getCurrentLedgerIndex();
|
||||
|
||||
ScopedLock getLock() { return ScopedLock(mLock); }
|
||||
|
||||
// The current ledger is the ledger we believe new transactions should go in
|
||||
Ledger::pointer getCurrentLedger() { return mCurrentLedger; }
|
||||
Ledger::ref getCurrentLedger() { return mCurrentLedger; }
|
||||
|
||||
// The finalized ledger is the last closed/accepted ledger
|
||||
Ledger::pointer getClosedLedger() { return mFinalizedLedger; }
|
||||
Ledger::ref getClosedLedger() { return mFinalizedLedger; }
|
||||
|
||||
// The published ledger is the last fully validated ledger
|
||||
Ledger::ref getValidatedLedger() { return mPubLedger; }
|
||||
|
||||
TER doTransaction(const SerializedTransaction& txn, TransactionEngineParams params);
|
||||
|
||||
@@ -63,7 +85,11 @@ public:
|
||||
|
||||
void switchLedgers(Ledger::ref lastClosed, Ledger::ref newCurrent);
|
||||
|
||||
std::string getCompleteLedgers() { return mCompleteLedgers.toString(); }
|
||||
std::string getCompleteLedgers()
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
return mCompleteLedgers.toString();
|
||||
}
|
||||
|
||||
Ledger::pointer closeLedger(bool recoverHeldTransactions);
|
||||
|
||||
@@ -79,23 +105,39 @@ public:
|
||||
Ledger::pointer getLedgerByHash(const uint256& hash)
|
||||
{
|
||||
if (hash.isZero())
|
||||
return mCurrentLedger;
|
||||
return boost::make_shared<Ledger>(boost::ref(*mCurrentLedger), false);
|
||||
|
||||
if (mCurrentLedger && (mCurrentLedger->getHash() == hash))
|
||||
return mCurrentLedger;
|
||||
return boost::make_shared<Ledger>(boost::ref(*mCurrentLedger), false);
|
||||
|
||||
if (mFinalizedLedger && (mFinalizedLedger->getHash() == hash))
|
||||
return mFinalizedLedger;
|
||||
|
||||
return mLedgerHistory.getLedgerByHash(hash);
|
||||
}
|
||||
|
||||
void setLedgerRangePresent(uint32 minV, uint32 maxV) { mCompleteLedgers.setRange(minV, maxV); }
|
||||
void setLedgerRangePresent(uint32 minV, uint32 maxV)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
mCompleteLedgers.setRange(minV, maxV);
|
||||
}
|
||||
|
||||
void addHeldTransaction(const Transaction::pointer& trans);
|
||||
void fixMismatch(Ledger::ref ledger);
|
||||
|
||||
bool haveLedgerRange(uint32 from, uint32 to);
|
||||
|
||||
void resumeAcquiring();
|
||||
|
||||
void sweep(void) { mLedgerHistory.sweep(); }
|
||||
|
||||
void addValidateCallback(callback& c) { mOnValidate.push_back(c); }
|
||||
|
||||
void checkAccept(const uint256& hash);
|
||||
void checkAccept(const uint256& hash, uint32 seq);
|
||||
void tryPublish();
|
||||
|
||||
static bool shouldAcquire(uint32 currentLedgerID, uint32 ledgerHistory, uint32 targetLedger);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -42,7 +42,7 @@ uint256 LedgerProposal::getSigningHash() const
|
||||
{
|
||||
Serializer s((32 + 32 + 32 + 256 + 256) / 8);
|
||||
|
||||
s.add32(sHP_Proposal);
|
||||
s.add32(theConfig.SIGN_PROPOSAL);
|
||||
s.add32(mProposeSeq);
|
||||
s.add32(mCloseTime);
|
||||
s.add256(mPreviousLedger);
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
#include "LoadManager.h"
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include "Log.h"
|
||||
#include "Config.h"
|
||||
|
||||
SETUP_LOG();
|
||||
|
||||
LoadManager::LoadManager(int creditRate, int creditLimit, int debitWarn, int debitLimit) :
|
||||
mCreditRate(creditRate), mCreditLimit(creditLimit), mDebitWarn(debitWarn), mDebitLimit(debitLimit),
|
||||
mCosts(LT_MAX)
|
||||
@@ -8,6 +15,7 @@ LoadManager::LoadManager(int creditRate, int creditLimit, int debitWarn, int deb
|
||||
addLoadCost(LoadCost(LT_RequestNoReply, 1, LC_CPU | LC_Disk));
|
||||
addLoadCost(LoadCost(LT_InvalidSignature, 100, LC_CPU));
|
||||
addLoadCost(LoadCost(LT_UnwantedData, 5, LC_CPU | LC_Network));
|
||||
addLoadCost(LoadCost(LT_BadData, 20, LC_CPU));
|
||||
|
||||
addLoadCost(LoadCost(LT_NewTrusted, 10, 0));
|
||||
addLoadCost(LoadCost(LT_NewTransaction, 2, 0));
|
||||
@@ -131,24 +139,70 @@ bool LoadManager::adjust(LoadSource& source, int credits) const
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64 LoadFeeTrack::scaleFee(uint64 fee)
|
||||
uint64 LoadFeeTrack::mulDiv(uint64 value, uint32 mul, uint64 div)
|
||||
{ // compute (value)*(mul)/(div) - avoid overflow but keep precision
|
||||
static uint64 boundary = (0x00000000FFFFFFFF);
|
||||
if (value > boundary) // Large value, avoid overflow
|
||||
return (value / div) * mul;
|
||||
else // Normal value, preserve accuracy
|
||||
return (value * mul) / div;
|
||||
}
|
||||
|
||||
uint64 LoadFeeTrack::scaleFeeLoad(uint64 fee, uint64 baseFee, uint32 referenceFeeUnits)
|
||||
{
|
||||
static uint64 midrange(0x00000000FFFFFFFF);
|
||||
int factor = (mLocalTxnLoadFee > mRemoteTxnLoadFee) ? mLocalTxnLoadFee : mRemoteTxnLoadFee;
|
||||
|
||||
if (fee > midrange) // large fee, divide first
|
||||
return (fee / lftNormalFee) * factor;
|
||||
else // small fee, multiply first
|
||||
return (fee * factor) / lftNormalFee;
|
||||
bool big = (fee > midrange);
|
||||
if (big) // big fee, divide first to avoid overflow
|
||||
fee /= baseFee;
|
||||
else // normal fee, multiply first for accuracy
|
||||
fee *= referenceFeeUnits;
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mLock);
|
||||
fee = mulDiv(fee, std::max(mLocalTxnLoadFee, mRemoteTxnLoadFee), lftNormalFee);
|
||||
}
|
||||
|
||||
if (big) // Fee was big to start, must now multiply
|
||||
fee *= referenceFeeUnits;
|
||||
else // Fee was small to start, mst now divide
|
||||
fee /= baseFee;
|
||||
|
||||
return fee;
|
||||
}
|
||||
|
||||
uint64 LoadFeeTrack::scaleFeeBase(uint64 fee, uint64 baseFee, uint32 referenceFeeUnits)
|
||||
{
|
||||
return mulDiv(fee, referenceFeeUnits, baseFee);
|
||||
}
|
||||
|
||||
uint32 LoadFeeTrack::getRemoteFee()
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mLock);
|
||||
return mRemoteTxnLoadFee;
|
||||
}
|
||||
|
||||
uint32 LoadFeeTrack::getLocalFee()
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mLock);
|
||||
return mLocalTxnLoadFee;
|
||||
}
|
||||
|
||||
uint32 LoadFeeTrack::getLoadFactor()
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mLock);
|
||||
return std::max(mLocalTxnLoadFee, mRemoteTxnLoadFee);
|
||||
}
|
||||
|
||||
void LoadFeeTrack::setRemoteFee(uint32 f)
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mLock);
|
||||
mRemoteTxnLoadFee = f;
|
||||
}
|
||||
|
||||
void LoadFeeTrack::raiseLocalFee()
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mLock);
|
||||
if (mLocalTxnLoadFee < mLocalTxnLoadFee) // make sure this fee takes effect
|
||||
mLocalTxnLoadFee = mLocalTxnLoadFee;
|
||||
|
||||
@@ -160,10 +214,55 @@ void LoadFeeTrack::raiseLocalFee()
|
||||
|
||||
void LoadFeeTrack::lowerLocalFee()
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mLock);
|
||||
mLocalTxnLoadFee -= (mLocalTxnLoadFee / lftFeeDecFraction ); // reduce by 1/16th
|
||||
|
||||
if (mLocalTxnLoadFee < lftNormalFee)
|
||||
mLocalTxnLoadFee = lftNormalFee;
|
||||
}
|
||||
|
||||
Json::Value LoadFeeTrack::getJson(uint64 baseFee, uint32 referenceFeeUnits)
|
||||
{
|
||||
Json::Value j(Json::objectValue);
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mLock);
|
||||
|
||||
// base_fee = The cost to send a "reference" transaction under no load, in millionths of a Ripple
|
||||
j["base_fee"] = Json::Value::UInt(baseFee);
|
||||
|
||||
// load_fee = The cost to send a "reference" transaction now, in millionths of a Ripple
|
||||
j["load_fee"] = Json::Value::UInt(
|
||||
mulDiv(baseFee, std::max(mLocalTxnLoadFee, mRemoteTxnLoadFee), lftNormalFee));
|
||||
}
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(LoadManager_test)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(LoadFeeTrack_test)
|
||||
{
|
||||
cLog(lsDEBUG) << "Running load fee track test";
|
||||
|
||||
Config d; // get a default configuration object
|
||||
LoadFeeTrack l;
|
||||
|
||||
BOOST_REQUIRE_EQUAL(l.scaleFeeBase(10000, d.FEE_DEFAULT, d.TRANSACTION_FEE_BASE), 10000);
|
||||
BOOST_REQUIRE_EQUAL(l.scaleFeeLoad(10000, d.FEE_DEFAULT, d.TRANSACTION_FEE_BASE), 10000);
|
||||
BOOST_REQUIRE_EQUAL(l.scaleFeeBase(1, d.FEE_DEFAULT, d.TRANSACTION_FEE_BASE), 1);
|
||||
BOOST_REQUIRE_EQUAL(l.scaleFeeLoad(1, d.FEE_DEFAULT, d.TRANSACTION_FEE_BASE), 1);
|
||||
|
||||
// Check new default fee values give same fees as old defaults
|
||||
BOOST_REQUIRE_EQUAL(l.scaleFeeBase(d.FEE_DEFAULT, d.FEE_DEFAULT, d.TRANSACTION_FEE_BASE), 10);
|
||||
BOOST_REQUIRE_EQUAL(l.scaleFeeBase(d.FEE_ACCOUNT_RESERVE, d.FEE_DEFAULT, d.TRANSACTION_FEE_BASE), 200 * SYSTEM_CURRENCY_PARTS);
|
||||
BOOST_REQUIRE_EQUAL(l.scaleFeeBase(d.FEE_OWNER_RESERVE, d.FEE_DEFAULT, d.TRANSACTION_FEE_BASE), 50 * SYSTEM_CURRENCY_PARTS);
|
||||
BOOST_REQUIRE_EQUAL(l.scaleFeeBase(d.FEE_NICKNAME_CREATE, d.FEE_DEFAULT, d.TRANSACTION_FEE_BASE), 1000);
|
||||
BOOST_REQUIRE_EQUAL(l.scaleFeeBase(d.FEE_OFFER, d.FEE_DEFAULT, d.TRANSACTION_FEE_BASE), 10);
|
||||
BOOST_REQUIRE_EQUAL(l.scaleFeeBase(d.FEE_CONTRACT_OPERATION, d.FEE_DEFAULT, d.TRANSACTION_FEE_BASE), 1);
|
||||
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
// vim:ts=4
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
#ifndef LOADSOURCE__H
|
||||
#define LOADSOURCE__H
|
||||
#ifndef LOADMANAGER__H
|
||||
#define LOADMANAGER__H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <boost/thread/mutex.hpp>
|
||||
|
||||
#include "../json/value.h"
|
||||
|
||||
#include "types.h"
|
||||
|
||||
enum LoadType
|
||||
@@ -16,6 +18,7 @@ enum LoadType
|
||||
LT_InvalidSignature, // An object whose signature we had to check and it failed
|
||||
LT_UnwantedData, // Data we have no use for
|
||||
LT_BadPoW, // Proof of work not valid
|
||||
LT_BadData, // Data we have to verify before rejecting
|
||||
|
||||
// Good things
|
||||
LT_NewTrusted, // A new transaction/validation/proposal we trust
|
||||
@@ -124,11 +127,28 @@ protected:
|
||||
uint32 mLocalTxnLoadFee; // Scale factor, lftNormalFee = normal fee
|
||||
uint32 mRemoteTxnLoadFee; // Scale factor, lftNormalFee = normal fee
|
||||
|
||||
boost::mutex mLock;
|
||||
|
||||
static uint64 mulDiv(uint64 value, uint32 mul, uint64 div);
|
||||
|
||||
public:
|
||||
|
||||
LoadFeeTrack() : mLocalTxnLoadFee(lftNormalFee), mRemoteTxnLoadFee(lftNormalFee) { ; }
|
||||
LoadFeeTrack() : mLocalTxnLoadFee(lftNormalFee), mRemoteTxnLoadFee(lftNormalFee)
|
||||
{ ; }
|
||||
|
||||
uint64 scaleFee(uint64 fee);
|
||||
// Scale from fee units to millionths of a ripple
|
||||
uint64 scaleFeeBase(uint64 fee, uint64 baseFee, uint32 referenceFeeUnits);
|
||||
|
||||
// Scale using load as well as base rate
|
||||
uint64 scaleFeeLoad(uint64 fee, uint64 baseFee, uint32 referenceFeeUnits);
|
||||
|
||||
uint32 getRemoteFee();
|
||||
uint32 getLocalFee();
|
||||
|
||||
uint32 getLoadBase() { return lftNormalFee; }
|
||||
uint32 getLoadFactor();
|
||||
|
||||
Json::Value getJson(uint64 baseFee, uint32 referenceFeeUnits);
|
||||
|
||||
void setRemoteFee(uint32);
|
||||
void raiseLocalFee();
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include "../websocketpp/src/logger/logger.hpp"
|
||||
|
||||
boost::recursive_mutex Log::sLock;
|
||||
|
||||
LogSeverity Log::sMinSeverity = lsINFO;
|
||||
@@ -45,6 +47,7 @@ Log::~Log()
|
||||
logMsg += " " + mPartitionName + ":";
|
||||
else
|
||||
logMsg += " ";
|
||||
|
||||
switch (mSeverity)
|
||||
{
|
||||
case lsTRACE: logMsg += "TRC "; break;
|
||||
@@ -55,16 +58,18 @@ Log::~Log()
|
||||
case lsFATAL: logMsg += "FTL "; break;
|
||||
case lsINVALID: assert(false); return;
|
||||
}
|
||||
|
||||
logMsg += oss.str();
|
||||
|
||||
boost::recursive_mutex::scoped_lock sl(sLock);
|
||||
|
||||
if (mSeverity >= sMinSeverity)
|
||||
std::cerr << logMsg << std::endl;
|
||||
if (outStream != NULL)
|
||||
(*outStream) << logMsg << std::endl;
|
||||
}
|
||||
|
||||
|
||||
std::string Log::rotateLog(void)
|
||||
std::string Log::rotateLog(void)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(sLock);
|
||||
boost::filesystem::path abs_path;
|
||||
@@ -81,15 +86,15 @@ std::string Log::rotateLog(void)
|
||||
if (failsafe == std::numeric_limits<uint32>::max()) {
|
||||
return "unable to create new log file; too many log files!";
|
||||
}
|
||||
abs_path = boost::filesystem::absolute("");
|
||||
abs_path /= *pathToLog;
|
||||
abs_path_str = abs_path.parent_path().string();
|
||||
abs_path = boost::filesystem::absolute("");
|
||||
abs_path /= *pathToLog;
|
||||
abs_path_str = abs_path.parent_path().string();
|
||||
|
||||
out << logRotateCounter;
|
||||
s = out.str();
|
||||
|
||||
abs_new_path_str = abs_path_str + "/" + s + "_" + pathToLog->filename().string();
|
||||
|
||||
abs_new_path_str = abs_path_str + "/" + s + + "_" + pathToLog->filename().string();
|
||||
|
||||
logRotateCounter++;
|
||||
|
||||
} while (boost::filesystem::exists(boost::filesystem::path(abs_new_path_str)));
|
||||
@@ -97,17 +102,15 @@ std::string Log::rotateLog(void)
|
||||
outStream->close();
|
||||
boost::filesystem::rename(abs_path, boost::filesystem::path(abs_new_path_str));
|
||||
|
||||
|
||||
|
||||
setLogFile(*pathToLog);
|
||||
|
||||
return abs_new_path_str;
|
||||
|
||||
}
|
||||
|
||||
void Log::setMinSeverity(LogSeverity s, bool all)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(sLock);
|
||||
|
||||
sMinSeverity = s;
|
||||
if (all)
|
||||
LogPartition::setSeverity(s);
|
||||
@@ -116,6 +119,7 @@ void Log::setMinSeverity(LogSeverity s, bool all)
|
||||
LogSeverity Log::getMinSeverity()
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(sLock);
|
||||
|
||||
return sMinSeverity;
|
||||
}
|
||||
|
||||
@@ -131,7 +135,6 @@ std::string Log::severityToString(LogSeverity s)
|
||||
case lsFATAL: return "Fatal";
|
||||
default: assert(false); return "Unknown";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
LogSeverity Log::stringToSeverity(const std::string& s)
|
||||
@@ -164,9 +167,9 @@ void Log::setLogFile(boost::filesystem::path path)
|
||||
if (outStream != NULL)
|
||||
delete outStream;
|
||||
outStream = newStream;
|
||||
if (outStream)
|
||||
Log(lsINFO) << "Starting up";
|
||||
|
||||
if (pathToLog != NULL)
|
||||
delete pathToLog;
|
||||
pathToLog = new boost::filesystem::path(path);
|
||||
}
|
||||
|
||||
@@ -186,3 +189,36 @@ void LogPartition::setSeverity(LogSeverity severity)
|
||||
for (LogPartition *p = headLog; p != NULL; p = p->mNextLog)
|
||||
p->mMinSeverity = severity;
|
||||
}
|
||||
|
||||
|
||||
namespace websocketpp
|
||||
{
|
||||
namespace log
|
||||
{
|
||||
LogPartition websocketPartition("WebSocket");
|
||||
|
||||
void websocketLog(websocketpp::log::alevel::value v, const std::string& entry)
|
||||
{
|
||||
if (websocketPartition.doLog(lsDEBUG))
|
||||
Log(lsDEBUG, websocketPartition) << entry;
|
||||
}
|
||||
|
||||
void websocketLog(websocketpp::log::elevel::value v, const std::string& entry)
|
||||
{
|
||||
LogSeverity s = lsDEBUG;
|
||||
if ((v & websocketpp::log::elevel::INFO) != 0)
|
||||
s = lsINFO;
|
||||
else if ((v & websocketpp::log::elevel::FATAL) != 0)
|
||||
s = lsFATAL;
|
||||
else if ((v & websocketpp::log::elevel::RERROR) != 0)
|
||||
s = lsERROR;
|
||||
else if ((v & websocketpp::log::elevel::WARN) != 0)
|
||||
s = lsWARNING;
|
||||
if (websocketPartition.doLog(s))
|
||||
Log(s, websocketPartition) << entry;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
|
||||
@@ -104,3 +104,5 @@ public:
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
|
||||
#include "NetworkOPs.h"
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include "utils.h"
|
||||
#include "Application.h"
|
||||
#include "Transaction.h"
|
||||
@@ -9,8 +12,6 @@
|
||||
#include "Log.h"
|
||||
#include "RippleAddress.h"
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
// This is the primary interface into the "client" portion of the program.
|
||||
// Code that wants to do normal operations on the network such as
|
||||
@@ -26,10 +27,15 @@
|
||||
SETUP_LOG();
|
||||
DECLARE_INSTANCE(InfoSub);
|
||||
|
||||
void InfoSub::onSendEmpty()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
NetworkOPs::NetworkOPs(boost::asio::io_service& io_service, LedgerMaster* pLedgerMaster) :
|
||||
mMode(omDISCONNECTED), mNeedNetworkLedger(false), mNetTimer(io_service), mLedgerMaster(pLedgerMaster),
|
||||
mCloseTimeOffset(0), mLastCloseProposers(0), mLastCloseConvergeTime(1000 * LEDGER_IDLE_INTERVAL),
|
||||
mLastValidationTime(0)
|
||||
mMode(omDISCONNECTED), mNeedNetworkLedger(false), mProposing(false), mValidating(false),
|
||||
mNetTimer(io_service), mLedgerMaster(pLedgerMaster), mCloseTimeOffset(0), mLastCloseProposers(0),
|
||||
mLastCloseConvergeTime(1000 * LEDGER_IDLE_INTERVAL), mLastValidationTime(0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -42,6 +48,14 @@ std::string NetworkOPs::strOperatingMode()
|
||||
"full"
|
||||
};
|
||||
|
||||
if (mMode == omFULL)
|
||||
{
|
||||
if (mProposing)
|
||||
return "proposing";
|
||||
if (mValidating)
|
||||
return "validating";
|
||||
}
|
||||
|
||||
return paStatusToken[mMode];
|
||||
}
|
||||
|
||||
@@ -99,6 +113,18 @@ bool NetworkOPs::haveLedgerRange(uint32 from, uint32 to)
|
||||
return mLedgerMaster->haveLedgerRange(from, to);
|
||||
}
|
||||
|
||||
bool NetworkOPs::addWantedHash(const uint256& h)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mWantedHashLock);
|
||||
return mWantedHashes.insert(h).second;
|
||||
}
|
||||
|
||||
bool NetworkOPs::isWantedHash(const uint256& h, bool remove)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mWantedHashLock);
|
||||
return (remove ? mWantedHashes.erase(h) : mWantedHashes.count(h)) != 0;
|
||||
}
|
||||
|
||||
void NetworkOPs::submitTransaction(Job&, SerializedTransaction::pointer iTrans, stCallback callback)
|
||||
{ // this is an asynchronous interface
|
||||
Serializer s;
|
||||
@@ -147,7 +173,7 @@ void NetworkOPs::submitTransaction(Job&, SerializedTransaction::pointer iTrans,
|
||||
|
||||
// Sterilize transaction through serialization.
|
||||
// This is fully synchronous and deprecated
|
||||
Transaction::pointer NetworkOPs::submitTransactionSync(const Transaction::pointer& tpTrans)
|
||||
Transaction::pointer NetworkOPs::submitTransactionSync(const Transaction::pointer& tpTrans, bool bSubmit)
|
||||
{
|
||||
Serializer s;
|
||||
tpTrans->getSTransaction()->add(s);
|
||||
@@ -161,7 +187,8 @@ Transaction::pointer NetworkOPs::submitTransactionSync(const Transaction::pointe
|
||||
}
|
||||
else if (tpTransNew->getSTransaction()->isEquivalent(*tpTrans->getSTransaction()))
|
||||
{
|
||||
(void) NetworkOPs::processTransaction(tpTransNew);
|
||||
if (bSubmit)
|
||||
(void) NetworkOPs::processTransaction(tpTransNew);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -177,6 +204,88 @@ Transaction::pointer NetworkOPs::submitTransactionSync(const Transaction::pointe
|
||||
return tpTransNew;
|
||||
}
|
||||
|
||||
void NetworkOPs::runTransactionQueue()
|
||||
{
|
||||
TXQEntry::pointer txn;
|
||||
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
theApp->getTxnQueue().getJob(txn);
|
||||
if (!txn)
|
||||
return;
|
||||
|
||||
{
|
||||
LoadEvent::autoptr ev = theApp->getJobQueue().getLoadEventAP(jtTXN_PROC);
|
||||
|
||||
boost::recursive_mutex::scoped_lock sl(theApp->getMasterLock());
|
||||
|
||||
Transaction::pointer dbtx = theApp->getMasterTransaction().fetch(txn->getID(), true);
|
||||
assert(dbtx);
|
||||
|
||||
TER r = mLedgerMaster->doTransaction(*dbtx->getSTransaction(), tapOPEN_LEDGER | tapNO_CHECK_SIGN);
|
||||
dbtx->setResult(r);
|
||||
|
||||
if (isTemMalformed(r)) // malformed, cache bad
|
||||
theApp->isNewFlag(txn->getID(), SF_BAD);
|
||||
else if(isTelLocal(r) || isTerRetry(r)) // can be retried
|
||||
theApp->isNewFlag(txn->getID(), SF_RETRY);
|
||||
|
||||
|
||||
bool relay = true;
|
||||
|
||||
if (isTerRetry(r))
|
||||
{ // transaction should be held
|
||||
cLog(lsDEBUG) << "Transaction should be held: " << r;
|
||||
dbtx->setStatus(HELD);
|
||||
theApp->getMasterTransaction().canonicalize(dbtx, true);
|
||||
mLedgerMaster->addHeldTransaction(dbtx);
|
||||
relay = false;
|
||||
}
|
||||
else if (r == tefPAST_SEQ)
|
||||
{ // duplicate or conflict
|
||||
cLog(lsINFO) << "Transaction is obsolete";
|
||||
dbtx->setStatus(OBSOLETE);
|
||||
relay = false;
|
||||
}
|
||||
else if (r == tesSUCCESS)
|
||||
{
|
||||
cLog(lsINFO) << "Transaction is now included in open ledger";
|
||||
dbtx->setStatus(INCLUDED);
|
||||
theApp->getMasterTransaction().canonicalize(dbtx, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
cLog(lsDEBUG) << "Status other than success " << r;
|
||||
if (mMode == omFULL)
|
||||
relay = false;
|
||||
dbtx->setStatus(INVALID);
|
||||
}
|
||||
|
||||
if (relay)
|
||||
{
|
||||
std::set<uint64> peers;
|
||||
if (theApp->getSuppression().swapSet(txn->getID(), peers, SF_RELAYED))
|
||||
{
|
||||
ripple::TMTransaction tx;
|
||||
Serializer s;
|
||||
dbtx->getSTransaction()->add(s);
|
||||
tx.set_rawtransaction(&s.getData().front(), s.getLength());
|
||||
tx.set_status(ripple::tsCURRENT);
|
||||
tx.set_receivetimestamp(getNetworkTimeNC()); // FIXME: This should be when we received it
|
||||
|
||||
PackedMessage::pointer packet = boost::make_shared<PackedMessage>(tx, ripple::mtTRANSACTION);
|
||||
theApp->getConnectionPool().relayMessageBut(peers, packet);
|
||||
}
|
||||
}
|
||||
|
||||
txn->doCallbacks(r);
|
||||
}
|
||||
}
|
||||
|
||||
if (theApp->getTxnQueue().stopProcessing(txn))
|
||||
theApp->getIOService().post(boost::bind(&NetworkOPs::runTransactionQueue, this));
|
||||
}
|
||||
|
||||
Transaction::pointer NetworkOPs::processTransaction(Transaction::pointer trans, stCallback callback)
|
||||
{
|
||||
LoadEvent::autoptr ev = theApp->getJobQueue().getLoadEventAP(jtTXN_PROC);
|
||||
@@ -556,8 +665,6 @@ bool NetworkOPs::checkLastClosedLedger(const std::vector<Peer::pointer>& peerLis
|
||||
// agree? And do we have no better ledger available?
|
||||
// If so, we are either tracking or full.
|
||||
|
||||
// FIXME: We may have a ledger with many recent validations but that no directly-connected
|
||||
// node is using. THis is kind of fundamental.
|
||||
cLog(lsTRACE) << "NetworkOPs::checkLastClosedLedger";
|
||||
|
||||
Ledger::pointer ourClosed = mLedgerMaster->getClosedLedger();
|
||||
@@ -570,8 +677,8 @@ bool NetworkOPs::checkLastClosedLedger(const std::vector<Peer::pointer>& peerLis
|
||||
{
|
||||
boost::unordered_map<uint256, currentValidationCount> current =
|
||||
theApp->getValidations().getCurrentValidations(closedLedger);
|
||||
typedef std::pair<const uint256, currentValidationCount> u256_cvc_pair;
|
||||
BOOST_FOREACH(u256_cvc_pair& it, current)
|
||||
typedef std::map<uint256, currentValidationCount>::value_type u256_cvc_pair;
|
||||
BOOST_FOREACH(const u256_cvc_pair& it, current)
|
||||
{
|
||||
ValidationCount& vc = ledgers[it.first];
|
||||
vc.trustedValidations += it.second.first;
|
||||
@@ -689,6 +796,7 @@ bool NetworkOPs::checkLastClosedLedger(const std::vector<Peer::pointer>& peerLis
|
||||
}
|
||||
return true;
|
||||
}
|
||||
clearNeedNetworkLedger();
|
||||
consensus = mAcquiringLedger->getLedger();
|
||||
}
|
||||
|
||||
@@ -707,7 +815,7 @@ void NetworkOPs::switchLastClosedLedger(Ledger::pointer newLedger, bool duringCo
|
||||
else
|
||||
cLog(lsERROR) << "JUMP last closed ledger to " << newLedger->getHash();
|
||||
|
||||
mNeedNetworkLedger = false;
|
||||
clearNeedNetworkLedger();
|
||||
newLedger->setClosed();
|
||||
Ledger::pointer openLedger = boost::make_shared<Ledger>(false, boost::ref(*newLedger));
|
||||
mLedgerMaster->switchLedgers(newLedger, openLedger);
|
||||
@@ -825,11 +933,32 @@ void NetworkOPs::processTrustedProposal(LedgerProposal::pointer proposal,
|
||||
|
||||
SHAMap::pointer NetworkOPs::getTXMap(const uint256& hash)
|
||||
{
|
||||
std::map<uint256, std::pair<int, SHAMap::pointer> >::iterator it = mRecentPositions.find(hash);
|
||||
if (it != mRecentPositions.end())
|
||||
return it->second.second;
|
||||
if (!haveConsensusObject())
|
||||
return SHAMap::pointer();
|
||||
return mConsensus->getTransactionTree(hash, false);
|
||||
}
|
||||
|
||||
void NetworkOPs::takePosition(int seq, SHAMap::ref position)
|
||||
{
|
||||
mRecentPositions[position->getHash()] = std::make_pair(seq, position);
|
||||
if (mRecentPositions.size() > 4)
|
||||
{
|
||||
std::map<uint256, std::pair<int, SHAMap::pointer> >::iterator it = mRecentPositions.begin();
|
||||
while (it != mRecentPositions.end())
|
||||
{
|
||||
if (it->second.first < (seq - 2))
|
||||
{
|
||||
mRecentPositions.erase(it);
|
||||
return;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SMAddNode NetworkOPs::gotTXData(const boost::shared_ptr<Peer>& peer, const uint256& hash,
|
||||
const std::list<SHAMapNode>& nodeIDs, const std::list< std::vector<unsigned char> >& nodeData)
|
||||
{
|
||||
@@ -876,6 +1005,26 @@ void NetworkOPs::consensusViewChange()
|
||||
setMode(omCONNECTED);
|
||||
}
|
||||
|
||||
void NetworkOPs::pubServer()
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mMonitorLock);
|
||||
|
||||
if (!mSubServer.empty())
|
||||
{
|
||||
Json::Value jvObj(Json::objectValue);
|
||||
|
||||
jvObj["type"] = "serverStatus";
|
||||
jvObj["server_status"] = strOperatingMode();
|
||||
jvObj["load_base"] = theApp->getFeeTrack().getLoadBase();
|
||||
jvObj["load_factor"] = theApp->getFeeTrack().getLoadFactor();
|
||||
|
||||
BOOST_FOREACH(InfoSub* ispListener, mSubServer)
|
||||
{
|
||||
ispListener->send(jvObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkOPs::setMode(OperatingMode om)
|
||||
{
|
||||
if (mMode == om) return;
|
||||
@@ -885,26 +1034,8 @@ void NetworkOPs::setMode(OperatingMode om)
|
||||
|
||||
mMode = om;
|
||||
|
||||
Log lg((om < mMode) ? lsWARNING : lsINFO);
|
||||
|
||||
lg << "STATE->" << strOperatingMode();
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mMonitorLock);
|
||||
|
||||
if (!mSubServer.empty())
|
||||
{
|
||||
Json::Value jvObj(Json::objectValue);
|
||||
|
||||
jvObj["type"] = "serverStatus";
|
||||
jvObj["server_status"] = strOperatingMode();
|
||||
|
||||
BOOST_FOREACH(InfoSub* ispListener, mSubServer)
|
||||
{
|
||||
ispListener->send(jvObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
Log((om < mMode) ? lsWARNING : lsINFO) << "STATE->" << strOperatingMode();
|
||||
pubServer();
|
||||
}
|
||||
|
||||
|
||||
@@ -970,39 +1101,87 @@ bool NetworkOPs::recvValidation(const SerializedValidation::pointer& val)
|
||||
return theApp->getValidations().addValidation(val);
|
||||
}
|
||||
|
||||
Json::Value NetworkOPs::getServerInfo()
|
||||
Json::Value NetworkOPs::getServerInfo(bool human, bool admin)
|
||||
{
|
||||
Json::Value info = Json::objectValue;
|
||||
|
||||
switch (mMode)
|
||||
{
|
||||
case omDISCONNECTED: info["serverState"] = "disconnected"; break;
|
||||
case omCONNECTED: info["serverState"] = "connected"; break;
|
||||
case omTRACKING: info["serverState"] = "tracking"; break;
|
||||
case omFULL: info["serverState"] = "validating"; break;
|
||||
default: info["serverState"] = "unknown";
|
||||
}
|
||||
if (theConfig.TESTNET)
|
||||
info["testnet"] = theConfig.TESTNET;
|
||||
|
||||
if (!theConfig.VALIDATION_PUB.isValid())
|
||||
info["serverState"] = "none";
|
||||
else
|
||||
info["validationPKey"] = theConfig.VALIDATION_PUB.humanNodePublic();
|
||||
info["server_state"] = strOperatingMode();
|
||||
|
||||
if (mNeedNetworkLedger)
|
||||
info["networkLedger"] = "waiting";
|
||||
info["network_ledger"] = "waiting";
|
||||
|
||||
info["completeLedgers"] = theApp->getLedgerMaster().getCompleteLedgers();
|
||||
if (admin)
|
||||
{
|
||||
if (theConfig.VALIDATION_PUB.isValid())
|
||||
info["pubkey_validator"] = theConfig.VALIDATION_PUB.humanNodePublic();
|
||||
else
|
||||
info["pubkey_validator"] = "none";
|
||||
}
|
||||
info["pubkey_node"] = theApp->getWallet().getNodePublic().humanNodePublic();
|
||||
|
||||
|
||||
info["complete_ledgers"] = theApp->getLedgerMaster().getCompleteLedgers();
|
||||
info["peers"] = theApp->getConnectionPool().getPeerCount();
|
||||
|
||||
Json::Value lastClose = Json::objectValue;
|
||||
lastClose["proposers"] = theApp->getOPs().getPreviousProposers();
|
||||
lastClose["convergeTime"] = theApp->getOPs().getPreviousConvergeTime();
|
||||
info["lastClose"] = lastClose;
|
||||
if (human)
|
||||
lastClose["converge_time_s"] = static_cast<double>(theApp->getOPs().getPreviousConvergeTime()) / 1000.0;
|
||||
else
|
||||
lastClose["converge_time"] = Json::Int(theApp->getOPs().getPreviousConvergeTime());
|
||||
info["last_close"] = lastClose;
|
||||
|
||||
if (mConsensus)
|
||||
info["consensus"] = mConsensus->getJson();
|
||||
// if (mConsensus)
|
||||
// info["consensus"] = mConsensus->getJson();
|
||||
|
||||
info["load"] = theApp->getJobQueue().getJson();
|
||||
if (admin)
|
||||
info["load"] = theApp->getJobQueue().getJson();
|
||||
|
||||
if (!human)
|
||||
{
|
||||
info["load_base"] = theApp->getFeeTrack().getLoadBase();
|
||||
info["load_factor"] = theApp->getFeeTrack().getLoadFactor();
|
||||
}
|
||||
else
|
||||
info["load_factor"] =
|
||||
static_cast<double>(theApp->getFeeTrack().getLoadBase()) / theApp->getFeeTrack().getLoadFactor();
|
||||
|
||||
bool valid = false;
|
||||
Ledger::pointer lpClosed = getValidatedLedger();
|
||||
if (lpClosed)
|
||||
valid = true;
|
||||
else
|
||||
lpClosed = getClosedLedger();
|
||||
|
||||
if (lpClosed)
|
||||
{
|
||||
uint64 baseFee = lpClosed->getBaseFee();
|
||||
uint64 baseRef = lpClosed->getReferenceFeeUnits();
|
||||
Json::Value l(Json::objectValue);
|
||||
l["seq"] = Json::UInt(lpClosed->getLedgerSeq());
|
||||
l["hash"] = lpClosed->getHash().GetHex();
|
||||
l["validated"] = valid;
|
||||
if (!human)
|
||||
{
|
||||
l["base_fee"] = Json::Value::UInt(baseFee);
|
||||
l["reserve_base"] = Json::Value::UInt(lpClosed->getReserve(0));
|
||||
l["reserve_inc"] = Json::Value::UInt(lpClosed->getReserveInc());
|
||||
l["close_time"] = Json::Value::UInt(lpClosed->getCloseTimeNC());
|
||||
}
|
||||
else
|
||||
{
|
||||
l["base_fee_xrp"] = static_cast<double>(Json::UInt(baseFee)) / SYSTEM_CURRENCY_PARTS;
|
||||
l["reserve_base_xrp"] =
|
||||
static_cast<double>(Json::UInt(lpClosed->getReserve(0) * baseFee / baseRef)) / SYSTEM_CURRENCY_PARTS;
|
||||
l["reserve_inc_xrp"] =
|
||||
static_cast<double>(Json::UInt(lpClosed->getReserveInc() * baseFee / baseRef)) / SYSTEM_CURRENCY_PARTS;
|
||||
l["age"] = Json::UInt(getCloseTimeNC() - lpClosed->getCloseTimeNC());
|
||||
}
|
||||
info["closed_ledger"] = l;
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
@@ -1042,12 +1221,8 @@ void NetworkOPs::pubProposedTransaction(Ledger::ref lpCurrent, const SerializedT
|
||||
|
||||
void NetworkOPs::pubLedger(Ledger::ref lpAccepted)
|
||||
{
|
||||
// Don't publish to clients ledgers we don't trust.
|
||||
// TODO: we need to publish old transactions when we get reconnected to the network otherwise clients can miss transactions
|
||||
if (NetworkOPs::omDISCONNECTED == getOperatingMode())
|
||||
return;
|
||||
|
||||
LoadEvent::autoptr event(theApp->getJobQueue().getLoadEventAP(jtPUBLEDGER));
|
||||
// Ledgers are published only when they acquire sufficient validations
|
||||
// Holes are filled across connection loss or other catastrophe
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mMonitorLock);
|
||||
@@ -1061,6 +1236,11 @@ void NetworkOPs::pubLedger(Ledger::ref lpAccepted)
|
||||
jvObj["ledger_hash"] = lpAccepted->getHash().ToString();
|
||||
jvObj["ledger_time"] = Json::Value::UInt(utFromSeconds(lpAccepted->getCloseTimeNC()));
|
||||
|
||||
jvObj["fee_ref"] = Json::UInt(lpAccepted->getReferenceFeeUnits());
|
||||
jvObj["fee_base"] = Json::UInt(lpAccepted->getBaseFee());
|
||||
jvObj["reserve_base"] = Json::UInt(lpAccepted->getReserve(0));
|
||||
jvObj["reserve_inc"] = Json::UInt(lpAccepted->getReserveInc());
|
||||
|
||||
BOOST_FOREACH(InfoSub* ispListener, mSubLedger)
|
||||
{
|
||||
ispListener->send(jvObj);
|
||||
@@ -1068,26 +1248,24 @@ void NetworkOPs::pubLedger(Ledger::ref lpAccepted)
|
||||
}
|
||||
}
|
||||
|
||||
// Don't lock since pubAcceptedTransaction is locking.
|
||||
if (!mSubTransactions.empty() || !mSubRTTransactions.empty() || !mSubAccount.empty() || !mSubRTAccount.empty() || !mSubmitMap.empty() )
|
||||
{
|
||||
// we don't lock since pubAcceptedTransaction is locking
|
||||
if (!mSubTransactions.empty() || !mSubRTTransactions.empty() || !mSubAccount.empty() || !mSubRTAccount.empty() || !mSubmitMap.empty() )
|
||||
SHAMap& txSet = *lpAccepted->peekTransactionMap();
|
||||
|
||||
for (SHAMapItem::pointer item = txSet.peekFirstItem(); !!item; item = txSet.peekNextItem(item->getTag()))
|
||||
{
|
||||
SHAMap& txSet = *lpAccepted->peekTransactionMap();
|
||||
SerializerIterator it(item->peekSerializer());
|
||||
|
||||
for (SHAMapItem::pointer item = txSet.peekFirstItem(); !!item; item = txSet.peekNextItem(item->getTag()))
|
||||
{
|
||||
SerializerIterator it(item->peekSerializer());
|
||||
// OPTIMIZEME: Could get transaction from txn master, but still must call getVL
|
||||
Serializer txnSer(it.getVL());
|
||||
SerializerIterator txnIt(txnSer);
|
||||
SerializedTransaction stTxn(txnIt);
|
||||
|
||||
// OPTIMIZEME: Could get transaction from txn master, but still must call getVL
|
||||
Serializer txnSer(it.getVL());
|
||||
SerializerIterator txnIt(txnSer);
|
||||
SerializedTransaction stTxn(txnIt);
|
||||
TransactionMetaSet::pointer meta = boost::make_shared<TransactionMetaSet>(
|
||||
stTxn.getTransactionID(), lpAccepted->getLedgerSeq(), it.getVL());
|
||||
|
||||
TransactionMetaSet::pointer meta = boost::make_shared<TransactionMetaSet>(
|
||||
stTxn.getTransactionID(), lpAccepted->getLedgerSeq(), it.getVL());
|
||||
|
||||
pubAcceptedTransaction(lpAccepted, stTxn, meta->getResultTER(), meta);
|
||||
}
|
||||
pubAcceptedTransaction(lpAccepted, stTxn, meta->getResultTER(), meta);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1122,10 +1300,12 @@ Json::Value NetworkOPs::transJson(const SerializedTransaction& stTxn, TER terRes
|
||||
void NetworkOPs::pubAcceptedTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult,TransactionMetaSet::pointer& meta)
|
||||
{
|
||||
Json::Value jvObj = transJson(stTxn, terResult, true, lpCurrent, "transaction");
|
||||
if(meta) jvObj["meta"]=meta->getJson(0);
|
||||
|
||||
if (meta) jvObj["meta"] = meta->getJson(0);
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mMonitorLock);
|
||||
|
||||
BOOST_FOREACH(InfoSub* ispListener, mSubTransactions)
|
||||
{
|
||||
ispListener->send(jvObj);
|
||||
@@ -1137,25 +1317,24 @@ void NetworkOPs::pubAcceptedTransaction(Ledger::ref lpCurrent, const SerializedT
|
||||
}
|
||||
}
|
||||
|
||||
pubAccountTransaction(lpCurrent,stTxn,terResult,true,meta);
|
||||
pubAccountTransaction(lpCurrent, stTxn, terResult, true, meta);
|
||||
}
|
||||
|
||||
|
||||
void NetworkOPs::pubAccountTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult, bool bAccepted,TransactionMetaSet::pointer& meta)
|
||||
void NetworkOPs::pubAccountTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult, bool bAccepted, TransactionMetaSet::pointer& meta)
|
||||
{
|
||||
boost::unordered_set<InfoSub*> notify;
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mMonitorLock);
|
||||
|
||||
if(!bAccepted && mSubRTAccount.empty()) return;
|
||||
if (!bAccepted && mSubRTAccount.empty()) return;
|
||||
|
||||
if (!mSubAccount.empty() || (!mSubRTAccount.empty()) )
|
||||
{
|
||||
typedef const std::pair<RippleAddress,bool> AccountPair;
|
||||
BOOST_FOREACH(AccountPair& affectedAccount, getAffectedAccounts(stTxn))
|
||||
std::vector<RippleAddress> accounts = meta ? meta->getAffectedAccounts() : stTxn.getMentionedAccounts();
|
||||
BOOST_FOREACH(const RippleAddress& affectedAccount, accounts)
|
||||
{
|
||||
subInfoMapIterator simiIt = mSubRTAccount.find(affectedAccount.first.getAccountID());
|
||||
subInfoMapIterator simiIt = mSubRTAccount.find(affectedAccount.getAccountID());
|
||||
|
||||
if (simiIt != mSubRTAccount.end())
|
||||
{
|
||||
@@ -1164,9 +1343,10 @@ void NetworkOPs::pubAccountTransaction(Ledger::ref lpCurrent, const SerializedTr
|
||||
notify.insert(ispListener);
|
||||
}
|
||||
}
|
||||
if(bAccepted)
|
||||
|
||||
if (bAccepted)
|
||||
{
|
||||
simiIt = mSubAccount.find(affectedAccount.first.getAccountID());
|
||||
simiIt = mSubAccount.find(affectedAccount.getAccountID());
|
||||
|
||||
if (simiIt != mSubAccount.end())
|
||||
{
|
||||
@@ -1180,10 +1360,12 @@ void NetworkOPs::pubAccountTransaction(Ledger::ref lpCurrent, const SerializedTr
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: This can crash. An InfoSub can go away while we hold a regular pointer to it.
|
||||
if (!notify.empty())
|
||||
{
|
||||
Json::Value jvObj = transJson(stTxn, terResult, bAccepted, lpCurrent, "account");
|
||||
if(meta) jvObj["meta"]=meta->getJson(0);
|
||||
|
||||
if (meta) jvObj["meta"] = meta->getJson(0);
|
||||
|
||||
BOOST_FOREACH(InfoSub* ispListener, notify)
|
||||
{
|
||||
@@ -1192,45 +1374,19 @@ void NetworkOPs::pubAccountTransaction(Ledger::ref lpCurrent, const SerializedTr
|
||||
}
|
||||
}
|
||||
|
||||
// JED: I know this is sort of ugly. I'm going to rework this to get the affected accounts in a different way when we want finer granularity than just "account"
|
||||
std::map<RippleAddress,bool> NetworkOPs::getAffectedAccounts(const SerializedTransaction& stTxn)
|
||||
{
|
||||
std::map<RippleAddress,bool> accounts;
|
||||
|
||||
BOOST_FOREACH(const SerializedType& it, stTxn.peekData())
|
||||
{
|
||||
const STAccount* sa = dynamic_cast<const STAccount*>(&it);
|
||||
if (sa)
|
||||
{
|
||||
RippleAddress na = sa->getValueNCA();
|
||||
accounts[na]=true;
|
||||
}else
|
||||
{
|
||||
if( it.getFName() == sfLimitAmount )
|
||||
{
|
||||
const STAmount* amount = dynamic_cast<const STAmount*>(&it);
|
||||
if(amount)
|
||||
{
|
||||
RippleAddress na;
|
||||
na.setAccountID(amount->getIssuer());
|
||||
accounts[na]=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return accounts;
|
||||
}
|
||||
|
||||
//
|
||||
// Monitoring
|
||||
//
|
||||
|
||||
|
||||
|
||||
void NetworkOPs::subAccount(InfoSub* ispListener, const boost::unordered_set<RippleAddress>& vnaAccountIDs,bool rt)
|
||||
void NetworkOPs::subAccount(InfoSub* ispListener, const boost::unordered_set<RippleAddress>& vnaAccountIDs, uint32 uLedgerIndex, bool rt)
|
||||
{
|
||||
subInfoMapType& subMap=mSubAccount;
|
||||
if(rt) subMap=mSubRTAccount;
|
||||
subInfoMapType& subMap = rt ? mSubRTAccount : mSubAccount;
|
||||
|
||||
// For the connection, monitor each account.
|
||||
BOOST_FOREACH(const RippleAddress& naAccountID, vnaAccountIDs)
|
||||
{
|
||||
ispListener->insertSubAccountInfo(naAccountID, uLedgerIndex);
|
||||
}
|
||||
|
||||
boost::recursive_mutex::scoped_lock sl(mMonitorLock);
|
||||
|
||||
@@ -1239,7 +1395,7 @@ void NetworkOPs::subAccount(InfoSub* ispListener, const boost::unordered_set<Rip
|
||||
subInfoMapType::iterator simIterator = subMap.find(naAccountID.getAccountID());
|
||||
if (simIterator == subMap.end())
|
||||
{
|
||||
// Not found
|
||||
// Not found, note that account has a new single listner.
|
||||
boost::unordered_set<InfoSub*> usisElement;
|
||||
|
||||
usisElement.insert(ispListener);
|
||||
@@ -1247,21 +1403,30 @@ void NetworkOPs::subAccount(InfoSub* ispListener, const boost::unordered_set<Rip
|
||||
}
|
||||
else
|
||||
{
|
||||
// Found
|
||||
// Found, note that the account has another listener.
|
||||
simIterator->second.insert(ispListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkOPs::unsubAccount(InfoSub* ispListener, const boost::unordered_set<RippleAddress>& vnaAccountIDs,bool rt)
|
||||
void NetworkOPs::unsubAccount(InfoSub* ispListener, const boost::unordered_set<RippleAddress>& vnaAccountIDs, bool rt)
|
||||
{
|
||||
subInfoMapType& subMap= rt ? mSubRTAccount : mSubAccount;
|
||||
subInfoMapType& subMap = rt ? mSubRTAccount : mSubAccount;
|
||||
|
||||
// For the connection, unmonitor each account.
|
||||
// FIXME: Don't we need to unsub?
|
||||
// BOOST_FOREACH(const RippleAddress& naAccountID, vnaAccountIDs)
|
||||
// {
|
||||
// ispListener->deleteSubAccountInfo(naAccountID);
|
||||
// }
|
||||
|
||||
boost::recursive_mutex::scoped_lock sl(mMonitorLock);
|
||||
|
||||
BOOST_FOREACH(const RippleAddress& naAccountID, vnaAccountIDs)
|
||||
{
|
||||
subInfoMapType::iterator simIterator = subMap.find(naAccountID.getAccountID());
|
||||
|
||||
|
||||
if (simIterator == mSubAccount.end())
|
||||
{
|
||||
// Not found. Done.
|
||||
@@ -1304,6 +1469,17 @@ void NetworkOPs::storeProposal(const LedgerProposal::pointer& proposal, const Ri
|
||||
props.push_back(proposal);
|
||||
}
|
||||
|
||||
InfoSub::~InfoSub()
|
||||
{
|
||||
NetworkOPs& ops = theApp->getOPs();
|
||||
ops.unsubTransactions(this);
|
||||
ops.unsubRTTransactions(this);
|
||||
ops.unsubLedger(this);
|
||||
ops.unsubServer(this);
|
||||
ops.unsubAccount(this, mSubAccountInfo, true);
|
||||
ops.unsubAccount(this, mSubAccountInfo, false);
|
||||
}
|
||||
|
||||
#if 0
|
||||
void NetworkOPs::subAccountChanges(InfoSub* ispListener, const uint256 uLedgerHash)
|
||||
{
|
||||
@@ -1317,9 +1493,16 @@ void NetworkOPs::unsubAccountChanges(InfoSub* ispListener)
|
||||
// <-- bool: true=added, false=already there
|
||||
bool NetworkOPs::subLedger(InfoSub* ispListener, Json::Value& jvResult)
|
||||
{
|
||||
jvResult["ledger_index"] = getClosedLedger()->getLedgerSeq();
|
||||
jvResult["ledger_hash"] = getClosedLedger()->getHash().ToString();
|
||||
jvResult["ledger_time"] = Json::Value::UInt(utFromSeconds(getClosedLedger()->getCloseTimeNC()));
|
||||
Ledger::pointer lpClosed = getClosedLedger();
|
||||
|
||||
jvResult["ledger_index"] = lpClosed->getLedgerSeq();
|
||||
jvResult["ledger_hash"] = lpClosed->getHash().ToString();
|
||||
jvResult["ledger_time"] = Json::Value::UInt(utFromSeconds(lpClosed->getCloseTimeNC()));
|
||||
|
||||
jvResult["fee_ref"] = Json::UInt(lpClosed->getReferenceFeeUnits());
|
||||
jvResult["fee_base"] = Json::UInt(lpClosed->getBaseFee());
|
||||
jvResult["reserve_base"] = Json::UInt(lpClosed->getReserve(0));
|
||||
jvResult["reserve_inc"] = Json::UInt(lpClosed->getReserveInc());
|
||||
|
||||
return mSubLedger.insert(ispListener).second;
|
||||
}
|
||||
@@ -1335,10 +1518,17 @@ bool NetworkOPs::subServer(InfoSub* ispListener, Json::Value& jvResult)
|
||||
{
|
||||
uint256 uRandom;
|
||||
|
||||
jvResult["stand_alone"] = theConfig.RUN_STANDALONE;
|
||||
if (theConfig.RUN_STANDALONE)
|
||||
jvResult["stand_alone"] = theConfig.RUN_STANDALONE;
|
||||
|
||||
if (theConfig.TESTNET)
|
||||
jvResult["testnet"] = theConfig.TESTNET;
|
||||
|
||||
getRand(uRandom.begin(), uRandom.size());
|
||||
jvResult["random"] = uRandom.ToString();
|
||||
jvResult["random"] = uRandom.ToString();
|
||||
jvResult["server_status"] = strOperatingMode();
|
||||
jvResult["load_base"] = theApp->getFeeTrack().getLoadBase();
|
||||
jvResult["load_factor"] = theApp->getFeeTrack().getLoadFactor();
|
||||
|
||||
return mSubServer.insert(ispListener).second;
|
||||
}
|
||||
@@ -1373,4 +1563,34 @@ bool NetworkOPs::unsubRTTransactions(InfoSub* ispListener)
|
||||
return !!mSubTransactions.erase(ispListener);
|
||||
}
|
||||
|
||||
RPCSub* NetworkOPs::findRpcSub(const std::string& strUrl)
|
||||
{
|
||||
RPCSub* rspResult;
|
||||
boost::recursive_mutex::scoped_lock sl(mMonitorLock);
|
||||
|
||||
subRpcMapType::iterator it;
|
||||
|
||||
it = mRpcSubMap.find(strUrl);
|
||||
if (it == mRpcSubMap.end())
|
||||
{
|
||||
rspResult = (RPCSub*)(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
rspResult = it->second;
|
||||
}
|
||||
|
||||
return rspResult;
|
||||
}
|
||||
|
||||
RPCSub* NetworkOPs::addRpcSub(const std::string& strUrl, RPCSub* rspEntry)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mMonitorLock);
|
||||
|
||||
mRpcSubMap.insert(std::make_pair(strUrl, rspEntry));
|
||||
|
||||
return rspEntry;
|
||||
}
|
||||
|
||||
|
||||
// vim:ts=4
|
||||
|
||||
@@ -22,24 +22,28 @@ class LedgerConsensus;
|
||||
|
||||
DEFINE_INSTANCE(InfoSub);
|
||||
|
||||
class RPCSub;
|
||||
|
||||
class InfoSub : public IS_INSTANCE(InfoSub)
|
||||
{
|
||||
public:
|
||||
|
||||
virtual ~InfoSub() { ; }
|
||||
|
||||
virtual void send(const Json::Value& jvObj) = 0;
|
||||
|
||||
protected:
|
||||
boost::unordered_set<RippleAddress> mSubAccountInfo;
|
||||
boost::unordered_set<RippleAddress> mSubAccountTransaction;
|
||||
|
||||
boost::mutex mLock;
|
||||
boost::mutex mLockInfo;
|
||||
|
||||
public:
|
||||
void insertSubAccountInfo(RippleAddress addr)
|
||||
|
||||
virtual ~InfoSub();
|
||||
|
||||
virtual void send(const Json::Value& jvObj) = 0;
|
||||
|
||||
void onSendEmpty();
|
||||
|
||||
void insertSubAccountInfo(RippleAddress addr, uint32 uLedgerIndex)
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mLock);
|
||||
boost::mutex::scoped_lock sl(mLockInfo);
|
||||
|
||||
mSubAccountInfo.insert(addr);
|
||||
}
|
||||
};
|
||||
@@ -68,8 +72,11 @@ protected:
|
||||
|
||||
typedef boost::unordered_map<uint160,std::pair<InfoSub*,uint32> > subSubmitMapType;
|
||||
|
||||
typedef boost::unordered_map<std::string, RPCSub* > subRpcMapType;
|
||||
|
||||
OperatingMode mMode;
|
||||
bool mNeedNetworkLedger;
|
||||
bool mProposing, mValidating;
|
||||
boost::posix_time::ptime mConnectTime;
|
||||
boost::asio::deadline_timer mNetTimer;
|
||||
boost::shared_ptr<LedgerConsensus> mConsensus;
|
||||
@@ -88,6 +95,8 @@ protected:
|
||||
uint32 mLastValidationTime;
|
||||
SerializedValidation::pointer mLastValidation;
|
||||
|
||||
// Recent positions taken
|
||||
std::map<uint256, std::pair<int, SHAMap::pointer> > mRecentPositions;
|
||||
|
||||
// XXX Split into more locks.
|
||||
boost::recursive_mutex mMonitorLock;
|
||||
@@ -95,11 +104,15 @@ protected:
|
||||
subInfoMapType mSubRTAccount;
|
||||
subSubmitMapType mSubmitMap; // TODO: probably dump this
|
||||
|
||||
subRpcMapType mRpcSubMap;
|
||||
|
||||
boost::unordered_set<InfoSub*> mSubLedger; // accepted ledgers
|
||||
boost::unordered_set<InfoSub*> mSubServer; // when server changes connectivity state
|
||||
boost::unordered_set<InfoSub*> mSubTransactions; // all accepted transactions
|
||||
boost::unordered_set<InfoSub*> mSubRTTransactions; // all proposed and accepted transactions
|
||||
|
||||
boost::recursive_mutex mWantedHashLock;
|
||||
boost::unordered_set<uint256> mWantedHashes;
|
||||
|
||||
void setMode(OperatingMode);
|
||||
|
||||
@@ -110,7 +123,8 @@ protected:
|
||||
|
||||
void pubAcceptedTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult,TransactionMetaSet::pointer& meta);
|
||||
void pubAccountTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult,bool accepted,TransactionMetaSet::pointer& meta);
|
||||
std::map<RippleAddress,bool> getAffectedAccounts(const SerializedTransaction& stTxn);
|
||||
|
||||
void pubServer();
|
||||
|
||||
public:
|
||||
NetworkOPs(boost::asio::io_service& io_service, LedgerMaster* pLedgerMaster);
|
||||
@@ -126,8 +140,9 @@ public:
|
||||
OperatingMode getOperatingMode() { return mMode; }
|
||||
std::string strOperatingMode();
|
||||
|
||||
Ledger::pointer getClosedLedger() { return mLedgerMaster->getClosedLedger(); }
|
||||
Ledger::pointer getCurrentLedger() { return mLedgerMaster->getCurrentLedger(); }
|
||||
Ledger::ref getClosedLedger() { return mLedgerMaster->getClosedLedger(); }
|
||||
Ledger::ref getValidatedLedger() { return mLedgerMaster->getValidatedLedger(); }
|
||||
Ledger::ref getCurrentLedger() { return mLedgerMaster->getCurrentLedger(); }
|
||||
Ledger::pointer getLedgerByHash(const uint256& hash) { return mLedgerMaster->getLedgerByHash(hash); }
|
||||
Ledger::pointer getLedgerBySeq(const uint32 seq) { return mLedgerMaster->getLedgerBySeq(seq); }
|
||||
|
||||
@@ -146,8 +161,9 @@ public:
|
||||
//
|
||||
typedef boost::function<void (Transaction::pointer, TER)> stCallback; // must complete immediately
|
||||
void submitTransaction(Job&, SerializedTransaction::pointer, stCallback callback = stCallback());
|
||||
Transaction::pointer submitTransactionSync(const Transaction::pointer& tpTrans);
|
||||
Transaction::pointer submitTransactionSync(const Transaction::pointer& tpTrans, bool bSubmit=true);
|
||||
|
||||
void runTransactionQueue();
|
||||
Transaction::pointer processTransaction(Transaction::pointer, stCallback);
|
||||
Transaction::pointer processTransaction(Transaction::pointer transaction)
|
||||
{ return processTransaction(transaction, stCallback()); }
|
||||
@@ -206,6 +222,7 @@ public:
|
||||
SMAddNode gotTXData(const boost::shared_ptr<Peer>& peer, const uint256& hash,
|
||||
const std::list<SHAMapNode>& nodeIDs, const std::list< std::vector<unsigned char> >& nodeData);
|
||||
bool recvValidation(const SerializedValidation::pointer& val);
|
||||
void takePosition(int seq, SHAMap::ref position);
|
||||
SHAMap::pointer getTXMap(const uint256& hash);
|
||||
bool hasTXSet(const boost::shared_ptr<Peer>& peer, const uint256& set, ripple::TxSetStatus status);
|
||||
void mapComplete(const uint256& hash, SHAMap::ref map);
|
||||
@@ -222,18 +239,24 @@ public:
|
||||
void needNetworkLedger() { mNeedNetworkLedger = true; }
|
||||
void clearNeedNetworkLedger() { mNeedNetworkLedger = false; }
|
||||
bool isNeedNetworkLedger() { return mNeedNetworkLedger; }
|
||||
void setProposing(bool p, bool v) { mProposing = p; mValidating = v; }
|
||||
bool isProposing() { return mProposing; }
|
||||
bool isValidating() { return mValidating; }
|
||||
void consensusViewChange();
|
||||
int getPreviousProposers() { return mLastCloseProposers; }
|
||||
int getPreviousConvergeTime() { return mLastCloseConvergeTime; }
|
||||
uint32 getLastCloseTime() { return mLastCloseTime; }
|
||||
void setLastCloseTime(uint32 t) { mLastCloseTime = t; }
|
||||
Json::Value getServerInfo();
|
||||
Json::Value getServerInfo(bool human, bool admin);
|
||||
uint32 acceptLedger();
|
||||
boost::unordered_map<uint160,
|
||||
std::list<LedgerProposal::pointer> >& peekStoredProposals() { return mStoredProposals; }
|
||||
void storeProposal(const LedgerProposal::pointer& proposal, const RippleAddress& peerPublic);
|
||||
uint256 getConsensusLCL();
|
||||
|
||||
bool addWantedHash(const uint256& h);
|
||||
bool isWantedHash(const uint256& h, bool remove);
|
||||
|
||||
// client information retrieval functions
|
||||
std::vector< std::pair<Transaction::pointer, TransactionMetaSet::pointer> >
|
||||
getAccountTxs(const RippleAddress& account, uint32 minLedger, uint32 maxLedger);
|
||||
@@ -250,8 +273,8 @@ public:
|
||||
//
|
||||
// Monitoring: subscriber side
|
||||
//
|
||||
void subAccount(InfoSub* ispListener, const boost::unordered_set<RippleAddress>& vnaAccountIDs,bool rt);
|
||||
void unsubAccount(InfoSub* ispListener, const boost::unordered_set<RippleAddress>& vnaAccountIDs,bool rt);
|
||||
void subAccount(InfoSub* ispListener, const boost::unordered_set<RippleAddress>& vnaAccountIDs, uint32 uLedgerIndex, bool rt);
|
||||
void unsubAccount(InfoSub* ispListener, const boost::unordered_set<RippleAddress>& vnaAccountIDs, bool rt);
|
||||
|
||||
bool subLedger(InfoSub* ispListener, Json::Value& jvResult);
|
||||
bool unsubLedger(InfoSub* ispListener);
|
||||
@@ -264,6 +287,9 @@ public:
|
||||
|
||||
bool subRTTransactions(InfoSub* ispListener);
|
||||
bool unsubRTTransactions(InfoSub* ispListener);
|
||||
|
||||
RPCSub* findRpcSub(const std::string& strUrl);
|
||||
RPCSub* addRpcSub(const std::string& strUrl, RPCSub* rspEntry);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -13,4 +13,6 @@ Offer::Offer(SerializedLedgerEntry::pointer ledgerEntry) : AccountItem(ledgerEnt
|
||||
mTakerGets = mLedgerEntry->getFieldAmount(sfTakerGets);
|
||||
mTakerPays = mLedgerEntry->getFieldAmount(sfTakerPays);
|
||||
mSeq = mLedgerEntry->getFieldU32(sfSequence);
|
||||
}
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "AccountItems.h"
|
||||
|
||||
|
||||
class Offer : public AccountItem
|
||||
{
|
||||
RippleAddress mAccount;
|
||||
@@ -20,4 +19,6 @@ public:
|
||||
RippleAddress getAccount(){ return(mAccount); }
|
||||
int getSeq(){ return(mSeq); }
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
// vim:ts=4
|
||||
|
||||
@@ -1,17 +1,28 @@
|
||||
#include "OfferCancelTransactor.h"
|
||||
#include "Log.h"
|
||||
|
||||
SETUP_LOG();
|
||||
|
||||
TER OfferCancelTransactor::doApply()
|
||||
{
|
||||
TER terResult;
|
||||
const uint32 uOfferSequence = mTxn.getFieldU32(sfOfferSequence);
|
||||
const uint32 uAccountSequenceNext = mTxnAccount->getFieldU32(sfSequence);
|
||||
|
||||
Log(lsDEBUG) << "doOfferCancel: uAccountSequenceNext=" << uAccountSequenceNext << " uOfferSequence=" << uOfferSequence;
|
||||
cLog(lsDEBUG) << "OfferCancel: uAccountSequenceNext=" << uAccountSequenceNext << " uOfferSequence=" << uOfferSequence;
|
||||
|
||||
const uint32 uTxFlags = mTxn.getFlags();
|
||||
|
||||
if (uTxFlags)
|
||||
{
|
||||
cLog(lsINFO) << "OfferCancel: Malformed transaction: Invalid flags set.";
|
||||
|
||||
return temINVALID_FLAG;
|
||||
}
|
||||
|
||||
if (!uOfferSequence || uAccountSequenceNext-1 <= uOfferSequence)
|
||||
{
|
||||
Log(lsINFO) << "doOfferCancel: uAccountSequenceNext=" << uAccountSequenceNext << " uOfferSequence=" << uOfferSequence;
|
||||
cLog(lsINFO) << "OfferCancel: uAccountSequenceNext=" << uAccountSequenceNext << " uOfferSequence=" << uOfferSequence;
|
||||
|
||||
terResult = temBAD_SEQUENCE;
|
||||
}
|
||||
@@ -22,13 +33,13 @@ TER OfferCancelTransactor::doApply()
|
||||
|
||||
if (sleOffer)
|
||||
{
|
||||
Log(lsWARNING) << "doOfferCancel: uOfferSequence=" << uOfferSequence;
|
||||
cLog(lsWARNING) << "OfferCancel: uOfferSequence=" << uOfferSequence;
|
||||
|
||||
terResult = mEngine->getNodes().offerDelete(sleOffer, uOfferIndex, mTxnAccountID);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log(lsWARNING) << "doOfferCancel: offer not found: "
|
||||
cLog(lsWARNING) << "OfferCancel: offer not found: "
|
||||
<< RippleAddress::createHumanAccountID(mTxnAccountID)
|
||||
<< " : " << uOfferSequence
|
||||
<< " : " << uOfferIndex.ToString();
|
||||
|
||||
@@ -4,6 +4,8 @@ class OfferCancelTransactor : public Transactor
|
||||
{
|
||||
public:
|
||||
OfferCancelTransactor(const SerializedTransaction& txn,TransactionEngineParams params, TransactionEngine* engine) : Transactor(txn,params,engine) {}
|
||||
|
||||
|
||||
TER doApply();
|
||||
};
|
||||
};
|
||||
|
||||
// vim:ts=4
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#include "Application.h"
|
||||
|
||||
#include "OfferCreateTransactor.h"
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
@@ -24,7 +26,7 @@ TER OfferCreateTransactor::takeOffers(
|
||||
{
|
||||
assert(saTakerPays && saTakerGets);
|
||||
|
||||
Log(lsINFO) << "takeOffers: against book: " << uBookBase.ToString();
|
||||
cLog(lsINFO) << "takeOffers: against book: " << uBookBase.ToString();
|
||||
|
||||
uint256 uTipIndex = uBookBase;
|
||||
const uint256 uBookEnd = Ledger::getQualityNext(uBookBase);
|
||||
@@ -53,14 +55,14 @@ TER OfferCreateTransactor::takeOffers(
|
||||
sleOfferDir = mEngine->entryCache(ltDIR_NODE, mEngine->getLedger()->getNextLedgerIndex(uTipIndex, uBookEnd));
|
||||
if (sleOfferDir)
|
||||
{
|
||||
Log(lsINFO) << "takeOffers: possible counter offer found";
|
||||
cLog(lsINFO) << "takeOffers: possible counter offer found";
|
||||
|
||||
uTipIndex = sleOfferDir->getIndex();
|
||||
uTipQuality = Ledger::getQuality(uTipIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log(lsINFO) << "takeOffers: counter offer book is empty: "
|
||||
cLog(lsINFO) << "takeOffers: counter offer book is empty: "
|
||||
<< uTipIndex.ToString()
|
||||
<< " ... "
|
||||
<< uBookEnd.ToString();
|
||||
@@ -72,14 +74,14 @@ TER OfferCreateTransactor::takeOffers(
|
||||
|| (bPassive && uTakeQuality == uTipQuality))
|
||||
{
|
||||
// Done.
|
||||
Log(lsINFO) << "takeOffers: done";
|
||||
cLog(lsINFO) << "takeOffers: done";
|
||||
|
||||
terResult = tesSUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Have an offer directory to consider.
|
||||
Log(lsINFO) << "takeOffers: considering dir: " << sleOfferDir->getJson(0);
|
||||
cLog(lsINFO) << "takeOffers: considering dir: " << sleOfferDir->getJson(0);
|
||||
|
||||
SLE::pointer sleBookNode;
|
||||
unsigned int uBookEntry;
|
||||
@@ -89,7 +91,7 @@ TER OfferCreateTransactor::takeOffers(
|
||||
|
||||
SLE::pointer sleOffer = mEngine->entryCache(ltOFFER, uOfferIndex);
|
||||
|
||||
Log(lsINFO) << "takeOffers: considering offer : " << sleOffer->getJson(0);
|
||||
cLog(lsINFO) << "takeOffers: considering offer : " << sleOffer->getJson(0);
|
||||
|
||||
const uint160 uOfferOwnerID = sleOffer->getFieldAccount(sfAccount).getAccountID();
|
||||
STAmount saOfferPays = sleOffer->getFieldAmount(sfTakerGets);
|
||||
@@ -98,14 +100,14 @@ TER OfferCreateTransactor::takeOffers(
|
||||
if (sleOffer->isFieldPresent(sfExpiration) && sleOffer->getFieldU32(sfExpiration) <= mEngine->getLedger()->getParentCloseTimeNC())
|
||||
{
|
||||
// Offer is expired. Expired offers are considered unfunded. Delete it.
|
||||
Log(lsINFO) << "takeOffers: encountered expired offer";
|
||||
cLog(lsINFO) << "takeOffers: encountered expired offer";
|
||||
|
||||
usOfferUnfundedFound.insert(uOfferIndex);
|
||||
}
|
||||
else if (uOfferOwnerID == uTakerAccountID)
|
||||
{
|
||||
// Would take own offer. Consider old offer expired. Delete it.
|
||||
Log(lsINFO) << "takeOffers: encountered taker's own old offer";
|
||||
cLog(lsINFO) << "takeOffers: encountered taker's own old offer";
|
||||
|
||||
usOfferUnfundedFound.insert(uOfferIndex);
|
||||
}
|
||||
@@ -113,16 +115,16 @@ TER OfferCreateTransactor::takeOffers(
|
||||
{
|
||||
// Get offer funds available.
|
||||
|
||||
Log(lsINFO) << "takeOffers: saOfferPays=" << saOfferPays.getFullText();
|
||||
cLog(lsINFO) << "takeOffers: saOfferPays=" << saOfferPays.getFullText();
|
||||
|
||||
STAmount saOfferFunds = mEngine->getNodes().accountFunds(uOfferOwnerID, saOfferPays, true);
|
||||
STAmount saTakerFunds = mEngine->getNodes().accountFunds(uTakerAccountID, saTakerPays, true);
|
||||
STAmount saOfferFunds = mEngine->getNodes().accountFunds(uOfferOwnerID, saOfferPays);
|
||||
STAmount saTakerFunds = mEngine->getNodes().accountFunds(uTakerAccountID, saTakerPays);
|
||||
SLE::pointer sleOfferAccount; // Owner of offer.
|
||||
|
||||
if (!saOfferFunds.isPositive())
|
||||
{
|
||||
// Offer is unfunded, possibly due to previous balance action.
|
||||
Log(lsINFO) << "takeOffers: offer unfunded: delete";
|
||||
cLog(lsINFO) << "takeOffers: offer unfunded: delete";
|
||||
|
||||
boost::unordered_set<uint160>::iterator account = usAccountTouched.find(uOfferOwnerID);
|
||||
if (account != usAccountTouched.end())
|
||||
@@ -145,33 +147,35 @@ TER OfferCreateTransactor::takeOffers(
|
||||
STAmount saSubTakerGot;
|
||||
STAmount saTakerIssuerFee;
|
||||
STAmount saOfferIssuerFee;
|
||||
STAmount saOfferRate = STAmount::setRate(uTipQuality);
|
||||
|
||||
Log(lsINFO) << "takeOffers: applyOffer: saTakerPays: " << saTakerPays.getFullText();
|
||||
Log(lsINFO) << "takeOffers: applyOffer: saTakerPaid: " << saTakerPaid.getFullText();
|
||||
Log(lsINFO) << "takeOffers: applyOffer: saTakerFunds: " << saTakerFunds.getFullText();
|
||||
Log(lsINFO) << "takeOffers: applyOffer: saOfferFunds: " << saOfferFunds.getFullText();
|
||||
Log(lsINFO) << "takeOffers: applyOffer: saPay: " << saPay.getFullText();
|
||||
Log(lsINFO) << "takeOffers: applyOffer: saOfferPays: " << saOfferPays.getFullText();
|
||||
Log(lsINFO) << "takeOffers: applyOffer: saOfferGets: " << saOfferGets.getFullText();
|
||||
Log(lsINFO) << "takeOffers: applyOffer: saTakerPays: " << saTakerPays.getFullText();
|
||||
Log(lsINFO) << "takeOffers: applyOffer: saTakerGets: " << saTakerGets.getFullText();
|
||||
cLog(lsINFO) << "takeOffers: applyOffer: saTakerPays: " << saTakerPays.getFullText();
|
||||
cLog(lsINFO) << "takeOffers: applyOffer: saTakerPaid: " << saTakerPaid.getFullText();
|
||||
cLog(lsINFO) << "takeOffers: applyOffer: saTakerFunds: " << saTakerFunds.getFullText();
|
||||
cLog(lsINFO) << "takeOffers: applyOffer: saOfferFunds: " << saOfferFunds.getFullText();
|
||||
cLog(lsINFO) << "takeOffers: applyOffer: saPay: " << saPay.getFullText();
|
||||
cLog(lsINFO) << "takeOffers: applyOffer: saOfferPays: " << saOfferPays.getFullText();
|
||||
cLog(lsINFO) << "takeOffers: applyOffer: saOfferGets: " << saOfferGets.getFullText();
|
||||
cLog(lsINFO) << "takeOffers: applyOffer: saOfferRate: " << saOfferRate.getFullText();
|
||||
cLog(lsINFO) << "takeOffers: applyOffer: saTakerPays: " << saTakerPays.getFullText();
|
||||
cLog(lsINFO) << "takeOffers: applyOffer: saTakerGets: " << saTakerGets.getFullText();
|
||||
|
||||
bool bOfferDelete = STAmount::applyOffer(
|
||||
mEngine->getNodes().rippleTransferRate(uTakerAccountID, uOfferOwnerID, uTakerPaysAccountID),
|
||||
mEngine->getNodes().rippleTransferRate(uOfferOwnerID, uTakerAccountID, uTakerGetsAccountID),
|
||||
saOfferRate,
|
||||
saOfferFunds,
|
||||
saPay, // Driver XXX need to account for fees.
|
||||
saPay,
|
||||
saOfferPays,
|
||||
saOfferGets,
|
||||
saTakerPays,
|
||||
saTakerGets,
|
||||
saSubTakerPaid,
|
||||
saSubTakerGot,
|
||||
saTakerIssuerFee,
|
||||
saOfferIssuerFee);
|
||||
|
||||
Log(lsINFO) << "takeOffers: applyOffer: saSubTakerPaid: " << saSubTakerPaid.getFullText();
|
||||
Log(lsINFO) << "takeOffers: applyOffer: saSubTakerGot: " << saSubTakerGot.getFullText();
|
||||
cLog(lsINFO) << "takeOffers: applyOffer: saSubTakerPaid: " << saSubTakerPaid.getFullText();
|
||||
cLog(lsINFO) << "takeOffers: applyOffer: saSubTakerGot: " << saSubTakerGot.getFullText();
|
||||
|
||||
// Adjust offer
|
||||
|
||||
@@ -186,7 +190,7 @@ TER OfferCreateTransactor::takeOffers(
|
||||
if (bOfferDelete)
|
||||
{
|
||||
// Offer now fully claimed or now unfunded.
|
||||
Log(lsINFO) << "takeOffers: offer claimed: delete";
|
||||
cLog(lsINFO) << "takeOffers: offer claimed: delete";
|
||||
|
||||
usOfferUnfundedBecame.insert(uOfferIndex); // Delete unfunded offer on success.
|
||||
|
||||
@@ -195,36 +199,48 @@ TER OfferCreateTransactor::takeOffers(
|
||||
}
|
||||
else
|
||||
{
|
||||
Log(lsINFO) << "takeOffers: offer partial claim.";
|
||||
cLog(lsINFO) << "takeOffers: offer partial claim.";
|
||||
}
|
||||
|
||||
assert(uTakerGetsAccountID == saSubTakerGot.getIssuer());
|
||||
assert(uTakerPaysAccountID == saSubTakerPaid.getIssuer());
|
||||
|
||||
// Offer owner pays taker.
|
||||
// saSubTakerGot.setIssuer(uTakerGetsAccountID); // XXX Move this earlier?
|
||||
assert(!!saSubTakerGot.getIssuer());
|
||||
|
||||
mEngine->getNodes().accountSend(uOfferOwnerID, uTakerAccountID, saSubTakerGot);
|
||||
mEngine->getNodes().accountSend(uOfferOwnerID, uTakerGetsAccountID, saOfferIssuerFee);
|
||||
|
||||
saTakerGot += saSubTakerGot;
|
||||
terResult = mEngine->getNodes().accountSend(uOfferOwnerID, uTakerAccountID, saSubTakerGot);
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
terResult = mEngine->getNodes().accountSend(uOfferOwnerID, uTakerGetsAccountID, saOfferIssuerFee);
|
||||
// Taker pays offer owner.
|
||||
// saSubTakerPaid.setIssuer(uTakerPaysAccountID);
|
||||
assert(!!saSubTakerPaid.getIssuer());
|
||||
|
||||
mEngine->getNodes().accountSend(uTakerAccountID, uOfferOwnerID, saSubTakerPaid);
|
||||
mEngine->getNodes().accountSend(uTakerAccountID, uTakerPaysAccountID, saTakerIssuerFee);
|
||||
if (tesSUCCESS == terResult)
|
||||
terResult = mEngine->getNodes().accountSend(uTakerAccountID, uOfferOwnerID, saSubTakerPaid);
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
terResult = mEngine->getNodes().accountSend(uTakerAccountID, uTakerPaysAccountID, saTakerIssuerFee);
|
||||
|
||||
saTakerGot += saSubTakerGot;
|
||||
saTakerPaid += saSubTakerPaid;
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
terResult = temUNCERTAIN;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cLog(lsINFO) << "takeOffers: " << transToken(terResult);
|
||||
|
||||
// On storing meta data, delete offers that were found unfunded to prevent encountering them in future.
|
||||
if (tesSUCCESS == terResult)
|
||||
{
|
||||
BOOST_FOREACH(const uint256& uOfferIndex, usOfferUnfundedFound)
|
||||
{
|
||||
|
||||
cLog(lsINFO) << "takeOffers: found unfunded: " << uOfferIndex.ToString();
|
||||
|
||||
terResult = mEngine->getNodes().offerDelete(uOfferIndex);
|
||||
if (tesSUCCESS != terResult)
|
||||
break;
|
||||
@@ -236,24 +252,28 @@ TER OfferCreateTransactor::takeOffers(
|
||||
// On success, delete offers that became unfunded.
|
||||
BOOST_FOREACH(const uint256& uOfferIndex, usOfferUnfundedBecame)
|
||||
{
|
||||
cLog(lsINFO) << "takeOffers: became unfunded: " << uOfferIndex.ToString();
|
||||
|
||||
terResult = mEngine->getNodes().offerDelete(uOfferIndex);
|
||||
if (tesSUCCESS != terResult)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cLog(lsINFO) << "takeOffers< " << transToken(terResult);
|
||||
|
||||
return terResult;
|
||||
}
|
||||
|
||||
TER OfferCreateTransactor::doApply()
|
||||
{
|
||||
Log(lsWARNING) << "doOfferCreate> " << mTxn.getJson(0);
|
||||
cLog(lsWARNING) << "OfferCreate> " << mTxn.getJson(0);
|
||||
const uint32 uTxFlags = mTxn.getFlags();
|
||||
const bool bPassive = isSetBit(uTxFlags, tfPassive);
|
||||
STAmount saTakerPays = mTxn.getFieldAmount(sfTakerPays);
|
||||
STAmount saTakerGets = mTxn.getFieldAmount(sfTakerGets);
|
||||
|
||||
Log(lsINFO) << boost::str(boost::format("doOfferCreate: saTakerPays=%s saTakerGets=%s")
|
||||
cLog(lsINFO) << boost::str(boost::format("OfferCreate: saTakerPays=%s saTakerGets=%s")
|
||||
% saTakerPays.getFullText()
|
||||
% saTakerGets.getFullText());
|
||||
|
||||
@@ -265,7 +285,7 @@ TER OfferCreateTransactor::doApply()
|
||||
|
||||
const uint256 uLedgerIndex = Ledger::getOfferIndex(mTxnAccountID, uSequence);
|
||||
|
||||
Log(lsINFO) << "doOfferCreate: Creating offer node: " << uLedgerIndex.ToString() << " uSequence=" << uSequence;
|
||||
cLog(lsINFO) << "OfferCreate: Creating offer node: " << uLedgerIndex.ToString() << " uSequence=" << uSequence;
|
||||
|
||||
const uint160 uPaysCurrency = saTakerPays.getCurrency();
|
||||
const uint160 uGetsCurrency = saTakerGets.getCurrency();
|
||||
@@ -278,51 +298,51 @@ TER OfferCreateTransactor::doApply()
|
||||
|
||||
if (uTxFlags & tfOfferCreateMask)
|
||||
{
|
||||
Log(lsINFO) << "doOfferCreate: Malformed transaction: Invalid flags set.";
|
||||
cLog(lsINFO) << "OfferCreate: Malformed transaction: Invalid flags set.";
|
||||
|
||||
return temINVALID_FLAG;
|
||||
}
|
||||
else if (bHaveExpiration && !uExpiration)
|
||||
{
|
||||
Log(lsWARNING) << "doOfferCreate: Malformed offer: bad expiration";
|
||||
cLog(lsWARNING) << "OfferCreate: Malformed offer: bad expiration";
|
||||
|
||||
terResult = temBAD_EXPIRATION;
|
||||
}
|
||||
else if (bHaveExpiration && mEngine->getLedger()->getParentCloseTimeNC() >= uExpiration)
|
||||
{
|
||||
Log(lsWARNING) << "doOfferCreate: Expired transaction: offer expired";
|
||||
cLog(lsWARNING) << "OfferCreate: Expired transaction: offer expired";
|
||||
|
||||
terResult = tesSUCCESS; // Only charged fee.
|
||||
}
|
||||
else if (saTakerPays.isNative() && saTakerGets.isNative())
|
||||
{
|
||||
Log(lsWARNING) << "doOfferCreate: Malformed offer: XRP for XRP";
|
||||
cLog(lsWARNING) << "OfferCreate: Malformed offer: XRP for XRP";
|
||||
|
||||
terResult = temBAD_OFFER;
|
||||
}
|
||||
else if (!saTakerPays.isPositive() || !saTakerGets.isPositive())
|
||||
{
|
||||
Log(lsWARNING) << "doOfferCreate: Malformed offer: bad amount";
|
||||
cLog(lsWARNING) << "OfferCreate: Malformed offer: bad amount";
|
||||
|
||||
terResult = temBAD_OFFER;
|
||||
}
|
||||
else if (uPaysCurrency == uGetsCurrency && uPaysIssuerID == uGetsIssuerID)
|
||||
{
|
||||
Log(lsWARNING) << "doOfferCreate: Malformed offer: redundant offer";
|
||||
cLog(lsWARNING) << "OfferCreate: Malformed offer: redundant offer";
|
||||
|
||||
terResult = temREDUNDANT;
|
||||
}
|
||||
else if (saTakerPays.isNative() != !uPaysIssuerID || saTakerGets.isNative() != !uGetsIssuerID)
|
||||
{
|
||||
Log(lsWARNING) << "doOfferCreate: Malformed offer: bad issuer";
|
||||
cLog(lsWARNING) << "OfferCreate: Malformed offer: bad issuer";
|
||||
|
||||
terResult = temBAD_ISSUER;
|
||||
}
|
||||
else if (!mEngine->getNodes().accountFunds(mTxnAccountID, saTakerGets, true).isPositive())
|
||||
else if (!mEngine->getNodes().accountFunds(mTxnAccountID, saTakerGets).isPositive())
|
||||
{
|
||||
Log(lsWARNING) << "doOfferCreate: delay: Offers must be at least partially funded.";
|
||||
cLog(lsWARNING) << "OfferCreate: delay: Offers must be at least partially funded.";
|
||||
|
||||
terResult = terUNFUNDED;
|
||||
terResult = tecUNFUNDED_OFFER;
|
||||
}
|
||||
|
||||
if (tesSUCCESS == terResult && !saTakerPays.isNative())
|
||||
@@ -331,25 +351,26 @@ TER OfferCreateTransactor::doApply()
|
||||
|
||||
if (!sleTakerPays)
|
||||
{
|
||||
Log(lsWARNING) << "doOfferCreate: delay: can't receive IOUs from non-existent issuer: " << RippleAddress::createHumanAccountID(uPaysIssuerID);
|
||||
cLog(lsWARNING) << "OfferCreate: delay: can't receive IOUs from non-existent issuer: " << RippleAddress::createHumanAccountID(uPaysIssuerID);
|
||||
|
||||
terResult = terNO_ACCOUNT;
|
||||
}
|
||||
}
|
||||
|
||||
STAmount saOfferPaid;
|
||||
STAmount saOfferGot;
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
{
|
||||
STAmount saOfferPaid;
|
||||
STAmount saOfferGot;
|
||||
const uint256 uTakeBookBase = Ledger::getBookBase(uGetsCurrency, uGetsIssuerID, uPaysCurrency, uPaysIssuerID);
|
||||
|
||||
Log(lsINFO) << boost::str(boost::format("doOfferCreate: take against book: %s for %s -> %s")
|
||||
cLog(lsINFO) << boost::str(boost::format("OfferCreate: take against book: %s for %s -> %s")
|
||||
% uTakeBookBase.ToString()
|
||||
% saTakerGets.getFullText()
|
||||
% saTakerPays.getFullText());
|
||||
|
||||
// Take using the parameters of the offer.
|
||||
Log(lsWARNING) << "doOfferCreate: takeOffers: BEFORE saTakerGets=" << saTakerGets.getFullText();
|
||||
cLog(lsWARNING) << "OfferCreate: takeOffers: BEFORE saTakerGets=" << saTakerGets.getFullText();
|
||||
terResult = takeOffers(
|
||||
bPassive,
|
||||
uTakeBookBase,
|
||||
@@ -361,11 +382,11 @@ TER OfferCreateTransactor::doApply()
|
||||
saOfferGot // How much was got.
|
||||
);
|
||||
|
||||
Log(lsWARNING) << "doOfferCreate: takeOffers=" << terResult;
|
||||
Log(lsWARNING) << "doOfferCreate: takeOffers: saOfferPaid=" << saOfferPaid.getFullText();
|
||||
Log(lsWARNING) << "doOfferCreate: takeOffers: saOfferGot=" << saOfferGot.getFullText();
|
||||
Log(lsWARNING) << "doOfferCreate: takeOffers: saTakerPays=" << saTakerPays.getFullText();
|
||||
Log(lsWARNING) << "doOfferCreate: takeOffers: AFTER saTakerGets=" << saTakerGets.getFullText();
|
||||
cLog(lsWARNING) << "OfferCreate: takeOffers=" << terResult;
|
||||
cLog(lsWARNING) << "OfferCreate: takeOffers: saOfferPaid=" << saOfferPaid.getFullText();
|
||||
cLog(lsWARNING) << "OfferCreate: takeOffers: saOfferGot=" << saOfferGot.getFullText();
|
||||
cLog(lsWARNING) << "OfferCreate: takeOffers: saTakerPays=" << saTakerPays.getFullText();
|
||||
cLog(lsWARNING) << "OfferCreate: takeOffers: AFTER saTakerGets=" << saTakerGets.getFullText();
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
{
|
||||
@@ -374,21 +395,47 @@ TER OfferCreateTransactor::doApply()
|
||||
}
|
||||
}
|
||||
|
||||
Log(lsWARNING) << "doOfferCreate: takeOffers: saTakerPays=" << saTakerPays.getFullText();
|
||||
Log(lsWARNING) << "doOfferCreate: takeOffers: saTakerGets=" << saTakerGets.getFullText();
|
||||
Log(lsWARNING) << "doOfferCreate: takeOffers: mTxnAccountID=" << RippleAddress::createHumanAccountID(mTxnAccountID);
|
||||
Log(lsWARNING) << "doOfferCreate: takeOffers: FUNDS=" << mEngine->getNodes().accountFunds(mTxnAccountID, saTakerGets, true).getFullText();
|
||||
cLog(lsWARNING) << "OfferCreate: takeOffers: saTakerPays=" << saTakerPays.getFullText();
|
||||
cLog(lsWARNING) << "OfferCreate: takeOffers: saTakerGets=" << saTakerGets.getFullText();
|
||||
cLog(lsWARNING) << "OfferCreate: takeOffers: mTxnAccountID=" << RippleAddress::createHumanAccountID(mTxnAccountID);
|
||||
cLog(lsWARNING) << "OfferCreate: takeOffers: FUNDS=" << mEngine->getNodes().accountFunds(mTxnAccountID, saTakerGets).getFullText();
|
||||
|
||||
// Log(lsWARNING) << "doOfferCreate: takeOffers: uPaysIssuerID=" << RippleAddress::createHumanAccountID(uPaysIssuerID);
|
||||
// Log(lsWARNING) << "doOfferCreate: takeOffers: uGetsIssuerID=" << RippleAddress::createHumanAccountID(uGetsIssuerID);
|
||||
// cLog(lsWARNING) << "OfferCreate: takeOffers: uPaysIssuerID=" << RippleAddress::createHumanAccountID(uPaysIssuerID);
|
||||
// cLog(lsWARNING) << "OfferCreate: takeOffers: uGetsIssuerID=" << RippleAddress::createHumanAccountID(uGetsIssuerID);
|
||||
|
||||
if (tesSUCCESS == terResult
|
||||
&& saTakerPays // Still wanting something.
|
||||
&& saTakerGets // Still offering something.
|
||||
&& mEngine->getNodes().accountFunds(mTxnAccountID, saTakerGets, true).isPositive()) // Still funded.
|
||||
if (tesSUCCESS != terResult
|
||||
|| !saTakerPays // Wants nothing more.
|
||||
|| !saTakerGets // Offering nothing more.
|
||||
|| !mEngine->getNodes().accountFunds(mTxnAccountID, saTakerGets).isPositive()) // Not funded.
|
||||
{
|
||||
// Complete as is.
|
||||
nothing();
|
||||
}
|
||||
else if (mTxnAccount->getFieldAmount(sfBalance).getNValue() < mEngine->getLedger()->getReserve(mTxnAccount->getFieldU32(sfOwnerCount)+1))
|
||||
{
|
||||
if (isSetBit(mParams, tapOPEN_LEDGER)) // Ledger is not final, can vote no.
|
||||
{
|
||||
// Hope for more reserve to come in or more offers to consume.
|
||||
terResult = tecINSUF_RESERVE_OFFER;
|
||||
}
|
||||
else if (!saOfferPaid && !saOfferGot)
|
||||
{
|
||||
// Ledger is final, insufficent reserve to create offer, processed nothing.
|
||||
|
||||
terResult = tecINSUF_RESERVE_OFFER;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ledger is final, insufficent reserve to create offer, processed something.
|
||||
|
||||
// Consider the offer unfunded. Treat as tesSUCCESS.
|
||||
nothing();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We need to place the remainder of the offer into its order book.
|
||||
Log(lsINFO) << boost::str(boost::format("doOfferCreate: offer not fully consumed: saTakerPays=%s saTakerGets=%s")
|
||||
cLog(lsINFO) << boost::str(boost::format("OfferCreate: offer not fully consumed: saTakerPays=%s saTakerGets=%s")
|
||||
% saTakerPays.getFullText()
|
||||
% saTakerGets.getFullText());
|
||||
|
||||
@@ -397,17 +444,14 @@ TER OfferCreateTransactor::doApply()
|
||||
boost::bind(&Ledger::qualityDirDescriber, _1, saTakerPays.getCurrency(), uPaysIssuerID,
|
||||
saTakerGets.getCurrency(), uGetsIssuerID, uRate));
|
||||
|
||||
// Update owner count.
|
||||
if (tesSUCCESS == terResult)
|
||||
{
|
||||
terResult = mEngine->getNodes().ownerCountAdjust(mTxnAccountID, 1, mTxnAccount);
|
||||
}
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
{
|
||||
mEngine->getNodes().ownerCountAdjust(mTxnAccountID, 1, mTxnAccount); // Update owner count.
|
||||
|
||||
uint256 uBookBase = Ledger::getBookBase(uPaysCurrency, uPaysIssuerID, uGetsCurrency, uGetsIssuerID);
|
||||
|
||||
Log(lsINFO) << boost::str(boost::format("doOfferCreate: adding to book: %s : %s/%s -> %s/%s")
|
||||
cLog(lsINFO) << boost::str(boost::format("OfferCreate: adding to book: %s : %s/%s -> %s/%s")
|
||||
% uBookBase.ToString()
|
||||
% saTakerPays.getHumanCurrency()
|
||||
% RippleAddress::createHumanAccountID(saTakerPays.getIssuer())
|
||||
@@ -424,13 +468,13 @@ TER OfferCreateTransactor::doApply()
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
{
|
||||
Log(lsWARNING) << "doOfferCreate: sfAccount=" << RippleAddress::createHumanAccountID(mTxnAccountID);
|
||||
Log(lsWARNING) << "doOfferCreate: uPaysIssuerID=" << RippleAddress::createHumanAccountID(uPaysIssuerID);
|
||||
Log(lsWARNING) << "doOfferCreate: uGetsIssuerID=" << RippleAddress::createHumanAccountID(uGetsIssuerID);
|
||||
Log(lsWARNING) << "doOfferCreate: saTakerPays.isNative()=" << saTakerPays.isNative();
|
||||
Log(lsWARNING) << "doOfferCreate: saTakerGets.isNative()=" << saTakerGets.isNative();
|
||||
Log(lsWARNING) << "doOfferCreate: uPaysCurrency=" << saTakerPays.getHumanCurrency();
|
||||
Log(lsWARNING) << "doOfferCreate: uGetsCurrency=" << saTakerGets.getHumanCurrency();
|
||||
cLog(lsWARNING) << "OfferCreate: sfAccount=" << RippleAddress::createHumanAccountID(mTxnAccountID);
|
||||
cLog(lsWARNING) << "OfferCreate: uPaysIssuerID=" << RippleAddress::createHumanAccountID(uPaysIssuerID);
|
||||
cLog(lsWARNING) << "OfferCreate: uGetsIssuerID=" << RippleAddress::createHumanAccountID(uGetsIssuerID);
|
||||
cLog(lsWARNING) << "OfferCreate: saTakerPays.isNative()=" << saTakerPays.isNative();
|
||||
cLog(lsWARNING) << "OfferCreate: saTakerGets.isNative()=" << saTakerGets.isNative();
|
||||
cLog(lsWARNING) << "OfferCreate: uPaysCurrency=" << saTakerPays.getHumanCurrency();
|
||||
cLog(lsWARNING) << "OfferCreate: uGetsCurrency=" << saTakerGets.getHumanCurrency();
|
||||
|
||||
SLE::pointer sleOffer = mEngine->entryCreate(ltOFFER, uLedgerIndex);
|
||||
|
||||
@@ -448,13 +492,13 @@ TER OfferCreateTransactor::doApply()
|
||||
if (bPassive)
|
||||
sleOffer->setFlag(lsfPassive);
|
||||
|
||||
Log(lsINFO) << boost::str(boost::format("doOfferCreate: final terResult=%s sleOffer=%s")
|
||||
cLog(lsINFO) << boost::str(boost::format("OfferCreate: final terResult=%s sleOffer=%s")
|
||||
% transToken(terResult)
|
||||
% sleOffer->getJson(0));
|
||||
}
|
||||
}
|
||||
|
||||
tLog(tesSUCCESS != terResult, lsINFO) << boost::str(boost::format("doOfferCreate: final terResult=%s") % transToken(terResult));
|
||||
tLog(tesSUCCESS != terResult, lsINFO) << boost::str(boost::format("OfferCreate: final terResult=%s") % transToken(terResult));
|
||||
|
||||
return terResult;
|
||||
}
|
||||
|
||||
@@ -19,4 +19,4 @@ public:
|
||||
TER doApply();
|
||||
};
|
||||
|
||||
|
||||
// vim:ts=4
|
||||
|
||||
@@ -305,7 +305,7 @@ public:
|
||||
bool work(Interpreter* interpreter)
|
||||
{
|
||||
Data::pointer index=interpreter->popStack();
|
||||
if(index->isInt32())
|
||||
if(index->isInt32())
|
||||
{
|
||||
interpreter->pushStack( interpreter->getContractData(index->getInt()));
|
||||
return(true);
|
||||
@@ -315,4 +315,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
|
||||
@@ -16,6 +16,7 @@ class OrderBook
|
||||
OrderBook(SerializedLedgerEntry::pointer ledgerEntry); // For accounts in a ledger
|
||||
public:
|
||||
typedef boost::shared_ptr<OrderBook> pointer;
|
||||
typedef const boost::shared_ptr<OrderBook>& ref;
|
||||
|
||||
// returns NULL if ledgerEntry doesn't point to an order
|
||||
// if ledgerEntry is an Order it creates the OrderBook this order would live in
|
||||
@@ -29,6 +30,6 @@ public:
|
||||
|
||||
// looks through the best offers to see how much it would cost to take the given amount
|
||||
STAmount& getTakePrice(STAmount& takeAmount);
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
// vim:ts=4
|
||||
|
||||
@@ -45,7 +45,7 @@ void OrderBookDB::getBooks(const uint160& issuerID, const uint160& currencyID, s
|
||||
{
|
||||
if( mIssuerMap.find(issuerID) == mIssuerMap.end() )
|
||||
{
|
||||
BOOST_FOREACH(OrderBook::pointer book, mIssuerMap[issuerID])
|
||||
BOOST_FOREACH(OrderBook::ref book, mIssuerMap[issuerID])
|
||||
{
|
||||
if(book->getCurrencyIn()==currencyID)
|
||||
{
|
||||
|
||||
@@ -27,4 +27,6 @@ public:
|
||||
// returns the best rate we can find
|
||||
float getPrice(uint160& currencyIn,uint160& currencyOut);
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
// vim:ts=4
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user