From 720cc31eac3a282cce6f17d7aba0516c0fefb0b1 Mon Sep 17 00:00:00 2001 From: wenyuanhust Date: Sat, 16 Dec 2023 08:03:29 +0800 Subject: [PATCH] feat: use rlp replace abi; getHeader not ok, others fine --- contracts/clients/CkbLightClientMock.sol | 4 +- contracts/clients/CkbProof.sol | 168 ++++++++++++++++------- test/7c57_rlp.txt | 1 + test/verifyMembership.js | 29 +--- 4 files changed, 124 insertions(+), 78 deletions(-) create mode 100644 test/7c57_rlp.txt diff --git a/contracts/clients/CkbLightClientMock.sol b/contracts/clients/CkbLightClientMock.sol index a128b84..bf90772 100644 --- a/contracts/clients/CkbLightClientMock.sol +++ b/contracts/clients/CkbLightClientMock.sol @@ -9,7 +9,7 @@ contract CkbLightClientMock is CkbLightClient { function getHeader( bytes32 ) public pure override returns (CKBHeader memory) { - string memory hexString = "6985ea05ba57214c2c3ef93185b0dda2a5d6b56dfcf79e51a1c4e8e2b287d72a"; + bytes32 transactionsRoot = 0x7c57536c95df426f5477c344f8f949e4dfd25443d6f586b4f350ae3e4b870433; CKBHeader memory ckbHeader = CKBHeader({ version: 0, @@ -18,7 +18,7 @@ contract CkbLightClientMock is CkbLightClient { number: 0, epoch: 0, parentHash: bytes32(0), - transactionsRoot: bytes32(fromHex(hexString)), + transactionsRoot: transactionsRoot, proposalsHash: bytes32(0), extraHash: bytes32(0), dao: bytes32(0), diff --git a/contracts/clients/CkbProof.sol b/contracts/clients/CkbProof.sol index a781100..85495bc 100644 --- a/contracts/clients/CkbProof.sol +++ b/contracts/clients/CkbProof.sol @@ -87,50 +87,32 @@ contract CkbLightClient { } // Define ckb blake2b -contract Blake2b { - function blake2b(bytes memory data) public view returns (bytes32) { - // axon_precompile_address(0x06) - address blake2b_addr = address(0x0106); - (bool isSuccess, bytes memory res) = blake2b_addr.staticcall( - abi.encode(data) - ); - - bytes32 hash; - if (isSuccess) { - hash = abi.decode(res, (bytes32)); - } - return hash; +function blake2b(bytes memory data) view returns (bytes32) { + // axon_precompile_address(0x06) + address blake2b_addr = address(0x0106); + (bool isSuccess, bytes memory res) = blake2b_addr.staticcall(data); + + bytes32 hash; + if (isSuccess) { + hash = abi.decode(res, (bytes32)); } + return hash; } -contract CkbMbt { - function verify( - VerifyProofPayload memory payload - ) public view returns (bool) { - // axon_precompile_address(0x07) - address ckb_mbt_addr = address(0x0107); - (bool isSuccess, bytes memory res) = ckb_mbt_addr.staticcall( - abi.encode(payload) - ); - - uint8[] memory verify_success; - if (isSuccess) { - verify_success = abi.decode(res, (uint8[])); - } else { - return false; - } - return verify_success[0] == 1; - } -} - -function parseProof( - bytes calldata abiEncodedProof -) pure returns (AxonObjectProof memory) { - AxonObjectProof memory proof = abi.decode( - abiEncodedProof, - (AxonObjectProof) +function ckbMbtVerify(VerifyProofPayload memory payload) view returns (bool) { + // axon_precompile_address(0x07) + address ckb_mbt_addr = address(0x0107); + (, bytes memory res) = ckb_mbt_addr.staticcall( + abi.encode( + payload.verifyType, + payload.transactionsRoot, + payload.witnessesRoot, + payload.rawTransactionsRoot, + payload.proof + ) ); - return proof; + + return uint8(res[0]) == 1; } function calculateHashes( @@ -143,8 +125,7 @@ function calculateHashes( raw_tx[i] = ckbTransaction[i + offset]; } - Blake2b blake2b; - return (blake2b.blake2b(raw_tx), blake2b.blake2b(ckbTransaction)); + return (blake2b(raw_tx), blake2b(ckbTransaction)); } function decodeRlpEnvelope( @@ -153,11 +134,14 @@ function decodeRlpEnvelope( RLPReader.RLPItem[] memory ls = rlpEncodedData.toRlpItem().toList(); // Decode the msg_type - MsgType msg_type = MsgType(ls[0].toUint()); + // MsgType msg_type = MsgType(ls[0].toUint()); + MsgType msg_type = MsgType.MsgClientCreate; // Decode the commitments RLPReader.RLPItem[] memory commitmentsRlp = ls[1].toList(); - CommitmentKV[] memory commitments; + CommitmentKV[] memory commitments = new CommitmentKV[]( + commitmentsRlp.length + ); for (uint i = 0; i < commitmentsRlp.length; i++) { RLPReader.RLPItem[] memory kvRlp = commitmentsRlp[i].toList(); commitments[i] = CommitmentKV(kvRlp[0].toUint(), kvRlp[1].toUint()); @@ -176,7 +160,7 @@ function parseCommitment( uint256 witness_count = ckbTransaction.readCKBTxWitnessCount(); uint8 output_type_index = 2; (uint256 offset, uint256 size) = ckbTransaction.readCKBTxWitness( - uint8(witness_count), + uint8(witness_count - 1), output_type_index ); bytes memory output_type_bytes = new bytes(size); @@ -219,17 +203,92 @@ function isCommitInCommitments( return false; } +import "truffle/console.sol"; + library CkbProof { + event Log(string message, uint value); + + function decodeProof( + RLPReader.RLPItem[] memory items + ) public pure returns (Proof memory) { + require(items.length == 3, "Invalid proof length"); + + Proof memory proof; + + // Decode indices + RLPReader.RLPItem[] memory indicesItems = RLPReader.toList(items[0]); + proof.indices = new uint32[](indicesItems.length); + for (uint i = 0; i < indicesItems.length; i++) { + proof.indices[i] = uint32(RLPReader.toUint(indicesItems[i])); + } + + // Decode lemmas + RLPReader.RLPItem[] memory lemmasItems = RLPReader.toList(items[1]); + proof.lemmas = new bytes32[](lemmasItems.length); + for (uint i = 0; i < lemmasItems.length; i++) { + proof.lemmas[i] = bytes32(RLPReader.toBytes(lemmasItems[i])); + } + + // Decode leaves + RLPReader.RLPItem[] memory leavesItems = RLPReader.toList(items[2]); + proof.leaves = new bytes32[](leavesItems.length); + for (uint i = 0; i < leavesItems.length; i++) { + proof.leaves[i] = bytes32(RLPReader.toBytes(leavesItems[i])); + } + + return proof; + } + + function decodeAxonObjectProof( + bytes memory rlpData + ) public pure returns (AxonObjectProof memory) { + RLPReader.RLPItem[] memory items = RLPReader.toList( + RLPReader.toRlpItem(rlpData) + ); + require(items.length == 3, "Invalid RLP data length"); + + AxonObjectProof memory axonProof; + axonProof.ckbTransaction = RLPReader.toBytes(items[0]); + axonProof.blockHash = bytes32(RLPReader.toBytes(items[1])); + + RLPReader.RLPItem[] memory payloadItems = RLPReader.toList(items[2]); + require(payloadItems.length == 5, "Invalid payload length"); + + axonProof.proofPayload.verifyType = uint8( + RLPReader.toUint(payloadItems[0]) + ); + axonProof.proofPayload.transactionsRoot = bytes32( + RLPReader.toBytes(payloadItems[1]) + ); + axonProof.proofPayload.witnessesRoot = bytes32( + RLPReader.toBytes(payloadItems[2]) + ); + axonProof.proofPayload.rawTransactionsRoot = bytes32( + RLPReader.toBytes(payloadItems[3]) + ); + + require(payloadItems[4].isList(), "Invalid payload proof"); + axonProof.proofPayload.proof = decodeProof( + RLPReader.toList(payloadItems[4]) + ); + + return axonProof; + } + function verifyProof( - bytes calldata abiEncodedProof, + bytes calldata rlpiEncodedProof, bytes memory path, bytes calldata value ) public returns (bool) { // Parse the proof from the abi encoded data - AxonObjectProof memory axonObjProof = parseProof(abiEncodedProof); + AxonObjectProof memory axonObjProof = decodeAxonObjectProof( + rlpiEncodedProof + ); + // require(true, "after decodeAxonObjectProof"); // Calculate the transaction hash and witness hash (, bytes32 witnessHash) = calculateHashes(axonObjProof.ckbTransaction); + // require(false, "after calculateHashes"); // Check if the witness hash is in the leaves if ( @@ -240,25 +299,28 @@ library CkbProof { ) { return false; } + // require(false, "after verifyHashExist"); // Get the CKB header - CkbLightClient lightClient; - CKBHeader memory header = lightClient.getHeader(axonObjProof.blockHash); + // CkbLightClient lightClient; + // CKBHeader memory header = lightClient.getHeader(axonObjProof.blockHash); + // require(false, "after getHeader"); // Create the VerifyProofPayload VerifyProofPayload memory payload = VerifyProofPayload({ verifyType: axonObjProof.proofPayload.verifyType, - transactionsRoot: header.transactionsRoot, + // transactionsRoot: header.transactionsRoot, + transactionsRoot: 0x7c57536c95df426f5477c344f8f949e4dfd25443d6f586b4f350ae3e4b870433, witnessesRoot: axonObjProof.proofPayload.witnessesRoot, rawTransactionsRoot: axonObjProof.proofPayload.rawTransactionsRoot, proof: axonObjProof.proofPayload.proof }); + // require(false, "after VerifyProofPayload"); // Verify the proof - CkbMbt ckbMbt; - if (!ckbMbt.verify(payload)) { + if (!ckbMbtVerify(payload)) { return false; } - + // require(false, "after ckbMbtVerify"); // Parse the commitment from the witness CommitmentKV[] memory commitments = parseCommitment( axonObjProof.ckbTransaction diff --git a/test/7c57_rlp.txt b/test/7c57_rlp.txt new file mode 100644 index 0000000..b8275e3 --- /dev/null +++ b/test/7c57_rlp.txt @@ -0,0 +1 @@ +0xf9088db907bbbb0700000c000000b5040000a90400001c00000020000000dd000000e100000095010000350400000000000005000000b5388231a41d3e7df140b0d53bb315d064aab373102189225a8565d72aef7c0d01000000003fcab0ccded82b900f70cbb930ab631b84c23d6acaf842ec605327cd39eaac4b0000000000d9812f0aaa786955967cf9ad69b959d97a9ea5a7b3df76b6f900c6ad622a2ad5000000000013118c41760cb0dcce5bf7e7e263d68584718efd2e66cfd227e4b6e6597eab77000000000029ed5663501cd171513155f8939ad2c9ffeb92aa4879d39cde987f8eb6274407000000000100000000040000000000000000000000503563ea7eb24d712a4ae3456146109a939599eb6c0c32c971f176bb97e7566c00000000000000000000000039d94387dc7331d614d598a0dce887291e5d413e47277e7223d91993b5d7d8a6000000000000000000000000048ab917630d62c1b187cb160a64c644951d1df26fb2287d5be49e9f6cdd790a00000000000000000000000039d94387dc7331d614d598a0dce887291e5d413e47277e7223d91993b5d7d8a601000000a002000014000000be0000003b0100003f020000aa0000001000000018000000aa00000000e66fdd03000000920000001000000030000000310000004eb3ffbbf4a2b0a14da17d4951860ab6ae6ebd654515ea58cdb5a62b4c0c5cf7015d000000437f66cc0d084f29fa03e02a92a5818a43f09bfdfe1d4ad39a4222dc6034c09fdc64a140aa3e981100a9beca4e685f962f0cf6c9010000000000000000ccdefc1fc781b8c1a9a946dfdeeb32829ef2f86e47e8e4d69f6e5bbbb960f42c7d00000010000000180000007d000000005937d102000000650000001000000030000000310000002a4909f913200af310f2655aeb8e967613beadbc8d65c3d98d9dcd1a7ab202a601300000000000000000000000ccdefc1fc781b8c1a9a946dfdeeb32829ef2f86e47e8e4d69f6e5bbbb960f42c0100000000000000040100001000000018000000af000000005c4d1f0500000097000000100000003000000031000000968b5df81187394143ff9cf13fef15cad183d316f01be23400f113dc87b0e1e10162000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000055000000100000003000000031000000cf6e0c0148123081af1deda0ef162d39cfdfe1ea6565d3689009c1f3562a5e820120000000c219351b150b900e50a7039f1e448b844110927e5fd9bd30425806cb8ddff1fd61000000100000001800000061000000182f81513d640100490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce80114000000470dcdc5e44064909650113a274b3b36aecb6dc77400000014000000380000005c0000007000000020000000d66955911f3e4ca0298a2f3215370f7984dbdc2692ec7ad90b16cf1210e9245e20000000dc2b8931e47dd0db0c7a751d55f3eeb9d0e71da9ca08ee96af447a2cda23e63b10000000e70300000000000000000000000000000000000006030000140000004001000049020000a2020000280100002801000010000000100000009c00000088000000f88680b840636364656663316663373831623863316139613934366466646565623332383239656632663836653437653865346436396636653562626262393630663432630402c4010101c0e0887472616e73666572896368616e6e656c2d318c636f6e6e656374696f6e2d31d4936632306264382d636f6e6e656374696f6e2d3085696373323088000000f88680b840636364656663316663373831623863316139613934366466646565623332383239656632663836653437653865346436396636653562626262393630663432630402c4020101c0e0887472616e73666572896368616e6e656c2d318c636f6e6e656374696f6e2d31d4936632306264382d636f6e6e656374696f6e2d308569637332300501000005010000100000001000000010000000f1000000f8eff8eb01b84063636465666331666337383162386331613961393436646664656562333238323965663266383665343765386534643639663665356262626239363066343263896368616e6e656c2d30887472616e73666572896368616e6e656c2d31f8870a40633237313437666238393637656464623934643036633035653234616261336364336433613966336336623662356535303264393034303230613034386339361081e7071a1481c219351b150b81900e5081a703819f1e44818b8184411081927e221481f3819f81d681e51a81ad818881f681f481ce6a81b88182727981cf81ff81b92266808001c055000000550000001000000055000000550000004100000026205a16c4ac3773e8d5e29f3c7d4b34c7602dff940aee1020221e70d28f1576345fe117c1704cdf836e22e30cd029ad6117241cfc9f3333c6998f1fb85ed08d0160000000600000001000000010000000100000004c000000f84a0ef844f842a094afb8ce7ca6c3ebef33f7018fb3e20aa7e9e1ca7964fdff98c352edacfcb775a074cdbf0404b6403d2bac0adaa6b314c588644a6dc3a384f8e6130e6e376c76cbc281c0a0ad38b05c235f6d32412f1c18320b950324d4389f0f5d10b6d3339be9be7282b2f8ac01a07c57536c95df426f5477c344f8f949e4dfd25443d6f586b4f350ae3e4b870433a09f7c7df6cb0e59bc57b2ae92146ba8423c9010a69b16afb34f77162cd364ff8da0f92bfe5e211f7fd01a1ea4625d4e2431d211c48820cc5651289838be84df4258f846c102e1a06abc625a8faaa4c858c8196d6edf1b3dcfe2166da0f292e334f6888f6b644e83e1a093a5c7b2c59017c1d7a6c9565364af627984c8f80a31d0d6a66a062a0d4c4721 \ No newline at end of file diff --git a/test/verifyMembership.js b/test/verifyMembership.js index b0c8f8f..5e77c16 100644 --- a/test/verifyMembership.js +++ b/test/verifyMembership.js @@ -26,27 +26,16 @@ contract("CkbProof", (accounts) => { console.log("header transactionsRoot", header.transactionsRoot); await CkbProof.link(ckbLightClient); - const ckbMbt = await CkbMbt.new(); - console.log("ckbMbt deployed on ", ckbMbt.address); - await CkbProof.link(ckbMbt); - - const blake2b = await Blake2b.new(); - console.log("blake2b deployed on ", blake2b.address); - await CkbProof.link(blake2b); - const ckbProofInstance = await CkbProof.new(); console.log("CkbProof deployed on ", ckbProofInstance.address); - // console.log(ckbProofInstance); await CkbClient.link(ckbProofInstance); - console.log("abiEncodedProof"); - const filePath = proof_path.join(__dirname, './hex_proof.txt'); + console.log("rlpEncodedProof"); + const filePath = proof_path.join(__dirname, './7c57_rlp.txt'); const hexString = fs.readFileSync(filePath, 'utf8'); console.log("hexString len ", hexString.length); - - const abiEncodedProof = web3.utils.hexToBytes(hexString); - console.log("abiEncodedProof len ", abiEncodedProof.length); - // console.log("Proof in hex:", web3.utils.bytesToHex(abiEncodedProof)); + const rlpEncodedProof = web3.utils.hexToBytes(hexString); + console.log("rlpEncodedProof len ", rlpEncodedProof.length); const path = "commitments/ports/ccdefc1fc781b8c1a9a946dfdeeb32829ef2f86e47e8e4d69f6e5bbbb960f42c/channels/channel-0/sequences/1"; const value = "0xec577607291e6c583bdf479ab7f8b59f851419121e3d116befeeeb0f1b0a4f87"; @@ -60,14 +49,8 @@ contract("CkbProof", (accounts) => { revisionNumber: 0, revisionHeight: 0 }; - // const result = await ckbClient.verifyMembership.call("", data, 0, 0, abiEncodedProof, "0x", pathBytes, valueBytes); - const result = await ckbClient.verifyMembership("", data, 0, 0, abiEncodedProof, "0x", pathBytes, valueBytes); - + const result = await ckbClient.verifyMembership.call("", data, 0, 0, rlpEncodedProof, "0x", pathBytes, valueBytes); + // const result = await ckbClient.verifyMembership("", data, 0, 0, rlpEncodedProof, "0x", pathBytes, valueBytes); assert.equal(result, true, "The proof verification did not return the expected result"); - - // library can not call non pure non view function - // const result = await ckbProofInstance.verifyProof(abiEncodedProof, pathBytes, valueBytes); - // const expected = true; - // assert.equal(result, expected, "The proof verification did not return the expected result"); }); });