国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

剝開(kāi)比原看代碼11:比原是如何通過(guò)接口/create-account創(chuàng)建帳戶的

haobowd / 860人閱讀

摘要:而本文將繼續(xù)討論,比原是如何通過(guò)接口來(lái)創(chuàng)建帳戶的。把各信息打包在一起,稱之為另外,在第處還是一個(gè)需要注意的。比原在代碼中使用它保存各種數(shù)據(jù),比如區(qū)塊帳戶等。到這里,我們已經(jīng)差不多清楚了比原的是如何根據(jù)用戶提交的參數(shù)來(lái)創(chuàng)建帳戶的。

作者:freewind

比原項(xiàng)目倉(cāng)庫(kù):

Github地址:https://github.com/Bytom/bytom

Gitee地址:https://gitee.com/BytomBlockc...

在前面,我們探討了從瀏覽器的dashboard中進(jìn)行注冊(cè)的時(shí)候,數(shù)據(jù)是如何從前端發(fā)到后端的,并且后端是如何創(chuàng)建密鑰的。而本文將繼續(xù)討論,比原是如何通過(guò)/create-account接口來(lái)創(chuàng)建帳戶的。

在前面我們知道在API.buildHandler中配置了與創(chuàng)建帳戶相關(guān)的接口配置:

api/api.go#L164-L244

func (a *API) buildHandler() {
    // ...
    if a.wallet != nil {
        // ...
        m.Handle("/create-account", jsonHandler(a.createAccount))
        // ...

可以看到,/create-account對(duì)應(yīng)的handler是a.createAccount,它是我們本文將研究的重點(diǎn)。外面套著的jsonHandler是用來(lái)自動(dòng)JSON與GO數(shù)據(jù)類型之間的轉(zhuǎn)換的,之前討論過(guò),這里不再說(shuō)。

我們先看一下a.createAccount的代碼:

api/accounts.go#L15-L30

// POST /create-account
func (a *API) createAccount(ctx context.Context, ins struct {
    RootXPubs []chainkd.XPub `json:"root_xpubs"`
    Quorum    int            `json:"quorum"`
    Alias     string         `json:"alias"`
}) Response {

    // 1. 
    acc, err := a.wallet.AccountMgr.Create(ctx, ins.RootXPubs, ins.Quorum, ins.Alias)
    if err != nil {
        return NewErrorResponse(err)
    }

    // 2. 
    annotatedAccount := account.Annotated(acc)
    log.WithField("account ID", annotatedAccount.ID).Info("Created account")

    // 3.
    return NewSuccessResponse(annotatedAccount)
}

可以看到,它需要前端傳過(guò)來(lái)root_xpubs、quorumalias這三個(gè)參數(shù),我們?cè)谥暗奈恼轮幸部吹?,前端也的確傳了過(guò)來(lái)。這三個(gè)參數(shù),通過(guò)jsonHandler的轉(zhuǎn)換,到這個(gè)方法的時(shí)候,已經(jīng)成了合適的GO類型,我們可以直接使用。

這個(gè)方法主要分成了三塊:

使用a.wallet.AccountMgr.Create以及用戶發(fā)送的參數(shù)去創(chuàng)建相應(yīng)的帳戶

調(diào)用account.Annotated(acc),把a(bǔ)ccount對(duì)象轉(zhuǎn)換成可以被JSON化的對(duì)象

向前端發(fā)回成功信息。該信息會(huì)被jsonHandler自動(dòng)轉(zhuǎn)為JSON發(fā)到前端,用于顯示提示信息

第3步?jīng)]什么好說(shuō)的,我們主要把目光集中在前兩步,下面將依次結(jié)合源代碼詳解。

創(chuàng)建相應(yīng)的帳戶

創(chuàng)建帳戶使用的是a.wallet.AccountMgr.Create方法,先看代碼:

account/accounts.go#L145-L174

// Create creates a new Account.
func (m *Manager) Create(ctx context.Context, xpubs []chainkd.XPub, quorum int, alias string) (*Account, error) {
    m.accountMu.Lock()
    defer m.accountMu.Unlock()

    // 1.
    normalizedAlias := strings.ToLower(strings.TrimSpace(alias))

    // 2.
    if existed := m.db.Get(aliasKey(normalizedAlias)); existed != nil {
        return nil, ErrDuplicateAlias
    }

    // 3. 
    signer, err := signers.Create("account", xpubs, quorum, m.getNextAccountIndex())
    id := signers.IDGenerate()
    if err != nil {
        return nil, errors.Wrap(err)
    }

    // 4.
    account := &Account{Signer: signer, ID: id, Alias: normalizedAlias}

    // 5. 
    rawAccount, err := json.Marshal(account)
    if err != nil {
        return nil, ErrMarshalAccount
    }

    // 6. 
    storeBatch := m.db.NewBatch()
    accountID := Key(id)
    storeBatch.Set(accountID, rawAccount)
    storeBatch.Set(aliasKey(normalizedAlias), []byte(id))
    storeBatch.Write()

    return account, nil
}

我們把該方法分成了6塊,這里依次講解:

把傳進(jìn)來(lái)的帳戶別名進(jìn)行標(biāo)準(zhǔn)化修正,比如去掉兩頭空白并小寫(xiě)

從數(shù)據(jù)庫(kù)中尋找該別名是否已經(jīng)用過(guò)。因?yàn)閹艉蛣e名是一一對(duì)應(yīng)的,帳戶創(chuàng)建成功后,會(huì)在數(shù)據(jù)庫(kù)中把別名記錄下來(lái)。所以如果能從數(shù)據(jù)庫(kù)中查找,說(shuō)明已經(jīng)被占用,會(huì)返回一個(gè)錯(cuò)誤信息。這樣前臺(tái)就可以提醒用戶更換。

創(chuàng)建一個(gè)Signer,實(shí)際上就是對(duì)xpubs、quorum等參數(shù)的正確性進(jìn)行檢查,沒(méi)問(wèn)題的話會(huì)把這些信息捆綁在一起,否則返回錯(cuò)誤。這個(gè)Signer我感覺(jué)是檢查過(guò)沒(méi)問(wèn)題簽個(gè)字的意思。

把第3步創(chuàng)建的signer和id,還有前面的標(biāo)準(zhǔn)化之后的別名拿起來(lái),放在一起,就組成了一個(gè)帳戶

把帳戶對(duì)象變成JSON,方便后面往數(shù)據(jù)庫(kù)里存

把帳戶相關(guān)的數(shù)據(jù)保存在數(shù)據(jù)庫(kù),其中別名與id對(duì)應(yīng)(方便以后查詢別名是否存在),id與account對(duì)象(JSON格式)對(duì)應(yīng),保存具體的信息

這幾步中的第3步中涉及到的方法比較多,需要再細(xì)致分析一下:

signers.Create

blockchain/signers/signers.go#L67-L90

// Create creates and stores a Signer in the database
func Create(signerType string, xpubs []chainkd.XPub, quorum int, keyIndex uint64) (*Signer, error) {
    // 1. 
    if len(xpubs) == 0 {
        return nil, errors.Wrap(ErrNoXPubs)
    }

    // 2.
    sort.Sort(sortKeys(xpubs)) // this transforms the input slice
    for i := 1; i < len(xpubs); i++ {
        if bytes.Equal(xpubs[i][:], xpubs[i-1][:]) {
            return nil, errors.WithDetailf(ErrDupeXPub, "duplicated key=%x", xpubs[i])
        }
    }

    // 3. 
    if quorum == 0 || quorum > len(xpubs) {
        return nil, errors.Wrap(ErrBadQuorum)
    }

    // 4.
    return &Signer{
        Type:     signerType,
        XPubs:    xpubs,
        Quorum:   quorum,
        KeyIndex: keyIndex,
    }, nil
}

這個(gè)方法可以分成4塊,主要就是檢查參數(shù)是否正確,還是比較清楚的:

xpubs不能為空

xpubs不能有重復(fù)的。檢查的時(shí)候就先排序,再看相鄰的兩個(gè)是否相等。我覺(jué)得這一塊代碼應(yīng)該抽出來(lái),比如findDuplicated這樣的方法,直接放在這里太過(guò)于細(xì)節(jié)了。

檢查quorum,它是意思是“所需的簽名數(shù)量”,它必須小于等于xpubs的個(gè)數(shù),但不能為0。這個(gè)參數(shù)到底有什么用這個(gè)可能已經(jīng)觸及到比較核心的東西,放在以后研究。

把各信息打包在一起,稱之為Singer

另外,在第2處還是一個(gè)需要注意的sortKeys。它實(shí)際上對(duì)應(yīng)的是type sortKeys []chainkd.XPub,為什么要這么做,而不是直接把xpubs傳給sort.Sort呢?

