arnaucube
arnaucube/circomlibPublic Self-hosted repository
circomlib/src/smt.js
src/smt.jsRaw
  1const bigInt = require("snarkjs").bigInt;
  2
  3const SMTMemDB = require("./smt_memdb");
  4const mimc7 = require("./mimc7");
  5
  6function smtHash(arr) {
  7    let r = bigInt(0);
  8    for (let i=0; i<arr.length; i++) {
  9        r = mimc7.hash(r, bigInt(arr[i]), 91 );
 10    }
 11    return r;
 12}
 13
 14class SMT {
 15
 16    constructor(db, root) {
 17        this.db = db;
 18        this.root = root;
 19    }
 20
 21    _splitBits(_key) {
 22
 23        let k = bigInt(_key);
 24        const res = [];
 25
 26        while (!k.isZero()) {
 27            if (k.isOdd()) {
 28                res.push(true);
 29            } else {
 30                res.push(false);
 31            }
 32            k = k.shr(1);
 33        }
 34
 35        while (res.length<256) res.push(false);
 36
 37        return res;
 38    }
 39
 40    async insert(_key, _value) {
 41        const key = bigInt(_key);
 42        const value = bigInt(_value);
 43        let addedOne = false;
 44        const res = {};
 45        res.oldRoot = this.root;
 46        const newKeyBits = this._splitBits(key);
 47        const resFind = await this.find(key);
 48
 49        if (resFind.found) throw new Error("Key already exists");
 50
 51        res.sibblings = resFind.sibblings;
 52
 53        if (!resFind.isOld0) {
 54            const oldKeyits = this._splitBits(resFind.notFoundKey);
 55            for (let i= res.sibblings.length; oldKeyits[i] == newKeyBits[i]; i++) {
 56                res.sibblings.push(bigInt.zero);
 57            }
 58            res.sibblings.push(smtHash([1, resFind.notFoundKey, resFind.notFoundValue]));
 59            addedOne = true;
 60        }
 61
 62        const inserts = [];
 63
 64        let rt = smtHash([1, key, value]);
 65        inserts.push([rt,[1, key, value]] );
 66
 67        for (let i=res.sibblings.length-1; i>=0; i--) {
 68            if (newKeyBits[i]) {
 69                rt = smtHash([res.sibblings[i], rt]);
 70                inserts.push([rt,[res.sibblings[i], rt]] );
 71            } else {
 72                rt = smtHash([rt, res.sibblings[i]]);
 73                inserts.push([rt,[rt, res.sibblings[i]]] );
 74            }
 75        }
 76
 77        if (addedOne) res.sibblings.pop();
 78        while ((res.sibblings.length>0) && (res.sibblings[res.sibblings.length-1].isZero())) {
 79            res.sibblings.pop();
 80        }
 81        res.oldKey = resFind.notFoundKey;
 82        res.oldValue = resFind.notFoundValue;
 83        res.newRoot = rt;
 84        res.isOld0 = resFind.isOld0;
 85
 86        this.root = rt;
 87
 88        await this.db.save(rt, inserts);
 89
 90        return res;
 91    }
 92
 93    async find(key) {
 94        const keyBits = this._splitBits(key);
 95        return await this._find(key, keyBits, this.root, 0);
 96    }
 97
 98    async _find(key, keyBits, root, level) {
 99        if (typeof root === "undefined") root = this.root;
100
101        let res;
102        if (root.isZero()) {
103            res = {
104                found: false,
105                sibblings: [],
106                notFoundKey: key,
107                notFoundValue: bigInt.zero,
108                isOld0: true
109            };
110            return res;
111        }
112
113        const record = await this.db.get(root);
114
115        if ((record.length==3)&&(record[0].equals(bigInt.one))) {
116            if (record[1].equals(key)) {
117                res = {
118                    found: true,
119                    sibblings: [],
120                    foundValue: record[2],
121                    isOld0: false
122                };
123            } else {
124                res = {
125                    found: false,
126                    sibblings: [],
127                    notFoundKey: record[1],
128                    notFoundValue: record[2],
129                    isOld0: false
130                };
131            }
132        } else {
133            if (keyBits[level] == 0) {
134                const res = await this.find(key, keyBits, record[1], level+1);
135                res.sibblings.unshift(record[2]);
136            } else {
137                res = await this.find(key, keyBits, record[2], level+1);
138                res.sibblings.unshift(record[1]);
139            }
140        }
141        return res;
142    }
143}
144
145async function loadFromFile(fileName) {
146
147}
148
149async function newMemEmptyTrie() {
150    const db = new SMTMemDB();
151    const rt = await db.getRoot();
152    const smt = new SMT(db, rt);
153    return smt;
154}
155
156module.exports.loadFromFile = loadFromFile;
157module.exports.newMemEmptyTrie = newMemEmptyTrie;