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

Transaction Construction, Pt 1. @method and compile() #115

2 changes: 1 addition & 1 deletion src/examples/ex00_preimage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ await isReady;
console.log('generating keypair...');
const kp = Main.generateKeypair();

const preimage = Field.random();
const preimage = Field.one;
const hash = Poseidon.hash([preimage]);

console.log('prove...');
Expand Down
15 changes: 13 additions & 2 deletions src/examples/simple_snapp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ class SimpleSnapp extends SmartContract {
this.x.set(x);
}

@method async update(y: Field) {
let x = await this.x.get();
@method update(y: Field) {
let x = this.x.get();
Copy link
Contributor

@MartinMinkov MartinMinkov Apr 15, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How will the .get function work synchronously? I figured that it would eventually make a network call to get the state of a contract on the chain, and that would need to be async?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I laid out here how I think it will work: #113

The only alternative I see would be to refactor Pickles so that it can handle async circuits. This would be better for users (enables them to make their methods async, which adds a lot of flexibility), but is probably much harder to implement.

this.x.set(x.add(y));
}
}
Expand All @@ -37,6 +37,12 @@ const account2 = Local.testAccounts[1].privateKey;
const snappPrivKey = PrivateKey.random();
const snappPubKey = snappPrivKey.toPublicKey();

console.log('compile');
let {
provers: [updateProver],
} = SimpleSnapp.compile(snappPubKey);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should references of snapp be scrubbed for zkapp? Not sure if that's already done in a future PR :P

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that's in a future PR :P


console.log('deploy');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume the console logs are removed in further PRs

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They aren't! Are you against logging out what's happening in examples? I tend to find that helpful, especially logging the initial and final states here helps developers confirm that it's working

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's fair -- these seem okay to keep. I think we should do something more fancy in the future, but we can do that later.

await Mina.transaction(account1, async () => {
let snapp = new SimpleSnapp(snappPubKey);

Expand All @@ -49,6 +55,11 @@ await Mina.transaction(account1, async () => {
.send()
.wait();

console.log('prove');
let proof = updateProver(Field(1));
console.log({ proof });

console.log('update');
let snappState = (await Mina.getAccount(snappPubKey)).snapp.appState[0];
console.log('initial state: ' + snappState);

Expand Down
23 changes: 23 additions & 0 deletions src/lib/circuit_value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,3 +239,26 @@ export function circuitMain(
Array.from(publicIndexSet).map((i) => paramTypes[i])
);
}

export function toFieldsMagic(thing: any): Field[] {
if (typeof thing == 'object') {
if (thing.toFields) {
// console.log('toFields');
return thing.toFields();
}

if (Array.isArray(thing)) {
// console.log('array', thing.length);
return thing.map((x) => toFieldsMagic(x)).flat();
}

let fields = [] as Field[];
let keys = Object.keys(thing).sort();
for (let key of keys) {
// console.log({ key });
fields.push(...toFieldsMagic(thing[key]));
}
return fields;
}
return [];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it less surprising for this to throw or somehow accumulate the skipped data instead of return [] for non-toFields-able fields of an object? I'm thinking this will lead to lots of subtle errors

Copy link
Member Author

@mitschabaude mitschabaude Apr 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function isn't used right now, but being able to return [] is important to handle mixed objects, which contain both circuit- and non-circuit properties. For example, a party.body.update.verificationKey contains a property value: string, which holds the base58-formatted verification key. This string should be ignored by toFieldsMagic(), so that a Party can be turned into field elements.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accumulating the skipped data could be an option!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decided to remove the function instead of changing it (b80d39f), since it's unused and most likely won't be useful, because if we want to compute hash inputs we'll have to conform to the more elaborate system used in Mina.

}
29 changes: 15 additions & 14 deletions src/lib/mina.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,7 @@ import {
} from '../snarky';
import { UInt32, UInt64 } from './int';
import { PrivateKey, PublicKey } from './signature';
import {
Body,
EpochDataPredicate,
ProtocolStatePredicate,
} from './party';
import { Body, EpochDataPredicate, ProtocolStatePredicate } from './party';

interface TransactionId {
wait(): Promise<void>;
Expand All @@ -41,19 +37,24 @@ export let nextTransactionId: { value: number } = { value: 0 };

type PartyPredicate = UInt32 | FullAccountPredicate | undefined;

export let currentTransaction:
export type CurrentTransaction =
| undefined
| {
sender: PrivateKey;
parties: Array<{ body: Body; predicate: PartyPredicate }>;
nextPartyIndex: number;
protocolState: ProtocolStatePredicate;
}
| undefined = undefined;
};

export let currentTransaction: CurrentTransaction = undefined;
export function setCurrentTransaction(transaction: CurrentTransaction) {
currentTransaction = transaction;
}

interface Mina {
transaction(sender: PrivateKey, f: () => void | Promise<void>): Transaction;
currentSlot(): UInt32;
getAccount(publicKey: PublicKey): Promise<Account>;
getAccount(publicKey: PublicKey): Account;
}

interface MockMina extends Mina {
Expand Down Expand Up @@ -92,7 +93,7 @@ export const LocalBlockchain: () => MockMina = () => {
testAccounts.push({ privateKey: k, publicKey: pk });
}

const getAccount = (pk: PublicKey): Promise<Account> => {
const getAccount = (pk: PublicKey) => {
const r = ledger.getAccount(pk);
if (r == null) {
throw new Error(
Expand All @@ -104,7 +105,7 @@ export const LocalBlockchain: () => MockMina = () => {
nonce: new UInt32(r.nonce.value),
snapp: r.snapp,
};
return new Promise((r) => r(a));
return a;
}
};

Expand Down Expand Up @@ -294,13 +295,13 @@ export function currentSlot(): UInt32 {
/**
* @return The account data associated to the given public key.
*/
export function getAccount(pubkey: PublicKey): Promise<Account> {
export function getAccount(pubkey: PublicKey) {
return activeInstance.getAccount(pubkey);
}

/**
* @return The balance associated to the given public key.
*/
export function getBalance(pubkey: PublicKey): Promise<UInt64> {
return activeInstance.getAccount(pubkey).then((a) => a.balance);
export function getBalance(pubkey: PublicKey) {
return activeInstance.getAccount(pubkey).balance;
}
34 changes: 17 additions & 17 deletions src/lib/party.ts
Original file line number Diff line number Diff line change
Expand Up @@ -758,27 +758,27 @@ export class Party<P> {
return party;
}

static createSigned(signer: PrivateKey): Promise<Party<UInt32>> {
static createSigned(signer: PrivateKey) {
// TODO: This should be a witness block that uses the setVariable
// API to set the value of a variable after it's allocated

const pk = signer.toPublicKey();
const body: Body = Body.keepAll(pk);
return Mina.getAccount(pk).then((a) => {
if (Mina.currentTransaction === undefined) {
throw new Error(
'Party.createSigned: Cannot run outside of a transaction'
);
}

if (a == null) {
throw new Error('Party.createSigned: Account not found');
}

const party = new Party(body, a.nonce);
Mina.currentTransaction.nextPartyIndex++;
Mina.currentTransaction.parties.push(party);
return party;
});
let a = Mina.getAccount(pk);

if (Mina.currentTransaction === undefined) {
throw new Error(
'Party.createSigned: Cannot run outside of a transaction'
);
}

if (a == null) {
throw new Error('Party.createSigned: Account not found');
}

const party = new Party(body, a.nonce);
Mina.currentTransaction.nextPartyIndex++;
Mina.currentTransaction.parties.push(party);
return party;
}
}
Loading