mina-attestations
Version:
Private Attestations on Mina
154 lines • 5.18 kB
JavaScript
import { Bytes, Field, UInt8 } from 'o1js';
import { DynamicArray, DynamicArrayBase, provableDynamicArray, } from "./dynamic-array.js";
import { ProvableFactory } from "../provable-factory.js";
import { assert, chunk, stringLength } from "../util.js";
import { DynamicSHA2 } from "./dynamic-sha2.js";
import { z } from 'zod';
export { DynamicBytes, DynamicBytesBase };
/**
* Specialization of `DynamicArray` to bytes,
* with added helper methods to convert instances to/from values.
*
* ```ts
* const Bytes = DynamicBytes({ maxLength: 120 });
*
* let bytes = Bytes.fromString('hello');
* let bytes2 = Bytes.fromBytes([1, 2, 3]);
*
* let string = bytes.toString();
* let uint8array = bytes2.toBytes();
* ```
*/
function DynamicBytes({ maxLength, }) {
// assert maxLength bounds
assert(maxLength >= 0, 'maxLength must be >= 0');
assert(maxLength < 2 ** 16, 'maxLength must be < 2^16');
class DynamicBytes_ extends DynamicBytesBase {
static get maxLength() {
return maxLength;
}
static get provable() {
return provableBytes;
}
static fromBytes(bytes) {
if (bytes instanceof Uint8Array)
return provableBytes.fromValue(bytes);
if (bytes instanceof Bytes.Base)
bytes = bytes.bytes;
return new DynamicBytes_(bytes.map((t) => UInt8.from(t)), Field(bytes.length));
}
static fromHex(hex) {
assert(hex.length % 2 === 0, 'Hex string must have even length');
let bytes = chunk([...hex], 2).map((s) => parseInt(s.join(''), 16));
return DynamicBytes_.fromBytes(bytes);
}
static fromString(s) {
return DynamicBytes_.fromBytes(new TextEncoder().encode(s));
}
}
const provableBytes = provableDynamicArray(UInt8, DynamicBytes_)
.mapValue({
there(s) {
return Uint8Array.from(s, ({ value }) => Number(value));
},
backAndDistinguish(s) {
// gracefully handle different maxLength
if (s instanceof DynamicBytesBase) {
if (s.maxLength === maxLength)
return s;
if (s.maxLength < maxLength)
return s.growMaxLengthTo(maxLength);
// shrinking max length will only work outside circuit
s = s.toBytes();
}
return [...s].map((t) => ({ value: BigInt(t) }));
},
})
.build();
return DynamicBytes_;
}
DynamicBytes.fromBytes = function (bytes) {
return DynamicBytes({ maxLength: bytes.length }).fromBytes(bytes);
};
DynamicBytes.from = function (input) {
if (typeof input === 'string') {
let Bytes = DynamicBytes({ maxLength: stringLength(input) });
return Bytes.fromString(input);
}
if (input instanceof Uint8Array || Array.isArray(input)) {
let Bytes = DynamicBytes({ maxLength: input.length });
return Bytes.fromBytes(input);
}
assert(input instanceof DynamicArray.Base, 'invalid input');
if (input instanceof DynamicBytesBase)
return input;
// if this is not a DynamicBytes, we construct an equivalent one
let Bytes = DynamicBytes({ maxLength: input.maxLength });
let bytes = new Bytes(input.array, input.length);
bytes._indexMasks = input._indexMasks;
bytes.__dummyMask = input.__dummyMask;
bytes._indicesInRange = input._indicesInRange;
return bytes;
};
class DynamicBytesBase extends DynamicArrayBase {
get innerType() {
return UInt8;
}
/**
* Hash the bytes using variants of SHA2.
*/
hashToBytes(algorithm) {
switch (algorithm) {
case 'sha2-256':
return DynamicSHA2.hash(256, this);
case 'sha2-384':
return DynamicSHA2.hash(384, this);
case 'sha2-512':
return DynamicSHA2.hash(512, this);
default:
assert(false, 'unsupported hash kind');
}
}
/**
* Convert DynamicBytes to a byte array.
*/
toBytes() {
return this.toValue();
}
/**
* Convert DynamicBytes to a hex string.
*/
toHex() {
return [...this.toBytes()]
.map((b) => b.toString(16).padStart(2, '0'))
.join('');
}
/**
* Convert DynamicBytes to a string.
*/
toString() {
return new TextDecoder().decode(this.toBytes());
}
growMaxLengthTo(maxLength) {
return DynamicBytes.from(super.growMaxLengthTo(maxLength));
}
}
DynamicBytes.Base = DynamicBytesBase;
// serialize/deserialize
ProvableFactory.register('DynamicBytes', DynamicBytes, {
typeSchema: z.object({ maxLength: z.number() }),
valueSchema: z.string(),
typeToJSON(constructor) {
return { maxLength: constructor.maxLength };
},
typeFromJSON(json) {
return DynamicBytes({ maxLength: json.maxLength });
},
valueToJSON(_, value) {
return value.toHex();
},
valueFromJSON(type, value) {
return type.fromHex(value);
},
});
//# sourceMappingURL=dynamic-bytes.js.map