零知识证明学习

文档,测试

claimPK.zok

import "hashes/sha256/512bitPadded.zok" as sha256;
import "utils/pack/u32/nonStrictUnpack256.zok" as unpack256;
import "utils/pack/u32/unpack128.zok" as unpack128;
import "utils/pack/u32/pack128.zok" as pack128;
import "hashes/sha256/256bitPadded.zok" as sha256duan;
 
def toz(field[2] a, field pk, field s) -> field[2] {
    u32[4] una0 = unpack128(a[0]);
 
    u32[4] una1 = unpack128(a[1]);
    u32[8] unpacka = [...una0, ...una1];
    u32[4] unpk = unpack128(pk);
    u32[4] uns = unpack128(s);
    u32[8] unpackb = [...unpk, ...uns];
    u32[8] h = sha256(unpacka,unpackb);
    field hp1 = pack128(h[..4]);
    field hp2 = pack128(h[4..]);
    return [hp1,hp2];
}
def main(field[2] a, field[2] z, private field pk, private field s) -> field {
    field[2] mut zp = toz(a, pk, s);
    assert(zp[0] == z[0]);
    assert(zp[1] == z[1]);
    return 1;
}

零知识证明学习_第1张图片

该函数toz接收三个参数 apks,其中a是长度为2的field数组,pks分别是field类型的私有参数。函数的返回类型是长度为2的field数组。

函数toz的目的是将输入的参数进行一系列处理后,返回一个长度为2的field数组。具体的处理步骤如下:

  • a数组的两个元素拆解为两个128位的field数组una0una1
  • 将两个128位的field数组合并成一个256位的field数组unpacka
  • pks两个私有参数拆解为两个128位的field数组unpkuns
  • 将两个128位的field数组合并成一个256位的field数组unpackb
  • 调用sha256函数对合并后的unpackaunpackb进行SHA256哈希运算,得到256位的哈希值h
  • 将256位的哈希值h拆分为两个128位的field数组,并分别赋值给hp1hp2
  • 最后,将hp1hp2组成一个长度为2的field数组,并作为函数的返回值返回。

该函数main接收四个参数azpks,其中az都是长度为2的field数组,pks分别是field类型的私有参数。函数的返回类型是field

函数main的目的是验证零知识证明。它通过调用函数toz来计算zp,然后将zp的两个元素分别与输入参数z的两个元素进行比较。如果zp[0]等于z[0]并且zp[1]等于z[1],则返回1,表示验证成功,否则返回验证失败。

responseSK.zok

import "hashes/sha256/512bitPadded" as sha256;
import "utils/pack/u32/nonStrictUnpack256" as unpack256;
import "utils/pack/u32/unpack128.zok" as unpack128;
import "utils/pack/u32/pack128.zok" as pack128;
import "hashes/sha256/256bitPadded" as sha256duan;
 
def toz(field[2] a, field pk, field s) -> field[2] {
    u32[4] una0 = unpack128(a[0]);
    u32[4] una1 = unpack128(a[1]);
    u32[8] unpacka = [...una0, ...una1];
    u32[4] unpk = unpack128(pk);
    u32[4] uns = unpack128(s);
    u32[8] unpackb = [...unpk, ...uns];
    u32[8] h = sha256(unpacka,unpackb);
    field hp1 = pack128(h[..4]);
    field hp2 = pack128(h[4..]);
    return [hp1,hp2];
}
def sktopk(field[2] sk) -> field[2] {
    u32[4] unsk0 = unpack128(sk[0]);
    u32[4] unsk1 = unpack128(sk[1]);
    u32[8] unpacksk = [...unsk0, ...unsk1];
    u32[8] h = sha256duan(unpacksk);
    field hp1 = pack128(h[..4]);
    field hp2 = pack128(h[4..]);
    return [hp1,hp2];   
}
def ton(field[2] sk, field s) -> field[2] {
    u32[4] unsk0 = unpack128(sk[0]);
    u32[4] unsk1 = unpack128(sk[1]);
    u32[8] unpackna = [...[0; 4], ...unsk0];
    u32[4] uns = unpack128(s);
    u32[8] unpacknb = [...unsk1, ...uns];
    u32[8] h = sha256(unpackna, unpacknb);
    field hp1 = pack128(h[..4]);
    field hp2 = pack128(h[4..]);
    return [hp1,hp2];
}
def main(field[2] a, field[2] z, field[2] ne, private field[2] sk, private field s, private field e ) -> field {
    field[2] pkp = sktopk(sk);
    field pk = pkp[0];
    field[2] zp = toz(a, pk, s);
     assert(zp[0] == z[0]);
     assert(zp[1] == z[1]);
    field[2] nep = ton(sk, e);
     assert(nep[0] == ne[0]);
     assert(nep[1] == ne[1]);
    return 1;
}

这段代码使用了 ZoKrates 语言编写,实现了一个零知识证明系统。主要包含三个函数 tozsktopkton,以及一个主函数 main

  1. 函数 toz

    • 输入:field[2] afield pkfield s
    • 输出:(field[2])
    • 逻辑:该函数将输入的参数 apks 进行一系列处理,然后返回一个长度为 2 的 field 数组作为结果。
    • 具体逻辑:
      • a 数组的两个元素拆解为两个 128 位的 field 数组 una0una1
      • 将两个 128 位的 field 数组合并成一个 256 位的 field 数组 unpacka
      • pks 两个参数拆解为两个 128 位的 field 数组 unpkuns
      • 将两个 128 位的 field 数组合并成一个 256 位的 field 数组 unpackb
      • 调用 sha256 函数对合并后的 unpackaunpackb 进行 SHA256 哈希运算,得到一个 256 位的哈希值 h
      • 将 256 位的哈希值 h 拆分为两个 128 位的 field 数组,并分别赋值给 hp1hp2
      • 最后,将 hp1hp2 组成一个长度为 2 的 field 数组,并作为函数的返回值返回。
  2. 函数 sktopk

    • 输入:field[2] sk
    • 输出:(field[2])
    • 逻辑:该函数将输入的参数 sk 进行一系列处理,然后返回一个长度为 2 的 field 数组作为结果。
    • 具体逻辑:
      • sk 数组的两个元素拆解为两个 128 位的 field 数组 unsk0unsk1
      • 将两个 128 位的 field 数组合并成一个 256 位的 field 数组 unpacksk
      • 调用 sha256duan 函数对合并后的 unpacksk 进行 SHA256 哈希运算,得到一个 256 位的哈希值 h
      • 将 256 位的哈希值 h 拆分为两个 128 位的 field 数组,并分别赋值给 hp1hp2
      • 最后,将 hp1hp2 组成一个长度为 2 的 field 数组,并作为函数的返回值返回。
  3. 函数 ton

    • 输入:field[2] skfield s
    • 输出:(field[2])
    • 逻辑:该函数将输入的参数 sks 进行一系列处理,然后返回一个长度为 2 的 field 数组作为结果。
    • 具体逻辑:
      • sk 数组的两个元素拆解为两个 128 位的 field 数组 unsk0unsk1
      • [0; 128]unsk0 两个 128 位的 field 数组合并成一个 256 位的 field 数组 unpackna
      • s 参数拆解为一个 128 位的 field 数组 uns
      • unsk1uns 两个 128 位的 field 数组合并成一个 256 位的 field 数组 unpacknb
      • 调用 sha256 函数对合并后的 unpacknaunpacknb 进行 SHA256 哈希运算,得到一个 256 位的哈希值 h
      • 将 256 位的哈希值 h 拆分为两个 128 位的 field 数组,并分别赋值给 hp1hp2
      • 最后,将 hp1hp2 组成一个长度为 2 的 field 数组,并作为函数的返回值返回。
  4. 函数 main

    • 输入:field[2] afield[2] zfield[2] ne,私有输入 field[2] sk,私有输入 field s,私有输入 field e
    • 输出:(field)
    • 逻辑:该函数是主函数,用于验证零知识证明。
    • 具体逻辑:
      • 调用 sktopk 函数,获取 sk 经过处理后的公钥 pk
      • 调用 toz 函数,计算输入参数 apks 的处理结果 zp
      • 验证 zp 的两个元素是否与输入参数 z 的两个元素相等。
      • 调用 ton 函数,计算输入参数 ske 的处理结果 nep
      • 验证 nep 的两个元素是否与输入参数 ne 的两个元素相等。
      • 如果所有验证通过,返回 1,表示零知识证明验证成功。

这个代码示例中实现了一个基本的零知识证明系统,用于证明某些敏感信息的正确性,而不需要透露实际的敏感信息。请注意,具体的应用场景和逻辑细节可能需要根据实际需求进行定制。

ARC.sol

pragma solidity ^0.5.0;
contract ARC{
    struct token{
        string name;
        string value;
        address creator;
        bytes32 hash;
        bool exisit;
        bool claimed;
    }
    address owner;
    mapping(bytes32 => token) tokens;
    event LogTokenCreate(bytes32, string, string, address, bytes32);
    event LogTokenRevocation(bytes32);
    
    function tokenCreate(string memory  _name, string memory _value, bytes32  _hash)public returns (bytes32) {
        //bytes32 tokenId = sha256(abi.encodePacked(_name,_value,msg.sender,_hash,now));
        bytes32 tokenId = sha256(abi.encodePacked(_name,_value,_hash,_name));
        tokens[tokenId].name = _name;
        tokens[tokenId].value = _value;
        tokens[tokenId].creator = msg.sender;
        tokens[tokenId].hash = _hash;
        tokens[tokenId].exisit = true;
        tokens[tokenId].claimed = false;
        emit LogTokenCreate(tokenId, _name, _value, msg.sender, _hash);
        return tokenId;
    }
    function tokenRevocation(bytes32 _tokenId)public {
        address creator = creatorQuery(_tokenId);
        if (msg.sender == creator){
            revocation(_tokenId);
            emit LogTokenRevocation(_tokenId);
        }
    }
    function changeStatus(bytes32 _tokenId)public {
        tokens[_tokenId].claimed = true;
    }
    function revocation(bytes32 _tokenId)public {
        tokens[_tokenId].exisit = false;
    }
    function creatorQuery(bytes32 _tokenId)public view returns(address){
        address creator = tokens[_tokenId].creator;
        return creator;
    }
    function exisitQuery(bytes32 _tokenId)public view returns(bool){
        bool exisit = tokens[_tokenId].exisit;
        return exisit;
    }
    function claimedQuery(bytes32 _tokenId)public view returns(bool){
        bool claimed = tokens[_tokenId].claimed;
        return claimed;
    }    
    function tokenQuery(bytes32 _tokenId)public view returns(bytes32, string memory, string memory, bytes32, address, bool, bool){
        string memory name = tokens[_tokenId].name;
        string memory value = tokens[_tokenId].value;
        bytes32 hash = tokens[_tokenId].hash;
        address creator = tokens[_tokenId].creator;
        bool exisit = tokens[_tokenId].exisit;
        bool claimed = tokens[_tokenId].claimed;
        return (_tokenId, name, value, hash, creator, exisit, claimed);
    }
}

这是一个名为 ARC 的 Solidity 智能合约。该合约定义了一个 token 结构体,包含了一些代币的基本属性。合约还定义了一些用于创建、撤销和查询代币的函数。

以下是合约中重要部分的解释:

  1. struct token: 代币结构体,包含以下属性:

    • name: 代币的名称,类型为字符串(string)。
    • value: 代币的值,类型为字符串(string)。
    • creator: 代币的创建者的地址,类型为地址(address)。
    • hash: 代币的哈希值,类型为字节32位(bytes32)。
    • exisit: 代币是否存在的标志,类型为布尔值(bool)。
    • claimed: 代币是否被认领的标志,类型为布尔值(bool)。
  2. address owner: 合约的所有者的地址,类型为地址(address)。

  3. mapping(bytes32 => token) tokens: 用于存储代币的映射(键-值对),其中键为代币的唯一标识符 bytes32,值为代币的结构体。

  4. event LogTokenCreate: 创建代币时触发的事件,用于记录代币的信息。

  5. event LogTokenRevocation: 撤销代币时触发的事件,用于记录被撤销代币的标识符。

  6. function tokenCreate(string memory _name, string memory _value, bytes32 _hash)public returns (bytes32): 创建代币的函数。该函数接受代币的名称 _name、值 _value 和哈希 _hash 作为输入参数,并返回一个代币的唯一标识符 bytes32

  7. function tokenRevocation(bytes32 _tokenId)public: 撤销代币的函数。只有代币的创建者可以调用该函数,用于撤销代币的存在。

  8. function changeStatus(bytes32 _tokenId)public: 更改代币状态的函数。用于将代币标记为已认领。

  9. function revocation(bytes32 _tokenId)public: 真正执行代币撤销的内部函数。将代币标记为不存在。

  10. function creatorQuery(bytes32 _tokenId)public view returns(address): 查询代币的创建者地址的函数。

  11. function exisitQuery(bytes32 _tokenId)public view returns(bool): 查询代币是否存在的函数。

  12. function claimedQuery(bytes32 _tokenId)public view returns(bool): 查询代币是否被认领的函数。

  13. function tokenQuery(bytes32 _tokenId)public view returns(bytes32, string memory, string memory, bytes32, address, bool, bool): 查询代币的完整信息的函数。返回代币的标识符、名称、值、哈希、创建者地址、是否存在和是否被认领的信息。

总体来说,这个智能合约实现了一个简单的代币系统,允许用户创建、撤销和查询代币的信息,并在相应的事件中记录代币的操作。

函数tokenCreate

参数说明:

  • _name: 代币的名称,类型为字符串(string)。用于描述代币的名称,例如 "MyToken"。
  • _value: 代币的值,类型为字符串(string)。用于描述代币的具体数值,例如 "100"。
  • _hash: 代币的哈希值,类型为字节32位(bytes32)。用于记录代币的哈希值,通常用于代币的唯一标识。

返回值:

  • 代币的唯一标识符,类型为字节32位(bytes32)。在合约中,唯一标识符是通过对代币的名称、值、哈希和其他信息进行哈希运算得到的。

函数功能: 该函数用于创建新的代币,并将代币的信息存储在合约的 tokens 映射中。每个代币都有一个唯一的标识符,用于在映射中进行索引和查询。

函数代码逻辑:

  1. 首先,根据传入的 _name_value_hash 参数,使用 sha256 哈希函数计算代币的唯一标识符 tokenId。这里的哈希运算使用了 Solidity 的内置函数 sha256,通过将代币的名称、值、哈希和其他信息拼接在一起,保证了每个代币都有一个唯一的标识符。abi.encodePacked

  2. 接下来,将新代币的信息存储在合约的 tokens 映射中。使用计算得到的 tokenId 作为键,将代币的信息作为值存储在映射中。具体的代币信息包括:

    • name: 代币的名称。
    • value: 代币的值。
    • creator: 代币的创建者的地址,即调用该函数的合约调用者(发送交易的地址)。
    • hash: 代币的哈希值。
    • exisit: 代币是否存在的标志,此时为 true,表示代币已经创建并存在。
    • claimed: 代币是否被认领的标志,此时为 false,表示代币还未被认领。
  3. 最后,通过触发 LogTokenCreate 事件,记录创建代币的信息。这里的事件用于在区块链上记录代币的操作,方便用户或其他合约进行查询。

  4. 函数最后返回 tokenId,即新创建代币的唯一标识符,供调用方使用和保存。

总结: tokenCreate 函数是用于创建代币的合约方法。它接受代币的名称、值和哈希作为输入参数,并根据这些信息计算代币的唯一标识符。然后,将代币的信息存储在合约的映射中,表示该代币已经创建并存在。最后,通过触发事件,记录代币的创建信息。这样,其他用户或合约可以通过查询事件和映射,获取代币的信息和状态。

函数tokenRevocation

参数说明:

  • _tokenId: 待撤销代币的唯一标识符,类型为字节32位(bytes32)。该标识符用于在映射 tokens 中索引和定位待撤销的代币。

返回值:

  • 无返回值。函数执行完成后,会触发事件 LogTokenRevocation 来记录代币的撤销。

函数功能: 该函数用于撤销已创建的代币,将相应代币在映射 tokens 中的状态标记为不存在。

函数代码逻辑:

  1. 首先,根据传入的 _tokenId 参数,在映射 tokens 中查找相应的代币。

  2. 然后,通过调用 revocation 函数将代币在映射中的状态标记为不存在。revocation 函数的作用是将代币的 exisit 标志设为 false,表示该代币不存在了。

  3. 接着,通过触发 LogTokenRevocation 事件,记录代币的撤销信息。这样,在区块链上就会有一条记录,表示该代币已被撤销。