這是因?yàn)椋?b>sort.Sort需要傳進(jìn)來(lái)的對(duì)象擁有以下接口:

type Interface interface {
    // Len is the number of elements in the collection.
    Len() int
    // Less reports whether the element with
    // index i should sort before the element with index j.
    Less(i, j int) bool
    // Swap swaps the elements with indexes i and j.
    Swap(i, j int)
}

但是xpubs是沒(méi)有的。所以我們把它的類型重新定義成sortKeys后,就可以添加上這些方法了:

blockchain/signers/signers.go#L94-L96

func (s sortKeys) Len() int           { return len(s) }
func (s sortKeys) Less(i, j int) bool { return bytes.Compare(s[i][:], s[j][:]) < 0 }
func (s sortKeys) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
m.getNextAccountIndex()

然后是signers.Create("account", xpubs, quorum, m.getNextAccountIndex())中的m.getNextAccountIndex(),它的代碼如下:

account/accounts.go#L119-L130

func (m *Manager) getNextAccountIndex() uint64 {
    m.accIndexMu.Lock()
    defer m.accIndexMu.Unlock()

    var nextIndex uint64 = 1
    if rawIndexBytes := m.db.Get(accountIndexKey); rawIndexBytes != nil {
        nextIndex = common.BytesToUnit64(rawIndexBytes) + 1
    }

    m.db.Set(accountIndexKey, common.Unit64ToBytes(nextIndex))
    return nextIndex
}

從這個(gè)方法可以看出,它用于產(chǎn)生自增的數(shù)字。這個(gè)數(shù)字保存在數(shù)據(jù)庫(kù)中,其key為accountIndexKey(常量,值為[]byte("AccountIndex")),value的值第一次為1,之后每次調(diào)用都會(huì)把它加1,返回的同時(shí)把它也保存在數(shù)據(jù)庫(kù)里。這樣比原程序就算重啟該數(shù)字也不會(huì)丟失。

signers.IDGenerate()

上代碼:

blockchain/signers/idgenerate.go#L21-L41

//IDGenerate generate signer unique id
func IDGenerate() string {
    var ourEpochMS uint64 = 1496635208000
    var n uint64

    nowMS := uint64(time.Now().UnixNano() / 1e6)
    seqIndex := uint64(nextSeqID())
    seqID := uint64(seqIndex % 1024)
    shardID := uint64(5)

    n = (nowMS - ourEpochMS) << 23
    n = n | (shardID << 10)
    n = n | seqID

    bin := make([]byte, 8)
    binary.BigEndian.PutUint64(bin, n)
    encodeString := base32.HexEncoding.WithPadding(base32.NoPadding).EncodeToString(bin)

    return encodeString

}

從代碼中可以看到,這個(gè)算法還是相當(dāng)復(fù)雜的,從注釋上來(lái)看,它是要生成一個(gè)“不重復(fù)”的id。如果我們細(xì)看代碼中的算法,發(fā)現(xiàn)它沒(méi)并有和我們的密鑰或者帳戶有關(guān)系,所以我不太明白,如果僅僅是需要一個(gè)不重復(fù)的id,為什么不能直接使用如uuid這樣的算法。另外這個(gè)算法是否有名字呢?已經(jīng)提了issue向開(kāi)發(fā)人員詢問(wèn):https://github.com/Bytom/bytom/issues/926

現(xiàn)在可以回到我們的主線a.wallet.AccountMgr.Create上了。關(guān)于創(chuàng)建帳戶的流程,上面已經(jīng)基本講了,但是還有一些地方我們還沒(méi)有分析:

上面多次提到使用了數(shù)據(jù)庫(kù),那么使用的是什么數(shù)據(jù)庫(kù)?在哪里進(jìn)行了初始化?

這個(gè)a.wallet.AccountMgr.Create方法中對(duì)應(yīng)的AccountMgr對(duì)象是在哪里構(gòu)造出來(lái)的?

數(shù)據(jù)庫(kù)與AccountMgr的初始化

比原在內(nèi)部使用了leveldb這個(gè)數(shù)據(jù)庫(kù),從配置文件config.toml中就可以看出來(lái):

$ cat config.toml
fast_sync = true
db_backend = "leveldb"

這是一個(gè)由Google開(kāi)發(fā)的性能非常高的Key-Value型的NoSql數(shù)據(jù)庫(kù),比特幣也用的是它。

