首页 > 编程语言 >区块链编程golang(五)—钱包

区块链编程golang(五)—钱包

时间:2024-08-17 14:39:42浏览次数:11  
标签:return tx err 编程 golang func byte 区块 string

 block/ block.go

package blockchain

import (
    "bytes"
    "crypto/sha256"
    "encoding/gob"
    "log"
)

type Block struct {
    Hash         []byte
    Transactions []*Transaction
    PrevHash     []byte
    Nonce        int
}

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[:]
}

func CreateBlock(txs []*Transaction, prevHash []byte) *Block {
    block := &Block{[]byte{}, txs, prevHash, 0}
    pow := NewProof(block)
    nonce, hash := pow.Run()

    block.Hash = hash[:]
    block.Nonce = nonce

    return block
}

func Genesis(coinbase *Transaction) *Block {
    return CreateBlock([]*Transaction{coinbase}, []byte{})
}

func (b *Block) Serialize() []byte {
    var res bytes.Buffer
    encoder := gob.NewEncoder(&res)

    err := encoder.Encode(b)

    Handle(err)

    return res.Bytes()
}

func Deserialize(data []byte) *Block {
    var block Block

    decoder := gob.NewDecoder(bytes.NewReader(data))

    err := decoder.Decode(&block)

    Handle(err)

    return &block
}

func Handle(err error) {
    if err != nil {
        log.Panic(err)
    }
}

 

block/ blockchain.go

package blockchain

import (
    "encoding/hex"
    "fmt"
    "os"
    "runtime"

    "github.com/dgraph-io/badger"
)

const (
    dbPath      = "./tmp/blocks"
    dbFile      = "./tmp/blocks/MANIFEST"
    genesisData = "First Transaction from Genesis"
)

type BlockChain struct {
    LastHash []byte
    Database *badger.DB
}

type BlockChainIterator struct {
    CurrentHash []byte
    Database    *badger.DB
}

func DBexists() bool {
    if _, err := os.Stat(dbFile); os.IsNotExist(err) {
        return false
    }

    return true
}

func ContinueBlockChain(address string) *BlockChain {
    if DBexists() == false {
        fmt.Println("No existing blockchain found, create one!")
        runtime.Goexit()
    }

    var lastHash []byte

    opts := badger.DefaultOptions
    opts.Dir = dbPath
    opts.ValueDir = dbPath

    db, err := badger.Open(opts)
    Handle(err)

    err = db.Update(func(txn *badger.Txn) error {
        item, err := txn.Get([]byte("lh"))
        Handle(err)
        lastHash, err = item.Value()

        return err
    })
    Handle(err)

    chain := BlockChain{lastHash, db}

    return &chain
}

func InitBlockChain(address string) *BlockChain {
    var lastHash []byte

    if DBexists() {
        fmt.Println("Blockchain already exists")
        runtime.Goexit()
    }

    opts := badger.DefaultOptions
    opts.Dir = dbPath
    opts.ValueDir = dbPath

    db, err := badger.Open(opts)
    Handle(err)

    err = db.Update(func(txn *badger.Txn) error {
        cbtx := CoinbaseTx(address, genesisData)
        genesis := Genesis(cbtx)
        fmt.Println("Genesis created")
        err = txn.Set(genesis.Hash, genesis.Serialize())
        Handle(err)
        err = txn.Set([]byte("lh"), genesis.Hash)

        lastHash = genesis.Hash

        return err

    })

    Handle(err)

    blockchain := BlockChain{lastHash, db}
    return &blockchain
}

func (chain *BlockChain) AddBlock(transactions []*Transaction) {
    var lastHash []byte

    err := chain.Database.View(func(txn *badger.Txn) error {
        item, err := txn.Get([]byte("lh"))
        Handle(err)
        lastHash, err = item.Value()

        return err
    })
    Handle(err)

    newBlock := CreateBlock(transactions, lastHash)

    err = chain.Database.Update(func(txn *badger.Txn) error {
        err := txn.Set(newBlock.Hash, newBlock.Serialize())
        Handle(err)
        err = txn.Set([]byte("lh"), newBlock.Hash)

        chain.LastHash = newBlock.Hash

        return err
    })
    Handle(err)
}

func (chain *BlockChain) Iterator() *BlockChainIterator {
    iter := &BlockChainIterator{chain.LastHash, chain.Database}

    return iter
}

