UNPKG

o1js

Version:

TypeScript framework for zk-SNARKs and zkApps

477 lines (457 loc) 14.7 kB
import { AccountUpdate, Authorized, GenericData } from './account-update.js'; import { AccountId, AccountTiming } from './account.js'; import { AccountUpdateAuthorizationKind } from './authorization.js'; import { TokenId, Update } from './core.js'; import { Precondition } from './preconditions.js'; import { GenericStatePreconditions, GenericStateUpdates } from './state.js'; import { AccountUpdate as V1AccountUpdateImpl } from '../v1/account-update.js'; import { VerificationKey } from '../../proof-system/verification-key.js'; import { Bool } from '../../provable/bool.js'; import { Field } from '../../provable/field.js'; import { UInt32, UInt64, Int64, Sign } from '../../provable/int.js'; import { PrivateKey } from '../../provable/crypto/signature.js'; import { Actions as V1Actions, Events as V1Events, Sign as V1Sign, TokenSymbol as V1TokenSymbol, ZkappUri as V1ZkappUri, } from '../../../bindings/mina-transaction/v1/transaction-leaves.js'; import * as TypesV1 from '../../../bindings/mina-transaction/gen/v1/transaction.js'; import * as ValuesV1 from '../../../bindings/mina-transaction/gen/v1/transaction-bigint.js'; import * as JsonV1 from '../../../bindings/mina-transaction/gen/v1/transaction-json.js'; import { jsLayout as layoutV1 } from '../../../bindings/mina-transaction/gen/v1/js-layout.js'; import { expect } from 'expect'; import { ZkappConstants } from '../v1/constants.js'; import { testV1V2ClassEquivalence, testV1V2ValueEquivalence, testV2Encoding, } from './test/utils.js'; import { Signature, signFieldElement, zkAppBodyPrefix, } from '../../../mina-signer/src/signature.js'; import { Types } from '../../../bindings/mina-transaction/v1/types.js'; import { packToFields, hashWithPrefix } from '../../../lib/provable/crypto/poseidon.js'; function testHashEquality(v1: TypesV1.AccountUpdate, v2: Authorized) { expect(TypesV1.AccountUpdate.toInput(v1)).toEqual(v2.toInput()); // HACK const v1Impl = { ...v1 } as V1AccountUpdateImpl; Object.setPrototypeOf(v1Impl, V1AccountUpdateImpl.prototype); expect(v1Impl.hash()).toEqual(v2.hash('testnet')); } const privateKey = PrivateKey.random(); const publicKey = privateKey.toPublicKey(); const zkappUri = 'http://somewhere.com'; const tokenSymbol = 'erdigo'; const verificationKey = new VerificationKey({ data: 'testing 123', hash: new Field(60), }); const events = [[new Field(10)], [new Field(20), new Field(30)]]; const actions = [[new Field(100), new Field(200)], [new Field(300)]]; const V1Auth = { Impossible: { constant: new Bool(true), signatureNecessary: new Bool(true), signatureSufficient: new Bool(false), }, None: { constant: new Bool(true), signatureNecessary: new Bool(false), signatureSufficient: new Bool(true), }, Proof: { constant: new Bool(false), signatureNecessary: new Bool(false), signatureSufficient: new Bool(false), }, Signature: { constant: new Bool(false), signatureNecessary: new Bool(true), signatureSufficient: new Bool(true), }, ProofOrSignature: { constant: new Bool(false), signatureNecessary: new Bool(false), signatureSufficient: new Bool(true), }, }; const V1AccountUpdate = TypesV1.provableFromLayout< TypesV1.AccountUpdate, ValuesV1.AccountUpdate, JsonV1.AccountUpdate >(layoutV1.AccountUpdate as any /* WOOPS */); const v1AccountUpdate: TypesV1.AccountUpdate = { authorization: { proof: 'proof', signature: 'signature', }, body: { publicKey, tokenId: TokenId.MINA.value, callData: new Field(1000), callDepth: 2, balanceChange: Int64.create(new UInt64(100), V1Sign.minusOne), incrementNonce: new Bool(false), useFullCommitment: new Bool(true), implicitAccountCreationFee: new Bool(true), mayUseToken: { parentsOwnToken: new Bool(false), inheritFromParent: new Bool(true), }, authorizationKind: { isSigned: new Bool(true), isProved: new Bool(false), verificationKeyHash: verificationKey.hash, }, preconditions: { network: { snarkedLedgerHash: { isSome: new Bool(true), value: new Field(111), }, blockchainLength: { isSome: new Bool(true), value: { lower: new UInt32(222), upper: new UInt32(333), }, }, minWindowDensity: { isSome: new Bool(true), value: { lower: new UInt32(444), upper: new UInt32(555), }, }, totalCurrency: { isSome: new Bool(true), value: { lower: new UInt64(666), upper: new UInt64(777), }, }, globalSlotSinceGenesis: { isSome: new Bool(true), value: { lower: new UInt32(888), upper: new UInt32(999), }, }, stakingEpochData: { ledger: { hash: { isSome: new Bool(true), value: new Field(1111), }, totalCurrency: { isSome: new Bool(true), value: { lower: new UInt64(2222), upper: new UInt64(3333), }, }, }, seed: { isSome: new Bool(true), value: new Field(4444), }, startCheckpoint: { isSome: new Bool(true), value: new Field(5555), }, lockCheckpoint: { isSome: new Bool(true), value: new Field(6666), }, epochLength: { isSome: new Bool(true), value: { lower: new UInt32(7777), upper: new UInt32(8888), }, }, }, nextEpochData: { ledger: { hash: { isSome: new Bool(true), value: new Field(9999), }, totalCurrency: { isSome: new Bool(true), value: { lower: new UInt64(11111), upper: new UInt64(22222), }, }, }, seed: { isSome: new Bool(true), value: new Field(33333), }, startCheckpoint: { isSome: new Bool(true), value: new Field(44444), }, lockCheckpoint: { isSome: new Bool(true), value: new Field(55555), }, epochLength: { isSome: new Bool(true), value: { lower: new UInt32(66666), upper: new UInt32(77777), }, }, }, }, account: { balance: { isSome: new Bool(true), value: { lower: new UInt64(88888), upper: new UInt64(99999), }, }, nonce: { isSome: new Bool(true), value: { lower: new UInt32(111111), upper: new UInt32(222222), }, }, receiptChainHash: { isSome: new Bool(true), value: new Field(333333), }, delegate: { isSome: new Bool(true), value: publicKey, }, state: new Array(ZkappConstants.MAX_ZKAPP_STATE_FIELDS).fill({ isSome: new Bool(true), value: new Field(444444), }), actionState: { isSome: new Bool(true), value: new Field(555555), }, provedState: { isSome: new Bool(true), value: new Bool(true), }, isNew: { isSome: new Bool(true), value: new Bool(true), }, }, validWhile: { isSome: new Bool(true), value: { lower: new UInt32(666666), upper: new UInt32(777777), }, }, }, events: V1Events.fromList(events), actions: V1Actions.fromList(actions), update: { appState: new Array(ZkappConstants.MAX_ZKAPP_STATE_FIELDS).fill({ isSome: new Bool(true), value: new Field(8), }), delegate: { isSome: new Bool(true), value: publicKey }, verificationKey: { isSome: new Bool(true), value: { ...verificationKey }, }, permissions: { isSome: new Bool(true), value: { editState: V1Auth.Proof, access: V1Auth.None, send: V1Auth.Impossible, receive: V1Auth.Signature, setDelegate: V1Auth.Proof, setPermissions: V1Auth.Proof, setVerificationKey: { auth: V1Auth.Signature, txnVersion: new UInt32(3), }, setZkappUri: V1Auth.Signature, editActionState: V1Auth.Proof, setTokenSymbol: V1Auth.ProofOrSignature, incrementNonce: V1Auth.None, setVotingFor: V1Auth.Proof, setTiming: V1Auth.Signature, }, }, zkappUri: { isSome: new Bool(true), value: V1ZkappUri.fromJSON(zkappUri), }, tokenSymbol: { isSome: new Bool(true), value: V1TokenSymbol.fromJSON(tokenSymbol), }, timing: { isSome: new Bool(true), value: { initialMinimumBalance: new UInt64(20), cliffTime: new UInt32(5), cliffAmount: new UInt64(1), vestingPeriod: new UInt32(15), vestingIncrement: new UInt64(1), }, }, votingFor: { isSome: new Bool(true), value: new Field(500), }, }, }, }; const v2AccountUpdate: Authorized = new Authorized( { proof: 'proof', signature: 'signature', }, new AccountUpdate<'GenericState', Field[], Field[]>('GenericState', GenericData, GenericData, { accountId: new AccountId(publicKey, TokenId.MINA), verificationKeyHash: verificationKey.hash, callData: new Field(1000), balanceChange: Int64.create(new UInt64(100), Sign.minusOne), incrementNonce: new Bool(false), useFullCommitment: new Bool(true), implicitAccountCreationFee: new Bool(true), mayUseToken: { parentsOwnToken: new Bool(false), inheritFromParent: new Bool(true), }, authorizationKind: AccountUpdateAuthorizationKind.Signature(), preconditions: { network: { snarkedLedgerHash: new Field(111), blockchainLength: Precondition.InRange.betweenInclusive(new UInt32(222), new UInt32(333)), minWindowDensity: Precondition.InRange.betweenInclusive(new UInt32(444), new UInt32(555)), totalCurrency: Precondition.InRange.betweenInclusive(new UInt64(666), new UInt64(777)), globalSlotSinceGenesis: Precondition.InRange.betweenInclusive( new UInt32(888), new UInt32(999) ), stakingEpochData: { ledger: { hash: new Field(1111), totalCurrency: Precondition.InRange.betweenInclusive( new UInt64(2222), new UInt64(3333) ), }, seed: new Field(4444), startCheckpoint: new Field(5555), lockCheckpoint: new Field(6666), epochLength: Precondition.InRange.betweenInclusive(new UInt32(7777), new UInt32(8888)), }, nextEpochData: { ledger: { hash: new Field(9999), totalCurrency: Precondition.InRange.betweenInclusive( new UInt64(11111), new UInt64(22222) ), }, seed: new Field(33333), startCheckpoint: new Field(44444), lockCheckpoint: new Field(55555), epochLength: Precondition.InRange.betweenInclusive(new UInt32(66666), new UInt32(77777)), }, }, account: { balance: Precondition.InRange.betweenInclusive(new UInt64(88888), new UInt64(99999)), nonce: Precondition.InRange.betweenInclusive(new UInt32(111111), new UInt32(222222)), receiptChainHash: new Field(333333), delegate: publicKey, state: new GenericStatePreconditions( new Array(ZkappConstants.MAX_ZKAPP_STATE_FIELDS).fill( Precondition.Equals.equals(new Field(444444)) ) ), actionState: new Field(555555), isProven: new Bool(true), isNew: new Bool(true), }, validWhile: Precondition.InRange.betweenInclusive(new UInt32(666666), new UInt32(777777)), }, pushEvents: events, pushActions: actions, setState: new GenericStateUpdates( new Array(ZkappConstants.MAX_ZKAPP_STATE_FIELDS).fill(Update.set(new Field(8))) ), setDelegate: publicKey, setVerificationKey: verificationKey, setPermissions: { editState: 'Proof', access: 'None', send: 'Impossible', receive: 'Signature', setDelegate: 'Proof', setPermissions: 'Proof', setVerificationKey: 'Signature', setZkappUri: 'Signature', editActionState: 'Proof', setTokenSymbol: 'Either', incrementNonce: 'None', setVotingFor: 'Proof', setTiming: 'Signature', }, setZkappUri: zkappUri, setTokenSymbol: tokenSymbol, setTiming: new AccountTiming({ // TODO: a proper timing api to construct these initialMinimumBalance: new UInt64(20), cliffTime: new UInt32(5), cliffAmount: new UInt64(1), vestingPeriod: new UInt32(15), vestingIncrement: new UInt64(1), }), setVotingFor: new Field(500), }) ); // encoding tests { // TODO: the fact that all these extra type-annotation are required means we didn't encode this // type well for typescript's poor type inference testV2Encoding<Authorized>(Authorized, v2AccountUpdate); testV1V2ClassEquivalence<number, TypesV1.AccountUpdate, Authorized>( V1AccountUpdate, Authorized, 0 ); testHashEquality(V1AccountUpdate.empty(), Authorized.empty()); testV1V2ValueEquivalence<number, TypesV1.AccountUpdate, Authorized>( V1AccountUpdate, Authorized, v1AccountUpdate, v2AccountUpdate, 2 ); testHashEquality(v1AccountUpdate, v2AccountUpdate); } // signature test { let v1Hash = hashWithPrefix( zkAppBodyPrefix('testnet'), packToFields(Types.AccountUpdate.toInput(v1AccountUpdate)) ); let v1Signature = signFieldElement(v1Hash.toBigInt(), privateKey.toBigInt(), 'testnet'); const v2Update = v2AccountUpdate.toAccountUpdate(); let v2Hash = v2Update.commit('testnet').accountUpdateCommitment.toBigInt(); let v2Signature = signFieldElement(v2Hash, privateKey.toBigInt(), 'testnet'); expect(Signature.toBase58(v1Signature)).toEqual(Signature.toBase58(v2Signature)); } console.log('\n:)');