vtk刪除一個actor_如何構建一個基于actor的簡單區塊鏈

vtk刪除一個actor

Scalachain is a blockchain built using the Scala programming language and the actor model (Akka Framework).

Scalachain是使用Scala編程語言和參與者模型( Akka Framework )構建的區塊鏈。

In this story I will show the development process to build this simple prototype of a blockchain. This means that the project is not perfect, and there may be better implementations. For all these reasons any contribution — may it be a suggestion, or a PR on the GitHub repository — is very welcome! :-)

在這個故事中,我將展示構建此簡單區塊鏈原型的開發過程。 這意味著該項目并不完美,并且可能會有更好的實現。 由于所有這些原因,我們非常歡迎您提供任何意見(包括建議或GitHub 存儲庫上的PR)! :-)

Let’s start with a little introduction to the blockchain. After that we can define the simplified model that we will implement.

讓我們從對區塊鏈的一些介紹開始。 之后,我們可以定義將要實現的簡化模型。

區塊鏈快速入門 (Quick Introduction to the blockchain)

There a lot of good articles that explain how a blockchain works, so I will do a high level introduction just to provide some context to this project.

有很多很好的文章解釋了區塊鏈的工作原理,因此我將做一個高層次的介紹,只是為該項目提供一些背景信息。

The blockchain is a distributed ledger: it registers some transaction of values (like coins) between a sender and a receiver. What makes a blockchain different from a traditional database is the decentralized nature of the blockchain: it is distributed among several communicating nodes that guarantee the validity of the transactions registered.

區塊鏈是一種分布式賬本 :它在發送方和接收方之間注冊一些價值交易(如硬幣)。 區塊鏈與傳統數據庫的不同之處在于區塊鏈的分散性:它分布在多個通信節點之間,以保證注冊交易的有效性。

The blockchain stores transactions in blocks, that are created —we say mined — by nodes investing computational power. Every block is created by solving a cryptographic puzzle that is hard to solve, but easy to verify. In this way, every block represents the work needed to solve such puzzle. This is the reason why the cryptographic puzzle is called the Proof of Work: the solution of the puzzle is the proof that a node spent a certain amount of work to solve it and mine the block.

區塊鏈將交易存儲在區塊中,區塊是由投資計算能力的節點創建的(我們說是開采的) 。 每個區塊都是通過解決難以解決但易于驗證的密碼難題創建的。 這樣,每個方塊代表解決此類難題所需的工作。 這就是密碼難題被稱為工作量證明的原因:難題的解決方案是節點花費大量工作來解決它和挖掘區塊的證明。

Why do nodes invest computational power to mine a block? Because the creation of a new block is rewarded by a predefined amount of coins. In this way nodes are encouraged to mine new blocks, contributing in the growth and strength of the blockchain.

為什么節點要投入計算能力來挖掘一塊? 因為新塊的創建將獲得預定義數量的硬幣獎勵。 通過這種方式,鼓勵節點挖掘新區塊,為區塊鏈的增長和實力做出貢獻。

The solution of the Proof Of Work depends on the values stored in the last mined block. In this way every block is chained to the previous one. This means that, to change a mined block, a node should mine again all the blocks above the modified one. Since every block represents an amount of work, this operation would be unfeasible once several blocks are mined upon the modified one. This is the foundation of the distributed consensus, The agreement of all the nodes on the validity of the blocks (that is the transactions) stored in the blockchain.

工作量證明的解決方案取決于存儲在最后一個開采區塊中的值。 這樣,每個塊都鏈接到前一個塊。 這意味著,要更改已開采的區塊,節點應再次開采已修改區塊上方的所有區塊。 由于每個塊代表大量的工作,因此一旦在修改后的塊中挖掘了幾個塊,此操作將是不可行的。 這是分布式 共識的基礎,所有節點對區塊鏈中存儲的區塊(即交易)有效性的共識

It may happen that different nodes mine a block at the same time, creating different “branches” from the same blockchain — this is called a fork in the blockchain. This situation is solved when a branch becomes longer than the others: the longest chain always wins, so the winning branch becomes the new blockchain.

可能會發生不同的節點同時挖掘一個區塊,從而從同一區塊鏈創建不同的“分支”的情況-這在區塊鏈中稱為分叉 。 當分支變得比其他分支更長時,這種情況就解決了:最長的鏈總是獲勝,因此獲勝的分支成為新的區塊鏈。

區塊鏈模型 (The blockchain model)

Scalachain is based on a blockchain model that is a simplification of the Bitcoin one.

Scalachain基于一種區塊鏈模型,該模型簡化了比特幣。

The main components of our blockchain model are the Transaction, the Chain, the Proof of Work (PoW) algorithm, and the Node. The transactions are stored inside the blocks of the chain, that are mined using the PoW. The node is the server that runs the blockchain.

我們的區塊鏈模型的主要組件是交易,鏈,工作量證明(PoW)算法和節點。 事務存儲在鏈的各個塊中,這些塊使用PoW進行挖掘。 節點是運行區塊鏈的服務器。

Transaction

交易

Transactions register the movement of coins between two entities. Every transaction is composed by a sender, a recipient, and an amount of coin. Transactions will be registered inside the blocks of our blockchain.

交易記錄了兩個實體之間硬幣的移動。 每筆交易都由發送者,接收者和一定數量的硬幣組成。 交易將被注冊在我們的區塊鏈中。

Chain

The chain is a linked list of blocks containing a list of transactions. Every block of the chain has an index, the proof that validates it (more on this later), the list of transactions, the hash of the previous block, the list of previous blocks, and a timestamp. Every block is chained to the previous one by its hash, that is computed converting the block to a JSON string and then hashing it through a SHA-256 hashing function.

鏈是包含事務列表的塊的鏈接列表。 鏈中的每個區塊都有一個索引,對其進行驗證的證明(稍后會詳細介紹),事務列表,上一個區塊的哈希,上一個區塊的列表以及時間戳。 每個塊都通過其哈希值鏈接到前一個塊,該哈希值是將塊轉換為JSON字符串,然后通過SHA-256哈希函數對其進行哈希計算。

PoW

工作量

The PoW algorithm is required to mine the blocks composing the blockchain. The idea is to solve a cryptographic puzzle that is hard to solve, but easy to verify having the proof. The PoW algorithm that is implemented in Scalachain is similar to the Bitcoin one (based on Hashcash). It consists in finding a hash with N leading zeros, that is computed starting from the hash of the last block and a number, that is the proof of our algorithm.

需要PoW算法來挖掘組成區塊鏈的區塊。 這個想法是要解決一個密碼難題,這個難題很難解決,但是容易驗證有證據。 Scalachain中實現的PoW算法類似于比特幣一種(基于Hashcash )。 它包括找到一個具有N個前導零的哈希,該哈希從最后一個塊的哈希和一個數字開始計算,這是我們算法的證明。

We can formalize it as:

我們可以將其形式化為:

NzerosHash = SHA-256(previousNodeHash + proof)

The higher is N, the harder is to find the proof. In Scalachain N=4 (It will be configurable eventually).

N越高,找到證明就越難。 在Scalachain中,N = 4(最終將可配置)。

Node

節點

The Node is the server running our blockchain. It provides some REST API to interact with it and perform basic operations such as send a new transaction, get the list of pending transactions, mine a block, and get the current status of the blockchain.

節點是運行我們的區塊鏈的服務器。 它提供了一些REST API與之交互并執行基本操作,例如發送新交易,獲取待處理交易列表,挖掘區塊并獲取區塊鏈的當前狀態。

Scala中的區塊鏈實施 (Blockchain implementation in Scala)

We are going to implement the defined model using the Scala Programming Language. From an high level view, the things we need to implement a blockchain are:

我們將使用Scala編程語言實現定義的模型。 從高級的角度來看,我們實現區塊鏈所需要做的事情是:

  • transactions

    交易
  • the chain of blocks containing lists of transactions

    包含交易清單的區塊鏈
  • the PoW algorithm to mine new blocks

    PoW算法來挖掘新塊

These components are the essential parts of a blockchain.

這些組件是區塊鏈的基本組成部分。

Transaction

交易

The transaction is a very simple object: it has a sender, a recipient and a value. We can implement it as a simple case class.

事務是一個非常簡單的對象:它有一個發送者,一個接收者和一個值。 我們可以將其實現為簡單的case class

case class Transaction(sender: String, recipient: String, value: Long)

Chain

The chain is the core of our blockchain: it is a linked list of blocks containing transactions.

鏈是我們區塊鏈的核心:它是包含交易的區塊的鏈表。

sealed trait Chain {val index: Intval hash: Stringval values: List[Transaction]val proof: Longval timestamp: Long
}

We start by creating a sealed trait that represents the block of our chain. The Chain can have two types: it can be an EmptyChain or a ChainLink. The former is our block zero (the genesis block), and it is implemented as a singleton (it is a case object), while the latter is a regular mined block.

我們從創建一個sealed trait開始,該sealed trait代表了我們的區塊鏈。 Chain可以有兩種類型:它可以是EmptyChainChainLink 。 前者是我們的零區塊( 創世區塊 ),它被實現為一個單例(它是一個case object ),而后者是一個常規的開采區塊。

case class ChainLink(index: Int, proof: Long, values: List[Transaction], previousHash: String = "", tail: Chain = EmptyChain, timestamp: Long = System.currentTimeMillis()) extends Chain {val hash = Crypto.sha256Hash(this.toJson.toString)
}case object EmptyChain extends Chain {val index = 0val hash = "1"val values = Nilval proof = 100Lval timestamp = System.currentTimeMillis()
}

Let’s look more in detail at our chain. It provides an index, that is the current height of the blockchain. There is the list of Transaction, the proof that validated the block, and the timestamp of the block creation. The hash value is set to a default one in the EmptyChain, while in the ChainLink it is computed converting the object to its JSON representation and hashing it with an utility function (see the crypto package in the repository). The ChainLink provides also the hash of the previous block in the chain (our link between blocks). The tail field is a reference to the previously mined blocks. This may not be the most efficient solution, but it is useful to see how the blockchain grows in our simplified implementation.

讓我們詳細了解一下我們的鏈。 它提供了一個索引,即區塊鏈的當前高度。 這里有Transaction清單,驗證區塊的證明以及區塊創建的時間戳。 哈希值在EmptyChain設置為默認值,而在ChainLink ,將其計算為將對象轉換為其JSON表示并使用實用程序函數對其進行哈希處理(請參見存儲庫中的crypto包)。 ChainLink還提供鏈中上一個塊(我們的塊之間的鏈接)的哈希。 尾部字段是對先前開采的區塊的引用。 這可能不是最有效的解決方案,但是了解簡化的實現中區塊鏈的增長方式很有用。

We can improve our Chain with some utilities. We can add it a companion object that defines an apply method to create a new chain passing it a list of blocks. A companion object is like a “set of static methods” — doing an analogy with Java — that has complete access rights on the fields and methods of the class/trait.

我們可以使用一些實用程序來改進我們的Chain 。 我們可以為它添加一個伴隨對象 ,該對象定義了apply方法,以創建一個新鏈,將鏈列表傳遞給它。 伴隨對象就像一個“靜態方法集”(類似于Java),它具有對類/特征的字段和方法的完全訪問權限。

object Chain {def apply[T](b: Chain*): Chain = {if (b.isEmpty) EmptyChainelse {val link = b.head.asInstanceOf[ChainLink]ChainLink(link.index, link.proof, link.values, link.previousHash, apply(b.tail: _*))}}
}

If the list of blocks is empty, we simply initialize our blockchain with an EmptyChain. Otherwise we create a new ChainLink adding as a tail the result of the apply method on the remaining blocks of the list. In this way the list of blocks is added following the order of the list.

如果塊列表為空,則只需使用EmptyChain初始化我們的EmptyChain鏈。 否則,我們將創建一個新的ChainLink作為尾部,在列表的其余塊上添加apply方法的結果。 這樣,將按照列表的順序添加塊列表。

It would be nice to have the possibility to add a new block to our chain using a simple addition operator, like the one we have on List. We can define our own addition operator :: inside the Chain trait.

能夠使用一個簡單的加法運算符(如List上的加法運算符)將新塊添加到我們的鏈中,將是很好的。 我們可以在Chain特征中定義自己的加法運算符::

sealed trait Chain {val index: Intval hash: Stringval values: List[Transaction]val proof: Longval timestamp: Longdef ::(link: Chain): Chain = link match {case l:ChainLink => ChainLink(l.index, l.proof, l.values, this.hash, this)case _ => throw new InvalidParameterException("Cannot add invalid link to chain")}
}

We pattern match on the block that is passed as an argument: if it is a valid ChainLink object we add it as the head of our chain, putting the chain as the tail of the new block, otherwise we throw an exception.

我們對作為參數傳遞的塊進行模式匹配:如果它是有效的ChainLink對象,則將其添加為鏈的頭部,將鏈作為新塊的尾部,否則拋出異常。

PoW

工作量

The PoW algorithm is fundamental for the mining of new blocks. We implement it as a simple algorithm:

PoW算法是挖掘新塊的基礎。 我們將其實現為一個簡單的算法:

  1. Take the hash of the last block and a number representing the proof.

    取最后一塊的哈希值和代表證明的數字。

2. Concatenate the hash and the proof in a string.

2.將哈希和證明連接在字符串中。

3. hash the resulting string using the SHA-256 algorithm.

3.使用SHA-256算法對所得字符串進行哈希處理。

4. check the 4 leading characters of the hash: if they are four zeros return the proof.

4.檢查哈希的4個前導字符:如果它們是四個零,則返回證明。

5. otherwise repeat the algorithm increasing the proof by one.

5.否則重復算法,將證明加一。

This a simplification of the HashCash algorithm used in the Bitcoin blockchain.

這是比特幣區塊鏈中使用的HashCash算法的簡化。

Since it is a recursive function, we can implement it as a tail recursive one to improve the usage of resources.

由于它是一種遞歸函數,因此我們可以將其實現為尾遞歸函數,以提高資源利用率。

object ProofOfWork {def proofOfWork(lastHash: String): Long = {@tailrecdef powHelper(lastHash: String, proof: Long): Long = {if (validProof(lastHash, proof))proofelsepowHelper(lastHash, proof + 1)}val proof = 0powHelper(lastHash, proof)}def validProof(lastHash: String, proof: Long): Boolean = {val guess = (lastHash ++ proof.toString).toJson.toStringval guessHash = Crypto.sha256Hash(guess)(guessHash take 4) == "0000"}
}

The validProof function is used to check if the proof we are testing is the correct one. The powHelper function is a helper function that executes our loop using tail recursion, increasing the proof at each step. The proofOfWork function wrap all the things up, and is exposed by the ProofOfWork object.

validProof函數用于檢查我們正在測試的證明是否正確。 powHelper函數是一個輔助函數,它使用尾部遞歸執行我們的循環,從而增加了每一步的證明。 proofOfWork函數將所有內容包裝起來,并由ProofOfWork對象公開。

演員模型 (The actor model)

The actor model is a programming model designed for concurrent processing, scalability, and fault tolerance. The model defines the atomic elements that compose the software systems — the actors — and the way this elements interact between them. In this project we will use the actor model implemented in Scala by the Akka Framework.

actor模型是一種為并行處理可伸縮性容錯能力設計的編程模型。 該模型定義了構成軟件系統的原子元素- 參與者 -以及這些元素之間的交互方式。 在這個項目中,我們將使用由Akka Framework在Scala中實現的actor模型。

Actor

演員

The actor is the atomic unit of the actor model. it is a computational unit that can send and receive messages. Every actor has an internal private state and a mailbox. When an actor receives and compute a message, it can react in 3 ways:

角色是角色模型的原子單位。 它是可以發送和接收消息的計算單元。 每個參與者都有一個內部私有狀態和一個郵箱。 當一個參與者接收并計算一條消息時,它可以通過三種方式做出React:

  • Send a message to another actor.

    向其他演員發送消息。
  • Change its internal state.

    更改其內部狀態。
  • Create another actor.

    創建另一個演員。

Communication is asynchronous, and messages are popped out from the mailbox and processed in series. To enable the parallel computation of messages you need to create several actors. Many actors together crate an actor system. The behavior of the application arises from the interaction between actors providing different functionalities.

通信是異步的 ,并且消息從郵箱彈出并按順序處理。 要啟用消息的并行計算,您需要創建多個參與者。 許多演員共同創建一個演員系統 。 應用程序的行為源自提供不同功能的參與者之間的交互。

Actors are independent

演員是獨立的

Actors are independent one to another, and they do not share their internal state. This fact has a couple of important consequences:

演員彼此獨立,他們沒有內部狀態。 這個事實有兩個重要的后果:

1. Actors can process messages without side-effects one to another.

1.演員可以處理沒有副作用的消息。

2. It’s not important where an actor is — be it your laptop, a sever, or in the cloud — once we know its address we can request its services sending it a message.

2.演員所在的位置(無論是您的筆記本電腦,服務器還是云)都不重要,一旦我們知道了演員的地址,便可以請求其發送消息的服務。

The first point makes concurrent computation very easy. We can be sure that the processing of a message will not interfere with the processing of another one. To achieve concurrent processing we can deploy several actors able to process the same kind of message.

第一點使并發計算非常容易。 我們可以確保處理一條消息不會干擾另一條消息的處理。 為了實現并發處理,我們可以部署幾個能夠處理相同類型消息的參與者。

The second point is all about scalability: we need more computational power? No problem: we can start a new machine and deploy new actors that will join the existing actor system. Their mailbox addresses will be discoverable by existing actors, that will start communicate with them.

第二點是關于可伸縮性的 :我們需要更多的計算能力嗎? 沒問題:我們可以啟動一臺新機器并部署新角色,這些角色將加入現有角色系統。 現有參與者可以發現他們的郵箱地址,并開始與他們進行通信。

Actors are supervised

演員受到監督

As we said in the description of the actor, one of the possible reaction to a message is the creation of other actors. When this happens, the father becomes the supervisor of its children. If a children fails, the supervisor can decide the action to take, may it be create a new actor, ignore the failure, or throw it up to its own supervisor. In this way the Actor System becomes a hierarchy tree, each node supervising its children. This is the way the actor model provides fault tolerance.

正如我們在演員描述中所說的那樣,對消息的可能React之一就是創建其他演員。 發生這種情況時,父親變成了孩子們的導師 。 如果孩子失敗了,監督者可以決定要采取的行動,可以是創建新的演員,忽略失敗,還是將其交給自己的監督者。 這樣,Actor系統就變成了一個層次樹,每個節點都在監督其子節點。 這就是參與者模型提供容錯能力的方式

經紀人,一個簡單的演員 (Broker, a simple actor)

The first actor we are going to implement is the Broker Actor: it is the manager of the transactions of our blockchain. Its responsibilities are the addition of new transactions, and the retrieval of pending ones.

我們要實施的第一個參與者是經紀人參與者:它是我們區塊鏈交易的管理者。 它的職責是添加新交易以及檢索未決交易。

The Broker Actor reacts to three kind of messages, defined in the companion object of the Broker class:

Broker Actor對在Broker類的companion object中定義的三種消息作出React:

object Broker {sealed trait BrokerMessagecase class AddTransaction(transaction: Transaction) extends BrokerMessagecase object GetTransactions extends BrokerMessagecase object Clear extends BrokerMessageval props: Props = Props(new Broker)
}

We create a trait BrokerMessage to identify the messages of the Broker Actor. Every other message will extend this trait. AddTransaction adds a new transaction to the list of pending ones. GetTransaction retrieve the pending transactions, and Clear empties the list. The props value is used to initialize the actor when it will be created.

我們創建一個特征BrokerMessage來標識Broker Actor的消息。 其他所有消息都將擴展此特性。 AddTransaction將新事務添加到掛起的事務列表中。 GetTransaction檢索掛起的事務,而Clear清空列表。 props值用于在創建actor時對其進行初始化。

class Broker extends Actor with ActorLogging {import Broker._var pending: List[Transaction] = List()override def receive: Receive = {case AddTransaction(transaction) => {pending = transaction :: pendinglog.info(s"Added $transaction to pending Transaction")}case GetTransactions => {log.info(s"Getting pending transactions")sender() ! pending}case Clear => {pending = List()log.info("Clear pending transaction List")}}
}

The Broker class contains the business logic to react to the different messages. I won’t go into the details because it is trivial. The most interesting thing is how we respond to a request of the pending transactions. We send them to the sender() of the GetTransaction message using the tell (!) operator. This operator means “send the message and don’t wait for a response” — aka fire-and-forget.

Broker class包含對不同消息做出React的業務邏輯。 我將不贅述,因為它是微不足道的。 最有趣的是我們如何響應未決交易的請求。 我們使用tell ( ! )運算符將它們sender()GetTransaction消息的sender() 。 此運算符的意思是“發送消息,不要等待響應”,又名即發即棄。

礦工,不同州的演員 (Miner, an actor with different states)

The Miner Actor is the one mining new blocks for our blockchain. Since we don’t want mine a new block while we are mining another one, the Miner Actor will have two states: ready, when it is ready to mine a new block, and busy, when it is mining a block.

礦工演員是為我們的區塊鏈挖掘新區塊的人之一。 由于我們不希望在挖掘另一個區塊時挖掘一個新區塊,因此礦工Actor將具有兩種狀態: ready ,準備挖掘一個新區塊的狀態和busy ,挖掘一個區塊的狀態。

Let’s start by defining the companion object with the messages of the Miner Actor. The pattern is the same, with a sealed trait — MinerMessage — used to define the kind of messages this actor reacts to.

讓我們開始定義帶有礦工演員消息的companion object 。 模式是相同的,具有密封的特征MinerMessage ,用于定義該MinerMessage的消息的類型。

object Miner {sealed trait MinerMessagecase class Validate(hash: String, proof: Long) extends MinerMessagecase class Mine(hash: String) extends MinerMessagecase object Ready extends MinerMessageval props: Props = Props(new Miner)
}

The Validate message asks for a validation of a proof, and pass to the Miner the hash and the proof to check. Since this component is the one interacting with the PoW algorithm, it is its duty to execute this check. The Mine message asks for the mining starting from a specified hash. The last message, Ready, triggers a state transition.

Validate消息要求驗證證明,并將哈希值和證明傳遞給礦工進行檢查。 由于該組件是與PoW算法交互的組件,因此執行此檢查是其職責。 Mine消息要求從指定的哈希開始進行挖掘。 最后一條消息Ready觸發狀態轉換。

Same actor, different states

同一演員,不同州

The peculiarity of this actor is that it reacts to the messages according to its state: busy or ready. Let’s analyze the difference in the behavior:

這個actor的獨特之處在于,它根據消息的狀態( busyready對消息做出React。 讓我們分析一下行為上的區別:

  • busy: the Miner is busy mining a block. If a new mining request comes, it should deny it. If it is requested to be ready, the Miner should change its state to the ready one.

    繁忙 :礦工正在忙于開采一個街區。 如果有新的采礦請求,則應拒絕。 如果要求準備就緒,則礦工應將其狀態更改為就緒狀態。

  • ready: the Miner is idle. If a mining request come, it should start mining a new block. If it is requested to be ready, it should say: “OK, I’m ready!”

    準備好 :礦工閑置。 如果出現挖掘請求,則應開始挖掘新塊。 如果要求準備就緒,則應該說:“好,我準備好了!”

  • both: the Miner should be always available to verify the correctness of a proof, both in a ready or busy state.

    兩者 :礦工在準備就緒或繁忙狀態下應始終可用以驗證證明的正確性。

Time so see how we can implement this logic in our code. We start by defining the common behavior, the validation of a proof.

時間到了,看看如何在代碼中實現此邏輯。 我們首先定義常見行為,即證明的有效性。

We define a function validate that reacts to the Validate message: if the proof is valid we respond to the sender with a success, otherwise with a failure. The ready and the busy states are defined as functions that “extends” the validate one, since that is a behavior we want in both states.

我們定義了一個功能validate ,它對Validate消息做出React:如果證明有效,我們以成功的方式響應發送方,否則以失敗的方式響應。 ready狀態和busy狀態被定義為“擴展” validate狀態的功能,因為這是我們在兩種狀態下都想要的行為。

def validate: Receive = {case Validate(hash, proof) => {log.info(s"Validating proof $proof")if (ProofOfWork.validProof(hash, proof)){log.info("proof is valid!")sender() ! Success}else{log.info("proof is not valid")sender() ! Failure(new InvalidProofException(hash, proof))}}}

A couple of things to highlight here.

這里有兩點要強調。

1. The state transition is triggered using the become function, provided by the Akka Framework. This takes as an argument a function that returns a Receive object, like the ones we defined for the validation, busy, and ready state.

1.使用Akka Framework提供的become功能觸發狀態轉換。 該函數將返回Receive對象的函數作為參數,就像我們為validationbusyready狀態定義的函數一樣。

2. When a mining request is received by the Miner, it responds with a Future containing the execution of the PoW algorithm. In this way we can work asynchronously, making the Miner free to do other tasks, such as the validation one.

2.當礦工收到挖掘請求時,它將以包含有執行PoW算法的Future響應。 這樣,我們可以異步工作,使礦工可以自由地執行其他任務,例如驗證任務。

3. The supervisor of this Actor controls the state transition. The reason of this choice is that the Miner is agnostic about the state of the system. It doesn’t know when the mining computation in the Future will be completed, and it can’t know if the block that it is mining has been already mined from another node. This would require to stop mining the current hash, and start mining the hash of the new block.

3.該Actor的主管控制狀態轉換。 這種選擇的原因是,礦工對系統狀態不了解。 它不知道Future的挖掘計算何時完成,也不知道它正在挖掘的塊是否已經從另一個節點中挖掘出來。 這將需要停止挖掘當前的哈希,并開始挖掘新塊的哈希。

The last thing is to provide an initial state overriding the receive function.

最后一件事是提供一個覆蓋receive功能的初始狀態。

override def receive: Receive = {case Ready => become(ready)}

We start waiting for a Ready message. When it comes, we start our Miner.

我們開始等待Ready消息。 當它來的時候,我們開始我們的礦工。

區塊鏈,一個持久的參與者 (Blockchain, a persistent actor)

The Blockchain Actor interacts with the business logic of the blockchain. It can add a new block to the blockchain, and it can retrieve information about the state of the blockchain. This actor has another superpower: it can persist and recover the state of the blockchain. This is possible implementing the PersistentActor trait provided by the Akka Framework.

區塊鏈參與者與區塊鏈的業務邏輯進行交互。 它可以向區塊鏈添加一個新塊,并且可以檢索有關區塊鏈狀態的信息。 這個參與者還有另一個超級大國:它可以持久并恢復區塊鏈的狀態。 可以實現Akka框架提供的PersistentActor特性。

object Blockchain {sealed trait BlockchainEventcase class AddBlockEvent(transactions: List[Transaction], proof: Long) extends BlockchainEventsealed trait BlockchainCommandcase class AddBlockCommand(transactions: List[Transaction], proof: Long) extends BlockchainCommandcase object GetChain extends BlockchainCommandcase object GetLastHash extends BlockchainCommandcase object GetLastIndex extends BlockchainCommandcase class State(chain: Chain)def props(chain: Chain, nodeId: String): Props = Props(new Blockchain(chain, nodeId))
}
view raw

We can see that the companion object of this actor has more elements than the other ones. The State class is where we store the state of our blockchain, that is its Chain. The idea is to update the state every time a new block is created.

我們可以看到該companion object具有比其他參與者更多的元素。 State類是我們存儲區塊鏈狀態(即Chain 。 想法是每次創建新塊時都更新狀態。

For this purpose, there are two different traits: BlockchainEvent and BlockchainCommand. The former is to handle the events that will trigger the persistence logic, the latter is used to send direct commands to the actor. The AddBlockEvent message is the event that will update our state. The AddBlockCommand, GetChain, GetLastHash, and LastIndex commands are the one used to interact with the underlying blockchain.

為此,有兩個不同的特征: BlockchainEventBlockchainCommand 。 前者用于處理將觸發持久性邏輯的事件,后者用于將直接命令發送給參與者。 AddBlockEvent消息是將更新我們狀態的事件。 AddBlockCommandGetChainGetLastHashLastIndex命令是用于與基礎區塊鏈進行交互的命令。

The usual props function initializes the Blockchain Actor with the initial Chain and the nodeId of the Scalachain node.

常用的props函數使用初始Chain和Scalachain節點的nodeId初始化Blockchain Actor。

class Blockchain(chain: Chain, nodeId: String) extends PersistentActor with ActorLogging{import Blockchain._var state = State(chain)override def persistenceId: String = s"chainer-$nodeId"//Code...
}

The Blockchain Actor extends the trait PersistentActor provided by the Akka framework. In this way we have out-of-the-box all the logic required to persist and recover our state.

區塊鏈演員擴展了Akka框架提供的特征PersistentActor 。 這樣,我們就可以開箱即用地保存和恢復狀態所需的所有邏輯。

We initialize the state using the Chain provided as an argument upon creation. The nodeId is part of the persistenceId that we override. The persistence logic will use it to identify the persisted state. Since we can have multiple Scalachain nodes running in the same machine, we need this value to correctly persist and recover the state of each node.

我們使用創建時作為參數提供的Chain初始化狀態。 nodeId是我們覆蓋的persistenceId一部分。 持久性邏輯將使用它來識別持久狀態。 由于我們可以在同一臺機器上運行多個Scalachain節點,因此我們需要此值才能正確保留并恢復每個節點的狀態。

def updateState(event: BlockchainEvent) = event match {case AddBlockEvent(transactions, proof) =>{state = State(ChainLink(state.chain.index + 1, proof, transactions) :: state.chain)log.info(s"Added block ${state.chain.index} containing ${transactions.size} transactions")}}

The updateState function executes the update of the Actor state when the AddBlockEvent is received.

收到AddBlockEvent時, updateState函數將執行Actor狀態的更新。

override def receiveRecover: Receive = {case SnapshotOffer(metadata, snapshot: State) => {log.info(s"Recovering from snapshot ${metadata.sequenceNr} at block ${snapshot.chain.index}")state = snapshot}case RecoveryCompleted => log.info("Recovery completed")case evt: AddBlockEvent => updateState(evt)}

The receiveRecover function reacts to the recovery messages sent by the persistence logic. During the creation of an actor a persisted state (snapshot) may be offered to it using the SnapshotOffer message. In this case the current state becomes the one provided by the snapshot.

receiveRecover函數對持久性邏輯發送的恢復消息做出React。 在創建actor期間,可以使用SnapshotOffer消息向其提供持久狀態( 快照 )。 在這種情況下,當前狀態變為快照提供的狀態。

RecoveryCompleted message informs us that the recovery process completed successfully. The AddBlockEvent triggers the updateState function passing the event itself.

RecoveryCompleted消息通知我們恢復過程已成功完成。 AddBlockEvent觸發updateState函數傳遞事件本身。

override def receiveCommand: Receive = {case SaveSnapshotSuccess(metadata) => log.info(s"Snapshot ${metadata.sequenceNr} saved successfully")case SaveSnapshotFailure(metadata, reason) => log.error(s"Error saving snapshot ${metadata.sequenceNr}: ${reason.getMessage}")case AddBlockCommand(transactions : List[Transaction], proof: Long) => {persist(AddBlockEvent(transactions, proof)) {event =>updateState(event)}// This is a workaround to wait until the state is persisteddeferAsync(Nil) { _ =>saveSnapshot(state)sender() ! state.chain.index}}case AddBlockCommand(_, _) => log.error("invalid add block command")case GetChain => sender() ! state.chaincase GetLastHash => sender() ! state.chain.hashcase GetLastIndex => sender() ! state.chain.index}

The receiveCommand function is used to react to the direct commands sent to the actor. Let’s skip the GetChain, GetLastHash, and GetLastIndex commands, since they are trivial. The AddBlockCommand is the interesting part: it creates and fires an AddBlock event, that is persisted in the event journal of the Actor. In this way events can be replayed in case of recovery.

receiveCommand函數用于對發送給角色的直接命令做出React。 讓我們跳過GetChainGetLastHashGetLastIndex命令,因為它們很簡單。 AddBlockCommand是有趣的部分:它創建并觸發一個AddBlock事件,該事件將AddBlock在Actor的事件日志中。 這樣,在恢復的情況下可以重播事件。

The deferAsync function waits until the state is updated after the processing of the event. Once the event has been executed the actor can save the snapshot of the state, and inform the sender of the message with the updated last index of the Chain. The SaveSnapshotSucces and SaveSnapshotFailure messages helps us to keep track of possible failures.

deferAsync函數將等待,直到事件處理后狀態被更新為止。 一旦執行了事件,參與者就可以保存狀態的快照,并使用Chain的更新后的最后索引將消息通知給發件人。 SaveSnapshotSuccesSaveSnapshotFailure消息有助于我們跟蹤可能的故障。

節點,一個演員來統治他們 (Node, an actor to rule them all)

The Node Actor is the backbone of our Scalachain node. It is the supervisor of all the other actors (Broker, Miner, and Blockchain), and the one communicating with the outside world through the REST API.

Node Actor是我們Scalachain節點的骨干。 它是所有其他參與者(經紀人,礦工和區塊鏈)的主管 ,也是通過REST API與外界通信的人。

object Node {sealed trait NodeMessagecase class AddTransaction(transaction: Transaction) extends NodeMessagecase class CheckPowSolution(solution: Long) extends NodeMessagecase class AddBlock(proof: Long) extends NodeMessagecase object GetTransactions extends NodeMessagecase object Mine extends NodeMessagecase object StopMining extends NodeMessagecase object GetStatus extends NodeMessagecase object GetLastBlockIndex extends NodeMessagecase object GetLastBlockHash extends NodeMessagedef props(nodeId: String): Props = Props(new Node(nodeId))def createCoinbaseTransaction(nodeId: String) = Transaction("coinbase", nodeId, 100)
}

The Node Actor has to handle all the high level messages that coming from the REST API. This is the reason why we find in the companion object more or less the same messages we implemented in the children actors. The props function takes a nodeId as an argument to create our Node Actor. This will be the one used for the initialization of Blockchain Actor. The createCoinbaseTransaction simply creates a transaction assigning a predefined coin amount to the node itself. This will be the reward for the successful mining of a new block of the blockchain.

Node Actor必須處理來自REST API的所有高級消息。 這就是為什么我們在companion object或多或少地發現在子actor中實現的相同消息的原因。 props函數將nodeId作為參數來創建我們的Node Actor。 這將是用于初始化Blockchain Actor的工具。 createCoinbaseTransaction只是創建一個將預定義硬幣數量分配給節點本身的交易。 這將是成功挖掘區塊鏈新區塊的獎勵

class Node(nodeId: String) extends Actor with ActorLogging {import Node._implicit lazy val timeout = Timeout(5.seconds)val broker = context.actorOf(Broker.props)val miner = context.actorOf(Miner.props)val blockchain = context.actorOf(Blockchain.props(EmptyChain, nodeId))miner ! Ready//Code...
}

Let’s look at the initialization of the Node Actor. The timeout value is used by the ask (?) operator (this will be explained shortly). All our actors are created in the actor context, using the props function we defined in each actor.

讓我們看一下Node Actor的初始化。 超時值由ask ( ? )運算符使用(稍后將對此進行說明)。 我們所有的參與者都是在參與者context中使用我們在每個參與者中定義的props函數創建的。

The Blockchain Actor is initialized with the EmptyChain and the nodeId of the Node. Once everything is created, we inform the Miner Actor to be ready to mine sending it a Ready message. Ok, we are now ready to receive some message and react to it.

使用EmptyChain和節點的nodeId初始化Blockchain Actor。 創建完所有內容后,我們會通知礦工演員準備好向其發送Ready消息。 好的,我們現在準備接收一些消息并對它做出React。

override def receive: Receive = {case AddTransaction(transaction) => {//Code...}case CheckPowSolution(solution) => {//Code...}case AddBlock(proof) => {//Code...}case Mine => {//Code...}case GetTransactions => broker forward Broker.GetTransactionscase GetStatus => blockchain forward GetChaincase GetLastBlockIndex => blockchain forward GetLastIndexcase GetLastBlockHash => blockchain forward GetLastHash}

This is an overview of the usual receive function that we should override. I will analyze the logic of the most complex cases later, now let’s look at the last four. Here we forward the messages to the Blockchain Actor, since it isn’t required any processing. Using the forward operator the sender() of the message will be the one that originated the message, not the Node Actor. In this way the Blockchain Actor will respond to the original sender of the message (the REST API layer).

這是我們應該重寫的常規receive函數的概述。 稍后,我將分析最復雜case的邏輯,現在讓我們看一下最后四個。 在這里,我們將消息轉發到Blockchain Actor,因為它不需要任何處理。 使用forward運算符,消息的sender()將是消息的始發者,而不是Node Actor。 這樣,Blockchain Actor將響應消息的原始發送者(REST API層)。

override def receive: Receive = {case AddTransaction(transaction) => {val node = sender()broker ! Broker.AddTransaction(transaction)(blockchain ? GetLastIndex).mapTo[Int] onComplete {case Success(index) => node ! (index + 1)case Failure(e) => node ! akka.actor.Status.Failure(e)}}//Code...
}

The AddTransaction message triggers the logic to store a new transaction in the list of pending ones of our blockchain. The Node Actor responds with the index of the block that will contain the transaction.

AddTransaction消息觸發了將新交易存儲在我們的區塊鏈未決交易列表中的邏輯。 Node Actor以將包含事務的塊的index作為響應。

First of all we store the “address” of the sender() of the message in a node value to use it later. We send to the Broker Actor a message to add a new transaction, then we ask to the Blockchain Actor the last index of the chain. The ask operator — the one expressed with ? — is used to send a message to an actor and wait for a response. The response (mapped to an Int value) can be a Success or a Failure.

首先,我們將消息的sender()的“地址”存儲在node值中,以備后用。 我們向經紀人Actor發送一條消息以添加新交易,然后我們ask區塊鏈Actor ask鏈的最后一個索引。 ask運算符-用表示的那個? —用于向演員發送消息并等待響應。 響應(映射到Int值)可以是SuccessFailure

In the first case we send back to the sender (node) the index+1, since it will be the index of the next mined block. In case of failure, we respond to the sender with a Failure containing the reason of the failure. Remember this pattern:

在第一種情況下,我們將index+1發送回發送方( node ),因為它將是下一個已開采區塊的索引。 如果發生故障,我們將以包含Failure原因的“故障”響應發件人。 記住這種模式:

ask → wait for a response → handle success/failure

詢問→等待回應→處理成功/失敗

because we will see it again.

因為我們會再次看到它。

override def receive: Receive = {//Code...case CheckPowSolution(solution) => {val node = sender()(blockchain ? GetLastHash).mapTo[String] onComplete {case Success(hash: String) => miner.tell(Validate(hash, solution), node)case Failure(e) => node ! akka.actor.Status.Failure(e)}}//Code...
}
view raw

This time we have to check if a solution to the PoW algorithm is correct. We ask to the Blockchain Actor the hash of the last block, and we tell the Miner Actor to validate the solution against the hash. In the tell function we pass to the Miner the Validate message along with the address of the sender, so that the miner can respond directly to it. This is another approach, like the forward one we saw before.

這次我們必須檢查PoW算法的解決方案是否正確。 我們向區塊鏈參與者詢問最后一個區塊的哈希值,然后告訴礦工參與者針對哈希值驗證解決方案。 在tell函數中,我們將Validate消息以及發送者的地址傳遞給礦工,以便礦工可以直接對其進行響應。 這是另一種方法,就像forward一個我們以前看到。

override def receive: Receive = {//Code...case AddBlock(proof) => {val node = sender()(self ? CheckPowSolution(proof)) onComplete {case Success(_) => {(broker ? Broker.GetTransactions).mapTo[List[Transaction]] onComplete {case Success(transactions) => blockchain.tell(AddBlockCommand(transactions, proof), node)case Failure(e) => node ! akka.actor.Status.Failure(e)}broker ! Clear}case Failure(e) => node ! akka.actor.Status.Failure(e)}}//Code...
}

Other nodes can mine blocks, so we may receive a request to add a block that we didn’t mine. The proof is enough to add the new block, since we assume that all the nodes share the same list of pending transactions.

其他節點可以挖掘塊,因此我們可能會收到添加未挖掘塊的請求。 該證明足以添加新的塊,因為我們假設所有節點共享相同的待處理事務列表。

override def receive: Receive = {//Code...case Mine => {val node = sender()(blockchain ? GetLastHash).mapTo[String] onComplete {case Success(hash) => (miner ? Miner.Mine(hash)).mapTo[Future[Long]] onComplete {case Success(solution) => waitForSolution(solution)case Failure(e) => log.error(s"Error finding PoW solution: ${e.getMessage}")}case Failure(e) => node ! akka.actor.Status.Failure(e)}}//Code...}def waitForSolution(solution: Future[Long]) = Future {solution onComplete {case Success(proof) => {broker ! Broker.AddTransaction(createCoinbaseTransaction(nodeId))self ! AddBlock(proof)miner ! Ready}case Failure(e) => log.error(s"Error finding PoW solution: ${e.getMessage}")}}

This is a simplification, in the Bitcoin network there cannot be such assumption. First of all we should check if the solution is valid. We do this sending a message to the node itself: self ? CheckPowSolution(proof). If the proof is valid, we get the list of pending transaction from the Broker Actor, then we tell to the Blockchain Actor to add to the chain a new block containing the transactions and the validated proof. The last thing to do is to command the Broker Actor to clear the list of pending transactions.

這是一種簡化,在比特幣網絡中不可能有這樣的假設。 首先,我們應該檢查解決方案是否有效。 我們這樣做是向節點本身發送一條消息: self ? CheckPowSolution(proof) self ? CheckPowSolution(proof) 。 如果證明有效,我們從經紀人代理那里獲得未決交易的清單,然后tell區塊鏈參與者將包含交易和經過驗證的證明的新區塊添加到鏈中。 最后要做的是命令Broker Actor清除掛起的事務列表。

The last message is the request to start mining a new block. We need the hash of the last block in the chain, so we request it to the Blockchain Actor. Once we have the hash, we can start mining a new block.

最后一條消息是開始挖掘新塊的請求。 我們需要鏈中最后一個區塊的哈希,因此我們將其請求給Blockchain Actor。 有了哈希后,就可以開始挖掘新塊了。

The PoW algorithm is a long-running operation, so the Miner Actor responds immediately with a Future containing the computation. The waitForSolution function waits for the computation to complete, while the Node Actor keeps doing its business.

PoW算法是一項長期運行的操作,因此,礦工Actor立即使用包含計算的Future做出響應。 waitForSolution函數等待計算完成,而Node Actor繼續進行其業務。

When we have a solution, we reward ourselves adding the coinbase transaction to the list of pending transactions. Then we add the new block to the chain and tell the Miner Actor to be ready to mine another block.

當我們有解決方案時,我們會獎勵自己將coinbase交易添加到未決交易列表中。 然后,我們將新塊添加到鏈中,并告知礦工演員準備開采另一個塊。

帶有Akka HTTP的REST API (REST API with Akka HTTP)

This last section describes the server and REST API. This is the most “external” part of our application, the one connecting the outside world to the Scalachain node. We will make use of Akka HTTP library, which is part of the Akka Framework. Let’s start looking at the server, the entry point of our application.

最后一部分介紹了服務器和REST API。 這是我們應用程序中最“外部”的部分,將外部世界連接到Scalachain節點。 我們將使用Akka HTTP庫,它是Akka Framework的一部分。 讓我們開始看看服務器,這是我們應用程序的入口點。

object Server extends App with NodeRoutes {val address = if (args.length > 0) args(0) else "localhost"val port = if (args.length > 1) args(1).toInt else 8080implicit val system: ActorSystem = ActorSystem("scalachain")implicit val materializer: ActorMaterializer = ActorMaterializer()val node: ActorRef = system.actorOf(Node.props("scalaChainNode0"))lazy val routes: Route = statusRoutes ~ transactionRoutes ~ mineRoutesHttp().bindAndHandle(routes, address, port)println(s"Server online at http://$address:$port/")Await.result(system.whenTerminated, Duration.Inf)}

Since the Server is our entry point, it needs to extend the App trait. It extends also NodeRoutes, a trait that contains all the http routes to the various endpoint of the node.

由于Server是我們的切入點,因此它需要擴展App特性。 它還擴展了NodeRoutes ,這是一個特征,其中包含到節點各個端點的所有http路由。

The system value is where we store our ActorSystem. Every actor created in this system will be able to talk to the others inside it. Akka HTTP requires also the definition of another value, the ActorMaterializer. This relates to the Akka Streams module, but since Akka HTTP is built on top of it, we still need this object to be initialized in our server (if you want to go deep on the relation with streams, look here).

system值是我們存儲ActorSystem 。 在此系統中創建的每個演員都可以與其中的其他角色交談。 Akka HTTP還需要定義另一個值ActorMaterializer 。 這與Akka Streams模塊有關,但是由于Akka HTTP是在其之上構建的,因此我們仍然需要在服務器中初始化該對象(如果您想深入了解與流的關系,請參見此處 )。

The Node Actor is created along with the HTTP routes of the node, that are chained using the ~ operator. Don’t worry about the routes now, we will be back to them in a moment.

將使用~運算符將Node Actor與節點的HTTP路由一起創建。 現在不用擔心路線,我們稍后會再與他們聯系。

The last thing to do is to start our server using the function Http().bindHandle, that will also bind the routes we pass to it as an argument. The Await.result function will wait the termination signal to stop the server.

最后要做的是使用功能Http().bindHandle啟動服務器,該服務器還將綁定我們作為參數傳遞給它的路由。 Await.result函數將等待終止信號以停止服務器。

The server will be useless without the routes to trigger the business logic of the application. We define the routes in the trait NodeRoutes, differentiating them according to the different logic they trigger:

如果沒有路由來觸發應用程序的業務邏輯,服務器將毫無用處。 我們在特征NodeRoutes定義路由,并根據它們觸發的不同邏輯對其進行區分:

  • statusRoutes contains the endpoints to ask the Scalachain node for its status.

    statusRoutes包含向Scalachain節點詢問其狀態的端點。

  • transactionRoutes handles everything related to transactions.

    transactionRoutes處理與事務相關的所有事情。

  • mineRoutes has the endpoint to start the mining process

    mineRoutes具有端點來開始挖掘過程

Notice that this differentiation is a logic one, just to keep things ordered and readable. The three routes will be chained in a single one after their initialization in the server.

請注意,這種區分是一種邏輯,只是為了保持事物的有序性和可讀性。 在服務器中初始化后,這三個路由將被鏈接為一個路由。

//Imports...
import com.elleflorio.scalachain.utils.JsonSupport._
// Imports...trait NodeRoutes extends SprayJsonSupport {implicit def system: ActorSystemdef node: ActorRefimplicit lazy val timeout = Timeout(5.seconds)//Code...
}

The NodeRoutes trait extends SprayJsonSupport to add JSON serialization/deserialization. SprayJson is a Scala library analogous to Jackson in Java, and it comes for free with Akka HTTP.

NodeRoutes特性擴展了SprayJsonSupport以添加JSON序列化/反序列化。 SprayJson是一個Scala庫,類似于Java中的Jackson,它隨Akka HTTP免費提供。

To convert our objects to a JSON string we import the class JsonSupport defined in the utils package, which contains custom reader/writer for every object. I won’t go into the details, you can find the class in the repository if you want to look at the implementation.

要將對象轉換為JSON字符串,我們導入utils包中定義的JsonSupport類, JsonSupport包含每個對象的自定義讀取器/寫入器。 我不會詳細介紹,如果您要查看實現,則可以在存儲庫中找到該類 。

We have a couple of implicit values. The ActorSystem is used to define the system of actors, while the Timeout is used by the OnSuccess function that waits for a response from the actors. The ActorRef is defined by overriding in the server implementation.

我們有幾個隱式值。 ActorSystem用于定義參與者的系統,而TimeoutOnSuccess函數使用,該函數等待參與者的響應。 通過覆蓋服務器實現中定義ActorRef

//Code...lazy val statusRoutes: Route = pathPrefix("status") {concat(pathEnd {concat(get {val statusFuture: Future[Chain] = (node ? GetStatus).mapTo[Chain]onSuccess(statusFuture) { status =>complete(StatusCodes.OK, status)}})})}//Code...

The endpoint to get the status of the blockchain is defined in the statusRoutes. We define the pathPrefix as "status" so the node will respond to the path ` http://<address>:<port/status. After that there is the definition of the HTTP actions we want to enable on the path. Here we want to get the status of the blockchain, so we define only the get action. Inside that we ask the Node Actor to get the current Chain. When the actor responds, the Chain is sent as a JSON along with an ok status in the complete method.

用于獲取區塊鏈狀態的端點在statusRoutes中定義。 我們將pathPrefix定義為“狀態”,以便節點將響應路徑`http:// <地址>:<端口/狀態。 然后,定義了我們要在路徑上啟用的HTTP操作。 在這里,我們要獲取區塊鏈的狀態,因此我們僅定義get操作。 在其中,我們要求Node Actor獲取當前的Chain。 當actor響應時,在complete方法中,Chain作為JSON連同ok狀態一起發送。

//Code...lazy val transactionRoutes: Route = pathPrefix("transactions") {concat(pathEnd {concat(get {val transactionsRetrieved: Future[List[Transaction]] =(node ? GetTransactions).mapTo[List[Transaction]]onSuccess(transactionsRetrieved) { transactions =>complete(transactions.toList)}},post {entity(as[Transaction]) { transaction =>val transactionCreated: Future[Int] =(node ? AddTransaction(transaction)).mapTo[Int]onSuccess(transactionCreated) { done =>complete((StatusCodes.Created, done.toString))}}})})}//Code...

The transactionRoutes allows the interaction with the pending transactions of the node. We define the HTTP action get to retrieve the list of pending transactions. This time we also define the HTTP action post to add a new transaction to the list of pending ones. The entity(as[Transaction]) function is used to deserialize the JSON body into a Transaction object.

transactionRoutes允許與節點的待處理事務進行交互。 我們定義HTTP操作get來檢索未決事務列表。 這次,我們還定義了HTTP操作post以將新事務添加到掛起的事務列表中。 entity(as[Transaction])函數用于將JSON主體反序列化為Transaction對象。

//Code...
lazy val mineRoutes: Route = pathPrefix("mine") {concat(pathEnd {concat(get {node ! Minecomplete(StatusCodes.OK)})})}//Code...

The last route is the MineRoutes. This is a very simple one, used only to ask the Scalachain node to start mine a new block. We define a get action since we do not need to send anything to start the mining process. It is not required to wait for a response, since it may take some time, so we immediately respond with an Ok status.

最后一條路線是MineRoutes 。 這是一個非常簡單的示例,僅用于要求Scalachain節點開始挖掘一個新塊。 我們定義了一個get動作,因為我們不需要發送任何東西就可以開始挖掘過程。 不需要等待響應,因為它可能需要一些時間,因此我們會立即以Ok狀態進行響應。

The API to interact with the Scalachain node are documented here.

與Scalachain節點進行交互的API記錄在這里 。

結論 (Conclusion)

With the last section, we concluded our tour inside Scalachain. This prototype of a blockchain is far from a real implementation, but we learned a lot of interesting things:

在最后一部分中,我們結束了Scalachain內部之旅。 區塊鏈的原型遠非真正的實現,但我們學到了很多有趣的東西:

  • How a blockchain works, at least from an high level perspective.

    至少從高層的角度來看,區塊鏈是如何工作的。
  • How to use functional programming (Scala) to build a blockchain.

    如何使用函數式編程(Scala)構建區塊鏈
  • How the Actor Model works, and its application to our use case using the Akka Framework.

    Actor模型如何工作,以及如何使用Akka Framework將其應用到我們的用例中。
  • How to use the Akka HTTP library to create a sever to run our blockchain, along with the APIs to interact with it.

    如何使用Akka HTTP庫創建一個服務器來運行我們的區塊鏈,以及與之交互的API。

The code is not perfect, and some things can be implemented in a better way. For this reason, feel free to contribute to the project! ;-)

代碼并不完美,有些事情可以用更好的方式實現。 因此, 隨時為該項目做貢獻! ;-)

翻譯自: https://www.freecodecamp.org/news/how-to-build-a-simple-actor-based-blockchain-aac1e996c177/

vtk刪除一個actor

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/392960.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/392960.shtml
英文地址,請注明出處:http://en.pswp.cn/news/392960.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

java枚舉的簡單介紹

1.枚舉&#xff0c;enum關鍵字&#xff0c;相當于public final static. 2.舉例&#xff1a; 首先定義了一個名為spiciness的枚舉類型。 public enum Spiciness {NOT, MILD, MEDIUM, HOT, FLAMING } 再來測試一下enum&#xff0c;這個測試方法表明它有tostring()方法&#xff0…

瀏覽器中插入富文本編輯器

常用的富文本編輯器有CKEditor、UEEditor、TinyEditor、KindEditor等、以下以kindeditor編輯器的使用為例。 1.官網下載KindEditor編輯器http://kindeditor.net/down.php&#xff0c; 當前最新版本為4.1.11&#xff0c;解壓縮后放入項目的static目錄&#xff0c;作為js插件引用…

獲取Extjs文本域中的內容

經常在Ext.select()和Ext.query()等問題上糾結&#xff0c;今天終于有了點新認識&#xff1a; 需求&#xff0c;假設我們的頁面上有個panel ,其id為clusterstab_edit_details,這個panel的內部有個textarea,這個textarea的name為editDetails_Description,那么我們有多少方法可以…

android觸摸指紋會觸發按鍵功能,Android P新特性:利用觸摸指紋識別器能阻止手機息屏...

設想你正在閱讀手機上的文章&#xff0c;突然間顯示屏變暗了一點。顯然&#xff0c;你設置的30秒或1分鐘超時息屏對于常規使用來說還可以&#xff0c;但對于閱讀純文本片段&#xff0c;還遠遠不夠。因此&#xff0c;這時你會輕觸屏幕&#xff0c;可能會上下滑動&#xff0c;以防…

leetcode37. 解數獨(hashmap+回溯)

編寫一個程序&#xff0c;通過已填充的空格來解決數獨問題。 一個數獨的解法需遵循如下規則&#xff1a; 數字 1-9 在每一行只能出現一次。 數字 1-9 在每一列只能出現一次。 數字 1-9 在每一個以粗實線分隔的 3x3 宮內只能出現一次。 空白格用 ‘.’ 表示。 Note: 給定的數獨序…

malloc、calloc、realloc和alloca各種的區別

需要先包含頭文件 #include"malloc.h"malloc是標準的在堆中開辟新的空間比如char *pt(char *)malloc(10*sizeof(char));需要free(p)才會釋放空間calloc也是開辟空間&#xff0c;但是使用方式不一樣比如char *pt(char *)calloc(100, sizeof(char));然后用calloc開辟的…

如何對第一個Vue.js組件進行單元測試

by Sarah Dayan通過莎拉達揚 In Build Your First Vue.js Component we made a star rating component. We’ve covered many fundamental concepts to help you create more complex Vue.js components.在“ 構建您的第一個Vue.js組件”中&#xff0c;我們制作了星級評分組件…

Asp.NetCoreWebApi - RESTful Api

REST 常用http動詞 WebApi 在 Asp.NetCore 中的實現3.1. 創建WebApi項目.3.2. 集成Entity Framework Core操作Mysql 3.2.1. 安裝相關的包(為Xxxx.Infrastructure項目安裝)3.2.2. 建立Entity和Context3.2.3. ConfigureService中注入EF服務3.2.4. 遷移數據庫3.2.5. 數據庫遷移結果…

android動畫影子效果,Android TV常用動畫的效果,View選中變大且有陰影(手機也能用)...

因為電視屏幕比較大&#xff0c;而我們看電視時距離電視有一定距離&#xff0c;這樣就需要動畫效果比較明顯&#xff0c;這個動畫就是應用最廣泛的&#xff0c;因為很酷&#xff0c;呵呵&#xff0c;你懂得&#xff0c;看了就知道。效果如下圖&#xff1a;public class MainAct…

leetcode226. 翻轉二叉樹(dfs)

翻轉一棵二叉樹。示例&#xff1a;輸入&#xff1a;4/ \2 7/ \ / \ 1 3 6 9 輸出&#xff1a;4/ \7 2/ \ / \ 9 6 3 1代碼 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode righ…

Java BigDecimal Rounding Mode

UP        往絕對值大了取 DOWN      往絕對值小了取 CEILING     往值大了取 FLOOR      往值小了取 HALF_UP     不管正負&#xff0c;四舍五入 HALF_DOWN  不管正負&#xff0c;五舍六入 HALF_EVEN    整數部分為奇數&#xff1a;四舍五入…

如何成為一名有效的軟件工程師

by Luis Santiago路易斯圣地亞哥(Luis Santiago) 如何成為一名有效的軟件工程師 (How to become an effective software engineer) When I first started my journey as a software engineer I quickly noticed the great amount of cognitive load involved when working on …

linux 高可用----keepalived+lvs

什么是高可用&#xff1f; HA&#xff08;high availability&#xff09;即高可用性&#xff1b;就是在高可用集群中發生單點故障時&#xff0c;能夠自動轉移資源并切換服務&#xff0c;以保證服務一直在線的機制。 LVS LVS&#xff1a;&#xff08;linux virtual server&#…

用戶配置相關文件

用戶配置相關文件小總結 /etc/passwd 記錄用戶相關的信息 /etc/shadow 密碼影子文件 /etc/group 記錄用戶組相關的信息 /etc/gshadow 密碼影子文件&#xff08;組密碼&#xff09; /etc/passwd 文件中各段的內容 第1段&#xff1a;用戶名 第…

華為5c android n風格,華為榮耀暢玩5C的屏幕怎么樣

華為榮耀暢玩5C的屏幕怎么樣屏幕方面&#xff0c;華為榮耀暢玩5C采用了5.2英寸1080P級別GFF貼合屏幕&#xff0c;塑料邊框采用了弧面狀的設計&#xff0c;握感比較舒適。華為榮耀暢玩5C采用了雙主天線的設計&#xff0c;分別在上下的塑料區域。此外&#xff0c;邊框以及后蓋的上…

spring解析配置文件(三)

一、從XmlBeanDefinitionReader的registerBeanDefinitions&#xff08;doc,resource&#xff09;開始 1 protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) 2 throws BeanDefinitionStoreException { 3 try { 4 …

leetcode968. 監控二叉樹(dfs)

給定一個二叉樹&#xff0c;我們在樹的節點上安裝攝像頭。 節點上的每個攝影頭都可以監視其父對象、自身及其直接子對象。 計算監控樹的所有節點所需的最小攝像頭數量。 輸入&#xff1a;[0,0,null,0,0] 輸出&#xff1a;1 解釋&#xff1a;如圖所示&#xff0c;一臺攝像頭足…

linux系統部署war包,查看tomcat日志

1.部署war包app/tomcat/bin在tomcat/bin 目錄下啟動 .startup.sh&#xff0c;在啟動過程中tomcat會對war包進行解壓&#xff0c;形成相應的項目目錄 執行命令&#xff1a;./startup.sh 2.實時查看tomcat的日志。首先需要到tomcat的日志目錄下。我的目錄供你參考app/tomcat/logs…

代碼編寫工具_這些工具將幫助您編寫簡潔的代碼

代碼編寫工具看一下Prettier&#xff0c;ESLint&#xff0c;Husky&#xff0c;Lint-Staged和EditorConfig (A look at Prettier, ESLint, Husky, Lint-Staged and EditorConfig) Learning to write good code, but you don’t know where to start… Going through style-guide…

使用kibana和elasticsearch日志實時繪制圖表

前言&#xff1a; 此文接的是上篇&#xff0c;上次的內容是&#xff0c;用python操作elasticsearch存儲&#xff0c;實現數據的插入和查詢。 估計有些人一看我的標題&#xff0c;以為肯定是 logstash kibana elasticsearch的組合。這三個家伙也確實總是勾搭在一塊。 其實logst…