总结: tokenRevocation 函数是用于撤销代币的合约方法。它接受待撤销代币的唯一标识符作为输入参数,通过标记代币在映射中的状态为不存在,实现代币的撤销。函数执行完成后,会触发事件 LogTokenRevocation 来记录代币的撤销信息。这样,其他用户或合约可以通过查询事件和映射,获取代币的最新状态。需要注意的是,只有代币的创建者(即 creator)才有权限调用该函数来撤销代币。

函数changeStatus

参数说明:

  • _tokenId: 待更改状态的代币的唯一标识符,类型为字节32位(bytes32)。该标识符用于在映射 tokens 中索引和定位待更改状态的代币。

返回值:

  • 无返回值。函数执行完成后,将会更改代币的状态。

函数功能: 该函数用于将已创建的代币的状态标记为已认领(claimed)。

函数代码逻辑:

  1. 首先,根据传入的 _tokenId 参数,在映射 tokens 中查找相应的代币。

  2. 然后,将代币的状态 claimed 设为 true,表示该代币已被认领。

总结: changeStatus 函数用于更改代币的状态,将其标记为已认领。在实际使用场景中,这个函数可能用于表示某个代币已经被使用或处理。需要注意的是,只有合约的创建者或拥有者才有权限调用此函数来更改代币的状态。其他用户或合约可以通过查询映射 tokens,获取代币的最新状态。

函数revocation

参数说明:

  • _tokenId: 待撤销的代币的唯一标识符,类型为字节32位(bytes32)。该标识符用于在映射 tokens 中索引和定位待撤销的代币。

返回值:

  • 无返回值。函数执行完成后,将会撤销代币,将其从合约中移除。

函数功能: 该函数用于撤销(吊销)已创建的代币。

函数代码逻辑:

  1. 首先,根据传入的 _tokenId 参数,在映射 tokens 中查找相应的代币。

  2. 然后,将代币的状态 exisit 设为 false,表示该代币不存在。

总结: revocation 函数用于撤销代币,将其从合约中移除。在实际使用场景中,这个函数可能用于使代币无效或作废。需要注意的是,只有合约的创建者或拥有者才有权限调用此函数来撤销代币。其他用户或合约可以通过查询映射 tokens,检查代币的状态来确定其是否有效。

函数creatorQuery

参数说明:

  • _tokenId: 待查询代币的唯一标识符,类型为字节32位(bytes32)。该标识符用于在映射 tokens 中索引和定位待查询的代币。

返回值:

  • address: 代币的创建者的地址。

函数功能: 该函数用于查询特定代币的创建者(creator)的地址。

函数代码逻辑:

  1. 首先,根据传入的 _tokenId 参数,在映射 tokens 中查找相应的代币。

  2. 然后,获取该代币的创建者(creator)的地址。

  3. 最后,将获取到的地址作为返回值。

总结: creatorQuery 函数是一个只读函数,用于查询特定代币的创建者的地址。它允许其他用户或合约查询特定代币的创建者信息,但不会对合约状态进行修改。这在构建代币管理系统中很有用,因为它可以提供代币的拥有者信息,以及对代币的创建者身份进行验证。

函数exisitQuery

参数说明:

  • _tokenId: 待查询代币的唯一标识符,类型为字节32位(bytes32)。该标识符用于在映射 tokens 中索引和定位待查询的代币。

返回值:

  • bool: 代币是否存在的布尔值。如果代币存在,则返回 true,否则返回 false

函数功能: 该函数用于查询特定代币是否存在。

函数代码逻辑:

  1. 首先,根据传入的 _tokenId 参数,在映射 tokens 中查找相应的代币。

  2. 然后,获取该代币的 exisit 属性,即代币是否存在的状态。

  3. 最后,将获取到的状态值作为返回值。

总结: exisitQuery 函数是一个只读函数,用于查询特定代币是否存在。它允许其他用户或合约查询代币的存在状态,但不会对合约状态进行修改。这在构建代币管理系统中很有用,因为它可以提供对代币是否已经创建的确认。

函数claimedQuery

参数说明:

  • _tokenId: 待查询代币的唯一标识符,类型为字节32位(bytes32)。该标识符用于在映射 tokens 中索引和定位待查询的代币。

返回值:

  • bool: 代币的认领状态的布尔值。如果代币已经被认领,则返回 true,否则返回 false

函数功能: 该函数用于查询特定代币的认领状态。

函数代码逻辑:

  1. 首先,根据传入的 _tokenId 参数,在映射 tokens 中查找相应的代币。

  2. 然后,获取该代币的 claimed 属性,即代币的认领状态。

  3. 最后,将获取到的认领状态值作为返回值。

总结: claimedQuery 函数是一个只读函数,用于查询特定代币的认领状态。它允许其他用户或合约查询代币是否已经被认领,但不会对合约状态进行修改。这在构建代币认领系统中很有用,因为它可以提供对代币是否已经被认领的确认。

函数tokenQuery

参数说明:

  • _tokenId: 待查询代币的唯一标识符,类型为字节32位(bytes32)。该标识符用于在映射 tokens 中索引和定位待查询的代币。

返回值:

  • bytes32: 代币的唯一标识符,即 _tokenId
  • string memory: 代币的名称,即 name
  • string memory: 代币的值,即 value
  • bytes32: 代币的哈希值,即 hash
  • address: 代币的创建者地址,即 creator
  • bool: 代币是否存在的布尔值,即 exisit
  • bool: 代币的认领状态的布尔值,即 claimed

函数功能: 该函数用于查询特定代币的详细信息,包括代币的名称、值、哈希值、创建者地址、是否存在以及认领状态。

函数代码逻辑:

  1. 首先,根据传入的 _tokenId 参数,在映射 tokens 中查找相应的代币。

  2. 然后,获取该代币的各个属性值,包括 namevaluehashcreatorexisitclaimed

  3. 最后,将获取到的各个属性值作为返回值,按照函数签名的顺序返回。

总结: tokenQuery 函数是一个只读函数,用于查询特定代币的详细信息。通过调用该函数,其他用户或合约可以获得代币的名称、值、哈希值、创建者地址、是否存在以及认领状态等信息。由于该函数是只读的,不会对合约状态进行修改。这在代币的查询和展示方面非常有用。

执行命令生成验证proof的智能合约solidity源码:verify.sol

responseSK/verifier.sol

// This file is LGPL3 Licensed

/**
 * @title Elliptic curve operations on twist points for alt_bn128
 * @author Mustafa Al-Bassam ([email protected])
 * @dev Homepage: https://github.com/musalbas/solidity-BN256G2
 */

