【区块链】SIA系统代码分析:存储证明

问题: 如何证明托管主机确实存储了某个文件。

回答:

1.       先从交易(Transaction)开始说起,一个交易包含了多个关键信息。 其中文件合同,文件合同更新和存储证明这三者使云存储成为可能。

以下是一个Transaction所用到的数据结构定义:

TransactionID   crypto.Hash  // 定义了一个交易的标识符,用来区分不同的交易

FileContractID crypto.Hash  // 交易所用到的文件合同的ID

     Transactionstruct{

          SiacoinInputs        []SiacoinInput        

          SiacoinOutputs       []SiacoinOutput       

          FileContracts         []FileContract           // 文件合同

          FileContractRevisions []FileContractRevision // 文件合同更新

          StorageProofs        []StorageProof           // 存储证明

          SiafundInputs        []SiafundInput        

          SiafundOutputs       []SiafundOutput       

          MinerFees            []Currency            

          ArbitraryData         [][]byte              

          TransactionSignatures []TransactionSignature

}

 

2.       文件合同(FileContracts)储存了租用方和托管主机间的协议,租用方同意给代存文件的托管主机付费,而托管主机在一定的时间段里为租用方代存文件。在一开始租用和托管双方均在合同里放siacoin。租用方的币用来在托管方完成合约任务后支付托管方。托管方的钱是押金,一旦托管方不能执行合约中的义务,押金将被没收。文件合同记录在区块链中,这个区块链相当于文件合同的第三方代管者。文件合同的核心是使用了文件的Merkle root hash。文件会分成大小一样的分段(segment)然后散列到一个Merkle树上。

以下是文件合同数据结构定义:

FileContract struct {

        FileSize           uint64         

        FileMerkleRoot     crypto.Hash    // 文件合同的核心

        WindowStart        BlockHeight    

        WindowEnd          BlockHeight     

        Payout             Currency       

        ValidProofOutputs  []SiacoinOutput

        MissedProofOutputs []SiacoinOutput

        UnlockHash         UnlockHash     

        RevisionNumber     uint64         

}

文件合同要求托管主机必须在” WindowStart”和”WindowEnd”期间,向区块链上提交存储证明(StorageProof),以证明自己还存储着文件。如果提交了存储证明,有效证明输出(ValidProofOutputs)被创建,托管主机获得存储的奖励;如果丢失存储证明,丢失证明输出(MissedProofOutputs)被创建,托管主机不仅无法获得奖励,而且还会失去先前交的押金。这个机制保证了托管主机有动力去持续的保存文件。

 

3.       存储证明(StorageProof)实际上是实现了文件合同,如果没有存储证明的提交,仅仅是合同创建了,也是相当于没有任何监管,并且没有存储证明,也无法证明托管主机确实存储了某个文件。存储证明提交到区块链上,对所有人都是公开的。 一个存储证明包含存储文件的一个分段,文件Merkle树上的一组散列。 这两个条件组合起来,就能够证明这个分段就是所存储文件的一部分,也就证明了这个托管主机确实存储了文件。 为了保证严谨性,文件分段是随机选择的。

以下是存储证明数据结构的定义:

        StorageProofstruct{

            ParentIDFileContractID           // 文件合同的ID

            Segment  [crypto.SegmentSize]byte // 文件分段

            HashSet  []crypto.Hash            // 散列值

        }

    目前系统定义的segmentSize的大小是64byte,这个值是权衡了区块链空间和网络带宽后定义的。

   

4.       验证存储证明是否合法相关代码实现介绍

