-
block struct
-
type Block struct { // 1. block height Height int64 // 2. Previous block hash PrevBlockHash []byte // 3. Data,In the future, it will be used to store data such as transactions Data []byte // 4. Timestamp Timestamp int64 // 5. Block Hash Hash []byte }
-
-
Set Block Hash Function
-
// 2. Set Block hash func (block *Block) SetHash() { // 1. Convert Height to a byte array // use IntToHex() Function heightBytes := IntToHex(block.Height) // 2.Converts Timestamp to a byte array // 2.1 strconv.FormatInt() // The first parameter converts int64 to a string // The second parameter ranges from 2 to 36, representing the base system timeString := strconv.FormatInt(block.Timestamp, 2) timebytes := []byte(timeString) fmt.Println("SetHash timeString", timeString, "\n timebytes := ", timebytes, "\n heightBytes:=", heightBytes) // 3.Concatenate all properties blockbytes := bytes.Join([][]byte{heightBytes, block.PrevBlockHash, block.Data, timebytes, block.Hash}, []byte{}) // 4.Generating hash HashValue := sha256.Sum256(blockbytes) // Processing HashValue is 32 bytes block.Hash = HashValue[:] } // utils.go // Convert int64 to byte Array func IntToHex(num int64) []byte { buff := new(bytes.Buffer) err := binary.Write(buff, binary.BigEndian, num) if err != nil { log.Panic("IntToHex error", err) } return buff.Bytes() }
-
-
Create block function
-
func NewBlock(data string, height int64, prevBlockHash []byte) *Block { // CreateBlock block := &Block{ Height: height, PrevBlockHash: prevBlockHash, Data: []byte(data), Timestamp: time.Now().Unix(), Hash: nil, } fmt.Println("old block = ", block) // Sethash block.SetHash() return block }
-
-
The Genesis block is the first block of the blockchain.
-
It usually has a height of 1 and a block Hash of 0 as a 32-bit array
-
// Create Genesis Block func CreateGenesisBlock(data string) *Block { // create [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] // 32-bit byte array var ZeroBlockHash [32]byte return NewBlock(data, 1, ZeroBlockHash[:]) } // main.go block := block.CreateGenesisBlock("Genesis Block") fmt.Println("Genesis block = ", block)
-
-
A blockchain is composed of many blocks, and the genesis block is the first block of the blockchain.
-
Blockchain.go
-
Blockchain struct
-
type Blockchain struct { Blocks []*Block // Stores ordered blocks }
-
-
Create blockchain with Genesis block
-
func CreateBlockchainWithGenesisBlock() *Blockchain { GenesisBlock := CreateGenesisBlock("Genesis block Data...") return &Blockchain{Blocks: []*Block{GenesisBlock}} }
-
-
-
Add block to blckchain
-
func (blockchain *Blockchain) AddBlockToBlockchain(data string, height int64, prevHash []byte) { newBlock := NewBlock(data, height, prevHash) // Adds blocks to the chain array blockchain.Blocks = append(blockchain.Blocks, newBlock) }
-
-
Proof of Work (PoW) is the earliest consensus algorithm adopted in blockchain technology.
-
How It Works
-
Puzzle Solving
难题计算 -
Simple Verification
验证简单 -
Reward Mechanism
奖励机制
-
-
Create Proof Of Work Struct
-
type ProofOfWork struct { Block *Block // The block to validate Target *big.Int // Big Data Storage // Represents the difficulty of our data // int64 may overflow its range larger }
-
-
Difficulty
-
After running, 0000 0000 0000 0000 1001 0001 0000 .... 0001 is shifted 256-targetBit to the left
-
// Difficulty // 0000 0000 0000 0000 1001 0001 0000 .... 0001 // A 256-bits Hash must have at least 16/targetBit zeroes in front of it const targetBit = 16
-
-
Run()
- Concatenate the properties of the Block into a byte array
- Generate hash
- If the hash is valid, the result is returned
-
func (proofOfWork *ProofOfWork) Run() ([]byte, int64) { var hashInt big.Int // Store our newly generated hash value var hash [32]byte for nonce := 0; ; nonce++ { // 1. prepare Data dataBytes := proofOfWork.prepareData(nonce) // 2. Generate hash hash = sha256.Sum256(dataBytes) fmt.Printf("\r%x", hash) // 2.2. Store in HashInt hashInt.SetBytes(hash[:]) // 3. Checking the Validity of generate hash /* func (x *big.Int) Cmp(y *big.Int) (r int) Cmp compares x and y and returns: -1 if x < y 0 if x == y +1 if x > y */ if proofOfWork.Target.Cmp(&hashInt) == 1 { fmt.Println() return hash[:], int64(nonce) } } } // Concatenate the properties of the Block into a byte array. func (pow *ProofOfWork) prepareData(nonce int) []byte { data := bytes.Join( [][]byte{ pow.Block.PrevBlockHash, pow.Block.Data, IntToHex(pow.Block.Timestamp), IntToHex(int64(pow.Block.Height)), IntToHex(int64(targetBit)), IntToHex(int64(nonce)), }, []byte{}, ) return data }
-
Create a new proof of work object
func NewProofOfWork(Block *Block) *ProofOfWork { /* target two 0 0000 0001 Shift left(8-2 =6) bit 0100 0000 =64 0010 0000 =32 < Just move it two places to the left ,32 */ // 1.Create a taget with an initial value of 1 target := big.NewInt(1) // 2.Shift 256-target Bit to the left target = target.Lsh(target, 256-targetBit) return &ProofOfWork{Block: Block, Target: target} }
-
Verify that the hash is valid
// Determine whether the generated hash is preceded by Target zeros func (proofOfWork *ProofOfWork) IsVaild() bool { /* proofOfWork.Block.Hash proofOfWork.Target */ var hashInt big.Int hashInt.SetBytes(proofOfWork.Block.Hash) /* func (x *big.Int) Cmp(y *big.Int) (r int) mp compares x and y and returns: -1 if x < y 0 if x == y +1 if x > y */ return proofOfWork.Target.Cmp(&hashInt) == 1 } func main() { block1 := block.NewBlock("text", 1, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) fmt.Println("block.nonce=", block1.Nonce) fmt.Println("block.hash =", block1.Hash) // The upper block has been verified, quick verification pow := block.NewProofOfWork(block1) fmt.Println("is vaied", pow.IsVaild()) //true }
-
To store blocks on disk, serialization is needed
-
block.go
-
// use "encoding/gob" // Serialize the block into a byte array func (block *Block) Serialize() []byte { var result bytes.Buffer encoder := gob.NewEncoder(&result) err := encoder.Encode(block) if err != nil { log.Panic(err) } return result.Bytes() } // Deserializing the byte array returns the block structure func DeSerializeBlock(blockBytes []byte) *Block { var block Block decoder := gob.NewDecoder(bytes.NewReader(blockBytes)) err := decoder.Decode(&block) if err != nil { log.Panic(err) } return &block }
-
"github.com/boltdb/bolt"
-
Introduction
-
CreateTable
-
package main import ( "github.com/boltdb/bolt") func main() { // Open the my.db data file in your current directory. // It will be created if it doesn't exist. db, err := bolt.Open("my.db", 0600, nil) if err != nil { log.Fatal(err) } err = db.Update(func(tx *bolt.Tx) error { // 1. create BlockBucket Table b, err := tx.CreateBucket([]byte("BlockBucket")) if err != nil {return fmt.Errorf("create bucket error,%s", err)} // 2. Putting data into a table, in key-value pairs if b != nil { err := b.Put([]byte("1"), []byte("Send 100 to 2")) if err != nil {log.Panic("Save data to bucket error", err)} } return nil }) defer db.Close() }
-
GetTable
-
func main() { // Open the my.db data file in your current directory. // It will be created if it doesn't exist. db, err := bolt.Open("my.db", 0600, nil) if err != nil { log.Fatal(err) } err = db.Update(func(tx *bolt.Tx) error { // 1. Get BlockBucket Table b := tx.Bucket([]byte("BlockBucket")) // 2. Putting data into a table, in key-value pairs if b != nil { err := b.Put([]byte("1"), []byte("Send 100 to 2")) if err != nil {log.Panic("Save data to bucket error", err)} } return nil }) defer db.Close() }
-
-
ViewTable
-
// ViewTable err = db.View(func(tx *bolt.Tx) error { b := tx.Bucket([]byte("BlockBucket")) if b != nil { key1 := b.Get([]byte("1")) fmt.Println(key1) } return nil })
-
-
-
Persistence store
-
Blockchain uses db object, just need to add db object to blockchain property.
-
Modify Blockchain Struct
-
const dbName = "blockchain.db" const blockTableName = "block" type Blockchain struct { Tip []byte // The hash of the latest block DB *bolt.DB // Database }
-
-
Modify CreateBlockchainWithGenesisBlock() Function
-
func CreateBlockchainWithGenesisBlock() *Blockchain { var blockHash []byte // Create or open the database db, err := bolt.Open(dbName, 0600, nil) if err != nil { log.Fatal(err) } defer db.Close() err = db.Update(func(tx *bolt.Tx) error { b, err := tx.CreateBucket([]byte(blockTableName)) if err != nil { log.Panic(err) } //The table exists if b != nil { // Create GenisisBlock GenesisBlock := CreateGenesisBlock("Genesis block Data...") // Store the Genesis block in a table err := b.Put(GenesisBlock.Hash, GenesisBlock.Serialize()) if err != nil { log.Panic(err) } // Store the hash of the latest block err = b.Put([]byte("TipBlockHash"), GenesisBlock.Hash) if err != nil { log.Panic(err) } blockHash = GenesisBlock.Hash } return nil }) return &Blockchain{blockHash, db} }
-
-
func (blockchain *Blockchain) AddBlockToBlockchain(data string) { blockchain.DB.Update(func(tx *bolt.Tx) error { // 1. Get Bucket b := tx.Bucket([]byte(blockTableName)) // 2. Create New Block if b != nil { // 1. **Get The Latest Block** TipBlockBytes := b.Get(blockchain.Tip) // 2. DeSerialize TipBlock := DeSerializeBlock(TipBlockBytes) // 3. Create New Block newBlock := NewBlock(data, TipBlock.Height+1, TipBlock.Hash) // 4. Serialize New Block ,place it in the obtained table err := b.Put(newBlock.Hash, newBlock.Serialize()) if err != nil { log.Panic(err) } // Store the hash of the latest block err = b.Put([]byte("TipBlockHash"), newBlock.Hash) if err != nil { log.Panic(err) } } return nil }) }
- Iterate over all blocks to output information
func (blockchain *Blockchain) PrintBlockchain() { var block *Block var currentHash []byte = blockchain.Tip for { err := blockchain.DB.Update(func(tx *bolt.Tx) error { // 1.Get Bucket b := tx.Bucket([]byte(blockTableName)) if b != nil { // Gets the byte array of the current block blockBytes := b.Get(currentHash) // DeSerializeBlock block = DeSerializeBlock(blockBytes) fmt.Printf("Height:%d\n", block.Height) fmt.Printf("PrevBlockHash:%x\n", block.PrevBlockHash) fmt.Printf("Data:%s\n", block.Data) //fmt.Printf("Timestamp:%s\n", time.Unix(block.Timestamp, 0).Format("2006-01-02 03:04:05 PM")) fmt.Printf("Timestamp:%s\n", time.Unix(block.Timestamp, 0).Format("2006-01-02 15:04:05 PM")) fmt.Printf("Hash:%x\n", block.Hash) fmt.Printf("Nonce:%d\n\n", block.Nonce) } return nil }) if err != nil { log.Panic(err) } var hashInt big.Int hashInt.SetBytes(block.PrevBlockHash) if big.NewInt(0).Cmp(&hashInt) == 0 { break } // Iterate currentHash = block.PrevBlockHash } }
Write an iterator that optimizes the above code to reduce repetition
-
Iterator Struct
-
type BlockchainIterator struct { CurrentHash []byte // The latest Block Hash DB *bolt.DB // DB } // Get the next block func (blockchainIterator *BlockchainIterator) NextPrevBlock() *Block { var currentBlock *Block err := blockchainIterator.DB.View(func(tx *bolt.Tx) error { b := tx.Bucket([]byte(blockTableName)) if b != nil { currentBlockBytes := b.Get(blockchainIterator.CurrentHash) // The block corresponding to the current iterator Hash is retrieved currentBlock = DeSerializeBlock(currentBlockBytes) blockchainIterator.CurrentHash = currentBlock.PrevBlockHash } return nil }) if err != nil { log.Panic(err) } return currentBlock } // Get the blockchain iterator object func (blockchain *Blockchain) Iterator() *BlockchainIterator { return &BlockchainIterator{blockchain.Tip, blockchain.DB} }
-
Modify PrintBlockchain()
-
func (blockchain *Blockchain) PrintBlockchain() { blockchainIterator := blockchain.Iterator() for { block := blockchainIterator.NextPrevBlock() fmt.Printf("Height:%d\n", block.Height) fmt.Printf("PrevBlockHash:%x\n", block.PrevBlockHash) fmt.Printf("Data:%s\n", block.Data) fmt.Printf("Timestamp:%s\n", time.Unix(block.Timestamp, 0).Format("2006-01-02 15:04:05 PM")) fmt.Printf("Hash:%x\n", block.Hash) fmt.Printf("Nonce:%d\n\n", block.Nonce) var hashInt big.Int hashInt.SetBytes(block.PrevBlockHash) if big.NewInt(0).Cmp(&hashInt) == 0 { break } } }
-
-
Input
- main.exe addBlock -data "block1"
-
// main.exe addBlock -data "block1" func main() { args := os.Args fmt.Printf("%v\n", args) // [D:\dev\main.exe addblock -data block1] fmt.Printf("%v\n", args[1]) // addblock fmt.Printf("%v\n", args[2]) // -data fmt.Printf("%v\n", args[2:]) // [-data block1] }
-
input
-
flagString := flag.String("printchain", "string", "Print out all the block information") flagInt := flag.Int("number", 5, "Printing an integer") flagBool := flag.Bool("open", false, "Judging true and false") flag.Parse() fmt.Printf("%s\n", *flagString) // string fmt.Printf("%d\n", *flagInt) // 5 fmt.Printf("%v\n", *flagBool) // false
Cli Struct
type Cli struct{}Cli function
-
Check OsArgs Valid
-
func isVaildArgs() { if len(os.Args) < 2 { printUsage() os.Exit(1) } }
-
-
Print Cli Cmd
-
func printUsage() { fmt.Println("Usage:") fmt.Println("\t createBlockchain -address address") fmt.Println("\t send -from FROM -to TO -amount AMOUNT -----transaction") fmt.Println("\t printchain -----print block chain info") fmt.Println("\t getbalance -address ----get address balance") }
-
-
func (cli *Cli) Run() { isVaildArgs() // 1.printchain printchainCMD := flag.NewFlagSet("printchain", flag.ExitOnError) // 2.send //.\main.exe send -from '[\"address1","address2\"]' -to '[\"address3\",\"address4\"]' -amount '[\"2\",\"3\"]' sendBlockCMD := flag.NewFlagSet("send", flag.ExitOnError) flagFrom := sendBlockCMD.String("from", "", "from address....") flagTo := sendBlockCMD.String("to", "", "to address....") flagAmount := sendBlockCMD.String("amount", "", "amount....") //3.createblockchain createBlockChainCMD := flag.NewFlagSet("createBlockchain", flag.ExitOnError) createBlockChainWithAddress := createBlockChainCMD.String("address", "", "Genesis Block Address....") //4.getbalance getbalanceCMD := flag.NewFlagSet("getbalance", flag.ExitOnError) getbalanceCMDWithAddress := getbalanceCMD.String("address", "", "Get Address Amount") switch os.Args[1] { case "send": err := sendBlockCMD.Parse(os.Args[2:]) if err != nil { log.Panic(err) } case "printchain": err := printchainCMD.Parse(os.Args[2:]) if err != nil { log.Panic(err) } case "createBlockchain": err := createBlockChainCMD.Parse(os.Args[2:]) if err != nil { log.Panic(err) } case "getbalance": err := getbalanceCMD.Parse(os.Args[2:]) if err != nil { log.Panic(err) } default: printUsage() os.Exit(1) } if createBlockChainCMD.Parsed() { if *createBlockChainWithAddress == "" { fmt.Println("Address == "") printUsage() os.Exit(1) } cli.CreateGenesisBlockchain(*createBlockChainWithAddress) } if sendBlockCMD.Parsed() { if *flagFrom == "" || *flagTo == "" || *flagAmount == "" { printUsage() os.Exit(1) } from := block.JSONToArray(*flagFrom) to := block.JSONToArray(*flagTo) amount := block.JSONToArray(*flagAmount) cli.send(from, to, amount) } if printchainCMD.Parsed() { cli.Printchain() } if getbalanceCMD.Parsed() { if *getbalanceCMDWithAddress == "" { fmt.Println("Address == "") printUsage() os.Exit(1) } cli.getBalance(*getbalanceCMDWithAddress) } }
-
Create Genesis Blockchain
-
func (cli *Cli) CreateGenesisBlockchain(address string) { blockchain := block.CreateBlockchainWithGenesisBlock(address) defer blockchain.DB.Close() }
-
// creates a blockchain with a genesis block func CreateBlockchainWithGenesisBlock(genesisBlockAddress string) *Blockchain { // Exit if the database exists if DbExists() { fmt.Println("Genesis block already exists") os.Exit(1) return nil } // Create or open the database db, err := bolt.Open(dbName, 0600, nil) if err != nil { log.Fatal(err) } // Close the database when the function exits defer db.Close() var GenesisBlockHash []byte err = db.Update(func(tx *bolt.Tx) error { // Create a bucket b, err := tx.CreateBucket([]byte(blockTableName)) if err != nil { log.Panic(err) } // Bucket already exists if b != nil { // Create the GenesisBlock // Create a Coinbase Transaction txCoinbase := NewCoinbaseTransaction(genesisBlockAddress) GenesisBlock := CreateGenesisBlock([]*Transaction{txCoinbase}) // Store the genesis block in the bucket err := b.Put(GenesisBlock.Hash, GenesisBlock.Serialize()) if err != nil { log.Panic(err) } // Store the hash of the latest block err = b.Put([]byte("TipBlockHash"), GenesisBlock.Hash) if err != nil { log.Panic(err) } // Use to Return GenesisBlockHash = GenesisBlock.Hash } return nil }) if err != nil { log.Panic(err) } return &Blockchain{GenesisBlockHash, db} }
-
-
Print Block Info
-
func (cli *Cli) Printchain() { if !block.DbExists() { fmt.Println("Database does not exist") os.Exit(1) return } blockchain := block.GetBlockObject() defer blockchain.DB.Close() blockchain.PrintBlockchain() } -
// PrintBlockchain prints information for all blocks in the blockchain func (blockchain *Blockchain) PrintBlockchain() { // Initialize the iterator to start from the latest block blockchainIterator := blockchain.Iterator() // Iterate over all blocks in the blockchain for { // Get the next previous block in the chain block := blockchainIterator.NextPrevBlock() // Print the block header information fmt.Printf("---------------Block Height %d------------------\n", block.Height) fmt.Printf("PrevBlockHash: %x\n", block.PrevBlockHash) fmt.Printf("Timestamp: %s\n", time.Unix(block.Timestamp, 0).Format("2006-01-02 15:04:05 PM")) fmt.Printf("Hash: %x\n", block.Hash) fmt.Printf("Nonce: %d\n", block.Nonce) // Print the transactions in the block fmt.Printf("---------------Block %d - Txs: %v---------------\n", block.Height, len(block.Txs)) for _, tx := range block.Txs { fmt.Printf("Txs: tx.hash=%x\n", tx.TxHash) fmt.Printf("Vins → ") for _, in := range tx.Vins { fmt.Printf("in.TxHash: %x, Signature: %x, (in.Vout Index): %d\n", in.TxHash, in.ScriptSig, in.Vout) } fmt.Printf("Vouts→ \n") for i, out := range tx.Vouts { fmt.Printf("tx.%d Value: %d, ScriptPublicKey: %s\n", i, out.Value, out.ScriptPublicKey) } fmt.Printf("--------------End of Block %d - Tx Ended--------------\n", block.Height) } // If the current block's previous hash is zero, it means we've reached the genesis block if bytes.Equal(block.PrevBlockHash, []byte{}) { break } } }
-
-
What is a Blockchain Transaction
- A blockchain transaction refers to a data operation that occurs within a blockchain network, typically representing the transfer of assets or information from one address to another.
- 区块链交易是指在区块链网络中发生的一次数据操作,通常用于表示资产或信息从一个地址转移到另一个地址的过程。
- Each transaction is recorded in a block and permanently stored on the blockchain after being validated through a consensus mechanism (such as Proof of Work or Proof of Stake).
- 每个交易都会被记录在一个区块中,并通过共识机制验证后永久存储在区块链上。
- A blockchain transaction refers to a data operation that occurs within a blockchain network, typically representing the transfer of assets or information from one address to another.
-
Basic Structure of a Transaction
- A typical blockchain transaction usually contains the following key components:
- Inputs(输入)
- Outputs(输出)
- Signatures(签名)
- Transaction Fee(交易费用)
- Timestamp(时间戳)
- Other Fields
- Data field (for storing smart contract code or additional information)
- A typical blockchain transaction usually contains the following key components:
-
Transaction Workflow
- Create Transaction
- Broadcast Transaction(广播交易)
- Validate Transaction(验证交易)
- Package Transaction(打包交易)
- Confirm Transaction(确认交易)
-
UTXO Model (used by Bitcoin)
-
Each transaction consumes previous unspent outputs (UTXOs) and creates new UTXOs.
- 每笔交易会消耗之前的未花费输出(UTXO),并创建新的UTXO。
-
Advantages: Strong privacy, independent transactions.
- 优点:隐私性强,交易独立。
-
Disadvantages: Managing multiple UTXOs is complex.
- 缺点:管理多个UTXO较为复杂。
-
-
- Each address has a balance, and transactions directly modify the balance of the address.
- 每个地址都有一个余额,交易直接修改地址的余额。
- Advantages: Simple and easy to understand, suitable for smart contracts.
- 优点:简单易懂,适合智能合约。
- Disadvantages: Lower privacy.
- 缺点:隐私性较低。
- Each address has a balance, and transactions directly modify the balance of the address.
-
Smart Contract Transactions
-
In blockchains that support smart contracts (such as Ethereum), transactions can also trigger the execution of smart contracts. These transactions typically include the following:
-
Contract Address: Specifies the address of the smart contract to be called.
-
Function Call: Specifies the contract function to be executed and its parameters.
-
Gas Fee: Payment for computational resources required to execute the contract code.
-
-
在支持智能合约的区块链(如以太坊)中,交易还可以触发智能合约的执行。这类交易通常包含以下内容:
-
调用地址:指定要调用的智能合约地址。
-
函数调用:指定要执行的合约函数及其参数。
-
Gas费用:为执行合约代码支付的计算资源费用。
-
-
-
Characteristics of Transactions
- Immutability: Once a transaction is written to the blockchain, it cannot be altered.
- 不可篡改性:一旦交易被写入区块链,就无法更改。
- Transparency: All transactions are publicly visible to everyone on the network.
- 透明性:所有交易对全网公开,任何人都可以查看。
- Decentralization: Transactions are validated and recorded by a distributed network, without the need for a centralized trust institution.
- 去中心化:交易由分布式网络共同验证和记录,无需中心化的信任机构。
- Immutability: Once a transaction is written to the blockchain, it cannot be altered.
-
UTXO
- unspend transaction output
-
Transaction Struct
type Transaction struct { //1. transaction hash TxHash []byte //2. inputs Vins []*TxInput //3. outputs Vouts []*TxOutput } type TxOutput struct { Value int64 ScriptPublicKey string }
-
utxo struct
-
type UTXO struct { TxHash []byte Index int Output *TxOutput } // The balance must be greater than 0 func (utxo *UTXO) IsValid() bool { return utxo.Output.Value > 0 }
-
vin
-
type TxInput struct { // 1.transaction hash TxHash []byte // 2. store TxOutput,in Vout`s index Vout int // 3. address/Signatures ScriptSig string } // Determine which wallet address the current purchase is coming from func (TxInput *TxInput) UnlockWithAddress(address string) bool { return TxInput.ScriptSig == address }
-
-
vout
-
type TxOutput struct { Value int64 ScriptPublicKey string } func (txOutput *TxOutput) UnlockWithAddress(address string) bool { return txOutput.ScriptPublicKey == address }
-
-
-
Serialize the block into a byte array
-
func (tx *Transaction) HashTransactionSerialize() { var result bytes.Buffer encoder := gob.NewEncoder(&result) err := encoder.Encode(tx) if err != nil { log.Panic(err) } hash := sha256.Sum256(result.Bytes()) tx.TxHash = hash[:] }
-
-
Determine if this transaction is the transaction in the Genesis block
-
// Check if Coinbase trades func (tx *Transaction) IsCoinbaseTransaction() bool { return len(tx.Vins[0].TxHash) == 0 && tx.Vins[0].Vout == -1 }
-
-
There are two types of Transaction creation
-
// 1. create Transaction when creating the Genesis block func NewCoinbaseTransaction(address string) *Transaction { // Representative consumption txInput := &TxInput{[]byte{}, -1, "Genesis Data"} // Is for unspent (need to judge, had this amount of money) txOutput := &TxOutput{10, address} txCoinbase := &Transaction{[]byte{}, []*TxInput{txInput}, []*TxOutput{txOutput}} //Set hash txCoinbase.HashTransactionSerialize() return txCoinbase }
-
// 2. Transactions that occur when a money transfer is made func NewSimpleTransaction(from string, to string, amount int64, blockchain *Blockchain, txs []*Transaction) *Transaction { //1. Create a function that returns the Transaction corresponding to all unspent transaction outputs for the from address //unSpentTransactionOutputUTXO := blockchain.UnSpentTransactionsOutputWithAddress(from) // Both cases, same block // {hash1,[0,1]} // difficult block // {hash1,[0],hash2,[1]} // By a function that returns the amount of money I can spend money, SpendAbleUTXO_Dic := blockchain.FindSpendAbleUTXO(from, amount, txs) //dic {hash,[0:2],hash,[1,4]} var txIntups []*TxInput var txOutputs []*TxOutput //By a function that returns the amount of money I can spend //TxInput := &TxInput{[]byte("c2f026bb3e904c79e3be268e1fe04dcafec29f147f04dd3b896c15060c540140"), 0, from} //That's a mistake for txHash, indexArray := range SpendAbleUTXO_Dic { bytes, _ := hex.DecodeString(txHash) for _, index := range indexArray { //Create Input to consume Output TxInput := &TxInput{bytes, index, from} txIntups = append(txIntups, TxInput) //str := hex.EncodeToString(bytes) //fmt.Println("InputHash===========", str) } } //TxInput := &TxInput{bytes, 0, from} // Transfer txOutput := &TxOutput{amount, to} txOutputs = append(txOutputs, txOutput) // Make change txOutput = &TxOutput{money - amount, from} txOutputs = append(txOutputs, txOutput) txCoinbase := &Transaction{[]byte{}, txIntups, txOutputs} //Set hash txCoinbase.HashTransactionSerialize() return txCoinbase }
-
blockchain func
// Find available UTXOs when transferring money func (blockchain *Blockchain) FindSpendAbleUTXO(form string, amount int64, txs []*Transaction) (int64, map[string][]int) { // 1.Get all UTXOs UTXOs := blockchain.UnSpentTransactionsOutputWithAddress(form, txs) var SpendAbleUTXO map[string][]int = make(map[string][]int) // 2. Iterate over utxos var value int64 for _, utxo := range UTXOs { value = value + utxo.Output.Value hash := hex.EncodeToString(utxo.TxHash) SpendAbleUTXO[hash] = append(SpendAbleUTXO[hash], utxo.Index) if value >= amount { break } } if value < amount { fmt.Println(form, "'s Fund is not enough") os.Exit(1) } return value, SpendAbleUTXO }
-
Cli
func (cli *Cli) Run(){ sendBlockCMD := flag.NewFlagSet("send", flag.ExitOnError) flagFrom := sendBlockCMD.String("from", "", "From Address....") flagTo := sendBlockCMD.String("to", "", "to Address....") flagAmount := sendBlockCMD.String("amount", "", "Transfer amount....") switch os.Args[1] { case "send": err := sendBlockCMD.Parse(os.Args[2:]) if err != nil { log.Panic(err) } default: printUsage() os.Exit(1) } if sendBlockCMD.Parsed() { if *flagFrom == "" || *flagTo == "" || *flagAmount == "" { printUsage() os.Exit(1) } from := block.JSONToArray(*flagFrom) to := block.JSONToArray(*flagTo) amount := block.JSONToArray(*flagAmount) cli.send(from, to, amount) } } // SendCli func (cli *Cli) send(from []string, to []string, amount []string) { if !block.DbExists() { fmt.Println("Database does not exist") os.Exit(1) return } blockchain := block.GetBlockObject() defer blockchain.DB.Close() blockchain.MineNewBlock(from, to, amount) }
-