library BN256G2 {
    uint256 internal constant FIELD_MODULUS = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47;
    uint256 internal constant TWISTBX = 0x2b149d40ceb8aaae81be18991be06ac3b5b4c5e559dbefa33267e6dc24a138e5;
    uint256 internal constant TWISTBY = 0x9713b03af0fed4cd2cafadeed8fdf4a74fa084e52d1852e4a2bd0685c315d2;
    uint internal constant PTXX = 0;
    uint internal constant PTXY = 1;
    uint internal constant PTYX = 2;
    uint internal constant PTYY = 3;
    uint internal constant PTZX = 4;
    uint internal constant PTZY = 5;

    /**
     * @notice Add two twist points
     * @param pt1xx Coefficient 1 of x on point 1
     * @param pt1xy Coefficient 2 of x on point 1
     * @param pt1yx Coefficient 1 of y on point 1
     * @param pt1yy Coefficient 2 of y on point 1
     * @param pt2xx Coefficient 1 of x on point 2
     * @param pt2xy Coefficient 2 of x on point 2
     * @param pt2yx Coefficient 1 of y on point 2
     * @param pt2yy Coefficient 2 of y on point 2
     * @return (pt3xx, pt3xy, pt3yx, pt3yy)
     */
    function ECTwistAdd(
        uint256 pt1xx, uint256 pt1xy,
        uint256 pt1yx, uint256 pt1yy,
        uint256 pt2xx, uint256 pt2xy,
        uint256 pt2yx, uint256 pt2yy
    ) public view returns (
        uint256, uint256,
        uint256, uint256
    ) {
        if (
            pt1xx == 0 && pt1xy == 0 &&
            pt1yx == 0 && pt1yy == 0
        ) {
            if (!(
                pt2xx == 0 && pt2xy == 0 &&
                pt2yx == 0 && pt2yy == 0
            )) {
                assert(_isOnCurve(
                    pt2xx, pt2xy,
                    pt2yx, pt2yy
                ));
            }
            return (
                pt2xx, pt2xy,
                pt2yx, pt2yy
            );
        } else if (
            pt2xx == 0 && pt2xy == 0 &&
            pt2yx == 0 && pt2yy == 0
        ) {
            assert(_isOnCurve(
                pt1xx, pt1xy,
                pt1yx, pt1yy
            ));
            return (
                pt1xx, pt1xy,
                pt1yx, pt1yy
            );
        }

        assert(_isOnCurve(
            pt1xx, pt1xy,
            pt1yx, pt1yy
        ));
        assert(_isOnCurve(
            pt2xx, pt2xy,
            pt2yx, pt2yy
        ));

        uint256[6] memory pt3 = _ECTwistAddJacobian(
            pt1xx, pt1xy,
            pt1yx, pt1yy,
            1,     0,
            pt2xx, pt2xy,
            pt2yx, pt2yy,
            1,     0
        );

        return _fromJacobian(
            pt3[PTXX], pt3[PTXY],
            pt3[PTYX], pt3[PTYY],
            pt3[PTZX], pt3[PTZY]
        );
    }

    /**
     * @notice Multiply a twist point by a scalar
     * @param s     Scalar to multiply by
     * @param pt1xx Coefficient 1 of x
     * @param pt1xy Coefficient 2 of x
     * @param pt1yx Coefficient 1 of y
     * @param pt1yy Coefficient 2 of y
     * @return (pt2xx, pt2xy, pt2yx, pt2yy)
     */
    function ECTwistMul(
        uint256 s,
        uint256 pt1xx, uint256 pt1xy,
        uint256 pt1yx, uint256 pt1yy
    ) public view returns (
        uint256, uint256,
        uint256, uint256
    ) {
        uint256 pt1zx = 1;
        if (
            pt1xx == 0 && pt1xy == 0 &&
            pt1yx == 0 && pt1yy == 0
        ) {
            pt1xx = 1;
            pt1yx = 1;
            pt1zx = 0;
        } else {
            assert(_isOnCurve(
                pt1xx, pt1xy,
                pt1yx, pt1yy
            ));
        }

        uint256[6] memory pt2 = _ECTwistMulJacobian(
            s,
            pt1xx, pt1xy,
            pt1yx, pt1yy,
            pt1zx, 0
        );

        return _fromJacobian(
            pt2[PTXX], pt2[PTXY],
            pt2[PTYX], pt2[PTYY],
            pt2[PTZX], pt2[PTZY]
        );
    }

    /**
     * @notice Get the field modulus
     * @return The field modulus
     */
    function GetFieldModulus() public pure returns (uint256) {
        return FIELD_MODULUS;
    }

    function submod(uint256 a, uint256 b, uint256 n) internal pure returns (uint256) {
        return addmod(a, n - b, n);
    }

    function _FQ2Mul(
        uint256 xx, uint256 xy,
        uint256 yx, uint256 yy
    ) internal pure returns (uint256, uint256) {
        return (
            submod(mulmod(xx, yx, FIELD_MODULUS), mulmod(xy, yy, FIELD_MODULUS), FIELD_MODULUS),
            addmod(mulmod(xx, yy, FIELD_MODULUS), mulmod(xy, yx, FIELD_MODULUS), FIELD_MODULUS)
        );
    }

    function _FQ2Muc(
        uint256 xx, uint256 xy,
        uint256 c
    ) internal pure returns (uint256, uint256) {
        return (
            mulmod(xx, c, FIELD_MODULUS),
            mulmod(xy, c, FIELD_MODULUS)
        );
    }

    function _FQ2Add(
        uint256 xx, uint256 xy,
        uint256 yx, uint256 yy
    ) internal pure returns (uint256, uint256) {
        return (
            addmod(xx, yx, FIELD_MODULUS),
            addmod(xy, yy, FIELD_MODULUS)
        );
    }

    function _FQ2Sub(
        uint256 xx, uint256 xy,
        uint256 yx, uint256 yy
    ) internal pure returns (uint256 rx, uint256 ry) {
        return (
            submod(xx, yx, FIELD_MODULUS),
            submod(xy, yy, FIELD_MODULUS)
        );
    }

    function _FQ2Div(
        uint256 xx, uint256 xy,
        uint256 yx, uint256 yy
    ) internal view returns (uint256, uint256) {
        (yx, yy) = _FQ2Inv(yx, yy);
        return _FQ2Mul(xx, xy, yx, yy);
    }

    function _FQ2Inv(uint256 x, uint256 y) internal view returns (uint256, uint256) {
        uint256 inv = _modInv(addmod(mulmod(y, y, FIELD_MODULUS), mulmod(x, x, FIELD_MODULUS), FIELD_MODULUS), FIELD_MODULUS);
        return (
            mulmod(x, inv, FIELD_MODULUS),
            FIELD_MODULUS - mulmod(y, inv, FIELD_MODULUS)
        );
    }

    function _isOnCurve(
        uint256 xx, uint256 xy,
        uint256 yx, uint256 yy
    ) internal pure returns (bool) {
        uint256 yyx;
        uint256 yyy;
        uint256 xxxx;
        uint256 xxxy;
        (yyx, yyy) = _FQ2Mul(yx, yy, yx, yy);
        (xxxx, xxxy) = _FQ2Mul(xx, xy, xx, xy);
        (xxxx, xxxy) = _FQ2Mul(xxxx, xxxy, xx, xy);
        (yyx, yyy) = _FQ2Sub(yyx, yyy, xxxx, xxxy);
        (yyx, yyy) = _FQ2Sub(yyx, yyy, TWISTBX, TWISTBY);
        return yyx == 0 && yyy == 0;
    }

    function _modInv(uint256 a, uint256 n) internal view returns (uint256 result) {
        bool success;
        assembly {
            let freemem := mload(0x40)
            mstore(freemem, 0x20)
            mstore(add(freemem,0x20), 0x20)
            mstore(add(freemem,0x40), 0x20)
            mstore(add(freemem,0x60), a)
            mstore(add(freemem,0x80), sub(n, 2))
            mstore(add(freemem,0xA0), n)
            success := staticcall(sub(gas, 2000), 5, freemem, 0xC0, freemem, 0x20)
            result := mload(freemem)
        }
        require(success);
    }

    function _fromJacobian(
        uint256 pt1xx, uint256 pt1xy,
        uint256 pt1yx, uint256 pt1yy,
        uint256 pt1zx, uint256 pt1zy
    ) internal view returns (
        uint256 pt2xx, uint256 pt2xy,
        uint256 pt2yx, uint256 pt2yy
    ) {
        uint256 invzx;
        uint256 invzy;
        (invzx, invzy) = _FQ2Inv(pt1zx, pt1zy);
        (pt2xx, pt2xy) = _FQ2Mul(pt1xx, pt1xy, invzx, invzy);
        (pt2yx, pt2yy) = _FQ2Mul(pt1yx, pt1yy, invzx, invzy);
    }

    function _ECTwistAddJacobian(
        uint256 pt1xx, uint256 pt1xy,
        uint256 pt1yx, uint256 pt1yy,
        uint256 pt1zx, uint256 pt1zy,
        uint256 pt2xx, uint256 pt2xy,
        uint256 pt2yx, uint256 pt2yy,
        uint256 pt2zx, uint256 pt2zy) internal pure returns (uint256[6] memory pt3) {
            if (pt1zx == 0 && pt1zy == 0) {
                (
                    pt3[PTXX], pt3[PTXY],
                    pt3[PTYX], pt3[PTYY],
                    pt3[PTZX], pt3[PTZY]
                ) = (
                    pt2xx, pt2xy,
                    pt2yx, pt2yy,
                    pt2zx, pt2zy
                );
                return pt3;
            } else if (pt2zx == 0 && pt2zy == 0) {
                (
                    pt3[PTXX], pt3[PTXY],
                    pt3[PTYX], pt3[PTYY],
                    pt3[PTZX], pt3[PTZY]
                ) = (
                    pt1xx, pt1xy,
                    pt1yx, pt1yy,
                    pt1zx, pt1zy
                );
                return pt3;
            }

            (pt2yx,     pt2yy)     = _FQ2Mul(pt2yx, pt2yy, pt1zx, pt1zy); // U1 = y2 * z1
            (pt3[PTYX], pt3[PTYY]) = _FQ2Mul(pt1yx, pt1yy, pt2zx, pt2zy); // U2 = y1 * z2
            (pt2xx,     pt2xy)     = _FQ2Mul(pt2xx, pt2xy, pt1zx, pt1zy); // V1 = x2 * z1
            (pt3[PTZX], pt3[PTZY]) = _FQ2Mul(pt1xx, pt1xy, pt2zx, pt2zy); // V2 = x1 * z2

            if (pt2xx == pt3[PTZX] && pt2xy == pt3[PTZY]) {
                if (pt2yx == pt3[PTYX] && pt2yy == pt3[PTYY]) {
                    (
                        pt3[PTXX], pt3[PTXY],
                        pt3[PTYX], pt3[PTYY],
                        pt3[PTZX], pt3[PTZY]
                    ) = _ECTwistDoubleJacobian(pt1xx, pt1xy, pt1yx, pt1yy, pt1zx, pt1zy);
                    return pt3;
                }
                (
                    pt3[PTXX], pt3[PTXY],
                    pt3[PTYX], pt3[PTYY],
                    pt3[PTZX], pt3[PTZY]
                ) = (
                    1, 0,
                    1, 0,
                    0, 0
                );
                return pt3;
            }

            (pt2zx,     pt2zy)     = _FQ2Mul(pt1zx, pt1zy, pt2zx,     pt2zy);     // W = z1 * z2
            (pt1xx,     pt1xy)     = _FQ2Sub(pt2yx, pt2yy, pt3[PTYX], pt3[PTYY]); // U = U1 - U2
            (pt1yx,     pt1yy)     = _FQ2Sub(pt2xx, pt2xy, pt3[PTZX], pt3[PTZY]); // V = V1 - V2
            (pt1zx,     pt1zy)     = _FQ2Mul(pt1yx, pt1yy, pt1yx,     pt1yy);     // V_squared = V * V
            (pt2yx,     pt2yy)     = _FQ2Mul(pt1zx, pt1zy, pt3[PTZX], pt3[PTZY]); // V_squared_times_V2 = V_squared * V2
            (pt1zx,     pt1zy)     = _FQ2Mul(pt1zx, pt1zy, pt1yx,     pt1yy);     // V_cubed = V * V_squared
            (pt3[PTZX], pt3[PTZY]) = _FQ2Mul(pt1zx, pt1zy, pt2zx,     pt2zy);     // newz = V_cubed * W
            (pt2xx,     pt2xy)     = _FQ2Mul(pt1xx, pt1xy, pt1xx,     pt1xy);     // U * U
            (pt2xx,     pt2xy)     = _FQ2Mul(pt2xx, pt2xy, pt2zx,     pt2zy);     // U * U * W
            (pt2xx,     pt2xy)     = _FQ2Sub(pt2xx, pt2xy, pt1zx,     pt1zy);     // U * U * W - V_cubed
            (pt2zx,     pt2zy)     = _FQ2Muc(pt2yx, pt2yy, 2);                    // 2 * V_squared_times_V2
            (pt2xx,     pt2xy)     = _FQ2Sub(pt2xx, pt2xy, pt2zx,     pt2zy);     // A = U * U * W - V_cubed - 2 * V_squared_times_V2
            (pt3[PTXX], pt3[PTXY]) = _FQ2Mul(pt1yx, pt1yy, pt2xx,     pt2xy);     // newx = V * A
            (pt1yx,     pt1yy)     = _FQ2Sub(pt2yx, pt2yy, pt2xx,     pt2xy);     // V_squared_times_V2 - A
            (pt1yx,     pt1yy)     = _FQ2Mul(pt1xx, pt1xy, pt1yx,     pt1yy);     // U * (V_squared_times_V2 - A)
            (pt1xx,     pt1xy)     = _FQ2Mul(pt1zx, pt1zy, pt3[PTYX], pt3[PTYY]); // V_cubed * U2
            (pt3[PTYX], pt3[PTYY]) = _FQ2Sub(pt1yx, pt1yy, pt1xx,     pt1xy);     // newy = U * (V_squared_times_V2 - A) - V_cubed * U2
    }

    function _ECTwistDoubleJacobian(
        uint256 pt1xx, uint256 pt1xy,
        uint256 pt1yx, uint256 pt1yy,
        uint256 pt1zx, uint256 pt1zy
    ) internal pure returns (
        uint256 pt2xx, uint256 pt2xy,
        uint256 pt2yx, uint256 pt2yy,
        uint256 pt2zx, uint256 pt2zy
    ) {
        (pt2xx, pt2xy) = _FQ2Muc(pt1xx, pt1xy, 3);            // 3 * x
        (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt1xx, pt1xy); // W = 3 * x * x
        (pt1zx, pt1zy) = _FQ2Mul(pt1yx, pt1yy, pt1zx, pt1zy); // S = y * z
        (pt2yx, pt2yy) = _FQ2Mul(pt1xx, pt1xy, pt1yx, pt1yy); // x * y
        (pt2yx, pt2yy) = _FQ2Mul(pt2yx, pt2yy, pt1zx, pt1zy); // B = x * y * S
        (pt1xx, pt1xy) = _FQ2Mul(pt2xx, pt2xy, pt2xx, pt2xy); // W * W
        (pt2zx, pt2zy) = _FQ2Muc(pt2yx, pt2yy, 8);            // 8 * B
        (pt1xx, pt1xy) = _FQ2Sub(pt1xx, pt1xy, pt2zx, pt2zy); // H = W * W - 8 * B
        (pt2zx, pt2zy) = _FQ2Mul(pt1zx, pt1zy, pt1zx, pt1zy); // S_squared = S * S
        (pt2yx, pt2yy) = _FQ2Muc(pt2yx, pt2yy, 4);            // 4 * B
        (pt2yx, pt2yy) = _FQ2Sub(pt2yx, pt2yy, pt1xx, pt1xy); // 4 * B - H
        (pt2yx, pt2yy) = _FQ2Mul(pt2yx, pt2yy, pt2xx, pt2xy); // W * (4 * B - H)
        (pt2xx, pt2xy) = _FQ2Muc(pt1yx, pt1yy, 8);            // 8 * y
        (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt1yx, pt1yy); // 8 * y * y
        (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt2zx, pt2zy); // 8 * y * y * S_squared
        (pt2yx, pt2yy) = _FQ2Sub(pt2yx, pt2yy, pt2xx, pt2xy); // newy = W * (4 * B - H) - 8 * y * y * S_squared
        (pt2xx, pt2xy) = _FQ2Muc(pt1xx, pt1xy, 2);            // 2 * H
        (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt1zx, pt1zy); // newx = 2 * H * S
        (pt2zx, pt2zy) = _FQ2Mul(pt1zx, pt1zy, pt2zx, pt2zy); // S * S_squared
        (pt2zx, pt2zy) = _FQ2Muc(pt2zx, pt2zy, 8);            // newz = 8 * S * S_squared
    }

    function _ECTwistMulJacobian(
        uint256 d,
        uint256 pt1xx, uint256 pt1xy,
        uint256 pt1yx, uint256 pt1yy,
        uint256 pt1zx, uint256 pt1zy
    ) internal pure returns (uint256[6] memory pt2) {
        while (d != 0) {
            if ((d & 1) != 0) {
                pt2 = _ECTwistAddJacobian(
                    pt2[PTXX], pt2[PTXY],
                    pt2[PTYX], pt2[PTYY],
                    pt2[PTZX], pt2[PTZY],
                    pt1xx, pt1xy,
                    pt1yx, pt1yy,
                    pt1zx, pt1zy);
            }
            (
                pt1xx, pt1xy,
                pt1yx, pt1yy,
                pt1zx, pt1zy
            ) = _ECTwistDoubleJacobian(
                pt1xx, pt1xy,
                pt1yx, pt1yy,
                pt1zx, pt1zy
            );

            d = d / 2;
        }
    }
}
// This file is MIT Licensed.
//
// Copyright 2017 Christian Reitwiessner
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
pragma solidity ^0.5.0;
library Pairing {
    struct G1Point {
        uint X;
        uint Y;
    }
    // Encoding of field elements is: X[0] * z + X[1]
    struct G2Point {
        uint[2] X;
        uint[2] Y;
    }
    /// @return the generator of G1
    function P1() pure internal returns (G1Point memory) {
        return G1Point(1, 2);
    }
    /// @return the generator of G2
    function P2() pure internal returns (G2Point memory) {
        return G2Point(
            [11559732032986387107991004021392285783925812861821192530917403151452391805634,
             10857046999023057135944570762232829481370756359578518086990519993285655852781],
            [4082367875863433681332203403145435568316851327593401208105741076214120093531,
             8495653923123431417604973247489272438418190587263600148770280649306958101930]
        );
    }
    /// @return the negation of p, i.e. p.addition(p.negate()) should be zero.
    function negate(G1Point memory p) pure internal returns (G1Point memory) {
        // The prime q in the base field F_q for G1
        uint q = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
        if (p.X == 0 && p.Y == 0)
            return G1Point(0, 0);
        return G1Point(p.X, q - (p.Y % q));
    }
    /// @return the sum of two points of G1
    function addition(G1Point memory p1, G1Point memory p2) internal returns (G1Point memory r) {
        uint[4] memory input;
        input[0] = p1.X;
        input[1] = p1.Y;
        input[2] = p2.X;
        input[3] = p2.Y;
        bool success;
        assembly {
            success := call(sub(gas, 2000), 6, 0, input, 0xc0, r, 0x60)
            // Use "invalid" to make gas estimation work
            switch success case 0 { invalid() }
        }
        require(success);
    }
    /// @return the sum of two points of G2
    function addition(G2Point memory p1, G2Point memory p2) internal returns (G2Point memory r) {
        (r.X[1], r.X[0], r.Y[1], r.Y[0]) = BN256G2.ECTwistAdd(p1.X[1],p1.X[0],p1.Y[1],p1.Y[0],p2.X[1],p2.X[0],p2.Y[1],p2.Y[0]);
    }
    /// @return the product of a point on G1 and a scalar, i.e.
    /// p == p.scalar_mul(1) and p.addition(p) == p.scalar_mul(2) for all points p.
    function scalar_mul(G1Point memory p, uint s) internal returns (G1Point memory r) {
        uint[3] memory input;
        input[0] = p.X;
        input[1] = p.Y;
        input[2] = s;
        bool success;
        assembly {
            success := call(sub(gas, 2000), 7, 0, input, 0x80, r, 0x60)
            // Use "invalid" to make gas estimation work
            switch success case 0 { invalid() }
        }
        require (success);
    }
    /// @return the result of computing the pairing check
    /// e(p1[0], p2[0]) *  .... * e(p1[n], p2[n]) == 1
    /// For example pairing([P1(), P1().negate()], [P2(), P2()]) should
    /// return true.
    function pairing(G1Point[] memory p1, G2Point[] memory p2) internal returns (bool) {
        require(p1.length == p2.length);
        uint elements = p1.length;
        uint inputSize = elements * 6;
        uint[] memory input = new uint[](inputSize);
        for (uint i = 0; i < elements; i++)
        {
            input[i * 6 + 0] = p1[i].X;
            input[i * 6 + 1] = p1[i].Y;
            input[i * 6 + 2] = p2[i].X[0];
            input[i * 6 + 3] = p2[i].X[1];
            input[i * 6 + 4] = p2[i].Y[0];
            input[i * 6 + 5] = p2[i].Y[1];
        }
        uint[1] memory out;
        bool success;
        assembly {
            success := call(sub(gas, 2000), 8, 0, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
            // Use "invalid" to make gas estimation work
            switch success case 0 { invalid() }
        }
        require(success);
        return out[0] != 0;
    }
    /// Convenience method for a pairing check for two pairs.
    function pairingProd2(G1Point memory a1, G2Point memory a2, G1Point memory b1, G2Point memory b2) internal returns (bool) {
        G1Point[] memory p1 = new G1Point[](2);
        G2Point[] memory p2 = new G2Point[](2);
        p1[0] = a1;
        p1[1] = b1;
        p2[0] = a2;
        p2[1] = b2;
        return pairing(p1, p2);
    }
    /// Convenience method for a pairing check for three pairs.
    function pairingProd3(
            G1Point memory a1, G2Point memory a2,
            G1Point memory b1, G2Point memory b2,
            G1Point memory c1, G2Point memory c2
    ) internal returns (bool) {
        G1Point[] memory p1 = new G1Point[](3);
        G2Point[] memory p2 = new G2Point[](3);
        p1[0] = a1;
        p1[1] = b1;
        p1[2] = c1;
        p2[0] = a2;
        p2[1] = b2;
        p2[2] = c2;
        return pairing(p1, p2);
    }
    /// Convenience method for a pairing check for four pairs.
    function pairingProd4(
            G1Point memory a1, G2Point memory a2,
            G1Point memory b1, G2Point memory b2,
            G1Point memory c1, G2Point memory c2,
            G1Point memory d1, G2Point memory d2
    ) internal returns (bool) {
        G1Point[] memory p1 = new G1Point[](4);
        G2Point[] memory p2 = new G2Point[](4);
        p1[0] = a1;
        p1[1] = b1;
        p1[2] = c1;
        p1[3] = d1;
        p2[0] = a2;
        p2[1] = b2;
        p2[2] = c2;
        p2[3] = d2;
        return pairing(p1, p2);
    }
}

contract CVC {
    using Pairing for *;
    struct VerifyingKey {
        Pairing.G1Point a;
        Pairing.G2Point b;
        Pairing.G2Point gamma;
        Pairing.G2Point delta;
        Pairing.G1Point[] gamma_abc;
    }
    struct Proof {
        Pairing.G1Point a;
        Pairing.G2Point b;
        Pairing.G1Point c;
    }
    function verifyingKey() pure internal returns (VerifyingKey memory vk) {
        vk.a = Pairing.G1Point(uint256(0x040c25fc4789e3be4030ec734962fa634f697d7fb40d6c8fd05a0a26b2e4bbdc), uint256(0x1952d16a501e559beb17fceafca1e1145fbfac88fe464c5a56009b6ec2cd0749));
        vk.b = Pairing.G2Point([uint256(0x1128f1935e0d096177555b680ae3f88979bde4491abdeacb69959d16baae2492), uint256(0x1fff28ca31727477fa5967bc3f9a8fabc19fb66c0f73f2c1deb970fe82f7f88f)], [uint256(0x0d73c74b2a151356db394753aeee2019f1be286e6e439620e8d31609b9931670), uint256(0x1aebf0f0bea297def5b0afcd8f07b6763f4b210cb2644c61e8b0a96d8aa29f63)]);
        vk.gamma = Pairing.G2Point([uint256(0x17b830425c19fcaac70e3aed973cff29a2bda118496d256f605d99128e810d6e), uint256(0x1c2874a7f2c0261295d5bc9fd185f92b09a79f55cca5fe3a18a3958b9ee923f3)], [uint256(0x127259d40080d801383da34e93ed656c4eb845cf425a57fb6a9635d962e2cc51), uint256(0x2950db7d050f3945aff9a28c13e52c8c7e46a4be24b83c45f5cedfbf416f35a2)]);
        vk.delta = Pairing.G2Point([uint256(0x0453affcd9fb3bb1f799335d35595c1afc0ab9c5b7903fd8474442714f61ee60), uint256(0x2df84d766e632a2ceff923712556a5367009530bfbcccdaf4775ad0834eb85b9)], [uint256(0x20ba7d68217431dc0485c69321daca807aea2bb6ec78a13ca0377a6dbfb23f64), uint256(0x09d1e5f6e4f0bb0ab0125554eff9e4bbb81ac050b60576e98a676ef9350982ec)]);
        vk.gamma_abc = new Pairing.G1Point[](6);
        vk.gamma_abc[0] = Pairing.G1Point(uint256(0x1058fe94fb4b390c4ab3c2a3327c663d01911853c28e1f944d3c5292730cbd7d), uint256(0x2d4a506f931551ed5133f9b24752846cbd174231e2b998ef08208ccf13daa378));
        vk.gamma_abc[1] = Pairing.G1Point(uint256(0x1c1b4912eb2bff3570787ebed6470e779cb910b5d05e43f06e2c73e80d5e4fd9), uint256(0x292dced20978d7d91a30c4f85db4c4efddc4c03c076a999bef9069e150c5ddd1));
        vk.gamma_abc[2] = Pairing.G1Point(uint256(0x1604bd3d8992736c644294fa3f876924e0cf5af429d187c47be860a58185e5b7), uint256(0x04ee5178cd96f4a5e8b4b6428731e0b09a53a1e6e8d53c244532dfa3dced513c));
        vk.gamma_abc[3] = Pairing.G1Point(uint256(0x1a5cb8fed48e2cc93d1da72ab20180946c9116fd35c9d605d4a56c8ea118540c), uint256(0x19e9e0fc2729ff64ff106d8282db3e78735f147bcbd9e53913e020e267abf59e));
        vk.gamma_abc[4] = Pairing.G1Point(uint256(0x04b6f731e0f08c65b4e57c9548e735ba49fec7ef7802bfe1cf4f87bcca652817), uint256(0x2050c461a809549fa589f680375b6d78490962cf62d842c01bacbef4c0bf987f));
        vk.gamma_abc[5] = Pairing.G1Point(uint256(0x2475a57830c18369133184c596dfeade07b2c22591fab0034a732f22f7ea80c2), uint256(0x0a252aad36a468e8e891f5e2fca952675f48e21bb572b796f4ca1b157683b94c));
    }
    function verify(uint[] memory input, Proof memory proof) internal returns (uint) {
        uint256 snark_scalar_field = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
        VerifyingKey memory vk = verifyingKey();
        require(input.length + 1 == vk.gamma_abc.length);
        // Compute the linear combination vk_x
        Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0);
        for (uint i = 0; i < input.length; i++) {
            require(input[i] < snark_scalar_field);
            vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.gamma_abc[i + 1], input[i]));
        }
        vk_x = Pairing.addition(vk_x, vk.gamma_abc[0]);
        if(!Pairing.pairingProd4(
             proof.a, proof.b,
             Pairing.negate(vk_x), vk.gamma,
             Pairing.negate(proof.c), vk.delta,
             Pairing.negate(vk.a), vk.b)) return 1;
        return 0;
    }
    event Verified(string s);
    function claimPKVerify(
            uint[2] memory a,
            uint[2][2] memory b,
            uint[2] memory c,
            uint[5] memory input
        ) public returns (bool r) {
        Proof memory proof;
        proof.a = Pairing.G1Point(a[0], a[1]);
        proof.b = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]);
        proof.c = Pairing.G1Point(c[0], c[1]);
        uint[] memory inputValues = new uint[](input.length);
        for(uint i = 0; i < input.length; i++){
            inputValues[i] = input[i];
        }
        if (verify(inputValues, proof) == 0) {
            emit Verified("Transaction successfully verified.");
            return true;
        } else {
            return false;
        }
    }
}
contract RVC {
    using Pairing for *;
    struct VerifyingKey {
        Pairing.G1Point a;
        Pairing.G2Point b;
        Pairing.G2Point gamma;
        Pairing.G2Point delta;
        Pairing.G1Point[] gamma_abc;
    }
    struct Proof {
        Pairing.G1Point a;
        Pairing.G2Point b;
        Pairing.G1Point c;
    }
    function verifyingKey() pure internal returns (VerifyingKey memory vk) {
        vk.a = Pairing.G1Point(uint256(0x24bcb7996d85be5d9f7d1295f9e2506d3177ffa9d7ca329aba36c0d978d8be1f), uint256(0x10d336469bcae09aeba5ef514b10e7678e429c2a6b71bc815f3e7c964411daf8));
        vk.b = Pairing.G2Point([uint256(0x051df0aa2aedfc7923fe58b173378da25a790f947bf69474a112fbfe1474c2c2), uint256(0x2b8d8c7f5a09fbe30b0ebfa38d17a56081dfa0d366b166bf68b65bf962a5501f)], [uint256(0x03ae12e9974ced8eb99fa6463bb267fe0548a5910cdedbf13d9eebfa6210dd11), uint256(0x0b6e18cd7bf7612e2dfc3709ff4c84194691131f04111479e3b59ee48a41047e)]);
        vk.gamma = Pairing.G2Point([uint256(0x19aaad3888d3d5924be12694b588298bb19b55e5a347e8277c50ae2dd2ec8ef2), uint256(0x0a60aee7371d4044e86d8ae29dc4312763ea0e405ff22a711260e51f5106be4a)], [uint256(0x022795508f9f366b781e6ccd81059d401970d6cd5714feb1c2e5f6cd52273330), uint256(0x28f4199934a4fcadab69f13631f6f0c739faf3e007762f62f0de81770b10fbae)]);
        vk.delta = Pairing.G2Point([uint256(0x0e0ec3f6413793a9f9abfff54b59278413610082f1c764e118ce81d735a041b2), uint256(0x0fdde4afb23307b1e49845438e2c2d4b952da2aec875c83ac84bb98940df8e05)], [uint256(0x08a64b3cdc9f78a24876da0c8ceb6a7bc84b825e1c54f773be218a03713ece4c), uint256(0x1d450d86dd7ae07171403ae8e8a2d81ad88d11526fb9509d20040ef491ef5765)]);
        vk.gamma_abc = new Pairing.G1Point[](8);
        vk.gamma_abc[0] = Pairing.G1Point(uint256(0x30481d3e3c8fce2b4a736911fff34ceb0e9aea241dadc984eaed3e210ec60d77), uint256(0x25293a548846d71c5c7af18d56a8c79121fd9ae48ddc795f78fdc82e24b26f22));
        vk.gamma_abc[1] = Pairing.G1Point(uint256(0x2ae5543b4079071b46dc1409003de64f9b21f806f6a52810ab5254d77ce872fb), uint256(0x2143f716dba471c9710eff5310698280284a47faff2df502015ec569aee33b63));
        vk.gamma_abc[2] = Pairing.G1Point(uint256(0x00cfe0b05e092660e8336deaefe11ac0287caa5fe6a6247c0265d30d4bfd7bb2), uint256(0x0f4dabd5c3dac5539a0537b9d2eef9c78eb3e342e8599f18f406fa0f58f036d6));
        vk.gamma_abc[3] = Pairing.G1Point(uint256(0x1c7304bf08fbf6325441dc9d84d88a3d8f787f1ac952b192afe7214d10be3b95), uint256(0x2a07571d0f18591f6212ff72bcd4e26793e8f5562150f53341fd8d9ec1686424));
        vk.gamma_abc[4] = Pairing.G1Point(uint256(0x28462f408ec2303119e9762f3b8e363780a08930975e3de96c3d927d3f0b1e01), uint256(0x045cb347f1d5e752c383965971d5dce5caa0b1290b9824c3258fda13a4c6c39d));
        vk.gamma_abc[5] = Pairing.G1Point(uint256(0x1f877e2b310859ccc8e5f5dcf729fd1ebcac87b9d3363e35a8ea0bfa3a5bd45a), uint256(0x0af3d6b8a7655003cb2a494930da28dc45874c20a8b998e4743cf8c5dfb10bd9));
        vk.gamma_abc[6] = Pairing.G1Point(uint256(0x2250078766e26e488dbd1fc8f5e538392bd0f5dbc74080c60cb58264ffc605fa), uint256(0x2fd616bbd5cf2f65ec20deeec7e2b46368ad7b203b3fce6d1083e6b8ae65e1ba));
        vk.gamma_abc[7] = Pairing.G1Point(uint256(0x0308f8c4ae08e6ac68f25c65ed7902a2383b6bcbc3e5afc6d1994b7e3b8281de), uint256(0x051190e7ef38768a3a56921ef02744fe213ae53577cc75905f781cd635bc618a));
    }
    function verify(uint[] memory input, Proof memory proof) internal returns (uint) {
        uint256 snark_scalar_field = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
        VerifyingKey memory vk = verifyingKey();
        require(input.length + 1 == vk.gamma_abc.length);
        // Compute the linear combination vk_x
        Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0);
        for (uint i = 0; i < input.length; i++) {
            require(input[i] < snark_scalar_field);
            vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.gamma_abc[i + 1], input[i]));
        }
        vk_x = Pairing.addition(vk_x, vk.gamma_abc[0]);
        if(!Pairing.pairingProd4(
             proof.a, proof.b,
             Pairing.negate(vk_x), vk.gamma,
             Pairing.negate(proof.c), vk.delta,
             Pairing.negate(vk.a), vk.b)) return 1;
        return 0;
    }
    event Verified(string s);
    function responseSKVerify(
            uint[2] memory a,
            uint[2][2] memory b,
            uint[2] memory c,
            uint[7] memory input
        ) public returns (bool r) {
        Proof memory proof;
        proof.a = Pairing.G1Point(a[0], a[1]);
        proof.b = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]);
        proof.c = Pairing.G1Point(c[0], c[1]);
        uint[] memory inputValues = new uint[](input.length);
        for(uint i = 0; i < input.length; i++){
            inputValues[i] = input[i];
        }
        if (verify(inputValues, proof) == 0) {
            emit Verified("Transaction successfully verified.");
            return true;
        } else {
            return false;
        }
    }
}

提供的 Solidity 代码是扭转点的alt_bn128椭圆曲线运算的实现。该库包含用于执行alt_bn128曲线上扭转点的加法和乘法的函数,以及用于执行字段算术运算的辅助函数。

以下是代码主要组件的简要概述:

  1. 常数:

    • 该库包含多个表示alt_bn128曲线元素的常量。
  2. 添加扭点:

    • 该函数计算alt_bn128曲线中两个扭转点的相加。ECTwistAdd
    • 它将两个扭转点的坐标作为输入,并返回结果扭转点的坐标。
  3. 扭转点的乘法:

    • 该函数计算alt_bn128曲线中扭转点的标量乘法。ECTwistMul
    • 它将标量值和扭曲点的坐标作为输入,并在标量乘法后返回结果扭曲点的坐标。s
  4. 字段算术函数:

    • 该库包含用于执行字段算术运算的函数,例如加法、减法、乘法、除法和反转。
    • 这些函数由点加法和乘法函数在内部使用。
  5. 曲线上的扭转点检查:

    • 该函数用于验证扭转点是否位于alt_bn128曲线上。_isOnCurve
  6. 内部加点和倍增功能:

    • 该库包含内部函数 和 ,它们分别在alt_bn128曲线上的雅可比坐标中执行点加法和加倍。_ECTwistAddJacobian ,_ECTwistDoubleJacobian
    • 这些函数用于主要的加法和乘法函数。
  7. 配对:

    • 该库还包括一些与配对计算相关的函数(未包含在您提供的代码片段中)。配对是基于配对的密码学中的一项重要操作,它支持高级加密功能。

提供的合约似乎是使用加密操作alt_bn128曲线的加密可验证凭据 (CVC) 协议的实现。但是,CVC 协议特定的详细信息在代码片段中不可见。值得注意的是,此实现旨在用于受信任的环境,因为代码不包括任何输入验证或针对潜在安全问题(如侧信道攻击或整数溢出)的保护。请记住,加密协议和库需要精确的实现以确保其安全性,任何修改或集成都应由安全专家仔细彻底审查。

library BN256G2

BN256G2是在alt_bn128曲线上进行椭圆曲线运算的Solidity库的一部分。在上面提到的代码中,BN256G2库提供了额外的功能,用于在alt_bn128曲线的G2群组上执行操作。

在椭圆曲线密码学中,G2是曲线上的第二个群组。这个库的目的是为了支持在G2群组上进行操作,使得开发者可以更灵活地使用alt_bn128曲线进行密码学计算。

具体来说,BN256G2库可能包含以下类型的功能:

  1. G2群组上的点加法:实现将两个G2群组上的点相加的操作。

  2. G2群组上的点乘法:实现将G2群组上的点与标量相乘的操作。

  3. 其他G2群组相关的实用功能:可能包含椭圆曲线上的点是否属于G2群组的检查等功能。

由于我无法查看实际代码内容,上述功能只是一般在类似的库中可能包含的功能。具体功能取决于开发者在设计和实现该库时的选择。

总体而言,BN256G2库扩展了主库CVC中提供的功能,使得在alt_bn128曲线的G2群组上进行椭圆曲线计算更加便捷。开发者可以使用该库来构建更复杂的密码学协议和算法,其中涉及到G2群组上的计算。

kmc.sol

pragma solidity ^0.5.0;
import "verify.sol";
import "ARC.sol";

contract KMC{
    ARC arc;
    CVC cvc;
    RVC rvc;
    mapping(bytes32 => bytes32) public claims;
    mapping(bytes32 => bytes32) public adjunction;
    event LogTokenClaim(address, bytes32, bytes32);
    event LogTokenResponse(bytes32, string);
    
    function setcliam(address c)public{
        cvc = CVC(c);
    }
    function setarc(address a)public{
        arc = ARC(a);
    }
    function setres(address r)public{
        rvc = RVC(r);
    }
    function tokenClaim(uint[2] memory a, uint[2][2] memory b, uint[2] memory c, uint[5] memory input) public returns (bool) {
    //bytes32 id = packToBytes32(input[0],input[1]);
    //bytes32 id = 0x2123e10a5c7a9e5bb3de5a568cb3a36f12ee705b47e47d109cdfcaf3b56faf21;
    //bytes32 id_claim = 0x36f12ee705b47e47d109cdfcaf3b56faf212123e10a5c7a9e5bb3de5a568cb3a;
    bytes32 id_claim = packToBytes32(input[2], input[3]);
    bytes32 id = packToBytes32(input[0], input[1]);

    // 调用名为"cvc"的合约的claimPKVerify函数来验证认领证明的有效性
    // 这里假设"cvc"是一个已经部署在区块链上的合约,并且有claimPKVerify函数
    // 该函数用于验证输入的a、b、c和input是否符合特定规则
    // 如果验证成功,返回值reslut将被设为true,否则将会触发require断言
    bool result = cvc.claimPKVerify(a, b, c, input);
    require(result == true, "verify failed!");

    // 调用名为"arc"的合约的creatorQuery函数来检查是否调用者是该代币的创建者
    // 这里假设"arc"是一个已经部署在区块链上的合约,并且有creatorQuery函数
    // 该函数用于查询与特定代币相关的创建者地址
    // 如果查询结果表明调用者不是创建者,则将会触发require断言
    require(arc.creatorQuery(id) == msg.sender, "you are not the creator of token");

    // 调用名为"arc"的合约的claimedQuery函数来检查该代币是否已被认领
    // 这里假设"arc"是一个已经部署在区块链上的合约,并且有claimedQuery函数
    // 该函数用于查询与特定代币相关的认领状态
    // 如果查询结果表明该代币已被认领,则将会触发require断言
    require(arc.claimedQuery(id) == false, "this token has been claimed");

    // 调用名为"arc"的合约的exisitQuery函数来检查该代币是否存在
    // 这里假设"arc"是一个已经部署在区块链上的合约,并且有exisitQuery函数
    // 该函数用于查询与特定代币相关的存在状态
    // 如果查询结果表明该代币不存在,则将会触发require断言
    require(arc.exisitQuery(id) == true, "this token has been revoked!");

    // 如果所有的require断言都通过,说明认领条件满足
    // 将认领的代币标记为已认领,并将认领信息记录在claims映射中
    claims[id_claim] = id_claim;
    arc.changeStatus(id);

    // 发出事件通知,表示认领成功
    emit LogTokenClaim(msg.sender, id, id_claim);

    // 返回true表示认领成功
    return true;
}
function tokenResponse(uint[2] memory a, uint[2][2] memory b, uint[2] memory c, uint[7] memory input, string memory res) public returns (bool) {
    //bytes32 id = 0x2123e10a5c7a9e5bb3de5a568cb3a36f12ee705b47e47d109cdfcaf3b56faf21;
    //bytes32 id_claim = 0x36f12ee705b47e47d109cdfcaf3b56faf212123e10a5c7a9e5bb3de5a568cb3a;
    //bytes32 id_ne = 0xcb3a36f12ee705b47e47d109cdfcaf3b56faf212123e10a5c7a9e5bb3de5a568;

    // 将输入参数input中的前4个uint值打包成bytes32类型的id和id_claim
    bytes32 id = packToBytes32(input[0], input[1]);
    bytes32 id_claim = packToBytes32(input[2], input[3]);

    // 将输入参数input中的第5个和第6个uint值打包成bytes32类型的id_ne
    bytes32 id_ne = packToBytes32(input[4], input[5]);

    // 调用名为"rvc"的合约的responseSKVerify函数来验证响应证明的有效性
    // 这里假设"rvc"是一个已经部署在区块链上的合约,并且有responseSKVerify函数
    // 该函数用于验证输入的a、b、c和input是否符合特定规则
    // 如果验证成功,返回值reslut将被设为true,否则将会触发require断言
    bool result = rvc.responseSKVerify(a, b, c, input);
    require(result == true, "verify failed!");

    // 调用名为"arc"的合约的exisitQuery函数来检查该代币是否存在
    // 这里假设"arc"是一个已经部署在区块链上的合约,并且有exisitQuery函数
    // 该函数用于查询与特定代币相关的存在状态
    // 如果查询结果表明该代币不存在,则将会触发require断言
    require(arc.exisitQuery(id) == true, "this token has been revoked!");

    // 检查之前的认领信息,确保该代币已被认领
    // 如果认领信息表明该代币尚未被认领,则将会触发require断言
    require(claims[id_claim] == id_claim, "this token has not been claimed");

    // 检查之前的邻接信息,确保该响应证明尚未被使用
    // 如果邻接信息表明该响应证明已经被使用,则将会触发require断言
    require(adjunction[id_ne] == 0, "proof has been used!");

    // 如果所有的require断言都通过,说明响应条件满足
    // 将响应证明标记为已使用,并将邻接信息记录在adjunction映射中
    adjunction[id_ne] = id_ne;

    // 发出事件通知,表示响应成功
    // 将id和res作为事件的参数,以便记录响应的内容
    emit LogTokenResponse(id, res);

    // 返回true表示响应成功
    return true;
}

    //function addclaim(bytes32 id_claim)public returns(uint){
    //    claims[id_claim] = id_claim;
    //}   
    function packToBytes32(uint256 high, uint256 low)public pure returns (bytes32){
        return bytes32(low) | (bytes32(high)<<128);
    }
}

这是一个名为 KMC 的智能合约,以下是对该合约中的主要功能和代码逻辑的详细解释:

  1. ARCCVCRVC 合约引用:合约中引入了 verify.solARC.sol 中的合约。verify.sol 应该包含一个名为 CVC、RVC 的合约,而 ARC.sol 应该包含一个名为 ARC 的合约。

  2. 合约变量:

    • ARC arc: 存储 ARC 合约的实例,用于与 ARC 合约进行交互。
    • CVC cvc: 存储 CVC 合约的实例,用于与 CVC 合约进行交互。
    • RVC rvc: 存储 RVC 合约的实例,用于与 RVC 合约进行交互。
    • mapping(bytes32 => bytes32) public claims: 映射,用于存储 tokenClaim 函数中的 id_claim 以及与之相关的信息。
    • mapping(bytes32 => bytes32) public adjunction: 映射,用于存储 tokenResponse 函数中的 id_ne 以及与之相关的信息。
  3. 事件:

    • event LogTokenClaim(address, bytes32, bytes32): 用于在 tokenClaim 函数中记录代币声明的事件。
    • event LogTokenResponse(bytes32, string): 用于在 tokenResponse 函数中记录代币响应的事件。
  4. 函数 setcliam(address c)setarc(address a)setres(address r):这些函数用于设置 CVCARCRVC 合约的实例。在这些函数中,参数分别为对应合约的地址,通过调用这些函数来设置合约实例。

  5. 函数 tokenClaim(uint[2] memory a, uint[2][2] memory b, uint[2] memory c, uint[5] memory input):该函数用于声明代币的所有权。具体逻辑如下:

    • 首先,调用 CVC 合约的 claimPKVerify 函数,验证声明的签名和证明信息。
    • 然后,检查当前用户是否为代币的创建者,代币是否未被认领,代币是否存在,并根据条件进行断言。
    • 如果通过所有验证,将 id_claim 存储到映射 claims 中,标记代币为已认领状态,并发出 LogTokenClaim 事件。
    • 最后,返回 true 表示操作成功。
  6. 函数 tokenResponse(uint[2] memory a, uint[2][2] memory b, uint[2] memory c, uint[7] memory input, string memory res):该函数用于响应代币的声明。具体逻辑如下:

    • 首先,调用 RVC 合约的 responseSKVerify 函数,验证响应的签名和证明信息。
    • 然后,检查代币是否存在,并根据条件进行断言。
    • 检查代币是否已被声明,并根据条件进行断言。
    • 检查给定的 id_ne 是否已被使用,并根据条件进行断言。
    • 如果通过所有验证,将 id_ne 存储到映射 adjunction 中,标记该证明已使用,并发出 LogTokenResponse 事件。
    • 最后,返回 true 表示操作成功。
  7. 函数 packToBytes32(uint256 high, uint256 low):该函数用于将两个 uint256 类型的整数 highlow 合并成一个 bytes32 类型的整数。

函数CVC

contract CVC {
    using Pairing for *;
    struct VerifyingKey {
        Pairing.G1Point a;
        Pairing.G2Point b;
        Pairing.G2Point gamma;
        Pairing.G2Point delta;
        Pairing.G1Point[] gamma_abc;
    }
    struct Proof {
        Pairing.G1Point a;
        Pairing.G2Point b;
        Pairing.G1Point c;
    }
    function verifyingKey() pure internal returns (VerifyingKey memory vk) {
        vk.a = Pairing.G1Point(uint256(0x040c25fc4789e3be4030ec734962fa634f697d7fb40d6c8fd05a0a26b2e4bbdc), uint256(0x1952d16a501e559beb17fceafca1e1145fbfac88fe464c5a56009b6ec2cd0749));
        vk.b = Pairing.G2Point([uint256(0x1128f1935e0d096177555b680ae3f88979bde4491abdeacb69959d16baae2492), uint256(0x1fff28ca31727477fa5967bc3f9a8fabc19fb66c0f73f2c1deb970fe82f7f88f)], [uint256(0x0d73c74b2a151356db394753aeee2019f1be286e6e439620e8d31609b9931670), uint256(0x1aebf0f0bea297def5b0afcd8f07b6763f4b210cb2644c61e8b0a96d8aa29f63)]);
        vk.gamma = Pairing.G2Point([uint256(0x17b830425c19fcaac70e3aed973cff29a2bda118496d256f605d99128e810d6e), uint256(0x1c2874a7f2c0261295d5bc9fd185f92b09a79f55cca5fe3a18a3958b9ee923f3)], [uint256(0x127259d40080d801383da34e93ed656c4eb845cf425a57fb6a9635d962e2cc51), uint256(0x2950db7d050f3945aff9a28c13e52c8c7e46a4be24b83c45f5cedfbf416f35a2)]);
        vk.delta = Pairing.G2Point([uint256(0x0453affcd9fb3bb1f799335d35595c1afc0ab9c5b7903fd8474442714f61ee60), uint256(0x2df84d766e632a2ceff923712556a5367009530bfbcccdaf4775ad0834eb85b9)], [uint256(0x20ba7d68217431dc0485c69321daca807aea2bb6ec78a13ca0377a6dbfb23f64), uint256(0x09d1e5f6e4f0bb0ab0125554eff9e4bbb81ac050b60576e98a676ef9350982ec)]);
        vk.gamma_abc = new Pairing.G1Point[](6);
        vk.gamma_abc[0] = Pairing.G1Point(uint256(0x1058fe94fb4b390c4ab3c2a3327c663d01911853c28e1f944d3c5292730cbd7d), uint256(0x2d4a506f931551ed5133f9b24752846cbd174231e2b998ef08208ccf13daa378));
        vk.gamma_abc[1] = Pairing.G1Point(uint256(0x1c1b4912eb2bff3570787ebed6470e779cb910b5d05e43f06e2c73e80d5e4fd9), uint256(0x292dced20978d7d91a30c4f85db4c4efddc4c03c076a999bef9069e150c5ddd1));
        vk.gamma_abc[2] = Pairing.G1Point(uint256(0x1604bd3d8992736c644294fa3f876924e0cf5af429d187c47be860a58185e5b7), uint256(0x04ee5178cd96f4a5e8b4b6428731e0b09a53a1e6e8d53c244532dfa3dced513c));
        vk.gamma_abc[3] = Pairing.G1Point(uint256(0x1a5cb8fed48e2cc93d1da72ab20180946c9116fd35c9d605d4a56c8ea118540c), uint256(0x19e9e0fc2729ff64ff106d8282db3e78735f147bcbd9e53913e020e267abf59e));
        vk.gamma_abc[4] = Pairing.G1Point(uint256(0x04b6f731e0f08c65b4e57c9548e735ba49fec7ef7802bfe1cf4f87bcca652817), uint256(0x2050c461a809549fa589f680375b6d78490962cf62d842c01bacbef4c0bf987f));
        vk.gamma_abc[5] = Pairing.G1Point(uint256(0x2475a57830c18369133184c596dfeade07b2c22591fab0034a732f22f7ea80c2), uint256(0x0a252aad36a468e8e891f5e2fca952675f48e21bb572b796f4ca1b157683b94c));
    }
    function verify(uint[] memory input, Proof memory proof) internal returns (uint) {
        uint256 snark_scalar_field = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
        VerifyingKey memory vk = verifyingKey();
        require(input.length + 1 == vk.gamma_abc.length);
        // Compute the linear combination vk_x
        Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0);
        for (uint i = 0; i < input.length; i++) {
            require(input[i] < snark_scalar_field);
            vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.gamma_abc[i + 1], input[i]));
        }
        vk_x = Pairing.addition(vk_x, vk.gamma_abc[0]);
        if(!Pairing.pairingProd4(
             proof.a, proof.b,
             Pairing.negate(vk_x), vk.gamma,
             Pairing.negate(proof.c), vk.delta,
             Pairing.negate(vk.a), vk.b)) return 1;
        return 0;
    }
    event Verified(string s);
    function claimPKVerify(
            uint[2] memory a,
            uint[2][2] memory b,
            uint[2] memory c,
            uint[5] memory input
        ) public returns (bool r) {
        Proof memory proof;
        proof.a = Pairing.G1Point(a[0], a[1]);
        proof.b = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]);
        proof.c = Pairing.G1Point(c[0], c[1]);
        uint[] memory inputValues = new uint[](input.length);
        for(uint i = 0; i < input.length; i++){
            inputValues[i] = input[i];
        }
        if (verify(inputValues, proof) == 0) {
            emit Verified("Transaction successfully verified.");
            return true;
        } else {
            return false;
        }
    }
}

函数CVC是在提供的Solidity代码中的一个库(contract),用于实现基于alt_bn128曲线的zk-SNARKs验证功能。该库包含了一些用于进行椭圆曲线操作的函数,以及用于验证zk-SNARKs证明的函数。

让我们逐步解释函数CVC中的重要部分:

  1. 结构体VerifyingKey:

    • 该结构体用于存储用于验证zk-SNARKs证明的公钥信息。
    • 它包含以下字段:
      • a:一个G1群组上的点,用于计算zk-SNARKs证明中的线性组合。
      • b:一个G2群组上的点,用于计算zk-SNARKs证明中的线性组合。
      • gamma:一个G2群组上的点,用于计算zk-SNARKs证明中的线性组合。
      • delta:一个G2群组上的点,用于计算zk-SNARKs证明中的线性组合。
      • gamma_abc:一个包含6个G1群组上的点的数组,用于计算zk-SNARKs证明中的线性组合。
  2. 结构体Proof:

    • 该结构体用于存储zk-SNARKs证明的信息。
    • 它包含以下字段:
      • a:一个G1群组上的点,是zk-SNARKs证明的一部分。
      • b:一个G2群组上的点,是zk-SNARKs证明的一部分。
      • c:一个G1群组上的点,是zk-SNARKs证明的一部分。
  3. 函数verifyingKey():

    • 这是一个internal函数,用于返回VerifyingKey结构体,即zk-SNARKs的公钥信息。
    • 函数内部硬编码了公钥信息,并将其封装在VerifyingKey结构体中。
  4. 函数verify(uint[] memory input, Proof memory proof):

    • 这是一个internal函数,用于验证zk-SNARKs证明的有效性。
    • 它接受一个包含输入数据的数组input和一个Proof结构体proof作为参数。
    • 在验证过程中,它会根据输入数据和公钥信息,计算并验证一系列椭圆曲线操作,以确定zk-SNARKs证明是否有效。
  5. 函数claimPKVerify(...):

    • 这是一个公开(public)函数,用于外部调用以验证zk-SNARKs证明。
    • 它接受zk-SNARKs证明的相关信息作为输入,并将其转换为Proof结构体。
    • 然后,它调用verify函数,验证zk-SNARKs证明的有效性。
    • 如果验证成功,则会触发一个名为"Verified"的事件,并返回true,表示zk-SNARKs证明有效;否则,返回false,表示zk-SNARKs证明无效。

总体而言,函数CVC提供了一个用于验证zk-SNARKs证明的库,其中使用了alt_bn128曲线上的椭圆曲线操作。使用该库,开发者可以进行零知识证明的验证,确保在zk-SNARKs方案中所提交的证明是否正确和有效。这对于实现更安全、隐私保护的区块链应用和协议非常有用。

函数RVC

contract RVC {
    using Pairing for *;
    struct VerifyingKey {
        Pairing.G1Point a;
        Pairing.G2Point b;
        Pairing.G2Point gamma;
        Pairing.G2Point delta;
        Pairing.G1Point[] gamma_abc;
    }
    struct Proof {
        Pairing.G1Point a;
        Pairing.G2Point b;
        Pairing.G1Point c;
    }
    function verifyingKey() pure internal returns (VerifyingKey memory vk) {
        vk.a = Pairing.G1Point(uint256(0x24bcb7996d85be5d9f7d1295f9e2506d3177ffa9d7ca329aba36c0d978d8be1f), uint256(0x10d336469bcae09aeba5ef514b10e7678e429c2a6b71bc815f3e7c964411daf8));
        vk.b = Pairing.G2Point([uint256(0x051df0aa2aedfc7923fe58b173378da25a790f947bf69474a112fbfe1474c2c2), uint256(0x2b8d8c7f5a09fbe30b0ebfa38d17a56081dfa0d366b166bf68b65bf962a5501f)], [uint256(0x03ae12e9974ced8eb99fa6463bb267fe0548a5910cdedbf13d9eebfa6210dd11), uint256(0x0b6e18cd7bf7612e2dfc3709ff4c84194691131f04111479e3b59ee48a41047e)]);
        vk.gamma = Pairing.G2Point([uint256(0x19aaad3888d3d5924be12694b588298bb19b55e5a347e8277c50ae2dd2ec8ef2), uint256(0x0a60aee7371d4044e86d8ae29dc4312763ea0e405ff22a711260e51f5106be4a)], [uint256(0x022795508f9f366b781e6ccd81059d401970d6cd5714feb1c2e5f6cd52273330), uint256(0x28f4199934a4fcadab69f13631f6f0c739faf3e007762f62f0de81770b10fbae)]);
        vk.delta = Pairing.G2Point([uint256(0x0e0ec3f6413793a9f9abfff54b59278413610082f1c764e118ce81d735a041b2), uint256(0x0fdde4afb23307b1e49845438e2c2d4b952da2aec875c83ac84bb98940df8e05)], [uint256(0x08a64b3cdc9f78a24876da0c8ceb6a7bc84b825e1c54f773be218a03713ece4c), uint256(0x1d450d86dd7ae07171403ae8e8a2d81ad88d11526fb9509d20040ef491ef5765)]);
        vk.gamma_abc = new Pairing.G1Point[](8);
        vk.gamma_abc[0] = Pairing.G1Point(uint256(0x30481d3e3c8fce2b4a736911fff34ceb0e9aea241dadc984eaed3e210ec60d77), uint256(0x25293a548846d71c5c7af18d56a8c79121fd9ae48ddc795f78fdc82e24b26f22));
        vk.gamma_abc[1] = Pairing.G1Point(uint256(0x2ae5543b4079071b46dc1409003de64f9b21f806f6a52810ab5254d77ce872fb), uint256(0x2143f716dba471c9710eff5310698280284a47faff2df502015ec569aee33b63));
        vk.gamma_abc[2] = Pairing.G1Point(uint256(0x00cfe0b05e092660e8336deaefe11ac0287caa5fe6a6247c0265d30d4bfd7bb2), uint256(0x0f4dabd5c3dac5539a0537b9d2eef9c78eb3e342e8599f18f406fa0f58f036d6));
        vk.gamma_abc[3] = Pairing.G1Point(uint256(0x1c7304bf08fbf6325441dc9d84d88a3d8f787f1ac952b192afe7214d10be3b95), uint256(0x2a07571d0f18591f6212ff72bcd4e26793e8f5562150f53341fd8d9ec1686424));
        vk.gamma_abc[4] = Pairing.G1Point(uint256(0x28462f408ec2303119e9762f3b8e363780a08930975e3de96c3d927d3f0b1e01), uint256(0x045cb347f1d5e752c383965971d5dce5caa0b1290b9824c3258fda13a4c6c39d));
        vk.gamma_abc[5] = Pairing.G1Point(uint256(0x1f877e2b310859ccc8e5f5dcf729fd1ebcac87b9d3363e35a8ea0bfa3a5bd45a), uint256(0x0af3d6b8a7655003cb2a494930da28dc45874c20a8b998e4743cf8c5dfb10bd9));
        vk.gamma_abc[6] = Pairing.G1Point(uint256(0x2250078766e26e488dbd1fc8f5e538392bd0f5dbc74080c60cb58264ffc605fa), uint256(0x2fd616bbd5cf2f65ec20deeec7e2b46368ad7b203b3fce6d1083e6b8ae65e1ba));
        vk.gamma_abc[7] = Pairing.G1Point(uint256(0x0308f8c4ae08e6ac68f25c65ed7902a2383b6bcbc3e5afc6d1994b7e3b8281de), uint256(0x051190e7ef38768a3a56921ef02744fe213ae53577cc75905f781cd635bc618a));
    }
    function verify(uint[] memory input, Proof memory proof) internal returns (uint) {
        uint256 snark_scalar_field = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
        VerifyingKey memory vk = verifyingKey();
        require(input.length + 1 == vk.gamma_abc.length);
        // Compute the linear combination vk_x
        Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0);
        for (uint i = 0; i < input.length; i++) {
            require(input[i] < snark_scalar_field);
            vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.gamma_abc[i + 1], input[i]));
        }
        vk_x = Pairing.addition(vk_x, vk.gamma_abc[0]);
        if(!Pairing.pairingProd4(
             proof.a, proof.b,
             Pairing.negate(vk_x), vk.gamma,
             Pairing.negate(proof.c), vk.delta,
             Pairing.negate(vk.a), vk.b)) return 1;
        return 0;
    }
    event Verified(string s);
    function responseSKVerify(
            uint[2] memory a,
            uint[2][2] memory b,
            uint[2] memory c,
            uint[7] memory input
        ) public returns (bool r) {
        Proof memory proof;
        proof.a = Pairing.G1Point(a[0], a[1]);
        proof.b = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]);
        proof.c = Pairing.G1Point(c[0], c[1]);
        uint[] memory inputValues = new uint[](input.length);
        for(uint i = 0; i < input.length; i++){
            inputValues[i] = input[i];
        }
        if (verify(inputValues, proof) == 0) {
            emit Verified("Transaction successfully verified.");
            return true;
        } else {
            return false;
        }
    }
}

函数RVC是另一个Solidity合约,用于实现基于alt_bn128曲线的zk-SNARKs验证功能。与之前提供的CVC合约类似,RVC合约也包含了一些用于进行椭圆曲线操作的函数,以及用于验证zk-SNARKs证明的函数。

让我们逐步解释函数RVC中的重要部分:

  1. 结构体VerifyingKey:

    • 该结构体用于存储用于验证zk-SNARKs证明的公钥信息,类似于CVC合约中的VerifyingKey结构体。
    • 它包含以下字段:
      • a:一个G1群组上的点,用于计算zk-SNARKs证明中的线性组合。
      • b:一个G2群组上的点,用于计算zk-SNARKs证明中的线性组合。
      • gamma:一个G2群组上的点,用于计算zk-SNARKs证明中的线性组合。
      • delta:一个G2群组上的点,用于计算zk-SNARKs证明中的线性组合。
      • gamma_abc:一个包含8个G1群组上的点的数组,用于计算zk-SNARKs证明中的线性组合。
  2. 结构体Proof:

    • 该结构体用于存储zk-SNARKs证明的信息,类似于CVC合约中的Proof结构体。
    • 它包含以下字段:
      • a:一个G1群组上的点,是zk-SNARKs证明的一部分。
      • b:一个G2群组上的点,是zk-SNARKs证明的一部分。
      • c:一个G1群组上的点,是zk-SNARKs证明的一部分。
  3. 函数verifyingKey():

    • 这是一个internal函数,用于返回VerifyingKey结构体,即zk-SNARKs的公钥信息。
    • 函数内部硬编码了公钥信息,并将其封装在VerifyingKey结构体中。
  4. 函数verify(uint[] memory input, Proof memory proof):

    • 这是一个internal函数,用于验证zk-SNARKs证明的有效性,类似于CVC合约中的verify函数。
    • 它接受一个包含输入数据的数组input和一个Proof结构体proof作为参数。
    • 在验证过程中,它会根据输入数据和公钥信息,计算并验证一系列椭圆曲线操作,以确定zk-SNARKs证明是否有效。
  5. 函数responseSKVerify(...):

    • 这是一个公开(public)函数,用于外部调用以验证zk-SNARKs证明,类似于CVC合约中的claimPKVerify函数。
    • 它接受zk-SNARKs证明的相关信息作为输入,并将其转换为Proof结构体。
    • 然后,它调用verify函数,验证zk-SNARKs证明的有效性。
    • 如果验证成功,则会触发一个名为"Verified"的事件,并返回true,表示zk-SNARKs证明有效;否则,返回false,表示zk-SNARKs证明无效。

总体而言,函数RVC提供了另一个用于验证zk-SNARKs证明的合约,其中使用了alt_bn128曲线上的椭圆曲线操作。使用该合约,开发者可以进行零知识证明的验证,确保在zk-SNARKs方案中所提交的证明是否正确和有效。这对于实现更安全、隐私保护的区块链应用和协议非常有用。

改进的kmc.sol方法一(验证所有权时应该用匿名地址绑定proof)

   设计中只考虑了nullfier hash来防止proof被重复使用,不过用户在tokenResponse函数时发送的交易在执行前就已经将proof暴露了,攻击者可以将参数中的res替换成自己的,随后提高gas fee以抢先打包交易,即可窃取用户的属性,可以考虑让交易发送者的地址或是res作为电路输入参与到proof的计算中,从而将proof与用户进行绑定。我的解决方法是验证所有权时应该用匿名地址绑定proof。

pragma solidity ^0.5.0;
import "verify.sol";
import "ARC.sol";

contract KMC{
    ARC arc;
    CVC cvc;
    RVC rvc;
    mapping(bytes32 => bytes32) public claims;
    mapping(bytes32 => bytes32) public adjunction;
    event LogTokenClaim(address, bytes32, bytes32);
    event LogTokenResponse(bytes32, string);
    
    function setcliam(address c)public{
        cvc = CVC(c);
    }
    function setarc(address a)public{
        arc = ARC(a);
    }
    function setres(address r)public{
        rvc = RVC(r);
    }
    function tokenClaim(uint[2] memory a, uint[2][2] memory b, uint[2] memory c, uint[5] memory input) public returns (bool){
        //bytes32 id = packToBytes32(input[0],input[1]);
         //bytes32 id = 0x2123e10a5c7a9e5bb3de5a568cb3a36f12ee705b47e47d109cdfcaf3b56faf21;
        //bytes32 id_claim = 0x36f12ee705b47e47d109cdfcaf3b56faf212123e10a5c7a9e5bb3de5a568cb3a;
        bytes32 id_claim = packToBytes32(input[2],input[3]);
        bytes32 id = packToBytes32(input[0],input[1]); 
        bool reslut = cvc.claimPKVerify(a,b,c,input);
        require (reslut == true, "verify failed!");
        require (arc.creatorQuery(id) == msg.sender, "you are not the creator of token");
        require (arc.claimedQuery(id) == false, "this token has been claimed");
        require (arc.exisitQuery(id) == true, "this token has been revocationed!");
        claims[id_claim] = id_claim;
        arc.changeStatus(id);
        emit LogTokenClaim(msg.sender, id, id_claim);
        return true;
    }
    
    function tokenResponse(uint[2] memory a, uint[2][2] memory b, uint[2] memory c, uint[7] memory input, string memory res) public returns (bool){
        bytes32 id = packToBytes32(input[0],input[1]);
        bytes32 id_claim = packToBytes32(input[2],input[3]);
        bytes32 id_ne = packToBytes32(input[4],input[5]);
        
        // 将交易发送者的地址作为匿名地址,将其hash后的值作为adjunction映射的key
        bytes32 senderHash = keccak256(abi.encodePacked(msg.sender));
        require (adjunction[senderHash] == 0, "proof has been used!");
        
        bool reslut = rvc.responseSKVerify(a,b,c,input);
        require (reslut == true, "verify failed!");
        require (arc.exisitQuery(id) == true, "this token has been revocationed!");
        require (claims[id_claim] == id_claim, "this token has not beenclaimed");
        
        // 将proof的值作为映射的value,并在执行完验证后,将adjunction映射中的key与相应的value绑定
        adjunction[senderHash] = id_ne;
        emit LogTokenResponse(id, res);
        return true;
    } 
    
    function packToBytes32(uint256 high, uint256 low) public pure returns (bytes32){
        return bytes32(low) | (bytes32(high)<<128);
    }
}

      

为了解决攻击者替换res参数并提高gas fee来窃取用户属性的问题,我们可以修改合约中的tokenResponse函数来实现验证所有权时使用匿名地址绑定proof。具体做法如下:

  1. 在tokenResponse函数中,将交易发送者的地址作为参数传入,并将其作为电路输入参与到proof的计算中。

  2. 在合约中定义一个mapping,用于将交易发送者的地址与相应的proof绑定。

  3. 修改tokenResponse函数,将交易发送者的地址作为匿名地址,将其hash后的值作为adjunction映射的key,将proof的值作为映射的value,并在执行完验证后,将adjunction映射中的key与相应的value绑定,确保该proof只能被对应的交易发送者使用,防止被重复使用。

改进的kmc.sol方法二(引入交易发送者的地址 (msg.sender) 作为电路的输入参数)

 函数tokenClaim中的input也可能被攻击者替换成自己的随后提高gas fee以抢先打包交易,即可窃取用户的属性,可以考虑让交易发送者的地址或是input作为电路输入参与到proof的计算中,从而将proof与用户进行绑定。

攻击者可以在发送交易时替换 input 参数,并用自己的参数进行计算,从而欺骗智能合约执行。

为了解决这个问题,我们可以引入交易发送者的地址 (msg.sender) 作为电路的输入参数,以确保每个用户的交易都与其地址绑定。这样,即使攻击者尝试使用其他用户的参数重新发送交易,由于 msg.sender 的不同,计算得到的结果将不再有效。

pragma solidity ^0.5.0;
import "verify.sol";
import "ARC.sol";

contract KMC {
    ARC arc;
    CVC cvc;
    RVC rvc;
    mapping(bytes32 => bytes32) public claims;
    mapping(bytes32 => bytes32) public adjunction;
    event LogTokenClaim(address indexed sender, bytes32 indexed tokenId, bytes32 indexed idClaim);
    event LogTokenResponse(address indexed sender, bytes32 indexed tokenId, string res);

    function setclaim(address c) public {
        cvc = CVC(c);
    }
    function setarc(address a) public {
        arc = ARC(a);
    }
    function setres(address r) public {
        rvc = RVC(r);
    }

    function tokenClaim(uint[2] memory a, uint[2][2] memory b, uint[2] memory c, uint[5] memory input) public returns (bool) {
        bytes32 id_claim = packToBytes32(input[2], input[3]);
        bytes32 id = packToBytes32(input[0], input[1]);

        bool result = cvc.claimPKVerify(a, b, c, input);
        require(result == true, "verify failed!");
        require(arc.creatorQuery(id) == msg.sender, "you are not the creator of token");
        require(arc.claimedQuery(id) == false, "this token has been claimed");
        require(arc.exisitQuery(id) == true, "this token has been revoked!");

        claims[id_claim] = id_claim;
        arc.changeStatus(id);
        emit LogTokenClaim(msg.sender, id, id_claim);
        return true;
    }

    function tokenResponse(uint[2] memory a, uint[2][2] memory b, uint[2] memory c, uint[7] memory input, string memory res) public returns (bool) {
        bytes32 id = packToBytes32(input[0], input[1]);
        bytes32 id_claim = packToBytes32(input[2], input[3]);
        bytes32 id_ne = packToBytes32(input[4], input[5]);

        bool result = rvc.responseSKVerify(a, b, c, input);
        require(result == true, "verify failed!");
        require(arc.exisitQuery(id) == true, "this token has been revoked!");
        require(claims[id_claim] == id_claim, "this token has not been claimed");
        require(adjunction[id_ne] == 0, "proof has been used!");
        adjunction[id_ne] = id_ne;
        emit LogTokenResponse(msg.sender, id, res);
        return true;
    }

    function packToBytes32(uint256 high, uint256 low) public pure returns (bytes32) {
        return bytes32(low) | (bytes32(high) << 128);
    }
}

       

在修改后的合约中,我们在 LogTokenClaimLogTokenResponse 事件中添加了 address indexed sender 参数,用于记录交易发送者的地址。这样,每个事件都会与特定的用户地址绑定,确保用户的交易和响应信息都与其地址关联,从而防止攻击者替换参数并窃取用户的属性。

在Solidity中,indexed关键字用于声明事件参数为"索引参数"。索引参数允许我们在以太坊的事件查询中使用该参数作为过滤条件,从而更有效地检索特定的事件日志。

在上述修改后的合约中,LogTokenClaimLogTokenResponse事件中的address indexed sender参数允许我们在以太坊区块链上使用该参数来过滤事件日志。当这两个事件被触发时,发送方(即调用相应函数的交易发送者)的地址将会被记录为事件的索引参数,因此可以通过该地址来查询与该地址相关的事件日志。

例如,假设我们想要查询所有由某个特定地址触发的LogTokenClaim事件,我们可以使用Web3.js或其他以太坊查询工具来过滤事件日志,指定address indexed sender参数为我们感兴趣的地址。这样,我们就可以只获取与特定地址相关的事件日志,而无需遍历整个事件日志列表。这样的查询可以更快速地检索到我们感兴趣的事件。

总的来说,使用indexed关键字对事件参数进行索引可以提高事件查询的效率和灵活性。

改进的kmc.sol方法三(交易发送者的地址作为一个参数传递给零知识证明的计算)

为了实现将交易发送者的地址(或其他相关数据)绑定到零知识证明中,我们需要做以下修改:

  1. tokenClaim 函数中,将交易发送者的地址作为一个参数传递给零知识证明的计算。为此,我们需要在函数签名中添加一个新的参数,用于接收交易发送者的地址。在这里,我们将使用 msg.sender 来获取交易发送者的地址。

  2. tokenResponse 函数中,同样将交易发送者的地址作为一个参数传递给零知识证明的计算。

以下是修改后的合约代码:

pragma solidity ^0.5.0;
import "verify.sol";
import "ARC.sol";

contract KMC {
    ARC arc;
    CVC cvc;
    RVC rvc;
    mapping(bytes32 => bytes32) public claims;
    mapping(bytes32 => bytes32) public adjunction;
    event LogTokenClaim(address, bytes32, bytes32);
    event LogTokenResponse(bytes32, string);
    
    function setcliam(address c) public {
        cvc = CVC(c);
    }
    
    function setarc(address a) public {
        arc = ARC(a);
    }
    
    function setres(address r) public {
        rvc = RVC(r);
    }
    
    function tokenClaim(uint[2] memory a, uint[2][2] memory b, uint[2] memory c, uint[5] memory input) public returns (bool) {
        bytes32 id_claim = packToBytes32(input[2], input[3]);
        bytes32 id = packToBytes32(input[0], input[1]); 
        
        // Add the sender's address as an additional input to the proof将发件人的地址添加为校样的附加输入
        bool result = cvc.claimPKVerify(a, b, c, input, msg.sender);
        require(result == true, "verify failed!");
        require(arc.creatorQuery(id) == msg.sender, "you are not the creator of the token");
        require(arc.claimedQuery(id) == false, "this token has been claimed");
        require(arc.exisitQuery(id) == true, "this token has been revoked!");
        claims[id_claim] = id_claim;
        arc.changeStatus(id);
        emit LogTokenClaim(msg.sender, id, id_claim);
        return true;
    }
    
    function tokenResponse(uint[2] memory a, uint[2][2] memory b, uint[2] memory c, uint[7] memory input, string memory res) public returns (bool) {
        bytes32 id = packToBytes32(input[0], input[1]);
        bytes32 id_claim = packToBytes32(input[2], input[3]);
        bytes32 id_ne = packToBytes32(input[4], input[5]);
        
        // Add the sender's address as an additional input to the proof将发件人的地址添加为校样的附加输入
        bool result = rvc.responseSKVerify(a, b, c, input, msg.sender);
        require(result == true, "verify failed!");
        require(arc.exisitQuery(id) == true, "this token has been revoked!");
        require(claims[id_claim] == id_claim, "this token has not been claimed");
        require(adjunction[id_ne] == 0, "proof has been used!");
        adjunction[id_ne] = id_ne;
        emit LogTokenResponse(id, res);
        return true;
    } 
    
    function packToBytes32(uint256 high, uint256 low) public pure returns (bytes32) {
        return bytes32(low) | (bytes32(high) << 128);
    }
}

在上述修改中,我们通过在函数签名中添加一个 address 类型的参数来接收交易发送者的地址,并将其作为额外的输入参数传递给 claimPKVerifyresponseSKVerify 函数。这样,每个具体的交易都会绑定交易发送者的地址,防止证明被用于其他交易,从而增强隐私保护。

修改CVC合约中的函数

function claimPKVerify(
    uint[2] memory a,
    uint[2][2] memory b,
    uint[2] memory c,
    uint[5] memory input,
    address sender
) public returns (bool r) {
    Proof memory proof;
    proof.a = Pairing.G1Point(a[0], a[1]);
    proof.b = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]);
    proof.c = Pairing.G1Point(c[0], c[1]);
    uint[] memory inputValues = new uint[](input.length + 1); // 在原有的输入数组长度上增加一个额外位置
    for(uint i = 0; i < input.length; i++){
        inputValues[i] = input[i];
    }
    inputValues[input.length] = uint(sender); // 将发送者地址转换为 uint 类型并添加到输入数组末尾
    if (verify(inputValues, proof) == 0) {
        emit Verified("Transaction successfully verified.");
        return true;
    } else {
        return false;
    }
}

修改合约RVC中的函数responseSKVerify 

function responseSKVerify(
        uint[2] memory a,
        uint[2][2] memory b,
        uint[2] memory c,
        uint[7] memory input,
        address sender // 添加地址参数
    ) public returns (bool r) {
        Proof memory proof;
        proof.a = Pairing.G1Point(a[0], a[1]);
        proof.b = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]);
        proof.c = Pairing.G1Point(c[0], c[1]);
        uint[] memory inputValues = new uint[](input.length);
        for(uint i = 0; i < input.length; i++){
            inputValues[i] = input[i];
        }
        inputValues[input.length] = uint(sender); // 将发送者地址转换为 uint 类型并添加到输入数组末尾
        if (verify(inputValues, proof) == 0) { 
            emit Verified("Transaction successfully verified.");
            return true;
        } else {
            return false;
        }
    }

ARC.SOL改进一(使用修饰器(modifier)可以在函数执行前对权限进行检查)

问题:安全性不足: 合约没有添加足够的权限控制机制,导致任何人都能调用tokenRevocation函数来撤销属性。应该对撤销操作进行权限控制,只有属性创建者或其他特定权限持有者才能执行撤销操作,以防止非授权用户恶意撤销属性。

解决:权限控制: 添加权限控制机制,确保只有属性创建者或其他授权用户才能执行撤销操作。可以使用修饰器(modifier)来限制只有合法用户可以执行撤销操作。

使用修饰器(modifier)可以在函数执行前对权限进行检查,从而限制只有合法用户可以执行撤销操作。下面是如何使用修饰器来实现权限控制:

pragma solidity ^0.5.0;

contract ARC {
    // ...(省略其他合约代码)

    // 修饰器:用于限制只有合法用户可以执行撤销操作
    modifier onlyCreator(bytes32 _tokenId) {
        require(msg.sender == tokens[_tokenId].creator, "You are not the creator of this token");
        _; // 执行原函数
    }

    function tokenRevocation(bytes32 _tokenId) public onlyCreator(_tokenId) {
        revocation(_tokenId);
        emit LogTokenRevocation(_tokenId);
    }

    // ...(省略其他合约代码)
}

上述代码中,我们定义了名为onlyCreator的修饰器,它会检查msg.sender是否等于指定_tokenId对应的属性的创建者(creator)。如果满足条件,修饰器内的代码将继续执行,否则将抛出异常并阻止函数继续执行。

tokenRevocation函数中,我们在函数定义之前使用了修饰器onlyCreator(_tokenId)。这样,当调用tokenRevocation函数时,会先执行修饰器内的权限检查,如果msg.sender是合法的创建者,那么才会执行撤销操作,否则将会抛出异常。

通过使用修饰器,我们可以确保只有合法的用户(即属性创建者)可以执行撤销操作,增强了合约的安全性和权限控制。

ARC.SOL改进二(使用加密算法对属性信息进行加密后再存储)

问题:属性哈希存储: 合约中使用属性哈希作为tokenId,这样会导致属性信息不可读。在实际应用中,可能需要考虑将属性信息明文存储或使用其他方式实现属性的唯一标识,方便用户查看和使用属性。

解决:属性信息存储: 考虑使用明文存储属性信息,或者使用加密算法对属性信息进行加密后再存储。这样可以方便用户查看和使用属性信息,同时保护属性信息的隐私安全。

为了使用加密算法对属性信息进行加密后再存储,我们可以使用 Solidity 中提供的加密库或外部库。在这里,我们使用了一个简单的加密/解密示例,用于演示加密和解密属性信息。请注意,此示例中使用的加密算法可能不足以用于真实的生产环境,并且还可能增加 gas 成本和合约复杂性。在实际应用中,您应该选择更加安全和高级的加密算法,并仔细考虑安全性和性能问题。

pragma solidity ^0.5.0;

contract ARC {
    struct token {
        bytes32 encryptedName; // Encrypted name
        bytes32 encryptedValue; // Encrypted value
        bytes32 creatorHash;
        address creator;
        bytes32 hash;
        bool exisit;
        bool claimed;
    }
    address owner;
    mapping(bytes32 => token) tokens;
    event LogTokenCreate(bytes32, bytes32, bytes32, address, bytes32);
    event LogTokenRevocation(bytes32);

    function encryptData(string memory data, string memory key) private pure returns (bytes32) {
        // Here, you can use more advanced encryption algorithms.在这里,您可以使用更高级的加密算法。
        // For simplicity, we use a basic XOR encryption as an example.为了简单起见,我们使用一个基本的XOR加密作为示例。
        bytes memory dataBytes = bytes(data);
        bytes memory keyBytes = bytes(key);
        bytes memory encryptedData = new bytes(dataBytes.length);
        for (uint256 i = 0; i < dataBytes.length; i++) {
            encryptedData[i] = dataBytes[i] ^ keyBytes[i % keyBytes.length];
        }
        return keccak256(encryptedData);
    }

    function tokenCreate(string memory _name, string memory _value, bytes32 _hash, string memory encryptionKey) public returns (bytes32) {
        bytes32 encryptedName = encryptData(_name, encryptionKey);
        bytes32 encryptedValue = encryptData(_value, encryptionKey);
        bytes32 tokenId = sha256(abi.encodePacked(encryptedName, encryptedValue, _hash, _name));
        tokens[tokenId].encryptedName = encryptedName;
        tokens[tokenId].encryptedValue = encryptedValue;
        tokens[tokenId].creatorHash = _hash;
        tokens[tokenId].creator = msg.sender;
        tokens[tokenId].hash = _hash;
        tokens[tokenId].exisit = true;
        tokens[tokenId].claimed = false;
        emit LogTokenCreate(tokenId, encryptedName, encryptedValue, msg.sender, _hash);
        return tokenId;
    }

    function decryptData(bytes32 encryptedData, string memory key) private pure returns (string memory) {
        // Here, you should implement the decryption logic that matches the encryption logic.在这里,您应该实现与加密逻辑匹配的解密逻辑。
        bytes memory dataBytes = abi.encodePacked(encryptedData);
        bytes memory keyBytes = bytes(key);
        bytes memory decryptedData = new bytes(dataBytes.length);
        for (uint256 i = 0; i < dataBytes.length; i++) {
            decryptedData[i] = dataBytes[i] ^ keyBytes[i % keyBytes.length];
        }
        return string(decryptedData);
    }

    function getTokenData(bytes32 tokenId, string memory decryptionKey) public view returns (string memory, string memory, bytes32, address, bool, bool) {
        require(tokens[tokenId].exisit, "Token does not exist");
        string memory name = decryptData(tokens[tokenId].encryptedName, decryptionKey);
        string memory value = decryptData(tokens[tokenId].encryptedValue, decryptionKey);
        bytes32 creatorHash = tokens[tokenId].creatorHash;
        address creator = tokens[tokenId].creator;
        bool exisit = tokens[tokenId].exisit;
        bool claimed = tokens[tokenId].claimed;
        return (name, value, creatorHash, creator, exisit, claimed);
    }

    // Other functions remain the same
}

在这个版本中,我们添加了两个辅助函数 encryptDatadecryptData 来进行加密和解密操作。encryptData 函数用于将属性信息进行加密,decryptData 函数用于解密加密后的属性信息。加密和解密使用了一个简单的 XOR 加密算法作为示例,您可以根据需要选择更加安全和高级的加密算法。

请注意,在实际应用中,您应该妥善保管解密密钥,确保只有合法用户能够获得解密密钥,以保护属性信息的隐私安全。

ARC.SOL改进三(基于改进二使用加密算法对属性信息进行加密后再存储)

问题:属性认领机制: 合约中只有一个简单的claimed标志表示属性是否被认领,但没有具体的属性认领机制。在实际应用中,可能需要设计更复杂的属性认领机制,确保只有合法的权限持有者才能认领属性。

解决:属性认领机制: 设计一个复杂的属性认领机制,可以结合数字签名、多重签名或其他加密技术来确保只有合法的权限持有者才能认领属性。可以设置多个验证步骤,增加认领的难度,防止非法认领。

为了设计一个复杂的属性认领机制,我们可以结合数字签名和多重签名来确保只有合法的权限持有者才能认领属性。下面是对 ARC.sol 合约的改进,增加认领属性的复杂性和安全性:

pragma solidity ^0.5.0;

contract ARC {
    struct Token {
        bytes32 encryptedName; // Encrypted name
        bytes32 encryptedValue; // Encrypted value
        address creator;
        bytes32 hash;
        bool exisit;
        bool claimed;
    }

    address public owner;
    mapping(bytes32 => Token) public tokens;
    mapping(bytes32 => bool) public claimedTokens;

    event LogTokenCreate(bytes32 tokenId, bytes32 encryptedName, bytes32 encryptedValue, address creator, bytes32 hash);
    event LogTokenRevocation(bytes32 tokenId);
    event LogTokenClaim(bytes32 tokenId, address claimer);

    modifier onlyOwner() {
        require(msg.sender == owner, "You are not the owner");
        _;
    }

    constructor() public {
        owner = msg.sender;
    }

    function encryptData(string memory data, string memory key) private pure returns (bytes32) {
        // Here, you can use more advanced encryption algorithms.
        // For simplicity, we use a basic XOR encryption as an example.
        bytes memory dataBytes = bytes(data);
        bytes memory keyBytes = bytes(key);
        bytes memory encryptedData = new bytes(dataBytes.length);
        for (uint256 i = 0; i < dataBytes.length; i++) {
            encryptedData[i] = dataBytes[i] ^ keyBytes[i % keyBytes.length];
        }
        return keccak256(encryptedData);
    }

    function createToken(string memory name, string memory value, bytes32 hash, string memory encryptionKey) public returns (bytes32) {
        bytes32 encryptedName = encryptData(name, encryptionKey);
        bytes32 encryptedValue = encryptData(value, encryptionKey);
        bytes32 tokenId = sha256(abi.encodePacked(encryptedName, encryptedValue, hash, name));
        tokens[tokenId] = Token(encryptedName, encryptedValue, msg.sender, hash, true, false);
        emit LogTokenCreate(tokenId, encryptedName, encryptedValue, msg.sender, hash);
        return tokenId;
    }

    function revokeToken(bytes32 tokenId) public {
        require(tokens[tokenId].exisit, "Token does not exist");
        address creator = tokens[tokenId].creator;
        require(msg.sender == creator, "You are not the creator");
        tokens[tokenId].exisit = false;
        emit LogTokenRevocation(tokenId);
    }

    function claimToken(bytes32 tokenId, address claimer, bytes32 claimerHash) public onlyOwner {
        require(tokens[tokenId].exisit, "Token does not exist");
        require(!claimedTokens[tokenId], "Token already claimed");
        require(claimer == tokens[tokenId].creator, "Invalid claimer");

        // Here, you can perform additional verification steps if needed, such as multi-signature verification or additional checks.

        claimedTokens[tokenId] = true;
        tokens[tokenId].claimed = true;
        emit LogTokenClaim(tokenId, claimer);
    }

    function decryptData(bytes32 encryptedData, string memory key) private pure returns (string memory) {
        // Here, you should implement the decryption logic that matches the encryption logic.
        bytes memory dataBytes = abi.encodePacked(encryptedData);
        bytes memory keyBytes = bytes(key);
        bytes memory decryptedData = new bytes(dataBytes.length);
        for (uint256 i = 0; i < dataBytes.length; i++) {
            decryptedData[i] = dataBytes[i] ^ keyBytes[i % keyBytes.length];
        }
        return string(decryptedData);
    }

    function getTokenData(bytes32 tokenId, string memory decryptionKey) public view returns (string memory, string memory, bytes32, address, bool, bool) {
        require(tokens[tokenId].exisit, "Token does not exist");
        string memory name = decryptData(tokens[tokenId].encryptedName, decryptionKey);
        string memory value = decryptData(tokens[tokenId].encryptedValue, decryptionKey);
        bytes32 creatorHash = tokens[tokenId].hash;
        address creator = tokens[tokenId].creator;
        bool exisit = tokens[tokenId].exisit;
        bool claimed = tokens[tokenId].claimed;
        return (name, value, creatorHash, creator, exisit, claimed);
    }
}

在这个改进版本中,我们添加了以下内容:

  1. onlyOwner 修饰器:用于限制只有合约的拥有者才能调用某些函数,如 claimToken
  2. claimToken 函数:用于认领属性的复杂认证过程。在这里,我们只简单地检查认领者是否是创建者,但您可以根据需求实现更复杂的验证过程,如多重签名等。
  3. claimedTokens 映射:用于记录已认领的属性,防止重复认领。

请注意,在实际应用中,您还应该根据具体的业务需求和安全要求,进一步设计和改进认领机制,确保属性的安全性和合法性。

运用多重签名,检查认领者是否是创建者,修改claimToken 函数

为了运用多重签名来检查认领者是否是创建者,我们需要引入多个签名者,并且所有签名者都必须对认领进行确认。下面是修改后的 claimToken 函数,使用多重签名来验证认领者是否是创建者:

pragma solidity ^0.5.0;

contract ARC {
    struct Token {
        bytes32 encryptedName; // Encrypted name
        bytes32 encryptedValue; // Encrypted value
        address creator;
        bytes32 hash;
        bool exisit;
        bool claimed;
    }

    address public owner;
    uint public numSignersRequired; // Number of required signers for claim索赔要求的签字人数量
    mapping(bytes32 => Token) public tokens;
    mapping(bytes32 => bool) public claimedTokens;
    mapping(bytes32 => mapping(address => bool)) public claimSignatures;

    event LogTokenCreate(bytes32 tokenId, bytes32 encryptedName, bytes32 encryptedValue, address creator, bytes32 hash);
    event LogTokenRevocation(bytes32 tokenId);
    event LogTokenClaim(bytes32 tokenId, address claimer);

    modifier onlyOwner() {
        require(msg.sender == owner, "You are not the owner");
        _;
    }

    constructor(uint _numSignersRequired) public {
        owner = msg.sender;
        numSignersRequired = _numSignersRequired;
    }

    function encryptData(string memory data, string memory key) private pure returns (bytes32) {
        // Encryption logic...加密逻辑。。。
    }

    function createToken(string memory name, string memory value, bytes32 hash, string memory encryptionKey) public returns (bytes32) {
        // Token creation logic...令牌创建逻辑。。。
    }

    function revokeToken(bytes32 tokenId) public {
        // Token revocation logic...令牌吊销逻辑。。。
    }

    function claimToken(bytes32 tokenId, address[] memory signers, bytes32[] memory signatures) public {
        require(tokens[tokenId].exisit, "Token does not exist");
        require(!claimedTokens[tokenId], "Token already claimed");
        require(signers.length == numSignersRequired, "Invalid number of signers");

        bytes32 messageHash = sha256(abi.encodePacked(this, tokenId));
        for (uint i = 0; i < signers.length; i++) {
            address signer = signers[i];
            require(claimSignatures[tokenId][signer] == false, "Signer already used");
            require(verifySignature(messageHash, signer, signatures[i]), "Invalid signature");
            claimSignatures[tokenId][signer] = true;
        }

        tokens[tokenId].claimed = true;
        claimedTokens[tokenId] = true;
        emit LogTokenClaim(tokenId, msg.sender);
    }

    function verifySignature(bytes32 messageHash, address signer, bytes32 signature) private pure returns (bool) {
        // Signature verification logic...签名验证逻辑。。。
    }

    function decryptData(bytes32 encryptedData, string memory key) private pure returns (string memory) {
        // Decryption logic...解密逻辑。。。
    }

    function getTokenData(bytes32 tokenId, string memory decryptionKey) public view returns (string memory, string memory, bytes32, address, bool, bool) {
        // Get token data logic...获取令牌数据逻辑。。。
    }
}

在这个修改版本中,我们添加了以下内容:

  1. numSignersRequired 变量:用于存储认领所需的签名者数量,即多重签名要求的签名者数量。
  2. claimSignatures 映射:用于记录已经对认领进行过签名的签名者。
  3. claimToken 函数的修改:现在 claimToken 函数接受两个参数 signerssignatures,其中 signers 是签名者的地址数组,signatures 是对应的签名数组。在函数中,我们检查签名者的数量是否满足要求,并逐个验证签名者的签名是否有效,并标记已经签名过的签名者。

请注意,上述代码中的 verifySignature 函数是一个模拟的函数,用于验证签名的有效性。在实际应用中,您需要实现实际的签名验证逻辑。同时,您还可以根据实际需求和安全要求进一步扩展和改进多重签名机制。

ARC.SOL改进四(添加恢复已撤销属性的功能)

问题:撤销属性后无法恢复: 一旦属性被撤销,合约中没有提供恢复已撤销属性的功能。在某些场景下,可能需要考虑添加恢复功能,允许合法用户重新创建已撤销的属性。

解决:恢复已撤销属性: 添加恢复已撤销属性的功能,允许合法用户重新创建已撤销的属性。这样可以增加用户的灵活性,避免不小心撤销属性造成不可挽回的损失。

pragma solidity ^0.5.0;

contract ARC {
    struct token {
        string name;
        string value;
        address creator;
        bytes32 hash;
        bool exisit;
        bool claimed;
    }

    address owner;
    mapping(bytes32 => token) tokens;
    mapping(bytes32 => bool) public revokedTokens;
    event LogTokenCreate(bytes32, string, string, address, bytes32);
    event LogTokenRevocation(bytes32);
    event LogTokenRestore(bytes32);

    constructor() public {
        owner = msg.sender;
    }

    modifier onlyOwner() {
        require(msg.sender == owner, "Only owner can perform this action");
        _;
    }

    function tokenCreate(string memory _name, string memory _value, bytes32 _hash) public returns (bytes32) {
        bytes32 tokenId = sha256(abi.encodePacked(_name, _value, _hash, _name));
        tokens[tokenId].name = _name;
        tokens[tokenId].value = _value;
        tokens[tokenId].creator = msg.sender;
        tokens[tokenId].hash = _hash;
        tokens[tokenId].exisit = true;
        tokens[tokenId].claimed = false;
        emit LogTokenCreate(tokenId, _name, _value, msg.sender, _hash);
        return tokenId;
    }

    function tokenRevocation(bytes32 _tokenId) public {
        address creator = creatorQuery(_tokenId);
        require(msg.sender == creator, "Only creator can revoke the token");
        require(tokens[_tokenId].exisit, "Token does not exist");
        require(!revokedTokens[_tokenId], "Token already revoked");

        revokedTokens[_tokenId] = true;
        tokens[_tokenId].exisit = false;
        tokens[_tokenId].claimed = false;
        emit LogTokenRevocation(_tokenId);
    }

   /* function restoreToken(bytes32 _tokenId) public onlyOwner {
        require(revokedTokens[_tokenId], "Token is not revoked");

        tokens[_tokenId].exisit = true;
        tokens[_tokenId].claimed = false;
        revokedTokens[_tokenId] = false;
        emit LogTokenRestore(_tokenId);
    } */
   function restoreToken(bytes32 _tokenId) public onlyOwner {
        require(claimedTokens[_tokenId], "Token is not revoked");
 
        tokens[_tokenId].exisit = true;
        tokens[_tokenId].claimed = false;
        claimedTokens[_tokenId] = false;
        emit LogTokenRevocation(_tokenId);
    }

    function creatorQuery(bytes32 _tokenId) public view returns (address) {
        address creator = tokens[_tokenId].creator;
        return creator;
    }

    function exisitQuery(bytes32 _tokenId) public view returns (bool) {
        bool exisit = tokens[_tokenId].exisit;
        return exisit;
    }

    function claimedQuery(bytes32 _tokenId) public view returns (bool) {
        bool claimed = tokens[_tokenId].claimed;
        return claimed;
    }

    function tokenQuery(bytes32 _tokenId) public view returns (bytes32, string memory, string memory, bytes32, address, bool, bool) {
        string memory name = tokens[_tokenId].name;
        string memory value = tokens[_tokenId].value;
        bytes32 hash = tokens[_tokenId].hash;
        address creator = tokens[_tokenId].creator;
        bool exisit = tokens[_tokenId].exisit;
        bool claimed = tokens[_tokenId].claimed;
        return (_tokenId, name, value, hash, creator, exisit, claimed);
    }
}

现在,我们已经添加了restoreToken函数,只有合约的拥有者(owner)可以调用该函数来恢复已撤销的属性。当属性被恢复后,它将重新出现在合约中,并可以再次被认领。

ARC.SOL改进五(Gas消耗的优化)

对于Gas消耗的优化,我们可以考虑使用更高效的哈希函数和数据结构。在Solidity 0.5.0版本中,sha256是默认的哈希函数,但它可能会消耗较多的Gas。我们可以尝试使用较低消耗的哈希函数,并优化数据结构。

在这里,我们将使用keccak256哈希函数替代sha256,因为keccak256通常比sha256更高效。此外,我们还将使用mapping数据结构来存储属性信息,以提高查找和更新的效率。

以下是优化后的ARC.sol合约:

pragma solidity ^0.5.0;

contract ARC {
    struct Token {
        string name;
        string value;
        address creator;
        bool exist;
        bool claimed;
    }

    address owner;
    mapping(bytes32 => Token) tokens;
    mapping(bytes32 => bool) public revokedTokens;
    event LogTokenCreate(bytes32, string, string, address);
    event LogTokenRevocation(bytes32);
    event LogTokenRestore(bytes32);

    constructor() public {
        owner = msg.sender;
    }

    modifier onlyOwner() {
        require(msg.sender == owner, "Only owner can perform this action");
        _;
    }

    function keccakHash(string memory _name, string memory _value, bytes32 _hash) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked(_name, _value, _hash));
    }

    function tokenCreate(string memory _name, string memory _value, bytes32 _hash) public returns (bytes32) {
        bytes32 tokenId = keccakHash(_name, _value, _hash);
        require(!tokens[tokenId].exist, "Token already exists");

        tokens[tokenId].name = _name;
        tokens[tokenId].value = _value;
        tokens[tokenId].creator = msg.sender;
        tokens[tokenId].exist = true;
        tokens[tokenId].claimed = false;
        emit LogTokenCreate(tokenId, _name, _value, msg.sender);
        return tokenId;
    }

    function tokenRevocation(bytes32 _tokenId) public {
        address creator = tokens[_tokenId].creator;
        require(msg.sender == creator, "Only creator can revoke the token");
        require(tokens[_tokenId].exist, "Token does not exist");
        require(!revokedTokens[_tokenId], "Token already revoked");

        revokedTokens[_tokenId] = true;
        tokens[_tokenId].exist = false;
        tokens[_tokenId].claimed = false;
        emit LogTokenRevocation(_tokenId);
    }

    function restoreToken(bytes32 _tokenId) public onlyOwner {
        require(revokedTokens[_tokenId], "Token is not revoked");

        tokens[_tokenId].exist = true;
        tokens[_tokenId].claimed = false;
        revokedTokens[_tokenId] = false;
        emit LogTokenRestore(_tokenId);
    }

    function creatorQuery(bytes32 _tokenId) public view returns (address) {
        address creator = tokens[_tokenId].creator;
        return creator;
    }

    function existQuery(bytes32 _tokenId) public view returns (bool) {
        bool exist = tokens[_tokenId].exist;
        return exist;
    }

    function claimedQuery(bytes32 _tokenId) public view returns (bool) {
        bool claimed = tokens[_tokenId].claimed;
        return claimed;
    }

    function tokenQuery(bytes32 _tokenId) public view returns (bytes32, string memory, string memory, address, bool, bool) {
        string memory name = tokens[_tokenId].name;
        string memory value = tokens[_tokenId].value;
        address creator = tokens[_tokenId].creator;
        bool exist = tokens[_tokenId].exist;
        bool claimed = tokens[_tokenId].claimed;
        return (_tokenId, name, value, creator, exist, claimed);
    }
}

在优化后的合约中,我们使用了keccak256函数进行哈希,而不是sha256,并将exist属性更名为exist,以更好地反映其含义。此外,我们还添加了keccakHash内部函数来处理属性哈希,并对存储的属性信息进行更高效的查找和更新。

ARC.SOL改进六(允许用户更新已创建的属性信息)

为使合约能够允许用户更新已创建的属性信息,我们需要添加一个名为updateToken的新函数。以下是具体步骤:

  1. 添加updateToken函数:在合约中添加一个新的公共函数updateToken,该函数用于允许用户更新已创建的属性信息。

  2. 参数验证:updateToken函数需要接收tokenIdnamevaluehash作为参数。在函数内部,我们需要验证调用者是否是属性的创建者,只有创建者才有权限更新属性。

  3. 属性更新:如果验证通过,我们将更新该属性的namevaluehash为新的输入值。

下面是修改后的合约代码,包含添加的updateToken函数:

pragma solidity ^0.5.0;

contract ARC {
    struct token {
        string name;
        string value;
        address creator;
        bytes32 hash;
        bool exisit;
        bool claimed;
    }
    
    address owner;
    mapping(bytes32 => token) tokens;
    
    event LogTokenCreate(bytes32, string, string, address, bytes32);
    event LogTokenRevocation(bytes32);
    event LogTokenUpdate(bytes32, string, string, bytes32);
    
    function tokenCreate(string memory _name, string memory _value, bytes32 _hash) public returns (bytes32) {
        bytes32 tokenId = sha256(abi.encodePacked(_name, _value, _hash, _name));
        tokens[tokenId].name = _name;
        tokens[tokenId].value = _value;
        tokens[tokenId].creator = msg.sender;
        tokens[tokenId].hash = _hash;
        tokens[tokenId].exisit = true;
        tokens[tokenId].claimed = false;
        emit LogTokenCreate(tokenId, _name, _value, msg.sender, _hash);
        return tokenId;
    }
    
    function tokenRevocation(bytes32 _tokenId) public {
        address creator = creatorQuery(_tokenId);
        if (msg.sender == creator) {
            revocation(_tokenId);
            emit LogTokenRevocation(_tokenId);
        }
    }
    
    function updateToken(bytes32 _tokenId, string memory _name, string memory _value, bytes32 _hash) public {
        address creator = creatorQuery(_tokenId);
        require(msg.sender == creator, "只有属性的创建者有权限更新属性");
        tokens[_tokenId].name = _name;
        tokens[_tokenId].value = _value;
        tokens[_tokenId].hash = _hash;
        emit LogTokenUpdate(_tokenId, _name, _value, _hash);
    }
    
    // 其他函数保持不变
}

以上修改后的合约代码允许用户更新已创建属性的信息,提供了更灵活和适应用户需求变化的功能。当用户调用updateToken函数时,合约将验证调用者是否是属性的创建者,并且只有创建者才能成功更新属性信息。

ARC.SOL改进七(优化事件记录的数据,减少区块链上的数据存储和查询成本)

为优化事件记录的数据,减少区块链上的数据存储和查询成本,我们可以做以下几点修改:

  1. 减少重复记录:避免在事件中重复记录已有的信息。目前在LogTokenCreate事件中记录了属性的tokenIdnamevaluecreatorhash,其中tokenId在函数内部已经返回了,无需重复记录。我们只需记录tokenIdcreatorhash信息。

  2. 移除不必要的事件:目前在撤销属性的tokenRevocation函数中,触发了LogTokenRevocation事件。但在撤销的同时,属性的exisit状态会被设置为false,因此在LogTokenCreate事件中记录的属性已经被撤销了,无需再记录撤销事件。因此,可以移除tokenRevocation函数中的事件触发。

以下是修改后的合约代码,对应上述的优化措施:

pragma solidity ^0.5.0;

contract ARC {
    struct token {
        string name;
        string value;
        address creator;
        bytes32 hash;
        bool exisit;
        bool claimed;
    }
    
    address owner;
    mapping(bytes32 => token) tokens;
    
    event LogTokenCreate(bytes32 indexed tokenId, address indexed creator, bytes32 hash);
    
    function tokenCreate(string memory _name, string memory _value, bytes32 _hash) public returns (bytes32) {
        bytes32 tokenId = sha256(abi.encodePacked(_name, _value, _hash, _name));
        tokens[tokenId].name = _name;
        tokens[tokenId].value = _value;
        tokens[tokenId].creator = msg.sender;
        tokens[tokenId].hash = _hash;
        tokens[tokenId].exisit = true;
        tokens[tokenId].claimed = false;
        emit LogTokenCreate(tokenId, msg.sender, _hash);
        return tokenId;
    }
    
    function tokenRevocation(bytes32 _tokenId) public {
        address creator = creatorQuery(_tokenId);
        require(msg.sender == creator, "只有属性的创建者有权限撤销属性");
        tokens[_tokenId].exisit = false;
    }
    
    function changeStatus(bytes32 _tokenId) public {
        tokens[_tokenId].claimed = true;
    }
    
    function creatorQuery(bytes32 _tokenId) public view returns(address) {
        address creator = tokens[_tokenId].creator;
        return creator;
    }
    
    function exisitQuery(bytes32 _tokenId) public view returns(bool) {
        bool exisit = tokens[_tokenId].exisit;
        return exisit;
    }
    
    function claimedQuery(bytes32 _tokenId) public view returns(bool) {
        bool claimed = tokens[_tokenId].claimed;
        return claimed;
    }
    
    function tokenQuery(bytes32 _tokenId) public view returns(bytes32, string memory, string memory, bytes32, address, bool, bool) {
        string memory name = tokens[_tokenId].name;
        string memory value = tokens[_tokenId].value;
        bytes32 hash = tokens[_tokenId].hash;
        address creator = tokens[_tokenId].creator;
        bool exisit = tokens[_tokenId].exisit;
        bool claimed = tokens[_tokenId].claimed;
        return (_tokenId, name, value, hash, creator, exisit, claimed);
    }
}

通过上述修改,我们优化了事件记录的数据,减少了不必要的重复记录,降低了区块链上的数据存储和查询成本。

ARC.SOL改进八(要实现 _value,_name 的机密性)

要实现交易方 _name 的机密性,我们可以使用加密算法对 _name 进行加密,并在合约中存储加密后的值。只有授权用户才能解密该值,这样可以保护 _name 的隐私安全。

以下是修改后的合约代码,使用加密算法进行交易方名称的加密和解密

pragma solidity ^0.5.0;

contract ARC {
    struct token {
        bytes encryptedName; // 修改为字节数组,用于存储加密后的交易方名称
        bytes encryptedValue; // 修改为字节数组,用于存储加密后的属性值
        address creator;
        bytes32 hash;
        bool exist;
        bool claimed;
    }
    address owner;
    mapping(bytes32 => token) tokens;
    event LogTokenCreate(bytes32, bytes, address, bytes32);
    event LogTokenRevocation(bytes32);

    function tokenCreate(bytes memory _name, bytes memory _value, bytes32 _hash) public returns (bytes32) {
        bytes32 tokenId = sha256(abi.encodePacked(_name, _value, _hash, _name));
        bytes memory encryptedName = encrypt(_name); // 加密交易方名称
        bytes memory encryptedValue = encrypt(_value); // 加密属性值
        tokens[tokenId].encryptedName = encryptedName;
        tokens[tokenId].encryptedValue = encryptedValue;
        tokens[tokenId].creator = msg.sender;
        tokens[tokenId].hash = _hash;
        tokens[tokenId].exist = true;
        tokens[tokenId].claimed = false;
        emit LogTokenCreate(tokenId, encryptedName, msg.sender, _hash);
        return tokenId;
    }

    function tokenRevocation(bytes32 _tokenId) public {
        address creator = creatorQuery(_tokenId);
        if (msg.sender == creator){
            revocation(_tokenId);
            emit LogTokenRevocation(_tokenId);
        }
    }

    function changeStatus(bytes32 _tokenId) public {
        tokens[_tokenId].claimed = true;
    }

    function revocation(bytes32 _tokenId) public {
        tokens[_tokenId].exist = false;
    }

    function creatorQuery(bytes32 _tokenId) public view returns(address) {
        address creator = tokens[_tokenId].creator;
        return creator;
    }

    function existQuery(bytes32 _tokenId) public view returns(bool) {
        bool exist = tokens[_tokenId].exist;
        return exist;
    }

    function claimedQuery(bytes32 _tokenId) public view returns(bool) {
        bool claimed = tokens[_tokenId].claimed;
        return claimed;
    }

    function tokenQuery(bytes32 _tokenId) public view returns (bytes32, bytes memory, bytes memory, bytes32, address, bool, bool) {
        bytes memory encryptedName = tokens[_tokenId].encryptedName;
        bytes memory encryptedValue = tokens[_tokenId].encryptedValue;
        bytes32 hash = tokens[_tokenId].hash;
        address creator = tokens[_tokenId].creator;
        bool exist = tokens[_tokenId].exist;
        bool claimed = tokens[_tokenId].claimed;
        return (_tokenId, encryptedName, encryptedValue, hash, creator, exist, claimed);
    }

    // 加密函数,可以根据需要选择合适的加密算法
    function encrypt(bytes memory _data) private pure returns (bytes memory) {
        // 在实际应用中,这里应该使用更强大的加密算法进行加密
        return abi.encodePacked(_data);
    }
}

在修改后的合约中,我们将 _name_value 修改为 bytes 类型,用于存储加密后的交易方名称和属性值。加密函数 encrypt 可以根据实际需求选择更合适的加密算法,确保交易方名称和属性值的机密性。在此示例中,我们简单地使用了 abi.encodePacked,实际使用时应该选择更加安全的加密算法。

你可能感兴趣的:(学习)