Edit me

[TOC]

客户端部分

命令行初始化

客户端部分的初始化和chorus相同,都是使用了

/home/pct/go/src/github.com/ontio/ontology/vendor/github.com/urfave/cli

库来生成一个命令行的解析器:

/home/pct/go/src/github.com/ontio/ontology/nodectl.go

app.Commands = []cli.Command{
   *test.NewCommand(),
   *wallet.NewCommand(),
   *transfer.NewCommand(),

wallet命令部分

/home/pct/go/src/github.com/ontio/ontology/cli/wallet/wallet.go

Action: walletAction,

此方法主要实现了下面几个功能:

password.GetConfirmedPassword() //用户配置密码
password.GetPassword() //用户确认密码
account.Create(name, encrypt, []byte(passwd)) //创建新的账户
account.Open(name, []byte(passwd)) //打开已创建账户
wallet.ChangePassword([]byte(passwd), newPassword) //更改密码
wallet.GetDefaultAccount() //获取默认账户,代码中实现的是获取创建的第一个账户
rpc.Call(cliCommon.RpcAddress(), "getbalance", 0,[]interface{}{address.ToBase58()}) //远程调用getbalance方法

account.Create

首先生成一个ClientImpl的对象:

cl := NewClient(path, passwordKey, true)

这个方法将密码用加密密文的形式保存到文件wallet.dat中,然后返回一个ClientImpl对象,接下来调用这个对象的CreateAccount方法:

_, err := cl.CreateAccount(encrypt)

CreateAccount先用NewAccount生成一个Account账户,在账户里生成了公钥私钥以及用户地址:

/home/pct/go/src/github.com/ontio/ontology/account/account.go

ac := NewAccount(encrypt)
pri, pub, _ := keypair.GenerateKeyPair(pkAlgorithm, params)
address := types.AddressFromPubKey(pub)

将生成的这个账号ac放入客户端的map中,然后保存账户信息即上面创建的公私钥地址到wallet.dat文件中:

cl.accounts[ac.Address] = ac
err := cl.SaveAccount(ac)

account.Open

Open方法就是根据用户输入的passwd,对比密文,然后将wallet.dat中的数据load进内存。


wallet.GetDefaultAccount()

返回账户信息中保存的第一个账户,也就是Create填充的accounts这个map的成员。


transfer交易部分

/home/pct/go/src/github.com/ontio/ontology/cli/transfer/transfer.go

Action: transferAction,

交易部分做的工作主要是将交易信息打包进一个types.Transaction类型的数据中,对交易进行了签名,然后通过远程RPC调用节点端的服务:

tx := cutils.NewInvokeTransaction(vmtypes.VmCode{
if err := signTransaction(acc, tx); err != nil {
resp, err := rpc.Call(clicommon.RpcAddress(), "sendrawtransaction", 0,

服务端初始化

init方法

先查找最先执行的init方法,init方法是按照字母顺序按序执行的:

/home/pct/go/src/github.com/ontio/ontology/common/config/config.go /home/pct/go/src/github.com/ontio/ontology/cli/cli.go /home/pct/go/src/github.com/ontio/ontology/main.go /home/pct/go/src/github.com/ontio/ontology/vm/wasmvm/disasm/log.go /home/pct/go/src/github.com/ontio/ontology/vm/wasmvm/validate/log.go /home/pct/go/src/github.com/ontio/ontology/vm/wasmvm/wasm/log.go /home/pct/go/src/github.com/ontio/ontology/http/base/rpc/rpc.go


config.go–init

config部分从config.json文件中读取配置信息到config对象中,方便后续解析,内容就是我们在环境搭建章节设置的内容:

{
  "Configuration": {
    "Magic": 7630401,
    "Version": 23,
    "SeedList": [
      "127.0.0.1:20338"
    ],
    "Bookkeepers": [
       "1202038895767b1a58682c0a38d23893371b223be93fae904aca158d7183d69955200b"
    ],
    "HttpRestPort": 20334,
    "HttpWsPort":20335,
    "HttpJsonPort": 20336,
    "HttpLocalPort": 20337,
    "NodePort": 20338,
    "NodeConsensusPort": 20389,
    "PrintLevel": 0,
    "IsTLS": false,
    "MaxTransactionInBlock": 50000,
    "MultiCoreNum": 4,
    "ConsensusType":"solo"
  }
}

cli.go–init

初始化log模块


main.go–init

配置运行平台内核数,实现更好的并发机制


log.go–init

初始化log模块


rpc.go–init

初始化multiplexer的路由表参数


main函数

位置:

/home/pct/go/src/github.com/ontio/ontology/main.go


0. 读取钱包文件

第一步是打开我们上面创建的wallet.dat文件,并读取信息。 然后调用了event的init方法,初始化消息总线,这部分需要关注一下,它和消息分发有关。

/home/pct/go/src/github.com/ontio/ontology/events/actor_event.go

//Init event hub
events.Init()

1. 创建数据库以及创世块

首先创建数据库文件

/home/pct/go/src/github.com/ontio/ontology/core/ledger/ledger.go

func NewLedger() (*Ledger, error) {
   ldgStore, err := ledgerstore.NewLedgerStore()

/home/pct/go/src/github.com/ontio/ontology/core/store/ledgerstore/ledger_store.go

func NewLedgerStore() (*LedgerStoreImp, error) {
blockStore, err := NewBlockStore(DBDirBlock, true) //levelDB的数据库,存储块信息
stateStore, err := NewStateStore(DBDirState, MerkleTreeStorePath) //levelDB数据库,存储Merkle树
eventState, err := NewEventStore(DBDirEvent) //levelDB数据库,存储事件信息
err = ledgerStore.init()    //初始化ledgerStore的内部参数,如Merkle树,block,以及当前block账单的处理,因为是第一个块,没有交易信息,所以先跳过
go ledgerStore.start() //启动协程,定义两个定时器,每10s钟对块进行处理,然后将处理结果广播到网络,每60s清除超时未处理过的区块

主要是下面几个文件和目录:

pct@Chandler:~/go/src/github.com/ontio/ontology/bulid/Chain$ ls
block  ledgerevent  merkle_tree.db  states

然后初始化创世块:

err = ledger.DefLedger.Init(defBookkeepers)

填充创世块的header,添加出块奖励交易。 接着创建一个Actor,实现一个Reactor机制,它的处理函数是func (self *LedgerActor) Receive(ctx actor.Context):

ldgerActor := ldgactor.NewLedgerActor()
ledgerPID := ldgerActor.Start()

3. 启动交易池服务

/home/pct/go/src/github.com/ontio/ontology/txnpool/server.go

建立交易池对象,并启动两个协程作为挖矿的矿工,对交易池进行处理,无账单时9s钟处理一次:

s = tp.NewTxPoolServer(tc.MAX_WORKER_NUM)

这个方法最终调用到txPoolWorker的start方法:

/home/pct/go/src/github.com/ontio/ontology/txnpool/proc/txnpool_worker.go

func (worker *txPoolWorker) start()

然后创建三个映射并注册,映射是用来处理账单信息的,实现体在

/home/pct/go/src/github.com/ontio/ontology/txnpool/proc/txnpool_actor.go 的Receive方法中

最后将事件广播出去,收到广播后如何处理后续再查:

// Subscribe the block complete event
var sub = events.NewActorSubscriber(txPoolPid)
sub.Subscribe(message.TOPIC_SAVE_BLOCK_COMPLETE)

建立交易池后,创建两个认证者并注册到交易池中:

stlValidator, _ := stateless.NewValidator("stateless_validator")
stlValidator.Register(txPoolServer.GetPID(tc.VerifyRspActor))

stfValidator, _ := statefull.NewValidator("statefull_validator")
stfValidator.Register(txPoolServer.GetPID(tc.VerifyRspActor))

4. 启动P2P网络

/home/pct/go/src/github.com/ontio/ontology/net/net.go

这部分初始化P2P网络然后创建三个协程负责不同的事务处理:

go n.initConnection()
go n.updateConnection()
go n.updateNodeInfo()

同时添加网路映射:

netServerPid, err := actor.SpawnNamed(props, "net_server")
txPoolServer.RegisterActor(tc.NetActor, p2pActor)

启动HTTP服务:

go restful.StartServer()

5. 启动共识模块

/home/pct/go/src/github.com/ontio/ontology/consensus/consensus.go

consensusService, _ := consensus.NewConsensusService(acct, pool, nil, p2pActor)
net.SetConsensusPid(consensusService.GetPID())
go consensusService.Start()
time.Sleep(5 * time.Second)
hserver.SetConsensusPid(consensusService.GetPID())
go localrpc.StartLocalServer()

6. 启动RPC服务

/home/pct/go/src/github.com/ontio/ontology/http/jsonrpc/rpc_server.go

go jsonrpc.StartRPCServer()
go websocket.StartServer()
if config.Parameters.HttpInfoStart {
   go nodeinfo.StartServer(noder)
}

我们在创建交易账单时调用的RPC方法就是在这里设置的:

func StartRPCServer() {
   rpc.HandleFunc("getrawtransaction", rpc.GetRawTransaction)
   rpc.HandleFunc("sendrawtransaction", rpc.SendRawTransaction)

也就是说客户端发起一笔交易后会调用rpc.SendRawTransaction方法进行处理。

Tags: