Bitcoin and Cryptocurrency Technologies 学习笔记:1.5 ScroogeCoin 一个简单的加密货币设计实现

这里介绍的是两个简单的加密货币GoofyCoin,ScroogeCoin,主要注意如何避免双花问题(double-spent)在作业需要对ScroogeCoin的交易有效性进行验证。

GroofyCoin

这里写图片描述

这种货币机制很简单,它有一个中心机构, Groofy 专门负责创建货币,而且仅有他能够创建,满足3条规则:

  • Groofy可以创建货币,通过在货币上签名
  • 任何货币的拥有者可以将其支付给其他人,通过简单的添加交易者的公钥,然后签上自己的名字
  • 任何人可以验证货币的有效性只需到达bock chain的末尾验证Groofy确实签署了他的签名。

double-spent 双花问题

这里写图片描述

如上图,同一个Alice 的硬币,他可以给另外一个人Chunk。而且chuck也能验证这是有效的。这是任何货币都需要解决的。举这个简单的例子就是为了说明这个问题。

Scrooge Coin

这个货币的例子主要是为了解决加密货币的 double-spent问题

首先他也是有一个中心机构 : Scrooge,专门负责创建货币和验证交易的合法性。因此有两种类型的交易:

Create Coin
这里写图片描述

这里做了一点优化是可以将多个硬币的创建放在一次交易信息中,每一个硬币后都有接受者的公钥。在实现中可以将transID用当前交易的hash来表示。

pay Coin

这里写图片描述

这种交易也是合并了很多同时的交易,消费ID是说当前硬币一定在之前的某次交易中出现,且附有本次消费者的公钥
这里写图片描述

类似于下图,也就是我们能通过某种手段经过消费ID找到之前的消费记录,也就是消费者的公钥 p k ,这个公钥是很有用的,因为每次交易都要要求有所有消费者的签名,而他们的公钥可以作为验证。

为了避免双花问题,这里Scrooge 做了一个未消费硬币的池子 UTXOPool 这个在后面的实验细节中有。总的来说,每次消费是有效的,需要满足以下一些规则:

这里写图片描述

  • 硬币在之前的交易中创建过
  • 不能有双花
  • 输入(消费者)的总金额大于等于输出(得到硬币的人)(大于意味着 剩余价值被SCrooge这个中心机构拿走了)
  • 本次交易被所有人签名

实现

下面讲讲他的实现

你将作业下载下来,你会发现有5个类:

  • Crypto.java 应用了java安全中自带的验证签名机制,不用管
  • Transaction.java: 每次交易的设计都在这里
    主要有如下几个数据
    private byte[] hash;//交易的ID本次交易所有数据的Hash
    private ArrayList<Input> inputs;//消费者信息,Input是在Transction中定义的,下同
    private ArrayList<Output> outputs;//接受者信息
  • Trancsaction.Input,里面包含3个field
        /** hash of the Transaction whose output is being used */
        public byte[] prevTxHash;//指向前一次交易的hash值
        /** used output's index in the previous transaction */
        public int outputIndex;//所指前一次交易中输出的第几项,就是outputs中的index
        /** the signature produced to check validity */
        public byte[] signature;//他对本次交易的签名,具体签名的的那些信息可见文件中的详细函数
  • Transaction.output
        /** value in bitcoins of the output */
        public double value;
        /** the address or public key of the recipient */
        public PublicKey address;//可用来验证签名

具体Scrooge是怎么通过input找到,Input的公钥来验证的呢,这个就是 UTXOPool 的功能了,这里面存了一个映射,记录当前还没有被所有者消费的硬币以及他的来源

  • UTXOPool.java
private HashMap<UTXO, Transaction.Output> H;//UTXO就是用Input的hash和index做的可hash和比较的数据结构。通过这个Pool就能找到对应的OUtput了

作业让我们实现的是两个函数

TxHandler

isValidTx

理清上面的结构之后解决这个问题已经不难了,逐一检查,就行,代码中已经写得很清楚了

 /**
     * @return true if:
     * (1) all outputs claimed by {@code tx} are in the current UTXO pool, 
     * (2) the signatures on each input of {@code tx} are valid, 
     * (3) no UTXO is claimed multiple times by {@code tx},
     * (4) all of {@code tx}s output values are non-negative, and
     * (5) the sum of {@code tx}s input values is greater than or equal to the sum of its output
     *     values; and false otherwise.
     */
    public boolean isValidTx(Transaction tx) {
        // IMPLEMENT THIS
        double sumOfOut =0;
        double sumOfIn = 0;
        Set<UTXO> used = new HashSet<>();
        for(int i=0 ; i<tx.numInputs() ; ++i){
            Transaction.Input input = tx.getInput(i);
            UTXO utxo = new UTXO(input.prevTxHash,input.outputIndex);
            if(!utxoPool.contains(utxo))return false;//check (1)
            Transaction.Output output =utxoPool.getTxOutput(utxo);// the consume coin correspond prev output coin;
            sumOfIn += output.value;//(5)
            if(!Crypto.verifySignature(output.address, tx.getRawDataToSign(i),
                    input.signature))return false;//check(2) 
            if(!used.add(utxo))return false;//check(3)
        }
        for(Transaction.Output output : tx.getOutputs()){
            if(output.value <0)return false;//check(5)
            sumOfOut+=output.value;
        }
        if(sumOfIn < sumOfOut)return false;//check(5);
        return true;
    }

handleTxs

这一个是给你组乱序的交易 TX[] 让你返回其中有效的交易,注意只检查一遍是不够的,因为新交易的加入 UTXOPool可能会使得后面又有新的合法交易出现。最简单的做法就是不动点算法,每次遍历,看看是否结果有更新,如果有继续遍历,直到没有为止,最终一定会收敛到全部加入,而且每次至少会加入一个交易,中的来说复杂度 O ( n 2 m a x ( I n . s i z e , o u t . s i z e ) )

 public Transaction[] handleTxs(Transaction[] possibleTxs) {
        // IMPLEMENT THIS
        HashSet<Transaction>txVis = new HashSet<>();
        //fixed point algorithm,iter untill no new transaction is valid
        while (true) {
            boolean updated = false;
            for(Transaction tx: possibleTxs){
                if(txVis.contains(tx))continue;
                if(isValidTx(tx)){
                    txVis.add(tx);
                    updated = true;
                    //add unspent coin
                    for(int i=0 ; i<tx.numOutputs() ; ++i){
                        UTXO utxo = new UTXO(tx.getHash(),i);
                        utxoPool.addUTXO(utxo, tx.getOutput(i));
                    }
                    //delete spent coin
                    for(int i=0 ; i<tx.numInputs() ; ++i){
                        Transaction.Input input = tx.getInput(i);
                        UTXO utxo = new UTXO(input.prevTxHash,input.outputIndex);
                        utxoPool.removeUTXO(utxo);
                    }
                }
            }
            if(!updated)break;
        };
        Transaction[] ret = new Transaction[txVis.size()];
        int idx =0;
        for(Transaction tx : txVis)
            ret[idx++] = tx;
        return ret;
    }

总结

这个货币已经很接近现实中的货币了,现实中货币能干的事情他都能干,而且有效。同时他也有现实中货币最大的缺点:中心化 有一个中心机构,完全有中心机构控制,所以通货膨胀什么的,,,你懂得 :)

猜你喜欢

转载自blog.csdn.net/dylan_frank/article/details/80211095
今日推荐