func (iter *BlockChainIterator) Next() *Block {
    var block *Block

    err := iter.Database.View(func(txn *badger.Txn) error {
        item, err := txn.Get(iter.CurrentHash)
        Handle(err)
        encodedBlock, err := item.Value()
        block = Deserialize(encodedBlock)

        return err
    })
    Handle(err)

    iter.CurrentHash = block.PrevHash

    return block
}

func (chain *BlockChain) FindUnspentTransactions(address string) []Transaction {
    var unspentTxs []Transaction

    spentTXOs := make(map[string][]int)

    iter := chain.Iterator()

    for {
        block := iter.Next()

        for _, tx := range block.Transactions {
            txID := hex.EncodeToString(tx.ID)

        Outputs:
            for outIdx, out := range tx.Outputs {
                if spentTXOs[txID] != nil {
                    for _, spentOut := range spentTXOs[txID] {
                        if spentOut == outIdx {
                            continue Outputs
                        }
                    }
                }
                if out.CanBeUnlocked(address) {
                    unspentTxs = append(unspentTxs, *tx)
                }
            }
            if tx.IsCoinbase() == false {
                for _, in := range tx.Inputs {
                    if in.CanUnlock(address) {
                        inTxID := hex.EncodeToString(in.ID)
                        spentTXOs[inTxID] = append(spentTXOs[inTxID], in.Out)
                    }
                }
            }
        }

        if len(block.PrevHash) == 0 {
            break
        }
    }
    return unspentTxs
}

func (chain *BlockChain) FindUTXO(address string) []TxOutput {
    var UTXOs []TxOutput
    unspentTransactions := chain.FindUnspentTransactions(address)

    for _, tx := range unspentTransactions {
        for _, out := range tx.Outputs {
            if out.CanBeUnlocked(address) {
                UTXOs = append(UTXOs, out)
            }
        }
    }
    return UTXOs
}

func (chain *BlockChain) FindSpendableOutputs(address string, amount int) (int, map[string][]int) {
    unspentOuts := make(map[string][]int)
    unspentTxs := chain.FindUnspentTransactions(address)
    accumulated := 0

Work:
    for _, tx := range unspentTxs {
        txID := hex.EncodeToString(tx.ID)

        for outIdx, out := range tx.Outputs {
            if out.CanBeUnlocked(address) && accumulated < amount {
                accumulated += out.Value
                unspentOuts[txID] = append(unspentOuts[txID], outIdx)

                if accumulated >= amount {
                    break Work
                }
            }
        }
    }

    return accumulated, unspentOuts
}

 

block/  transcation.go

package blockchain

import (
    "bytes"
    "crypto/sha256"
    "encoding/gob"
    "encoding/hex"
    "fmt"
    "log"
)

type Transaction struct {
    ID      []byte
    Inputs  []TxInput
    Outputs []TxOutput
}

func (tx *Transaction) SetID() {
    var encoded bytes.Buffer
    var hash [32]byte

    encode := gob.NewEncoder(&encoded)
    err := encode.Encode(tx)
    Handle(err)

    hash = sha256.Sum256(encoded.Bytes())
    tx.ID = hash[:]
}

func CoinbaseTx(to, data string) *Transaction {
    if data == "" {
        data = fmt.Sprintf("Coins to %s", to)
    }

    txin := TxInput{[]byte{}, -1, data}
    txout := TxOutput{100, to}

    tx := Transaction{nil, []TxInput{txin}, []TxOutput{txout}}
    tx.SetID()

    return &tx
}

func NewTransaction(from, to string, amount int, chain *BlockChain) *Transaction {
    var inputs []TxInput
    var outputs []TxOutput

    acc, validOutputs := chain.FindSpendableOutputs(from, amount)

    if acc < amount {
        log.Panic("Error: not enough funds")
    }

    for txid, outs := range validOutputs {
        txID, err := hex.DecodeString(txid)
        Handle(err)

        for _, out := range outs {
            input := TxInput{txID, out, from}
            inputs = append(inputs, input)
        }
    }

    outputs = append(outputs, TxOutput{amount, to})

    if acc > amount {
        outputs = append(outputs, TxOutput{acc - amount, from})
    }

    tx := Transaction{nil, inputs, outputs}
    tx.SetID()

    return &tx
}

func (tx *Transaction) IsCoinbase() bool {
    return len(tx.Inputs) == 1 && len(tx.Inputs[0].ID) == 0 && tx.Inputs[0].Out == -1
}

 

