Using Java SDK to Implement Offline Signature

  • 2021-07-10 19:55:32
  • OfStack

Strictly speaking, tx-signer does not belong to SDK, but is the implementation version of java in bytomd. Therefore, if you want to sign the transaction offline with tx-signer, you need to keep your private key locally.

If your goal is to be completely detached from the bytomd full node, you may need to do more extra work on your own. For example, when building a transaction, you need to spend several utxo (Unspent Transaction Output) as input to the transaction, and if there is no full node, you need to maintain utxo by yourself. When a transaction is completed and signed using tx-signer, it is necessary to implement P2P network protocol to broadcast the transaction to other nodes without the help of all nodes.

This article will not discuss the above technical details, but use bytomd full node to query the available utxo to build the transaction, sign and serialize the transaction, and also use bytomd to submit the transaction.

Preparatory work

Introducing Maven dependencies into your project

Get SDK source code


git clone https://github.com/Bytom/bytom-java-sdk.git

Package into an JAR package and install it into a local Maven repository


$ mvn clean install -DskipTests

Add dependencies to the project's POM file. Among them, the first dependency is the encapsulation of bytomd api, which can be used to query the available utxo and submit transactions; The second dependency is used to construct the transaction and sign the transaction offline.


  <dependency>
    <groupId>io.bytom</groupId>
    <artifactId>java-sdk</artifactId>
    <version>1.0.0</version>
  </dependency>
 
  <dependency>
    <groupId>io.bytom</groupId>
    <artifactId>tx-signer</artifactId>
    <version>1.0.0</version>
  </dependency>

Build a transaction

Ordinary transaction

Query available utxo

In this article, all nodes will be used to query the available utxo, and you can also build your own utxo maintenance scheme.


Client client = Client.generateClient();
UnspentOutput.QueryBuilder builder = new UnspentOutput.QueryBuilder();
builder.accountAlias = "bytom";
List<UnspentOutput> outputs = builder.list(client);

It takes only 4 lines of code to query the available utxo with SDK (see java-sdk documentation for specific documents of SDK). In QueryBuilder, you can specify whether it is an unconfirmed utxo (default false), or you can use from and count for paging queries (default queries all).
Suppose you query under the current account and get such an utxo:


{
  "account_alias": "bytom",
  "id": "ffdc59d0478277298de4afa458dfa7623c051a46b7a84939fb8227083411b156",
  "asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
  "asset_alias": "BTM",
  "amount": 41250000000,
  "account_id": "0G1R52O1G0A02",
  "address": "sm1qxls6ajp6fejc0j5kp8jwt2nj3kmsqazfumrkrr",
  "control_program_index": 1,
  "program": "001437e1aec83a4e6587ca9609e4e5aa728db7007449",
  "source_id": "2d3a5d920833778cc7c65d7c96fe5f3c4a1a61aa086ee093f44a0522dd499a34",
  "source_pos": 0,
  "valid_height": 4767,
  "change": false,
  "derive_rule": 0
}

Build a transaction

Now we need to go 0014c832e1579b4f96dc12dcfff39e8fe69a62d3f516 This control program is converted to 100 BTM. The code is as follows:


String btmAssetID = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
//  The following fields are the same as the utxo Fields in the 11 Correspondence 
SpendInput input = new SpendInput();
input.setAssetId(btmAssetID);
input.setAmount(41250000000L);
input.setProgram("001437e1aec83a4e6587ca9609e4e5aa728db7007449");
input.setSourcePosition(0);
input.setSourceID("2d3a5d920833778cc7c65d7c96fe5f3c4a1a61aa086ee093f44a0522dd499a34");
input.setChange(false);
input.setControlProgramIndex(1);
//  Select to use BIP32 Or BIP44 To derive the address, the default BIP44
input.setBipProtocol(BIPProtocol.BIP44);
//  Key index corresponding to account 
input.setKeyIndex(1);
//  Private key kept locally by itself, which is used to sign transactions 
input.setRootPrivateKey("4864bae85cf38bfbb347684abdbc01e311a24f99e2c7fe94f3c071d9c83d8a5a349722316972e382c339b79b7e1d83a565c6b3e7cf46847733a47044ae493257");

Transaction tx = new Transaction.Builder()
        .addInput(input)
        //  Add the ones that need to be transferred output
        .addOutput(new Output(btmAssetID, 10000000000L, "0014c832e1579b4f96dc12dcfff39e8fe69a62d3f516"))
        //  Remaining BTM Used for change 
        .addOutput(new Output(btmAssetID, 31250000000L, "0014bb8a039726df1b649738e9973db14a4b4fd4becf"))
        .setTimeRange(0)
        .build();

String rawTransaction = tx.rawTransaction();

When the build method is called on a transaction, the transaction is automatically authenticated and signed locally. Note that only simple field validation is done locally, and passing the local validation does not mean that the transaction is legal. Finally, the rawTransaction method is called on the transaction to return the serialized string of the transaction.

Submit a transaction

This article uses the bytomd full node to submit transactions:


