-
Notifications
You must be signed in to change notification settings - Fork 118
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
Hash inputs in JS #310
Hash inputs in JS #310
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some comments to guide review:
@@ -0,0 +1,179 @@ | |||
import { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is a sort of unit test, which is also run in Mina CI. I left it here just because for the Mina version, I had to remove the TS types, and it's easier to read & modify with the types in place
@@ -139,7 +139,7 @@ function toPermission(p: AuthRequired): Permission { | |||
type FetchedAccount = { | |||
publicKey: string; | |||
nonce: string; | |||
tokenId: string; | |||
token: string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this file has some unrelated fixes which turned up when debugging snarkyjs transactions against a local Mina node, while debugging timestamp preconditions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch on figuring out these fixes!
|
||
type Input = { fields?: Field[]; packed?: [Field, number][] }; | ||
|
||
type TokenSymbol = { symbol: string; field: Field }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
new TokenSymbol
type!
* Convert the {fields, packed} hash input representation to a list of field elements | ||
* Random_oracle_input.Chunked.pack_to_fields | ||
*/ | ||
function packToFields({ fields = [], packed = [] }: Input) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is the last step that's necessary to convert the result of toInput
to something which can be hashed
let fields = Types.Party.toFields(this); | ||
return Ledger.hashPartyFromFields(fields); | ||
let input = Types.Party.toInput(this); | ||
return hashWithPrefix(prefixes.body, packToFields(input)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is the main achievement of this PR!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome work!
@@ -57,8 +57,6 @@ let unimplementedPreconditions: LongKey[] = [ | |||
'account.isNew', | |||
// this is partially unimplemented because the field is not returned by the local blockchain | |||
'account.delegate', | |||
// this is unimplemented because setting this precondition made the integration test fail | |||
'network.timestamp', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed the timestamp precondition, so removed it from the list of unimplemented preconditions!
@@ -257,7 +257,7 @@ To write a correct circuit, you must avoid any dependency on the concrete value | |||
throw Error( | |||
'fetch can only be called when the State is assigned to a SmartContract @state.' | |||
); | |||
if (Mina.currentTransaction !== undefined) | |||
if (Mina.currentTransaction.has()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unrelated fix
let party = Party.createSigned(feePayerKey); | ||
let feePayerAddress = feePayerKey.toPublicKey(); | ||
let party = Party.defaultParty(feePayerAddress); | ||
party.body.useFullCommitment = Bool(true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unrelated fix, necessary for intg test
@@ -52,183 +56,115 @@ function asFieldsAndAux<T, TJson>(typeData: Layout, customTypes: CustomTypes) { | |||
}; | |||
} | |||
|
|||
function toJson(typeData: Layout, value: any, customTypes: CustomTypes): any { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lots of refactoring here -- see the new mapReduce
function at the bottom of the file, which abstracts what most of the functions here were doing!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good! Just one naming nit
src/snarky/parties-helpers.ts
Outdated
); | ||
} | ||
|
||
type MapReduceSpec<T, R> = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is semantically more of a fold
ala Fields.fold
in the ocaml code that’s generated from deriving fields
. I think something like FoldSpec and genericFold or layoutFold?
let delegate = PrivateKey.random().toPublicKey(); | ||
update.delegate.value = delegate; | ||
|
||
party.tokenSymbol.set('BLABLA'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hahaha! This token symbol is funny.
jsLayout.Party.entries.body.entries.preconditions.entries.network | ||
); | ||
let network = party.body.preconditions.network; | ||
party.network.stakingEpochData.ledger.hash.assertEquals(Field.random()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does invoking assertEquals on a random Field element do here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It changes a precondition on the Party
. I'm trying to add as much changes to the party here as possible, to not miss a part of it where the hashing logic doesn't agree between JS and OCaml (if I were just using the default party that I start out with, the hash input would be mostly zeros -- so two of those zeros could be switched and we wouldn't notice)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok that makes sense.
Party.Checked.t
on the OCaml side, where it would useParty.Checked.digest
to compute its hash. Under the hood, the most of the logic inParty.Checked.digest
is about converting the party to a list of field elements, the "hash input", which is then hashed.Types.Party.toInput()
completely on the JS side, removing a crucial dependency of snarkyjs onmina_base
. Party hashing in JS now only depends on OCaml viaPoseidon.update
which provides the "raw", non-application-specific hashing logictoInput
is implemented as usual, with a recursive function leveraging the auto-generated party structureparties-helpers.ts
which achieves sharing code between most of the functions which walk the auto-generated structureTokenSymbol
circuit type