block./ tx.go

package blockchain

type TxOutput struct {
    Value  int
    PubKey string
}

type TxInput struct {
    ID  []byte
    Out int
    Sig string
}

func (in *TxInput) CanUnlock(data string) bool {
    return in.Sig == data
}

func (out *TxOutput) CanBeUnlocked(data string) bool {
    return out.PubKey == data
}

 

wallet / utils.go

package wallet

import (
    "log"

    "github.com/mr-tron/base58"
)

func Base58Encode(input []byte) []byte {
    encode := base58.Encode(input)

    return []byte(encode)
}

func Base58Decode(input []byte) []byte {
    decode, err := base58.Decode(string(input[:]))
    if err != nil {
        log.Panic(err)
    }

    return decode
}

 

wallet / walllet.go

package wallet

import (
    "crypto/ecdsa"
    "crypto/elliptic"
    "crypto/rand"
    "crypto/sha256"
    "log"

    "golang.org/x/crypto/ripemd160"
)

const (
    checksumLength = 4
    version        = byte(0x00)
)

type Wallet struct {
    PrivateKey ecdsa.PrivateKey
    PublicKey  []byte
}

func (w Wallet) Address() []byte {
    pubHash := PublicKeyHash(w.PublicKey)

    versionedHash := append([]byte{version}, pubHash...)
    checksum := Checksum(versionedHash)

    fullHash := append(versionedHash, checksum...)
    address := Base58Encode(fullHash)

    return address
}

func NewKeyPair() (ecdsa.PrivateKey, []byte) {
    curve := elliptic.P256()

    private, err := ecdsa.GenerateKey(curve, rand.Reader)
    if err != nil {
        log.Panic(err)
    }

    pub := append(private.PublicKey.X.Bytes(), private.PublicKey.Y.Bytes()...)
    return *private, pub
}

func MakeWallet() *Wallet {
    private, public := NewKeyPair()
    wallet := Wallet{private, public}

    return &wallet
}

func PublicKeyHash(pubKey []byte) []byte {
    pubHash := sha256.Sum256(pubKey)

    hasher := ripemd160.New()
    _, err := hasher.Write(pubHash[:])
    if err != nil {
        log.Panic(err)
    }

    publicRipMD := hasher.Sum(nil)

    return publicRipMD
}

func Checksum(payload []byte) []byte {
    firstHash := sha256.Sum256(payload)
    secondHash := sha256.Sum256(firstHash[:])

    return secondHash[:checksumLength]
}

 

wallet / wallets.go

package wallet

import (
    "bytes"
    "crypto/elliptic"
    "encoding/gob"
    "fmt"
    "io/ioutil"
    "log"
    "os"
)

const walletFile = "./tmp/wallets.data"

type Wallets struct {
    Wallets map[string]*Wallet
}

func CreateWallets() (*Wallets, error) {
    wallets := Wallets{}
    wallets.Wallets = make(map[string]*Wallet)

    err := wallets.LoadFile()

    return &wallets, err
}

func (ws *Wallets) AddWallet() string {
    wallet := MakeWallet()
    address := fmt.Sprintf("%s", wallet.Address())

    ws.Wallets[address] = wallet

    return address
}

func (ws *Wallets) GetAllAddresses() []string {
    var addresses []string

    for address := range ws.Wallets {
        addresses = append(addresses, address)
    }

    return addresses
}

func (ws Wallets) GetWallet(address string) Wallet {
    return *ws.Wallets[address]
}

func (ws *Wallets) LoadFile() error {
    if _, err := os.Stat(walletFile); os.IsNotExist(err) {
        return err
    }

    var wallets Wallets

    fileContent, err := ioutil.ReadFile(walletFile)
    if err != nil {
        return err
    }

    gob.Register(elliptic.P256())
    decoder := gob.NewDecoder(bytes.NewReader(fileContent))
    err = decoder.Decode(&wallets)
    if err != nil {
        return err
    }

    ws.Wallets = wallets.Wallets

    return nil
}

func (ws *Wallets) SaveFile() {
    var content bytes.Buffer

    gob.Register(elliptic.P256())

    encoder := gob.NewEncoder(&content)
    err := encoder.Encode(ws)
    if err != nil {
        log.Panic(err)
    }

    err = ioutil.WriteFile(walletFile, content.Bytes(), 0644)
    if err != nil {
        log.Panic(err)
    }
}

 