HashMap<String, Object> body = new HashMap<>();
body.put("raw_transaction", "070100010160015e4b5cb973f5bef4eadde4c89b92ee73312b940e84164da0594149554cc8a2adeaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80c480c1240201160014cb9f2391bafe2bc1159b2c4c8a0f17ba1b4dd94e6302401cb779288be890a28c5209036da1a27d9fe74a51c38e0a10db4817bcf4fd05f68580239eea7dcabf19f144c77bf13d3674b5139aa51a99ba58118386c190af0e20bcbe020b05e1b7d0825953d92bf47897be08cd7751a37adb95d6a2e5224f55ab02013dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80b095e42001160014a82f02bc37bc5ed87d5f9fca02f8a6a7d89cdd5c000149ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80d293ad03012200200824e931fb806bd77fdcd291aad3bd0a4493443a4120062bd659e64a3e0bac6600");
Transaction.SubmitResponse response = client.request("submit-transaction", body, Transaction.SubmitResponse.class);

After the transaction is submitted successfully, response returns to the transaction ID.

Transaction of issuing assets

Query available utxo

When issuing assets, BTM needs to be used as the handling fee, so the first step also needs to query the available utxo under the current account. As mentioned above, it will not be repeated here.

Query the asset information to be issued

For example, the asset id to be issued is 7b38dc897329a288ea31031724ES128ES5c55E130ES80468a54695023380af2faad14


Asset.QueryBuilder builder = new Asset.QueryBuilder();
builder.setId("7b38dc897329a288ea31031724f5c55bcafec80468a546955023380af2faad14");
List<Asset> assets = builder.list(client);

Assume that the asset information obtained by the query is as follows:


{
      "type": "asset",
      "xpubs": [
        "5ff7f79f0fd4eb9ccb17191b0a1ac9bed5b4a03320a06d2ff8170dd51f9ad9089c4038ec7280b5eb6745ef3d36284e67f5cf2ed2a0177d462d24abf53c0399ed"
      ],
      "quorum": 1,
      "key_index": 3,
      "derive_rule": 0,
      "id": "7b38dc897329a288ea31031724f5c55bcafec80468a546955023380af2faad14",
      "alias": " Bang bang chicken ",
      "vm_version": 1,
      "issue_program": "ae20db11f9dfa39c9e66421c530fe027218edd3d5b1cd98f24c826f4d9c0cd131a475151ad",
      "raw_definition_byte": "7b0a202022646563696d616c73223a20382c0a2020226465736372697074696f6e223a207b7d2c0a2020226e616d65223a2022222c0a20202273796d626f6c223a2022220a7d",
      "definition": {
        "decimals": 8,
        "description": {},
        "name": "",
        "symbol": ""
      }
}

Build a transaction

Now you need to issue 1,000 stick chicken assets:


IssuanceInput issuanceInput = new IssuanceInput();
issuanceInput.setAssetId("7b38dc897329a288ea31031724f5c55bcafec80468a546955023380af2faad14");
issuanceInput.setAmount(100000000000L);
// issue program
issuanceInput.setProgram("ae20db11f9dfa39c9e66421c530fe027218edd3d5b1cd98f24c826f4d9c0cd131a475151ad");
//  You can not specify it. If you do not specify it, it will be randomly generated 1 A 
issuanceInput.setNonce("ac9d5a527f5ab00a");
issuanceInput.setKeyIndex(5);
// raw definition byte
issuanceInput.setRawAssetDefinition("7b0a202022646563696d616c73223a20382c0a2020226465736372697074696f6e223a207b7d2c0a2020226e616d65223a2022222c0a20202273796d626f6c223a2022220a7d");
//  The private key corresponding to the asset 
issuanceInput.setRootPrivateKey("4864bae85cf38bfbb347684abdbc01e311a24f99e2c7fe94f3c071d9c83d8a5a349722316972e382c339b79b7e1d83a565c6b3e7cf46847733a47044ae493257");

//  Create 1 A spend input As a handling fee, assume that there are currently 1 A 100BTM Adj. utxo And use the 1BTM As a handling fee, you will subsequently create 99BTM Change address of 
SpendInput feeInput = new SpendInput(btmAssetID, 10000000000L, "0014cb9f2391bafe2bc1159b2c4c8a0f17ba1b4dd94e");
feeInput.setKeyIndex(1);
feeInput.setChange(true);
feeInput.setSourceID("4b5cb973f5bef4eadde4c89b92ee73312b940e84164da0594149554cc8a2adea");
feeInput.setSourcePosition(2);
feeInput.setControlProgramIndex(457);
feeInput.setRootPrivateKey("4864bae85cf38bfbb347684abdbc01e311a24f99e2c7fe94f3c071d9c83d8a5a349722316972e382c339b79b7e1d83a565c6b3e7cf46847733a47044ae493257");

Transaction tx = new Transaction.Builder()
        .addInput(issuanceInput)
        .addInput(feeInput)
        //  The output Used to receive issued assets 
        .addOutput(new Output("7b38dc897329a288ea31031724f5c55bcafec80468a546955023380af2faad14", 100000000000L, "001437e1aec83a4e6587ca9609e4e5aa728db7007449"))
        //  Change 
        .addOutput(new Output(btmAssetID, 9800000000L, "00148be1104e04734e5edaba5eea2e85793896b77c56"))
        .setTimeRange(0)
        .build();

Submit a transaction

The way of submitting transactions is the same as that of ordinary transactions.

Destruction of assets transaction

Destroying assets is similar to issuing assets, and BTM is also required as a handling fee.

Query available utxo

The inquiry method is the same as that of ordinary transactions.

Build a transaction

Let's take the example of destroying an BTM. Suppose the query yields an utxo of 100BTM:


$ mvn clean install -DskipTests
0

Submit a transaction

The way of submitting transactions is the same as that of ordinary transactions.

bytom java sdk: https://github.com/Bytom/bytom-java-sdk/


Related articles: