Edit me

[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()


####

Tags: