Building a blockchain with Go-Part 4: Trading (1)

Source: Internet
Author: User
Tags miner digs
This is a creation in Article, where the information may have evolved or changed.

The series of articles I have put on GitHub: blockchain-tutorial, Updates will be on GitHub, and may not be synced here. If you want to run the code directly, you can clone the tutorial repository on GitHub and go to the SRC directory to execute make .

Introduction

Trading (transaction) is the core of bitcoin, and the only purpose of blockchain is to be able to store transactions securely and reliably. In a blockchain, once a transaction is created, no one can modify or delete it. In today's article, we will begin to implement this part of the transaction. However, since trading is a big topic, I will divide it into two parts: in this part of the day, we will implement the general mechanism of trading. In the second part, we will continue to discuss some of its details.

In addition, because the code implementation varies greatly, it does not list all the code in the article, here you can see all the changes.

No spoon.

If you've developed a Web application before, you might create two tables in the database in the implementation of the payment:

    • Accounts
    • Transactions

The account will store user information, which includes personal information and balances. Transaction stores money transfer information, which is the transfer of funds from one account to another. In Bitcoin, payment is another completely different way:

    1. No accounts (account)
    2. No balance (balance)
    3. No addresses (address)
    4. No money (coin)
    5. There is no sender and receiver (Sender,receiver) (here the sender and receiver is based on the current real-life scenario, the transaction between the two sides is one by one corresponding to the person. In Bitcoin, the "two sides" is the address, the address is the person, the person and address is not one by one corresponding relationship, a person may have a lot of addresses. )

Since the blockchain is a publicly open database, we do not want to store the sensitive information of the wallet owner (and therefore have some anonymity). Funds are not collected through accounts, and transactions are not transferred from one address to another, and there is no field or attribute to hold the account balance. Trading is all the content that the blockchain is meant to express. So what exactly is in the deal?

Bitcoin Trading

A transaction is composed of some inputs and outputs (output):

type Transaction struct {    ID   []byte    Vin  []TXInput    Vout []TXOutput}

For each new transaction, its input will refer to the output of a previous trade (reference) (here is an exception, the Coinbase deal we will talk about later). The so-called reference to a previous output, that is, the previous output is included in the input of another transaction. The output of the transaction, where the currency is actually stored. The following illustration illustrates the correlation between transactions:

Attention:

    1. There are some outputs that are not associated with an input
    2. The input of a transaction can refer to the output of the previous multiple trades
    3. An input must reference an output

Throughout this article, we will use words like "money", "coin (coin)", "Spend (spend)", "Send", "account" and so on. But in bitcoin, there is no such thing as a real concept. Trading is simply a script to lock (lock) some value (value), and these values can only be unlocked by the person who locked them (unlock).

Trade output

Let's start with output, first:

type TXOutput struct {    Value        int    ScriptPubKey string}

In fact, it is the output that stores the "coin" (note, which is the Value field above). The storage here refers to locking the output with a mathematical puzzle that is stored ScriptPubKey inside. Internally, Bitcoin uses a scripting language called Script , which defines the logic for locking and unlocking output. Although this language is quite primitive (and intended to avoid potential hacker attacks and abuses), it is not complicated, but we will not discuss its details here. You can find a detailed explanation here.

In Bitcoin, the value field stores the number of Satoshi , not the number of > BTC. A Satoshi equals one out of 10,000 >BTC (0.00000001 BTC), which is also the smallest currency unit in Bitcoin > (like a 1-cent coin).

Since the address has not yet been implemented, we will now avoid a complete script involving logic-related. ScriptPubKeyan arbitrary string (user-defined wallet address) will be stored.

By the way, with a scripting language like this, it also means that Bitcoin can actually be used as a smart contract platform.

On the output, it is very important that they are non-invisible, which means that you cannot reference only one of its parts. Or not, if you want to use it, you have to use it once. When a new transaction references an output, the output must be spent. If its value is greater than the desired value, then a change is generated and the change is returned to the sender. This is very similar to the real world scenario, when you want to pay, if something is worth $1, and you give a $5 bill, you get a $4 change.

Trade input

Here is the input:

type TXInput struct {    Txid      []byte    Vout      int    ScriptSig string}

As mentioned earlier, an input refers to an output from a previous trade: the ID of the Txid transaction is stored, and the index of Vout all output in the transaction is stored (because a transaction may have multiple outputs, which requires information to indicate which one is specific). ScriptSigis a script that provides data that can be used for an output ScriptPubKey . If ScriptSig the supplied data is correct, then the output is unlocked, then the unlocked value can be used to generate the new output, and if the data is incorrect, the output cannot be referenced in the input, or, in other words, the output cannot be used. This mechanism ensures that users cannot spend money that belongs to other people.

Again, because we haven't implemented the address, we ScriptSig 'll just store an arbitrary user-defined wallet address. We will implement the public key and signature (signature) in the next article.

To summarize briefly. The output is where the "coin" is stored. Each output comes with an unlock script that defines the logic to understand the output of the lock. Each new transaction must have at least one input and output. An input references the output of the previous transaction and provides the data (that ScriptSig is, the field) that is used to unlock the output in the output's unlock script, and the value of the unlock is used to generate the new output.

In other words, each input is the output of a previous trade, then from the beginning of a transaction to continue to trace back, it involves the input and output in the end who first exist? In other words, this is a chicken and egg who first who after the question, is there an egg or a chicken first?

Get the eggs first.

In bitcoin, there are eggs first, then chickens. The logic for entering the reference output is the classic "egg or chicken" problem: The input produces the output first, and then the output makes the input possible. In Bitcoin, the output is the first, and then the input is available. In other words, the first trade is only output, no input.

When the miner digs out a new block, it adds a coinbase trade to the new block. Coinbase Trading is a special trade that does not need to refer to the output of a previous trade. It generates coins (that is, the creation of a new currency), which is also a bonus for miners to get dug up, which can be understood as "issuing a SGD".

At the beginning of a blockchain, the first block, called the Genesis Block. It is this Genesis block that produces the first output of the blockchain. For the Genesis block, you do not need to reference the output of the previous transaction. Because there is no transaction at all before the Genesis block, there is no trade output that does not exist.

To create a Coinbase transaction:

func NewCoinbaseTX(to, data string) *Transaction {    if data == "" {        data = fmt.Sprintf("Reward to '%s'", to)    }    txin := TXInput{[]byte{}, -1, data}    txout := TXOutput{subsidy, to}    tx := Transaction{nil, []TXInput{txin}, []TXOutput{txout}}    tx.SetID()    return &tx}

Coinbase Trading has only one output and no input. In our implementation, it's Txid empty, Vout equal to-1. And, in the current line of sight, the Coinbase transaction does not ScriptSig store a script in, but simply stores an arbitrary string.

In Bitcoin, the first Coinbase transaction contains the following information: "The Times 03/jan/2009 Chancellor on brink of second bailout for banks". Click here to view.

subsidyIs the amount of the reward. In Bitcoin, this number is not actually stored, but is calculated based on the total number of chunks: The total number of chunks divided by 210000 subsidy . The prize for digging up the Genesis block is the BTC, which is 210000 halved after each chunk is dug. In our implementation, this reward value will be a constant (at least for now).

Save a transaction to a blockchain

From now on, each block must store at least one transaction. If there is no trade, it is impossible to dig up new blocks. This means that we should remove Block the Data field and replace it with a storage transaction:

type Block struct {    Timestamp     int64    Transactions  []*Transaction    PrevBlockHash []byte    Hash          []byte    Nonce         int}

NewBlockand the NewGenesisBlock corresponding changes must also be made:

func NewBlock(transactions []*Transaction, prevBlockHash []byte) *Block {    block := &Block{time.Now().Unix(), transactions, prevBlockHash, []byte{}, 0}    ...}func NewGenesisBlock(coinbase *Transaction) *Block {    return NewBlock([]*Transaction{coinbase}, []byte{})}

Next, modify the function that creates the new chain:

