Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Finish unimplemented preconditions #324

Merged
merged 14 commits into from
Aug 8, 2022
Merged
1 change: 0 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ export {
Party,
Permissions,
ZkappPublicInput,
getDefaultTokenId,
partiesToJson,
} from './lib/party';
export {
Expand Down
70 changes: 69 additions & 1 deletion src/lib/encoding.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import { Field } from '../snarky';
import { Field, Ledger } from '../snarky';

export {
stringToFields,
stringFromFields,
bytesToFields,
bytesFromFields,
Bijective,
TokenId,
ReceiptChainHash,
LedgerHash,
EpochSeed,
StateHash,
};

// functions for encoding data as field elements
Expand Down Expand Up @@ -251,3 +256,66 @@ function bigIntArrayToBytes(bigints: bigint[], bytesPerBigInt: number) {
}
return bytes.slice(0, i + 1);
}

// encoding of fields as base58, compatible with ocaml encodings (provided the versionByte and versionNumber are the same)

function fieldToBase58(x: Field, versionByte: number, versionNumber?: number) {
if (!x.isConstant()) {
throw Error("encode: Field is not constant, can't read its value");
}
let bytes = [...(x as any as InternalConstantField).value[1]];
if (versionNumber !== undefined) bytes.unshift(versionNumber);
let binaryString = String.fromCharCode(...bytes);
let ocamlBytes = { t: 9, c: binaryString, l: bytes.length };
return Ledger.encoding.toBase58(ocamlBytes, versionByte);
}
function fieldFromBase58(
base58: string,
versionByte: number,
versionNumber?: number
): Field {
let ocamlBytes = Ledger.encoding.ofBase58(base58, versionByte);
let bytes = [...ocamlBytes.c].map((_, i) => ocamlBytes.c.charCodeAt(i));
if (versionNumber !== undefined) bytes.shift();
let uint8array = new Uint8Array(32);
uint8array.set(bytes);
return Object.assign(Object.create(Field.one.constructor.prototype), {
value: [0, uint8array],
});
}

function customEncoding(versionByte: () => number, versionNumber?: number) {
return {
toBase58(field: Field) {
return fieldToBase58(field, versionByte(), versionNumber);
},
fromBase58(base58: string) {
return fieldFromBase58(base58, versionByte(), versionNumber);
},
};
}

const RECEIPT_CHAIN_HASH_VERSION = 1;
const LEDGER_HASH_VERSION = 1;
const EPOCH_SEED_VERSION = 1;
const STATE_HASH_VERSION = 1;

const TokenId = customEncoding(() => Ledger.encoding.versionBytes.tokenIdKey);
const ReceiptChainHash = customEncoding(
() => Ledger.encoding.versionBytes.receiptChainHash,
RECEIPT_CHAIN_HASH_VERSION
);
const LedgerHash = customEncoding(
() => Ledger.encoding.versionBytes.ledgerHash,
LEDGER_HASH_VERSION
);
const EpochSeed = customEncoding(
() => Ledger.encoding.versionBytes.epochSeed,
EPOCH_SEED_VERSION
);
const StateHash = customEncoding(
() => Ledger.encoding.versionBytes.stateHash,
STATE_HASH_VERSION
);

type InternalConstantField = { value: [0, Uint8Array] };
49 changes: 19 additions & 30 deletions src/lib/fetch.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import 'isomorphic-fetch';
import { Bool, Field, Ledger } from '../snarky';
import { UInt32, UInt64 } from './int';
import {
getDefaultTokenId,
Permission,
Permissions,
ZkappStateLength,
} from './party';
import { TokenId, Permission, Permissions, ZkappStateLength } from './party';
import { PublicKey } from './signature';
import { NetworkValue } from './precondition';
import { Types } from '../snarky/types';
import * as Encoding from './encoding';

