阿希链于5月份升级版本到1.5,由于阿希公司内部的变动,所以很久没有释放java版本的sdk,我根据1.4.6版本的java版sdk经过修改,完成满足1.5版本的合约交易提交的两个核心类。分别是TransactionBuilder和TransactionInfo。其中TransactionBuilder是对外暴露类,外部提供数据到buildTransaction方法。TransactionInfo内包含阿希相关交易必备的数据,包括签名、二级密码签名等。
下面上代码了,有问题留言!
首先是TransactionInfo.java
package com.asch.xiquspace.AschTools.transaction;
import com.alibaba.fastjson.JSON;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
public class TransactionInfo {
public Integer type = null;
public Integer timestamp = null;
public Long fee = null;
public String message = null;
public Object[] args = null;
public String senderPublicKey = null;
public String senderId = null;
public String[] signatures = null;
public String secondSignature = null;
public String id = null;
public byte[] getBytes(boolean skipSignature , boolean skipSignSignature) throws UnsupportedEncodingException {
ByteBuffer buffer = ByteBuffer.allocate(5120).order(ByteOrder.LITTLE_ENDIAN)
.putInt(getType().intValue())
.putInt(getTimestamp().intValue())
.putLong(getFee().longValue())
.put(getSenderId().getBytes())
.put(getMessage().getBytes())
.put(getArgsBuffer());
if (!skipSignature && getSignatures() != null) {
for (String temp : getSignatures()) {
byte[] buff = hexToByte(temp);
buffer.put(buff);
}
}
if (!skipSignSignature && getSecondSignature() != null) {
buffer.put(hexToByte(getSecondSignature()));
}
buffer.flip();
byte[] result = new byte[buffer.remaining()];
buffer.get(result);
return result;
}
public byte[] hexToByte(String str) {
byte[] bytes = new byte[str.length() / 2];
for(int i = 0; i < str.length() / 2; i++) {
String subStr = str.substring(i * 2, i * 2 + 2);
bytes[i] = (byte) Integer.parseInt(subStr, 16);
}
return bytes;
}
@Override
public String toString() {
return "TransactionInfo [type=" + type + ", timestamp=" + timestamp + ", fee=" + fee + ", message=" + message
+ ", args=" + Arrays.toString(args) + ", id=" + id + ", senderPublicKey=" + senderPublicKey
+ ", signature=" + Arrays.toString(signatures) + "]";
}
public String getMessage() {
return message;
}
public TransactionInfo setMessage(String message) {
this.message = message;
return this;
}
public String getSenderId() {
return senderId;
}
public TransactionInfo setSenderId(String senderId) {
this.senderId = senderId;
return this;
}
public String getSecondSignature() {
return secondSignature;
}
public TransactionInfo setSecondSignature(String secondSignature) {
this.secondSignature = secondSignature;
return this;
}
public String getId() {
return id;
}
public TransactionInfo setId(String id) {
this.id = id;
return this;
}
public Long getFee() {
return fee;
}
public TransactionInfo setFee(Long fee) {
this.fee = fee;
return this;
}
public Integer getTimestamp() {
return timestamp;
}
public TransactionInfo setTimestamp(Integer timestamp) {
this.timestamp = timestamp;
return this;
}
public String getSenderPublicKey() {
return senderPublicKey;
}
public TransactionInfo setSenderPublicKey(String senderPublicKey) {
this.senderPublicKey = senderPublicKey;
return this;
}
public Integer getType() {
return type;
}
public TransactionInfo setType(Integer type) {
this.type = type;
return this;
}
private byte[] getArgsBuffer() {
return JSON.toJSONString(getArgs()).getBytes();
}
public Object[] getArgs() {
return args;
}
public TransactionInfo setArgs(Object[] args) {
this.args = args;
return this;
}
public String[] getSignatures() {
return signatures;
}
public TransactionInfo setSignature(String[] signature) {
this.signatures = signature;
return this;
}
}
package com.asch.xiquspace.AschTools.transaction;
import com.asch.xiquspace.AschTools.AschBip39;
import so.asch.sdk.codec.Encoding;
import so.asch.sdk.dbc.Argument;
import so.asch.sdk.impl.AschFactory;
import so.asch.sdk.security.Ed25519;
import so.asch.sdk.security.SecurityException;
import so.asch.sdk.security.SecurityStrategy;
import so.asch.sdk.security.ripemd.RipeMD160;
import java.security.KeyPair;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.PublicKey;
public class TransactionBuilder {
private static final String SHA256_DIGEST_ALGORITHM = "SHA-256";
private static final MessageDigest sha256Digest;
static {
MessageDigest messageDigest = null;
RipeMD160 ripeMD160 = null;
try {
messageDigest = MessageDigest.getInstance(SHA256_DIGEST_ALGORITHM);
ripeMD160 = new RipeMD160();
} catch (Exception ex) {
}
sha256Digest = messageDigest;
}
public TransactionInfo buildTransaction(int type, Long fee, Object[] args, String message, String secret, String secondSecret) throws SecurityException {
KeyPair key = getSecurity().generateKeyPair(secret);
String publicKey = getPublicKeyString(secret);
String address = getSecurity().getBase58Address(publicKey);
TransactionInfo info = newTransaction(key.getPublic(), address, message)
.setType(type)
.setArgs(args)
.setFee(fee);
return signatureTransaction(info, key.getPrivate(), secondSecret);
}
protected TransactionInfo signatureTransaction(TransactionInfo info, PrivateKey privateKey, String secondSecret) {
try {
Argument.require(info != null, "transaction can not be null");
Argument.require(privateKey != null, "private key can not be null");
byte[] transactionBytes = info.getBytes(true, true);
System.out.println("signBuffer = " + Encoding.hex(transactionBytes));
byte[] hash = sha256Hash(transactionBytes);
String signture = Encoding.hex(Ed25519.signature(hash, privateKey));
String[] sings = new String[1];
sings[0] = signture;
info.setSignature(sings);
if(secondSecret != null && !"".equals(secondSecret)) {
KeyPair secondKeyPair = getSecurity().generateKeyPair(secondSecret);
byte[] transactionBytes1 = info.getBytes(true, true);
byte[] hash1 = sha256Hash(transactionBytes1);
String signture1 = Encoding.hex(Ed25519.signature(hash1, secondKeyPair.getPrivate()));
info.setSecondSignature(signture1);
}
info.setId(generateTransactionId(info));
} catch (Exception ex) {
try {
throw new SecurityException("setSignature transaction failed", ex);
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return info;
}
private String generateTransactionId(TransactionInfo transaction) throws SecurityException {
try {
byte[] transactionBytes = transaction.getBytes(false, false);
System.out.println("generateTransactionId = " + Encoding.hex(transactionBytes));
byte[] hash = sha256Hash(transactionBytes);
return Encoding.hex(hash);
} catch (Exception ex) {
throw new SecurityException("generate transaction id failed", ex);
}
}
protected TransactionInfo newTransaction(PublicKey pubKey, String address, String message)
throws SecurityException, SecurityException {
String publicKeyHex = getSecurity().encodePublicKey(pubKey);
return new TransactionInfo()
.setSenderId(address)
.setMessage(message)
.setTimestamp(Integer.valueOf(getSecurity().getTransactionTimestamp()))
.setSenderPublicKey(publicKeyHex);
}
private byte[] sha256Hash(byte[] message) {
sha256Digest.update(message);
return sha256Digest.digest();
}
protected SecurityStrategy getSecurity() {
return AschFactory.getInstance().getSecurity();
}
public PublicKey getPublicKey(String secret) {
SecurityStrategy security = AschFactory.getInstance().getSecurity();
if (!AschBip39.isValidMnemonicCode(secret)) {
return null;
}
try {
return security.generateKeyPair(secret).getPublic();
} catch (Exception ex) {
}
return null;
}
public String getPublicKeyString(String secret) {
SecurityStrategy security = AschFactory.getInstance().getSecurity();
if (!AschBip39.isValidMnemonicCode(secret)) {
return null;
}
try {
return security.encodePublicKey(security.generateKeyPair(secret).getPublic());
} catch (Exception ex) {
}
return null;
}
}
调用方法如下:
TransactionInfo info = new TransactionBuilder().buildTransaction(type, fee, args, message, secret, secondSecret);
ParameterMap map = new ParameterMap();
map.put("transaction", info);
Map mapHeader = new HashMap<>();
mapHeader.put("magic", magic);
mapHeader.put("Content-Type", "application/json");
String url = AschConfig.aschServerUrl + "/peer/transactions";
HttpREST.postJsonContent(url, map.toJSONString(), mapHeader, mHandler);
其中AschConfig.aschServerUrl是阿希主网的链接或者自己测试网络的链接,组装完成数据之后,则通过Http post到主网内完成交易!