To send a transaction from an account, you'd need to sign it beforehand. To sign it, you need the private key that corresponds to the account.
If you used bin/epoch keys_gen <password>
, then you should have your private key in generated_keys/key
and your public key (aka address) in generated_keys/key.pub
.
Let's read and decipher them.
But first let's learn the basics of working with erl
shell.
Open a remote console to the running erlang node with bin/epoch remote_console
, you would see something like
Erlang/OTP 20 [erts-9.3.3] [source] [64-bit] [smp:6:6] [ds:6:6:10] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V9.3.3 (abort with ^G)
(epoch@localhost)1> % you would have your cursor here
This is erl
or the erlang shell, each expression which you type in here should be ended with .
. Like if you type
io:fwrite("hello, world\n").
and the press ENTER
, it would result in
(epoch@localhost)1> io:fwrite("hello, world\n").
hello, world
ok
Ok, that's enough.
The privkey file would probably be at generated_keys/key
, let's read it:
(epoch@localhost)2> {ok, EncryptedPrivKeyBin} = file:read_file("generated_keys/key").
{ok,<<184,123,157,23,42,135,50,247,14,52,59,110,96,85,
196,17,27,81,214,118,143,154,6,37,118,126,242,...>>} % your output would be different
% now let's decode it
(epoch@localhost)3> Password = "my-password". % replace it with your own password
(epoch@localhost)4> DecryptedPrivKeyBin = crypto:block_decrypt(aes_ecb, crypto:hash(sha256, Password), EncryptedPrivKeyBin).
<<..., ..., ...>>
Now we have our private key decrypted DecryptedPrivKeyBin
. We can use it to sign spend transactions.
We can also export it to python / js sdks.
Let's also read the pubkey, which when base58check encoded and prepended with ak_
becomes our address.
(epoch@localhost)5> {ok, EncryptedPubKeyBin} = file:read_file("generated_keys/key.pub").
(epoch@localhost)6> DecryptedPubKeyBin = crypto:block_decrypt(aes_ecb, crypto:hash(sha256, Password), EncryptedPubKeyBin).
Now let's build and sign a spend tx.
% this is me. you should set it to where you want to send AE to. You can get it from `ak_dashjdfj` strings
(epoch@localhost)7> RecepientPubKey = <<123, 78, 62, 237, 77, 74, 238, 81, 138, 2, 156, 113, 195, 83, 91,
55, 127, 29, 134, 93, 90, 202, 3, 81, 158, 139, 30, 51, 5, 232, 203, 192>>.
(epoch@localhost)9> Nonce = 1. % should be `prev nonce of the account + 1` to avoid replay attacks
(epoch@localhost)10> {ok, SpendTx} = aec_spend_tx:new(#{
sender_id => aec_id:create(account, DecryptedPubKeyBin),
recipient_id => aec_id:create(account, RecepientPubKey),
amount => 10000, % this is in aeons, I think
fee => 1,
ttl => 3000, % this is the block height up to which the spend tx is valid, you can set it to `current block heihgt + 100`
nonce => Nonce,
payload => <<"">> % payload can be any binary, so let's leave it empty
}).
% Now we need to sign it
(epoch@localhost)11> EncodedSpendTx = aetx:serialize_to_binary(SpendTx),
EncodedSpendTxForNetwork = aec_governance:add_network_id(EncodedSpendTx),
Signature = enacl:sign_detached(EncodedSpendTxForNetwork, DecryptedPrivKeyBin),
SignedSpendTx = aetx_sign:new(SpendTx, [Signature]).
% Serialize it
(epoch@localhost)12> EncodedSignedSpendTx = aetx_sign:serialize_to_binary(SignedSpendTx).
% base58check encode it
(epoch@localhost)13> Base58CheckEncodedSignedSpendTx = aehttp_api_encoder:encode(transaction, EncodedSignedSpendTx).
<<"tx_+JkLAfhCuEBRjdPoC+tL3BtLXrDnRIAWZGQO6I2vadofjIR2QmM+Y80YPA2uakpSJ60BIIVxMVredCGZYu6teUXwdOBDmGsLuFH4TwwBoQGsthjeY"...>>
The last step can be done via curl
curl -X POST "http://localhost:3014/v2/transactions" \
-H "accept: application/json" -H "Content-Type: application/json" \
-d "{\"tx\": \"tx_asdkufgasudfgasjdfh\"}" # whatever you got in Base58CheckEncodedSignedSpendTx
There's probably a simpler way without posting the transaction to itself, but I haven't looked into it yet.