func CreateBlockchain(address string) *Blockchain {    ...    err = db.Update(func(tx *bolt.Tx) error {        cbtx := NewCoinbaseTX(address, genesisCoinbaseData)        genesis := NewGenesisBlock(cbtx)        b, err := tx.CreateBucket([]byte(blocksBucket))        err = b.Put(genesis.Hash, genesis.Serialize())        ...    })    ...}

Now, this function will accept an address as a parameter, which will be used to receive the reward for digging up the Genesis block.

Proof of workload

The proof-of-work algorithm must take into account the transactions stored in chunks to ensure the consistency and reliability of the blockchain transaction storage. So we have to modify the ProofOfWork.prepareData method:

func (pow *ProofOfWork) prepareData(nonce int) []byte {    data := bytes.Join(        [][]byte{            pow.block.PrevBlockHash,            pow.block.HashTransactions(), // This line was changed            IntToHex(pow.block.Timestamp),            IntToHex(int64(targetBits)),            IntToHex(int64(nonce)),        },        []byte{},    )    return data}

Unlike previously used pow.block.Data , now we use pow.block.HashTransactions() :

func (b *Block) HashTransactions() []byte {    var txHashes [][]byte    var txHash [32]byte    for _, tx := range b.Transactions {        txHashes = append(txHashes, tx.ID)    }    txHash = sha256.Sum256(bytes.Join(txHashes, []byte{}))    return txHash[:]}

We use hashes to provide a unique representation of the data, which has been encountered before. We want to be able to identify all the trades within a block by just a hash. To do this, we get a hash of each transaction, associate them, and then get a concatenated hash of the combination.

Bitcoin uses a more sophisticated technique: it represents all the transactions contained in a block as a Merkle tree, and then uses the root hash of the tree in the proof-of-work system (root hash). This method allows us to quickly retrieve whether a block contains a transaction, that is, the root hash is required to complete the judgment without downloading all the trades.

To check if it is correct so far:

$ blockchain_go createblockchain -address Ivan00000093450837f8b52b78c25f8163bb6137caf43ff4d9a01d1b731fa8ddcc8aDone!

Very good! We've got the first mining bonus, but how do we check the balance?

Non-spent trade output

We need to find all the unused trade outputs (unspent transactions outputs, UTXO). not spent (unspent) means that the output has not been included in the input of any transaction, or is not referenced by any input. In the illustration above, the output that is not spent is:

    1. tx0, Output 1;
    2. TX1, output 0;
    3. TX3, output 0;
    4. TX4, Output 0.

Of course, when we check the balance, we don't need to know all the UTXO on the entire blockchain, just focus on those UTXO we can unlock (we haven't implemented the key yet, so we'll use a user-defined address instead). First, let's define the locking and unlocking methods on the inputs and outputs:

func (in *TXInput) CanUnlockOutputWith(unlockingData string) bool {    return in.ScriptSig == unlockingData}func (out *TXOutput) CanBeUnlockedWith(unlockingData string) bool {    return out.ScriptPubKey == unlockingData}

Here, we're just comparing the script field with the unlockingData . This section is improved after we have implemented the address based on the private key in subsequent articles.

The next step is to find a transaction that contains an output that is not spent, which is quite difficult:

Func (BC *blockchain) findunspenttransactions (address string) []transaction {var unspenttxs []transaction Spenttxos: = Make (map[string][]int) BCI: = BC. Iterator () for {block: = BCI. Next () for _, TX: = range block. Transactions {txID: = hex. Encodetostring (tx.id) outputs:for outidx, out: = Range TX.        Vout {//was the output spent?              If SPENTTXOS[TXID]! = Nil {For _, Spentout: = Range Spenttxos[txid] {if spentout = = Outidx { Continue Outputs}}} if out. Canbeunlockedwith (address) {Unspenttxs = append (Unspenttxs, *TX)}} if Tx. Iscoinbase () = = False {for _, in: = Range TX. Vin {if in. Canunlockoutputwith (address) {intxid: = hex. Encodetostring (in. TXID) Spenttxos[intxid] = append (Spenttxos[intxid], in. Vout)}}}} If Len (block. Prevblockhash) = = 0 {break}} return Unspenttxs}

Since the transactions are stored in chunks, we have to check every trade in the blockchain. Start from output:

if out.CanBeUnlockedWith(address) {    unspentTXs = append(unspentTXs, tx)}

If an output is locked by an address, and this address happens to be the address of the transaction output we are looking for, then this output is what we want. But before we get to it, we need to check that the output is already contained in an input, which is to check if it has been spent:

if spentTXOs[txID] != nil {    for _, spentOut := range spentTXOs[txID] {        if spentOut == outIdx {            continue Outputs        }    }}

We skip the output that has already been included in the other inputs, which is included in the input, which means that the output has been spent and can no longer be used. After checking the output, we gather all the input that unlocks the given address lock (this does not apply to Coinbase transactions because they do not unlock the output):

if tx.IsCoinbase() == false {    for _, in := range tx.Vin {        if in.CanUnlockOutputWith(address) {            inTxID := hex.EncodeToString(in.Txid)            spentTXOs[inTxID] = append(spentTXOs[inTxID], in.Vout)        }    }}

This function returns a list of transactions containing the output that was not spent. To calculate the balance, we also need a function that takes these trades as inputs and returns only one output:

func (bc *Blockchain) FindUTXO(address string) []TXOutput {       var UTXOs []TXOutput       unspentTransactions := bc.FindUnspentTransactions(address)       for _, tx := range unspentTransactions {               for _, out := range tx.Vout {                       if out.CanBeUnlockedWith(address) {                               UTXOs = append(UTXOs, out)                       }               }       }       return UTXOs}

That's all! Now let's implement the getbalance command:

func (cli *CLI) getBalance(address string) {    bc := NewBlockchain(address)    defer bc.db.Close()    balance := 0    UTXOs := bc.FindUTXO(address)    for _, out := range UTXOs {        balance += out.Value    }    fmt.Printf("Balance of '%s': %d\n", address, balance)}

The account balance is the sum of all the unused trade outputs that are locked by the account address.

After digging up the Genesis block, check out our balance:

$ blockchain_go getbalance -address IvanBalance of 'Ivan': 10

This is our first money!

Send Currency

Now, we want to send some coins to others. To do this, we need to create a new transaction, put it in a block, and then dig out the block. We only implemented the Coinbase deal (which is a special deal) and now we need a generic deal:

func NewUTXOTransaction(from, to string, amount int, bc *Blockchain) *Transaction {    var inputs []TXInput    var outputs []TXOutput    acc, validOutputs := bc.FindSpendableOutputs(from, amount)    if acc < amount {        log.Panic("ERROR: Not enough funds")    }    // Build a list of inputs    for txid, outs := range validOutputs {        txID, err := hex.DecodeString(txid)        for _, out := range outs {            input := TXInput{txID, out, from}            inputs = append(inputs, input)        }    }    // Build a list of outputs    outputs = append(outputs, TXOutput{amount, to})    if acc > amount {        outputs = append(outputs, TXOutput{acc - amount, from}) // a change    }    tx := Transaction{nil, inputs, outputs}    tx.SetID()    return &tx}

Before we create a new output, we first have to find all the unused outputs and make sure that they store enough values (value), which is what the FindSpendableOutputs method does. Subsequently, for each output that is found, an input that references that output is created. Next, we create two outputs:

    1. One is locked by the recipient address. This is the currency that is actually transferred to the other address.
    2. One is locked by the sender address. This is a change. Generated only if the output is not spent exceeding the new exchange requirements. Remember: The output is not re-divided .

FindSpendableOutputsmethod is based on the FindUnspentTransactions method defined previously:

func (bc *Blockchain) FindSpendableOutputs(address string, amount int) (int, map[string][]int) {    unspentOutputs := make(map[string][]int)    unspentTXs := bc.FindUnspentTransactions(address)    accumulated := 0Work:    for _, tx := range unspentTXs {        txID := hex.EncodeToString(tx.ID)        for outIdx, out := range tx.Vout {            if out.CanBeUnlockedWith(address) && accumulated < amount {                accumulated += out.Value                unspentOutputs[txID] = append(unspentOutputs[txID], outIdx)                if accumulated >= amount {                    break Work                }            }        }    }    return accumulated, unspentOutputs}

This method iterates over all the unused trades and accumulates its values. When the accumulated value is greater than or equal to the value we want to transfer, it stops and returns the accumulated value, along with the output index that is grouped by the trade ID. We don't want to take out more money than we need to.

Now we can modify the Blockchain.MineBlock method:

func (bc *Blockchain) MineBlock(transactions []*Transaction) {    ...    newBlock := NewBlock(transactions, lastHash)    ...}

Finally, let's implement the send method:

func (cli *CLI) send(from, to string, amount int) {    bc := NewBlockchain(from)    defer bc.db.Close()    tx := NewUTXOTransaction(from, to, amount, bc)    bc.MineBlock([]*Transaction{tx})    fmt.Println("Success!")}

Sending a coin means creating a new transaction and packing the transaction into the blockchain by digging in a new block. However, Bitcoin does not do these things at once (but our implementation does). Instead, it puts all new trades in a memory pool (mempool), and then when a miner is ready to dig up a new block, it pulls out all the trades from the memory pool and creates a candidate block. Only when the block containing these deals is dug out and added to the blockchain, does the transaction begin to confirm.

Let's check to see if the sending currency can work:

$ blockchain_go send -from Ivan -to Pedro -amount 600000001b56d60f86f72ab2a59fadb197d767b97d4873732be505e0a65cc1e37Success!$ blockchain_go getbalance -address IvanBalance of 'Ivan': 4$ blockchain_go getbalance -address PedroBalance of 'Pedro': 6

Very good! Now, let's create more deals to make sure that sending coins from multiple outputs works as well:

$ blockchain_go send -from Pedro -to Helen -amount 200000099938725eb2c7730844b3cd40209d46bce2c2af9d87c2b7611fe9d5bdfSuccess!$ blockchain_go send -from Ivan -to Helen -amount 2000000a2edf94334b1d94f98d22d7e4c973261660397dc7340464f7959a7a9aaSuccess!

Now, Helen's currency is locked in two outputs: one from Pedro and one from Ivan. Let's send them to someone else:

$ blockchain_go send -from Helen -to Rachel -amount 3000000c58136cffa669e767b8f881d16e2ede3974d71df43058baaf8c069f1a0Success!$ blockchain_go getbalance -address IvanBalance of 'Ivan': 2$ blockchain_go getbalance -address PedroBalance of 'Pedro': 4$ blockchain_go getbalance -address HelenBalance of 'Helen': 1$ blockchain_go getbalance -address RachelBalance of 'Rachel': 3

It looks fine! Now, to test for some of the failed scenarios:

$ blockchain_go send -from Pedro -to Ivan -amount 5panic: ERROR: Not enough funds$ blockchain_go getbalance -address PedroBalance of 'Pedro': 4$ blockchain_go getbalance -address IvanBalance of 'Ivan': 2

Summarize

It's not easy, but now it's a deal! However, we still lack some key features like Bitcoin:

    1. Address. We do not have a real address based on the private key.
    2. Reward (reward). Mining is certainly not profitable now!
    3. UTXO set. Getting the balance requires scanning the entire blockchain, which can take a long time to do when there are so many chunks. And, if we want to validate the subsequent transactions, it will take a long time. And the UTXO set is to solve these problems, speed up the transaction-related operations.
    4. Memory Pool (Mempool). These transactions are stored in the memory pool before the trade is packaged into blocks. In our current implementation, a block contains only one transaction, which is quite inefficient.

Link:

    1. Full Source Codes
    2. Transaction
    3. Merkle Tree
    4. Coinbase

This article source code: Part_4

Original link: Building Blockchain in Go. Part 4:transactions 1

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.