main.go

package main

import (
    "os"

    "github.com/tensor-programming/golang-blockchain/cli"
)

func main() {
    defer os.Exit(0)
    cmd := cli.CommandLine{}
    cmd.Run()
}

 

go.mod 和 go.sum 文件一样

 

标签:return,tx,err,编程,golang,func,byte,区块,string
From: https://www.cnblogs.com/apenote/p/18364358

相关文章

  • Python编程常用英文单词大全!收藏别忘了!
      Python编程中常用的英文单词非常丰富,这些单词涵盖了编程的各个方面,包括基础概念、数据类型、控制结构、函数与模块、类与对象、异常处理等。以下是一些常用的英文单词及其简要说明:1.基础概念Variable(变量):用来存储和表示数据的容器。Function(函数):一段可重复使用的代码......
  • 基于gobot框架在BBB Debian运行的GoLang程序
    为了让GoLang与BBB(BeagleBoneBlack开发板)搭配使用,我们借助了gobot机器上框架(查阅“参考资料”里的链接),在BBBDebian系统上运行go程序来控制硬件,下面是我们的整个入门配置和测试记录(在PCUbuntu系统下进行):1.获取gobot源码  goget-d-ugithub.com/hybridgroup/gobot/...&......
  • 面试题:在Java中,多线程编程是常见的并发处理方式。请简述Java中实现多线程的几种主要方
    面试题:在Java中,多线程编程是常见的并发处理方式。请简述Java中实现多线程的几种主要方式,并解释每种方式的基本思想。更多关于多线程编程的深入解析、面试技巧、以及实战项目源码,手机浏览器即可访问面霸宝典【全拼音】.com,这里不仅可以优化你的简历,还能进行模拟面试,获取最新最......
  • 区块链编程go(四)-交易
     part1:packageblockchainimport("bytes""crypto/sha256""encoding/gob""encoding/hex""fmt""log")typeTransactionstruct{ID[]byteInputs[]TxIn......
  • 信息学奥赛一本通编程启蒙题解(3011~3015)
    前言Hello大家好,我是文宇.正文3011#include<iostream>usingnamespacestd;intmain(){ inta,b,s; a=880; b=500; s=a*b; cout<<s; return0;}注:没有输入的都可以直接输出.3012#include<iostream>usingnamespacestd;inta,b,t;intmain(){ a=10;b=20......
  • 信息学奥赛一本通编程启蒙题解(3021~3025)
    前言hello大家好,我是文宇。正文3021#include<iostream>usingnamespacestd;inta,b,c,d;intmain(){ cin>>a>>b>>c>>d; cout<<a+b+c+d; return0;}3022#include<bits/stdc++.h>usingnamespacestd;intmain(){ inta,b,c; ......
  • 【嵌入式开发之网络编程】互联网的基本概念
    计算机网络的定义计算机网络的精确定义并未统一:以功能完善的网络软件及通信协议实现资源共享和信息传递的系统。以传输信息为基本目的,用通信线路和通信设备将多个计算机连接起来的计算机系统的集合。计算机网络的分类 按照网络的作用范围进行分类类别作用范围或距离广域......
  • Golang使用Option设计模式优雅处理可选参数
    go语言不像其他语言函数的参数可以设置默认值以下是参考第三方库的写法packagemainimport"fmt"typeUserstruct{namestringageintidint}//Option代表可选参数typeOptionfunc(foo*User)//WithName为name字段提供一个设置器funcWithName(name......
  • 打造编程学习的高效笔记系统
    在编程学习的道路上,笔记不仅仅是知识的简单记录,更是我们理解、吸收和应用知识的重要工具。一个高效的笔记系统能够帮助我们更好地组织思路、加深记忆,并在需要时迅速找到所需的信息。那么,如何才能打造这样一个既实用又高效的编程学习笔记系统呢?目录一、笔记工具选择二、笔......
  • C++编程:内存栅栏(Memory Barrier)详解及在多线程编程中的应用
    文章目录0.引言1.什么是内存栅栏?2.为什么需要内存栅栏?本质原因是什么?2.1编译器优化2.2CPU乱序执行3.ARM64和x86架构下的内存栅栏差异3.1x86架构3.2ARM64架构4.代码示例4.1代码解析4.2memory_order_release和memory_order_acquire解释4.3为什么是“releas......