區塊鏈錢包-android篇

夏沫琅琊發表於2024-03-08


1:使用Protocol Buffers
首先根目錄gradle中新增依賴:

classpath "com.google.protobuf:protobuf-gradle-plugin:0.8.3"

然後專案檔案中新增plugin,新增依賴包:

apply plugin: 'com.google.protobuf'

protobuf {
protoc {
artifact = 'com.google.protobuf:protoc:3.6.1'
}
plugins {
javalite {
artifact = "com.google.protobuf:protoc-gen-javalite:3.0.0"
}
grpc {
artifact = 'io.grpc:protoc-gen-grpc-java:1.17.1'
}
}
generateProtoTasks {
all().each { task ->
task.plugins {
javalite {}
grpc {
// Options added to --grpc_out
option 'lite'
}
}
}
}
configurations {
all*.exclude group: 'com.android.support', module: 'support-v13'
}
}

dependencies {
api 'io.grpc:grpc-okhttp:1.17.1'
api 'io.grpc:grpc-protobuf-lite:1.17.1'
api 'io.grpc:grpc-stub:1.17.1'
}
最後main下新增proto路徑,新增proto檔案;

2:tron基礎類
關於tron的基礎類,我整理了個lib,使用時直接匯入lib即可。

主要包括wallet錢包類,管理類等內容。

這裡我主要貼出wallet的程式碼

package org.tron.walletserver;

import android.os.Build;
import android.support.annotation.RequiresApi;

import org.tron.common.crypto.ECKey;
import org.tron.common.utils.Utils;

import java.math.BigInteger;

public class Wallet {
private ECKey mECKey = null;

private boolean isWatchOnly = false;
private boolean isColdWallet = false;
private String walletName = "";
private String encPassword = "";
private String address = "";
private byte[] encPrivateKey;
private byte[] publicKey;


public Wallet() {
}

public Wallet(boolean generateECKey) {
    if(generateECKey) {
        mECKey = new ECKey(Utils.getRandom());
    }
}

@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public Wallet(String privateKey) {
    setPrivateKey(privateKey);
}

public boolean isOpen() {
    return mECKey != null && mECKey.getPrivKeyBytes() != null;
}

@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public boolean open(String password) {
    if(encPrivateKey != null) {
        setPrivateKey(AddressUtils.decryptPrivateKey(encPrivateKey, password));
        return isOpen();
    } else {
        return false;
    }
}

public byte[] getPublicKey() {
    return mECKey != null ? mECKey.getPubKey() : publicKey;
}

public void setPublicKey(byte[] publicKey) {
    this.publicKey = publicKey;
}

public byte[] getPrivateKey() {
    return mECKey != null ? mECKey.getPrivKeyBytes() : null;
}

@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public void setPrivateKey(String privateKey) {
    if(privateKey != null && !privateKey.isEmpty()) {
        ECKey tempKey = null;
        try {
            BigInteger priK = new BigInteger(privateKey, 16);
            tempKey = ECKey.fromPrivate(priK);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        mECKey = tempKey;
    } else {
        mECKey = null;
    }
}


public boolean isWatchOnly() {
    return isWatchOnly;
}

public void setWatchOnly(boolean watchOnly) {
    isWatchOnly = watchOnly;
}

public boolean isColdWallet() {
    return isColdWallet;
}

public void setColdWallet(boolean coldWallet) {
    isColdWallet = coldWallet;
}

public String getWalletName() {
    return walletName;
}

public void setWalletName(String walletName) {
    this.walletName = walletName;
}

public String getEncryptedPassword() {
    return encPassword;
}

public void setEncryptedPassword(String password) {
    this.encPassword = password;
}

@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public String getAddress() {
    if(mECKey != null) {
        return AddressUtils.encode58Check(mECKey.getAddress());
    }
    else if (publicKey != null){
        return AddressUtils.encode58Check(ECKey.fromPublicOnly(publicKey).getAddress());
    } else {
        return address;
    }
}

public void setAddress(String address) {
    this.address = address;
}

public ECKey getECKey() {
    return mECKey;
}

public byte[] getEncryptedPrivateKey() {
    return encPrivateKey;
}

public void setEncryptedPrivateKey(byte[] encPrivateKey) {
    this.encPrivateKey = encPrivateKey;
}

}
3:匯入錢包

其實就一步操作,就是根據私鑰 建立wallet物件。

//根據私鑰建立錢包
Wallet wallet = new Wallet(privateKey);
//設定錢包名稱,我這裡不需要就設定成錢包地址了。
wallet.setWalletName(wallet.getAddress());
//專案儲存匯入的錢包
TronManager.storeTron(wallet);

4:建立錢包
建立錢包需要兩個引數,一個是錢包名稱,一個是設定的密碼,錢包名稱還好,重要的是密碼,因為轉賬,匯出私鑰助記詞等資訊時,都是需要密碼驗證的。

Wallet wallet = new Wallet(true);
wallet.setWalletName(name);
WalletManager.store(this,wallet, pwd);
store方法是使用SharedPreferences儲存當前錢包的資訊,其中我們可以透過密碼來獲取wallet的eckey從而得到錢包的公鑰和私鑰。

5:錢包資訊
這裡主要用到專案中grpcClient類和walletmanager類。

這個類主要封裝了對應的網路請求以及錢包管理

grpcClient類的initGrpc需要傳入對應的fullnode和soliditynode,這個可以去下面的地址去選擇,

documentation/Official_Public_Node.md at master · tronprotocol/documentation · GitHub

查詢賬戶需要用到賬戶的地址:

Protocol.Account account = WalletManager.queryAccount(address, false);
GrpcAPI.AccountNetMessage accountNetMessage = WalletManager.getAccountNet(address);
GrpcAPI.AccountResourceMessage accountResMessage = WalletManager.getAccountRes(address);
account可以查詢balance和FrozenList;

accountNetMessage 可以查詢賬戶的頻寬資訊。

具體程式碼如下:

double balance=mAccount.getBalance()/1000000.0d;
binding.tvWalletAccount.setText(balance+"");

