I haven't tried the interface, but from the manual, it seems that you can deposit and withdraw BTC or withdraw EUR, but cannot deposit EUR.
I was just wondering how that will work then if there is no EUR in the system to support buy orders. It's a bit like TradeHill at the moment. They have markets for about 20 currencies, but there's no way to submit a buy order for the 17 or so that do not yet accept deposits.
The site owner will be the only one who will be able of depositing paper currencies.
So with this site, you CAN NOT buy bitcoins. But you can sell them.
By the way, here is the latest version of my code. I have totally rewritten it:
[code]
#!/bin/bash
THIS_IS_BETA() { true ; }
content() { echo "Content-type: ${2:-text}/${1:-html};" ; echo ; }
error() { content plain ; echo "$@" ; exit ; }
# uncomment this for debugging
#content plain ; exec 2>&1 ; set -e
# Testing installations on server
bash --version | grep -q -v ' [0-3]\.' ||
error "too old bash version. Please upgrade to >4.0"
openssl version | grep --q -v ' 0\.[0-9]' ||
error "wrong openssl version. Please upgrade to >1.0."
bc --version >/dev/null 2>&1 ||
error "missing bc. Please install the unix basic calculator."
dc --version >/dev/null 2>&1 ||
error "missing dc. Please install the unix desk calculator."
gawk --version >/dev/null 2>&1 ||
error "missing gawk. Please install GNU Awk."
[[ -f base58.sh ]] ||
error "missing file base58.sh"
. base58.sh ||
error "error in file base58.sh"
# database protection
unlock() { lockfile-remove "$0" ; }
error() { unlock ; content plain ; echo "$@" ; exit ; }
lock() {
local maximum_number_of_lock_attempts=2
if [[ -n "$maximum_number_of_lock_attempts" ]]
then lockfile-create -r "$maximum_number_of_lock_attempts" "$0"
else lockfile-create "$0"
fi || error "Could not lock the database. Please try again later."
}
# associative arrays to define different kinds of currencies
declare -A smallest_notes=([EUR]=5 [USD]=1)
declare -a paper_currencies=(${!smallest_notes
declare -A humanCurrencyNames=(\
BTC="bitcoin" \
[eur]="euro" \
[usd]="U.S. dollar" \
[jpy]="japanese yen" \
[chf]="swiss franc" \
)
# a function to determin the kind of a currency
currencyType() {
local currency="${1^^}"
if [[ "$currency" = 'BTC' ]]
then echo "crypto"
else
for c in "${paper_currencies
do
if [[ "$currency" = "$c" ]]
then echo "paper" ; return
fi
done
echo "unknown"
return 1
fi
}
# a function to compare floating point numbers
compare() {
bc <<<"if ($1 > $2) print \">\" else if ($1 < $2) print \"<\" else print \"=\""
}
# a function to get account balance in any given currency
getbalance() {
case "${1,,}" in
"$(THIS_IS_BETA || echo "btc")")
bitcoind getbalance "$2"
;;
*)
# server has illimited deposit capability
if [[ "$2" = "$server_account" ]]
then echo 1000
else
grep "^$2,${1^^}," "$0-moves.csv" |
gawk -F, -v OFS=, -v s=0 '{ s+=$3 } END { print s }'
fi
;;
esac
}
# some regular expressions
account_regex='^[a-z0-9]{40}$'
amount_regex='(0|0{,1}\.[0-9]+|[1-9][0-9]*\.[0-9]+|[1-9][0-9]*)'
# prevent use of real bitcoind for now
# (additionnal security to THIS_IS_BETA)
bitcoind() { : ; }
# server parameters
admin="XXXXXX"
server_privkey="-----BEGIN PRIVATE KEY-----
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
-----END PRIVATE KEY-----"
server_pubkey() { openssl pkey -pubout <<<"$server_privkey" ; }
server_account="$(
openssl pkey -pubout <<<"$server_privkey" |
openssl dgst -rmd160 -binary |
xxd -p)"
# command line client function
# requires curl, openssl version >1, xxd
euronymousClient() {
#you may have to modify this line manually
local url="
http://localhost/cgi-bin/euronymous"
case "${1,,}" in
getnewaddress)
if [[ -n "$2" ]]
then curl -s "$url?getnewaddress&account=$2"
elif ! local privkey="$(openssl genpkey -algorithm RSA)"
then echo "could not create private key" 2>&1 ; return 1
elif ! local account="$(
openssl pkey -pubout <<<"$privkey" |
openssl dgst -rmd160 -binary |
xxd -p)"
then echo "could not compute rmd160 of public key" 2>&1 ; return 2
elif ! gpg -e -r "$USER" <<<"$privkey" > "$account.pem.gpg"
then echo "could not encrypt private key" 2>&1 ; return 3
else
echo "created ./$account.pem.gpg" 2>&1
$FUNCNAME getnewaddress "$account"
fi
;;
withdraw|transfer|trade|cancel)
local client_privkey="$(cat)" command="$(date +%s) $@"
curl -s -d "$(
if [[ "${1,,}" = "withdraw" ]] && [[ "${3,,}" = "eur" ]]
then
echo "encrypted:"
openssl pkeyutl -encrypt -pubin -inkey <(curl -s "$url?pubkey") |
xxd -p
else cat
fi <<<"$command"
openssl pkey -pubout <<<"$client_privkey"
openssl dgst -rmd160 -sign <(echo "$client_privkey") -binary <<<"$command" |
xxd -p
)" "$url"
;;
getbalance)
curl -s "$url?getbalance¤cy=$2&account=$3"
;;
view)
local order="$(curl -s "$url?order=$2")"
if [[ "$order" = "no such order" ]]
then return 1
elif
echo "$order"
grep -q "^encrypted:$" <<<"$order"
then
local client_privkey="$(cat)"
if [[ -n "$client_privkey" ]]
then
echo "Decrypted command:"
sed '
1,2d
/-BEGIN PUBLIC KEY-/,$d
' <<<"$order" |
xxd -p -r |
openssl pkeyutl -decrypt -inkey <(echo "$client_privkey") 2>&1
fi
fi
;;
*)
echo "usage: $FUNCNAME {create|getnewaddress|withdraw|trade|cancel} [arguments...]
For more help, visit $url?help" >&2
return 2
;;
esac
}
clientcode() { sed -n '/^# command line client function/,/^}$/p' "$0"; }
# Ok now the real CGI part...
# GET requests
if [[ "$REQUEST_METHOD" = "GET" ]]
then
case "$QUERY_STRING" in
"")
# home page
content
echo "
$0 $0: almost anonymous currency exchange marketplace
Last adjudication: (TODO)
You can see which bitcoin addresses are associated to a given account with this form:
Our public key:
$(server_pubkey) "
;;
book)
# display trading book in HTML table
content plain
join -t, -v 1 <(
sort -t, "$0-trade-orders.csv"
) <(
sort -t, "$0-cancelled-trades.csv" ) |
cut -d, -f 4
;;
orders)
# display all command history in a big HTML table
content
echo "
$0 list of all orders "
;;
orders'&'format=csv)
# loads orders in CSV format
content csv
cat "$0-"{withdrawal,trading,transfer,cancel}"-orders.csv"
;;
moves)
# view of all accounting entries
content
echo "TODO"
;;
moves'&format=csv')
# loads accounting entries in CSV format
content csv
echo "TODO"
;;
faq)
# Frequently Asked Question HTML page
content
echo "
$0 Frequently Asked Questions $0 F.A.Q.
What is the purpose of this site?
This site aims to be a more or less anonymous bitcoin exchange
marketplace. By more or less, we mean that our priority is to
provide anonymity for us, not for you. Our main goal is to prevent
government from shutting our site down.
The reason we can not provide you any full anonymity, is that we
will need your postal address to send you your euros. However,
your accounts are anonymous and you can create as many of them as
you want. Therefore, we have no way to know exactly the total
amount of money you own in our book. This ignorance offers you
some protection.
What can I do on this site?
You can trade bitcoins against other national currencies, mainly
euros. You can create accounts, deposit bitcoins, withdraw
bitcoins, withdraw euros via cash in the mail, and trade
bitcoins/euros on your account against euros/bitcoins on some other
account.
What can I NOT do on this site?
You can NOT send us national currencies. Receiving national
currencies anonymously is almost impossible (if you know a
method, please let us know). Therefore, you can only fund your
account using bitcoins. If you want to sell your national
currencies in cash, we suggest you mirror this site.
Do you plan on publishing my postal address?
No, we will publish an encrypted version of the address. Only the owner
of the account will be able to decrypt it, so he can verify it was transmitted
correctly.
We shall, however, always publish the signature of each order, so that
we can prove our good faith in case of a disagreement.
Why are you not online all the time?
We have our reasons.
We shall try to be online at least a few hours every day,
though. In the future, it should be possible to submit orders with
anonymous email via i2p for instance.
Right now, you can ping our webserver to check if we are online:
$ ping -q -c 5 $HTTP_HOST Use this command in scripts if you want an order to be sent as soon as
the server is back online.
"
;;
help)
# command line client help HTML page
content
echo "
$0 command line client help Sendings orders on command line
To send commands you must use a bash command line function which
is published with the server's source
code.
Here it is for convenience:
Creating an account
An account number is just the rmd160 of a public key. Creating
one can be done offline since no commmunication with the server is
necessary.
Although the creation of an account can be made easy with our client (see below),
you can do it manually on your prefered shell command line. Just run:
$ openssl genpkey -algorithm RSA This will print on stdout a RSA private key that will be
perfectly suitable as a private key for an account on our server.
However, for better security, you might want to encrypt the output
using GPG:
$ openssl genpkey -algorithm RSA | gpg -e -r \$USER > priv.pem.gpg To get the account number, you can run:
$ gpg < priv.pem.gpg | openssl dgst -rmd160 You might then want to copy paste the account number in order to rename the
file priv.pem.gpg:
$ mv {priv,ACCOUNT_NUMBER}.pem.gpg Of course, you can write your own shell functions to automize this.
Deposit bitcoins
To deposit some bitcoins on your $0 account, you need to request
a bitcoin address where to send some bitcoins. So basically you
will ask the server to run a getnewaddress bitcoin
command.
This can be done only once, or as many times as you want. It's
up to you.
If you have already created an account as explained above, you can run:
$ euronymousClient getnewaddress ACCOUNT_NUMBER On the server side, this command will execute:
bitcoind getnewaddress ACCOUNT_NUMBER Thus creating the appropriate bitcoin account if it doesn't
exist already.
The command will print the generated bitcoin address on stdout.
You can then use this address to fund your account.
If you had not already created an account, you can have the
client create one for you by running the getnewaddress command with no account number:
$ euronymousClient getnewaddress Along with the bitcoin address on stdout, this command will also
create a private key in an ACCOUNT_NUMBER.pem.gpg file in the
current working directory.
You can retrieve a signed list of bitcoin addresses associated
to an account at this url (there is also a small html form on the home
page to do this):
http://$HTTP_HOST/cgi-bin/$0?show=addresses&account=ACCOUNT_NUMBER
This removes to us plausible deniability of an association
between a bitcoin address and one of your accounts. We suggest you
keep a copy of this list for each of your accounts.
Withdraw bitcoins
To withdraw some bitcoins, you need to provide a bitcoin address
and an amount of bitcoins you want to receive.
$ euronymousClient withdraw AMOUNT btc to BITCOIN_ADDRESS This command will read the private key from stdin, so a complete command could be:
$ euronymousClient withdraw AMOUNT btc to BITCOIN_ADDRESS < ACCOUNT_NUMBER.pem Notice that in most shells you can place the redirection
wherever you want on the command. Thus you could as well have
entered:
$ euronymousClient
You can use GnuPG for more security:
$ gpg < ACCOUNT_NUMBER.pem.gpg | euronymousClient withdraw AMOUNT btc to BITCOIN_ADDRESS
Or:
$
Withdraw euros
To withdraw euros, you need to provide a postal address where to
receive cash via mail. Only use multiples of 5 (in order to use
only bank notes). Postal fees will be deducted from your euro
account. For instance, if you withdraw ten euros, ten euros will be
sent but 10.55 euros will be deducted from your euro balance.
$ euronymousClient withdraw AMOUNT eur to POSTAL_ADDRESS
Again, this command reads the private key from stdin.
POSTAL_ADDRESS should be a double or single quote enclosed
string. Please do not put any carriage return or other fancy
character in the address. It might false the signature process.
Use a semi-colon ( if you want to specify a carriage return.
Exemple:
$ euronymousClient withdraw 50 eur to \"Satoshi Nakamoto;42, unknown street,;Tokyo, JAPAN\"
For this kind of order, the command is not sent in clear through
the internet. Instead, it is encrypted with the server's href=$0?pubkey>public key.
Transfer funds from one account to another
To do so you can run:
$ euronymousClient transfer AMOUNT {eur|btc} to ACCOUNT_NUMBER
AMOUNT should be a positive number. This commands reads private key from stdin.
Trade bitcoins vs. euros
To submit a trading order, you run a command such as:
$ euronymousClient trade 5.982 btc -19.43 eur
Use signed values to indicate the direction of your trade:
negative for the currency you want to sell, positive for the
currency you want to buy.
Only use three letters currency ISO
code.
This command also reads the private key from stdin.
You can use shell expansion magic if you want to make a trade
on a more conventionnal price/amount notation.
Example: buying 50 bitcoins at 16.4 EUR/BTC
$ amount=50 price=16.4; euronymousClient trade \$amount btc -\$(dc <<<\"4k \$amount \$price *n\") eur
Cancel an order
Euro withdrawals and trade orders are the only orders you can
cancel.
You can cancel a trading order as long as the order has
not been fully executed. Cancellation of a partially executed
trading order will only prevent the rest of the trade to be
adjudicated.
You can cancel a euro withdrawal as long as the letter
has not been sent yet.
To cancel an order, just run:
$ euronymousClient cancel REFERENCE_NUMBER
REFERENCE_NUMBER is a reference number published in the href=$0?orders>list of all orders. It is actually the md5sum of the
signature.
This command reads the private key from stdin.
Checking
You can visualize an order, whether it is a trading, withdrawal
or cancelling order, by running:
$ euronymousClient view ORDER_REFERENCE_NUMBER
If the order is a euro withdrawal order, thus containing a
postal address, then the command will read the private key from
standard input in order to decrypt the command.
"
;;
order=*)
# viewing specific order
ref="${QUERY_STRING#*=}"
content plain
if ! grep -q "^$ref$" "$0.orders"
then echo "no such order"
else sed -n "/^$ref$/,/^$/ {/^.*$/p}" "$0.orders"
fi
;;
getbalance'&'currency=*'&'account=*)
# balance inquiry
currency="${QUERY_STRING#*=}"
currency="${currency%%&*}"
account="${QUERY_STRING##*=}"
content plain
getbalance "$currency" "$account"
;;
pubkey)
# Server's public key displaying request
content plain
server_pubkey
;;
show=addresses'&'account=*)
# displaying bitcoin addresses associated to a given, specific account
account="${QUERY_STRING##*=}"
[[ "$account" =~ $account_regex ]] ||
error "'$account' is not a valid account format"
content plain
server_pubkey
{
echo "Here are the bitcoin addresses for account ref. $account:"
if THIS_IS_BETA
then echo NONE
else
list="$(bitcoind getaddressesbyaccount "${QUERY_STRING#*=}")"
echo "${list:-NONE}"
fi
} |
tee >(
openssl dgst -rmd160 -sign <(echo "$server_privkey") -binary |
xxd -p
)
;;
source_code)
# server's source code request
content plain
# some portions of the code must be hidden
sed -r '
/^admin=.*/s/.*/admin=XXXXXXXXX/
/^rpcpasswd=/s/.*/rpcpasswd=XXXXXXXX/
/^server_privkey="-----BEGIN PRIVATE KEY-----/,/-----END PRIVATE KEY-----"$/ {
s/.{64}/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/
s/.*==$/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/ }' "$0"
;;
source_code=client)
# source code request (only the client part)
content plain
clientcode
;;
getnewaddress'&'account=*)
# getnewaddress command
account="${QUERY_STRING##*=}"
[[ "$account" =~ $account_regex ]] ||
error "wrong account format"
bitcoin_address="$(THIS_IS_BETA || bitcoind getnewaddress "$account")" ||
error "could not generate address "
content plain
sleep 5 # we make the client wait a bit to prevent abuse
echo "$bitcoin_address"
;;
server_info)
# server information request
content plain
set
;;
*)
# error
error "unknown query string: $QUERY_STRING"
;;
esac
fi
# POST requests
if [[ "$REQUEST_METHOD" = "POST" ]]
then
# reading post data
#
# post data is supposed to be made of:
# - the command
# - the client's public key
# - the signature of the command, corresponding to the given public key
read -N $CONTENT_LENGTH post_data ||
error "could not retrieve post data"
# extracting public key first
pubkey="$(sed -n '/^-\+BEGIN PUBLIC KEY-\+$/,/^-\+END PUBLIC KEY-\+$/p' <<<"$post_data")"
# if there is no public key, then there is no point going further.
[[ -n "$pubkey" ]] ||
error "no public key in post data"
# checking that the public key is a valid one
openssl pkey -pubin -pubout <<<"$pubkey" >/dev/null ||
error "invalid public key in post data"
# now that we have the client's public key, we can compute the
# corresponding account number
account="$(openssl dgst -rmd160 -binary <<<"$pubkey" |xxd -p)"
# extracting signature
signature="$(sed '1,/^-\+END PUBLIC KEY-\+$/d' <<<"$post_data")"
# all commands in POST requests must be signed so if there is no signature, we stop here
[[ -n "$signature" ]] ||
error "no signature in post data"
# extracting the command
command="$(sed '/^-\+BEGIN PUBLIC KEY-\+$/,$d' <<<"$post_data")"
# if command begins with "encrypted:", then it is encrypted with the
# server's public key. This happens when the command is a paper currency
# withdrawal, as such a command has a postal address in it.
# In that case, we just have to decrypt it first.
if [[ "$command" =~ ^encrypted: ]]
then command="$(xxd -p -r <<<"${command#encrypted:}" |
openssl pkeyutl -decrypt -inkey <(echo "$server_privkey"))" ||
error "could not decrypt command"
fi
# now we check the signature
<<<"$command" openssl dgst -rmd160 \
-verify <(echo "$pubkey") -signature <(xxd -p -r <<< "$signature") ||
error "wrong signature"
# The signature is correct, we can make a reference number out of it.
# this is used as a unique identifier for all orders
reference="$(openssl dgst -md5 -binary <<<"$signature" | xxd -p)"
# checking that the reference is not already in the orders file
# (the database is not locked yet so we'll have to check that again later)
grep -q "^$reference$" "$0.orders" &&
error "this order has already been submitted"
# We put the command words in an array. It will help parsing
read -a command_array <<<"$command"
# first word shoud be a time in seconds since EPOCH
date="${command_array[0]}"
[[ "$date" =~ ^[1-9][0-9]*$ ]] ||
error "wrong date format"
# comparing given time with server time
now="$(date +%s)" acceptable_delay_in_minutes=2
((date error "claimed date ($(date -d @$date)) is more than $acceptable_delay_in_minutes minutes in the past"
# The server won't accept an order which pretends to be coming from the future.
((date>now)) &&
error "claimed date ($(date -d @$date)) is in the future"
# We store the base properties of order in a pre CSV record
order="$reference,$account,$date"
# LOCKING THE DATABASE
# We're about to parse the command. As we do, we'll extract data from the
# database, and we'll do many consistency check. We don't want these data
# to have changed once we commit to the actual database edition. So we
# lock the database NOW.
lock
# Let's check again that the order is not already in the database
! grep -q "^$reference$" "$0.orders" ||
error "this order has already been submitted"
# First command word was the time. We've already extracted it. The second
# word is the type of the order. We deal with the different
# possibilities in a case block.
# Each kind of order has its own format for storage. There is a common
# $0.orders file, though.
case "${command_array[1]^^}" in
WITHDRAW)
# withdraw order
# specific file for withdrawal orders is "$0-withdraw-orders.csv"
order_file="$0-withdraw-orders.csv"
# command should be of the form:
# TIME withdraw AMOUNT CURRENCY to DESTINATION
# DESTINATION could be several words,
# so the command should have at least 6 words
[[ "${#command_array error "not enough arguments for a withdraw order"
# Third word is the amount to be withdrawned
amount="${command_array[2]#+}"
[[ "$amount" =~ ^$amount_regex$ ]] ||
error "wrong amount format or negative amount"
# it should be positive
[[ "$amount" -ge 0 ]] ||
error "null amount in a withdrawal order"
# the 'to' preposition is not optionnal
[[ "${command_array[4]^^}" = "TO" ]] ||
error "wrong command format (was expecting 'to', not '${command_array[4]}')"
# storing currency name in lower case
currency="${command_array[3],,}"
# checking balance
balance="$(getbalance "$currency" "$account")" ||
error "could not get $currency balance for account $account"
# checking that there is enough funds for the requested
# withdrawal amount
[[ "$(compare "$amount" "$balance")" = '<' ]] ||
error "not enough ${humanCurrencyNames[$currency]:-$currency} funds on this account"
# Now, depending on whether we're dealing with a cryptographic
# currency or a paper currency, the procedure will not be the
# same. A cryptocurrency can be withdrawned electronically,
# but a paper currency must be sent via postal mail.
case "$(currencyType "$currency")" in
crypto)
# so far bitcoin is the only cryptocurrency, so we'll
# just assume we're dealing with bitcoins
bitcoin_address="${command_array[5]}"
checkBitcoinAddress "$bitcoin_address" ||
error "invalid bitcoin address"
order+="${command#* },$currency,$amount,$bitcoin_address"
bitcoind_args=(sendfrom "$account" "$bitcoin_address" "$amount" "${minconf:=1}" "$reference")
if THIS_IS_BETA
then echo "$(date): bitcoind ${bitcoind_args else
: bitcoind "${bitcoind_args fi
;;
paper)
humanName="${humanCurrencyNames[$currency]:-$currency}"
# paper currencies can only be sent via postal mail if the
# amount is a multiple of the smallest bank note in this
# currency
smallest_note="${smallest_notes["${currency^^}"]}"
[[ "$(dc <<<"$amount $smallest_note/$smallest_note*n")" = "$amount" ]] ||
error "amount in $humanName should be a multiple of $smallest_note in order to be sent via postal mail"
# for a paper withdrawal we can't store the full command
order+=",$amount $humanName withdrawal,$currency,$amount"
if ! THIS_IS_BETA
then
# Mail instructions to administrator
{
gpg -ae -r "$admin" 2>/dev/null || cat
} <<<"
order ref. $reference (account n° $account):
${command#* }
" |
mail -s "$amount $humanName withdrawal" "$admin" &
fi
# encrypt command to hide the address
command="encrypted:"
command+="$(openssl pkeyutl -encrypt -pubin -inkey <(echo "$pubkey") <<<"$command")"
;;
*)
error "unkown currency (or not supported yet)"
;;
esac
moves=("$account,${currency^^},-$amount")
;;
TRADE)
# trade order
# specific file for trade orders is "$0-trade-orders.csv"
order_file="$0-trade-orders.csv"
# command should be of the form:
# TIME trade AMOUNT1 CURRENCY1 AMOUNT2 CURRENCY2
# considering the format of a trade order, the command array
# should be six words long.
[[ "${#command_array error "incorrect number of arguments for a trade order"
# extracting amounts and currencies
amounts=("${command_array[2]}" "${command_array[4]}")
currencies=("${command_array[3]}" "${command_array[5]}")
humanNames=()
for c in "${currencies do humanNames+=("${humanCurrencyNames[$c]:-$c}")
done
# checking amounts
declare -i n i
for i in 0 1
do
# checking format
[[ ! "${amounts}" =~ ^[+-]{,1}$amount_regex$ ]] &&
error "wrong amount format for currency '${currencies}'"
# detecting the negative amount
[[ "${amounts::1}" = '-' ]] && n=i
# detecting a null amount
((${amounts%.*})) ||
error "'${currencies}' amount is null"
done
# checking that amounts don't have the same sign
((${amounts[0]%%.*}*${amounts[1]%%.*} < 0)) ||
error "amounts have the same sign"
# checking balance
balance="$(getbalance "${currencies[n]}" "$account")" ||
error "could not check balance"
# checking funds
[[ "$(compare "${amounts[n]:1}" "$balance")" = '>' ]] ||
error "not enough funds"
order+=",${command#* }"
moves=()
for i in 0 1
do
order+=",${amounts},${currencies}"
moves+=("$account,${amounts},${currencies},$reference")
done
;;
TRANSFER)
amount_to_transfer="${command_a