export {
fetchAccount,
Expand Down Expand Up @@ -82,10 +78,7 @@ async function fetchAccountInternal(
) {
const { publicKey, tokenId } = accountInfo;
let [response, error] = await makeGraphqlRequest(
accountQuery(
publicKey,
tokenId ?? Ledger.fieldToBase58(getDefaultTokenId())
),
accountQuery(publicKey, tokenId ?? TokenId.toBase58(TokenId.default)),
graphqlEndpoint,
config
);
Expand Down Expand Up @@ -169,12 +162,12 @@ type Account = {
balance: UInt64;
tokenId: Field;
tokenSymbol: string;
zkapp?: { appState: Field[] };
appState?: Field[];
permissions?: Permissions;
receiptChainHash?: Field;
receiptChainHash: Field;
delegate?: PublicKey;
sequenceState?: Field;
provedState?: Bool;
provedState: Bool;
};

type FlexibleAccount = {
Expand Down Expand Up @@ -238,20 +231,18 @@ function parseFetchedAccount({
publicKey !== undefined ? PublicKey.fromBase58(publicKey) : undefined,
nonce: nonce !== undefined ? UInt32.fromString(nonce) : undefined,
balance: balance && UInt64.fromString(balance.total),
zkapp: (zkappState && { appState: zkappState.map(Field) }) ?? undefined,
appState: (zkappState && zkappState.map(Field)) ?? undefined,
permissions:
permissions &&
(Object.fromEntries(
Object.entries(permissions).map(([k, v]) => [k, toPermission(v)])
) as unknown as Permissions),
// TODO: how is sequenceState related to sequenceEvents?
sequenceState:
sequenceEvents != undefined ? Field(sequenceEvents[0]) : undefined,
// TODO: how to parse receptChainHash?
// receiptChainHash:
// receiptChainHash !== undefined
// ? Ledger.fieldOfBase58(receiptChainHash)
// : undefined,
receiptChainHash:
receiptChainHash !== undefined
? Encoding.ReceiptChainHash.fromBase58(receiptChainHash)
: undefined,
delegate:
delegateAccount && PublicKey.fromBase58(delegateAccount.publicKey),
tokenId: token !== undefined ? Ledger.fieldOfBase58(token) : undefined,
Expand All @@ -269,7 +260,7 @@ function stringifyAccount(account: FlexibleAccount): FetchedAccount {
zkapp?.appState.map((s) => s.toString()) ??
Array(ZkappStateLength).fill('0'),
balance: { total: balance?.toString() ?? '0' },
token: tokenId ?? Ledger.fieldToBase58(getDefaultTokenId()),
token: tokenId ?? TokenId.toBase58(TokenId.default),
tokenSymbol: tokenSymbol ?? '',
};
}
Expand Down Expand Up @@ -303,7 +294,7 @@ function markAccountToBeFetched(
graphqlEndpoint: string
) {
let publicKeyBase58 = publicKey.toBase58();
let tokenBase58 = Ledger.fieldToBase58(tokenId);
let tokenBase58 = TokenId.toBase58(tokenId);
accountsToFetch[`${publicKeyBase58};${tokenBase58};${graphqlEndpoint}`] = {
publicKey: publicKeyBase58,
tokenId: tokenBase58,
Expand Down Expand Up @@ -355,9 +346,7 @@ function getCachedAccount(
) {
let account =
accountCache[
`${publicKey.toBase58()};${Ledger.fieldToBase58(
tokenId
)};${graphqlEndpoint}`
`${publicKey.toBase58()};${TokenId.toBase58(tokenId)};${graphqlEndpoint}`
]?.account;
if (account !== undefined) return parseFetchedAccount(account);
}
Expand Down Expand Up @@ -502,7 +491,7 @@ function parseFetchedBlock({
},
}: FetchedBlock): NetworkValue {
return {
snarkedLedgerHash: Field.zero, // TODO
snarkedLedgerHash: Encoding.LedgerHash.fromBase58(snarkedLedgerHash),
// TODO: use date or utcDate?
timestamp: UInt64.fromString(utcDate),
blockchainLength: UInt32.fromString(blockHeight),
Expand All @@ -525,12 +514,12 @@ function parseEpochData({
}: FetchedBlock['protocolState']['consensusState']['nextEpochData']): NetworkValue['nextEpochData'] {
return {
ledger: {
hash: Field.zero, // TODO
hash: Encoding.LedgerHash.fromBase58(hash),
totalCurrency: UInt64.fromString(totalCurrency),
},
seed: Field.zero, // TODO
startCheckpoint: Field.zero, // TODO
lockCheckpoint: Field.zero, // TODO
seed: Encoding.EpochSeed.fromBase58(seed),
startCheckpoint: Encoding.StateHash.fromBase58(startCheckpoint),
lockCheckpoint: Encoding.StateHash.fromBase58(lockCheckpoint),
epochLength: UInt32.fromString(epochLength),
};
}
Expand Down
5 changes: 5 additions & 0 deletions src/lib/hash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export {
salt,
TokenSymbol,
packToFields,
emptyReceiptChainHash,
};

class Sponge {
Expand Down Expand Up @@ -165,3 +166,7 @@ const TokenSymbol = {
return { symbol, field };
},
};

function emptyReceiptChainHash() {
return emptyHashWithPrefix('CodaReceiptEmpty');
}
Loading