区块链存证的技术实现
众所周知,区块链具有时间戳特性和不可篡改特性,这两个特性就用于数据的存证,这是区块链诞生除了CryptoCurrency之外,人们最容易想到的应用。区块链存证的技术原理很简单,在用户签名和发送交易前,用户将要存证的数据(如果数据量小,而且不用担心隐私问题,可以直接存储正文,如果数据量大,则计算该数据的Hash)附加到交易中,然后再进行签名广播。记账节点在验证了交易的合法性后,将该交易打包到区块中,并在区块中附加上时间戳信息。
存证的区块链写入
目前主流的区块链都具有将数据附加到交易中的特性。以比特币为例,其支持在Output中使用OP_RETURN来存放数据,不过由于比特币网络比较拥堵,所以比特币网络接受OP_RETURN存放的数据很有限,最多存放40个字节,后来又有版本调整,改成了80字节,总之是非常非常小,不过用于存放一个Hash值32字节还是足够了。以太坊是在交易中支持inputdata字段,如果以太坊交易的接受地址是外部地址,那么inputdata就是用于存证的数据内容,该数据内容就可以很长,而不是80字节的限制,可以是一整篇文章、公开信等。PalletOne的交易是由不同类型的Message组成,PaymentPayload负责Token的流转,DataPayload负责数据存放。当我们需要在PalletOne存证时,只需要创建具有PaymentPayload和DataPayload的交易,然后进行签名,广播。
使用区块链进行数据存证,我们可以得到以下几点共识:
- 在存证所在区块的时间戳之前,该数据已经存在(区块的时间戳特性)
- 拿到数据的内容,可以判断该数据在存证后是否更改(Hash函数的特性)
- 存证数据是由持有某私钥的人存证的,该人不可抵赖,别人也不可冒充(区块链的不可篡改和数字签名的特性)
虽然区块链存证具有以上的优势,但是比特币、以太坊等毕竟不是为数据存证而设计的链,所以在存证上只有一个字段,对索引、扩展、引用的支持都没有,需要第三方应用来实现。
存证安全性的加强
基于区块链的存证,主要是利用了区块链的不可篡改特性和时间戳特性,但是我们怎么能保证区块链的不可篡改和时间戳是正确的呢?
区块链的二次存证
如果存证数据是保存在一个私有链、联盟链或者是DPOS公链上,那么从理论上来说,该区块链没有完全的去中心化,一旦联合作恶,仍然有篡改历史数据的可能。而以POW为共识算法的比特币、以太坊网络则更去中心化,更难以篡改历史数据。所以,为了进一步加强存证所在链的安全性,我们可以定期将该区块链的最新区块Hash,在比特币或者以太坊进行存证,由他们来证明本存证链没有被篡改,从而证明该链上的所有存证数据没有被联合作恶篡改,Factom就是采用了类似的方式保证了本链的数据没有被篡改的。以下图右下角是Factom进行比特币网络的二次存证的示例:
可信时间戳
区块链的时间戳是指在每个区块头上,记账节点在产块时,写入的一个时间值。这个时间值并不要求十分精确,只要在一定的范围内,其他节点也是可以接受的。以比特币为例,大约每10分钟产一个块,而且比特币网络十分拥堵,所以要用比特币进行存证,而费用给的不是很高的话,可能几个小时,甚至几天都不被打包。从发起存证交易到被最终打包花了几天的时间,时间戳的误差范围就太大了。
对于要求更精确时间的存证,我们一方面使用高性能的公链或者联盟链,另一方面可以国家认可的可信时间服务,比如:https://www.tsa.cn/ 等。我们并不需要对每个存证都请求时间戳服务,我们可以根据存证所需要精确到的时间范围,每几分钟、几小时请求一次可信时间服务,获得签名的证书,并将证书包含到区块中即可。
以下是WoSign的时间戳服务示意图:
存证的索引
首先我们说索引,加入我有一张照片,我记得之前在区块上进行了存证,也就是将这张照片文件的hash放到了区块链上,但是我并不记得是什么时候放的了,那么我怎么查询这张照片的存证结果呢?因为比特币和以太坊在底层数据结构上不支持,所以只有靠第三方的区块链浏览器,或者写个工具扫描全账本,将所有存证数据放入数据库建立索引,然后依靠数据库索引来实现存证数据的查询。
而PalletOne上进行存证数据则不需要依赖第三方工具,PalletOne在存证数据所使用的DataPayload进行了专门的存证设计,其结构如下:
type DataPayload struct { MainData []byte `json:"main_data"` ExtraData []byte `json:"extra_data"` Reference []byte `json:"reference"` }
我们只需要将需要存证的照片Hash放到MainData中,签名并广播交易。当一个全账本在收到一个新区块,并存储到本地账本时,会解析其中包含的DataPayload.MainData,并建立MainData与TxHash的索引。而TxHash本身也具有和UnitHeader之间的索引,所以我们只需要知道MainData,就可以很快的查询到该存证所在的交易Hash,所在的单元Hash、高度、时间戳等。而通过交易Hash作为Key,又可以查询到交易本身(包括存证人、存证花费的Token、其他交易信息等)。
存证的扩展
在实际存证的过程中,大多数情况下,我们不可能只存储一个Hash就完了,而是希望将该Hash所对应的文件的特征、描述、标签等作为附加信息也存证起来。比如我们在做照片的版权存证时,我们在MainData中存入照片的Hash,在ExtraData中以json格式存入了照片的名字、作者、拍摄时间、拍摄地点以及其他的描述信息,这些信息不用太长,但也对照片起到了很好的描述作用。扩展信息由于是非标准化的,存证不同的内容,其ExtraData的格式和内容也千差万别,所以并不需要建立通用的索引。
存证的引用
除了常见的对最终结果的存证,对一个数据的多个版本进行存证也是常见的需求。比如我们有个多人协同写作的文档,每个人都可以在别人完成的文档的基础上进行进一步的修改和完善,而每一次修改的发布就是一个版本,我们可以将该文档的所有版本每次在发布时都存证起来。对文档的多个版本进行存证,就要求每次存证时,指定一个引用关系,比如我们必须指定本次存证的上一个版本Hash是什么,如果没有指定就可以认为是该文档的第一个版本,这是一种链式引用关系。当然我们也可以使用另一种引用方法,即第一个版本存证时没有引用关系,接下来所有版本的存证,都指定引用为第一个版本的Hash;因为区块链本身提供了时间戳服务,所以我们可以通过时间戳对所有后续版本进行排序,而确定先后顺序。Reference字段不仅仅可以和其他存证建立引用关系,也可以和TokenID建立引用关系,我们甚至可以进一步扩展Reference字段的内容,形成引用的DAG结构,以适配更复杂的应用场景。
存证与通证的结合——溯源
对区块链和比特币有点了解的都知道,UTXO是比特币的记账模型,在一笔交易中采用input和output来表示比特币的流转情况,每个input指向另一个output。而UTXO这种链式模型,就天然适合于溯源的底层模型。
如果我们要对某个物品进行溯源跟踪,那么首先要给该物品进行唯一标识,可以给该物品贴上全球唯一的二维码/条形码,然后在区块链系统中,我们有一个与该物品唯一标识对应的Token被创造出来。接下来只要该物品流转到下一个环节,那么区块链系统中也对该Token进行一次转账,交给下一个人。
既然这里涉及到Token的转账交易,那么就可以在这次转账交易中将DataPayload也附加上去,为溯源增加更多信息。
以基于PalletOne区块链为基础的“艺溯链”为例,该区块链应用主要是实现了对艺术品、工艺品的溯源。比如玉雕的溯源。当一个玉雕大师(工作室)完成了一个玉雕作品时,玉雕大师会为作品拍照,测量、填写材质、尺寸、重量等信息,并以此信息创建一个唯一的Token。创建后的Token是在玉雕大师的地址账户下,当玉雕要投放市场时,玉雕大师将玉雕交到门店,同时将Token转移给门店的地址。门店如果希望对玉雕进行鉴定,可以将Token自己转移给自己,同时在本次交易中,把鉴定证书的照片Hash,证书链接等作为DataPayload添加到Token的交易中。最终消费者在购买玉雕时,可以查询该玉雕对应Token的流转历史和存证信息,并在购买玉雕后,由商家将玉雕的Token转移到用户的地址账户名下。这样可以增加消费者对玉雕作品的信任。
总结
基于区块链的存证,可以使用比特币网络的二次存证实现存证链的不可篡改的可信。配合国家认可的可信时间戳服务,对区块的时间戳进行可信证明,保证了从法律层面认可区块链的存证时间戳。将存证数据分为可索引的MainData,可附加更多数据的ExtraData和可以建立引用关系的ReferenceData,可以实现大部分企业级的存证需求。存证数据和Token的UTXO模型结合,以及非同质化通证的支持,可以实现溯源功能的原生支持。
在溯源的技术实现上,虽然和UTXO结合,使用非同质化Token的方式进行溯源具有底层优势,但是对于大批量的商品并不合适。比如药品的溯源,如果我们为每一盒药都创建一个对应的Token,那么在药品出厂,一卡车一卡车的运到经销商时,相当于要做几万几十万个Token的转账,这种大批量的Token转移,每一个都需要单独签名和验签,严重制约了溯源的并发数,所以Token化的溯源更适合艺术品、珠宝等高价值,但量不大的场景。
如果针对单纯的存证场景,在联盟链中,Token和Gas就不是必须的了,如果每次存证都需要Token的转移或者Gas的消耗,则严重影响并发。所以联盟链的存证,是可以去掉对Token的依赖,用户发起的交易只需要包含2条Message:DataPayload和SignaturePayload即可。当然在公链场景下,Gas仍然是必须的!