Kiel mi desegnis blokojn kaj transakciojn en mia Go-blokĉeno

Por finfine fini kun blokoĉeno kaj ne nur datumbazo, ni devas aldoni 3 gravajn elementojn al nia projekto:

  • Priskribo de la blokdatumstrukturo kaj metodoj
  • Priskribo de datumstrukturo kaj transakciaj metodoj
  • Blockchain-funkcioj, kiuj konservas blokojn en datumbazo kaj trovas ilin tie laŭ ilia hash aŭ alteco (aŭ io alia).

Kiel mi desegnis blokojn kaj transakciojn en mia Go-blokĉeno

Ĉi tiu estas la dua artikolo pri blokĉeno por industrio, la unua tie.

Memorante la demandojn, kiujn legantoj demandis al mi pri la antaŭa artikolo en ĉi tiu serio, oni devas rimarki: en ĉi tiu kazo, la datumbazo LevelDB estas uzata por konservi blokĉenajn datumojn, sed nenio malhelpas vin uzi iun alian, ekzemple, MySQL. Nun ni rigardu la strukturon de ĉi tiuj datumoj.

Ni komencu per transakcioj: github.com/Rusldv/bcstartup/blob/master/transaction/builder.go

Jen ĝia datumstrukturo:

type TX struct {
	DataType byte		
	TxHash string 
	TxType byte	
	Timestamp int64		
	INs []TxIn
	OUTs []TxOut
}

type TxIn struct {
	ThatTxHash string
	TxOutN int
	ByteCode string
}

type TxOut struct {
	Value int
	ByteCode string
}

TX konservas la datumtipon (por transakcio 2), la haŝon de tiu transakcio, la specon de la transakcio mem, tempomarkon, kaj enigaĵojn kaj elirojn. TxIn-enigaĵoj stokas la haŝon de la transakcio, kies eligo estas referencita, la nombro de ĉi tiu eligo kaj bajtokodo, kaj TxOut-produktaĵoj stokas iom da valoro kaj ankaŭ bajtokodo.

Nun ni vidu, kiajn agojn transakcio povas plenumi sur siaj datumoj, t.e. Ni rigardu la metodojn.

Por krei transakcion, uzu la transakcion.NewTransaction(txtype byte) *TX-funkcio.

La metodo AddTxIn(thattxhash []byte, txoutn int, code []byte) (*TxIn, eraro) aldonas enigaĵon al la transakcio.

La metodo AddTxOut(valoro int, datumoj []bajto) (*TxOut, eraro) aldonas eligon al la transakcio.

La ToBytes() []bajta metodo igas la transakcion en bajtan tranĉaĵon.

La interna funkcio preByteHash(bajtoj []bajto) ĉeno estas uzata en Build() kaj Check() por igi la generitan transakcian haŝon kongrua kun transakciaj haŝoj generitaj de JavaScript-aplikoj.

La metodo Build() fiksas la transakcian haŝon jene: tx.TxHash = preByteHash(tx.ToBytes()).

La ĉenmetodo ToJSON() konvertas transakcion en JSON-ĉenon.

La erarmetodo FromJSON(datumoj []bajto) ŝarĝas transakcion de la formato JSON pasigita kiel bajta tranĉaĵo.

La bool-metodo Check() komparas la rezultan hash el la transakcia hashkampo kun la hash akirita kiel rezulto de hashado de tiu transakcio (ignorante la hashkampon).

Transakcioj estas aldonitaj al la bloko: github.com/Rusldv/bcstartup/blob/master/block/builder.go

La blokdatumstrukturo estas pli volumena:

type Block struct {
	DataType byte				
	BlockHeight int					
        Timestamp int64				 
        HeaderSize int					
        PrevBlockHash string				 
        SelfBlockHash string			
	TxsHash string			
	MerkleRoot string
	CreatorPublicKey string			
	CreatorSig string
	Version int
	TxsN int
	Txs []transaction.TX
}

DataType konservas la datumtipo, la nodo uzas ĝin kaj distingas la blokon de transakcio aŭ aliaj datumoj. Por bloko ĉi tiu valoro estas 1.

BlockHeight konservas la altecon de la bloko.
Tempostampo tempostampo.
HeaderSize estas la blokgrandeco en bajtoj.
PrevBlockHash estas la hash de la antaŭa bloko, kaj SelfBlockHash estas la hash de la nuna.
TxsHash estas ĝenerala haŝo de transakcioj.
MerkleRoot estas la radiko de la Merkle-arbo.

Plie en la kampoj estas la publika ŝlosilo de la kreinto de la bloko, la subskribo de la kreinto, la versio de la bloko, la nombro da transakcioj en la bloko, kaj ĉi tiuj transakcioj mem.

