【软件工程与实践】(5)jdchain的区块账户模型Account以及ContractAccount和UserAccount的账户职能

2021SC@SDUSC

一、京东区块链账户模型

1.区块链账户类型

区块链有两种账户类型:UTXO模型Account模型,而jdchain用的既是Account类型的账户模型

Account账户

对于 Account 模型,Account 模型保存了世界状态,链的状态一般在区块中以 StateRoot 和 ReceiptRoot 等形式进行共识。交易只是事件本身,不包含结果,交易的共识和状态的共识本质上可以隔离的。

Account账户的优缺点

优点:

合约以代码形式保存在 Account 中,并且 Account 拥有自身状态。这种模型具有更好的可编程性,容易开发人员理解,场景更广泛。

批量交易的成本较低。设想矿池向矿工支付手续费,UTXO 中因为每个 Input 和 Out 都需要单独 Witness script 或者 Locking script,交易本身会非常大,签名验证和交易存储都需要消耗链上宝贵的资源。而 Account 模型可以通过合约的方式极大的降低成本。

缺点:

Account 模型交易之间没有依赖性,需要解决重放问题。

对于实现闪电网络/雷电网络,Plasma 等,用户举证需要更复杂的 Proof 证明机制,子链向主链进行状态迁移需要更复杂的协议。

二、Account代码分析

1.接口

1)Account接口

接口引入了两个方法,分别可以获取区块链的ID和调用使用MerkleTree的特性来调取数据设置参数。

import com.jd.blockchain.ledger.BlockchainIdentity;
import com.jd.blockchain.ledger.TypedValue;

public interface Account {
    
    
	
	BlockchainIdentity getID();
	
	MerkleDataset<String, TypedValue> getDataset();
	
}

2)AccountAccessPolicy接口

代码展示了账户访问的策略,这个接口在提交专用账户的事务之前回先检查账户的访问政策,如果申请的账户满足条件,就可以返回一个true。

import com.jd.blockchain.crypto.PubKey;
import com.jd.blockchain.ledger.BlockchainIdentity;

import utils.Bytes;
public interface AccountAccessPolicy {
    
    

	boolean checkDataWriting(BlockchainIdentity account);

	boolean checkRegistering(Bytes address, PubKey pubKey);

}

2.类

1) ContractAccount事务账户

我先来看事务账户,它继承了AccountDecorator,同时调用了ContractInfo的接口。下方是ContractInfo的代码。

ContractInfo调用了两个接口用来获取区块的暗码以及最近更新的版本。

@com.jd.binaryproto.DataContract(code = 2560)
public interface ContractInfo extends com.jd.blockchain.ledger.BlockchainIdentity, com.jd.blockchain.ledger.AccountSnapshot {
    
    
    @com.jd.binaryproto.DataField(order = 4, primitiveType = com.jd.binaryproto.PrimitiveType.BYTES)
    byte[] getChainCode();

    @com.jd.binaryproto.DataField(order = 5, primitiveType = com.jd.binaryproto.PrimitiveType.INT64)
    long getChainCodeVersion();
}
public class ContractAccount extends AccountDecorator implements ContractInfo

我们接下来来看看这个类里面的方法:

方法setChaincode(),输入账户的密码,这个方法会返回账户的CHAIN_CODE_KEY即你输入的密码,以及字节值,版本号。

public long setChaincode(byte[] chaincode, long version) {
    
    
		TypedValue bytesValue = TypedValue.fromBytes(chaincode);
		return getHeaders().setValue(CHAIN_CODE_KEY, bytesValue, version);
	}

这两个就是类调用的接口方法的延伸,一种是无参调用,它会自动返回最新的版本密码;第二种方法则是输入你想要的版本密码,方法给你返回指定的版本密码。


	public byte[] getChainCode() {
    
    
		return getHeaders().getValue(CHAIN_CODE_KEY).getBytes().toBytes();
	}

	public byte[] getChainCode(long version) {
    
    
		return getHeaders().getValue(CHAIN_CODE_KEY, version).getBytes().toBytes();
	}

调用该代码会返回最新版本的数据账户密码。

public long getChainCodeVersion() {
    
    
		return getHeaders().getVersion(CHAIN_CODE_KEY);
	}

方法可以设置账户的参数,输入正确的key就可以改变value和版本。

public long setProperty(String key, String value, long version) {
    
    
		TypedValue bytesValue = TypedValue.fromText(value);
		return getHeaders().setValue(encodePropertyKey(key), bytesValue, version);
	}

输入正确的key,方法可以获取一个事物账户的value和版本,第一个方法会返回最新版本的参数,第二个方法会返回指定版本的参数。

	public String getProperty(String key) {
    
    
		BytesValue bytesValue = getHeaders().getValue(encodePropertyKey(key));
		return TypedValue.wrap(bytesValue).stringValue();
	}

	public String getProperty(String key, long version) {
    
    
		BytesValue bytesValue = getHeaders().getValue(encodePropertyKey(key), version);
		return TypedValue.wrap(bytesValue).stringValue();
	}

这个方法会返回账户的信息前缀所组合成的code,即公钥

	private String encodePropertyKey(String key) {
    
    
		return CONTRACT_INFO_PREFIX.concat(key);
	}

2)UserAccount用户账户

这个可以获取UserAccount的公钥,取款链的交易信息都是透明的,就体现在这里,这个公钥可以查询指定账户的交易信息,而这个方法就是获取账户公钥的方法,我们来看一看,如果账户的dataPubKey是空的那么方法就会追寻到区块的Header获取你的公钥,如果为空那么就返回null,如果不为空,公钥会经过加密算法返回。

public PubKey getDataPubKey() {
    
    
		if (dataPubKey == null) {
    
    
			BytesValue pkBytes = getHeaders().getValue(DATA_PUB_KEY);
			if (pkBytes == null) {
    
    
				return null;
			}
			dataPubKey = Crypto.resolveAsPubKey(pkBytes.getBytes().toBytes());
		}
		return dataPubKey;
	}

这两个方法可以用来更新自己的公钥,第一个方法无需输入版本,只需输入自己的公钥,即可更新;第二种是输入公钥以及指定版本,如果版本号大于-1则跟新成功,如果版本号小于-1,则证明这个公钥不在此区块中,系统会抛出错误信息。

public void setDataPubKey(PubKey pubKey) {
    
    
		long version = getHeaders().getVersion(DATA_PUB_KEY);
		setDataPubKey(pubKey, version);
	}public void setDataPubKey(PubKey pubKey, long version) {
    
    
		TypedValue value = TypedValue.fromPubKey(dataPubKey);
		long newVersion = getHeaders().setValue(DATA_PUB_KEY, value, version);
		if (newVersion > -1) {
    
    
			dataPubKey = pubKey;
		} else {
    
    
			throw new LedgerException("Data public key was updated failed!");
		}
	}

这下面四个方法和ContactAccount的方法类似,我就不多做赘述。

public long setProperty(String key, String value, long version) {
    
    
		return getHeaders().setValue(encodePropertyKey(key), TypedValue.fromText(value), version);
	}

	public String getProperty(String key) {
    
    
		BytesValue value = getHeaders().getValue(encodePropertyKey(key));
		return value == null ? null : value.getBytes().toUTF8String();
	}

	public String getProperty(String key, long version) {
    
    
		BytesValue value = getHeaders().getValue(encodePropertyKey(key), version);
		return value == null ? null : value.getBytes().toUTF8String();
	}

	private String encodePropertyKey(String key) {
    
    
		return USER_INFO_PREFIX+key;
	}

猜你喜欢

转载自blog.csdn.net/weixin_45932150/article/details/121054291
今日推荐