o1js
Version:
TypeScript framework for zk-SNARKs and zkApps
135 lines • 4.95 kB
JavaScript
import { __decorate, __metadata } from "tslib";
import { arrayProp, CircuitValue } from './types/circuit-value.js';
import { Field, Bool } from './wrapped.js';
import { Poseidon } from './crypto/poseidon.js';
import { MerkleTree, MerkleWitness } from './merkle-tree.js';
import { Provable } from './provable.js';
import { BinableFp } from '../../mina-signer/src/field-bigint.js';
export { MerkleMap, MerkleMapWitness };
class MerkleMap {
/**
* Creates a new, empty Merkle Map.
*
* A Merkle Map is a data structure that allows for efficient storage and
* retrieval of key-value pairs. The values are stored in a Merkle tree,
* and the keys are formed by using the first 254 bits of the key as an index.
* The inner Merkle tree has a height of 256.
*
* @returns A new MerkleMap
* @example
* ```ts
* const merkleMap = new MerkleMap();
* ```
*/
constructor() {
this.tree = new MerkleTree(256);
}
_keyToIndex(key) {
// the bit map is reversed to make reconstructing the key during proving more convenient
let bits = BinableFp.toBits(key.toBigInt()).reverse();
// Make sure that the key fits in 254 bits, in order to avoid collisions since the Pasta field modulus is smaller than 2^255
if (bits[0]) {
throw Error('Key must be less than 2^254, to avoid collisions in the field modulus. Please use a smaller key.');
}
let n = 0n;
for (let i = bits.length - 1; i >= 0; i--) {
n = (n << 1n) | BigInt(bits[i]);
}
return n;
}
/**
* Sets a key of the merkle map to a given value.
* @param key The key to set in the map.
* @param value The value to set.
* @example
* ```ts
* const key = Field(5);
* const value = Field(10);
* merkleMap.set(key, value);
* ```
*/
set(key, value) {
const index = this._keyToIndex(key);
this.tree.setLeaf(index, value);
}
/**
* Returns a value given a key. Values are by default Field(0).
* @param key The key to get the value from.
* @returns The value stored at the key.
* @example
* ```ts
* const key = Field(5);
* const value = merkleMap.get(key);
* console.log(value); // Output: the value at key 5 or Field(0) if key does not exist
* ```
*/
get(key) {
const index = this._keyToIndex(key);
return this.tree.getNode(0, index);
}
/**
* Returns the root of the Merkle Map.
* @returns The root of the Merkle Map.
* @example
* ```ts
* const root = merkleMap.getRoot();
* ```
*/
getRoot() {
return this.tree.getRoot();
}
/**
* Returns a circuit-compatible witness (also known as [Merkle Proof or Merkle Witness](https://computersciencewiki.org/index.php/Merkle_proof)) for the given key.
* @param key The key to make a witness for.
* @returns A MerkleMapWitness, which can be used to assert changes to the MerkleMap, and the witness's key.
* @example
* ```ts
* const key = Field(5);
* const witness = merkleMap.getWitness(key);
* ```
*/
getWitness(key) {
const index = this._keyToIndex(key);
class MyMerkleWitness extends MerkleWitness(256) {
}
const witness = new MyMerkleWitness(this.tree.getWitness(index));
return new MerkleMapWitness(witness.isLeft, witness.path);
}
}
class MerkleMapWitness extends CircuitValue {
constructor(isLefts, siblings) {
super();
this.isLefts = isLefts;
this.siblings = siblings;
}
/**
* Computes the merkle tree root for a given value and the key for this witness
* @param value The value to compute the root for.
* @returns A tuple of the computed merkle root, and the key that is connected to the path updated by this witness.
*/
computeRootAndKey(value) {
// Check that the computed key is less than 2^254, in order to avoid collisions since the Pasta field modulus is smaller than 2^255
this.isLefts[0].assertTrue();
let hash = value;
const isLeft = this.isLefts;
const siblings = this.siblings;
let key = Field(0);
for (let i = 0; i < 255; i++) {
const left = Provable.if(isLeft[i], hash, siblings[i]);
const right = Provable.if(isLeft[i], siblings[i], hash);
hash = Poseidon.hash([left, right]);
const bit = Provable.if(isLeft[i], Field(0), Field(1));
key = key.mul(2).add(bit);
}
return [hash, key];
}
}
__decorate([
arrayProp(Bool, 255),
__metadata("design:type", Array)
], MerkleMapWitness.prototype, "isLefts", void 0);
__decorate([
arrayProp(Field, 255),
__metadata("design:type", Array)
], MerkleMapWitness.prototype, "siblings", void 0);
//# sourceMappingURL=merkle-map.js.map