-
Notifications
You must be signed in to change notification settings - Fork 111
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
Experimental offchain storage pt 1 #1630
Merged
Merged
Changes from all commits
Commits
Show all changes
45 commits
Select commit
Hold shift + click to select a range
1d0bdde
move existing reducer code into a new folder
mitschabaude 941639d
remove `provable` reexport
mitschabaude 693f06b
for each method on merkle list
mitschabaude bf24822
scaffold out merkle map rollup
mitschabaude 1f2bf0d
some code golf
mitschabaude bd676ce
cloneable merkle tree
mitschabaude d29d490
get leaf of merkle tree
mitschabaude d9c3824
finish v0 of merkle map rollup
mitschabaude c5700ee
add outside proof logic
mitschabaude a1439c3
some comments with efficiency ideas
mitschabaude 8fc54e0
serialization and custom efficient hashing
mitschabaude f5256c7
finish merkle leaf serialization
mitschabaude 5a6604c
rename
mitschabaude 23d926d
start typing out stuff
mitschabaude f39e013
nice option type
mitschabaude ca2dc19
more api scaffolding
mitschabaude 6021181
example offchain state interaction
mitschabaude d442abf
bindings
mitschabaude cd1e402
minor
mitschabaude 98fb766
start implementation
mitschabaude 65645b3
minor
mitschabaude d19e3d0
fix
mitschabaude ecbce96
small fix
mitschabaude 246d2d6
small fixes
mitschabaude 9018c93
probably fixes
mitschabaude ebf3e47
+ update API
mitschabaude 7bc958d
implement set()
mitschabaude 80d66ab
don't use update() for now
mitschabaude bc2893d
change serialization and implement get()
mitschabaude e00fb47
settling
mitschabaude bd966c6
support onchain state default values
mitschabaude 4f525ba
set state fields explicitly
mitschabaude 7a10b49
Merge branch 'main' into feature/experimental-offchain-state
mitschabaude 5ead7a5
fix how we get contract
mitschabaude 0c51655
improve unconstrained + empty
mitschabaude 75c6dd2
refactor to two separate program methods for now
mitschabaude 1e5b5a3
disable proofs
mitschabaude e9df987
suprisingly non mutating api?
mitschabaude fc7e633
use merkle tree, not map (set leaf is different)
mitschabaude d7502aa
make unit test more test-like
mitschabaude cda0e1f
fixup contracts without state
mitschabaude 44d5616
some boyscouting
mitschabaude 0df6f4e
export option type
mitschabaude 744251e
option docs, initial state example
mitschabaude 28a4e69
changelog
mitschabaude File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Submodule bindings
updated
2 files
+13 −0 | crypto/poseidon.unit-test.ts | |
+1 −1 | mina-transaction/transaction-leaves.ts |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,7 +32,11 @@ export type { | |
FlexibleProvablePure, | ||
InferProvable, | ||
} from './lib/provable/types/struct.js'; | ||
export { provable, provablePure, Struct } from './lib/provable/types/struct.js'; | ||
export { | ||
provable, | ||
provablePure, | ||
} from './lib/provable/types/provable-derivers.js'; | ||
export { Struct } from './lib/provable/types/struct.js'; | ||
export { Unconstrained } from './lib/provable/types/unconstrained.js'; | ||
export { Provable } from './lib/provable/provable.js'; | ||
export { | ||
|
@@ -48,6 +52,7 @@ export { Gadgets } from './lib/provable/gadgets/gadgets.js'; | |
export { Types } from './bindings/mina-transaction/types.js'; | ||
|
||
export { MerkleList, MerkleListIterator } from './lib/provable/merkle-list.js'; | ||
export { Option } from './lib/provable/option.js'; | ||
|
||
export * as Mina from './lib/mina/mina.js'; | ||
export { | ||
|
@@ -59,12 +64,8 @@ export { | |
type PendingTransactionPromise, | ||
} from './lib/mina/transaction.js'; | ||
export type { DeployArgs } from './lib/mina/zkapp.js'; | ||
export { | ||
SmartContract, | ||
method, | ||
declareMethods, | ||
Reducer, | ||
} from './lib/mina/zkapp.js'; | ||
export { SmartContract, method, declareMethods } from './lib/mina/zkapp.js'; | ||
export { Reducer } from './lib/mina/actions/reducer.js'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. moved |
||
export { state, State, declareState } from './lib/mina/state.js'; | ||
|
||
export type { JsonProof } from './lib/proof-system/zkprogram.js'; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
import { OffchainState, OffchainStateCommitments } from './offchain-state.js'; | ||
import { PublicKey } from '../../provable/crypto/signature.js'; | ||
import { UInt64 } from '../../provable/int.js'; | ||
import { SmartContract, method } from '../zkapp.js'; | ||
import { Mina, State, state } from '../../../index.js'; | ||
import assert from 'assert'; | ||
|
||
const offchainState = OffchainState({ | ||
accounts: OffchainState.Map(PublicKey, UInt64), | ||
totalSupply: OffchainState.Field(UInt64), | ||
}); | ||
|
||
class StateProof extends offchainState.Proof {} | ||
|
||
// example contract that interacts with offchain state | ||
|
||
class ExampleContract extends SmartContract { | ||
// TODO could have sugar for this like | ||
// @OffchainState.commitment offchainState = OffchainState.Commitment(); | ||
@state(OffchainStateCommitments) offchainState = State( | ||
OffchainStateCommitments.empty() | ||
); | ||
|
||
@method | ||
async createAccount(address: PublicKey, amountToMint: UInt64) { | ||
offchainState.fields.accounts.set(address, amountToMint); | ||
|
||
// TODO `totalSupply` easily gets into a wrong state here on concurrent calls. | ||
// and using `.update()` doesn't help either | ||
let totalSupply = await offchainState.fields.totalSupply.get(); | ||
offchainState.fields.totalSupply.set(totalSupply.add(amountToMint)); | ||
} | ||
|
||
@method | ||
async transfer(from: PublicKey, to: PublicKey, amount: UInt64) { | ||
let fromOption = await offchainState.fields.accounts.get(from); | ||
let fromBalance = fromOption.assertSome('sender account exists'); | ||
|
||
let toOption = await offchainState.fields.accounts.get(to); | ||
let toBalance = toOption.orElse(0n); | ||
|
||
/** | ||
* FIXME using `set()` here is completely insecure, a sender can easily double-spend by sending multiple transactions, | ||
* which will all use the same initial balance. | ||
* Even using a naive version of `update()` would give a double-spend opportunity, because the updates are not rejected atomically: | ||
* if the `to` update gets accepted but the `from` update fails, it's a double-spend | ||
* => properly implementing this needs a version of `update()` that rejects all state actions in one update if any of them fails! | ||
*/ | ||
offchainState.fields.accounts.set(from, fromBalance.sub(amount)); | ||
offchainState.fields.accounts.set(to, toBalance.add(amount)); | ||
} | ||
|
||
@method.returns(UInt64) | ||
async getSupply() { | ||
return await offchainState.fields.totalSupply.get(); | ||
} | ||
|
||
@method.returns(UInt64) | ||
async getBalance(address: PublicKey) { | ||
return (await offchainState.fields.accounts.get(address)).orElse(0n); | ||
} | ||
|
||
@method | ||
async settle(proof: StateProof) { | ||
await offchainState.settle(proof); | ||
} | ||
} | ||
|
||
// test code below | ||
|
||
// setup | ||
const proofsEnabled = true; | ||
|
||
const Local = await Mina.LocalBlockchain({ proofsEnabled }); | ||
Mina.setActiveInstance(Local); | ||
|
||
let [sender, receiver, contractAccount] = Local.testAccounts; | ||
let contract = new ExampleContract(contractAccount); | ||
offchainState.setContractInstance(contract); | ||
|
||
if (proofsEnabled) { | ||
console.time('compile program'); | ||
await offchainState.compile(); | ||
console.timeEnd('compile program'); | ||
console.time('compile contract'); | ||
await ExampleContract.compile(); | ||
console.timeEnd('compile contract'); | ||
} | ||
|
||
// deploy and create first account | ||
|
||
console.time('deploy'); | ||
await Mina.transaction(sender, async () => { | ||
await contract.deploy(); | ||
}) | ||
.sign([sender.key, contractAccount.key]) | ||
.prove() | ||
.send(); | ||
console.timeEnd('deploy'); | ||
|
||
// create first account | ||
|
||
console.time('create account'); | ||
await Mina.transaction(sender, async () => { | ||
await contract.createAccount(sender, UInt64.from(1000)); | ||
}) | ||
.sign([sender.key]) | ||
.prove() | ||
.send(); | ||
console.timeEnd('create account'); | ||
|
||
// settle | ||
|
||
console.time('settlement proof 1'); | ||
let proof = await offchainState.createSettlementProof(); | ||
console.timeEnd('settlement proof 1'); | ||
|
||
console.time('settle 1'); | ||
await Mina.transaction(sender, () => contract.settle(proof)) | ||
.sign([sender.key]) | ||
.prove() | ||
.send(); | ||
console.timeEnd('settle 1'); | ||
|
||
// check balance and supply | ||
await checkAgainstSupply(1000n); | ||
|
||
// transfer | ||
|
||
console.time('transfer'); | ||
await Mina.transaction(sender, () => | ||
contract.transfer(sender, receiver, UInt64.from(100)) | ||
) | ||
.sign([sender.key]) | ||
.prove() | ||
.send(); | ||
console.timeEnd('transfer'); | ||
|
||
// settle | ||
|
||
console.time('settlement proof 2'); | ||
proof = await offchainState.createSettlementProof(); | ||
console.timeEnd('settlement proof 2'); | ||
|
||
console.time('settle 2'); | ||
await Mina.transaction(sender, () => contract.settle(proof)) | ||
.sign([sender.key]) | ||
.prove() | ||
.send(); | ||
console.timeEnd('settle 2'); | ||
|
||
// check balance and supply | ||
await checkAgainstSupply(1000n); | ||
|
||
// test helper | ||
|
||
async function checkAgainstSupply(expectedSupply: bigint) { | ||
let supply = (await contract.getSupply()).toBigInt(); | ||
assert.strictEqual(supply, expectedSupply); | ||
|
||
let balanceSender = (await contract.getBalance(sender)).toBigInt(); | ||
let balanceReceiver = (await contract.getBalance(receiver)).toBigInt(); | ||
|
||
console.log('balance (sender)', balanceSender); | ||
console.log('balance (recv)', balanceReceiver); | ||
assert.strictEqual(balanceSender + balanceReceiver, supply); | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
We should be able to remove this method entirely now, I think?
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.
no because this example uses the fact that it's a
@method
and affectsprovedState