11 Commits

Author SHA1 Message Date
Andre Jochems
5f961bbcaf WalletServer update 2016-03-28 18:07:52 +02:00
Andre Jochems
336757fb55 WalletServer update 2016-03-28 17:49:06 +02:00
Andre Jochems
34c622e0f0 fixed struct initialisations 2016-03-26 11:20:40 +01:00
Andre Jochems
7622169321 fixed struct initialisations 2016-03-26 11:06:24 +01:00
Andre Jochems
dba5caeac4 WalletServer commands implemented. 2016-03-25 15:23:36 +01:00
ajochems
e39da3a52a Updated unix makefile 2016-02-16 08:43:00 +01:00
Andre Jochems
9892f8b147 missing include 2016-02-15 08:29:49 +01:00
Andre Jochems
6a849dad4e casesensitive header include 2016-02-15 08:25:11 +01:00
Andre Jochems
b0c11f8974 WalletServer class added 2016-02-13 17:53:18 +01:00
Andre Jochems
093b9ec1ff Bug fixed in undefined color definition 2015-12-14 12:27:57 +01:00
Andre Jochems
7497a0233e Added missig dll 2015-12-11 23:30:19 +01:00
49 changed files with 168066 additions and 20161 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -239,7 +239,12 @@ HEADERS += src/qt/bitcoingui.h \
src/qt/qtquick_controls/cpp/qmlexchangeslistmodel.h \
src/qt/qtquick_controls/cpp/qmlexchangeslistitem.h \
src/qt/qtquick_controls/cpp/guiexchangeslistview.h \
src/qt/qtquick_controls/cpp/guiexchangescontrol.h
src/qt/qtquick_controls/cpp/guiexchangescontrol.h \
src/walletserver.h \
src/stomp/helpers.h \
src/stomp/booststomp.h \
src/stomp/stompframe.h \
src/walletserversession.h
SOURCES += src/qt/bitcoin.cpp \
src/qt/bitcoingui.cpp \
@@ -340,7 +345,14 @@ SOURCES += src/qt/bitcoin.cpp \
src/qt/qtquick_controls/cpp/qmlexchangeslistmodel.cpp \
src/qt/qtquick_controls/cpp/qmlexchangeslistitem.cpp \
src/qt/qtquick_controls/cpp/guiexchangeslistview.cpp \
src/qt/qtquick_controls/cpp/guiexchangescontrol.cpp
src/qt/qtquick_controls/cpp/guiexchangescontrol.cpp \
src/walletserver.cpp \
src/stomp/helpers.cpp \
src/stomp/booststomp.cpp \
src/stomp/stompframe.cpp \
src/walletsession.cpp \
src/json/json_spirit_value.cpp \
src/walletserversession.cpp
RESOURCES += src/qt/bitcoin.qrc
@@ -455,6 +467,6 @@ LIBS += -lssl -lcrypto -ldb_cxx$$BDB_LIB_SUFFIX -lpthread
# -lgdi32 has to happen after -lcrypto (see #681)
LIBS += -lws2_32 -lole32 -lmswsock -loleaut32 -luuid -lgdi32 -lshlwapi
LIBS += -lboost_system$$BOOST_LIB_SUFFIX -lboost_filesystem$$BOOST_LIB_SUFFIX -lboost_program_options$$BOOST_LIB_SUFFIX -lboost_thread$$BOOST_THREAD_LIB_SUFFIX
LIBS += -lboost_chrono$$BOOST_LIB_SUFFIX
LIBS += -lboost_chrono$$BOOST_LIB_SUFFIX -lboost_serialization$$BOOST_LIB_SUFFIX
system($$QMAKE_LRELEASE -silent $$TRANSLATIONS)

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 3.5.0, 2015-12-01T22:30:06. -->
<!-- Written by QtCreator 3.5.0, 2016-03-24T23:27:30. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
@@ -120,7 +120,7 @@
<value type="bool" key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild">true</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.1">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">C:/Users/a.jochems/Documents/GitHub/casinocoin-GUI-2.0</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">C:/Users/a.jochems/Documents/GitHub/casinocoin-WalletServer</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
@@ -227,8 +227,8 @@
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">casinocoin-qt-windows</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4RunConfiguration:C:/Users/a.jochems/Documents/GitHub/casinocoin-GUI-2.0/casinocoin-qt-windows.pro</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">casinocoin-qt-windows2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4RunConfiguration:C:/Users/a.jochems/Documents/GitHub/casinocoin-WalletServer/casinocoin-qt-windows.pro</value>
<value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.CommandLineArguments"></value>
<value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.ProFile">casinocoin-qt-windows.pro</value>
<value type="bool" key="Qt4ProjectManager.Qt4RunConfiguration.UseDyldImageSuffix">false</value>

View File

@@ -0,0 +1,89 @@
version: 1
dn: cn=schema
changetype: modify
add: attributeTypes
attributeTypes: ( 1.3.6.1.4.1.37505.1.2
NAME 'gender'
EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1024} )
attributeTypes: ( 1.3.6.1.4.1.37505.1.3
NAME 'country'
EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1024} )
attributeTypes: ( 1.3.6.1.4.1.37505.1.4
NAME 'nickName'
EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1024} )
attributeTypes: ( 1.3.6.1.4.1.37505.1.5
NAME 'timeZone'
EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1024} )
attributeTypes: ( 1.3.6.1.4.1.37505.1.6
NAME 'dateOfBirth'
EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1024} )
attributeTypes: ( 1.3.6.1.4.1.37505.1.7
NAME 'role'
EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1024} )
attributeTypes: ( 1.3.6.1.4.1.37505.1.8
NAME 'im'
EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1024} )
attributeTypes: ( 1.3.6.1.4.1.37505.1.9
NAME 'url'
EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1024} )
attributeTypes: ( 1.3.6.1.4.1.37505.1.10
NAME 'otherPhone'
EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1024} )
attributeTypes: ( 1.3.6.1.4.1.37505.1.11
NAME 'privatePersonalIdentifier'
EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1024} )
attributeTypes: ( 1.3.6.1.4.1.37505.1.12
NAME 'profileconfiguration'
EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1024} )
attributeTypes: ( 1.3.6.1.4.1.37505.1.13
NAME 'prefferedLanguage'
EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1024} )
attributeTypes: ( 1.3.6.1.4.1.37505.1.14
NAME 'organizationName'
EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1024} )
attributeTypes: ( 1.3.6.1.4.1.37505.1.15
NAME 'accountLocked'
EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1024} )
attributeTypes: ( 1.3.6.1.4.1.37505.1.16
NAME 'passwordTimestamp'
EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1024} )
-
add: objectClasses
objectClasses: ( 1.3.6.1.4.1.37505.1.1
NAME 'casinocoinUser'
DESC 'casinocoinUser'
SUP inetOrgPerson
STRUCTURAL
MAY ( gender $ country $ nickName $ timeZone $ dateOfBirth $ role $ im $ url $ otherPhone $ privatePersonalIdentifier $ profileconfiguration $ prefferedLanguage $ organizationName $ accountLocked $ passwordTimestamp)
)
-

View File

@@ -0,0 +1,71 @@
attributetype ( 1.1.1.301.1
NAME 'country'
DESC 'Country of a user'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE )
attributetype ( 1.1.1.301.2
NAME 'role'
DESC 'Role of a user'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE )
attributetype ( 1.1.1.301.3
NAME 'im'
DESC 'IM of a user'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE )
attributetype ( 1.1.1.301.4
NAME 'profileConfiguration'
DESC 'Profile Configuration of a user'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE )
attributetype ( 1.1.1.301.5
NAME 'url'
DESC 'URL Configuration of a user'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE )
attributetype ( 1.1.1.301.7
NAME 'accountLocked'
DESC 'Stores the status if a user account is locked'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE )
attributetype ( 1.1.1.301.8
NAME 'passwordTimestamp'
DESC 'Timestamp of last password change'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE )
attributetype ( 1.1.1.301.9
NAME 'scimId'
DESC 'scimId'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE )
attributetype ( 1.1.1.301.10
NAME 'lastModifiedDate'
DESC 'User last modifation date'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE )
attributetype ( 1.1.1.301.11
NAME 'createdDate'
DESC 'User creaetion date'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE )
attributetype ( 1.1.1.301.12
NAME 'location'
DESC 'User location'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE )
attributetype ( 1.1.1.301.13
NAME 'unlockTime'
DESC 'User Unlock Time'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE )
attributetype ( 1.1.1.301.14
NAME 'failedLoginAttempts'
DESC 'User Failed Login Attempts'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE )
objectclass ( 1.1.1.302.1
NAME 'casinocoinUser'
DESC 'Casinocoin user'
SUP inetOrgPerson
STRUCTURAL
MAY ( country $ role $ im $ profileConfiguration $ url $ accountLocked $ passwordTimestamp $ scimId $ lastModifiedDate $ createdDate $ location $ unlockTime $ failedLoginAttempts ) )

View File

@@ -0,0 +1,4 @@
dn: cn=module{0},cn=config
changetype: modify
add: olcModuleLoad
olcModuleLoad: ppolicy.la

View File

@@ -0,0 +1,5 @@
dn: ou=Policies,dc=casinocoin,dc=org
objectClass: top
objectClass: organizationalUnit
ou: Policies
description: Default password policies container

View File

@@ -0,0 +1,6 @@
dn: olcOverlay={0}ppolicy,olcDatabase={1}hdb,cn=config
objectClass: olcOverlayConfig
objectClass: olcPPolicyConfig
olcOverlay: {0}ppolicy
olcPPolicyDefault: cn=DefaultPPolicy,ou=Policies,dc=casinocoin,dc=org
olcPPolicyHashCleartext: TRUE

View File

@@ -0,0 +1,19 @@
dn: cn=DefaultPPolicy,ou=Policies,dc=casinocoin,dc=org
cn: DefaultPPolicy
objectClass: pwdPolicy
objectClass: device
objectClass: top
pwdAttribute: userPassword
pwdMaxAge: 2419200
pwdExpireWarning: 1814400
pwdInHistory: 3
pwdCheckQuality: 1
pwdMinLength: 8
pwdMaxFailure: 3
pwdLockout: TRUE
pwdLockoutDuration: 600
pwdGraceAuthNLimit: 0
pwdFailureCountInterval: 0
pwdMustChange: TRUE
pwdAllowUserChange: TRUE
pwdSafeModify: FALSE

View File

@@ -0,0 +1,4 @@
dn: olcDatabase={1}hdb,cn=config
changetype: modify
add: olcDbIndex
olcDbIndex: uid eq,pres,sub

View File

@@ -0,0 +1,4 @@
dn: olcDatabase={1}hdb,cn=config
changetype: modify
add: olcDbIndex
olcDbIndex: mail eq,pres,sub

View File

@@ -0,0 +1,8 @@
include /etc/ldap/schema/core.schema
include /etc/ldap/schema/cosine.schema
include /etc/ldap/schema/inetorgperson.schema
include /etc/ldap/schema/misc.schema
include /etc/ldap/schema/nis.schema
include /etc/ldap/schema/openldap.schema
include /etc/ldap/schema/ppolicy.schema
include /etc/ldap/schema/casinocoinUser.schema

View File

@@ -0,0 +1,4 @@
dn: cn=config
changetype: modify
replace: olcLogLevel
olcLogLevel: stats

View File

@@ -0,0 +1,15 @@
dn: ou=Users,dc=casinocoin,dc=org
objectClass: organizationalUnit
ou: Users
dn: ou=Groups,dc=casinocoin,dc=org
objectClass: organizationalUnit
ou: Groups
dn: ou=WalletUsers,ou=Groups,dc=casinocoin,dc=org
objectClass: organizationalUnit
ou: WalletUsers
dn: ou=WalletApplications,ou=Groups,dc=casinocoin,dc=org
objectClass: organizationalUnit
ou: WalletApplications

View File

@@ -0,0 +1,71 @@
attributetype ( 1.1.1.301.1
NAME 'country'
DESC 'Country of a user'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE )
attributetype ( 1.1.1.301.2
NAME 'role'
DESC 'Role of a user'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE )
attributetype ( 1.1.1.301.3
NAME 'im'
DESC 'IM of a user'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE )
attributetype ( 1.1.1.301.4
NAME 'profileConfiguration'
DESC 'Profile Configuration of a user'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE )
attributetype ( 1.1.1.301.5
NAME 'url'
DESC 'URL Configuration of a user'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE )
attributetype ( 1.1.1.301.7
NAME 'accountLocked'
DESC 'Stores the status if a user account is locked'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE )
attributetype ( 1.1.1.301.8
NAME 'passwordTimestamp'
DESC 'Timestamp of last password change'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE )
attributetype ( 1.1.1.301.9
NAME 'scimId'
DESC 'scimId'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE )
attributetype ( 1.1.1.301.10
NAME 'lastModifiedDate'
DESC 'User last modifation date'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE )
attributetype ( 1.1.1.301.11
NAME 'createdDate'
DESC 'User creaetion date'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE )
attributetype ( 1.1.1.301.12
NAME 'location'
DESC 'User location'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE )
attributetype ( 1.1.1.301.13
NAME 'unlockTime'
DESC 'User Unlock Time'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE )
attributetype ( 1.1.1.301.14
NAME 'failedLoginAttempts'
DESC 'User Failed Login Attempts'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE )
objectclass ( 1.1.1.302.1
NAME 'casinocoinUser'
DESC 'Casinocoin user'
SUP inetOrgPerson
STRUCTURAL
MAY ( country $ role $ im $ profileConfiguration $ url $ accountLocked $ passwordTimestamp $ scimId $ lastModifiedDate $ createdDate $ location $ unlockTime $ failedLoginAttempts ) )

View File

@@ -0,0 +1,15 @@
dn: ou=Users,dc=casinocoin,dc=org
objectClass: organizationalUnit
ou: Users
dn: ou=Groups,dc=casinocoin,dc=org
objectClass: organizationalUnit
ou: Groups
dn: ou=WalletUsers,ou=Groups,dc=casinocoin,dc=org
objectClass: organizationalUnit
ou: WalletUsers
dn: ou=WalletApplications,ou=Groups,dc=casinocoin,dc=org
objectClass: organizationalUnit
ou: WalletApplications

View File

