[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方法进行处理。