func validStorageProofs(tx*bolt.Tx, t types.Transaction) error {

    if(build.Release == "standard" && blockHeight(tx) < 100e3)|| (build.Release == "testing" && blockHeight(tx) < 10) {

                  returnvalidStorageProofs100e3(tx, t)

    }

    for _, sp :=range t.StorageProofs {

                  //验证存储证明本身是否合法。 返回值是文件分段的index

                  segmentIndex,err := storageProofSegment(tx, sp.ParentID)

                  iferr != nil {

                                returnerr

                  }

                  // 从数据库中取一个文件合同

                  fc,err := getFileContract(tx, sp.ParentID)

                  iferr != nil {

                                returnerr

                  }

                  // 根据文件size大小,计算叶子数

                  leaves:= crypto.CalculateLeaves(fc.FileSize)

                  segmentLen:= uint64(crypto.SegmentSize)

 

                  ifsegmentIndex == leaves-1 {

                                segmentLen= fc.FileSize % crypto.SegmentSize

                  }

                  ifsegmentLen == 0 {

                                segmentLen= uint64(crypto.SegmentSize)

                  }

                  // 验证分段是否是Merkle root的一部分

                  verified:= crypto.VerifySegment(

                                sp.Segment[:segmentLen],

                                sp.HashSet,

                                leaves,

                                segmentIndex,

                                fc.FileMerkleRoot,

                  )

                  if!verified && fc.FileSize > 0 {

                                returnerrInvalidStorageProof

                  }

    }

    return nil

}

 

// 验证分段是否是Merkleroot的一部分

func VerifySegment(base []byte, hashSet []Hash,numSegments, proofIndex uint64, root Hash) bool {

    proofSet :=make([][]byte, len(hashSet)+1)

    proofSet[0] =base

    for i := rangehashSet {

                  proofSet[i+1]= hashSet[i][:]

    }

    returnmerkletree.VerifyProof(NewHash(), root[:], proofSet, proofIndex, numSegments)

}

 

// 根据存储证明里定义的值,去计算Merkleroot并和实际的对比,看存储证明是否合法

func VerifyProof(h hash.Hash, merkleRoot []byte,proofSet [][]byte, proofIndex uint64, numLeaves uint64) bool {

    if merkleRoot== nil {

                  returnfalse

    }

    if proofIndex>= numLeaves {

                  returnfalse

    }

 

    height := 0

    iflen(proofSet) <= height {

                  returnfalse

    }

    sum :=leafSum(h, proofSet[height])

    height++

 

    // 当前子树计算完成,使用完全子树算法决定下一个兄弟子树的位置。

    // "stableEnd"表示最后一个完全子树的结束标识,

    stableEnd :=proofIndex

    for {

                  // 决定子树是否完成.

                  subTreeStartIndex:= (proofIndex / (1 << uint(height))) * (1 << uint(height))

                  // 减掉1是因为开始的index是包含在内的

                  subTreeEndIndex:= subTreeStartIndex + (1 << (uint(height))) - 1             

                  ifsubTreeEndIndex >= numLeaves {

                                //If the Merkle tree does not have a leaf at index

                                //'subTreeEndIndex', then the subtree of the current height is not

                                //a complete subtree.

                                break

                  }

                  stableEnd= subTreeEndIndex

 

                  iflen(proofSet) <= height {

                                returnfalse

                  }

                  ifproofIndex-subTreeStartIndex < 1<<uint(height-1) {

                                sum= nodeSum(h, sum, proofSet[height])

                  }else {

                                sum= nodeSum(h, proofSet[height], sum)

                  }

                  height++

    }

 

    if stableEnd!= numLeaves-1 {

                  iflen(proofSet) <= height {

                                returnfalse

                  }

                  sum= nodeSum(h, sum, proofSet[height])

                  height++

    }

 

    for height< len(proofSet) {

                  sum= nodeSum(h, proofSet[height], sum)

                  height++

    }

 

    // 根据我们传入的参数计算出来的Merkle root和实际的对比,一致则返回true.

    if bytes.Compare(sum,merkleRoot) == 0 {

                  returntrue

    }

    return false

}

 

5.       总结:托管主机通过存储证明来证明它确实存储了文件。通过存储证明数据结构中的{ ParentID,Segment,HashSet }去获取相应的文件合同,分段Index,叶子数等,进而去计算Merkle root,并和实际的Merkle root值对比。看两者是否一致。从而判断存储证明是否合法。

 

       

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值