@@ -0,0 +1,35 @@
dn: cn=casinocoinuser,cn=schema,cn=config
objectClass: olcSchemaConfig
cn: casinocoinuser
olcAttributeTypes: {0}( 1.1.1.301.1 NAME 'country' DESC 'Country of a user' SY
NTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
olcAttributeTypes: {1}( 1.1.1.301.2 NAME 'role' DESC 'Role of a user' SYNTAX 1
.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
olcAttributeTypes: {2}( 1.1.1.301.3 NAME 'im' DESC 'IM of a user' SYNTAX 1.3.6
.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
olcAttributeTypes: {3}( 1.1.1.301.4 NAME 'profileConfiguration' DESC 'Profile
Configuration of a user' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
olcAttributeTypes: {4}( 1.1.1.301.5 NAME 'url' DESC 'URL Configuration of a us
er' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
olcAttributeTypes: {5}( 1.1.1.301.7 NAME 'accountLocked' DESC 'Stores the stat
us if a user account is locked' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-V
ALUE )
olcAttributeTypes: {6}( 1.1.1.301.8 NAME 'passwordTimestamp' DESC 'Timestamp o
f last password change' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
olcAttributeTypes: {7}( 1.1.1.301.9 NAME 'scimId' DESC 'scimId' SYNTAX 1.3.6.1
.4.1.1466.115.121.1.15 SINGLE-VALUE )
olcAttributeTypes: {8}( 1.1.1.301.10 NAME 'lastModifiedDate' DESC 'User last m
odifation date' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
olcAttributeTypes: {9}( 1.1.1.301.11 NAME 'createdDate' DESC 'User creaetion d
ate' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
olcAttributeTypes: {10}( 1.1.1.301.12 NAME 'location' DESC 'User location' SYN
TAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
olcAttributeTypes: {11}( 1.1.1.301.13 NAME 'unlockTime' DESC 'User Unlock Time
' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
olcAttributeTypes: {12}( 1.1.1.301.14 NAME 'failedLoginAttempts' DESC 'User Fa
iled Login Attempts' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
olcObjectClasses: {0}( 1.1.1.302.1 NAME 'casinocoinUser' DESC 'Casinocoin user
' SUP inetOrgPerson STRUCTURAL MAY ( country $ role $ im $ profileConfigurati
on $ url $ accountLocked $ passwordTimestamp $ scimId $ lastModifiedDate $ cr
eatedDate $ location $ unlockTime $ failedLoginAttempts ) )

Binary file not shown.

View File

@@ -0,0 +1,8 @@
100 - No arguments supplied for WalletServer command.
101 - No Wallet ID supplied in arguments array.
102 - Invalid Account ID for given Wallet ID.
103 - Wallet file does not exist on WalletServer.
104 - No passphrase supplied for wallet encryption.
105 - Given SessionId is different than the one registered for AccountId.
106 - No session exists for given AccountId.
107 - Wallet Server could not parse the incomming message.

View File

@@ -0,0 +1,231 @@
--
-- Create Wallet
--
Request:
{
"command" : "createwallet",
"accountid" : "andre@jochems.com",
"sessionid" : "123123-12312321-123123123",
"correlationid" : "65bb1cf4-3ca2-4d32-9a3c-9ae264424b0e",
"arguments" : {
"passphrase" : "mywalletsecret"
}
}
Response:
{
"sessionid" : "123123-12312321-123123123",
"correlationid" : "65bb1cf4-3ca2-4d32-9a3c-9ae264424b0e",
"result" : {
"errorCode" : 0,
"errorMessage" : "",
"walletid" : "517beddb-8de2-4112-8aad-9e4d627916aa"
}
}
--
-- Open Wallet
--
Request:
{
"command" : "openwallet",
"accountid" : "andre@jochems.com",
"sessionid" : "123123-12312321-123123123",
"correlationid" : "65bb1cf4-3ca2-4d32-9a3c-9ae264424b0e",
"arguments" : {
"walletid" : "517beddb-8de2-4112-8aad-9e4d627916aa",
"passphrase" : "mywalletsecret"
}
}
Response:
{
"sessionid" : "123123-12312321-123123123",
"correlationid" : "65bb1cf4-3ca2-4d32-9a3c-9ae264424b0e",
"result" : {
"errorCode" : 0,
"errorMessage" : ""
}
}
--
-- Close Wallet
--
Request:
{
"command" : "closewallet",
"accountid" : "andre@jochems.com",
"sessionid" : "123123-12312321-123123123",
"correlationid" : "65bb1cf4-3ca2-4d32-9a3c-9ae264424b0e",
"arguments" : {
"walletid" : "517beddb-8de2-4112-8aad-9e4d627916aa"
}
}
Response:
{
"sessionid" : "123123-12312321-123123123",
"correlationid" : "65bb1cf4-3ca2-4d32-9a3c-9ae264424b0e",
"result" : {
"errorCode" : 0,
"errorMessage" : ""
}
}
--
-- Get Wallet Info
--
Request:
{
"command" : "getinfo",
"accountid" : "andre@jochems.com",
"sessionid" : "123123-12312321-123123123",
"correlationid" : "65bb1cf4-3ca2-4d32-9a3c-9ae264424b0e",
"arguments" : {
"walletid" : "517beddb-8de2-4112-8aad-9e4d627916aa"
}
}
Response:
{
"sessionid" : "123123-12312321-123123123",
"correlationid" : "65bb1cf4-3ca2-4d32-9a3c-9ae264424b0e",
"result" : {
"errorCode" : 0,
"errorMessage" : "",
"balance" : 250.412323212,
"blocks" : 1377377,
"difficulty" : 21.41622924,
"keypoololdest" : 1445349028,
"keypoolsize" : 101,
"lastTransactions" : [
{ "txid" : "4378c3ffe1459bd5ff04ef19c9d56cfecaad45ae3bb0661768b42f9a40952bfa",
"address" : "CT8YNXrn4EmPXTin4ecMtuWu1gvvUQuEKH",
"category" : "receive",
"amount" : 0.90000000,
"confirmations" : 52920,
"blockhash" : "96b0885392d5b316e3cdb4fe9be0327790f7135f517ca38e50d9b4f8599d85b6",
"blockindex" : 1,
"blocktime" : 1454950142,
"time" : 1454950116,
"timereceived" : 1454950116
},
{ "txid" : "610157549d6c2f62809ad245134e5cdbc38e2738907daf376e71f523d3b99e8a",
"address" : "CVXzfjz3q1x3K8AeuCmhZ5cHK3ZzDgzURV",
"category" : "send",
"amount" : -1.00000000,
"fee" : -0.00100000,
"confirmations" : 66137,
"blockhash" : "7fd6b63c8f6c7e2b8bc48935efa560a50d16a1733c69c0a1c66e49a8ba44c1be",
"blockindex" : 1,
"blocktime" : 1454405676,
"time" : 1454405608,
"timereceived" : 1454405608
}
]
}
}
--
-- Get Wallet Transactions
--
Request:
{
"command" : "listtransactions",
"accountid" : "andre@jochems.com",
"sessionid" : "123123-12312321-123123123",
"correlationid" : "65bb1cf4-3ca2-4d32-9a3c-9ae264424b0e",
"arguments" : {
"walletid" : "517beddb-8de2-4112-8aad-9e4d627916aa",
"count" : 10,
"from" : 0
}
}
Response:
{
"sessionid" : "123123-12312321-123123123",
"correlationid" : "65bb1cf4-3ca2-4d32-9a3c-9ae264424b0e",
"result" : {
"errorCode" : 0,
"errorMessage" : "",
"indexFrom" : 0,
"indexTo" : 10,
"transactions" : [
{ "txid" : "4378c3ffe1459bd5ff04ef19c9d56cfecaad45ae3bb0661768b42f9a40952bfa",
"address" : "CT8YNXrn4EmPXTin4ecMtuWu1gvvUQuEKH",
"category" : "receive",
"amount" : 0.90000000,
"confirmations" : 52920,
"blockhash" : "96b0885392d5b316e3cdb4fe9be0327790f7135f517ca38e50d9b4f8599d85b6",
"blockindex" : 1,
"blocktime" : 1454950142,
"time" : 1454950116,
"timereceived" : 1454950116
},
{ "txid" : "610157549d6c2f62809ad245134e5cdbc38e2738907daf376e71f523d3b99e8a",
"address" : "CVXzfjz3q1x3K8AeuCmhZ5cHK3ZzDgzURV",
"category" : "send",
"amount" : -1.00000000,
"fee" : -0.00100000,
"confirmations" : 66137,
"blockhash" : "7fd6b63c8f6c7e2b8bc48935efa560a50d16a1733c69c0a1c66e49a8ba44c1be",
"blockindex" : 1,
"blocktime" : 1454405676,
"time" : 1454405608,
"timereceived" : 1454405608
}
]
}
}
--
-- Get Address book
--
Request:
{
"command" : "getaddresslist",
"accountid" : "andre@jochems.com",
"sessionid" : "123123-12312321-123123123",
"correlationid" : "65bb1cf4-3ca2-4d32-9a3c-9ae264424b0e",
"arguments" : {
"walletid" : "517beddb-8de2-4112-8aad-9e4d627916aa"
}
}
Response:
{
"sessionid" : "123123-12312321-123123123",
"correlationid" : "65bb1cf4-3ca2-4d32-9a3c-9ae264424b0e",
"result" : {
"errorCode" : 0,
"errorMessage" : ""
}
}
--
-- Send Coins
--
Request:
{
"command" : "sendtoaddress",
"accountid" : "andre@jochems.com",
"sessionid" : "123123-12312321-123123123",
"correlationid" : "65bb1cf4-3ca2-4d32-9a3c-9ae264424b0e",
"arguments" : {
"walletid" : "517beddb-8de2-4112-8aad-9e4d627916aa",
"address" : "AB3423SDSDFS",
"amount" : 234.67854,
"comment" : "Coins for a test transaction"
}
}
Response:
{
"sessionid" : "123123-12312321-123123123",
"correlationid" : "65bb1cf4-3ca2-4d32-9a3c-9ae264424b0e",
"result" : {
"errorCode" : 0,
"errorMessage" : ""
}
}

View File

@@ -266,6 +266,15 @@ static const CRPCCommand vRPCCommands[] =
{ "listlockunspent", &listlockunspent, false, false, true },
{ "verifychain", &verifychain, true, false, false },
{ "getcoinsupply", &getcoinsupply, true, false, false },
{ "startwalletserversession", &startwalletserversession, true, false, false },
{ "listwalletserversessions", &listwalletserversessions, true, false, false },
{ "stopwalletserversession", &stopwalletserversession, true, false, false },
{ "listwallets", &listwallets, true, false, false },
{ "wsopenwallet", &wsopenwallet, true, false, false },
{ "wsclosewallet", &wsclosewallet, true, false, false },
{ "wsgetinfo", &wsgetinfo, true, false, false },
{ "wsgetaddresslist", &wsgetaddresslist, true, false, false },
{ "wssendtoaddress", &wssendtoaddress, true, false, false },
};
CRPCTable::CRPCTable()

View File

@@ -64,6 +64,9 @@ enum RPCErrorCode
RPC_WALLET_WRONG_ENC_STATE = -15, // Command given in wrong wallet encryption state (encrypting an encrypted wallet etc.)
RPC_WALLET_ENCRYPTION_FAILED = -16, // Failed to encrypt the wallet
RPC_WALLET_ALREADY_UNLOCKED = -17, // Wallet is already unlocked
// Wallet Server errors
RPC_WALLETSERVER_INVALID_ID = -100, // Given identifier is already in a session
};
json_spirit::Object JSONRPCError(int code, const std::string& message);
@@ -207,5 +210,15 @@ extern json_spirit::Value gettxout(const json_spirit::Array& params, bool fHelp)
extern json_spirit::Value verifychain(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value getcoinsupply(const json_spirit::Array& params, bool fHelp);
//WalletServer RPC Commands
extern json_spirit::Value startwalletserversession(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value listwalletserversessions(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value stopwalletserversession(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value listwallets(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value wsopenwallet(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value wsclosewallet(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value wsgetinfo(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value wsgetaddresslist(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value wssendtoaddress(const json_spirit::Array& params, bool fHelp);
#endif

View File

@@ -8,8 +8,9 @@
#include "bitcoinrpc.h"
#include "net.h"
#include "init.h"
#include "util.h"
#include "ui_interface.h"
#include "walletserver.h"
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
@@ -26,7 +27,9 @@ using namespace std;
using namespace boost;
CWallet* pwalletMain;
WalletServer walletServer;
CClientUIInterface uiInterface;
boost::thread walletServerThread;
#ifdef WIN32
// Win32 LevelDB doesn't use filedescriptors, and the ones used for
@@ -78,6 +81,9 @@ volatile bool fRequestShutdown = false;
void StartShutdown()
{
// Stop WalletServer if running
StopWalletServerThread();
// initiate shutdown
boost::this_thread::sleep_for( boost::chrono::seconds( 1 ) );
fRequestShutdown = true;
}
@@ -226,7 +232,9 @@ bool AppInit(int argc, char* argv[])
fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno);
}
#endif
// WalletServer parameter
fWalletServer = GetBoolArg("-walletserver");
// create the thread to detect a shutdown
detectShutdownThread = new boost::thread(boost::bind(&DetectShutdownThread, &threadGroup));
fRet = AppInit2(threadGroup);
}
@@ -256,7 +264,6 @@ extern void noui_connect();
int main(int argc, char* argv[])
{
bool fRet = false;
// Connect bitcoind signal handlers
noui_connect();
@@ -1130,6 +1137,14 @@ bool AppInit2(boost::thread_group& threadGroup)
if (pwalletMain)
GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain);
// Start The WalletServer if defined
fWalletServer = GetBoolArg("-walletserver", false);
if (fWalletServer)
{
// Run a thread for the WalletServer
threadGroup.create_thread(boost::bind(&StartWalletServerThread));
}
// ********************************************************* Step 12: finished
uiInterface.InitMessage(_("Done loading"));

View File

@@ -6,3 +6,7 @@
// json spirit version 2.00
#include "json_spirit_value.h"
namespace json_spirit {
const char* Value_type_name[]={"obj", "array", "str", "bool", "int", "real", "null"};
}

View File

@@ -24,7 +24,7 @@
namespace json_spirit
{
enum Value_type{ obj_type, array_type, str_type, bool_type, int_type, real_type, null_type };
static const char* Value_type_name[]={"obj", "array", "str", "bool", "int", "real", "null"};
extern const char* Value_type_name[];
template< class Config > // Config determines whether the value uses std::string or std::wstring and
// whether JSON Objects are represented as vectors or maps

View File

@@ -25,6 +25,8 @@ BOOST_SUFFIX?=-mgw49-mt-s-1_55
INCLUDEPATHS= \
-I"$(CURDIR)" \
-I"$(CURDIR)\stomp" \
-I"$(CURDIR)\json" \
-I"C:\deps\boost_1_55_0" \
-I"C:\deps\db-4.8.30.NC\build_unix" \
-I"C:\deps\openssl-1.0.2d\include" \
@@ -45,6 +47,7 @@ LIBS= \
-l boost_program_options$(BOOST_SUFFIX) \
-l boost_thread$(BOOST_SUFFIX) \
-l boost_chrono$(BOOST_SUFFIX) \
-l boost_serialization$(BOOST_SUFFIX) \
-l db_cxx \
-l ssl \
-l crypto
@@ -106,7 +109,15 @@ OBJS= \
obj/bloom.o \
obj/noui.o \
obj/leveldb.o \
obj/txdb.o
obj/txdb.o \
obj/json/json_spirit_value.o \
obj/json/json_spirit_reader.o \
obj/json/json_spirit_writer.o \
obj/stomp/helpers.o \
obj/stomp/booststomp.o \
obj/stomp/stompframe.o \
obj/walletserver.o \
obj/walletserversession.o
ifdef USE_SSE2
DEFS += -DUSE_SSE2
@@ -147,7 +158,8 @@ test_casinocoin.exe: $(TESTOBJS) $(filter-out obj/init.o,$(OBJS:obj/%=obj/%))
clean:
rm -f casinocoind.exe test_casinocoin.exe
rm -f obj/*
rm -f obj/stomp/*
rm -f obj/*.o
rm -f obj-test/*
cd leveldb && $(MAKE) TARGET_OS=NATIVE_WINDOWS clean && cd ..

View File

@@ -54,6 +54,7 @@ LIBS += \
-l boost_filesystem$(BOOST_LIB_SUFFIX) \
-l boost_program_options$(BOOST_LIB_SUFFIX) \
-l boost_thread$(BOOST_LIB_SUFFIX) \
-l boost_serialization$(BOOST_LIB_SUFFIX) \
-l db_cxx$(BDB_LIB_SUFFIX) \
-l ssl \
-l crypto
@@ -126,6 +127,9 @@ xCXXFLAGS=-O2 -pthread -Wall -Wextra -Wformat -Wformat-security -Wno-unused-para
# adds some defaults in front. Unfortunately, LDFLAGS=... $(LDFLAGS) does not work.
xLDFLAGS=$(LDHARDENING) $(LDFLAGS)
HEADERS = $(wildcard *.h) \
stomp/$(wildcar *.h)
OBJS= \
leveldb/libleveldb.a \
obj/alert.o \
@@ -158,7 +162,11 @@ OBJS= \
obj/bloom.o \
obj/noui.o \
obj/leveldb.o \
obj/txdb.o
obj/txdb.o \
obj/stomp/helpers.o \
obj/stomp/booststomp.o \
obj/stomp/stompframe.o \
obj/walletserver.o
ifdef USE_SSE2
@@ -199,6 +207,7 @@ obj/%-sse2.o: %-sse2.cpp
rm -f $(@:%.o=%.d)
obj/%.o: %.cpp
mkdir -p obj/stomp
$(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $<
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \

View File

@@ -15,6 +15,7 @@
#include "ui_interface.h"
#include "paymentserver.h"
#include "splashscreen.h"
#include "walletserver.h"
#include <QMessageBox>
#if QT_VERSION < 0x050000
@@ -296,6 +297,7 @@ int main(int argc, char *argv[])
}
else
{
// Shutdown the core and its threads
threadGroup.interrupt_all();
threadGroup.join_all();
Shutdown();

View File

@@ -40,6 +40,7 @@ class GUI20Skin : public QObject
Q_PROPERTY( QColor colorTextActiveAutocomplete READ GetColorTextActiveAutocomplete CONSTANT )
Q_PROPERTY( QColor colorTextDisabled READ GetColorTextDisabled CONSTANT )
Q_PROPERTY( QColor colorTextDisabledAutocomplete READ GetColorTextDisabledAutocomplete CONSTANT )
Q_PROPERTY( QColor colorTextBlack READ GetColorTextBlack CONSTANT )
Q_ENUMS( ESizeConstants )
@@ -80,6 +81,7 @@ public:
const QColor GetColorTextActiveAutocomplete() const {return colorTextActiveAutocomplete;}
const QColor GetColorTextDisabled() const {return colorTextDisabled;}
const QColor GetColorTextDisabledAutocomplete() const {return colorTextDisabledAutocomplete;}
const QColor GetColorTextBlack() const {return colorTextBlack;}
private:
const QColor colorToolbarMainGradientBegin;

View File

@@ -4,12 +4,18 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <boost/assign/list_of.hpp>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <boost/lexical_cast.hpp>
#include "walletserver.h"
#include "wallet.h"
#include "walletdb.h"
#include "bitcoinrpc.h"
#include "init.h"
#include "base58.h"
#include "ui_interface.h"
using namespace std;
using namespace boost;
@@ -68,7 +74,7 @@ Value getinfo(const Array& params, bool fHelp)
proxyType proxy;
GetProxy(NET_IPV4, proxy);
const Array emptyArray;
Object obj;
obj.push_back(Pair("version", (int)CLIENT_VERSION));
obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION));
@@ -77,6 +83,7 @@ Value getinfo(const Array& params, bool fHelp)
obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance())));
}
obj.push_back(Pair("blocks", (int)nBestHeight));
obj.push_back(Pair("coinsupply", ValueFromAmount(GetTotalCoinSupply(nBestHeight,false))));
obj.push_back(Pair("timeoffset", (boost::int64_t)GetTimeOffset()));
obj.push_back(Pair("connections", (int)vNodes.size()));
obj.push_back(Pair("proxy", (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string())));
@@ -1626,3 +1633,209 @@ Value getcoinsupply(const Array& params, bool fHelp)
int64 coinSupply = GetTotalCoinSupply(height,noCheckpoints);
return ValueFromAmount(coinSupply);
}
Value startwalletserversession(const Array &params, bool fHelp)
{
if(!fWalletServer)
throw runtime_error(
"The server is not started in Wallet Server mode so no session can be created");
if (fHelp || params.size() != 1)
throw runtime_error(
"startwalletserversession [accountid]\n"
"Starts a Wallet Server session and returns a session id.\n"
"Pass in the [accountid] which will be used in future wallet server requests for the created session.");
// get the account id
std::string accountId = params[0].get_str();
// check if accountId is not already in a session
if(walletServer.isNewAccountId(accountId))
{
// Create a session id
boost::uuids::uuid uuid = boost::uuids::random_generator()();
std::string sessionId = boost::lexical_cast<std::string>(uuid);
// create session object
Session wsSession = {accountId, sessionId, (int)time(NULL), false, 0};
// Notify the Wallet Server of the newsession
walletServer.NotifyStartNewWalletServerSession(accountId, wsSession);
Object ret;
ret.push_back(Pair("accountid", accountId));
ret.push_back(Pair("sessionid", sessionId));
return ret;
}
else
throw JSONRPCError(RPC_WALLETSERVER_INVALID_ID, "Given identifier is already in an active session");
}
Value listwalletserversessions(const Array &params, bool fHelp)
{
if(!fWalletServer)
throw runtime_error(
"The server is not started in Wallet Server mode so no sessions list available");
if (fHelp || params.size() > 0)
throw runtime_error(
"listwalletserversessions\n"
"Returns a list with current active walletserver sessions.");
// get the sessions
sessionKeyValueType &wsSessions = walletServer.getSessions();
// create the output object
Array ret;
BOOST_FOREACH( sessionKeyValueType::value_type &session, wsSessions )
{
Object o;
o.push_back(Pair("accountid", session.second.email));
o.push_back(Pair("sessionid", session.second.sessionId));
o.push_back(Pair("creationtime", session.second.creationTime));
o.push_back(Pair("walletopen", session.second.walletOpen));
o.push_back(Pair("lastcommandtime", session.second.lastCommandTime));
ret.push_back(o);
}
return ret;
}
Value stopwalletserversession(const Array &params, bool fHelp)
{
if(!fWalletServer)
throw runtime_error(
"The server is not started in Wallet Server mode so no sessions list available");
if (fHelp || params.size() != 1)
throw runtime_error(
"stopwalletserversession [sessionid]\n"
"Removes a Wallet Server session and closes the wallet if open.\n"
"Pass in the [sessionid] of the session that has to be removed.");
// get the session id
std::string sessionId = params[0].get_str();
// remove session and return the result
Object ret;
ret.push_back(Pair("result", walletServer.deleteSession(sessionId)));
return ret;
}
Value listwallets(const Array &params, bool fHelp)
{
if(!fWalletServer)
throw runtime_error(
"The server is not started in Wallet Server mode so no wallet list available");
if (fHelp || params.size() > 0)
throw runtime_error(
"listwallets\n"
"Returns a list with wallets stored by the walletserver.");
// get the wallets
keyValueType wsWallets = walletServer.getWallets();
// create the output object
Array ret;
BOOST_FOREACH( keyValueType::value_type &wallet, wsWallets )
{
Object o;
o.push_back(Pair("walletid", wallet.first));
o.push_back(Pair("accountid", wallet.second));
ret.push_back(o);
}
return ret;
}
Value wsopenwallet(const Array &params, bool fHelp)
{
if(!fWalletServer)
throw runtime_error(
"The server is not started in Wallet Server mode so command is not available");
if (fHelp || params.size() != 3)
throw runtime_error(
"wsopenwallet [sessionid] [accountid] [walletid]\n"
"Open a wallet for a session. account and wallet id combination.");
// get the wallets
keyValueType wsWallets = walletServer.getWallets();
// create the output object
Array ret;
BOOST_FOREACH( keyValueType::value_type &wallet, wsWallets )
{
Object o;
o.push_back(Pair("walletid", wallet.first));
o.push_back(Pair("accountid", wallet.second));
ret.push_back(o);
}
return ret;
}
Value wsclosewallet(const Array &params, bool fHelp)
{
if(!fWalletServer)
throw runtime_error(
"The server is not started in Wallet Server mode so command is not available");
if (fHelp || params.size() != 3)
throw runtime_error(
"wsclosewallet [sessionid] [accountid] [walletid]\n"
"Open a wallet for a session. account and wallet id combination.");
// get the wallets
keyValueType wsWallets = walletServer.getWallets();
// create the output object
Array ret;
BOOST_FOREACH( keyValueType::value_type &wallet, wsWallets )
{
Object o;
o.push_back(Pair("walletid", wallet.first));
o.push_back(Pair("accountid", wallet.second));
ret.push_back(o);
}
return ret;
}
Value wsgetinfo(const Array &params, bool fHelp)
{
if(!fWalletServer)
throw runtime_error(
"The server is not started in Wallet Server mode so command is not available");
if (fHelp || params.size() != 3)
throw runtime_error(
"wsgetinfo [sessionid] [accountid] [walletid]\n"
"Get the Wallet Information for a given session. account and wallet id combination.");
Object obj;
obj.push_back(Pair("version", (int)CLIENT_VERSION));
obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION));
return obj;
}
Value wsgetaddresslist(const Array &params, bool fHelp)
{
if(!fWalletServer)
throw runtime_error(
"The server is not started in Wallet Server mode so command is not available");
if (fHelp || params.size() != 3)
throw runtime_error(
"wsgetaddresslist [sessionid] [accountid] [walletid]\n"
"Open a wallet for a session. account and wallet id combination.");
// get the wallets
keyValueType wsWallets = walletServer.getWallets();
// create the output object
Array ret;
BOOST_FOREACH( keyValueType::value_type &wallet, wsWallets )
{
Object o;
o.push_back(Pair("walletid", wallet.first));
o.push_back(Pair("accountid", wallet.second));
ret.push_back(o);
}
return ret;
}
Value wssendtoaddress(const Array &params, bool fHelp)
{
if(!fWalletServer)
throw runtime_error(
"The server is not started in Wallet Server mode so command is not available");
if (fHelp || params.size() != 5)
throw runtime_error(
"wssendtoaddress [sessionid] [accountid] [walletid] [address] [amount]\n"
"Open a wallet for a session. account and wallet id combination.");
// get the wallets
keyValueType wsWallets = walletServer.getWallets();
// create the output object
Array ret;
BOOST_FOREACH( keyValueType::value_type &wallet, wsWallets )
{
Object o;
o.push_back(Pair("walletid", wallet.first));
o.push_back(Pair("accountid", wallet.second));
ret.push_back(o);
}
return ret;
}

601
src/stomp/booststomp.cpp Normal file
View File

@@ -0,0 +1,601 @@
/*
BoostStomp - a STOMP (Simple Text Oriented Messaging Protocol) client
----------------------------------------------------
Copyright (c) 2012 Elias Karakoulakis <elias.karakoulakis@gmail.com>
SOFTWARE NOTICE AND LICENSE
BoostStomp is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation, either version 3 of the License,
or (at your option) any later version.
BoostStomp is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with BoostStomp. If not, see <http://www.gnu.org/licenses/>.
for more information on the LGPL, see:
http://en.wikipedia.org/wiki/GNU_Lesser_General_Public_License
*/
// based on the ASIO async TCP client example found on Boost documentation:
// http://www.boost.org/doc/libs/1_46_1/doc/html/boost_asio/example/timeouts/async_tcp_client.cpp
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/format.hpp>
#include <boost/thread.hpp>
#include "booststomp.h"
#include "main.h"
namespace STOMP {
using namespace std;
using namespace boost;
using namespace boost::asio;
using boost::asio::ip::tcp;
// used by debug_print
boost::mutex global_stream_lock;
// ----------------------------
// constructor
// ----------------------------
BoostStomp::BoostStomp(string& hostname, int& port, AckMode ackmode /*= ACK_AUTO*/):
// ----------------------------
// protected members setup
//m_sendqueue (new std::queue<Frame*>()),
//m_sendqueue_mutex (new boost::mutex()),
m_hostname (hostname),
m_port (port),
m_ackmode (ackmode),
m_stopped (true),
m_connected (false),
m_io_service (new io_service()),
m_io_service_work (new io_service::work(*m_io_service)),
m_strand (new io_service::strand(*m_io_service)),
m_socket (new tcp::socket(*m_io_service)),
// private members
m_protocol_version("1.0"),
m_transaction_id(0)
// ----------------------------
{
// map STOMP server commands to handler methods
cmd_map["CONNECTED"] = &BoostStomp::process_CONNECTED;
cmd_map["MESSAGE"] = &BoostStomp::process_MESSAGE;
cmd_map["RECEIPT"] = &BoostStomp::process_RECEIPT;
cmd_map["ERROR"] = &BoostStomp::process_ERROR;
// set default debug flag
m_showDebug = false;
}
// ----------------------------
// destructor
// ----------------------------
BoostStomp::~BoostStomp()
// ----------------------------
{
// first stop io_service so as to exit the run loop (when idle)
m_io_service->stop();
// then interrupt the worker thread
worker_thread->interrupt();
// delete m_heartbeat_timer; // no need, its a shared_ptr
delete worker_thread;
}
// ----------------------------
// worker thread
// ----------------------------
void BoostStomp::worker( boost::shared_ptr< boost::asio::io_service > _io_service )
{
debug_print("Worker thread: starting...");
while(!m_stopped) {
_io_service->run();
debug_print("Worker thread: io_service is stopped...");
_io_service->reset();
boost::this_thread::sleep_for( boost::chrono::seconds(1));
}
debug_print("Worker thread finished.");
}
// ----------------------------
// ASIO HANDLERS (protected)
// ----------------------------
// Called by the user of the client class to initiate the connection process.
// The endpoint iterator will have been obtained using a tcp::resolver.
void BoostStomp::start(string& login, string& passcode)
{
debug_print("starting...");
m_stopped = false;
tcp::resolver resolver(*m_io_service);
tcp::resolver::iterator endpoint_iter = resolver.resolve(tcp::resolver::query(
m_hostname,
to_string<int>(m_port, std::dec),
boost::asio::ip::resolver_query_base::numeric_service)
);
// Start the connect actor.
start_connect(endpoint_iter, login, passcode);
}
void BoostStomp::start()
{
std::string empty = "";
start(empty, empty);
}
// This function terminates all the actors to shut down the connection. It
// may be called by the user of the client class, or by the class itself in
// response to graceful termination or an unrecoverable error.
void BoostStomp::stop()
{
debug_print("stopping...");
if (m_connected && m_socket->is_open()) {
Frame frame( "DISCONNECT");
frame.encode(stomp_request);
debug_print("Sending DISCONNECT frame...");
boost::asio::write(*m_socket, stomp_request);
}
m_connected = false;
m_stopped = true;
if (m_heartbeat_timer != NULL) {
m_heartbeat_timer->cancel();
}
//
m_socket->close();
//
}
//
// --------------------------------------------------
// ---------- TCP CONNECTION SETUP ------------------
// --------------------------------------------------
// --------------------------------------------------
void BoostStomp::start_connect(tcp::resolver::iterator endpoint_iter, string& login, string& passcode)
// --------------------------------------------------
{
if (endpoint_iter != tcp::resolver::iterator())
{
debug_print(boost::format("STOMP: Connecting to %1%...") % endpoint_iter->endpoint() );
// Try TCP connection synchronously (the first frame to send is the CONNECT frame)
boost::system::error_code ec;
m_socket->connect(endpoint_iter->endpoint(), ec);
if (!ec) {
// now we are connected to STOMP server's TCP port/
debug_print(boost::format("STOMP TCP connection to %1% is active") % endpoint_iter->endpoint() );
// Send the CONNECT request synchronously (immediately).
hdrmap headers;
headers["accept-version"] = "1.1";
headers["host"] = m_hostname;
if (!login.empty()) {
headers["login"] = login;
headers["passcode"] = passcode;
}
Frame frame( "CONNECT", headers );
frame.encode(stomp_request);
debug_print("Sending CONNECT frame...");
boost::asio::write(*m_socket, stomp_request);
// start the read actor so as to receive the CONNECTED frame
start_stomp_read_headers();
// start worker thread (m_io_service.run())
worker_thread = new boost::thread( boost::bind( &BoostStomp::worker, this, m_io_service ) );
} else {
// We need to close the socket used in the previous connection attempt
// before starting a new one.
m_socket->close();
// Try the next available endpoint.
start_connect(++endpoint_iter, login, passcode);
}
}
else
{
// There are no more endpoints to try.
stop();
debug_print("Connection unsuccessful. Sleeping, then retrying...");
boost::this_thread::sleep_for( boost::chrono::seconds(3));
start();
}
}
// -----------------------------------------------
// ---------- INPUT ACTOR SETUP ------------------
// -----------------------------------------------
// -----------------------------------------------
void BoostStomp::start_stomp_read_headers()
// -----------------------------------------------
{
debug_print("start_stomp_read_headers");
// Start an asynchronous operation to read at least the STOMP frame command & headers (till the double newline delimiter)
boost::asio::async_read_until(
*m_socket,
stomp_response,
"\n\n",
boost::bind(&BoostStomp::handle_stomp_read_headers, this, placeholders::error()));
}
// -----------------------------------------------
void BoostStomp::handle_stomp_read_headers(const boost::system::error_code& ec)
// -----------------------------------------------
{
if (m_stopped)
return;
if (!ec)
{
std::size_t bodysize = 0;
try {
debug_print("handle_stomp_read_headers");
m_rcvd_frame = new Frame(stomp_response, cmd_map); // freed by consume_frame
hdrmap& _headers = m_rcvd_frame->headers();
// if the frame headers contain 'content-length', use that to call the proper async_read overload
if (_headers.find("content-length") != _headers.end()) {
string& content_length = _headers["content-length"];
debug_print(boost::format("received response (command+headers: %1% bytes, content-length: %2%)") % stomp_response.size() % content_length );
bodysize = lexical_cast<size_t>(content_length);
}
start_stomp_read_body(bodysize);
} catch(NoMoreFrames&) {
debug_print("No more frames!");
// break;
} catch(std::exception& e) {
debug_print(boost::format("handle_stomp_read in loop: unknown exception in Frame constructor:\n%1%") % e.what());
exit(10);
}
}
else
{
std::cerr << "BoostStomp: Error on receive: " << ec.message() << "\n";
stop();
start();
}
}
// -----------------------------------------------
void BoostStomp::start_stomp_read_body(std::size_t bodysize)
// -----------------------------------------------
{
debug_print("start_stomp_read_body");
// Start an asynchronous operation to read at least the STOMP frame body
if (bodysize == 0) {
boost::asio::async_read_until(
*m_socket, stomp_response,
'\0', // NULL signifies the end of the body
boost::bind(&BoostStomp::handle_stomp_read_body, this, placeholders::error(), placeholders::bytes_transferred()));
} else {
boost::asio::async_read(
*m_socket, stomp_response,
boost::asio::transfer_at_least(bodysize),
boost::bind(&BoostStomp::handle_stomp_read_body, this, placeholders::error(), placeholders::bytes_transferred()));
}
}
// -----------------------------------------------
void BoostStomp::handle_stomp_read_body(const boost::system::error_code& ec, std::size_t bytes_transferred = 0)
// -----------------------------------------------
{
if (m_stopped)
return;
if (!ec)
{
debug_print(boost::format("received response (%1% bytes) (buffer: %2% bytes)") % bytes_transferred % stomp_response.size() );
if (m_rcvd_frame != NULL) {
m_rcvd_frame->parse_body(stomp_response);
consume_received_frame();
}
//
//debug_print("stomp_response contents after Frame scanning:");
//hexdump(stomp_response);
// wait for the next incoming frame from the server...
start_stomp_read_headers();
}
else
{
std::cerr << "BoostStomp: Error on receive: " << ec.message() << "\n";
stop();
start();
}
}
// ------------------------------------------
void BoostStomp::consume_received_frame()
// ------------------------------------------
{
if (m_rcvd_frame != NULL) {
// is there a declared handler for the command in the received STOMP frame?
if (pfnStompCommandHandler_t handler = cmd_map[m_rcvd_frame->command()]) {
debug_print(boost::format("-- consume_frame: calling %1% command handler") % m_rcvd_frame->command());
// call STOMP command handler
(this->*handler)();
}
delete m_rcvd_frame;
}
m_rcvd_frame = NULL;
};
// ------------------------------------------------
// ---------- OUTPUT ACTOR SETUP ------------------
// ------------------------------------------------
// -----------------------------------------------
void BoostStomp::start_stomp_write()
// -----------------------------------------------
{
if ((m_stopped) || (!m_connected))
return;
//debug_print("start_stomp_write");
Frame* frame = NULL;
// send all STOMP frames in queue
while (m_sendqueue.try_pop(frame)) {
debug_print(boost::format("Sending %1% frame...") % frame->command() );
frame->encode(stomp_request);
try {
boost::asio::write(
*m_socket,
stomp_request
);
debug_print("Sent!");
delete frame;
} catch (boost::system::system_error& err){
m_connected = false;
debug_print(boost::format("Error writing to STOMP server: error code:%1%, message:%2%") % err.code() % err.what());
// put! the kot! down! slowly!
if(err.code().value() != 10009)
m_sendqueue.push(frame);
stop();
}
};
}
// -----------------------------------------------
void BoostStomp::start_stomp_heartbeat()
// -----------------------------------------------
{
// Start an asynchronous operation to send a heartbeat message.
//debug_print("Sending heartbeat...");
boost::asio::async_write(
*m_socket,
m_heartbeat,
boost::bind(&BoostStomp::handle_stomp_heartbeat, this, _1)
);
}
// -----------------------------------------------
void BoostStomp::handle_stomp_heartbeat(const boost::system::error_code& ec)
// -----------------------------------------------
{
if (m_stopped)
return;
if (!ec)
{
// Wait 10 seconds before sending the next heartbeat.
m_heartbeat_timer->expires_from_now(boost::posix_time::seconds(10));
m_heartbeat_timer->async_wait(
boost::bind(
&BoostStomp::start_stomp_heartbeat,
this
)
);
}
else
{
std::cout << "Error on sending heartbeat: " << ec.message() << "\n";
stop();
}
}
//-----------------------------------------
void BoostStomp::process_CONNECTED()
//-----------------------------------------
{
m_connected = true;
// try to get supported protocol version from headers
hdrmap _headers = m_rcvd_frame->headers();
if (_headers.find("version") != _headers.end()) {
m_protocol_version = _headers["version"];
debug_print(boost::format("server supports STOMP version %1%") % m_protocol_version);
}
if (m_protocol_version == "1.1") {
// we are connected to a version 1.1 STOMP server, setup heartbeat
m_heartbeat_timer = boost::shared_ptr< deadline_timer> ( new deadline_timer( *m_io_service ));
std::ostream os( &m_heartbeat);
os << "\n";
// we can start the heartbeat actor
start_stomp_heartbeat();
}
// in case of reconnection, we need to re-subscribe to all subscriptions
for (subscription_map::iterator it = m_subscriptions.begin(); it != m_subscriptions.end(); it++) {
//string topic = (*it).first;
do_subscribe((*it).first);
};
}
//-----------------------------------------
void BoostStomp::process_MESSAGE()
//-----------------------------------------
{
bool acked = true;
hdrmap& _headers = m_rcvd_frame->headers();
if (_headers.find("destination") != _headers.end()) {
string& dest = _headers["destination"];
//
if (pfnOnStompMessage_t callback_function = m_subscriptions[dest]) {
//debug_print(boost::format("-- consume_frame: firing callback for %1%") % dest);
//
acked = callback_function(m_rcvd_frame);
};
};
// acknowledge frame, if in "Client" or "Client-Individual" ack mode
if ((m_ackmode == ACK_CLIENT) || (m_ackmode == ACK_CLIENT_INDIVIDUAL)) {
acknowledge(m_rcvd_frame, acked);
}
}
//-----------------------------------------
void BoostStomp::process_RECEIPT()
//-----------------------------------------
{
hdrmap& _headers = m_rcvd_frame->headers();
if (_headers.find("receipt_id") != _headers.end()) {
string& receipt_id = _headers["receipt_id"];
// do something with receipt...
debug_print(boost::format("receipt-id == %1%") % receipt_id);
};
}
//-----------------------------------------
void BoostStomp::process_ERROR()
//-----------------------------------------
{
hdrmap& _headers = m_rcvd_frame->headers();
string errormessage = (_headers.find("message") != _headers.end()) ?
_headers["message"] :
"(unknown error!)";
errormessage += m_rcvd_frame->body().c_str();
//throw(errormessage);
cerr << endl << "============= BoostStomp got an ERROR frame from server: =================" << endl << errormessage << endl;
}
//-----------------------------------------
bool BoostStomp::send_frame( Frame* frame )
//-----------------------------------------
{
// send_frame is called from the application thread. Do not dereference frame here!!! (shared data)
//debug_print(boost::format("send_frame: Adding frame to send queue...") % frame->command() );
//debug_print("send_frame: Adding frame to send queue...");
m_sendqueue.push(frame); // concurrent_queue does all the thread safety stuff
// tell io_service to start the output actor so as the frame get sent from the worker thread
m_strand->post(
boost::bind(&BoostStomp::start_stomp_write, this)
);
return(true);
}
// ---------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------
// ------------------------ PUBLIC INTERFACE ------------------------
// ---------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------
// ------------------------------------------
bool BoostStomp::subscribe( string& topic, pfnOnStompMessage_t callback )
// ------------------------------------------
{
debug_print(boost::format("Setting callback function for %1%") % topic);
m_subscriptions[topic] = callback;
return(do_subscribe(topic));
}
// ------------------------------------------
bool BoostStomp::do_subscribe(const string& topic)
// ------------------------------------------
{
hdrmap hm;
hm["id"] = lexical_cast<string>(boost::this_thread::get_id());
hm["destination"] = topic;
return(send_frame(new Frame( "SUBSCRIBE", hm )));
}
// ------------------------------------------
bool BoostStomp::unsubscribe( string& topic )
// ------------------------------------------
{
hdrmap hm;
hm["destination"] = topic;
m_subscriptions.erase(topic);
return(send_frame(new Frame( "UNSUBSCRIBE", hm )));
}
// ------------------------------------------
bool BoostStomp::acknowledge(Frame* frame, bool acked = true)
// ------------------------------------------
{
hdrmap hm = frame->headers();
string _ack_cmd = (acked ? "ACK" : "NACK");
return(send_frame(new Frame( _ack_cmd, hm )));
}
// ------------------------------------------
int BoostStomp::begin()
// ------------------------------------------
// returns a new transaction id
{
hdrmap hm;
// create a new transaction id
hm["transaction"] = lexical_cast<string>(m_transaction_id++);
Frame* frame = new Frame( "BEGIN", hm );
send_frame(frame);
return(m_transaction_id);
};
// ------------------------------------------
bool BoostStomp::commit(int transaction_id)
// ------------------------------------------
{
hdrmap hm;
// add required header
hm["transaction"] = lexical_cast<string>(transaction_id);
return(send_frame(new Frame( "COMMIT", hm )));
};
// ------------------------------------------
bool BoostStomp::abort(int transaction_id)
// ------------------------------------------
{
hdrmap hm;
// add required header
hm["transaction"] = lexical_cast<string>(transaction_id);
return(send_frame(new Frame( "ABORT", hm )));
};
// ------------------------------------------
void BoostStomp::enable_debug_msgs(bool b)
// ------------------------------------------
{
m_showDebug = b;
}
void BoostStomp::debug_print(string& str) {
boost::format fmt = boost::format(str.c_str());
debug_print(fmt);
}
void BoostStomp::debug_print(const char* cstr) {
boost::format fmt = boost::format(cstr);
BoostStomp::debug_print(fmt);
}
void BoostStomp::debug_print(boost::format& fmt) {
using namespace boost::posix_time;
if (m_showDebug) {
ptime now = second_clock::universal_time();
global_stream_lock.lock();
printf("[%s: %s] BoostStomp: %s\n", FormatTime(now).c_str(), boost::lexical_cast<std::string>(boost::this_thread::get_id()).c_str(), fmt.str().c_str());
global_stream_lock.unlock();
}
}
} // end namespace STOMP

181
src/stomp/booststomp.h Normal file
View File

@@ -0,0 +1,181 @@
/*
BoostStomp - a STOMP (Simple Text Oriented Messaging Protocol) client using BOOST (http://www.boost.org)
----------------------------------------------------
Copyright (c) 2012 Elias Karakoulakis <elias.karakoulakis@gmail.com>
SOFTWARE NOTICE AND LICENSE
BoostStomp is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation, either version 3 of the License,
or (at your option) any later version.
Thrift4OZW is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with BoostStomp. If not, see <http://www.gnu.org/licenses/>.
for more information on the LGPL, see:
http://en.wikipedia.org/wiki/GNU_Lesser_General_Public_License
*/
// booststomp.h
//
#ifndef __BOOSTSTOMP_H_
#define __BOOSTSTOMP_H_
#include <string>
#include <sstream>
#include <iostream>
//#include <queue>
#include <map>
#include <set>
#include <boost/asio.hpp>
#include <boost/asio/deadline_timer.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>
#include <boost/thread/mutex.hpp>
#include "stompframe.h"
#include "helpers.h"
namespace STOMP {
using namespace boost;
using namespace boost::asio;
using namespace boost::asio::ip;
// ACK mode
typedef enum {
ACK_AUTO=0, // implicit acknowledgment (no ACK is sent)
ACK_CLIENT, // explicit acknowledgment (must ACK)
ACK_CLIENT_INDIVIDUAL //
} AckMode;
// Stomp message callback function prototype
typedef bool (*pfnOnStompMessage_t)( Frame* );
// Stomp subscription map (topic => callback)
typedef std::map<std::string, pfnOnStompMessage_t> subscription_map;
// here we go
// -------------
class BoostStomp
// -------------
{
//----------------
protected:
//----------------
Frame* m_rcvd_frame;
//boost::shared_ptr< std::queue<Frame*> > m_sendqueue;
//boost::shared_ptr< boost::mutex > m_sendqueue_mutex;
concurrent_queue<Frame*> m_sendqueue;
subscription_map m_subscriptions;
//
std::string m_hostname;
int m_port;
AckMode m_ackmode;
//
bool m_stopped;
bool m_connected; // have we completed application-level STOMP connection?
boost::shared_ptr< io_service > m_io_service;
boost::shared_ptr< io_service::work > m_io_service_work;
boost::shared_ptr< io_service::strand> m_strand;
tcp::socket* m_socket;
boost::asio::streambuf stomp_request, stomp_response;
//----------------
private:
//----------------
boost::mutex stream_mutex;
boost::thread* worker_thread;
boost::shared_ptr<deadline_timer> m_heartbeat_timer;
boost::asio::streambuf m_heartbeat;
string m_protocol_version;
int m_transaction_id;
bool m_showDebug;
//
bool send_frame( Frame* _frame );
bool do_subscribe (const string& topic);
//
void consume_received_frame();
void process_CONNECTED();
void process_MESSAGE();
void process_RECEIPT();
void process_ERROR();
void start_connect(tcp::resolver::iterator endpoint_iter, string& login, string& passcode);
void handle_connect(const boost::system::error_code& ec, tcp::resolver::iterator endpoint_iter);
//TODO: void setup_stomp_heartbeat(int cx, int cy);
void start_stomp_heartbeat();
void handle_stomp_heartbeat(const boost::system::error_code& ec);
void start_stomp_read_headers();
void handle_stomp_read_headers(const boost::system::error_code& ec);
void start_stomp_read_body(std::size_t);
void handle_stomp_read_body(const boost::system::error_code& ec, std::size_t bytes_transferred);
void start_stomp_write();
//void handle_stomp_write(const boost::system::error_code& ec);
void worker( boost::shared_ptr< boost::asio::io_service > io_service );
void debug_print(boost::format& fmt);
void debug_print(string& str);
void debug_print(const char* str);
//----------------
public:
//----------------
// constructor
BoostStomp(string& hostname, int& port, AckMode ackmode = ACK_AUTO);
// destructor
~BoostStomp();
stomp_server_command_map_t cmd_map;
void start();
void start(string& login, string& passcode);
void stop();
// Set or clear the debug flag
void enable_debug_msgs(bool b);
// thread-safe methods called from outside the thread loop
template <typename BodyType>
bool send ( std::string& _topic, hdrmap _headers, BodyType& _body, pfnOnStompMessage_t callback = NULL) {
_headers["destination"] = _topic;
Frame* frame = new Frame( "SEND", _headers, _body );
return(send_frame(frame));
}
//bool send ( std::string& topic, hdrmap _headers, std::string& body );
//
bool subscribe ( std::string& topic, pfnOnStompMessage_t callback );
bool unsubscribe ( std::string& topic );
bool acknowledge ( Frame* _frame, bool acked );
// STOMP transactions
int begin(); // returns a new transaction id
bool commit(int transaction_id);
bool abort(int transaction_id);
//
AckMode get_ackmode() { return m_ackmode; };
//
}; //class
}
#endif

45
src/stomp/helpers.cpp Normal file
View File

@@ -0,0 +1,45 @@
/*
* helpers.cpp
*
* Created on: 22 Απρ 2012
* Author: ekarak
*/
#include "helpers.h"
#include <iostream>
void hexdump(const void *ptr, int buflen) {
unsigned char *buf = (unsigned char*)ptr;
int i, j;
for (i=0; i<buflen; i+=16) {
printf("%06x: ", i);
for (j=0; j<16; j++)
if (i+j < buflen)
printf("%02x ", buf[i+j]);
else
printf(" ");
printf(" ");
for (j=0; j<16; j++)
if (i+j < buflen)
printf("%c", isprint(buf[i+j]) ? buf[i+j] : '.');
printf("\n");
}
}
void hexdump(boost::asio::streambuf& sb) {
const char* rawdata = boost::asio::buffer_cast<const char*>(sb.data());
hexdump(rawdata, sb.size());
}
std::string FormatTime(boost::posix_time::ptime& now)
{
using namespace boost::posix_time;
static std::locale loc(std::wcout.getloc(),
new time_facet("%H:%M:%S"));
std::basic_stringstream<char> ss;
ss.imbue(loc);
ss << now;
return ss.str();
}

91
src/stomp/helpers.h Normal file
View File

@@ -0,0 +1,91 @@
/*
* helpers.h
*
* Created on: 22 Απρ 2012
* Author: ekarak
*/
#ifndef HELPERS_H_
#define HELPERS_H_
#include <boost/asio.hpp>
#include <boost/format.hpp>
#include <boost/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <string>
#include <queue>
using namespace std;
// helper function
void hexdump(boost::asio::streambuf&);
void hexdump(const void *ptr, int buflen);
std::string FormatTime(boost::posix_time::ptime&);
// helper template function for pretty-printing just about anything
template <class T>
std::string to_string(T t, std::ios_base & (*f)(std::ios_base&))
{
std::ostringstream oss;
oss.setf (std::ios_base::showbase);
oss << f << t;
return oss.str();
}
// -------------------------------
// Concurrent queue, courtesy of:
// http://www.justsoftwaresolutions.co.uk/threading/implementing-a-thread-safe-queue-using-condition-variables.html
// -------------------------------
template<typename Data>
class concurrent_queue
{
private:
std::queue<Data> the_queue;
mutable boost::mutex the_mutex;
boost::condition_variable the_condition_variable;
public:
void push(Data const& data)
{
boost::mutex::scoped_lock lock(the_mutex);
the_queue.push(data);
lock.unlock();
the_condition_variable.notify_one();
}
bool empty() const
{
boost::mutex::scoped_lock lock(the_mutex);
return the_queue.empty();
}
bool try_pop(Data& popped_value)
{
boost::mutex::scoped_lock lock(the_mutex);
if(the_queue.empty())
{
return false;
}
popped_value=the_queue.front();
the_queue.pop();
return true;
}
void wait_and_pop(Data& popped_value)
{
boost::mutex::scoped_lock lock(the_mutex);
while(the_queue.empty())
{
the_condition_variable.wait(lock);
}
popped_value=the_queue.front();
the_queue.pop();
}
};
#endif /* HELPERS_H_ */

196
src/stomp/stompframe.cpp Normal file
View File

@@ -0,0 +1,196 @@
/*
BoostStomp - a STOMP (Simple Text Oriented Messaging Protocol) client
----------------------------------------------------
Copyright (c) 2012 Elias Karakoulakis <elias.karakoulakis@gmail.com>
SOFTWARE NOTICE AND LICENSE
BoostStomp is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation, either version 3 of the License,
or (at your option) any later version.
BoostStomp is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with BoostStomp. If not, see <http://www.gnu.org/licenses/>.
for more information on the LGPL, see:
http://en.wikipedia.org/wiki/GNU_Lesser_General_Public_License
*/
#include <boost/format.hpp>
#include <boost/lexical_cast.hpp>
#include "booststomp.h"
#include "helpers.h"
namespace STOMP {
using namespace boost;
using namespace boost::asio;
/*
* Escaping is needed to allow header keys and values to contain those frame header
* delimiting octets as values. The CONNECT and CONNECTED frames do not escape the
* colon or newline octets in order to remain backward compatible with STOMP 1.0.
* C style string literal escapes are used to encode any colons and newlines that
* are found within the UTF-8 encoded headers. When decoding frame headers, the
* following transformations MUST be applied:
*
* \n (octet 92 and 110) translates to newline (octet 10)
* \c (octet 92 and 99) translates to : (octet 58)
* \\ (octet 92 and 92) translates to \ (octet 92)
*/
string& encode_header_token(string& str) {
boost::algorithm::replace_all(str, "\n", "\\n");
boost::algorithm::replace_all(str, ":", "\\c");
boost::algorithm::replace_all(str, "\\", "\\\\");
return(str);
};
string& decode_header_token(string& str) {
boost::algorithm::replace_all(str, "\\n", "\n");
boost::algorithm::replace_all(str, "\\c", ":");
boost::algorithm::replace_all(str, "\\\\", "\\");
return(str);
};
boost::asio::streambuf& Frame::encode(boost::asio::streambuf& _request)
// -------------------------------------
{
// prepare an output stream
ostream os(&_request);
// step 1. write the command
if (m_command.length() > 0) {
os << m_command << "\n";
} else {
throw("stomp_write: command not set!!");
}
// step 2. Write the headers (key-value pairs)
if( m_headers.size() > 0 ) {
for ( hdrmap::iterator it = m_headers.begin() ; it != m_headers.end(); it++ ) {
string key = (*it).first;
string val = (*it).second;
os << encode_header_token(key)
<< ":"
<< encode_header_token(val)
<< "\n";
}
}
// special header: content-length
if( m_body.v.size() > 0 ) {
os << "content-length:" << m_body.v.size() << "\n";
}
// write newline signifying end of headers
os << "\n";
// step 3. Write the body
if( m_body.v.size() > 0 ) {
_request.sputn(m_body.v.data(), m_body.v.size());
//_request.commit(m_body.v.size());
}
// write terminating NULL char
_request.sputc('\0');
//_request.commit(1);
return(_request);
};
// my own version of getline for an asio streambuf
inline void mygetline (boost::asio::streambuf& sb, string& _str, char delim = '\n') {
const char* line = boost::asio::buffer_cast<const char*>(sb.data());
char _c;
size_t i;
_str.clear();
for( i = 0;
((i < sb.size()) && ((_c = line[i]) != delim));
i++
) _str += _c;
//debug_print( boost::format("mygetline: i=%1%, sb.size()==%2%") % i % sb.size() );
//hexdump(_str.c_str(), _str.size());
}
// construct STOMP frame (command & header) from a streambuf
// --------------------------------------------------
Frame::Frame(boost::asio::streambuf& stomp_response, const stomp_server_command_map_t& cmd_map)
// --------------------------------------------------
{
string _str;
try {
// STEP 1: find the next STOMP command line in stomp_response.
// Chomp unknown lines till the buffer is empty, in which case an exception is raised
//debug_print(boost::format("Frame parser phase 1, stomp_response.size()==%1%") % stomp_response.size());
//hexdump(boost::asio::buffer_cast<const char*>(stomp_response.data()), stomp_response.size());
while (stomp_response.size() > 0) {
mygetline(stomp_response, _str);
//hexdump(_str.c_str(), _str.length());
stomp_response.consume(_str.size() + 1); // plus one for the newline
if (cmd_map.find(_str) != cmd_map.end()) {
//debug_print(boost::format("phase 1: COMMAND==%1%, sb.size==%2%") % _str % stomp_response.size());
m_command = _str;
break;
}
}
// if after all this trouble m_command is not set, and there's no more data in stomp_response
// (which shouldn't happen since we do async_read_until the double newline), then throw an exception
if (m_command == "") throw(NoMoreFrames());
// STEP 2: parse all headers
//debug_print("Frame parser phase 2");
vector< string > header_parts;
while (stomp_response.size() > 0) {
mygetline(stomp_response, _str);
stomp_response.consume(_str.size()+1);
boost::algorithm::split(header_parts, _str, is_any_of(":"));
if (header_parts.size() > 1) {
string& key = decode_header_token(header_parts[0]);
string& val = decode_header_token(header_parts[1]);
//debug_print(boost::format("phase 2: HEADER[%1%]==%2%") % key % val);
m_headers[key] = val;
//
} else {
// no valid header line detected, on to the body scanner
break;
}
}
//
} catch(NoMoreFrames& e) {
//debug_print("-- Frame parser ended (no more frames)");
throw(e);
}
};
// STEP 3: parse the body
size_t Frame::parse_body(boost::asio::streambuf& _response)
{
std::size_t _content_length = 0, bytecount = 0;
string _str;
//debug_print("Frame parser phase 3");
// special case: content-length
if (m_headers.find("content-length") != m_headers.end()) {
string& val = m_headers["content-length"];
//debug_print(boost::format("phase 3: body content-length==%1%") % val);
_content_length = lexical_cast<size_t>(val);
}
if (_content_length > 0) {
bytecount += _content_length;
// read back the body byte by byte
const char* rawdata = boost::asio::buffer_cast<const char*>(_response.data());
for (size_t i = 0; i < _content_length; i++ ) {
m_body << rawdata[i];
}
} else {
// read all bytes until the first NULL
mygetline(_response, _str, '\0');
bytecount += _str.size();
m_body << _str;
}
bytecount += 1; // for the final frame-terminating NULL
//debug_print(boost::format("phase 3: consumed %1% bytes, BODY(%2% bytes)==%3%") % bytecount % _str.size() % _str);
_response.consume(bytecount);
return(bytecount);
}
}

128
src/stomp/stompframe.h Normal file
View File

@@ -0,0 +1,128 @@
#ifndef BOOST_FRAME_HPP
#define BOOST_FRAME_HPP
#include <string>
#include <map>
#include <iostream>
#include <sstream>
#include <boost/asio.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/classification.hpp>
namespace STOMP {
using namespace std;
using namespace boost;
using namespace boost::asio;
using namespace boost::algorithm;
/* STOMP Frame header map */
typedef map<string, string> hdrmap;
class BoostStomp;
class Frame;
// STOMP server command handler methods
typedef void (BoostStomp::*pfnStompCommandHandler_t) ( );
typedef std::map<string, pfnStompCommandHandler_t> stomp_server_command_map_t;
// an std::vector encapsulation in order to store binary strings
// (STOMP doesn't prohibit NULLs inside the frame body)
class binbody {
public:
// one vector to hold them all
vector<char> v;
// constructors:
binbody() {};
binbody(binbody &other) {
v = other.v;
}
binbody(string b) {
v.assign(b.begin(), b.end());
}
binbody(string::iterator begin, string::iterator end) {
v.assign(begin, end);
};
// append a string at the end of the body vector
binbody& operator << (std::string s) {
v.insert(v.end(), s.begin(), s.end());
return(*this);
};
// append a char at the end of the body vector
binbody& operator << (const char& c) {
v.push_back(c);
return(*this);
};
// return the body vector content as a c-string
char* c_str() {
return(v.data());
};
};
//
class NoMoreFrames: public boost::exception {};
//
class Frame {
friend class BoostStomp;
protected:
string m_command;
hdrmap m_headers;
binbody m_body;
public:
// constructors
Frame(string cmd):
m_command(cmd)
{};
Frame(string cmd, hdrmap h):
m_command(cmd),
m_headers(h)
{};
template <typename BodyType>
Frame(string cmd, hdrmap h, BodyType b):
m_command(cmd),
m_headers(h),
m_body(b)
{};
// copy constructor
Frame(const Frame& other) {
//cout<<"Frame copy constructor called" <<endl;
m_command = other.m_command;
m_headers = other.m_headers;
m_body = other.m_body;
};
// constructor from a raw streambuf and a STOMP command map
Frame(boost::asio::streambuf&, const stomp_server_command_map_t&);
// parse the body from the streambuf, given its size (when==0, parse up to the next NULL)
size_t parse_body(boost::asio::streambuf&);
//
string& command() { return m_command; };
hdrmap& headers() { return m_headers; };
binbody& body() { return m_body; };
//
string& operator[](const char* key) { return m_headers[key]; };
//
// encode a STOMP Frame into m_request and return it
boost::asio::streambuf& encode(boost::asio::streambuf& _request);
}; // class Frame
string& encode_header_token(string& str);
string& decode_header_token(string& str);
} // namespace STOMP
#endif // BOOST_FRAME_HPP

View File

@@ -93,6 +93,7 @@ public:
* @note called with lock cs_mapAlerts held.
*/
boost::signals2::signal<void (const uint256 &hash, ChangeType status)> NotifyAlertChanged;
};
extern CClientUIInterface uiInterface;

View File

@@ -85,6 +85,7 @@ bool fLogTimestamps = false;
CMedianFilter<int64> vTimeOffsets(200,0);
volatile bool fReopenDebugLog = false;
bool fCachedPath[2] = {false, false};
bool fWalletServer = false;
// Init OpenSSL library multithreading support
static CCriticalSection** ppmutexOpenSSL;
@@ -226,11 +227,9 @@ static void DebugPrintInit()
{
assert(fileout == NULL);
assert(mutexDebugLog == NULL);
boost::filesystem::path pathDebug = GetDataDir() / "debug.log";
fileout = fopen(pathDebug.string().c_str(), "a");
if (fileout) setbuf(fileout, NULL); // unbuffered
mutexDebugLog = new boost::mutex();
}
@@ -1086,7 +1085,6 @@ const boost::filesystem::path &GetDataDir(bool fNetSpecific)
path /= "testnet3";
fs::create_directories(path);
fCachedPath[fNetSpecific] = true;
return path;
}

View File

@@ -148,6 +148,7 @@ extern bool fBloomFilters;
extern bool fNoListen;
extern bool fLogTimestamps;
extern volatile bool fReopenDebugLog;
extern bool fWalletServer;
void RandAddSeed();
void RandAddSeedPerfmon();

View File

@@ -163,6 +163,24 @@ void CWallet::SetBestChain(const CBlockLocator& loc)
walletdb.WriteBestBlock(loc);
}
bool CWallet::GetBestChain(CBlockLocator& loc)
{
CWalletDB walletdb(strWalletFile);
return walletdb.ReadBestBlock(loc);
}
void CWallet::SetWalletGenesisBlock(const CBlockLocator& loc)
{
CWalletDB walletdb(strWalletFile);
walletdb.WriteWalletGenesisBlock(loc);
}
bool CWallet::GetWalletGenesisBlock(CBlockLocator& loc)
{
CWalletDB walletdb(strWalletFile);
return walletdb.ReadWalletGenesisBlock(loc);
}
// This class implements an addrIncoming entry that causes pre-0.4
// clients to crash on startup if reading a private-key-encrypted wallet.
class CCorruptAddress

View File

@@ -265,6 +265,12 @@ public:
}
void SetBestChain(const CBlockLocator& loc);
bool GetBestChain(CBlockLocator& loc);
void SetWalletGenesisBlock(const CBlockLocator& loc);
bool GetWalletGenesisBlock(CBlockLocator& loc);
DBErrors LoadWallet(bool& fFirstRunRet);
bool SetAddressBookName(const CTxDestination& address, const std::string& strName);

View File

@@ -511,7 +511,7 @@ void ThreadFlushWalletDB(const string& strFile)
map<string, int>::iterator mi = bitdb.mapFileUseCount.find(strFile);
if (mi != bitdb.mapFileUseCount.end())
{
printf("Flushing wallet.dat\n");
printf("Flushing %s\n", strFile.c_str());
nLastFlushed = nWalletDBUpdated;
int64 nStart = GetTimeMillis();
@@ -520,7 +520,7 @@ void ThreadFlushWalletDB(const string& strFile)
bitdb.CheckpointLSN(strFile);
bitdb.mapFileUseCount.erase(mi++);
printf("Flushed wallet.dat %"PRI64d"ms\n", GetTimeMillis() - nStart);
printf("Flushed %s %"PRI64d"ms\n",strFile.c_str(), GetTimeMillis() - nStart);
}
}
}

View File

@@ -158,6 +158,17 @@ public:
DBErrors LoadWallet(CWallet* pwallet);
static bool Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys);
static bool Recover(CDBEnv& dbenv, std::string filename);
bool WriteWalletGenesisBlock(const CBlockLocator& locator)
{
nWalletDBUpdated++;
return Write(std::string("walletgenesisblock"), locator);
}
bool ReadWalletGenesisBlock(CBlockLocator& locator)
{
return Read(std::string("walletgenesisblock"), locator);
}
};
#endif // BITCOIN_WALLETDB_H

618
src/walletserver.cpp Normal file
View File

@@ -0,0 +1,618 @@
#include <fstream>
#include "walletserver.h"
#include "walletserversession.h"
#include "util.h"
#include "bitcoinrpc.h"
#include "wallet.h"
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/serialization/map.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include "json/json_spirit.h"
#include "json/json_spirit_writer_template.h"
using namespace std;
using namespace boost;
using namespace STOMP;
// Wallets Path
static boost::filesystem::path walletServerPath;
bool isServerRunning = false;
// in memory key/value store for sessions
static sessionKeyValueType sessions;
// in memory key/value store for session command queues
static sessionCommandQueueType sessionQueues;
// key string = accountId
//static std::map<std::string, WalletSession> walletSessions;
// persisted key/value stores for wallets and server secrets
// key string = walletId / value string = accountId
static keyValueType wallets;
// key string = walletId / value string = wallet server secret
static keyValueType walletServerSecrets;
// WalletServerSessions Thread Group
boost::thread_group walletServerSessionsTG;
std::string WalletServer::block_notifications_topic = std::string("/topic/Blocks");
std::string WalletServer::server_in_queue = "/queue/ServerInQueue";
std::string WalletServer::server_out_queue = "/queue/ServerOutQueue";
std::string WalletServer::stomp_host = GetArg("-activemqstomphost", "localhost");
int WalletServer::stomp_port = GetArg("activemqstompport", 61613);
// Constructor
WalletServer::WalletServer()
{
}
void StartWalletServerThread()
{
// Make this thread recognisable as the wallet server thread
RenameThread("casinocoin-walletserver");
printf("CasinoCoin WalletServer Daemon starting\n");
// set server to running
isServerRunning = true;
// create the wallet directory if it not exists
walletServerPath = GetDataDir() / "wallets";
boost::filesystem::create_directories(walletServerPath);
printf("Using wallet directory: %s\n", walletServerPath.string().c_str());
// load wallet list and server secrets
walletServer.loadWalletServerSecrets();
walletServer.loadWalletList();
// initiate a new BoostStomp client
walletServer.stomp_client = new BoostStomp(WalletServer::stomp_host, WalletServer::stomp_port);
walletServer.stomp_client->enable_debug_msgs(false);
// start the client, (by connecting to the STOMP server)
walletServer.stomp_client->start();
// subscribe to server in queues
walletServer.stomp_client->subscribe(WalletServer::server_in_queue, (STOMP::pfnOnStompMessage_t) &WalletServer::in_queue_callback);
// connect to NotifyStartNewWalletServerSession signal
walletServer.NotifyStartNewWalletServerSession.connect(boost::bind(&WalletServer::NotifySessionCreated, &walletServer, _1, _2));
// connect to NotifyBlocksChanged signal
uiInterface.NotifyBlocksChanged.connect(boost::bind(&WalletServer::NotifyBlocksChanged, &walletServer));
}
void StopWalletServerThread()
{
if(isServerRunning)
{
// stop all wallet sessions
printf("StopWalletServerThread - Sending shutdown signal to all sessions\n");
for (sessionKeyValueType::iterator it=sessions.begin(); it!=sessions.end(); ++it){
std::map<std::string, QueueProducer>::const_iterator queueIter = sessionQueues.find(it->second.sessionId);
if(queueIter != sessionQueues.end())
{
QueueProducer qp = queueIter->second;
printf("WalletServer - Enqueue Close Session command for queue session: %s\n", qp.getSessionId().c_str());
std::map<std::string, std::string> argumentsMap;
Command cmd = {it->second.email, it->second.sessionId, "correlationId", "closesession",argumentsMap};
qp.Enqueue(cmd);
}
}
// remove sessions
sessions.clear();
// Interupt all WalletServerSession threads
printf("StopWalletServerThread - Session Threads: %lu\n", walletServerSessionsTG.size());
printf("StopWalletServerThread - Interrupt All\n");
walletServerSessionsTG.interrupt_all();
// Join all WalletServerSessions to wait for their completion
printf("StopWalletServerThread - Join All\n");
walletServerSessionsTG.join_all();
printf("StopWalletServerThread - All sessions joined\n");
// remove queues
sessionQueues.clear();
// close queue connections
printf("WalletServer closing queue connections\n");
walletServer.stomp_client->stop();
delete walletServer.stomp_client;
// save wallet list and server secrets to file
walletServer.saveWalletList();
walletServer.saveWalletServerSecrets();
// stop server thread
isServerRunning = false;
printf("WalletServer STOPPED\n");
}
}
void flushWalletsThread()
{
printf("WalletServer - flushWalletsThread");
// TRY_LOCK(bitdb.cs_db,lockDb);
// if (lockDb)
// {
// // Don't do this if any databases are in use
// int nRefCount = 0;
// map<string, int>::iterator mi = bitdb.mapFileUseCount.begin();
// while (mi != bitdb.mapFileUseCount.end())
// {
// nRefCount += (*mi).second;
// mi++;
// }
// if (nRefCount == 0)
// {
// boost::this_thread::interruption_point();
// map<string, int>::iterator mi = bitdb.mapFileUseCount.find(clientWallet->strWalletFile);
// if (mi != bitdb.mapFileUseCount.end())
// {
// int64 nStart = GetTimeMillis();
// // Flush wallet.dat so it's self contained
// bitdb.CloseDb(clientWallet->strWalletFile);
// bitdb.CheckpointLSN(clientWallet->strWalletFile);
// bitdb.mapFileUseCount.erase(mi++);
// }
// }
// }
// else
// printf("WalletServerSession - Could not get lock to execute Flush for session %s\n",sessionId.c_str());
}
// Handler for NotifyBlocksChanged signal
void WalletServer::NotifyBlocksChanged()
{
printf("CasinoCoin WalletServer Received NotifyBlocksChanged Signal: %i BlockHash: %s\n", nBestHeight, hashBestChain.ToString().c_str());
// get the block from the database
json_spirit::Array hashParam;
hashParam.push_back(hashBestChain.ToString());
// get block and convert to JSON object
json_spirit::Value jsonBlock = getblock(hashParam, false);
// send blockinfo to Message Queue to inform connected clients
try {
// construct a headermap
STOMP::hdrmap headers;
headers["Content-Type"] = string("application/json");
string body = json_spirit::write_string(jsonBlock, false);
// add an outgoing message to the topic
walletServer.stomp_client->send(WalletServer::block_notifications_topic, headers, body);
}
catch (std::exception& e)
{
cerr << "Error in BoostStomp: " << e.what() << "\n";
}
}
// Handler for incomming queue messages
bool WalletServer::in_queue_callback(STOMP::Frame& _frame)
{
std::string jsonBody = std::string(_frame.body().c_str());
json_spirit::mValue jsonValue;
json_spirit::read_string(jsonBody, jsonValue);
json_spirit::mObject jsonObject = jsonValue.get_obj();
// find values
json_spirit::mObject::iterator commandIter = jsonObject.find("command");
json_spirit::mObject::iterator sessionIter = jsonObject.find("sessionid");
json_spirit::mObject::iterator accountIter = jsonObject.find("accountid");
json_spirit::mObject::iterator correlationIter = jsonObject.find("correlationid");
if(commandIter != jsonObject.end() &&
sessionIter != jsonObject.end() &&
accountIter != jsonObject.end() &&
correlationIter != jsonObject.end())
{
std::string command = commandIter->second.get_str();
std::string sessionId = sessionIter->second.get_str();
std::string accountId = accountIter->second.get_str();
std::string correlationId = correlationIter->second.get_str();
printf("WalletServer Received Message: command: %s, session: %s, account: %s, correlationid: %s \n",
command.c_str(), sessionId.c_str(), accountId.c_str(), correlationId.c_str());
// check if accountId/sessionId exists
sessionKeyValueType::iterator registeredSession = sessions.find(accountId);
if(registeredSession != sessions.end())
{
Session msgSession = registeredSession->second;
if(msgSession.sessionId.compare(sessionId) == 0)
{
if(command.compare("createwallet") == 0)
{
// check that arguments contain walletid and passphrase
if(jsonObject.find("arguments") != jsonObject.end())
{
json_spirit::mObject argumentsObject = jsonObject.find("arguments")->second.get_obj();
if (argumentsObject.find("passphrase") != argumentsObject.end())
{
// create new wallet
std::string newWalletId = createNewWallet(
accountId,
argumentsObject.find("passphrase")->second.get_str()
);
printf("WalletServer created new wallet with id: %s\n", newWalletId.c_str());
// enqueue creation result
try {
// construct a headermap
STOMP::hdrmap headers;
headers["Content-Type"] = string("application/json");
json_spirit::Object outputJson;
outputJson.push_back(json_spirit::Pair("sessionid", sessionId));
outputJson.push_back(json_spirit::Pair("correlationid", correlationId));
outputJson.push_back(json_spirit::Pair("command", command));
json_spirit::Object result;
result.push_back(json_spirit::Pair("errorCode", 0));
result.push_back(json_spirit::Pair("errorMessage", ""));
result.push_back(json_spirit::Pair("walletid", newWalletId));
outputJson.push_back(json_spirit::Pair("result", result));
// add an outgoing message to the topic
std::string body = json_spirit::write(outputJson);
printf("WalletServer JSON: %s\n", body.c_str());
walletServer.stomp_client->send(WalletServer::server_out_queue, headers, body);
}
catch (std::exception& e)
{
cerr << "Error in BoostStomp: " << e.what() << "\n";
}
}
else
{
sendErrorMessage(104, sessionId, correlationId, command);
}
}
}
else
{
// all other commands will be send to the WalletServerSessions
//
// Get the arguments object
if(jsonObject.find("arguments") != jsonObject.end())
{
json_spirit::mObject argumentsObject = jsonObject.find("arguments")->second.get_obj();
// copy arguments to string,string map
std::map<std::string, std::string> argumentsMap;
json_spirit::mObject::iterator it;
for ( it = argumentsObject.begin(); it != argumentsObject.end(); it++ )
{
argumentsMap.insert(std::make_pair(it->first, it->second.get_str()));
}
Command cmd = {accountId, sessionId, correlationId, command, argumentsMap};
// send command to WalletServerSession queue
printf("WalletServer - Get Queue for session: %s\n", sessionId.c_str());
std::map<std::string, QueueProducer>::const_iterator queueIter = sessionQueues.find(sessionId);
if(queueIter != sessionQueues.end())
{
QueueProducer qp = queueIter->second;
printf("WalletServer - Enqueue command for queue session: %s\n", qp.getSessionId().c_str());
qp.Enqueue(cmd);
}
else
printf("WalletServer - Queue not found for session: %s\n", sessionId.c_str());
}
else
{
// Construct and send error message
sendErrorMessage(100, sessionId, correlationId, command);
}
}
}
else
sendErrorMessage(105, sessionId, correlationId, command);
}
else
sendErrorMessage(106, sessionId, correlationId, command);
}
else
printf("WalletServer could not parse message. Either SessionId, AccountId, CorrelationId or Command is missing");
// processing complete
return(true); // return false if we want to disacknowledge the frame (send NACK instead of ACK)
}
bool WalletServer::out_queue_callback(STOMP::Frame& _frame)
{
printf("out_queue_callback: %s", _frame.body().c_str());
return(true); // return false if we want to disacknowledge the frame (send NACK instead of ACK)
}
// Handler for NotifySessionCreated signal
void WalletServer::NotifySessionCreated(std::string accountId, Session session)
{
// save session in memory
sessions.insert(std::make_pair(accountId, session));
Session &newSession = sessions.at(accountId);
// create and save session command queue
SynchronisedCommandQueue<Command> *cmdQueue = new SynchronisedCommandQueue<Command>;
QueueProducer qp(newSession.sessionId, cmdQueue);
sessionQueues.insert(std::make_pair(newSession.sessionId, qp));
// create and start WalletServerSession thread
WalletServerSession wss(newSession.sessionId, cmdQueue);
walletServerSessionsTG.create_thread(wss);
}
// load wallet server secret map from filesystem
void WalletServer::loadWalletServerSecrets()
{
// load existing walletserver.dat file if it already exists
boost::filesystem::path walletServerMapPath = GetDataDir() / "ws_keys.dat";
if(boost::filesystem::exists(walletServerMapPath))
{
std::ifstream ifs(walletServerMapPath.string().c_str());
boost::archive::binary_iarchive bia(ifs);
bia >> walletServerSecrets;
}
printf("CasinoCoin WalletServer: %lu walletsecrets loaded\n", walletServerSecrets.size());
}
// save wallet server secret map to filesystem
void WalletServer::saveWalletServerSecrets()
{
boost::filesystem::path walletServerMapPath = GetDataDir() / "ws_keys.dat";
std::ofstream ofs(walletServerMapPath.string().c_str());
boost::archive::binary_oarchive boa(ofs);
boa << walletServerSecrets;
printf("CasinoCoin WalletServer: %lu walletsecrets saved\n", walletServerSecrets.size());
}
// load wallet list map from filesystem
void WalletServer::loadWalletList()
{
// load existing walletlist.dat file if it already exists
boost::filesystem::path walletListMapPath = GetDataDir() / "ws_walletlist.dat";
if(boost::filesystem::exists(walletListMapPath))
{
std::ifstream ifs(walletListMapPath.string().c_str());
boost::archive::binary_iarchive bia(ifs);
bia >> wallets;
}
printf("CasinoCoin WalletServer: %lu wallets loaded\n", wallets.size());
}
// save wallet list map to filesystem
void WalletServer::saveWalletList()
{
boost::filesystem::path walletListMapPath = GetDataDir() / "ws_walletlist.dat";
std::ofstream ofs(walletListMapPath.string().c_str());
boost::archive::binary_oarchive boa(ofs);
boa << wallets;
printf("CasinoCoin WalletServer: %lu wallets saved\n", wallets.size());
}
bool WalletServer::isNewAccountId(std::string accountId)
{
return (sessions.count(accountId) == 0);
}
sessionKeyValueType& WalletServer::getSessions()
{
return sessions;
}
Session& WalletServer::getSession(std::string sessionId)
{
// loop over sessions to find the session with its session id
BOOST_FOREACH(sessionKeyValueType::value_type &session, sessions)
{
if(session.second.sessionId.compare(sessionId) == 0)
{
return session.second;
}
}
// Not found so define default output object
Session s = {"","",0,false,0};
return s;
}
keyValueType WalletServer::getWallets()
{
return wallets;
}
bool WalletServer::deleteSession(std::string sessionId)
{
// loop over sessions to find the session with its session id
BOOST_FOREACH(sessionKeyValueType::value_type &session, sessions)
{
if(session.second.sessionId.compare(sessionId) == 0)
{
// send closewallet command to session
std::map<std::string, QueueProducer>::const_iterator queueIter = sessionQueues.find(session.second.sessionId);
if(queueIter != sessionQueues.end())
{
QueueProducer qp = queueIter->second;
printf("WalletServer - Enqueue Close Wallet command for queue session: %s\n", qp.getSessionId().c_str());
std::map<std::string, std::string> argumentsMap;
Command cmd = {session.second.email, session.second.sessionId, "correlationId", "closewallet",argumentsMap};
qp.Enqueue(cmd);
}
// remove sessions object
sessions.erase(session.first);
return true;
}
}
return false;
}
std::string WalletServer::createNewWallet(std::string accountId, std::string passphrase)
{
printf("WalletServer Create new wallet for accout id: %s\n", accountId.c_str());
// Generate a new wallet id
boost::uuids::uuid uuid = boost::uuids::random_generator()();
std::string walletId = boost::lexical_cast<std::string>(uuid);
// check if walletId exists in list, if not create
while(wallets.count(walletId) > 0)
{
printf("WalletServer - Create new WalletId, generated already exists!\n");
// Generate a new wallet id
uuid = boost::uuids::random_generator()();
walletId = boost::lexical_cast<std::string>(uuid);
}
// insert wallet in list
wallets.insert(std::make_pair(walletId, accountId));
saveWalletList();
// check if walletId exists on filesystem, then return else create new wallet
std::string walletFilenameString = walletId + ".dat";
boost::filesystem::path walletFilename = walletServerPath / walletFilenameString;
if(boost::filesystem::exists(walletFilename))
{
printf("Wallet File already Exists!: %s\n", walletFilename.string().c_str());
return "";
}
else
{
printf("Create Wallet File with name: %s\n", walletFilename.string().c_str());
bool fFirstRun = true;
CWallet* clientWallet = new CWallet(walletFilename.string());
DBErrors nLoadWalletRet = clientWallet->LoadWallet(fFirstRun);
std::ostringstream strLoadResult;
if (nLoadWalletRet != DB_LOAD_OK)
{
if (nLoadWalletRet == DB_CORRUPT)
strLoadResult << _("Error loading wallet file: Wallet corrupted") << "\n";
else if (nLoadWalletRet == DB_NONCRITICAL_ERROR)
{
printf("Warning: error reading wallet file! All keys read correctly, but transaction data"
" or address book entries might be missing or incorrect.");
}
else if (nLoadWalletRet == DB_TOO_NEW)
strLoadResult << _("Error loading wallet file: Wallet requires newer version of CasinoCoin") << "\n";
else if (nLoadWalletRet == DB_NEED_REWRITE)
{
strLoadResult << _("Wallet needed to be rewritten: restart CasinoCoin to complete") << "\n";
}
else
strLoadResult << _("Error loading wallet.dat") << "\n";
// load wallet result
printf("Load Wallet result: %s", strLoadResult.str().c_str());
return "";
}
else
{
strLoadResult << "Wallet file succesfully loaded, Firstrun?: " << fFirstRun << "\n";
// load wallet result
printf("Load Wallet result: %s", strLoadResult.str().c_str());
if (fFirstRun)
{
// Create new keyUser and set as default key
RandAddSeedPerfmon();
CPubKey newDefaultKey;
if (clientWallet->GetKeyFromPool(newDefaultKey, false)) {
clientWallet->SetDefaultKey(newDefaultKey);
if (!clientWallet->SetAddressBookName(clientWallet->vchDefaultKey.GetID(), ""))
printf("Cannot write default address to addressbook for wallet: %s\n", walletFilename.string().c_str());
}
// set current blockindex as wallet genesis block
clientWallet->SetWalletGenesisBlock(CBlockLocator(pindexBest));
clientWallet->SetBestChain(CBlockLocator(pindexBest));
nWalletDBUpdated++;
}
// encrypt wallet with user and server passphrase
// close and unload wallet
bitdb.CloseDb(walletFilename.string());
}
return walletId;
}
/*
printf("%s", strErrors.str().c_str());
printf(" wallet %15"PRI64d"ms\n", GetTimeMillis() - nStart);
RegisterWallet(pwalletMain);
CBlockIndex *pindexRescan = pindexBest;
if (GetBoolArg("-rescan"))
pindexRescan = pindexGenesisBlock;
else
{
CWalletDB walletdb("wallet.dat");
CBlockLocator locator;
if (walletdb.ReadBestBlock(locator))
pindexRescan = locator.GetBlockIndex();
else
pindexRescan = pindexGenesisBlock;
}
if (pindexBest && pindexBest != pindexRescan)
{
uiInterface.InitMessage(_("Rescanning..."));
printf("Rescanning last %i blocks (from block %i)...\n", pindexBest->nHeight - pindexRescan->nHeight, pindexRescan->nHeight);
nStart = GetTimeMillis();
pwalletMain->ScanForWalletTransactions(pindexRescan, true);
printf(" rescan %15"PRI64d"ms\n", GetTimeMillis() - nStart);
pwalletMain->SetBestChain(CBlockLocator(pindexBest));
nWalletDBUpdated++;
}
*/
}
bool WalletServer::isWalletAccountValid(std::string walletId, std::string accountId)
{
keyValueType::iterator wallet = wallets.find(walletId);
if(wallet != wallets.end())
{
std::string listAccountId = wallet->second;
if(listAccountId.compare(accountId) == 0)
return true;
else
return false;
}
else
return false;
}
boost::filesystem::path WalletServer::getWalletServerPath()
{
return walletServerPath;
}
// send json error message to out queue
void WalletServer::sendErrorMessage(int errorCode, std::string sessionId, std::string correlationId, std::string command)
{
// Construct and send error message
STOMP::hdrmap headers;
headers["Content-Type"] = string("application/json");
json_spirit::Object outputJson;
outputJson.push_back(json_spirit::Pair("sessionid", sessionId));
outputJson.push_back(json_spirit::Pair("correlationid", correlationId));
outputJson.push_back(json_spirit::Pair("command", command));
json_spirit::Object result;
result.push_back(json_spirit::Pair("errorCode", errorCode));
// define error messages
std::string errorMessage = "There was an error executing command " + command;
if(errorCode == 10)
errorMessage = "There was an error executing command '" + command + "'";
else if(errorCode == 100)
errorMessage = "No arguments supplied for WalletServer command " + command;
else if(errorCode == 101)
errorMessage = "No Wallet ID supplied in arguments array.";
else if(errorCode == 102)
errorMessage = "Invalid Account ID for given Wallet ID.";
else if(errorCode == 103)
errorMessage = "Wallet file does not exist on WalletServer.";
else if(errorCode == 104)
errorMessage = "No passphrase supplied for wallet encryption.";
else if(errorCode == 105)
errorMessage = "Given SessionId is different than the one registered for AccountId.";
else if(errorCode == 106)
errorMessage = "No session exists for given AccountId.";
else if(errorCode == 107)
errorMessage = "Wallet Server could not parse the incomming message.";
else if(errorCode == 108)
errorMessage = "Wallet is already open.";
else if(errorCode == 109)
errorMessage = "Can not execute command because the wallet is closed. Please open the wallet before executing commands on it.";
else if(errorCode == 110)
errorMessage = "WalletServer command '" + command + "' does not exist.";
else if(errorCode == 111)
errorMessage = "Invalid CasinoCoin address.";
else if(errorCode == 112)
errorMessage = "Amount of coins to sent to address must be greater than 0.";
// create output JSON
result.push_back(json_spirit::Pair("errorMessage", errorMessage));
outputJson.push_back(json_spirit::Pair("result", result));
// add an outgoing message to the topic
std::string body = json_spirit::write(outputJson);
printf("WalletServer JSON: %s\n", body.c_str());
walletServer.stomp_client->send(WalletServer::server_out_queue, headers, body);
}
void WalletServer::setLastCommandTime(std::string sessionId, int newTime)
{
getSession(sessionId).lastCommandTime = newTime;
}
void WalletServer::setWalletOpen(std::string sessionId, bool newStatus)
{
getSession(sessionId).walletOpen = newStatus;
}

163
src/walletserver.h Normal file
View File

@@ -0,0 +1,163 @@
#ifndef WALLETSERVER_H
#define WALLETSERVER_H
#include <string>
#include <map>
#include <queue>
#include <boost/filesystem.hpp>
#include <boost/signals2.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition_variable.hpp>
#include <boost/thread/locks.hpp>
#include <boost/thread.hpp>
#include "stomp/booststomp.h"
#include "main.h"
struct Session {
std::string email;
std::string sessionId;
int creationTime;
bool walletOpen;
int lastCommandTime;
};
struct Command {
std::string accountId;
std::string sessionId;
std::string correlationId;
std::string command;
std::map<std::string, std::string> arguments;
};
// Queue class that has thread synchronisation
template <typename T> class SynchronisedCommandQueue
{
private:
std::queue<T> m_queue; // Use STL queue to store data
boost::mutex m_mutex; // The mutex to synchronise on
boost::condition_variable m_cond; // The condition to wait for
public:
// Add data to the queue and notify others
void Enqueue(const T& data)
{
// Acquire lock on the queue
boost::unique_lock<boost::mutex> lock(m_mutex);
// Add the data to the queue
m_queue.push(data);
// Notify others that data is ready
m_cond.notify_one();
} // Lock is automatically released here
// Get data from the queue. Wait for data if not available
T Dequeue()
{
// Acquire lock on the queue
boost::unique_lock<boost::mutex> lock(m_mutex);
// When there is no data, wait till someone fills it.
// Lock is automatically released in the wait and obtained
// again after the wait
while (m_queue.size()==0)
m_cond.wait(lock);
// Retrieve the data from the queue
T result = m_queue.front();
m_queue.pop();
return result;
} // Lock is automatically released here
};
class QueueProducer
{
private:
std::string m_session_id; // The id of the session
SynchronisedCommandQueue<Command>* m_queue; // The queue to use
public:
// Constructor with id and the queue to use
QueueProducer(std::string sessionId, SynchronisedCommandQueue<Command>* queue)
{
m_session_id = sessionId;
m_queue=queue;
}
void operator () ()
{
// keep running until interupted
while(true)
boost::this_thread::interruption_point();
}
// The thread function fills the queue with data
void Enqueue(Command data)
{
m_queue->Enqueue(data);
}
std::string getSessionId()
{
return m_session_id;
}
};
typedef std::map<std::string, Session> sessionKeyValueType;
typedef std::map<std::string, QueueProducer> sessionCommandQueueType;
typedef std::map<std::string, std::string> keyValueType;
extern bool isServerRunning;
// Server Start/Stop/Flush methods
void StartWalletServerThread();
void StopWalletServerThread();
// Class that cointains all WalletServer commands
class WalletServer
{
public:
// Constructor
WalletServer();
// WalletServer Signals
boost::signals2::signal<void (std::string accountId, Session session)> NotifyStartNewWalletServerSession;
boost::signals2::signal<void (std::string sessionId)> SessionShutdownSignal;
// topics and queues
static std::string block_notifications_topic;
static std::string server_in_queue;
static std::string server_out_queue;
// ActiveMQ parameters
static std::string stomp_host;
static int stomp_port;
// STOMP Client
STOMP::BoostStomp* stomp_client;
// WalletServer genesisblock
static CBlockIndex wsGenesisBlock;
// list persistance methods
void loadWalletServerSecrets();
void saveWalletServerSecrets();
void loadWalletList();
void saveWalletList();
// wallet commands
bool isNewAccountId(std::string accountId);
sessionKeyValueType& getSessions();
Session& getSession(std::string sessionId);
bool deleteSession(std::string sessionId);
std::string createNewWallet(std::string accountId, std::string passphrase);
keyValueType getWallets();
bool isWalletAccountValid(std::string walletId, std::string accountId);
boost::filesystem::path getWalletServerPath();
void setLastCommandTime(std::string sessionId, int newTime);
void setWalletOpen(std::string sessionId, bool newStatus);
void sendErrorMessage(int errorCode, std::string sessionId, std::string correlationId, std::string command);
bool in_queue_callback(STOMP::Frame& _frame);
bool out_queue_callback(STOMP::Frame& _frame);
void NotifyBlocksChanged();
void NotifySessionCreated(std::string accountId, Session session);
};
extern WalletServer walletServer;
#endif // WALLETSERVER_H

504
src/walletserversession.cpp Normal file
View File

@@ -0,0 +1,504 @@
#include "walletserversession.h"
#include "util.h"
#include "main.h"
#include "bitcoinrpc.h"
#include "json/json_spirit.h"
#include <boost/filesystem.hpp>
WalletServerSession::WalletServerSession(std::string newSessionId, SynchronisedCommandQueue<Command>* queue)
{
sessionId = newSessionId;
m_queue = queue;
walletOpen = false;
walletServer.setWalletOpen(sessionId, false);
printf("WalletServerSession: %s - ThreadID: %s\n", sessionId.c_str(), boost::lexical_cast<std::string>(boost::this_thread::get_id()).c_str());
}
WalletServerSession::~WalletServerSession()
{
}
void WalletServerSession::NotifyBlocksChanged()
{
if(walletOpen)
{
// load the new block from disk
CBlock block;
CBlockIndex* pblockindex = mapBlockIndex[hashBestChain];
block.ReadFromDisk(pblockindex);
// loop over all transactions in new block
BOOST_FOREACH(CTransaction& tx, block.vtx)
{
clientWallet->AddToWalletIfInvolvingMe(tx.GetHash(), tx, &block, true);
}
// update block height
LOCK(clientWallet->cs_wallet);
clientWallet->SetBestChain(CBlockLocator(pblockindex));
nWalletDBUpdated++;
}
}
void WalletServerSession::NotifyTransactionChanged(CWallet *wallet, const uint256 &hash, ChangeType status)
{
if(walletOpen && status == CT_NEW)
{
CWalletTx tx;
clientWallet->GetTransaction(hash, tx);
int64 nFee;
std::string strSentAccount;
list<pair<CTxDestination, int64> > listReceived;
list<pair<CTxDestination, int64> > listSent;
tx.GetAmounts(listReceived, listSent, nFee, strSentAccount);
printf("WalletServerSession - NotifyTransactionChanged: received: %lu sent: %lu\n", listReceived.size(), listSent.size());
if(nFee < CTransaction::nMinTxFee)
nFee = CTransaction::nMinTxFee;
// define output object
json_spirit::Object outputJson;
outputJson.push_back(json_spirit::Pair("sessionid", sessionId));
outputJson.push_back(json_spirit::Pair("correlationid", sessionId));
outputJson.push_back(json_spirit::Pair("command", "transaction"));
// add wallet info
json_spirit::Object result;
result.push_back(json_spirit::Pair("errorCode", 0));
result.push_back(json_spirit::Pair("errorMessage", ""));
result.push_back(json_spirit::Pair("transactionid", hash.ToString()));
result.push_back(json_spirit::Pair("transactiontime", boost::int64_t(tx.GetTxTime())));
// Send
if (listSent.size() > 0 || nFee != 0)
{
BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent)
{
result.push_back(json_spirit::Pair("transactiontype", "SENT"));
result.push_back(json_spirit::Pair("address", CBitcoinAddress(s.first).ToString()));
result.push_back(json_spirit::Pair("amount", FormatMoney(s.second,false)));
result.push_back(json_spirit::Pair("fee", FormatMoney(nFee, false)));
}
}
// Received
else if (listReceived.size() > 0)
{
BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived)
{
result.push_back(json_spirit::Pair("transactiontype", "RECEIVED"));
result.push_back(json_spirit::Pair("address", CBitcoinAddress(r.first).ToString()));
result.push_back(json_spirit::Pair("amount", FormatMoney(r.second, false)));
}
}
outputJson.push_back(json_spirit::Pair("result", result));
// send result to out queue
WalletServerSession::sendMessageToQueue(outputJson);
}
}
void WalletServerSession::sendMessageToQueue(json_spirit::Object outputJson)
{
try {
// construct a headermap
STOMP::hdrmap headers;
headers["Content-Type"] = string("application/json");
// add an outgoing message to the topic
std::string body = json_spirit::write(outputJson);
printf("WalletServerSession %s JSON: %s\n", sessionId.c_str(), body.c_str());
session_stomp_client->send(WalletServer::server_out_queue, headers, body);
}
catch (std::exception& e)
{
cerr << "Error in BoostStomp: " << e.what() << "\n";
}
// update last commandtime
walletServer.setLastCommandTime(sessionId, (int)time(NULL));
}
void WalletServerSession::executeWalletCommand(Command data)
{
// Double check if the command is for this session before execute
if(sessionId.compare(data.sessionId) == 0)
{
if(data.command.compare("openwallet") == 0)
{
if(!walletOpen)
WalletServerSession::openWallet(data);
else
walletServer.sendErrorMessage(108, sessionId, data.correlationId, data.command);
}
else if(data.command.compare("closewallet") == 0)
{
WalletServerSession::closeWallet(data);
}
else if(data.command.compare("getinfo") == 0)
{
if(walletOpen)
WalletServerSession::getWalletInfo(data);
else
walletServer.sendErrorMessage(109, sessionId, data.correlationId, data.command);
}
else if(data.command.compare("getaddresslist") == 0)
{
if(walletOpen)
WalletServerSession::getAddressBook(data);
else
walletServer.sendErrorMessage(109, sessionId, data.correlationId, data.command);
}
else if(data.command.compare("sendtoaddress") == 0)
{
if(walletOpen)
WalletServerSession::sendCoinsToAddress(data);
else
walletServer.sendErrorMessage(109, sessionId, data.correlationId, data.command);
}
else if(data.command.compare("closesession") == 0)
{
WalletServerSession::closeSession();
}
else
{
printf("WalletServerSession command not found: %s\n", data.command.c_str());
walletServer.sendErrorMessage(110, sessionId, data.correlationId, data.command);
}
}
}
void WalletServerSession::closeWalletIfOpen()
{
printf("Close WalletServerSession sessionId: %s - Current WalletId: %s - Current Thread ID: %s\n",
sessionId.c_str(), walletId.c_str(), boost::lexical_cast<std::string>(boost::this_thread::get_id()).c_str());
if(walletOpen)
{
// detach and close wallet file
printf("%s detach\n", clientWallet->strWalletFile.c_str());
bitdb.dbenv.lsn_reset(clientWallet->strWalletFile.c_str(), 0);
printf("%s closed\n", clientWallet->strWalletFile.c_str());
// Create close wallet result message
json_spirit::Object outputJson;
outputJson.push_back(json_spirit::Pair("sessionid", sessionId));
outputJson.push_back(json_spirit::Pair("command", "closewallet"));
json_spirit::Object result;
result.push_back(json_spirit::Pair("errorCode", 0));
result.push_back(json_spirit::Pair("errorMessage", ""));
outputJson.push_back(json_spirit::Pair("result", result));
// send result to out queue
WalletServerSession::sendMessageToQueue(outputJson);
// Close Queue connection
printf("WalletServerSession sessionId: %s stop stomp client.\n", this->sessionId.c_str());
this->session_stomp_client->stop();
printf("WalletServerSession sessionId: %s delete stomp client.\n", this->sessionId.c_str());
delete this->session_stomp_client;
printf("WalletServerSession sessionId: %s wallet close finished.\n", sessionId.c_str());
walletOpen = false;
walletServer.setWalletOpen(sessionId, false);
}
}
void WalletServerSession::openWallet(Command data)
{
printf("WalletServerSession sessionId: %s Execute %s\n", this->sessionId.c_str(), data.command.c_str());
// define output object
json_spirit::Object outputJson;
outputJson.push_back(json_spirit::Pair("sessionid", sessionId));
outputJson.push_back(json_spirit::Pair("correlationid", data.correlationId));
outputJson.push_back(json_spirit::Pair("command", "openWallet"));
// get WalletId from command arguments
std::string openWalletId = "";
std::map<std::string, std::string>::iterator it;
it = data.arguments.find("walletid");
if(it != data.arguments.end())
{
openWalletId = it->second;
}
if(openWalletId.length() == 0)
{
// send error message
json_spirit::Object result;
result.push_back(json_spirit::Pair("errorCode", 101));
result.push_back(json_spirit::Pair("errorMessage", "No Wallet ID supplied in arguments array."));
outputJson.push_back(json_spirit::Pair("result", result));
}
else
{
// verify walletId/accountId combination is valid
bool commandValid = walletServer.isWalletAccountValid(openWalletId, data.accountId);
if(commandValid)
{
// check wallet file is available on filesystem
std::string walletFilenameString = openWalletId + ".dat";
this->walletFilename = walletServer.getWalletServerPath() / walletFilenameString;
if(boost::filesystem::exists(this->walletFilename))
{
printf("Wallet File Exists so open the wallet: %s\n", this->walletFilename.string().c_str());
// open wallet
int64 nStart = GetTimeMillis();
bool fFirstRun = true;
clientWallet = new CWallet(this->walletFilename.string());
DBErrors nLoadWalletRet = clientWallet->LoadWallet(fFirstRun);
std::ostringstream strLoadResult;
if (nLoadWalletRet != DB_LOAD_OK)
{
if (nLoadWalletRet == DB_CORRUPT)
strLoadResult << "Error loading wallet file: Wallet corrupted";
else if (nLoadWalletRet == DB_NONCRITICAL_ERROR)
{
strLoadResult << "Warning: error reading wallet file! All keys read correctly, but transaction data"
" or address book entries might be missing or incorrect.";
}
else if (nLoadWalletRet == DB_TOO_NEW)
strLoadResult << "Error loading wallet file: Wallet requires newer version of CasinoCoin";
else if (nLoadWalletRet == DB_NEED_REWRITE)
{
strLoadResult << "Wallet needed to be rewritten: restart CasinoCoin to complete";
}
else
strLoadResult << "Error loading " << walletFilenameString.c_str();
// load wallet result
printf("WalletServerSession - Load Wallet result: %s\n", strLoadResult.str().c_str());
// send error message
json_spirit::Object result;
result.push_back(json_spirit::Pair("errorCode", 103));
std::string openWalletError = std::string("Open Wallet Error: ");
openWalletError.append(strLoadResult.str());
result.push_back(json_spirit::Pair("errorMessage", openWalletError));
outputJson.push_back(json_spirit::Pair("result", result));
}
else
{
// connect to new Transaction Notifications
printf("WalletServerSession - Connect to new Transaction Notification\n");
clientWallet->NotifyTransactionChanged.connect(boost::bind(&WalletServerSession::NotifyTransactionChanged, this, _1, _2, _3));
// set wallet open in session
walletOpen = true;
walletServer.setWalletOpen(sessionId, true);
walletServer.setLastCommandTime(sessionId, (int)time(NULL));
// get blockchain best block
CBlockIndex *pindexBlockchain = pindexBest;
// get wallet genesis block
CBlockIndex *pindexWalletGenesisBlock;
CBlockLocator genesisLocator;
if(clientWallet->GetWalletGenesisBlock(genesisLocator))
pindexWalletGenesisBlock = genesisLocator.GetBlockIndex();
else
pindexWalletGenesisBlock = pindexGenesisBlock;
// get wallet best block
CBlockLocator walletLocator;
CBlockIndex *pindexWallet;
if (clientWallet->GetBestChain(walletLocator))
pindexWallet = walletLocator.GetBlockIndex();
else
pindexWallet = pindexWalletGenesisBlock;
printf("WalletServerSession - pindexBlockchain: %i pindexWallet: %i pindexWalletGenesisBlock: %i\n", pindexBlockchain->nHeight, pindexWallet->nHeight, pindexWalletGenesisBlock->nHeight);
if(pindexBlockchain->nHeight > pindexWallet->nHeight)
{
CBlockIndex *pindexRescan = pindexWallet;
if(pindexWalletGenesisBlock->nHeight > pindexRescan->nHeight)
pindexRescan = pindexWalletGenesisBlock;
printf("WalletServerSession - Rescanning last %i blocks (from block %i) for wallet %s\n", pindexBest->nHeight - pindexRescan->nHeight, pindexRescan->nHeight, walletFilenameString.c_str());
nStart = GetTimeMillis();
clientWallet->ScanForWalletTransactions(pindexRescan);
printf("WalletServerSession - rescan %15"PRI64d"ms\n", GetTimeMillis() - nStart);
clientWallet->SetBestChain(CBlockLocator(pindexBest));
nWalletDBUpdated++;
}
// Add wallet transactions that aren't already in a block to mapTransactions
clientWallet->ReacceptWalletTransactions();
// set walletId in session
walletId = openWalletId;
// send result message to out queue
json_spirit::Object result;
result.push_back(json_spirit::Pair("errorCode", 0));
result.push_back(json_spirit::Pair("errorMessage", ""));
outputJson.push_back(json_spirit::Pair("result", result));
}
}
else
{
// send error message
json_spirit::Object result;
result.push_back(json_spirit::Pair("errorCode", 103));
std::string openWalletFileError = "Wallet file " + this->walletFilename.string() + " does not exist on WalletServer.";
result.push_back(json_spirit::Pair("errorMessage", openWalletFileError));
outputJson.push_back(json_spirit::Pair("result", result));
}
}
else
{
// send error message
json_spirit::Object result;
result.push_back(json_spirit::Pair("errorCode", 102));
result.push_back(json_spirit::Pair("errorMessage", "Invalid Account ID for given Wallet ID."));
outputJson.push_back(json_spirit::Pair("result", result));
}
}
// send result to out queue
WalletServerSession::sendMessageToQueue(outputJson);
}
void WalletServerSession::closeWallet(Command data)
{
printf("WalletServerSession sessionId: %s Execute closeWallet\n", sessionId.c_str());
closeWalletIfOpen();
}
json_spirit::Value WalletServerSession::getWalletInfo(Command data)
{
printf("WalletServerSession sessionId: %s Execute getWalletInfo\n", this->sessionId.c_str());
// define output object
json_spirit::Object outputJson;
outputJson.push_back(json_spirit::Pair("sessionid", sessionId));
outputJson.push_back(json_spirit::Pair("correlationid", data.correlationId));
outputJson.push_back(json_spirit::Pair("command", "getinfo"));
// add wallet info
json_spirit::Object result;
result.push_back(json_spirit::Pair("errorCode", 0));
result.push_back(json_spirit::Pair("errorMessage", ""));
result.push_back(json_spirit::Pair("version",(int)CLIENT_VERSION));
result.push_back(json_spirit::Pair("protocolversion",(int)PROTOCOL_VERSION));
result.push_back(json_spirit::Pair("blocks", (int)nBestHeight));
result.push_back(json_spirit::Pair("coinsupply", FormatMoney(GetTotalCoinSupply(nBestHeight,false),false)));
result.push_back(json_spirit::Pair("timeoffset", (boost::int64_t)GetTimeOffset()));
result.push_back(json_spirit::Pair("connections",(int)vNodes.size()));
result.push_back(json_spirit::Pair("difficulty", (double)GetDifficulty()));
if (clientWallet) {
result.push_back(json_spirit::Pair("walletversion", clientWallet->GetVersion()));
result.push_back(json_spirit::Pair("defaultaddress", CBitcoinAddress(clientWallet->vchDefaultKey.GetID()).ToString()));
result.push_back(json_spirit::Pair("balance", FormatMoney(clientWallet->GetBalance(),false)));
result.push_back(json_spirit::Pair("unconfirmedbalance", FormatMoney(clientWallet->GetUnconfirmedBalance(),false)));
balancesMapType balances = clientWallet->GetAddressBalances();
json_spirit::Array addressBalanceArray;
BOOST_FOREACH(balancesMapType::value_type balance, balances)
{
json_spirit::Object addressInfo;
addressInfo.push_back(json_spirit::Pair("address",CBitcoinAddress(balance.first).ToString()));
addressInfo.push_back(json_spirit::Pair("balance", ValueFromAmount(balance.second)));
addressBalanceArray.push_back(addressInfo);
}
result.push_back(json_spirit::Pair("addresses", addressBalanceArray));
result.push_back(json_spirit::Pair("keypoololdest",(boost::int64_t)clientWallet->GetOldestKeyPoolTime()));
result.push_back(json_spirit::Pair("keypoolsize", (int)clientWallet->GetKeyPoolSize()));
}
result.push_back(json_spirit::Pair("paytxfee", FormatMoney(CTransaction::nMinTxFee, false)));
result.push_back(json_spirit::Pair("mininput", FormatMoney(nMinimumInputValue, false)));
if (clientWallet && clientWallet->IsCrypted())
result.push_back(json_spirit::Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime));
outputJson.push_back(json_spirit::Pair("result", result));
// send result to out queue
WalletServerSession::sendMessageToQueue(outputJson);
return outputJson;
}
json_spirit::Value WalletServerSession::getAddressBook(Command data)
{
printf("WalletServerSession sessionId: %s Execute getAddressBook\n", sessionId.c_str());
balancesMapType balances = clientWallet->GetAddressBalances();
// define output object
json_spirit::Object outputJson;
outputJson.push_back(json_spirit::Pair("sessionid", sessionId));
outputJson.push_back(json_spirit::Pair("correlationid", data.correlationId));
outputJson.push_back(json_spirit::Pair("command", data.command));
// add wallet info
json_spirit::Object result;
result.push_back(json_spirit::Pair("errorCode", 0));
result.push_back(json_spirit::Pair("errorMessage", ""));
json_spirit::Array addressBalanceArray;
BOOST_FOREACH(balancesMapType::value_type balance, balances)
{
json_spirit::Object addressInfo;
addressInfo.push_back(json_spirit::Pair("address",CBitcoinAddress(balance.first).ToString()));
addressInfo.push_back(json_spirit::Pair("balance", ValueFromAmount(balance.second)));
addressBalanceArray.push_back(addressInfo);
}
result.push_back(json_spirit::Pair("addresses", addressBalanceArray));
outputJson.push_back(json_spirit::Pair("result", result));
// send result to out queue
WalletServerSession::sendMessageToQueue(outputJson);
return outputJson;
}
json_spirit::Value WalletServerSession::sendCoinsToAddress(Command data)
{
std::string strAddress = "";
std::string strAmount = "";
std::string strComment = "";
// loop over arguments and get the values
std::map<std::string, std::string>::iterator argit;
for(argit = data.arguments.begin(); argit != data.arguments.end(); argit++) {
if(argit->first.compare("address")==0)
strAddress = argit->second;
else if(argit->first.compare("amount")==0)
strAmount = argit->second;
else if(argit->first.compare("comment")==0)
strComment = argit->second;
}
printf("WalletServerSession: %s sendCoinsToAddress: %s Amount: %s\n", sessionId.c_str(), strAddress.c_str(), strAmount.c_str());
// define output object
json_spirit::Object outputJson;
outputJson.push_back(json_spirit::Pair("sessionid", sessionId));
outputJson.push_back(json_spirit::Pair("correlationid", data.correlationId));
outputJson.push_back(json_spirit::Pair("command", data.command));
// create coin address
CBitcoinAddress address(strAddress);
if (!address.IsValid())
{
// send error message
json_spirit::Object result;
result.push_back(json_spirit::Pair("errorCode", 111));
result.push_back(json_spirit::Pair("errorMessage", "Invalid CasinoCoin address."));
outputJson.push_back(json_spirit::Pair("result", result));
// send result to out queue
WalletServerSession::sendMessageToQueue(outputJson);
return outputJson;
}
// Amount
int64 nAmount;
ParseMoney(strAmount, nAmount);
if (nAmount <= 0)
{
// send error message
json_spirit::Object result;
result.push_back(json_spirit::Pair("errorCode", 112));
result.push_back(json_spirit::Pair("errorMessage", "Amount of coins to sent to address must be greater than 0."));
outputJson.push_back(json_spirit::Pair("result", result));
// send result to out queue
WalletServerSession::sendMessageToQueue(outputJson);
return outputJson;
}
// Wallet comments
CWalletTx wtx;
if(!strComment.empty())
wtx.mapValue["comment"] = strComment;
// check if wallet is locked
// if (pwalletMain->IsLocked())
// throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
// execute send
string strError = clientWallet->SendMoneyToDestination(address.Get(), nAmount, wtx);
if (strError != "")
{
// send error message
json_spirit::Object result;
result.push_back(json_spirit::Pair("errorCode", 10));
result.push_back(json_spirit::Pair("errorMessage", "Error sending coins: " + strError));
outputJson.push_back(json_spirit::Pair("result", result));
}
else
{
json_spirit::Object result;
result.push_back(json_spirit::Pair("errorCode", 0));
result.push_back(json_spirit::Pair("errorMessage", ""));
result.push_back(json_spirit::Pair("txid", wtx.GetHash().GetHex()));
outputJson.push_back(json_spirit::Pair("result", result));
}
// send result to out queue
WalletServerSession::sendMessageToQueue(outputJson);
return outputJson;
}
void WalletServerSession::closeSession()
{
printf("WalletServerSession sessionId: %s Execute closeSession\n", sessionId.c_str());
closeWalletIfOpen();
// remove wallet object
if(clientWallet != NULL)
delete clientWallet;
// set shutdownComplete
shutdownComplete = true;
printf("WalletServerSession sessionId: %s Session Close Finished!\n", sessionId.c_str());
}

95
src/walletserversession.h Normal file
View File

@@ -0,0 +1,95 @@
#ifndef WALLETSERVERSESSION_H
#define WALLETSERVERSESSION_H
#include <string>
#include "walletserver.h"
#include "wallet.h"
#include "json/json_spirit.h"
#include "stomp/booststomp.h"
#include <boost/filesystem.hpp>
#include <boost/thread.hpp>
#include <boost/thread/condition_variable.hpp>
#include <boost/thread/mutex.hpp>
typedef std::map<CTxDestination, int64> balancesMapType;
// Class that consumes objects from a queue
class WalletServerSession
{
private:
SynchronisedCommandQueue<Command>* m_queue; // The queue to use
std::string sessionId; // The id of the wallet session
std::string walletId; // The wallet id of the session
boost::filesystem::path walletFilename;
// ActiveMQ Connection
STOMP::BoostStomp* session_stomp_client;
// Wallet
CWallet* clientWallet;
bool walletOpen;
// Handle new block signal
void NotifyBlocksChanged();
// Handle new transaction signal
void NotifyTransactionChanged(CWallet *wallet, const uint256 &hash, ChangeType status);
// shutdown condition
bool shutdownComplete;
// wallet commands
void openWallet(Command data);
void closeWallet(Command data);
json_spirit::Value getWalletInfo(Command data);
json_spirit::Value getAddressBook(Command data);
json_spirit::Value sendCoinsToAddress(Command data);
public:
// Constructor with id and the queue to use.
WalletServerSession(std::string newSessionId, SynchronisedCommandQueue<Command>* queue);
// Destructor
~WalletServerSession();
// Default operator that starts the ActiveMQ connection and reads data from the WalletServer queue
void operator () ()
{
printf("WalletServerSession - ActiveMQ connection for session: %s\n", sessionId.c_str());
// initialize wallet pointer to NULL
clientWallet = NULL;
shutdownComplete = false;
// initiate a new BoostStomp client
session_stomp_client = new STOMP::BoostStomp(WalletServer::stomp_host, WalletServer::stomp_port);
session_stomp_client->enable_debug_msgs(false);
// start the client, (by connecting to the STOMP server)
session_stomp_client->start();
// connect to Signals
uiInterface.NotifyBlocksChanged.connect(boost::bind(&WalletServerSession::NotifyBlocksChanged, this));
printf("WalletServerSession - Start Dequeue for session: %s\n", sessionId.c_str());
while (true)
{
// Get the data from the queue and print it
Command data = m_queue->Dequeue();
printf("Consumer Session: %s consumed: %s for session: %s ThreadID: %s\n",
sessionId.c_str(), data.command.c_str(), data.sessionId.c_str(),
boost::lexical_cast<std::string>(boost::this_thread::get_id()).c_str());
executeWalletCommand(data);
// if closesesion command then wait for shutdown complete
if(data.command.compare("closesession") == 0)
{
while(!shutdownComplete)
{
printf("Wait until closesession shutdownComplete");
MilliSleep(100);
}
}
// Make sure we can be interrupted
boost::this_thread::interruption_point();
}
}
// public methods
void sendMessageToQueue(json_spirit::Object outputJson);
void executeWalletCommand(Command data);
void closeWalletIfOpen();
void setWalletOpen();
bool isWalletOpen();
void closeSession();
};
#endif // WALLETSERVERSESSION_H

View File

@@ -2,7 +2,7 @@
; HM NIS Edit Wizard helper defines
!define PRODUCT_NAME "Casinocoin Wallet"
!define PRODUCT_VERSION "2.0.0.0"
!define PRODUCT_VERSION "2.0.1.0"
!define PRODUCT_PUBLISHER "Casinocoin"
!define PRODUCT_WEB_SITE "http://www.casinocoin.org"
!define PRODUCT_DIR_REGKEY "Software\Microsoft\Windows\CurrentVersion\App Paths\casinocoin-qt.exe"
@@ -47,7 +47,7 @@ var ICONS_GROUP
; MUI end ------
Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"
OutFile "release\casinocoin-2.0.0.0-setup.exe"
OutFile "release\casinocoin-2.0.1.0-setup.exe"
InstallDir "$PROGRAMFILES\Casinocoin"
InstallDirRegKey HKLM "${PRODUCT_DIR_REGKEY}" ""
ShowInstDetails show
@@ -76,6 +76,7 @@ Section "MainSection" SEC01
File "release\plugins\imageformats\qwebp.dll"
SetOutPath "$INSTDIR\plugins\platforms"
File "release\plugins\platforms\qwindows.dll"
File "release\plugins\platforms\qminimal.dll"
SetOutPath "$INSTDIR\qml\QtQml\Models.2"
File "release\qml\QtQml\Models.2\modelsplugin.dll"
File "release\qml\QtQml\Models.2\plugins.qmltypes"
@@ -658,6 +659,7 @@ Section Uninstall
Delete "$INSTDIR\qml\QtQml\Models.2\plugins.qmltypes"
Delete "$INSTDIR\qml\QtQml\Models.2\modelsplugin.dll"
Delete "$INSTDIR\plugins\platforms\qwindows.dll"
Delete "$INSTDIR\plugins\platforms\qminimal.dll"
Delete "$INSTDIR\plugins\imageformats\qwebp.dll"
Delete "$INSTDIR\plugins\imageformats\qwbmp.dll"
Delete "$INSTDIR\plugins\imageformats\qtiff.dll"