比原在代碼中使用它保存各種數(shù)據(jù),比如區(qū)塊、帳戶等。

我們看一下,它是在哪里進(jìn)行了初始化。

可以看到,在創(chuàng)建比原節(jié)點(diǎn)對(duì)象的時(shí)候,有大量的與數(shù)據(jù)庫(kù)以及帳戶相關(guān)的初始化操作:

node/node.go#L59-L142

func NewNode(config *cfg.Config) *Node {
    // ...

    // Get store
    coreDB := dbm.NewDB("core", config.DBBackend, config.DBDir())
    store := leveldb.NewStore(coreDB)

    tokenDB := dbm.NewDB("accesstoken", config.DBBackend, config.DBDir())
    accessTokens := accesstoken.NewStore(tokenDB)

    // ...

    txFeedDB := dbm.NewDB("txfeeds", config.DBBackend, config.DBDir())
    txFeed = txfeed.NewTracker(txFeedDB, chain)

    // ...

    if !config.Wallet.Disable {
        // 1. 
        walletDB := dbm.NewDB("wallet", config.DBBackend, config.DBDir())
        // 2.
        accounts = account.NewManager(walletDB, chain)
        assets = asset.NewRegistry(walletDB, chain)
        // 3. 
        wallet, err = w.NewWallet(walletDB, accounts, assets, hsm, chain)
        // ...
    }
    // ...
}

那么我們?cè)诒疚闹杏玫降模褪沁@里的walletDB,在上面代碼中的數(shù)字1對(duì)應(yīng)的地方。

另外,AccountMgr的初始化在也這個(gè)方法中進(jìn)行了??梢钥吹?,在第2處,生成的accounts對(duì)象,就是我們前面提到的a.wallet.AccountMgr中的AccountMgr。這可以從第3處看到,accounts以參數(shù)形式傳給了NewWallet生成了wallet對(duì)象,它對(duì)應(yīng)的字段就是AccountMgr。

然后,當(dāng)Node對(duì)象啟動(dòng)時(shí),它會(huì)啟動(dòng)web api服務(wù):

node/node.go#L169-L180

func (n *Node) OnStart() error {
    // ...
    n.initAndstartApiServer()
    // ...
}

initAndstartApiServer方法里,又會(huì)創(chuàng)建API對(duì)應(yīng)的對(duì)象:

node/node.go#L161-L167

func (n *Node) initAndstartApiServer() {
    n.api = api.NewAPI(n.syncManager, n.wallet, n.txfeed, n.cpuMiner, n.miningPool, n.chain, n.config, n.accessTokens)
    // ...
}

可以看到,它把n.wallet對(duì)象傳給了NewAPI,所以/create-account對(duì)應(yīng)的handlera.createAccount中才可以使用a.wallet.AccountMgr.Create,因?yàn)檫@里的a指的就是api。

這樣的話,與創(chuàng)建帳戶的流程及相關(guān)的對(duì)象的初始化我們就都清楚了。

Annotated(acc)

下面就回到我們的API.createAccount中的第2塊代碼:

    // 2. 
    annotatedAccount := account.Annotated(acc)
    log.WithField("account ID", annotatedAccount.ID).Info("Created account")

我們來(lái)看一下account.Annotated(acc)

account/indexer.go#L27-L36

//Annotated init an annotated account object
func Annotated(a *Account) *query.AnnotatedAccount {
    return &query.AnnotatedAccount{
        ID:       a.ID,
        Alias:    a.Alias,
        Quorum:   a.Quorum,
        XPubs:    a.XPubs,
        KeyIndex: a.KeyIndex,
    }
}

這里出現(xiàn)的query指的是比原項(xiàng)目中的一個(gè)包blockchain/query,相應(yīng)的AnnotatedAccount的定義如下:

blockchain/query/annotated.go#L57-L63

type AnnotatedAccount struct {
    ID       string           `json:"id"`
    Alias    string           `json:"alias,omitempty"`
    XPubs    []chainkd.XPub   `json:"xpubs"`
    Quorum   int              `json:"quorum"`
    KeyIndex uint64           `json:"key_index"`
}

可以看到,它的字段與之前我們?cè)趧?chuàng)建帳戶過(guò)程中出現(xiàn)的字段都差不多,不同的是后面多了一些與json相關(guān)的注解。在后在前面的account.Annotated方法中,也是簡(jiǎn)單的把Account對(duì)象里的數(shù)字賦值給它。

