Skip to content

Commit

Permalink
add nonexistence, left working
Browse files Browse the repository at this point in the history
  • Loading branch information
mconcat committed Mar 9, 2021
1 parent adf3454 commit 56c49d1
Show file tree
Hide file tree
Showing 4 changed files with 796 additions and 51 deletions.
47 changes: 47 additions & 0 deletions sol/ics23.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package ics23

import (
"math/big"

ics23 "github.com/confio/ics23/go"
)

func LeafOpToABI(op *ics23.LeafOp) ICS23LeafOp {
if op == nil {
return ICS23LeafOp{}
}
return ICS23LeafOp{
Valid: true,
Hash: uint8(op.Hash),
PrehashKey: uint8(op.PrehashKey),
PrehashValue: uint8(op.PrehashValue),
Expand All @@ -15,22 +21,63 @@ func LeafOpToABI(op *ics23.LeafOp) ICS23LeafOp {
}

func InnerOpToABI(op *ics23.InnerOp) ICS23InnerOp {
if op == nil {
return ICS23InnerOp{}
}
return ICS23InnerOp{
Valid: true,
Hash: uint8(op.Hash),
Prefix: op.Prefix,
Suffix: op.Suffix,
}
}

func ExistenceProofToABI(op *ics23.ExistenceProof) ICS23ExistenceProof {
if op == nil {
return ICS23ExistenceProof{}
}
path := make([]ICS23InnerOp, len(op.Path))
for i, op := range op.Path {
path[i] = InnerOpToABI(op)
}
return ICS23ExistenceProof{
Valid: true,
Key: op.Key,
Value: op.Value,
Leaf: LeafOpToABI(op.Leaf),
Path: path,
}
}

func NonExistenceProofToABI(op *ics23.NonExistenceProof) ICS23NonExistenceProof {
if op == nil {
return ICS23NonExistenceProof{}
}
return ICS23NonExistenceProof{
Valid: true,
Key: op.Key,
Left: ExistenceProofToABI(op.Left),
Right: ExistenceProofToABI(op.Right),
}
}

func ProofSpecToABI(spec *ics23.ProofSpec) ICS23ProofSpec {
childOrder := make([]*big.Int, len(spec.InnerSpec.ChildOrder))
for i, x := range spec.InnerSpec.ChildOrder {
childOrder[i] = big.NewInt(int64(x))
}

return ICS23ProofSpec{
LeafSpec: LeafOpToABI(spec.LeafSpec),
InnerSpec: ICS23InnerSpec{
ChildOrder: childOrder,
ChildSize: big.NewInt(int64(spec.InnerSpec.ChildSize)),
MinPrefixLength: big.NewInt(int64(spec.InnerSpec.MinPrefixLength)),
MaxPrefixLength: big.NewInt(int64(spec.InnerSpec.MaxPrefixLength)),
EmptyChild: spec.InnerSpec.EmptyChild,
Hash: uint8(spec.InnerSpec.Hash),
},
MaxDepth: big.NewInt(int64(spec.MaxDepth)),
MinDepth: big.NewInt(int64(spec.MinDepth)),
}
}
200 changes: 193 additions & 7 deletions sol/ics23.sol
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
pragma solidity >=0.8.0;

pragma experimental ABIEncoderV2;

// Current solidity ICS23 implementation only supports Existence
// NonExistence is planned to be supported, Batch and Compressed are not

contract ICS23 {
// Data structures and helper functions

enum HashOp{NO_HASH, SHA256, SHA512, KECCAK, RIPEMD160, BITCOIN}
enum LengthOp{NO_PREFIX, VAR_PROTO, VAR_RLP, FIXED32_BIG, FIXED32_LITTLE, FIXED64_BIG, FIXED64_LITTLE, REQUIRE_32_BYTES, REQUIRE_64_BYTES}

struct ExistenceProof {
bool valid;
bytes key;
bytes value;
LeafOp leaf;
InnerOp[] path;
}

struct NonExistenceProof {
bool valid;
bytes key;
ExistenceProof left;
ExistenceProof right;
}

struct LeafOp {
bool valid;
HashOp hash;
HashOp prehash_key;
HashOp prehash_value;
Expand All @@ -27,19 +31,39 @@ contract ICS23 {
}

struct InnerOp {
bool valid;
HashOp hash;
bytes prefix;
bytes suffix;
}

struct ProofSpec {
LeafOp leafSpec;
InnerSpec innerSpec;
uint maxDepth;
uint minDepth;
}

struct InnerSpec {
uint[] childOrder;
uint childSize;
uint minPrefixLength;
uint maxPrefixLength;
bytes emptyChild;
HashOp hash;
}

LeafOp iavlSpec = LeafOp(
true,
HashOp.SHA256,
HashOp.NO_HASH,
HashOp.SHA256,
LengthOp.VAR_PROTO,
hex"00"
);


enum Ordering {LT, EQ, GT}

function doHashOrNoop(HashOp op, bytes memory preimage) public pure returns (bytes memory) {
if (op == HashOp.NO_HASH) {
return preimage;
Expand Down Expand Up @@ -154,6 +178,143 @@ contract ICS23 {
);
}

function verifyNonExistence(NonExistenceProof memory proof, ProofSpec memory spec, bytes memory root, bytes memory key) public pure returns (bool) {
if (!proof.left.valid && !proof.right.valid) {
return false;
}
if (proof.left.valid) {
if (!verifyExistence(proof.left, spec.leafSpec, root, proof.left.key, proof.left.value)) {
return false;
}
if (compareBytes(key, proof.right.key) != Ordering.LT) {
return false;
}
}
if (proof.right.valid) {
if (!verifyExistence(proof.right, spec.leafSpec, root, proof.right.key, proof.right.value)) {
return false;
}
if (compareBytes(key, proof.left.key) != Ordering.GT) {
return false;
}
}

if (!proof.left.valid) {
if (!isLeftMost(spec.innerSpec, proof.right.path)) {
return false;
}
} else if (!proof.right.valid) {
if (!isRightMost(spec.innerSpec, proof.left.path)) {
return false;
}
} else {
if (!isLeftNeighbor(spec.innerSpec, proof.left.path, proof.right.path)) {
return false;
}
}

return true;
}

function orderFromPadding(InnerSpec memory spec, InnerOp memory op) public pure returns (uint) {
uint maxBranch = spec.childOrder.length;

for (uint branch = 0; branch < maxBranch; branch++) {
(uint minPrefix, uint maxPrefix, uint suffix) = getPadding(spec, branch);
if (hasPadding(op, minPrefix, maxPrefix, suffix)) {
return branch;
}
}

revert();
}

function isLeftStep(InnerSpec memory spec, InnerOp memory left, InnerOp memory right) public pure returns (bool) {
uint leftidx = orderFromPadding(spec, left);
uint rightidx = orderFromPadding(spec, right);
return leftidx + 1 == rightidx;
}

function isLeftMost(InnerSpec memory spec, InnerOp[] memory path) public pure returns (bool) {
(uint minPrefix, uint maxPrefix, uint suffix) = getPadding(spec, 0);

for (uint i = 0; i < path.length; i++) {
if (!hasPadding(path[i], minPrefix, maxPrefix, suffix)) {
return false;
}
}
return true;
}

function isRightMost(InnerSpec memory spec, InnerOp[] memory path) public pure returns (bool) {
uint last = spec.childOrder.length - 1;
(uint minPrefix, uint maxPrefix, uint suffix) = getPadding(spec, last);

for (uint i = 0; i < path.length; i++) {
if (!hasPadding(path[i], minPrefix, maxPrefix, suffix)) {
return false;
}
}
return true;

}

function isLeftNeighbor(InnerSpec memory spec, InnerOp[] memory left, InnerOp[] memory right) public pure returns (bool) {
uint top = 1;
for ( ;
equalBytes(left[left.length-top].prefix, right[right.length-top].prefix) && equalBytes(left[left.length-top].suffix, right[right.length-top].suffix);
top++) {}

InnerOp memory topLeft = left[left.length-top];
InnerOp memory topRight = right[right.length-top];

if (!isLeftStep(spec, topLeft, topRight)) {
return false;
}

if (!isRightMost(spec, left)) {
return false;
}

if (!isLeftMost(spec, right)) {
return false;
}

return true;
}

function getPosition(uint[] memory order, uint branch) public pure returns (uint) {
require(branch < order.length);
for (uint i = 0; i < order.length; i++) {
if (order[i] == branch) {
return i;
}
}
revert();
}

function hasPadding(InnerOp memory op, uint minPrefix, uint maxPrefix, uint suffix) public pure returns (bool) {
if (op.prefix.length < minPrefix) {
return false;
}
if (op.prefix.length > maxPrefix) {
return false;
}
return op.suffix.length == suffix;
}

function getPadding(InnerSpec memory spec, uint branch) public pure returns (uint, uint, uint) {
uint idx = getPosition(spec.childOrder, branch);

uint prefix = idx * spec.childSize;
uint minPrefix = prefix + spec.minPrefixLength;
uint maxPrefix = prefix + spec.maxPrefixLength;

uint suffix = (spec.childOrder.length - 1 - idx) * spec.childSize;

return (minPrefix, maxPrefix, suffix);
}

function equalBytes(bytes memory bz1, bytes memory bz2) public pure returns (bool) {
if (bz1.length != bz2.length) {
return false;
Expand All @@ -165,11 +326,36 @@ contract ICS23 {
}
return true;
}


function compareBytes(bytes memory bz1, bytes memory bz2) public pure returns (Ordering) {
for (uint i = 0; i < bz1.length && i < bz2.length; i++) {
if (bz1[i] < bz2[i]) {
return Ordering.LT;
}
if (bz1[i] > bz2[i]) {
return Ordering.GT;
}
}
if (bz1.length == bz2.length) {
return Ordering.EQ;
}
if (bz1.length < bz2.length) {
return Ordering.LT;
}
if (bz1.length > bz2.length) {
return Ordering.GT;
}
revert("should not reach this line");
}

// ICS23 interface implementation

// verifyMembership is synonym for verifyExistence
function verifyMembership(LeafOp memory spec, bytes memory root, ExistenceProof memory proof, bytes memory key, bytes memory value) public pure returns (bool) {
return verifyExistence(proof, spec, root, key, value);
}

function verifyNonMembership(ProofSpec memory spec, bytes memory root, NonExistenceProof memory proof, bytes memory key) public pure returns (bool) {
return verifyNonExistence(proof, spec, root, key);
}
}
Loading

0 comments on commit 56c49d1

Please sign in to comment.