Solana for java
- Solana 中应用的共识机制包括 Proof of History(POH) +Tower BFT+Proof of Replicate(复制证明)。POH 可将事件的时间编码排序输入到分布式账本中,随后利用 Tower BFT 共识机制来确认 POH 顺序的一致性。可将 Solana 将出块和验证分为两个不同的阶段,由一个节点(leader)负责收集交易并排序出块,其它节点负责验证。
- Solana 中没有分片等结构,每笔交易都包含了前一笔交易都 hash,因此类似于每笔交易都是一个区块。
-
领导者
-
验证者
- 验证节点参与交易验证;
- 验证者在投票时不断更新自己的根叉;
- 每次槽高度越过一个纪元(EPOCH)边界时,验证者都会更新其领导者时间表。
使用委托权益证明共识算法,激励代币持有者验证交易。SOL三个常用治理模型:
- Staking
- 质押竞选成为Leader,通过质押的数量的占比来轮换,所以这成了一个内卷的游戏;
- SOL 每年 -15% 代币通货膨胀,将 95% 膨胀的代币 分配给质押者;
- Fee
- Solana 中的交易费用会根据系统负载动态调整。50% 的费用被烧掉,这间接使 SOL 持有者受益,因为这降低了 SOL 的整体供应量。其余部分由提议包含交易的区块的验证者保留;
- DAO
-
公私钥
- 算法 ed25519
-
地址
-
签名
-
Hash 算法说明
- 使用的散列算法是 SHA-256
-
手续费模型
- Solana 中的交易费用会根据系统负载动态调整。
-
暂不支持交易加速
-
如何防止假充值
- 目前扫链没有进行中的中间状态,当交易 finalized 才给出交易结果;
-
扫块注意问题
- 暂时没有特别注意的状态
-
sol 和 spl 交易类型分类:
- sol 转账,目标地址为 spl token account 也可正常上链,但 token account 上的 sol 无法正常转出,通过 closing-accounts 关闭账号,从而实现对账号中余额的回收;
- spl 交易目标类型只能是 spl token account,如果目标是 sol account 无法上链,交易失败;
- spl token account 地址派生算法(可离线计算默认关联账户);
- 目标spl token account 不存在,对手续费无影响,但创建关联账户时需要转账 0.002 SQL 保持关联账户激活状态,有相关 rpc 可判断目标 spl token account 是否存在;
- sol account + spl token + nonce(0~255) 可计算出多个 关联账户;
- 全部转出 sol: total - fee;
- 全部转出 spl token: spl total + 足够手续费;
-
如何和 sol 自定义合约交互(example-helloworld)
/**
* Chain
*/
public interface Chain {
/**
* 检测地址有效性
* @param address
* @return
*/
boolean checkAddress(String address);
/**
* 随机生成密钥 以及 地址
* @return
*/
WalletPair genPair();
/**
* pub2Address
* @param publicKey 公钥
* @return
*/
String pub2Address(String publicKey);
/**
* 构建 TxPayload
* @param input
* @return
* @throws RpcException
*/
TTransaction buildTxPayload(TSignInput input) throws RpcException;
/**
* 签名原始交易
* @param secretKey 私钥
* @param message 待签名数据
* @return
*/
String signTransaction(String secretKey,byte[] message);
/**
* 合并交易并序列化
* @param transaction
* @param signatures 签名后的数组
* @return
*/
SignResult combineTransaction(TTransaction transaction,List<String> signatures);
/**
* 提交交易上链
* @param rawtx
* @return
* @throws RpcException
*/
RpcResult submitTransaction(String rawtx) throws RpcException;
/**
* 获取 手续费
* @param input 暂时可为空
* @return
* @throws RpcException
*/
RpcResult getTxFees(RpcInput input) throws RpcException;
/**
* 获取 账户余额
* @param input
* @return
* @throws RpcException
*/
RpcResult getBanlance(RpcInput input) throws RpcException;
/**
* 获取最新块高
* @return
* @throws RpcException
*/
long getLatestBlockHeight() throws RpcException;
/**
* 指定块高获取 交易块信息
* @param blockHeight
* @return
* @throws RpcException
*/
RpcBlock getBlockHeightTransactions(long blockHeight) throws RpcException;
/**
* 获取特定 txhash 相关的交易
* @param txHash
* @return
* @throws RpcException
*/
List<RpcTransaction> getTxHashTransaction(String txHash) throws RpcException;
}- 生成公私钥对
public class WalletPair { String publicKey; String secretKey; String address; } public void generateAccountPairTest() { Chain solanaChain = new Solana(Cluster.MAINNET.getEndpoint(),""); WalletPair pair = solanaChain.genPair(); }
- 公钥derive地址
public void public2AddressTest() { Chain solanaChain = new Solana(Cluster.MAINNET.getEndpoint(),""); String publicKey = "CuBUgjUkk1F9zubiy2Ns19cnLpk9UNMCQx13zZ8QAtJR"; String emptyAddress = solanaChain.pub2Address("CuBUgjUkk1F9zubiy2Ns19"); String address = solanaChain.pub2Address(publicKey); assertEquals(null, emptyAddress); assertEquals("CuBUgjUkk1F9zubiy2Ns19cnLpk9UNMCQx13zZ8QAtJR", address); }
- 交易签名分以下几种
- Sol Tranfer
- Token Tranfer
- CreateAccount And TokenTranfer 组装
- 复杂的合约交易
case 1: Sol Tranfer case 2: Token Tranfer case 3: CreateAccount And Token Tranfer 详见单元测试 buildTxPayload
- 进行私钥签名sign,返回签名后的数据 。
byte[] message = solTx.getTransaction().messageSerialize(); Logger.info("message = " + Hex.toHexString(message)); String signature = solanaChain.signTransaction(HexSecretKey, message);
- 交易和私钥签名组装combine,返回最终上链的序列化数据。
List<String> signatures = new ArrayList<String>(); signatures.add(signature); SignResult result = solanaChain.combineTransaction(solTx, signatures);
- 计算submit交易hash:需要根据交易签名组装交易后,预计算出此交易的hash。
RpcResult res = solanaChain.submitTransaction(result.getRawTx());
-
查询最新块高度
Chain solanaChain = new Solana(Cluster.DEVNET.getEndpoint(),""); ("BlockHeight = " + solanaChain.getLatestBlockHeight());
-
指定高度查询区块信息,包含解析块交易,获取块交易中的主链币交易和Token交易,数据如下
public abstract class RpcBlock { /**块 时间 */ private long blockTime; /**块高 */ private String blockHeight; /**块哈希 */ private String blockhash; /**交易列表 */ private List<RpcTransaction> transactions; } public abstract class RpcTransaction { /** * owner 下某合约币的关联账户 */ private String fromAccount; /** * dest 下某合约币的关联账户 */ private String toAccount; /** * 交易类型 */ private TransactionType type; /** * from address */ private String from; /** * to address */ private String to; /** * 合约地址 address */ private String contractAddress; /** * 转账数量 */ private String amount; /** * 转账数量 暂定整数 long */ private String uiAmount; /** * 手续费 */ private String fee; /** * 手续费 */ private String uiFee; /** * token 精度 */ private int decimal; /** * txhash */ private String txhash; /** * status */ private Commitment status; }
-
根据交易Hash获取指定交易
/**交易列表 */ List<RpcTransaction> transactions = getTxHashTransaction("2Z91u...c3Vy");
-
预估手续费
public class FeeInfo { public string fee; public String uiFee; } 详见单元测试: getFeeInfo()
-
转账前置参数
public abstract class TSignInput{ /** * owner 下某合约币的关联账户 */ private String fromAccount; /** * dest 下某合约币的关联账户, 可不传 */ private String toAccount; /** * 最近 blockHash */ private String recentBlockHash; /** * from address */ private String from; /** * to address */ private String to; /** * 合约地址 address */ private String contractAddress; /** * 转账数量 */ private String amount; /** * 手续费 */ private String fee; /** * token 精度 */ private int decimal; /** * 操作类型 */ private ActionType actionType; }
-
查询余额方法,包括主链币和合约token币
public class BanlanceInfo { public String amount; public String uiAmount; public int decimals; } 主链币详见:getOwnerBanlance() 代币详见:getTokenBanlance()
-
提交上链方法
RpcResult res = solanaChain.submitTransaction(result.getRawTx()); Logger.info("RpcResult = " + res.getTxHash());
-
检测地址
public void checkAddressTest() { Chain solanaChain = new Solana(Cluster.MAINNET.getEndpoint(),"MAINNET"); boolean isAddress1 = solanaChain.checkAddress("CuBUgjUkk1F9zubiy2Ns19cnLpk9UNMCQx13zZ8QAtJR"); boolean isAddress2 = solanaChain.checkAddress("CuBUgjUkk1F9aubiy2Ns19cnLpk9UNMCQx13zZ8QAtJ"); assertEquals(true, isAddress1); assertEquals(false, isAddress2); }
- 节点性能要求高
- 节点:
- 全节点(全部节点数据,占用大量磁盘)
- Rpc 节点(推荐搭建,定时删除冗余数据)
- 验证节点(在 Rpc 基础上,可以参与交易验证)
- 节点启动参数说明 Solana启动验证节点教程
- 节点配置说明
- 配置推荐:64core 256mem 2x2080Ti
- 磁盘推荐: 1T GPT分区
- 节点运行是否正常,提供校验命令
- 查看端口
solana 端口: 8899 root@solana:~# lsof -i:8899 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME solana-va 11967 root 116u IPv4 54896 0t0 TCP *:8899 (LISTEN)
- 查看mainnet集群信息(包含我们自己的节点信息)
root@solana:~# curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getClusterNodes"}' http://api.mainnet-beta.solana.com


