本篇文章主要分析波场tron的交易签名验证的流程,汇总在哪些情况下会抛出哪些异常,希望大家在看到相应的报错后能够识别出问题出在哪里。重点不是介绍tron的签名体系。详细关于tron的账户权限机制请参考:https://cn.developers.tron.network/docs/多重签名
签名验证主要是发生在java-tron接收到交易之后,废话不多说直接贴代码:
//验证签名
//task1:如果之前已经验证通过, 直接返回
//task2:验证签名总数是否在正确的范围之内
//task3:验证签名,并处理异常
public boolean validateSignature(Manager manager)
throws ValidateSignatureException {
//----------------------------------------------------------------------
//task1:如果之前已经验证通过, 直接返回
//----------------------------------------------------------------------
if (isVerified == true) {
return true;
}
//----------------------------------------------------------------------
//task2:验证签名总数是否在正确的范围之内
//----------------------------------------------------------------------
//如果签名的总数小于0, 则抛出异常
if (this.transaction.getSignatureCount() <= 0
|| this.transaction.getRawData().getContractCount() <= 0) {
throw new ValidateSignatureException("miss sig or contract");
}
//如果超过5个签名, 向上抛出异常
if (this.transaction.getSignatureCount() > manager.getDynamicPropertiesStore()
.getTotalSignNum()) {
throw new ValidateSignatureException("too many signatures");
}
byte[] hash = this.getRawHash().getBytes();
//----------------------------------------------------------------------
//task3:验证签名,并处理异常
//----------------------------------------------------------------------
try {
if (!validateSignature(this.transaction, hash, manager)) {
isVerified = false;
throw new ValidateSignatureException("sig error");
}
} catch (SignatureException e) {
isVerified = false;
throw new ValidateSignatureException(e.getMessage());
} catch (PermissionException e) {
isVerified = false;
throw new ValidateSignatureException(e.getMessage());
} catch (SignatureFormatException e) {
isVerified = false;
throw new ValidateSignatureException(e.getMessage());
}
//验证通过,标记验证通过
isVerified = true;
return true;
}
validateSignature(Manager manager)函数是签名验证的入口,这个函数主要分3部分,首先是判断这个交易是不是被验证过, 如果是,则直接返回。第2步是判断交易中签名的个数是否在正常范围之内,由于波场支持多重签名,所以交易中可以存在多个签名,但是每个权限(Permission)最多只能支持5个签名,所以这里判断签名的个数必须大于0,或者小于等于5。如果签名个数没有问题,则直接调用validateSignature(Transaction transaction,
byte[] hash, Manager manager)做签名验证。
//验证一个交易的签名
//task1:根据交易的中的permissionid,从账户中获取permission对象
//task2:根据账户中定义的permission, 检查交易中的所有签名总权重是否达到正确的阀值
public static boolean validateSignature(Transaction transaction,
byte[] hash, Manager manager)
throws PermissionException, SignatureException, SignatureFormatException {
AccountStore accountStore = manager.getAccountStore();
//----------------------------------------------------------------------
//task1:根据交易的中的permissionid,从账户中获取permission对象
//----------------------------------------------------------------------
//获取交易中的contract
Transaction.Contract contract = transaction.getRawData().getContractList().get(0);
//获取permissionID
int permissionId = contract.getPermissionId();
//交易调用方
byte[] owner = getOwner(contract);
//获取交易调用方的账户
AccountCapsule account = accountStore.get(owner);
Permission permission = null;
//如果account不存在
if (account == null) {
//交易中的指定的permissionid 为0,使用默认的权限
if (permissionId == 0) {
permission = AccountCapsule.getDefaultPermission(ByteString.copyFrom(owner));
}
//交易中的指定饿permissionid为1, 使用默认的active权限
if (permissionId == 2) {
permission = AccountCapsule
.createDefaultActivePermission(ByteString.copyFrom(owner), manager);
}
} else {
//从账户中获取相应的permissionid
permission = account.getPermissionById(permissionId);
}
//如果账户中的permission不存在,向上抛出异常
if (permission == null) {
throw new PermissionException("permission isn't exit");
}
//检查操作权限
if (permissionId != 0) {
//如果permisionID不能于0, 只能等于2
if (permission.getType() != PermissionType.Active) {
throw new PermissionException("Permission type is error");
}
//check oprations
//检查权限,如果没有权限,向上抛出异常
if (!Wallet.checkPermissionOprations(permission, contract)) {
throw new PermissionException("Permission denied");
}
}
//----------------------------------------------------------------------
//task2:根据账户中定义的permission, 检查交易中的所有签名总权重是否达到正确的阀值
//----------------------------------------------------------------------
//检查权重是否超过阀值
long weight = checkWeight(permission, transaction.getSignatureList(), hash, null);
//检查权限的权重是否超过了阀值
if (weight >= permission.getThreshold()) {
return true;
}
return false;
}
validateSignature(Transaction transaction,
byte[] hash, Manager manager)函数主要是根据用户指定的permissionid从账户中取出permission,然后根据permission中签名的要求来验证交易中签名是否正确。
函数中首先就是从交易中抽取了出PermissionId,如果用户发送交易时没有指定PermissionId,java-tron会默认设定为0, 也就是用账户中的owner permission。如果交易发起方账户在链上不存在,则创建一个默认的permission来进行验证,这种情况一般发生在账户创建的交易中。
如果permissionid不为0, 那账户中的permission的类型必须是active。关于多重签名的详细介绍参考:https://cn.developers.tron.network/docs/多重签名。
如果permission类型是active的,还需要检查交易发起方是否具备当前的交易类型的操作权限,checkPermissionOprations函数做操作权限的检查。
如果上述的检查都通过,最后进入权重检查, 也就是统计交易中的签名的总权重,看是否达到了permission的最低要求。
//计算一个交易中所有签名的权重
//task1:如果签名的个数超过了该权限允许的地址总个数,向上抛出异常
//task2:统计所有签名的权重, 同时处理错误的情况
public static long checkWeight(Permission permission, List<ByteString> sigs, byte[] hash,
List<ByteString> approveList)
throws SignatureException, PermissionException, SignatureFormatException {
long currentWeight = 0;
// if (signature.size() % 65 != 0) {
// throw new SignatureFormatException("Signature size is " + signature.size());
// }
//----------------------------------------------------------------------
//task1:如果签名的个数超过了该权限允许的地址总个数,向上抛出异常
//----------------------------------------------------------------------
if (sigs.size() > permission.getKeysCount()) {
throw new PermissionException(
"Signature count is " + (sigs.size()) + " more than key counts of permission : "
+ permission.getKeysCount());
}
//----------------------------------------------------------------------
//task2:统计所有签名的权重, 同时处理错误的情况
//----------------------------------------------------------------------
HashMap addMap = new HashMap();
for (ByteString sig : sigs) {
//检查签名格式是否正确
if (sig.size() < 65) {
throw new SignatureFormatException(
"Signature size is " + sig.size());
}
String base64 = TransactionCapsule.getBase64FromByteString(sig);
//从签名中恢复地址
byte[] address = ECKey.signatureToAddress(hash, base64);
//获取这个地址的权重
long weight = getWeight(permission, address);
//如果这个地址的权重为0, 说明这个地址没有权利签名交易,向上抛出异常
if (weight == 0) {
throw new PermissionException(
ByteArray.toHexString(sig.toByteArray()) + " is signed by " + Wallet
.encode58Check(address) + " but it is not contained of permission.");
}
//检查是否不是存在一个地址多词签名的情况,否则向上抛出异常
if (addMap.containsKey(base64)) {
throw new PermissionException(Wallet.encode58Check(address) + " has signed twice!");
}
addMap.put(base64, weight);
if (approveList != null) {
approveList.add(ByteString.copyFrom(address)); //out put approve list.
}
//增加总的权重
currentWeight += weight;
}
return currentWeight;
}
首先判断交易中的签名总数有没有超过账户permission定义的key的总个数,如果超过了就向上抛出异常。检查通过后,会遍历交易中的签名,判断是否正确,并根据permission中的定义统计交易中签名的总权重,最后将总权重返回。
报错 | 原因 |
---|---|
miss sig or contract | 交易中签名数为0或者交易没有contract |
too many signatures | 交易中签名书超过5个 |
permission isn’t exit | 账户中不存在permission |
Permission type is error | 用户指定的permissionid不为0, 但是账户中的permission类型不是active |
Permission denied | 当前交易发起方账户不具备交易的指定的contract权限 |
operations size must 32 | 账户中permission的定义的操作码集合有误 |
Signature size is xxx | 签名数据有误 |
Signature count is xxx, more than key counts of permission : xxx | 签名总数超过了账户定义的key数量 |
“xxxxx” is signed by “xxxxxx”, but it is not contained of permission. | 签名的地址不在账户的permission定义的key列表中 |
“xxxxx” has signed twice! | 交易中存在多签的情况 |
sig error | 签名权重不够 |