Skip to content

Commit

Permalink
Merge pull request #1458 from o1-labs/feat/calldata-hashing-perf
Browse files Browse the repository at this point in the history
Use `HashInput` packing for computing call data
  • Loading branch information
MartinMinkov authored Feb 22, 2024
2 parents b2639e8 + 9ce4ea6 commit 7f1745a
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 47 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- `transaction.send()` no longer throws an error if the transaction was not successful for `Mina.LocalBlockchain` and `Mina.Network`. Instead, it returns a `PendingTransaction` object that contains the error. Use `transaction.sendOrThrowIfError` to throw the error if the transaction was not successful.
- `transaction.wait()` no longer throws an error if the transaction was not successful for `Mina.LocalBlockchain` and `Mina.Network`. Instead, it returns either a `IncludedTransaction` or `RejectedTransaction`. Use `transaction.waitOrThrowIfError` to throw the error if the transaction was not successful.
- `transaction.hash()` is no longer a function, it is now a property that returns the hash of the transaction.
- Improved efficiency of computing `AccountUpdate.callData` by packing field elements into as few field elements as possible https://github.com/o1-labs/o1js/pull/1458
- This leads to a large reduction in the number of constraints used when inputs to a zkApp method are many field elements (e.g. a long list of `Bool`s)

### Added

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
"prepublish:web": "NODE_ENV=production node src/build/build-web.js",
"prepublish:node": "node src/build/copy-artifacts.js && rimraf ./dist/node && npx tsc -p tsconfig.node.json && node src/build/copy-to-dist.js && NODE_ENV=production node src/build/build-node.js",
"prepublishOnly": "npm run prepublish:web && npm run prepublish:node",
"dump-vks": "./run tests/vk-regression/vk-regression.ts --bundle --dump",
"dump-vks": "npm run build && ./run tests/vk-regression/vk-regression.ts --bundle --dump",
"format": "prettier --write --ignore-unknown **/*",
"clean": "rimraf ./dist && rimraf ./src/bindings/compiled/_node_bindings",
"clean-all": "npm run clean && rimraf ./tests/report && rimraf ./tests/test-artifacts",
Expand Down
10 changes: 10 additions & 0 deletions src/lib/hash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export {
packToFields,
emptyReceiptChainHash,
hashConstant,
isHashable,
};

type Hashable<T> = { toInput: (x: T) => HashInput; empty: () => T };
Expand Down Expand Up @@ -180,6 +181,15 @@ function packToFields({ fields = [], packed = [] }: HashInput) {
return fields.concat(packedBits);
}

function isHashable<T>(obj: any): obj is Hashable<T> {
if (!obj) {
return false;
}
const hasToInput = 'toInput' in obj && typeof obj.toInput === 'function';
const hasEmpty = 'empty' in obj && typeof obj.empty === 'function';
return hasToInput && hasEmpty;
}

const TokenSymbolPure: ProvableExtended<
{ symbol: string; field: Field },
string
Expand Down
14 changes: 14 additions & 0 deletions src/lib/scalar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,20 @@ class Scalar {
return Scalar.toFields(this);
}

/**
* **Warning**: This function is mainly for internal use. Normally it is not intended to be used by a zkApp developer.
*
* This function is the implementation of `ProvableExtended.toInput()` for the {@link Scalar} type.
*
* @param value - The {@link Scalar} element to get the `input` array.
*
* @return An object where the `fields` key is a {@link Field} array of length 1 created from this {@link Field}.
*
*/
static toInput(x: Scalar): { packed: [Field, number][] } {
return { packed: Scalar.toFields(x).map((f) => [f, 1]) };
}

/**
* Part of the {@link Provable} interface.
*
Expand Down
34 changes: 27 additions & 7 deletions src/lib/zkapp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,13 @@ import {
} from './circuit-value.js';
import { Provable, getBlindingValue, memoizationContext } from './provable.js';
import * as Encoding from '../bindings/lib/encoding.js';
import { Poseidon, hashConstant } from './hash.js';
import {
HashInput,
Poseidon,
hashConstant,
isHashable,
packToFields,
} from './hash.js';
import { UInt32, UInt64 } from './int.js';
import * as Mina from './mina.js';
import {
Expand Down Expand Up @@ -479,16 +485,30 @@ function computeCallData(
) {
let { returnType, methodName } = methodIntf;
let args = methodArgumentTypesAndValues(methodIntf, argumentValues);
let argSizesAndFields: Field[][] = args.map(({ type, value }) => [
Field(type.sizeInFields()),
...type.toFields(value),
]);

let input: HashInput = { fields: [], packed: [] };
for (let { type, value } of args) {
if (isHashable(type)) {
input = HashInput.append(input, type.toInput(value));
} else {
input.fields!.push(
...[Field(type.sizeInFields()), ...type.toFields(value)]
);
}
}
const totalArgFields = packToFields(input);
let totalArgSize = Field(
args.map(({ type }) => type.sizeInFields()).reduce((s, t) => s + t, 0)
);
let totalArgFields = argSizesAndFields.flat();

let returnSize = Field(returnType?.sizeInFields() ?? 0);
let returnFields = returnType?.toFields(returnValue) ?? [];
input = { fields: [], packed: [] };
if (isHashable(returnType)) {
input = HashInput.append(input, returnType.toInput(returnValue));
} else {
input.fields!.push(...(returnType?.toFields(returnValue) ?? []));
}
let returnFields = packToFields(input);
let methodNameFields = Encoding.stringToFields(methodName);
return [
// we have to encode the sizes of arguments / return value, so that fields can't accidentally shift
Expand Down
Loading

0 comments on commit 7f1745a

Please sign in to comment.