Ni rigardu ĝiajn metodojn:
Por krei blokon, uzu la funkcion block.NewBlock(): NewBlock(prevBlockHash string, alteco int) *Bloko, kiu prenas la haŝon de la antaŭa bloko kaj la altecon fiksitan por la kreita bloko en la blokĉeno. La bloktipo ankaŭ estas agordita de la konstanto de pakaĵo de tipoj:

b.DataType = types.BLOCK_TYPE.

La metodo AddTx(tx *transaction.TX) aldonas transakcion al bloko.

La metodo Build() ŝarĝas valorojn en la kampojn de la bloko kaj generas kaj agordas ĝian nunan haŝon.

La ToBytesHeader() []bajta metodo konvertas la blokan kaplinion (sen transakcioj) en bajtan tranĉaĵon.

La ĉenmetodo ToJSON() konvertas la blokon al JSON-formato en ĉenprezento de la datumoj.

La erara metodo FromJSON(data []bajto) ŝargas datumojn de JSON en blokstrukturon.

La Check() bool-metodo generas blokan hash kaj komparas ĝin kun tiu specifita en la blokhash kampo.

La metodo de ĉeno GetTxsHash() redonas la totalan haŝon de ĉiuj transakcioj en la bloko.

La metodo GetMerkleRoot() specifas la radikon de la Merkle-arbo por transakcioj en bloko.

La metodo Sign(privk string) subskribas blokon per la privata ŝlosilo de la blokkreinto.

La metodo SetHeight(height int) skribas la altecon de la bloko al la blokstruktura kampo.

La GetHeight() int-metodo resendas la altecon de la bloko kiel specifita en la responda kampo de la blokstrukturo.

La ToGOBBytes() []bajta metodo kodas blokon en GOB-formato kaj resendas ĝin kiel bajta tranĉaĵo.

La erarmetodo FromGOBBytes(data []bajto) skribas blokdatenojn al la blokstrukturo de la pasita bajta tranĉaĵo en GOB-formato.

La metodo de ĉeno GetHash() resendas la haŝon de la donita bloko.

La metodo de ĉeno GetPrevHash() resendas la haŝon de la antaŭa bloko.

La metodo SetPublicKey(pubk string) skribas la publikan ŝlosilon de la blokkreinto al la bloko.

Tiel, uzante la metodojn de la objekto Block, ni povas facile konverti ĝin en formaton por transsendo per la reto kaj ŝpari al la datumbazo LevelDB.

La funkcioj de la blokĉena pako respondecas pri ŝparado al la blokĉeno: github.com/Rusldv/bcstartup/tree/master/blockchain

Por fari tion, la bloko devas efektivigi la IBlock-interfacon:

type IGOBBytes interface {
	ToGOBBytes() []byte
	FromGOBBytes(data []byte) error
}

type IBlock interface {
	IGOBBytes
	GetHash() string
	GetPrevHash() string
	GetHeight() int
	Check() bool

}

La datumbaza konekto estas kreita unufoje kiam la pakaĵo estas pravalorigita en la funkcio init():

db, err = leveldb.OpenFile(BLOCKCHAIN_DB_DEBUG, nil).

CloseDB() estas envolvaĵo por db.Cloce() - vokita post laborado kun la pakaĵfunkcioj por fermi la konekton al la datumbazo.

La erarfunkcio SetTargetBlockHash(hash string) skribas la hash de la nuna bloko kun la ŝlosilo specifita de la BLOCK_HASH-konstanto al la datumbazo.

La funkcio GetTargetBlockHash() (ŝnuro, eraro) redonas la haŝon de la nuna bloko konservita en la datumbazo.

La erara funkcio SetTargetBlockHeight(height int) skribas al la datumbazo la valoron de la blokĉena alteco por la nodo kun la ŝlosilo specifita de la konstanto BLOCK_HEIGHT.

La funkcio GetTargetBlockHeight() (int, eraro) redonas la altecon de la blokĉeno por donita nodo, stokita en la datumbazo.

La bool-funkcio CheckBlock (bloko IBlock) kontrolas blokon por ĝusteco antaŭ ol aldoni ĉi tiun blokon al la blokĉeno.

La erara funkcio AddBlock (bloko IBlock) aldonas blokon al la blokĉeno.

La funkcioj por retrovi kaj vidi blokojn estas en la explore.go-dosiero de la blokĉena pako:

La funkcio GetBlockByHash(hash string) (*block.Block, eraro) kreas malplenan blokobjekton, ŝarĝas blokon en ĝin de la datumbazo, kies hash estis transdonita al ĝi, kaj resendas montrilon al ĝi.

La kreado de genez-bloko estas farita per la erarfunkcio Genesis() el la genesis.go-dosiero de la blokĉena pakaĵo.

La sekva artikolo parolos pri konekti klientojn al nodo uzante la mekanismon WebSocket.

fonto: www.habr.com

Aldoni komenton