        long frozenTotal = 0;
        for(Protocol.Account.Frozen frozen : mAccount.getFrozenList()) {
            frozenTotal += frozen.getFrozenBalance();
        }
        binding.tvWalletTronWeight.setText((frozenTotal/1000000)+"");

        GrpcAPI.AccountNetMessage accountNetMessage = TronUtil.getAccountNet(getActivity(), wallet.getWalletName());

        long bandwidth = accountNetMessage.getNetLimit() + accountNetMessage.getFreeNetLimit();
        long bandwidthUsed = accountNetMessage.getNetUsed()+accountNetMessage.getFreeNetUsed();
        long bandwidthLeft = bandwidth - bandwidthUsed;

        binding.tvWalletBandwidth.setText(bandwidthLeft+"");

6:轉賬

byte[] toRaw;
try {
toRaw = WalletManager.decodeFromBase58Check(sendAddress);
} catch (IllegalArgumentException ignored) {
Toast.makeText(this,"請輸入一個有效地址",Toast.LENGTH_SHORT).show();
return;
}

    double amount=Double.parseDouble(sendAmount);
    Contract.TransferContract contract = WalletManager.createTransferContract(toRaw, WalletManager.decodeFromBase58Check(mWallet.getAddress()), (long) (amount * 1000000.0d));
    Protocol.Transaction transaction = WalletManager.createTransaction4Transfer(contract);
    byte[]  mTransactionBytes = transaction.toByteArray();
    try {
        mTransactionUnsigned = Protocol.Transaction.parseFrom(mTransactionBytes);
    } catch (InvalidProtocolBufferException e) {
        e.printStackTrace();
    }

根據上面的程式碼,獲取到mTransactionUnsigned ,然後輸入密碼校驗密碼並獲取到wallet的eckey。

設定時間戳,交易簽名。

mTransactionSigned = TransactionUtils.setTimestamp(mTransactionUnsigned);
mTransactionSigned = TransactionUtils.sign(mTransactionSigned, mWallet.getECKey());
WalletManager.broadcastTransaction(mTransactionSigned);
最後一步是廣播交易。

具體的程式碼可檢視git。

git地址

本文由部落格一文多發平臺 OpenWrite 釋出!

相關文章