Merge branch 'master' of github.com:jedmccaleb/NewCoin

Conflicts:
	ripple2010.vcxproj.filters
	rippled-example.cfg
This commit is contained in:
jed
2013-01-26 13:18:10 -08:00
220 changed files with 14555 additions and 4050 deletions

10
.gitignore vendored
View File

@@ -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
View File

@@ -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
View 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

View File

@@ -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
View 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
View 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
View 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

View File

@@ -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

View File

@@ -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
View 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');
};

View File

@@ -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" />

View File

@@ -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
View 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

View File

@@ -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"

View File

@@ -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

View File

@@ -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" />

View File

@@ -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" />

View File

@@ -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

BIN
rippled.1 Normal file

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:rippled.xcodeproj">
</FileRef>
</Workspace>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<Bucket
type = "1"
version = "1.0">
</Bucket>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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__ */

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -6,4 +6,6 @@ public:
AccountSetTransactor(const SerializedTransaction& txn,TransactionEngineParams params, TransactionEngine* engine) : Transactor(txn,params,engine) {}
TER doApply();
};
};
// vim:ts=4

View File

@@ -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);

View File

@@ -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

View File

@@ -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
View 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
View 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);
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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);

View File

@@ -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

View File

@@ -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.
}

View File

@@ -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();

View File

@@ -32,4 +32,4 @@ void Contract::executeAccept()
//interpreter.interpret(this,code);
}
// vim:ts=4

View File

@@ -27,4 +27,6 @@ public:
void executeAccept();
};
#endif
#endif
// vim:ts=4

View File

@@ -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 ( \

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -1,6 +1,7 @@
#include "InstanceCounter.h"
InstanceType* InstanceType::sHeadInstance = NULL;
bool InstanceType::sMultiThreaded = false;
std::vector<InstanceType::InstanceCount> InstanceType::getInstanceCounts(int min)
{

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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
View 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

View File

@@ -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

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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)
{

View File

@@ -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);

View File

@@ -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

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -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

View File

@@ -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();

View File

@@ -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

View File

@@ -104,3 +104,5 @@ public:
};
#endif
// vim:ts=4

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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();

View File

@@ -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

View File

@@ -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;
}

View File

@@ -19,4 +19,4 @@ public:
TER doApply();
};
// vim:ts=4

View File

@@ -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

View File

@@ -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

View File

@@ -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)
{

View File

@@ -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