独自暗号貨幣を作ろう 第10回

第1回 第2回 第3回 第4回 第5回 第6回 第7回 第8回 第9回


<取引の有効性の検証>
取引入力への署名を実装することができたので、次は取引の有効性の検証を実装しましょう。現時点で、取引には新しい貨幣を生成する取引と貨幣を移動する取引の2種類がありますが、新しい貨幣を生成する取引に関しては特に検証することはありません(ブロックに含まれる新しい貨幣を生成する取引がブロックの報酬とブロックに含まれる取引手数料に合致するかといった検証はブロック側の職分なのでここでは実装しません)。
貨幣を移動する取引に関しては、完全な有効性の検証のためには次のような検証を行わなければなりません。
 1.署名の検証(署名が公開鍵に対応する秘密鍵によって為されたものであるか)
 2.公開鍵の検証(公開鍵の暗号学的要約値が取引入力が参照している取引出力の口座番号に合致するか)
 3.額面価格の検証(全ての取引入力の総額面価格が全ての取引出力の総額面金額以上であるか)

夫々の検証を行うメソッドを追加し、全ての検証を行うメソッドも追加します。又、取引手数料を取得するためのメソッドも追加します。夫々のメソッドに引数として渡された夫々の取引出力が正しいものであることを前提としているのは前回と同様です。

public class TransferTransaction<TxidHashType, PubKeyHashType, PubKeyType> : Transaction<TxidHashType, PubKeyHashType>
    where TxidHashType : HASHBASE
    where PubKeyHashType : HASHBASE
    where PubKeyType : DSAPUBKEYBASE
{
(中略)
    public bool VerifySignature(TransactionOutput<PubKeyHashType>[] prevTxOutputs)
    {
        if (prevTxOutputs.Length != inputs.Length)
            throw new ArgumentException("inputs_and_prev_outputs");
 
        byte[] bytesToSign = GetBytesToSign(prevTxOutputs);
 
        for (int i = 0; i < inputs.Length; i++)
            if (!inputs[i].senderPubKey.Verify(bytesToSign, inputs[i].senderSig))
                return false;
        return true;
    }
 
    public bool VerifyPubKey(TransactionOutput<PubKeyHashType>[] prevTxOutputs)
    {
        if (prevTxOutputs.Length != inputs.Length)
            throw new ArgumentException("inputs_and_prev_outputs");
 
        for (int i = 0; i < inputs.Length; i++)
            if (!(Activator.CreateInstance(typeof(PubKeyHashType), inputs[i].senderPubKey.pubKey) as PubKeyHashType).Equals(prevTxOutputs[i].receiverPubKeyHash))
                return false;
        return true;
    }
 
    public bool VerifyAmount(TransactionOutput<PubKeyHashType>[] prevTxOutputs)
    {
        if (prevTxOutputs.Length != inputs.Length)
            throw new ArgumentException("inputs_and_prev_outputs");
 
        return GetFee(prevTxOutputs).rawAmount >= 0;
    }
 
    public bool VerifyAll(TransactionOutput<PubKeyHashType>[] prevTxOutputs)
    {
        if (prevTxOutputs.Length != inputs.Length)
            throw new ArgumentException("inputs_and_prev_outputs");
 
        if (Version == 0)
            return VerifySignature(prevTxOutputs) && VerifyPubKey(prevTxOutputs) && VerifyAmount(prevTxOutputs);
        else
            throw new NotSupportedException("transfer_tx_verify_all");
    }
 
    public CurrencyUnit GetFee(TransactionOutput<PubKeyHashType>[] prevTxOutputs)
    {
        if (prevTxOutputs.Length != inputs.Length)
            throw new ArgumentException("inputs_and_prev_outputs");
 
        long totalPrevOutputs = 0;
        for (int i = 0; i < prevTxOutputs.Length; i++)
            totalPrevOutputs += prevTxOutputs[i].amount.rawAmount;
        long totalOutpus = 0;
        for (int i = 0; i < outputs.Length; i++)
            totalOutpus += outputs[i].amount.rawAmount;
 
        return new CurrencyUnit(totalPrevOutputs - totalOutpus);
    }
}