[TOC]
目录结构
pct@Chandler:~/go/src/github.com/ontio/ontology/core$ tree -L 1
.
├── genesis
├── ledger
├── payload
├── signature
├── states
├── store
├── types
├── utils
├── validation
└── vote
10 directories, 0 files
core目录下文件较多,从名称上分析,大概有创世块,账本,存储,签名,状态机,类型,认证,投票以及通用模块。我们从简单的开始逐步分析。 — —
states
在interfaces.go中定义了一个通用的接口,在其他文件中定义了一些基础的数据结构,并且隐式的实现了StateValue接口
type StateValue interface {
Serialize(w io.Writer) error
Deserialize(r io.Reader) error
}
#####
types
此目录下是一些基础类型的定义,它还引用了common,ontology-crypto目录下的部分代码。 —
address.go
此文件的作用是生成地址,它提供了三个方法:
func AddressFromPubKey(pubkey keypair.PublicKey) common.Address //通过一个公钥生成地址,然后将地址第一个字节改为0x01
func AddressFromMultiPubKeys(pubkeys []keypair.PublicKey, m int) (common.Address, error) //通过多个公钥生成地址,公钥数量不能超过24,然后将首字节改为0x02
func AddressFromBookkeepers(bookkeepers []keypair.PublicKey) (common.Address, error) //通过config.json中配置的用户公钥来生成地址,会调用AddressFromMultiPubKeys,此函数是创建创世块时调用的
header.go
此部分定义区块头的数据结构:
type Header struct {
Version uint32 //版本
PrevBlockHash common.Uint256 //前向区块的Hash
TransactionsRoot common.Uint256 //交易的Merkle根节点Hash
BlockRoot common.Uint256 //区块根节点Hash
Timestamp uint32 //时间戳
Height uint32 //高度
ConsensusData uint64 //共识数据
NextBookkeeper common.Address //关于共识节点的数据
Bookkeepers []keypair.PublicKey //共识节点的公钥
SigData [][]byte //签名
hash common.Uint256 //Hash值
}
header提供了下面方法,同样有字节序列化和解析方法,注意,但凡实现了这两个方法都是隐性的实现了上面的接口:
func (bd *Header) SerializeUnsigned(w io.Writer) error //将header进行字节序列化不包含Bookkeepers和签名,计算签名时调用
func (bd *Header) Serialize(w io.Writer) error //将header进行字节序列化包含Bookkeepers和签名,存储或者传输时调用
func (bd *Header) Deserialize(r io.Reader) error //解析序列化的header
func (bd *Header) Hash() common.Uint256 //计算header签名哈希
func (bd *Header) GetMessage() []byte //返回SerializeUnsigned序列化结果
func (bd *Header) ToArray() []byte //返回Serialize序列化结果
transaction_attribute.go
交易数据存储的数据结构,定义了四种属性类型,同时提供序列化和解析方法:
Nonce TransactionAttributeUsage = 0x00
Script TransactionAttributeUsage = 0x20
DescriptionUrl TransactionAttributeUsage = 0x81
Description TransactionAttributeUsage = 0x90
transaction.go
此部分定义智能合约或者说账单相关的数据结构,在ONT中它们两个似乎是相同的:
type Transaction struct {
Version byte //版本号
TxType TransactionType //交易类型
Nonce uint32 //交易随机数
Payload Payload //接口,定义了序列化和解析两个方法,和StateValue完全一致
Attributes []*TxAttribute //交易属性,存放交易数据
Fee []*Fee //好像是记录费用的,里面有金额和付款人,可以有多笔
NetWorkFee common.Fixed64 //还有一个网络费用,这个可能是给矿工的小费
Sigs []*Sig //账单签名,可以有多个,是列表形式,里面存储公钥和签名信息
hash *common.Uint256 //账单哈希
}
这个Payload接口实现了这个数据结构的灵活性,因为只要是实现了Serialize和Deserialize两个方法的变量都可以存在这个字段,这样一来,这个结构体就能容纳交易信息和不同种类的智能合约信息,后面在创世块初始化时我们会看到创建了四个Transaction对象,分别放入了不同的数据到Payload字段。 文件还提供下面的一些方法:
func (self *Transaction) GetSignatureAddresses() []common.Address //根据签名中的公钥计算用户地址
func (tx *Transaction) GetTotalFee() common.Fixed64 //计算费用总额
func (tx *Transaction) GetNetworkFee() common.Fixed64 //获取网络费用
smartcontract.go
只定义了结构体,未定义任何方法:
type SmartCodeEvent struct {
TxHash string
Action string
Result interface{}
Error int64
}
block.go
定义区块数据结构:
type Block struct {
Header *Header
Transactions []*Transaction
hash *common.Uint256
}
同时定义了序列化和解析方法,同时定义了一个重建Merkle根哈希的方法,它把账单全部循环一遍重新计算根节点哈希:
func (b *Block) RebuildMerkleRoot() error {
txs := b.Transactions
transactionHashes := []common.Uint256{}
for _, tx := range txs {
transactionHashes = append(transactionHashes, tx.Hash())
}
hash, err := common.ComputeMerkleRoot(transactionHashes)
b.Header.TransactionsRoot = hash
payload
bookkeeper.go
存储共识节点的信息,同时实现了序列化和解析两个方法,等同于实现了Payload或者StateValue接口。
type Bookkeeper struct {
PubKey keypair.PublicKey
Action BookkeeperAction
Cert []byte
Issuer keypair.PublicKey
}
deploy_code.go
定义部署智能合约的数据结构:
// DeployCode is an implementation of transaction payload for deploy smartcontract
type DeployCode struct {
Code stypes.VmCode
NeedStorage bool
Name string
Version string
Author string
Email string
Description string
}
invoke_code.go
定义调用智能合约的数据结构:
// InvokeCode is an implementation of transaction payload for invoke smartcontract
type InvokeCode struct {
GasLimit common.Fixed64
Code stypes.VmCode
}
##### 定义了投票节点的数据结构,节点最大数量为1024:
type Vote struct {
PubKeys []keypair.PublicKey // vote node list
Account common.Address
}
utils
utils下只有一个文件:transaction_builder.go,定义了两个方法,这两个方法分别返回的是一样的对象,但是却是不同的功能,前者负责创建合约,后者负责调用合约,也是通过Payload接口来实现的:
func NewDeployTransaction(code stypes.VmCode, name, version, author, email, desp string, needStorage bool) *types.Transaction //创建部署智能合约的对象
func NewInvokeTransaction(vmcode stypes.VmCode) *types.Transaction //创建调用智能合约的对象
genesis
genesis下只有一个文件,首先定义两个常数,版本号和nonce值:
BlockVersion uint32 = 0
GenesisNonce uint64 = 2083236893
然后定义了几个全局变量,通过地址 {0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01} 创建了一个名为ONT的智能合约对象,将地址,名称等信息打包成code存入Payload字段;这里的地址就是我们转账时命令行需要输入的地址:
./nodectl transfer --contract ff00000000000000000000000000000000000001 --value 10 --from 0181beb9cfba23c777421eaf57e357e0fc331cbf --to 01f3aecd2ba7a5b704fbd5bac673e141d5109e3e
因此,从这里就可以看出,在ONT里,转账是通过智能合约实现的、 通过地址 {0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02} 创建了一个名为ONG的智能合约对象,也将地址,名称等信息打包成code存入Payload字段;然后计算这两个对象的哈希作为ID赋值给了两个全局变量:
ONTTokenID = ONTToken.Hash()
ONGTokenID = ONGToken.Hash()
这里就对应到白皮书里讲的分布式身份识别标识ONT ID。这两个ID从名称看是管理代币和通用代币,后面看看它们是否起到了代币的作用。 接下来定义了出块时间:
var GenBlockTime = (config.DEFAULT_GEN_BLOCK_TIME * time.Second)
每6s出一个块。 最后就是创世块的初始化过程,GenesisBlockInit:
nextBookkeeper, err := types.AddressFromBookkeepers(defaultBookkeeper)
获取下个出块节点,但是从代码上分析不出如何确定下个节点,单个节点的情况下,返回的总是自身地址,后续运行多个节点看下运行情况。 用上面设置的几个变量创建区块头:
genesisHeader := &types.Header{
Version: BlockVersion,
PrevBlockHash: common.Uint256{},
TransactionsRoot: common.Uint256{},
Timestamp: uint32(uint32(time.Date(2017, time.February, 23, 0, 0, 0, 0, time.UTC).Unix())),
Height: uint32(0),
ConsensusData: GenesisNonce,
NextBookkeeper: nextBookkeeper,
Bookkeepers: nil,
SigData: nil,
}
创建两个智能合约,和上面的ONTTokenID,ONGTokenID是重复的,完全可以直接使用,这里没有用,可能其他地方会修改那两个全局变量:
ont := newGoverningToken()
ong := newUtilityToken()
用这些参数创建区块:
genesisBlock := &types.Block{
Header: genesisHeader,
Transactions: []*types.Transaction{
ont,
ong,
newGoverningInit(),
newUtilityInit(),
},
}
区块里含有四个交易,或者说智能合约,如同上面所述,在ONT里,交易似乎和智能合约糅杂了,在结构体Transaction中,多了个Payload的字段,newGoverningInit()和newUtilityInit()把一个Contract对象放入了这个字段,因为Contract实现了Payload接口,因此是可以实现这种灵活操作的。 最后调用下面方法,计算创世区块的Merkle根节点哈希,其实就是计算上面创建的这四个智能合约的Merkle哈希:
genesisBlock.RebuildMerkleRoot()
####