為什么需要一個(gè)AnnotatedAccount呢?原因很簡(jiǎn)單,因?yàn)槲覀冃枰堰@些數(shù)據(jù)傳給前端。在API.createAccount的最后,第3步,會(huì)向前端返回NewSuccessResponse(annotatedAccount),由于這個(gè)值將會(huì)被jsonHandler轉(zhuǎn)換成JSON,所以它需要有一些跟json相關(guān)的注解才行。

同時(shí),我們也可以根據(jù)AnnotatedAccount的字段來(lái)了解,我們最后將會(huì)向前端返回什么樣的數(shù)據(jù)。

到這里,我們已經(jīng)差不多清楚了比原的/create-account是如何根據(jù)用戶提交的參數(shù)來(lái)創(chuàng)建帳戶的。

注:在閱讀代碼的過(guò)程中,對(duì)部分代碼進(jìn)行了重構(gòu),主要是從一些大方法分解出來(lái)了一些更具有描述性的小方法,以及一些變量名稱的修改,增加可讀性。#924

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/24181.html

相關(guān)文章

  • 剝開(kāi)原看代碼09:通過(guò)dashboard創(chuàng)建密鑰時(shí),前端數(shù)據(jù)是如何傳到后端?

    摘要:下一步,將進(jìn)入比原的節(jié)點(diǎn)也就是后端。它具體是怎么創(chuàng)建密鑰的,這在以后的文章中將詳細(xì)討論。當(dāng)我們清楚了在本文中,前后端數(shù)據(jù)是如何交互的,就很容易推廣到更多的情景。 作者:freewind 比原項(xiàng)目倉(cāng)庫(kù): Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com/BytomBlockc... 在前面一篇文章,我們粗略...

    MangoGoing 評(píng)論0 收藏0
  • 剝開(kāi)原看代碼08:比原Dashboard是怎么做出來(lái)?

    摘要:所以本文本來(lái)是想去研究一下,當(dāng)別的節(jié)點(diǎn)把區(qū)塊數(shù)據(jù)發(fā)給我們之后,我們應(yīng)該怎么處理,現(xiàn)在換成研究比原的是怎么做出來(lái)的。進(jìn)去后會(huì)看到大量的與相關(guān)的配置。它的功能主要是為了在訪問(wèn)與的函數(shù)之間增加了一層轉(zhuǎn)換。 作者:freewind 比原項(xiàng)目倉(cāng)庫(kù): Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com/BytomBlo...

    CHENGKANG 評(píng)論0 收藏0
  • 剝開(kāi)原看代碼12:比原如何通過(guò)/create-account-receiver創(chuàng)建地址

    摘要:繼續(xù)看生成地址的方法由于這個(gè)方法里傳過(guò)來(lái)的是而不是對(duì)象,所以還需要再用查一遍,然后,再調(diào)用這個(gè)私有方法創(chuàng)建地址該方法可以分成部分在第塊中主要關(guān)注的是返回值。 作者:freewind 比原項(xiàng)目倉(cāng)庫(kù): Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com/BytomBlockc... 在比原的dashboard中...

    oneasp 評(píng)論0 收藏0
  • 剝開(kāi)原看代碼10:比原如何通過(guò)/create-key接口創(chuàng)建密鑰

    摘要:如果傳的是,就會(huì)在內(nèi)部使用默認(rèn)的隨機(jī)數(shù)生成器生成隨機(jī)數(shù)并生成密鑰。使用的是,生成的是一個(gè)形如這樣的全球唯一的隨機(jī)數(shù)把密鑰以文件形式保存在硬盤(pán)上。 作者:freewind 比原項(xiàng)目倉(cāng)庫(kù): Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com/BytomBlockc... 在前一篇,我們探討了從瀏覽器的dashb...

    ccj659 評(píng)論0 收藏0
  • 剝開(kāi)原看代碼16:比原如何通過(guò)/list-transactions顯示交易信息?

    摘要:前端是如何獲取交易數(shù)據(jù)并顯示出來(lái)的我們先在比原的前端代碼庫(kù)中尋找。這過(guò)程中的推導(dǎo)就不再詳說(shuō),需要的話可以看前面講解比原是如何顯示余額的那篇文章。的定義是其中的值是。 作者:freewind 比原項(xiàng)目倉(cāng)庫(kù): Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com/BytomBlockc... 在前一篇文章中,我們...

    hankkin 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<