Understanding the Abelian Java SDK by Demos
NOTE
All demos described in this document are run on the testnet of the Abelian blockchain. The testnet is a separate blockchain from the mainnet and is used for testing purposes only. The built-in accounts used in the demos already has some ABELs and transactions on the testnet. As such, you can run the demos without setting up any testnet accounts or sending any ABELs to the built-in accounts.
1. Prepare the environment
1.1. Get the demos
Download and decompress abec4j-demo-x.y.z.zip
to get the demo directory abel4j-demo
. Go to the demo directory and we will run all the demos from there.
unzip abel4j-demo-x.y.z.zip
cd abel4j-demo
1.2. Test Java environment
Run the following command to test if your Java environment is ready for the demos.
bin/abel4j-demo
The expected output should be like this:
Usage: java -jar abec4j-demo.jar <demoName> [demoArgs]
Available demos:
Crypto
AbecRPCClient
Account
WalletDB
GetSafeBlockInfo
GetTxInfo
GetCoins
ScanCoins
GenerateUnsignedRawTx
GenerateSignedRawTx
SubmitSignedRawTx
TrackSpentCoins
As shown above, run bin/abel4j-demo
without any arguments will list all the available demos.
1.3. Test native library
The Abelian SDK for Java depends on a native library libabelsdk
built from the Abelian SDK for Go. The native library binaries for all supported platforms (libabelsdk.so.x
for Linux,libabelsdk.x.dylib
for macOS) are already included in the SDK jar file abel4j-x.y.z.jar
.
As of the time of writing, the native library is built for the following platforms:
- Linux x86
- Linux arm64
- MacOS x86
- MacOS arm64 (Apple Silicon)
The SDK jar file contains all the above native libraries for the supported platforms. When you run the demos, the SDK will automatically load the native library compatible with your platform.
Run the following command to test if the native library included in the SDK jar file is compatible with your platform.
bin/abel4j-demo Crypto
The expected output should be like this:
demoName = Crypto
demoArgs = []
default.chain = testnet
==> Generate crypto seeds.
--> Seed[0] = [132 bytes|0x00000000DFBE10E4FCD8FFB8F1F3EF5A...44024F1FF3D7011220EFB9A4F3C90EDA]
--> Seed[1] = [132 bytes|0x0000000092E7E2D4BE673639D59CB6E7...4F827B5F9B7BA2BAAD9FB15B207C4119]
--> Seed[2] = [132 bytes|0x0000000055DCB1813AEEEA1BD03FA053...1C4333A827A46187FDAF75EC9216C90C]
==> Generate crypto keys and addresses.
--> Seed[0]
Seed = [132 bytes|0x00000000DFBE10E4FCD8FFB8F1F3EF5A...44024F1FF3D7011220EFB9A4F3C90EDA]
SpendSecretKey = [1540 bytes|0x0000000028B828B4F68E39A2C2A86190...501534A2B9103DE7C6B232769928C284]
SerialNoSecretKey = [1060 bytes|0x00000000609B828D70BFB4640431049F...C2BE4C2B6B078042592CECAF6C5EBEF2]
ViewSecretKey = [2408 bytes|0x00000000010000000B037D7E3A1DCA8A...CE50929E27BFD8B288360E8E296EBF77]
CryptoAddress = [10696 bytes|0x0000000023B834EA17A2682EC80A9433...21724F48950AD1029F67288923A53005]
--> Seed[1]
Seed = [132 bytes|0x0000000092E7E2D4BE673639D59CB6E7...4F827B5F9B7BA2BAAD9FB15B207C4119]
SpendSecretKey = [1540 bytes|0x00000000F2C4EA89B89EC788680112B2...082AFF37CC5695825341ACBB110F30A3]
SerialNoSecretKey = [1060 bytes|0x000000000CF9FF22696EDD18CFC8A21E...5FC5323FD4D941C691F5D1535F8C891A]
ViewSecretKey = [2408 bytes|0x000000000100000066C44D38547C8101...FBFB5BF9B34BF889ADDB50E8456850E8]
CryptoAddress = [10696 bytes|0x00000000BDAFDD346C0FA9A0B53AE086...5B8AD385460D171DEEF684C0455E5ECE]
--> Seed[2]
Seed = [132 bytes|0x0000000055DCB1813AEEEA1BD03FA053...1C4333A827A46187FDAF75EC9216C90C]
SpendSecretKey = [1540 bytes|0x00000000BA6EA96078B81AF86AF5DEAA...2403C1672000013D7C40EC2E70A086AE]
SerialNoSecretKey = [1060 bytes|0x000000009A846D6E662B7F29FB8EC0EB...AB8E7FDEA242640DF41528A41AFB6AD7]
ViewSecretKey = [2408 bytes|0x000000000100000095810B7F8581E0C3...92DA1B1A3DD1E244ECF7C3D00AD14BA7]
CryptoAddress = [10696 bytes|0x00000000ADD40C21F358097C57B87B91...E201BFE5752A8159803574C2F26B5C0D]
==> Generate crypto keys and addresses again (from the same seeds).
--> Seed[0]
Seed = [132 bytes|0x00000000DFBE10E4FCD8FFB8F1F3EF5A...44024F1FF3D7011220EFB9A4F3C90EDA]
SpendSecretKey = [1540 bytes|0x0000000028B828B4F68E39A2C2A86190...501534A2B9103DE7C6B232769928C284]
SerialNoSecretKey = [1060 bytes|0x00000000609B828D70BFB4640431049F...C2BE4C2B6B078042592CECAF6C5EBEF2]
ViewSecretKey = [2408 bytes|0x00000000010000000B037D7E3A1DCA8A...2B4DB6CDC583FD3D0039549DFC4AA38C]
CryptoAddress = [10696 bytes|0x0000000023B834EA17A2682EC80A9433...21724F48950AD1029F67288923A53005]
--> Seed[1]
Seed = [132 bytes|0x0000000092E7E2D4BE673639D59CB6E7...4F827B5F9B7BA2BAAD9FB15B207C4119]
SpendSecretKey = [1540 bytes|0x00000000F2C4EA89B89EC788680112B2...082AFF37CC5695825341ACBB110F30A3]
SerialNoSecretKey = [1060 bytes|0x000000000CF9FF22696EDD18CFC8A21E...5FC5323FD4D941C691F5D1535F8C891A]
ViewSecretKey = [2408 bytes|0x000000000100000066C44D38547C8101...E91753573DC5CD65256F37DC826646DE]
CryptoAddress = [10696 bytes|0x00000000BDAFDD346C0FA9A0B53AE086...5B8AD385460D171DEEF684C0455E5ECE]
--> Seed[2]
Seed = [132 bytes|0x0000000055DCB1813AEEEA1BD03FA053...1C4333A827A46187FDAF75EC9216C90C]
SpendSecretKey = [1540 bytes|0x00000000BA6EA96078B81AF86AF5DEAA...2403C1672000013D7C40EC2E70A086AE]
SerialNoSecretKey = [1060 bytes|0x000000009A846D6E662B7F29FB8EC0EB...AB8E7FDEA242640DF41528A41AFB6AD7]
ViewSecretKey = [2408 bytes|0x000000000100000095810B7F8581E0C3...A04EE6AFF39781967601D708A8FA2D2C]
CryptoAddress = [10696 bytes|0x00000000ADD40C21F358097C57B87B91...E201BFE5752A8159803574C2F26B5C0D]
If the Crypto
demo runs successfully, then the native library included in the SDK jar file is compatible with your platform.
Note that this demo is for testing purpose only. It invokes low-level cryptographic APIs of the native library which is unlikely to be used directly in your applications. Therefore, it doesn't matter if you don't understand the output here or its source code. The only important thing is whether the demo runs successfully.
1.3 Test the default Abec full node for the demos
The Abelian SDK for Java depends on an Abec full node to access the data on the Abelian blockchain. In the default settings, the demos will connect to an Abec full node maintained by the Abelian Foundation. It allows you to run the demos without setting up your own Abec full node.
Note that the default Abec full node is for testing purpose only. It is not stable and may be reset at any time. For production use, you should always set up your own Abec full node for real-world applications.
Run the following command to test if the default Abec full node is accessible from your machine.
bin/abel4j-demo AbecRPCClient
The expected output should be like this:
demoName = AbecRPCClient
demoArgs = []
default.chain = testnet
==> Call any RPC method using client.call(method, ...params).
--> Call without parameters.
Request: method: getinfo, params: []
Response: {id: 2869ab40-871a-4c40-b273-d7f936e3302d, result: {"version":120500,"protocolversion":70002,"blocks":250120,"bestblockhash":"efc7470328364ff6747adff00a77846bb1bdc813d0a04b083af350cbcbbc1346","worksum":"35573957182947495035841336086341706532256","timeoffset":0,"connections":0,"proxy":"","difficulty":1,"testnet":true,"relayfee":1.0E-6,"errors":"","nodetype":"SemiFullNode","witnessserviceheight":0}, error: null}
✅ This is a successful call.
Error: null
Result: {"version":120500,"protocolversion":70002,"blocks":250120,"bestblockhash":"efc7470328364ff6747adff00a77846bb1bdc813d0a04b083af350cbcbbc1346","worksum":"35573957182947495035841336086341706532256","timeoffset":0,"connections":0,"proxy":"","difficulty":1,"testnet":true,"relayfee":1.0E-6,"errors":"","nodetype":"SemiFullNode","witnessserviceheight":0}
--> Call with parameters.
Request: method: getblockhash, params: [1 items|0]
Response: {id: a34fe402-0a2b-45b9-945b-cde9a2dae108, result: "eb143c8328e3131a4474ee1811d3c3a9f27e5102064148dc172966ccb50c2e2b", error: null}
✅ This is a successful call.
Error: null
Result: "eb143c8328e3131a4474ee1811d3c3a9f27e5102064148dc172966ccb50c2e2b"
--> Call with wrong method name.
Request: method: getnothing, params: []
Response: {id: 126ffa5c-4584-4867-94f9-b767a0a0d186, result: null, error: (-32601, "Method not found")}
❌ This is a failed call.
Error: (-32601, "Method not found")
Result: null
--> Call with wrong parameters.
Request: method: getinfo, params: [2 items|0, [9 chars|0x0000000]]
Response: {id: b883a361-d6c0-4fac-9dd0-3eeb3e74fba5, result: null, error: (-32602, "wrong number of params (expected 0, received 2)")}
❌ This is a failed call.
Error: (-32602, "wrong number of params (expected 0, received 2)")
Result: null
==> Call builtin member methods of AbecRPCClient.
--> Get chain info by client.getChainInfo().
ChainInfo: {"rpc":"https://testnet-rpc-exchange.abelian.info","height":250120,"difficulty":1}
==> Get block hash by client.getBlockHash(height=835).
BlockHash: [32 bytes|0x7AF4230AB3E7105C4067B0AEE93D20FBCBEC90A6F9DE08DB2E7AD14D8C77E730]
==> Get block info by client.getBlockInfo(hash=[32 bytes|0x7AF4230AB3E7105C4067B0AEE93D20FBCBEC90A6F9DE08DB2E7AD14D8C77E730]).
BlockInfo: {"height":835,"hash":{"data":"7AF4230AB3E7105C4067B0AEE93D20FBCBEC90A6F9DE08DB2E7AD14D8C77E730","length":32},"prevHash":{"data":"07340CEE74926B2AC5F9C3FA0FBC039CD3E26F8E8844C8902B4EAB04688475A5","length":32},"time":1707375349,"txHashes":[{"data":"4F5D4A6B922FC01E7584016BF3AB92421C7EC881837A18B4B787D03D708990B4","length":32}]}
==> Get tx info by client.getTxInfo(txid=[32 bytes|0x7AF4230AB3E7105C4067B0AEE93D20FBCBEC90A6F9DE08DB2E7AD14D8C77E730]).
TxInfo (without inputs and outputs): {"txid":{"data":"4F5D4A6B922FC01E7584016BF3AB92421C7EC881837A18B4B787D03D708990B4","length":32},"time":1707375349,"blockHash":{"data":"7AF4230AB3E7105C4067B0AEE93D20FBCBEC90A6F9DE08DB2E7AD14D8C77E730","length":32},"blockTime":1707375349,"vins":[],"vouts":[],"confirmations":249286,"coinSerialNumbers":[]}
Inputs:
vins[0]:
serialnumber: [64 bytes|0x00000000000000000000000000000000...00000000000000000000000000000000]
Outputs
vouts[0]:
script: [20469 bytes|0x01000000FDEE4F9DB62D1CEB38299C21...765AEF4A9875FD4A1231A1E3F1A7AF39]
fingerprint: [32 bytes|0x15D6D39BE0574F7FE3FBCE7EF6238B5B44075BC90896DD4E1EFD6355729E1842]
If the AbecRPCClient
demo runs successfully, then the default Abec full node is accessible from your machine.
Milestone
If you have reached this point, congratulations! You have successfully installed the Abelian SDK for Java and tested if it is working properly. You can now learn how to use the SDK to develop your own applications. In the following section, we will omit some output of the demos for brevity. You can run the demos yourself to see the full output.
2. Understand account in Abelian SDK
2.1 Concepts
An account in the Abelian SDK (both for Java and Go) consists of the following components:
Private Key: A 132-byte random data used to generate all other keys and addresses of the account. It is the only thing you need to back up for an account. You can use the same seed to restore the account. It is also referred to as
Crypto Seed
in the low-level APIs of the Abelian SDK.Spend Key: A 1540-byte secret key to sign transactions and should be treated as sensitive as the private key. However, we don't need to back it up as it can be derived from the private key.
Serial Number Key: A 1060-byte semi-secret key to decode which input coins are actually spent in a transaction. It can be derived from the private key.
View Key: A 2408-byte semi-secret key to decode the amount of ABELs in the output coins belonging to the account. It can be derived from the private key.
Abel Address: A 10696-byte address to receive ABELs. We often referred to it as
Address
for short. It can be derived from the private key.Short Address: A 66-byte address to receive ABELs. It is calculated from the abel address and can be used as a shorter alternative to the lengthy abel address. Note that a short address must be registered on the Abelian Name Service (ANS) before it can be used to receive ABELs.
Fingerprint: A 32-byte fingerprint of the account. It is calculated from the abel address and is part of the short address (from byte 3 to byte 34). We can use the fingerprint as an unique identifier of the account (aka
Account ID
) because it can be extracted from both the abel address and the short address. However, in this document, we will keep using the short address as theAccount ID
for consistency.
Note
Serial Number Key or View Key are called semi-secret keys because revealing them to untrusted parties will only compromise the privacy of the account. The funds in the account are safe as long as the Spend Key and the Private Key is not revealed.
For trusted parties (e.g., an internal Abelian Super Node), the serial number key and the view key can be revealed to them to allow them to track the spent coins and the balance of the account. In this sense, they can be treated as public keys.
In the SDK, an account with the secret keys (i.e., private key and spend key) is called a
signer account
; an account without the secret keys is called aviewer account
.
2.2. Create, import and export accounts
The Account
demo shows how to create, import and export accounts:
bin/abel4j-demo Account
2.3. Built-in accounts
There are 10 built-in accounts that will be used in the following demos. Their addresses are exported to $HOME/.abel4j/accounts
by the Account
demo. The exported file for an account has an .abeladdress
extension which can be loaded by the Abelian Desktop Wallet Legacy for specifying the receiver when sending ABELs. Each file contains two lines with the first line being the abel address and the second line being the short address.
The built-in accounts will be used in the following demos illustrating the key functionalities required by a non-trivial application (e.g., an exchange wallet).
3. Separate cold data and hot data to support offline signing
3.1. Initialize wallet databases
The WalletDB
demo shows how to initialize wallet databases:
bin/abel4j-demo WalletDB
If you haven't run other demos that may change the databases, the output should be like this:
demoName = WalletDB
demoArgs = []
default.chain = testnet
==> Get table summary of the hot wallet db.
Count of accounts: 10
Count of coins : 0
Count of txs : 0
==> Get table summary of the cold wallet db.
Count of accounts: 10
The WalletDB
demo creates two wallet databases in sqlite3
format under $HOME/.abel4j
: a cold database named demo-testnet-cold-wallet.db
and a hot database named demo-testnet-hot-wallet.db
. You may use any sqlite3
GUI client or the sqlite3
command to view the content of the databases.
3.2. Cold Database
The cold database only stores the private keys of the managed accounts for offline signing. It contains only one table signer_account
:
sqlite3 $HOME/.abel4j/demo-testnet-cold-wallet.db
sqlite> .tables
signer_account
sqlite> .schema signer_account
CREATE TABLE `signer_account` (`shortAddressHex` VARCHAR , `privateKeyHex` VARCHAR , PRIMARY KEY (`shortAddressHex`) );
sqlite> select * from signer_account;
The truncated output of the above query is:
ABE31C80....337C|00000000F255....DA5E
ABE39759....7111|00000000B283....1F50
ABE32184....94BA|00000000E549....CE21
ABE3B927....63DF|00000000669D....1457
ABE3A161....E270|000000006BEE....751F
ABE38506....1C8B|0000000017F1....F2CF
ABE30206....EF9F|000000000955....6B32
ABE3C872....FAA6|000000000FB7....0F48
ABE34746....8A1C|000000007820....31DD
ABE377B8....7A70|00000000882A....4B90
In the signer_account
table, the shortAddressHex
column serves as the account ID and the privateKeyHex
column serves is the private key of the account. Other information of an account will be derived on the fly from the private key when needed.
3.3. Hot Database
The hot database stores all the data excluding secret keys of the managed accounts for online operations such as scanning UTXOs, tracking spent coins, generating unsigned raw transactions, etc. It contains three tables viewer_account
, coin
and tx
:
sqlite3 ~/.abel4j/demo-testnet-hot-wallet.db
sqlite> .tables
coin tx viewer_account
sqlite> .schema viewer_account
CREATE TABLE `viewer_account` (`shortAddressHex` VARCHAR , `serialNoKeyHex` VARCHAR , `viewKeyHex` VARCHAR , `addressHex` VARCHAR , PRIMARY KEY (`shortAddressHex`) );
sqlite> .schema coin
CREATE TABLE `coin` (`coinIDStr` VARCHAR , `ownerShortAddressHex` VARCHAR , `value` BIGINT , `blockHeight` BIGINT , `snHex` VARCHAR , `isSpent` BOOLEAN , PRIMARY KEY (`coinIDStr`) );
sqlite> .schema tx
CREATE TABLE `tx` (`txMd5` VARCHAR , `unsignedRawTxDataHex` VARCHAR , `signerShortAddresses` VARCHAR , `signedRawTxDataHex` VARCHAR , `txid` VARCHAR , `isSubmitted` BOOLEAN , PRIMARY KEY (`txMd5`) );
The viewer_account
table stores all information but the secret keys of the managed accounts. The schema of the other two tables will be explained when they are used in the following demos.
4. Manage coins in the hot wallet
4.1. Search blocks for viewable coins
The ScanCoins
demo shows how to scan for coins belonging to the accounts managed by the hot wallet:
bin/abel4j-demo ScanCoins 5115 5120
We choose the above range of block heights because we know in advance that they contain some transactions that send ABELs to the built-in accounts. The ScanCoins
demo will scan the blocks in the specified range and store the coins belonging to the managed accounts in the hot wallet database.
Let's check the status of the hot wallet database after running the ScanCoins demo:
bin/abel4j-demo WalletDB
The output should be like this:
demoName = WalletDB
demoArgs = []
default.chain = testnet
==> Get table summary of the hot wallet db.
Count of accounts: 10
Count of coins : 1
Count of txs : 0
==> Get table summary of the cold wallet db.
Count of accounts: 10
We can see that the ScanCoins
demo has scanned 6 blocks and found 1 coin belonging to the managed accounts. Let's further check the content of the coin
table:
sqlite3 ~/.abel4j/demo-testnet-hot-wallet.db
sqlite> select coinIDStr, ownerShortAddressHex, value, blockHeight, isSpent from coin;
The output of the above query is (truncated):
033B8968...A51EFBE0:1|ABE31C80...FC8A337C|19103083|5116|0
We define Coin ID
as the combination of the txid
and the vout
index of a coin in hex format. The definition ensures that each coin has a unique ID and can be easily located by the ID.
To understand the meaning of the fields, let's take a closer look at the first coin: it bloings to one of the built-in accounts (ABE31C80...FC8A337C
), has a value of 1.9103083 ABELs (19,103,083 Neutrinos), is in block 5116
and is not being detected as spent yet.
We now scan another range of blocks to see if there are more coins belonging to the managed accounts:
bin/abel4j-demo ScanCoins 5750 5770
It is supposed to find another 2 coins. Let's check the status of the hot wallet database again:
bin/abel4j-demo WalletDB
The output should be like this:
demoName = WalletDB
demoArgs = []
default.chain = testnet
==> Get table summary of the hot wallet db.
Count of accounts: 10
Count of coins : 3
Count of txs : 0
==> Get table summary of the cold wallet db.
Count of accounts: 10
Note
We can scan the same blocks multiple times without worrying about duplicate coins. The
ScanCoins
demo will only add new coins to the database and ignore the duplicate ones. You may verify this by running the following commands to see if the number of coins in the hot wallet database changes:
bin/abel4j-demo ScanCoins 5760 5770
bin/abel4j-demo WalletDB
4.2. Track spent coins
At this point, the isSpent field of all the coins in the hot wallet database is still 0
(false
) because we haven't scanned the blocks that contain the transactions spending the coins. The TrackSpentCoins
demo shows how to track the spent coins:
bin/abel4j-demo TrackSpentCoins 9398
The output should be like this:
demoName = TrackSpentCoins
demoArgs = [9398]
default.chain = testnet
==> Tracking spent coins in block 9398.
--> Tracking spent coins in tx [32 bytes|0x58C16A37383B1C06C488DF0733E9DC1ACBED314E4BEF72B842B934B47A48BE3F].
--> Tracking spent coins in tx [32 bytes|0xFDF55398C6D82EF31FC2316BC3A67B419E75A2C9C0DD51746CA5EFADE692EE28].
💰 Found spent coin: id=033B89683B84DFF7B2E2C05C995A75B3F13CFF3A6FE26841A37B90EFA51EFBE0:1, value=19103083.
We can see that there is 1 coin spent in block 9398. Let's check the coin
table again:
sqlite3 ~/.abel4j/demo-testnet-hot-wallet.db
sqlite> select coinIDStr, blockHeight, isSpent from coin;
The output of the above query is:
033B89683B84DFF7B2E2C05C995A75B3F13CFF3A6FE26841A37B90EFA51EFBE0:1|5116|1
31C796C2424BDE46750677DED4D6832204F096AE93A8E58AE6A881CAEE1202F7:1|5751|0
B61E8F894D11D6E4C9CC06FDF967B81065D56778C560F19796C4DA7A47975995:0|5760|0
We can see there is 1 coin previously found in block 5116 are now marked as spent. The isSpent
field of the other coins remain unchanged.
5. Make a transaction
Making a transaction is non-trivial task. It involves many steps such as selecting coins to spend, calculating the change, generating the raw transaction, signing the raw transaction, broadcasting the signed transaction, etc. However, it is pretty easy to make a transaction with our crafted demos.
5.1. Generate an unsigned raw transaction
The GenerateUnsignedRawTx
demo shows how to generate an unsigned raw transaction:
bin/abel4j-demo GenerateUnsignedRawTx
Note
Please record the value of
txMd5
in the output for later use.
It will generate an unsigned raw transaction with 2 inputs and 2 outputs. The input coins are selected from the coins in the hot wallet database where the isSpent field is 0
(false
). The output coins are generated by the demo itself. Specifically, the receiver addresses are randomly chosen from the built-in accounts and the total output value is calculated by summing up the values of the input coins and subtracting the transaction fee from it.
The generated unsigned raw transaction will be saved in the tx
table of the hot wallet database. Let's check the status of the hot wallet database after running the GenerateUnsignedRawTx
demo:
bin/abel4j-demo WalletDB
The output should be like this:
demoName = WalletDB
demoArgs = []
default.chain = testnet
==> Get table summary of the hot wallet db.
Count of accounts: 10
Count of coins : 3
Count of txs : 1
==> Get table summary of the cold wallet db.
Count of accounts: 10
Note that the count of records in the tx
table is 1
now. Let's check the content of the tx
table:
sqlite3 ~/.abel4j/demo-testnet-hot-wallet.db
sqlite> select txMd5, length(unsignedRawTxDataHex), length(signedRawTxDataHex), isSubmitted from tx;
The output of the above query is like this (you may see different values):
F99C8BD32950DFB4C27651B8B1D85519|494436||0
If you have recorded the value of txMd5
in the output of the GenerateUnsignedRawTx
demo, you can verify that it is the same as the value in the tx
table. We can also see that the value of unsignedRawTxDataHex
is not empty and the value of signedRawTxDataHex
is empty. This is because the signed raw transaction is not generated yet.
5.2. Sign the unsigned raw transaction
The GenerateSignedRawTx
demo shows how to sign the unsigned raw transaction generated by the GenerateUnsignedRawTx
demo:
bin/abel4j-demo GenerateSignedRawTx F99C8BD32950DFB4C27651B8B1D85519
The output should be like this (it's unlikely to be the same as yours):
demoName = GenerateSignedRawTx
demoArgs = [F99C8BD32950DFB4C27651B8B1D85519]
default.chain = testnet
==> Generating a signed raw tx with txMd5: F99C8BD32950DFB4C27651B8B1D85519
==> Getting unsigned raw tx data from the hot wallet db.
Got unsigned raw tx: txMd5=F99C8BD32950DFB4C27651B8B1D85519, data=[247218 bytes|0x02651DCB7A4E484157DC2F06F0176CE3...01D134171AFE337FA902FE40420F0000], signers=2.
==> Getting signer accounts from the cold wallet db.
Got 2 signer accounts:
Signer 0: [66 bytes|0xABE12184DF76D1C0FEB878E54F0FF390...B5C34A6D176A2DDB7D8DB03261EA392C]
Signer 1: [66 bytes|0xABE19759B3B1FE374681E82ACF5742AA...010A0FE3DFF275BE226A2DC60D9F3D09]
==> Signing the unsigned raw tx.
Signed raw tx: txid=[32 bytes|0x381E6311F18787A7A26AF9657984DE6748FBB1C6ED7495F562398BF10242114E], data=[1798256 bytes|0x010000000240D3D88382D20C5ADE42B1...E97EA3294D5C6D3B4B248FF0DD7245E1].
==> Saving the signed raw tx to the hot wallet db.
We pass the txMd5
as the argument to the demo. It will be used to find the unsigned raw transaction in the tx
table. The demo will sign the transaction and save the signed data back to the tx
table.
Let's check the status of the hot wallet database after running the GenerateSignedRawTx
demo:
sqlite3 ~/.abel4j/demo-testnet-hot-wallet.db
sqlite> select txMd5, length(unsignedRawTxDataHex), length(signedRawTxDataHex), isSubmitted from tx;
Now the output of the above query is like this (you may see different values):
F99C8BD32950DFB4C27651B8B1D85519|494436|3596512|0
We can see that the value of signedRawTxDataHex
is not empty now.
Note
The transaction was signed by the spend keys of the senders (who owns the input coins). The spend keys are generated on-demand from the private keys stored in the cold wallet database. The private keys are never exposed to the public network. This is how the cold wallet database protects the private keys from being stolen.
5.3. Broadcast the signed raw transaction
The last step is to broadcast the signed raw transaction to the Abelian network. The SubmitSignedRawTx
demo shows how to broadcast the signed raw transaction generated by the GenerateSignedRawTx
demo:
bin/abel4j-demo SubmitSignedRawTx F99C8BD32950DFB4C27651B8B1D85519
If the transaction is successfully submitted, please record the value of txid
in the output and search it on the Abelian Explorer to see if it is confirmed. You may also check the tx
table to see if the isSubmitted
field of the transaction is set to 1
(true
).
If the transaction fails to be submitted, please check the error message in the output. The most common error is that one or more coins in the inputs are spent before the transaction is submitted. If you know at which block height the coins are spent, you can run the TrackSpentCoins
demo to update the isSpent
field of the coins in the hot wallet database and try to make another unsigned raw transaction, sign it and submit it again.
6. Summary
In this document, we have learned how to install the Abelian SDK for Java, test if it is working properly, understand the concepts of account in the Abelian SDK, separate cold data and hot data to support offline signing, manage coins in the hot wallet and make a transaction with the crafted demos.
There are other demos in the SDK that are not covered in this document. You may run them yourself to see the results. You may also read the source code of the demos to learn how to use the SDK to develop your own applications.