Typescript/Javascript framework for zk-SNARKs and snapps

Overview

snarkyJS

snarkyJS is a Typescript/Javascript framework for writing zk-SNARKs and snapps.

It allows you to construct zk-SNARK circuits in Typescript and run the prover and verifier in the browser (via wasm) or on a server (via Rust).

snarkyJS is very much a work in progress. Examples on here may be incomplete, and APIs may change completely over the next few months.

If you would like to provide feedback, develop using snarkyJS, or beta-test it, please get in touch via this form.

Comments
  • Account / network precondition RFC

    Account / network precondition RFC

    Precondition RFC

    If users want to add preconditions on the account or protocol state, these will most often relate to the current on-chain state of these fields. A very intuitive model is to let users just "use" the current state -- for example, let them access the "current account balance". The mental model is that this balance is a variable (not a constant!) that depends on the current chain. It can be implemented by fetching the current balance from the chain, using it in the prover as the value for the variable, and adding a precondition which fixes the balance to exactly this value. If we wouldn't add the precondition, then the balance would be unconstrained, which would be against the intuition that we prove a computation which uses the current balance as input.

    Therefore, I think a good default is to just let users access the balance (and other fields) as the property of some object, and behind the scenes add the necessary precondition. We propose the following API to achieve that:

    // inside method
    let balance = this.currentAccount.balance; // automagically adds a precondition
    balance.assertLte(100e9); // this method can only be called if current balance <= 100 MINA
    

    Note that to use this API intuitively, one doesn't need to understand the concept of a precondition at all. It's enough to be aware of the this.currentAccount property. The returned balance is a plain UInt64 variable; the added precondition stays completely hidden. Similarly, we propose to have a this.currentProtocolState property which exposes protocol state preconditions. On other parties, we would have corresponding party.currentX properties.

    However, advanced users may want to declare arbitrary preconditions. For example, in the above example, it makes sense to actually not use the current balance, but just to add a precondition directly. We propose the following API:

    Party.assertBetween(this.accountPrecondition.balance, 0, 100e9);
    

    This works for any party, not just the this party. It mutates this.accountPrecondition.balance, which is the precondition (represented as an object {lower: UInt64, upper: UInt64}.

    The last example achieves the same as the first one -- it constrains the balance to be less than 100 MINA -- but without the fragility that the current balance needs to stay unchanged until the transaction is accepted. However, to use an API like this, you do have to understand the concept of a precondition. That's why it's OK to use more jargon ("precondition"), and make this a bit less discoverable (have to use Party), and let it not do any magic. Similar to this.accountPrecondition, we propose to expose this.protocolStatePrecondition, which would just be shortcuts to access party.body.accountPrecondition and party.body.protocolStatePrecondition.

    In addition to Party.assertBetween, there is also Party.assertEquals (already implemented), and we could likewise add Party.assertBelow and Party.assertAbove.

    Finally, you may want to reference the current state when declaring an arbitrary precondition. For example, possibly you want to restrict the nonce to an interval of the form [currentNonce, currentNonce + tolerance]. Or you may want to restrict it to be exactly currentNonce + 2, because you already know that there'll be 2 earlier transactions which increment the same nonce. For this final use case, we propose the following API:

    Party.assertEquals(this.accountPrecondition.nonce, this.currentAccount.nonce.add(2));
    

    This is the precisely same API as before, just making use of this.currentAccount. The logic would be that any explicitly set precondition would override any magically inserted precondition. So, in this case, nonce == currentNonce would be overriden to nonce == currentNonce + 2.

    TODO list

    • [ ] Implement the this.current* API and, for other parties, party.current*.
    • [ ] Implement this.*Precondition and party.*Precondition.
    • [x] Implement Party.assertBetween, Party.assertEquals
    • [ ] Fetch all account preconditions from chain
    • [ ] Fetch all protocol state preconditions from chain
    • [ ] Create unit-tests which confirm that
      • setting/fetching all preconditions is possible
      • set preconditions show up in the JSON transaction
    rfc 
    opened by mitschabaude 20
  • String Implementation

    String Implementation

    It would be convenient to have strings native in snapps. Snapp state could point to a URL with more information about the snapp itself. A username could be included in a circuit, etc...

    I have an implementation I have been working on, but I'm wondering what the spec should be if it were to become a PR into the library.

    The way my version works is I defined a UInt8 CircuitValue and implemented a StringCircuitValue as an array of UInt8. The primary interface with a snapp is in the fromBits and toBits functions which allow a user to initialize a field as bits, and convert back to a string. e.g.

      it('can serialize and deserialize from Field', () => {
        const input = 'Input String 123'
        const ztring = new StringCircuitValue(input);
        const field = Field.ofBits(ztring.toBits());
        expect(StringCircuitValue.fromBits(ztring.toBits()).toString()).to.eq(input);
        expect(StringCircuitValue.fromBits(field.toBits()).toString().replace(/\0/g, '')).to.eq(input);
      });
    

    I have not thought out edge cases like trying to pack in more than 256 bits of data, nor thought about performance or security. Just worked on the happy path until now. If there's interest in merging something like this into snarky, I would be happy to clean it up. Ideally there would be a spec of what o1 thinks a string ought to behave like, but if not, I'd like to get some feedback regarding concerns or holes I may not have considered. If this implementation doesn't work, then maybe it will spark some discussion to find one that does.

    To view my implementation, see these relevant files:

    String.ts: https://github.com/qcomps/cachebox/blob/2369e41e55d3b78285cad2cfa9a2d0ef545d4f41/src/lib/snarkyUtils/String.ts

    UInt8.ts (other than the relevant parts, this is a lazy port of UInt64, which may not actually work for math): https://github.com/qcomps/cachebox/blob/2369e41e55d3b78285cad2cfa9a2d0ef545d4f41/src/lib/snarkyUtils/UInt8.ts

    String.test.ts: https://github.com/qcomps/cachebox/blob/2369e41e55d3b78285cad2cfa9a2d0ef545d4f41/test/snarkyUtils/String.test.ts

    UInt8.test.ts: https://github.com/qcomps/cachebox/blob/2369e41e55d3b78285cad2cfa9a2d0ef545d4f41/test/snarkyUtils/UInt8.test.ts

    good first issue 
    opened by 45930 19
  • zkApp composability RFC

    zkApp composability RFC

    zkApp composability RFC

    "zkApp composability" refers to the ability to call zkApp methods from other zkApp methods. Technically, it uses the callData field on the zkApp party to connect the result of the called zkApp to the circuit/proof of the caller zkApp.

    An initial spec by @mrmr1993 laying out the ideas for how to use callData can be found here: https://o1-labs.github.io/snapps-txns-reference-impl/target/doc/snapps_txn_reference_impl/call_data/index.html

    We propose the following API for snarkyjs:

    API

    @method myMethod() {
      let calledContract = new CalledContract(address);
      let result = calledContract.calledMethod(arg);
      // .. do anything with result
    }
    

    That is, we can just call another method inside a smart contract method. That's the API 😃

    NEW: To enable returning a result to another zkApp, the result type must be annotated with a TS type:

    class CalledContract extends SmartContract {
      @method calledMethod(arg: UInt64): Bool { // <-- HERE
         // ...
      }
    }
    

    The return type annotation is captured by the decorator (like the argument types), and supports all circuit values.

    Since it is easy to miss adding this, we take care to catch the case that

    • a method is called by another method, and returns something else than undefined
    • BUT no return type annotation exists

    In this case, the following error is thrown:

    Error: To return a result from calledMethod() inside another zkApp, you need to declare the return type.
    This can be done by annotating the type at the end of the function signature. For example:
    
    @method calledMethod(): Field {
      // ...
    }
    
    Note: Only types built out of `Field` are valid return types. This includes snarkyjs primitive types and custom CircuitValues.
    

    zkApp calls can be arbitrarily nested! So, the calledMethod above could itself call another method. (In theory, a method could even call itself! But we would need logical branching to make that work -- right now, it can't conditionally call itself, so calling itself would cause infinite recursion.)

    How it works under the hood

    At a high level, calling a zkApp does not mean that the called method is executed inside the caller's circuit. Instead, the callee should have its own party, with its own execution proof for the method that was called. This means that we have to be all the more careful to constrain the callee party in the caller's circuit, in a way that we fully prove the intended statement: "I called this method, on this zkApp, with these inputs, and got this result".

    To make the caller prove that, we do the following:

    • In the callee circuit, we compute the following hash, and store the result in the callee's callData:
    this.body.callData = Poseidon.hash([...inputs, ...outputs, methodIndex, blindingValue]);
    

    --> inputs are the arguments of the method call, represented as an array of field elements --> outputs is the return value of the method call, represented as an array of field elements --> methodIndex identifies the method that is called, by an index starting with 0, among all the methods available on the called smart contract (the index is represented as a full Field) --> blindingValue is a random Field that is made accessible to both the caller and callee circuits at proving time (it has the same value in both proofs!). In the circuit, blindingValue is represented as a witness with no further constraints.

    • In the caller circuit, we witness the party of the callee, plus the result of hashing the callee's own children (= the calls field of the callee's public input), plus the return value of the called method. Then, in the caller circuit we perform the same hash as before and compare it to the callee's callData:
    // ... witness `callee` and `outputs` ...
    let callData = Poseidon.hash([...inputs, ...outputs, methodIndex, blindingValue]);
    callee.body.callData.assertEquals(callData);
    

    --> this check proves that we called a method with a particular index, with particular inputs and output. To also prove that we call a particular zkApp, we have to add more checks (see next bullet point) --> the blindingValue is needed to keep the inputs and outputs private. Note that the callData is sent to the network as part of the transaction, so it is public. If it would contain a hash of just the inputs and outputs, those could potentially be guessed and the guess checked against the hash, ruining user privacy. Since guessing the blindingValue is not possible, our approach keeps user inputs private.

    • In the caller's circuit, we also assert that the publicKey and tokenId on the callee are the ones provided by the user:
    callee.body.publicKey.assertEquals(calleeInstance.self.body.publicKey);
    callee.body.tokenId.assertEquals(calleeInstance.self.body.tokenId);
    

    --> these checks make sure that the child party in the transaction, which belongs to the callee, has to refer to the same account which is defined by publicKey and tokenId. Thus, we're proving that we're calling a particular zkApp.

    And that's all we do under the hood when you call another zkApp!

    An important thing to note is that we add stuff to the callee circuit, although the called method didn't specify that it can be called. To make that work, we in fact add that stuff to every zkApp method. In particular, the callData field will always be populated, regardless of whether a zkApp is called or not.

    Making all zkApps callable is a deliberate design decision. The alternative would be to add a special annotation, for example @callableMethod instead of @method, to tell snarkyjs that this method should add the callData computation to its circuit. That way, we would save a few constraints in methods that aren't callable. However, many zkApp developers would probably not consider the requirement of a special annotation at first, or overestimate the performance impact of adding it (it is really small, just 20-odd constraints for the extra Poseidon hash). To make the zkApp callable later, it would have to be re-deployed later. Also, we would have to ensure that a non-callable zkApp aren't called by accident, because that would leave the call inputs and output completely unconstrained, making the caller's proof spoof-able.

    In my opinion, it's much better to spend a few constraints to make composability the default!

    EDIT: Another decision I made is to not expose the callee's child parties to the caller's circuit. Instead, I just witness the relevant hash, leaving it up to the callee to do any checks on its children. The rough idea is that when you do a function call, you usually don't want to / shouldn't have to "look inside" that function and inspect inputs and outputs of nested function calls. I'm curious if there are considerations that I missed!

    rfc 
    opened by mitschabaude 15
  • [Tokens RFC] - Research desired functionality to expose

    [Tokens RFC] - Research desired functionality to expose

    Description

    Before implementing token functionality in SnarkyJS, it is important that we align on what we intended to expose and how it works. Some pre-homework to this task is to do a quick survey of existing token implementations in other chains. Following that, this issue has been broken into 3 steps.

    1. Examine protocol code to see what functionalities are exposed with tokens
    2. Interview a select number of protocol and product engineers to agree on interfaces that should be exposed in SnarkyJS
    • [x] - Brandon
    • [x] - Gregor
    • [x] - Matthew
    • [x] - Izaak
    1. Finalize RFC
    rfc 
    opened by MartinMinkov 11
  • Adding String Type

    Adding String Type

    Problem

    A ZKapp might want to manipulate "string"-like data or make assertions about a string other than its complete equality with another string. Bijective encoding is an existing string to field representation, but it cannot fulfill every task that we might want from a string. A canonical use case could be asserting that the string Today's date: 05/12/2022 contains the string 05/12/2022.

    Solution

    Adding a string type which can be manipulated char by char, which is just a wrapper of an array of fields.

    Drawbacks

    For now, each character takes up a whole field of storage. This implementation includes a lot of looping and comparison at the per-character level which can't be super fast.

    Fixes https://github.com/o1-labs/snarkyjs/issues/92

    opened by 45930 10
  • Fix web version (infinite loop)

    Fix web version (infinite loop)

    The web version of snarkyjs is reported to go into an infinite loop when compiling, cc @maht0rz @jackryanservia

    one difference to node is that for web, there's some manually written "table" of functions that are run from workers. if that table got out of date, we had the web version break sometimes here: https://github.com/o1-labs/snarkyjs/blob/main/src/chrome_bindings/worker_run.js

    opened by mitschabaude 8
  • Do we have to compile the circuit before verifying the serialized proof

    Do we have to compile the circuit before verifying the serialized proof

    The following code can be verified normally, but if I comment out this line of code await MyProgram.compile();, it will report an error:

    import { SelfProof, Field, ZkProgram, verify, isReady, shutdown } from 'snarkyjs';
    
    await isReady;
    let MyProgram = ZkProgram({
      publicInput: Field,
    
      methods: {
        baseCase: {
          privateInputs: [],
    
          method(publicInput: Field) {
            publicInput.assertEquals(Field.zero);
          },
        },
    
        inductiveCase: {
          privateInputs: [SelfProof],
    
          method(publicInput: Field, earlierProof: SelfProof<Field>) {
            earlierProof.verify();
            earlierProof.publicInput.add(1).assertEquals(publicInput);
          },
        },
      },
    });
    
    let MyProof = ZkProgram.Proof(MyProgram);
    
    console.log('program digest', MyProgram.digest());
    
    // console.log('compiling MyProgram...');
    await (async () => {
      // If you comment this line, `verify` return false
      await MyProgram.compile();
    })()
    
    
    const verificationKey = `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`
    
    let proof: any = {
      publicInput: [
        "0"
      ],
      maxProofsVerified: 1,
      proof: "KChzdGF0ZW1lbnQoKHByb29mX3N0YXRlKChkZWZlcnJlZF92YWx1ZXMoKHBsb25rKChhbHBoYSgoaW5uZXIoZGEzODE4YTU2OWVhMjI2MSBmMTg3NDYyY2EzNGFhYzEzKSkpKShiZXRhKGZjODI2M2VmYjg3NGFmZjYgZGE3YjllMDY5NDA0ODgxMikpKGdhbW1hKDAwODMxMThlYTM2MTAzYjQgOGU2MTc2MzRmOWY3MzE4OSkpKHpldGEoKGlubmVyKDdhNDVlZTNmMTljNTQ1ZTYgZTUzNzRhNWM2MGE3MmNkMikpKSkoam9pbnRfY29tYmluZXIoKSkpKShjb21iaW5lZF9pbm5lcl9wcm9kdWN0KFNoaWZ0ZWRfdmFsdWUgMHgyQzAzN0UxODg3NUMwRjRDOEQzREE1RUJCODBGNjFGMjU2QTY0NTg5MjYxOTc5RUVFODQ5NzU2OUM5NjI3NDBEKSkoYihTaGlmdGVkX3ZhbHVlIDB4MDA1MDAzQUZFMjJCN0Q1N0FDMjFCOTUxMDlBQTQxMUZCNTJDMzBEODY0N0E5QTY3MTNFRUUxMUEyODkwODFFNSkpKHhpKChpbm5lcihhNmM2ZjFkNzFhMjUxM2Q3IDcxYTM2Yjg1ZjliZTA4MDcpKSkpKGJ1bGxldHByb29mX2NoYWxsZW5nZXMoKChwcmVjaGFsbGVuZ2UoKGlubmVyKGZlNDgzMTJlNzJiNmU5NDAgZTUzMzQ5ZDRkMWFkMTUxMSkpKSkpKChwcmVjaGFsbGVuZ2UoKGlubmVyKDY0OTU0Y2E3OTIzOTdmZTAgNmQ0MzBhM2MzODgzYzE2MCkpKSkpKChwcmVjaGFsbGVuZ2UoKGlubmVyKDQ1OGZhN2M1OGY4YTE0ZTggOGUwZWQxMjNmM2M5YWExYikpKSkpKChwcmVjaGFsbGVuZ2UoKGlubmVyKDE1ODFiMTVlMTVlMzUzNTYgYWZkMjJlZWRjMDBjOTM2ZSkpKSkpKChwcmVjaGFsbGVuZ2UoKGlubmVyKGU1ZmQ3MzliMTUyMzMzYjIgNzQ4NmVmYWQ2NjkzNDZmNykpKSkpKChwcmVjaGFsbGVuZ2UoKGlubmVyKDcwN2ZiZTdkZjg5ZTk1MWUgNjYyMWU5MWU3ZDA1ZGYxNikpKSkpKChwcmVjaGFsbGVuZ2UoKGlubmVyKDE1NGNhZjQxNzM1M2NiZDcgY2I1ZmJkYzYzMmUxNDNmZCkpKSkpKChwcmVjaGFsbGVuZ2UoKGlubmVyKDc3ZTUzYmIxMTJmYTgyNDIgM2NkN2UxMWQ4YjlkYzQyMSkpKSkpKChwcmVjaGFsbGVuZ2UoKGlubmVyKDU4NDExNTI0NjIxNGRkNmIgNTE5OTNlYTBmZDU0M2RiZCkpKSkpKChwcmVjaGFsbGVuZ2UoKGlubmVyKGJjMzIzNWZmZTlkNmVhMTkgNzI4ZWVlYWUwODlkZTVmYSkpKSkpKChwcmVjaGFsbGVuZ2UoKGlubmVyKDg2NjM2YWY1NDBjYTZkYjkgNjIyNjgzMTE0YzZmM2EwNCkpKSkpKChwcmVjaGFsbGVuZ2UoKGlubmVyKGUyNzcyNmM2ZmMwN2M3YTkgODVkYTEyMWUzMjdjNDk2NykpKSkpKChwcmVjaGFsbGVuZ2UoKGlubmVyKDk4MWQ2NjYwMzk5ZDc1ZjIgZDQ0MjI5YWJiMTkzOGRlYykpKSkpKChwcmVjaGFsbGVuZ2UoKGlubmVyKDRjM2U5MzYyNDdhMDllZjIgNjZiN2M2NzkyMzhlMTQ1NikpKSkpKChwcmVjaGFsbGVuZ2UoKGlubmVyKGM5ZWJkOTllY2I4NzVlYjggMTlkZmU3MDUzNmJmM2YwNSkpKSkpKChwcmVjaGFsbGVuZ2UoKGlubmVyKDYzNDNkYjI4ZjlmZDI0MDQgMzdkOWVmNjE5MDBlOWRlZikpKSkpKSkoYnJhbmNoX2RhdGEoKHByb29mc192ZXJpZmllZCBOMCkoZG9tYWluX2xvZzIiXG4iKSkpKSkoc3BvbmdlX2RpZ2VzdF9iZWZvcmVfZXZhbHVhdGlvbnMoMTNjNzRmNzg0NTM0MWY1NCA2OTBlZmU4Y2I4ODI3MDNlIGQxNzMzY2NiYmNiYjBmODQgMzQ1NDhjODI3OWNkMDM2MykpKG1lc3NhZ2VzX2Zvcl9uZXh0X3dyYXBfcHJvb2YoKGNoYWxsZW5nZV9wb2x5bm9taWFsX2NvbW1pdG1lbnQoMHgxQjREQURDQzVFMDZEQjRDOEIyRDkzRjY1MjdFRTZEOEVGQUY4NTMzRTI1MjBFQkM3MkRBMkU4MjM3QUY3QTFDIDB4MzlBNjZCMUYwMjExMUU3MTRENTVBRTM5MEQwMTE4RDkxQTJFNEMxNUZGNzY1RTcyM0Q1ODIzMUM4QTdGN0JDRikpKG9sZF9idWxsZXRwcm9vZl9jaGFsbGVuZ2VzKCgoKHByZWNoYWxsZW5nZSgoaW5uZXIoMzM4MmIzYzlhY2U2YmY2ZiA3OTk3NDM1OGY5NzYxODYzKSkpKSkoKHByZWNoYWxsZW5nZSgoaW5uZXIoZGQzYTJiMDZlOTg4ODc5NyBkZDdhZTY0MDI5NDRhMWM3KSkpKSkoKHByZWNoYWxsZW5nZSgoaW5uZXIoYzZlOGU1MzBmNDljOWZjYiAwN2RkYmI2NWNkYTA5Y2RkKSkpKSkoKHByZWNoYWxsZW5nZSgoaW5uZXIoNTMyYzU5YTI4NzY5MWExMyBhOTIxYmNiMDJhNjU2ZjdiKSkpKSkoKHByZWNoYWxsZW5nZSgoaW5uZXIoZTI5Yzc3YjE4ZjEwMDc4YiBmODVjNWYwMGRmNmIwY2VlKSkpKSkoKHByZWNoYWxsZW5nZSgoaW5uZXIoMWRiZGE3MmQwN2IwOWM4NyA0ZDFiOTdlMmU5NWYyNmEwKSkpKSkoKHByZWNoYWxsZW5nZSgoaW5uZXIoOWM3NTc0N2M1NjgwNWYxMSBhMWZlNjM2OWZhY2VmMWU4KSkpKSkoKHByZWNoYWxsZW5nZSgoaW5uZXIoNWMyYjhhZGZkYmU5NjA0ZCA1YThjNzE4Y2YyMTBmNzliKSkpKSkoKHByZWNoYWxsZW5nZSgoaW5uZXIoMjJjMGIzNWM1MWUwNmI0OCBhNjg4OGI3MzQwYTk2ZGVkKSkpKSkoKHByZWNoYWxsZW5nZSgoaW5uZXIoOTAwN2Q3YjU1ZTc2NjQ2ZSBjMWM2OGIzOWRiNGU4ZTEyKSkpKSkoKHByZWNoYWxsZW5nZSgoaW5uZXIoNDQ0NWUzNWUzNzNmMmJjOSA5ZDQwYzcxNWZjOGNjZGU1KSkpKSkoKHByZWNoYWxsZW5nZSgoaW5uZXIoNDI5ODgyODQ0YmJjYWE0ZSA5N2E5MjdkN2QwYWZiN2JjKSkpKSkoKHByZWNoYWxsZW5nZSgoaW5uZXIoOTljYTNkNWJmZmZkNmU3NyBlZmU2NmE1NTE1NWM0Mjk0KSkpKSkoKHByZWNoYWxsZW5nZSgoaW5uZXIoNGI3ZGIyNzEyMTk3OTk1NCA5NTFmYTJlMDYxOTNjODQwKSkpKSkoKHByZWNoYWxsZW5nZSgoaW5uZXIoMmNkMWNjYmViMjA3NDdiMyA1YmQxZGUzY2YyNjQwMjFkKSkpKSkpKSkpKSkpKG1lc3NhZ2VzX2Zvcl9uZXh0X3N0ZXBfcHJvb2YoKGFwcF9zdGF0ZSgpKShjaGFsbGVuZ2VfcG9seW5vbWlhbF9jb21taXRtZW50cygpKShvbGRfYnVsbGV0cHJvb2ZfY2hhbGxlbmdlcygpKSkpKSkocHJldl9ldmFscygoZXZhbHMoKHB1YmxpY19pbnB1dCgweDIwNTgyMUMwQTU4NTQzNzhBQUQ0MzlBNzQzRTQ4MTI3RjQ1NENCNTk3RDM3Q0Q1Q0U5NkIyOURBRTgxOUI5RjAgMHgzMDZBOTIwMjY4NTJGOUJCRjEyQ0ZFNURBQTZCNzNGNEYyNTc3MTYyN0UwNUFDRjE1MjFFRTA0MDJEMUY0NjRFKSkoZXZhbHMoKHcoKCgweDI2NERFRjkyN0NENUQ0Nzk3QjhEQjVGMzE4QTFDNUVDMTY3MDY1QzEwQ0ExOTRCODExQjc2N0U1NTdDOThBRjMpKDB4MDMwNzgwNUVFQjA2MTFCRDkwQkUwRDQ3QUY3ODJCMTA1NTUwNTg0NzM1MkVCMUM3QjFEMkZCNUY5Mjk3MzBGRikpKCgweDExRUZFNDEzNzc1RDk2MTYwMzA5NzNEMkI0RDJDQTE1ODlDOEI1OTVEMjFDOTBBODA0RUZCOUZEMDFGOTc4RkQpKDB4MzEwMUJCNUYwMjEzREU1NjQxNzM3MjMzOEQxQUFGNkJDRkFFQzMwNDhERjM3NkY2NEY4OTAwQjZEMkMzQkEzMykpKCgweDA4MzJBQzg5QUM4ODhFMkIzQ0ZCMjdBMDJENTJCNzlFMzMzOTE4QkRCMzExMTQxNUM4NjVBMEFBRjE4ODJEMzApKDB4MzQxRjQyMDlGMkY3RjU4Q0NERjQ3QUM2OEU5Nzg5NjY2QjVGMjlDNEE0REEwQTUzREVFQTYwMEQ5MTM1ODI1MSkpKCgweDM5RjlCQUE4N0E1NkQxNDFBMTBEMjVCMDJEMkQwNTU3QUVENTJCMTk2MDYyNDFCODQ0Njg0NEMzNzUxMEZFRDIpKDB4MDYzMEJDMDQ1RTdDNjI4OUIwMkU5MEU2QkNEMzkxMUQ3RDBFODdFQzgwNDk3M0U4ODk0ODJCNjJDMEU0RUU2MCkpKCgweDM0RDU2QzZENTFFNDRENDA4Qjk3MDlFMTYwQkREMkRBRjI2RDlFQzlCNkMwQ0I1RDVDQkE1QjMxRkE0QzU0RTQpKDB4MzBFMUVGNDIyNzRBNzA1QjlEOUY1ODc4NkZGQTQyQUFERkQ3MEVCMEI2MDNDN0M2MDg1NzdEQUUzMkVBNkNBRikpKCgweDJEQUE4MDI2MUJCQzMzRkQ2RjU3ODU5QTczNkI2OENDQTcxNTM4NkMyRjg4NjQxQjkxNTE4NzlCRDc2Mzg5QTkpKDB4MTYyQkUwNTkwNjE4MTk1MTBDNkJGOEM0RDMzRjczNUE2NUQ2QUMxNTkyNkM1MDMwQkZEM0UxOTA5RkJGMTI3OCkpKCgweDIyRkMxNTI1MjREQzBENEZBN0Q0NEU2Nzc3RDEwQzM0MkNBQTYwMzczRjhBOURGNzlCOEYwNzU2QjUyQUJEOUYpKDB4MUU2QTk4OUM3MDIyQ0I4RkE5Q0NERjVFNDVFMUYyMEQwRjgwMDRBNkNERDU5MThCMjNFNDBERTJFMURBMzgyOCkpKCgweDNFRDVFQkJCMTlBRDU5RTEyMUU0RTQ0MDg2MzA4M0FBNkMzMzY2NEZEODM3RkRFRjNEMkRBNDVERTM5NEI1ODcpKDB4M0Y0QjRDQkI4OTkxQzQ1NUY2RkM4Q0FGMDdFQjMwNDY5RTVDM0JFNEIyMEI2NDQyRjc1Q0JENkIzM0Y3MjY3RCkpKCgweDFGMTQwRDVDNzZDMEQ3RDE2NjE1RTcyQjEyNzA1RTIyN0QxRjU3MUE1M0U3Nzg0QjQ1MTlFQUQwQjk5RDQ1NzUpKDB4MTQyNTgyQ0MxNjkxQzU5MUZEODlBOEQzMzY1NjU2RUE5RUQxQkZFQjdGNkY0OEIyMzVFMDU0NjU1RURERTgzRSkpKCgweDI3NzZCMjY0NDk3QzQ1OUFFQ0Q4MDRBNjY3MTVDOUU2RDNGQjJBRDRENzlCMTgyMkMzMjI5Q0FDMUNCNzE3ODApKDB4MEEyMjc0MjM4QUVDQzk2NDJEQUY5ODQ1Q0E4NkMyQ0NDQTU4NjNBRUMyNEQ2REZGNjE5RDQ3RDlBNjNCMDQ3NykpKCgweDBGOTRCMEIxNUE4NjRGMTQ1MzRCNjY2QTRDNkY5MzUyQjFBRkFFMDM1OEY0OENBQjM3N0Q3M0I4NDUyQzBDMzIpKDB4MkI0NzUwRkYxMjVFNjFCMDg1Q0U3MjJGMkQwMzUxRUFBMTE5RkU4NzM5RkUyMzU5Nzk3RTM2RkFFNzA0RDkyMSkpKCgweDExQ0E2RUJFMjNDOEQ5NzY4MTJEQjhENzY3NzRBNUNGRjM5QjAyNTk3MEZCNjEwN0I5RTcwNUM5RjNFMEYzRDkpKDB4MkJGMjdBNDBCOTc1RTgwMEVFNDU4MEUyRDYyQThDQzkzMUJFMDE0RDQwNkFCNTBBMzMzMDMxMDBBMTZDMDEwMikpKCgweDFDNjc3MDBBM0YyNjE5QUM4MDIwNDgzMkZGRThEQTg0OEJFMEE0Q0NBMjRCQkU1MDMyOUYxQzA2NjI2ODI3OUMpKDB4Mzc5NDYzNkZFRjIyREVCMDczN0VFQUI2RkI4MDBCOUUyMzEwMTA2QzAzMDA0QTY5NjJDMDE3OEVGM0JBNDFGNSkpKCgweDIwRTIwNDY2Nzc5Q0RCN0VGRUQ2QzBBMjVBMUU5MjJFRjdGM0IwMjlCMDRGMjIwMDk0RkFDOTEzM0JENDEzRUIpKDB4MDkxRTkyMTMxMEY3NUEwMUMzNjRDMzA5NUJEOTJCQTI4MjlDOThBQkQ1MTY0NUIwMDVGNDBCMDdGNEIyQkFERSkpKCgweDBDQjI1Qjk1NzI3QkVDNkJCMjc5QTM0MDYwQUQ2MjdDMzU2NzU2QTM2ODBCMzlBQzk5Q0IzNTNCNzU2NTgxMTMpKDB4MkQ3RTY0QUZEODIxQjlFNTc4MTYyQjU5NTFDNTVGQ0IwMTFCRUFGMkNBOTIzRUExRTM0NTk4RjAyRTQzQ0FBMykpKSkoeigoMHgyNUU1Qjg0OThCQThBQkFDNTFCNUFFRjI1NDQ2NEU5NzMwNEFFREUyRjJENzAzNTNFNjY1MkNBQ0M2MTExRTIxKSgweDMzNzQxQjBCM0RGREFCRjdBMzIyRUMwQUM1MkEwNkUzNEE2NDE2OTEwMzU2MjZDNDQ5RERCRDNDRDE2QUY0MzEpKSkocygoKDB4MDUzNjFEMzdCMTY5RTY0NERFREExMTQyN0UxRUZFMTU2NEIwNzA2OUIzMjVGQTAzQjhFQzIwODEwQ0VFRTU4NCkoMHgzQzNFRjYyQ0FDRDMyN0UxNDRENTg5QUJGQTIyMUVENDNGRDkyQzMyNTFFMDc1MjJDNzhCOUZBQUQxNTc4MjEzKSkoKDB4MTc4RDQzNTY3OUJERTBBNkQzNzc5NDA4RUNGMTlGMkQ5NTI2QUYyQUU0NkU4NzFGMDZEMzdFNzlBNDdEMTE1NykoMHgwNUExODNDNTYxNTg4NUY2N0RDNDNEMDU2RDA1QUE2OTI1QjZCOTA1NzY3ODBBNUZCMDJGM0ZBMDExRDdBREY2KSkoKDB4MjA1MkU4NTFDQjdBNTU1MTBGQzExQjgyODNCQTZBMDI1NDI5NDQ0RUYyQUI1MjVGRjc5MzVCMTM0MUVDNjMzNikoMHgyN0I5OEMxOTczNzc4OUU2QTQxMTJBQzY1Mjk4MkZERTVFMEQzQUM5ODBFOEUyMzZFNUEyMzczQ0U2Nzc1ODc3KSkoKDB4MTdCNUM2NzlGOUI5MDVCNDk4QkMwOEU1NkQ4NEUwMkEwNjlBNzM3Rjg3OUQ3MTQ5QzM4OURDQjRCQjVEMENBRCkoMHgyM0JFRjFDQ0QxREQxRUVEQzI5MDBENjE5MjlEMDY5MTNERjE1RUUzMDgxOUVDRTcxQzIyRjA2QzE1NjZCNjY2KSkoKDB4MTQ3MkRFNEU0NEFEQjIzMzU5OUQ2NDNFMURGQTlFRjA1RDhGMTE2QjNCMjYxQUNEOTFBNTQxODJCNjdDMkIwRikoMHgxQTVGNDg0MDM3RTlCOTU3NzgwQUM4OTM3NTVCNzFDREIwMjhCMTU0OUI3RDM5RjkzRTEzNUM2RjhENzMxNkNDKSkoKDB4MENFRDgyOUUwRDZDMjY4QUU0NTVDOURBMjlEQjExQzU4MzAwMjBGNTgzRTU1OTg4QjJFMDM2NjBCNkUzNkExMykoMHgzQjUzMzQ0RDZENTUxOTNENEMzOTA5OTA0MEU5OEM0MjlGREU0N0QzQjU2OUUyNzc4OTgyMTJDNjlGRTlGQ0U0KSkpKShnZW5lcmljX3NlbGVjdG9yKCgweDAzNzBFREVFOTgzMjNGQTgzNDVBREMxQkRENDEzOTI5QUExOUY5MjA0M0QwMzJCMzEyQjIwRkMyMkM0ODM4RkUpKDB4MzgzMzAyOUMyNjUzODIzRTdGMkRBOURBMkI1NzJCRkI1RDFCQzgxMjI2NDRFMTM3QkM4QUJDNTVCMzJDRkY0NCkpKShwb3NlaWRvbl9zZWxlY3RvcigoMHgwRTczOUQ2Q0QyNjk1NEM3OTcyNTRCQ0NEREJEOEEyODAyOEJENDA4M0I1MUVFNjc4OTgyNEM2MEQ3QkE4MjEzKSgweDMzNTRCMjVCNkRBRTE3RDNCMjNFRkREOTM2OTM2RDlERTFGNzZGRUEwODVDMjU4M0I0OUNGQjlEOTg5MjA0ODYpKSkobG9va3VwKCkpKSkpKShmdF9ldmFsMSAweDAyQTJFRTU5M0JDRjUxNTNBRUJDNjc3Q0I1QjY5RDQ0NjYwNTk0RDFFREUxQjYwNTQzMjkxRjY3N0MzMDU2OUIpKSkocHJvb2YoKG1lc3NhZ2VzKCh3X2NvbW0oKCgweDJDQkNGRkE2N0MwOUIzODdCNjlDRTVCQTRGNUE5MDc2QTYyOUY0RjQyRTAwQjcwODY3QjYwRTU5RkJENTA0NzIgMHgzN0FBNUYwMTlBNDE0NEI5MjlFNzQ5QjMyMkQ1MkY5MjUyODREOTMyNjc0NDY3Q0U1MjNBRTc0RTNENTFGRTNBKSkoKDB4MTkzMDhDMkFGN0I3RDk0RjJGQTc2MTk1RThERUYxMDA5NzQ5QUY1NTU4ODE0NTI5QzUyNTJBRDg2MzI4MTg4NyAweDM5MkJCNUNEMjQ5NDJCMjMzNDQzNkMwNkIyMkM0RTkzMzI4MjUwNENEQjU3QzM0NEJDM0RBNDNFNDlFRDUwNjQpKSgoMHgxQzBDRkY0QkFCMzlFRTFGRjJFNThFQTI2MkIzREVCMjM5RDgwMkJERDM1NjZEQzJFNkU4Q0IwNzlDMDVFNjY4IDB4MkIzMUExQ0JEMEE2QjdDOTRBNEQwRUQ0QUYwMTdGMTU3QjZFREQyMDZDM0YzQjkzQjlEMDM5NEU0M0YxQkZBNikpKCgweDBFQTkzQzQ0OUI2REM4QUY0QUU2MUIyOTQwQTg5NjA4OUFERDE2ODRGNzQ0MkQzRUUyMkRDQTk2NkU4MUY2NDIgMHgyQzBEQzg4NTY3RkUwRkFENDEwNjhGMkNDQzFEODRBMzE1MjRDQzRCMzg3M0REQUJCMzFBQkZCRTMzNTAxNkVDKSkoKDB4MEYwQ0YyRjc0MjVEQUEyM0I4QTYzQzM1RkYwQkYyNTgxQzQzM0NFNDZDN0NCNUU3MTRDMUUzRDNFRjdGNTRGNSAweDBGNTdCRjAzMzk0NzQxMzA0OTNBMkUwNEQwOEMxNUM3RkNBQzJBOEQ4NzJDQTFBOUQxMjRBOTRGMTQ5RTFGQTIpKSgoMHgxQTU1MUJFNzM1NTJCNEQxMzZDODlERUE1M0I4RDIzNjcxRTQ3OUJGN0YzRjIzNkZENjREMEYxNkI2OUQ2NUUwIDB4Mjc3MTY3MTU5QThCQkY3MTFGNTIyMTg1M0Y4NzZCRTZBRjRCRjg3Mzk2Qzc2N0ZFRDI4QTNCMTA4MzdFMEE1MSkpKCgweDBFNUREMjQ4QUNCMTA4NjZCQTAxRTk4QTMwRjFCNjQ1NTY1MEI1N0Y5QjhGNUQzMkEzMzcyOTlCMzJCRENCODAgMHgyOTE0RkE2MDNBRTQzOTE2QUFENUI4MzRCRkZCMzQ5MzcwRjQ1MkY0MUZBQzczMjFBRjM4NEI1OEM1QjI2OTk0KSkoKDB4M0QxRDRDNzgwRTU5QjIxREQwNjFGM0IzQjI5QTM0QUM1OTAzNUUyMTY4MUYwODM3MEVCNzQ5NDk0QjBEMUFFQiAweDE0MjkyQzIwQzNCMEZDOTVENTdFQUY5MDYwQ0ZBMTI1ODUyQzlBNkJFQkEyMzVEMEFDM0NGNDY2RTFCRDg1RkMpKSgoMHgxRTY0NkVBQzlFRjU2REEzQjFGRTc4QkJBMTk0MTk2RDMyMTI1Q0I5ODY1NzU4RjBERjRCRUZFM0M5MUU4RjFFIDB4MzJGRDcyOTg3MURCQ0U0NTUxM0U5ODY2MjhCQjM0MEFGNUQ0OTgzMDlGQTlBRUQ1MkY4QkQxOTM5RjlFRjlCMCkpKCgweDJGRjFGMjMzNzZDMTk3MTQ0NURGMzFGNkFDRjc4NzIwNEQ2NTY2NjZFNzE2ODVFMkNGMjE4MEY4NUVGQkNBMTkgMHgxODk5NEVGRTY3RkVCQzIyNDBCNDU1NUE0MDFBMEI1NUVBOUJCRjc2Qzg1MzUxRUFFMzc5ODUyOTRCNEM5QjJCKSkoKDB4MzExNkE1NUU2NThCM0UwMTlCNzM3OEUyMEFGMkRBQzhCNEVDOEVFNjRGNTdFMkUyNDI3M0E4MTA5MDQ4QTM3MyAweDI4ODcyRDQ3REM3RTRCMThGNTJCOTZDOTYzQzc1RkJGOTk2NjM5NTFERTc1QkI5MjRCNkEyQzBDNTE2NTRFRUMpKSgoMHgyRTIyQTFEMEYyRTg0RkExMjJBODgwREE5NkM1Q0NBM0VCNDFEODZFOTY5NDZGOEREMTQyNzVEOTUxODJBMDM0IDB4MURGNTA2RjdDQzkwNUVBRjg0NjY0MjcwMjU4NzM2MjNDMzE1NkM3RUYyRERFNTJGRjE1RTgxMEY0OTVDNTU5MikpKCgweDJFNDBFOEY3Nzc0QjEyNUQyRTg5QzJCODJFNzIxNERGODUwN0RFM0VERTI0REYzRUU1NTg1NTk1QkJGODgwOTQgMHgyNEI0Q0E2QjhERTgyOTM2ODc0MDk2NEQyRTkyMEFBRkEzNDI3QkE3NDZCM0UyMzA2QTBGMjdFODQwMDVBNTQyKSkoKDB4MzREQ0Y5RjM4NUZEMDQ2RTg4Nzg2MkU4RDExRTlFNDRGQzc2RkE3NzE4RjI4QjgzM0RBRUU0M0IyOEUyOUY3OSAweDM3M0I3MEJBMDk1QTNFMEZDODkxNjI4QjlEQzFGRkQ3MjU0RTc5ODI2NTUxNDFGN0VFREQ0MjM0MUYyMzY2RDkpKSgoMHgzNEU2RjVENERDRUY0MTE1RDE4RjdCMUQxRTlBRjMzQ0M4RTM4MkU3NEE2QTlEQTZFNDQxNEI5REM3ODk1NjUwIDB4MjI0RERCNjY4OUQ2QjZCNjEyNDI1MkQ2MDU2ODk1N0EzMjY0MDM4NzVGRkZDMUU3M0IyNzFCMUU1RDg0M0UzNykpKSkoel9jb21tKCgweDI3MEYxMkY2QjVCQjdBNjk1MkYyNDcyNDY4NjgzQ0FCQjg5MzM1ODIxNzhFQzhGMkJCRkY5RjQzMkZFN0M0OTkgMHgzMTYyQ0U5REQzQjYwRjFDNUUwMTEyN0JENTI3NTQ0OTFDNzZERkMwODgzNjkzQTJFMjk1NzM5NkQ0OUQ1Q0EzKSkpKHRfY29tbSgoMHgzNjExN0U5REY3QTI0MjM1Q0QzOTQxRDVENzdCRkMyRTI4Q0E3QzA1NDlENDlFNzVCNTc0RDg5N0VENEY3MDRGIDB4M0QyREU3QUI3MDlDRUY2QTcxNjE2MjA0ODU1M0UxNTQ3Mjc3NTEyQjU2NzUzNEZBOEIwQzZGMzI0MEU5QTNGMikoMHgwNDQ0OTY1RDM0NTQ3MEE5RkU3Mjc4MzNGOEIwRjQ4OTlGQkNDMUYxMUE4MTEyQTBFN0U4QTBBMTVCMzA0RDBEIDB4MjE3NzlBQTFFNjlCN0E5OTJDMEU1RTQ4MjM0QUMzODc5QTUyRUE1QTREMjg1NUM3RDJGODM4Q0U1RkNDNjE1MSkoMHgyM0M0MEYxOUIwRjNENDBEOUI1NzQ5REQzODc3REFEQTIxMTVCNUYxOUYwMkZFRjhBRjJBRTc5ODEzNENFQzVFIDB4MkY0MDgwMzU3NjcxMUM1MDI2OTIyODFFRjE2QjEzOUMxM0M1RDhCMTEyNUQ0QUEzNDZDN0E1QzYxRDdFOURCRSkoMHgyREJDQjNCMjQ5QkZFOUQxQzcyOEM0Rjc1NURBMUQxMUVEQTlEMDAxNEIyNEE2NDU4NTFBNEZCNzJDMzlGMzNEIDB4MTQ5MjY4NEFCN0NBOTVGMTdGRUU5Q0EwQTQxOUExNzQ2Q0IwNDc2MDIyQTgwRTkyMDQ0NEFCMTM0MkNBREQ4NCkoMHgxQjlDNDVERjY0RTBGRURBMTI3NDVDMEM0N0VGNzA2MTVFNzhENDNGN0E0MkJEM0I1N0FBNTM1MjEzRDRCNUQzIDB4MUFEQkRCOTQxNkJDQjk1OUVCRkIyMTlBRDlCOUQyNjZEMURFNkFDMkVCNDI3RUY5NjAxQkI2RDY4NTQ5NkE3MCkoMHgyQUY2QzI3NEMzRjgzMkQ4MDAwNTBCNjE5OTgxQTUxMThGQjk1ODIxQzUyQjY5ODJFQTY4MTJBNjI4ODc4NEQ2IDB4MTNGNDRENDkyM0ZFQTJFQTFBRkQzQkJFRUJEMTE5MERERkI1RkY5NDhCMkM5RTI0MDFCQjZBNTFBOTdBNTE4QykoMHgzRDcyOTJFMjE1MDgwRjRERUIyQUMxQkVDOTgyRDM3QjJBQjNBOUYxMkRFMjlERUExQjQxNzA5MUZGNjg0MzAzIDB4MEQxOUNBMEUxOTE0MDQ3NjREMEI1NDU4RDJCQTk1MEEwMjU4RENFOUE4NDY4NDNGMzE0NUI1MUI4MUQ0ODg5NikpKShsb29rdXAoKSkpKShvcGVuaW5ncygocHJvb2YoKGxyKCgoMHgyRTVFQkMyNDBBMDMzQ0E5RkFERDUxOTM4OUY4NEY1Q0YyQUVEMkJBQUM3NjFBREZBODNCMDg0Q0E1MkFFNjg5IDB4MDk1MzU2QURFNTRGMEMxNkVCQzE3MDMwM0NBODZCMEUyM0M0OUY4OEREMEU3MTA5NjcwOUEwN0FGQzUyQjY2RCkoMHgxMjg1QzBFMTVGMzdGOERBM0QzNTJEMERCN0VGNTQ4NUJDODQ4MDhDNTEyQjk4RTVDRDg4ODdBOTdEOEMxNUU4IDB4MDlBQjJCMTU0MzlCQzk3N0FFNDQ5MDFDMzQzOUY3MDIzQzUwQUU1NUJGNDZCMDZCNDA1MUNDMUU4MTZGM0JGMykpKCgweDExRDREMEVCOTJCMjYwQzlBRjQ5NjQ1REYzQjlCQTQwNEMxRjYzNTc5RDIyMjZFNzRBMkVBMzlFNjEyQTAxNzQgMHgwRkIyOEJCMUQ4NUJGRjZGRDdBQkRCODM0MEEyMzkyMDI4QjVGM0ZCMTExMUNFOTE3QzE4NzgwODc3MDhEODIzKSgweDE5NDg0OTY2ODA4MUI3NzU0NTZERDk2QkVDRTI1QjYxRUM3OEU0ODk0OUVGMEU0ODMwQjM5QTIyNEFBNzFBNzUgMHgxMjQ1QkI2MzM3QzRFQzc4MzA5MjkzMUExNTE5Q0JDQ0JDM0U4NjA5OTY2OUQ1OUQ2ODJDOTEyRTFFOTkyNjVGKSkoKDB4MEJCNkNFM0VERTBFQkIyRDY3NEQyODdBNTVDOTk3MzI5OTE4RTgwQTI2NkNERDBGOERBNDJGMDZDOEIwMzc1QSAweDEwNEU1QTI3Njg2RDZCMDRENDk1QTkyRDNGNjJBM0MxNjYzRTMzQzM5RkE0MDg2RTFDMjFFNjQwNDlGNzQxRDUpKDB4MkUzN0RDRTlCMEY1MEZFNEEzN0IyMzQ4QkYyMTE5Qjc5NzE0QzEwNDAxMTk0MzM1REVFMkUxMjBDNEJDM0I1RiAweDEwMkQ4MDAzNjBDM0Q4QzY3QzdBNTg2RUU4ODhENjYwNTAzMTRENjE2MkY2Q0NGNTc5MzY1MDgyNzFDNjQ5NzEpKSgoMHgxNDZGNTBGOTE2NDgyOEE3RDExN0NBODhFNDU0N0RGM0EzMTJCMUMxRkFCN0FCNEFEQzRBRjE5NzQ3NUY3Qjg3IDB4MTVBRUQ5NTdBMEE4NzhDMDM0QjZEQzJFMTg4MEMwMUE2NTQzNTg4M0EzMUI0MkQ5MDU1NjE4QUVDRTE4RDAzNCkoMHgzNENCODkzRUEyNkM1RjU5QzNGMkJFODFCQTA0QzBEQURBRTM4RDE0NzZFNjFGNDY1NUExNzBBM0JFNzJBMzlFIDB4MjE0QjQ1QTczOTVBREUxM0I5QjRFRjIyMUFDNDA1MUVBQUJDN0M2NDU5NTc4Q0RFOTJGMEJFQ0E1MUQ5N0YyMykpKCgweDA2NUEwRTJEQjRDNjk4NUMzOTlFQkI2RkE5NjZFNzkwMzlEMTc1Q0I2NTM5QjVEQzRFMUQwMTg0NThCODRFRjkgMHgwRjI4NTg5QjE1NERCNkY2NkJCMjZFMTVGM0M4RThBRUYxQzQxQUY1MTJCNTVDOTVGMTlCQTRBNTg5NkNFMDNFKSgweDBCNTY2M0NENkIzQzUzMTZEQ0JCMzk3NDQ5RTBBNDhDMjlGM0M4Mjg4MUVDNDNCMTVGQURGMTZCMUEwQjU4MDIgMHgxRTREQ0MwNzNGOTA2MjJCNkU0NUVBRTBENUJDQUEwRDQ3RDZGOTM3Mjc2RkMyNTVENUJBQzlFNUQ2RDYyRTU4KSkoKDB4MDIzMjM5MDA0NDU5M0FDRjAxRDVGRTUxN0Q3MkREQ0YyQ0IwMTEwNjRCQTQyMjQ5OEE3NUVGQUFCMEVDRUFEOCAweDFDNTkxNzAzN0ZFRUVEM0YxNUQ4Qjg3Q0M0MUYyNDBGNUFCMkQ2QkEwN0Q3OUIzMzMxOThBNDYwNjcyMTcwMjMpKDB4MkE2NTZFQ0U0MkEwMTU0NEQxRjZEMEQ3QkY1NzYwNTY1QTUzM0VERjc5MUYzNzdBN0ZBRjYwQzEzREQzOTE1OCAweDA4NDc2NkY3NThFRkRERjk3N0Q1NTQ3NkE4N0E4QjU5NjRBOEVBODRDQUMwQzNGM0JERkYyN0MyRjRCQzJCMTcpKSgoMHgxQzVBRDA1MzQ5M0Y0NjYyRURBMUJGMUU3N0RDRTEwQ0I0OEM1NDFEOEVFRUYzRDU1MDZDMzc5ODdCMkU3RERBIDB4MzBFMTRBRTc5NTE1MEYzQjdEMzU4NTIyNTdDMUUwMTdGQ0YxODFCNEYwODMzQkU1RkY3Mjc2QzcwOUJGMUM4RikoMHgwQzU3NDVCOUZDOTIyMDM0QUQ1QTNCRjJBREFGRTYzQzI5QzRBMDZGNUNGM0QwNkU3QzE0ODZBMDMzRkIxRDU5IDB4MkQ1REFFQkFGQzFCMEQ3NTQ3OUQyRkVCNEJEMDU5MTJBNDY1OUQ1RkQxQTFDQ0VBNkU2QzI2N0MwNjg1RTlGMSkpKCgweDIwOEU0QTU1OUMxMTVFMENGQTg1NUYxQjExNTRCQ0I0MkYxMkI5RURBQ0RGNURDOUQ0NUUwQTI4QTcxMjk3REIgMHgzMEIxQTU2OTc0RjJBRDNGOEQ0OTBENEI2M0M5RTdDQkVBNThCNTJBQTdDMjJCNDk3QzNEOTI5NkUzQzRGMkM5KSgweDAzQjI4MUNCOUQwRDUwN0E4NTA3NzIyQTRDQkM5QkJCOEU0QzNBRjVFMkVGNzkwNTNBQzVDNEQ3REJCNjlDQkIgMHgzMzI0QTNCOTY0OEMxRjlBQ0YyMjBCM0QzMkVCRTNGQjI2RDREQUU3NzYxMDE4QzhBQTdGRjc3NkJGRTY3RkI3KSkoKDB4MkNERjhCQjY1OTYxMDI1QkNDRTZGRTkyRDQ4MEM1QkE4MDBERDc0QjFCRDZCQzUxMkUyMkVGMUE3RThCRTIxOSAweDI3Mjc0M0ZCODU0QTM5RkVBREQ1RENERTZCQzA1M0VDNzlBQzBENUZEN0E4REU0Mzk2OUMwQTE4RjM1MTE4NTcpKDB4MTRFNDA3QTlBMDQ4RjZFM0QzNDAxQ0JERjA5QjE5OEE2NTFGN0FBOTE0MzJDMDY0QjA0MDZEREUwNzlGQTE0QSAweDJEOTA0OThDMDA2MjM1Mjk0RDdFNDZEMTlENjFGQjJFQTcyMkRCOUY2QkEyNTExMEEwREQyMzM4RkJCMzdEQjUpKSgoMHgyOTg2NzA3MTY2NjI5Nzk5MjlERUY0QUJCOEExNDIxOEU3MDUwNTcxMzQyODg5QzYyRjk5NjNFMzAyRjY4MTE3IDB4MEZGNTY5RjY5ODFEMjc0QTA1MjhERjlCNDcyMDUyNTIyMDE5NTEyNTk2MUJEQUQ3MzRCRTMyODM4Mjk5QkExRikoMHgzRkI3NUE1Rjk1M0I1RjNGREQ3QkIzNUUyMzI5MDUyMTJENUE1RkUwMzA1QTI4Mjk0N0ZFRDM2Qzk4MjhCMTlBIDB4M0NBQzE1MTA5Q0M1QjQzOTg5NzMyMUY2QTFCRUZGOTFDRDE4NkVGMjQ4MEYyNkVCRERCNUQwNDZDRTA0NUVGOCkpKCgweDIwNEZDMjQxNTU2RTE2RTY0NjM2MDY0RUJGNDdDNzdBNTM2MzkwOTNDNEZGNTEyNkNEMUJFRjYxMjEwRDg5RjQgMHgyRkZDM0Y2RDgyOTBEOTNFOTU1Q0ZCODRBRjI4RjkzRDRCRDVDRTE0QURFNEI2MEMwRkE5N0MwQUMyNjQ4Qzc4KSgweDM3NzFBM0Q4OEU0MEY2RkQwQkFBNUVEQjBCREFDRTM5RTgxOEQ1ODlDOEE3NzFDRTNBNjc1RDVGMjYwRjJEQ0EgMHgyMTQ0NjkzMzQwRDc3MjIxNUQxNTUxRDJEQjRCQzk1MEJEODE1N0JBMUM2RDA5NEQwODZCQjE3NDZFQTA4NDlGKSkoKDB4MTFBMkU2NTVDMDk4MzU0NDYwMEFENTUzNTRBRUUyQTgxRjY2RDQ4NUZBMEY0MDUyNzU1QkFBQjVDMkZBNTI1OSAweDE2MDlEQzE1NDdFNjZGNDcxQzVCRkQ2N0NDNDdEOUJEMDZFMEJGQ0I3RkM4QTVFOUJDMDIzQkMxN0ZDNjgwNUEpKDB4MEI1RjkxQzkyN0U4QTFDRUEyODZCNzJDMzQ0RjIyOTE4QzkyN0YxQ0RFNDNFMUY5RjE0Mzk1MDI1MzYwNzc2NiAweDE2OTdFRDIyNzAwMEJGQThCQjdENzQ1MEM2RDVCNDdCRTRCMzBDNTI5NUU2MzAyRDIxMDkyQjY4REYxMkY4ODgpKSgoMHgwNkE0OTNEMUY5MkNFQjdCQjc5MjQyREY1ODIwMDA5MTM0NjlGMUE0NzIyNzZENjdBOEY5QTUzMEZGODQ2MTI0IDB4MzY2QTA3RTdBQzFDQkE1NDNFNjRFNTk5Rjg5OTJCMjY3NzExNTFFQ0U5RTZBMzBEMkQ3N0U0NkMwNUQ0NEE2NCkoMHgxM0VGMDczMUVDMTZBMTg4RjQ2QTg3QjhCNUUwQ0QwMUYzQTU1NDBGRTAyOUFBMjg2RDcyNjk1NDEwMzE0QjVGIDB4Mzg1Njg4OTE0Nzc4QzdBRDgxRDdDOUVEMkQ1RkQ1MDc3NzZCODRERTJDODQ5OTc2OUUyRDY0QkE4RUI0NzZFQikpKCgweDIxQ0ZGODE4QTEzQkE4NUQ4NTI3NjJFOTVCMDFBQjgzOUM0RjY2MUJDRjMwOThBNDkzNTE5ODMyMDA5NUREQUUgMHgzQkMyMTM4M0I5MjYxQzJCNjhCQUFFMjU4RkRBRkFBMDQwNEEzNThFNkQ4Q0M1NkI4MjZGQzUwODk1QjMyNEVFKSgweDAyNjM4ODc1QUM3NEZGRjMzRDZBQ0NDNTU1OEM2OTEzNTFDNTdFMzZGNDY2MjFDMTg1MEIyNUM3Q0ZFQjRCODkgMHgzRDMxRDhENTIwOThFQjMwMDUzNzJENzk0QkMyQkJCMzBFRUFGRThEOTZBQTRCMUFERTMzN0M4NTc2MjQ1QTIwKSkoKDB4MjJEMjNFRDE2RkQ2REE5MTMwMzFGRUE4ODhFQ0U1OTUxNDFENzE0RTMwNTlGNzUwQjE3Njc4RUM5QzlGMkIzOCAweDEzNzAyODIwMUEyMzM4QzBDRTk4Mzc5QUI3NDg2QjJDNzFDRkUxNDRCNEZDMUU5RjBBOUJDRjIyMzdGMDM4QzMpKDB4MDVCMzY5ODJEOUZFMTk3NzA0MzlBOEZENkNDRDhERjVGRDk3MzE3ODU4Qjg2MDYzMzVEMEJBRjc2RDk0MUY2NyAweDM2OEM5RjA1NjE3RTFGQzlDNUZCMDAyNUEwRDk0NDgzMzQ2OEI0NTIwREY4MDJGNUNBREVDQzRFMTY0NzNCQkQpKSkpKHpfMSAweDAwM0QwOTkxRjEwQTUyRkFGRkE2QjZFMDNFM0M4NkI5MDdCMjU3NUI1NkM0RjUwOEM0NUY1MzY4QTY1NkFGMjUpKHpfMiAweDAzRUFFQTBFODU3NjM2RjY5OTdENzEzRTczOEU5MTNGMzcyMUU1MkExNDQ2M0M1QjFERTk5MEY5QzdGRTUxNkQpKGRlbHRhKDB4MzlCRUJEQTY1NjJGQTcyMDQ5ODI2RTQ2NDkzMEI5NUExOUFGRTk5N0JEQ0ZGQjc1M0ZBQTdGODI5Nzg4RTA4MSAweDFFRTU3QjlBRUVBNEU1RjVGOTgyQzJEMzhBQzA2NTcwNEE1MDQyOEE1NEE4NEFGNzUxNTMwQUI2NkI1NkZGQUYpKShjaGFsbGVuZ2VfcG9seW5vbWlhbF9jb21taXRtZW50KDB4MjREQTg0RjM2OTZGQkREMzRDOUQ4RTE0NjQ0OTFERjU1MTMyQTJCQTQ3QzIxQUM3NEY0QTk3ODUxOTdDQ0M5MiAweDFEQjZEMTcwOTNBNTA5RjYxNUEzNzQ1OEIyQ0Y2N0Q5Q0Q0ODc3QTkwNjE1Njk2RTFGNDc4RDNCOTZBQ0RERDgpKSkpKGV2YWxzKCh3KCgoMHgxNTEyNUZGNjc4QUQ4OUQxRTUwMEJBRjdDNjU5RjE4MjNEQjRGOEE5RkZCMkJEQTRDNjA2OTA3NzJGNDdEM0YwKSgweDM5Q0Y3N0RDOTMxMDkyMkIzNjMzRTVBNkQzNUFDREFCMDk2NEQ5RjlBRTlGRDI1NTY2QjRCMUVDNDhBMDZDNUMpKSgoMHgxMjJERTQxRTU2Rjg2N0Q1QzhBMDc2NThGMzI4MDQyNkVEMjU3Q0VERjg2NTMyNDY2REQzMDA1MTREMDUxNzM4KSgweDNEMjFBRTcyOUVGRUIyMzU4Q0M3RTY0QkFBNDFBQURENTU1MTk5QTMzRkI1RkE1NjkxQTc5NTA4MUUyNDVEMzApKSgoMHgyNEFFMTg4NzA1RkNERTAzOEM4OEUxRkUzNDk4MzAyNTYwN0E5RjUzMDAzOENGMENEMjU1QjYxRjZCMTI1OTk0KSgweDEzRjVCRkMxMDIzNjJENzUwNjZGQzJERDNBNjJGNEE4RUY3QjIyQ0I2QURGMEQ0QUNBQUY1QTY1NTlFMzg5NDMpKSgoMHgyQzlGODg1N0MzRkY3Rjc2MTZFMjQyNkUyRDc0OTE2MzJCNDI1OUE3RTFGN0Y2RDkxNTQ0N0ZCRkIwNzIxNzMzKSgweDJCMThBQTA4MzZGRDQ4NThCQzFBNThDNDU0MTUzMDE2QzkzOEVGNDE4NDA2OUI4RTcyNEQxNjI0MzVFQkQxRjYpKSgoMHgyODMwMDJBRTg1MkI4NTAyQjQ5MThFQzYwMDMzOTAyQzU3RkFDOTIwMEY5Qjg1NTg2M0ZEQjU1MzZFRDdEMzhDKSgweDAxRDE0NzBBNzlBRUY0NjUwMkE3NjlCNEU0MjAzMUVDODdENEY1QzE0NTA4RjNCMUUzMjVFMUQwOUU2NjdGMjIpKSgoMHgwOUY1N0Y4MTY2MzlBMEQxNTZBMTI0RUJENzM2OTc1QjlBREU0NDU5M0Q0RkQ3REIzQUQ2RDNBOTEwMTFBRUU2KSgweDEwQjU3MEJFOTJCNkU0RkNBM0M2RDlCQTQ1MDYyNDI0RTMwMDFBNzZEMThEMkVEOTVCQjZDMjFGRDQyRkZCNDcpKSgoMHgxQTcxODU0NDlBNDQ3M0NEODZDRkVFQzYyNzU3RDk4OUIyNTg2MDZBOEI3QjMxMzgzMDhGMkRBODcwRTlEOUM5KSgweDM1REZGRkYyQTY5NTY3NUFDQjgxNDM5OTc2NjMxREI4RUMwMDIzQ0I5MEQ2MTYxRDg1Njg1NUMwMzJERjI2QTUpKSgoMHgzRjBGREY5MTYwMjkwQURDNzNBRjQxNDM0RThDMDU2NDcyMjkyM0EzRjlDQTA1NkRFNjM2MzQxMDI5NUZFOTM1KSgweDA2ODYzRTVCNTJFNTI2QjY1NkQwMzBBMjExRjAxRkEyRDdFRDgxNzgxNTkwRDc5QUFBREQ1OTQ4NzM1NjA0NkYpKSgoMHgwQzRFMkIwNjgyNjRBQjg4REFEMjkyNkY5RjM4NjlGQTlEQUFBNUY2RTYwQzc2RjQ3QzNBQTYwQUI0NEM1MUJFKSgweDNCMzI0OTM2QURENUVCNDdCNDJCREVEMjAwQzVEQ0QxNEIxMTE4QTcxNDhFODc3RjE2RkM0NUIzNjVGMjc2RTIpKSgoMHgzOTBFQTVCMjBGOURDN0QyN0Q1RjdERkFBOThBMjE4NDQ3MERFMjIxMjQ2NTgwMDExODk0REI1QjY3MjE1OTEzKSgweDM4RjRGQjkzNkQ5OEQ0QjdFMDE5RjdBMDg3ODBDMEQ4REIyQzhEOUNBODhFNzIwQUNCRUUzQUQxREM2MEMzOUQpKSgoMHgwQzA5NUQ3MjdEMUQzRjMwRTY5QzRBNUFDRTJFMEY3MDg4MUI3QUQ5RTY1RkJDRjIxNkI2MENERUE0NTBDRjk0KSgweDBGQUU4ODUxQ0RCREE1M0REMjJCRTVFOEE3OTNEMzNGN0VCNEFEQTZEOTVBQkMwMDE4RTM5NENGQTk1NTI3RjUpKSgoMHgzMEQzNDUyQjhEREY0ODA2MjcxQTlCM0IxOUM1QzBERUZENjU3REY5OThFNzE3REY0NjI3RTgyNjdENDFDQTQxKSgweDEyQkUwQTE4RDE1OTMyRjcyNjY5NjZGNEI3MzQwQTY0NUREQzhGRDEyNUYwQTlFQTc0NTUzNDAzNjFBMEEzNTIpKSgoMHgzNjY5MjUyMDQ5NDZGNzYxMjA4M0UyNzI1RUU1NTk5QkE4MDAyNzM0NkM2RDlERDAyMDZGODZBNThERTlBNUYzKSgweDJCRjk0RDhCMjQxRkQ0MDUzMDlCRDQwNThEMzRGOEYxRkFBNjVCRDM0QTk1RjAyNjg5OEVFMDVFOEQ3MjQzNUMpKSgoMHgyMDJCRkU4QkQyRDFFODU5NURGQzYyRjI2RkMwMDU5NjJFOTRFNzZCREQyQUJFNTUyNDAxNEI3MjdBRTU5REM0KSgweDI2NzU3OEM4OTA5NTZEODg0MTAyRDFBNzkxQjM1Qzc3RjFFMDlCOEU5MTQ0NTY0ODAzOTAyQjlCMzBDQTJBMzYpKSgoMHgwNzU5QzJFNUM2QkZEQkRBMENEOTcyNTJFMTg5RDgyQkMyNTYxQjA0NDNDQUY4MDk3MTVGNjczQ0Y2N0RGRjY0KSgweDFDOUY0RTRBMTA5QTEyRTA4REUyN0NBRTIxRUZBQjBGQkM3RkZBREI0RDY4QzRBOTdBMzI4N0Y3QjU4M0Q4Q0UpKSkpKHooKDB4MDRFQTIwNDJGOUFDNkIwRTY1MDE0N0JDNkEyMDFCRkM5M0QzRTFDOTQ4QTBFRUY1MDc0QTlCOEQxNzU0MDVBNCkoMHgxRDMzQjU3MTIwNzg0OTZFRDAwNDUwN0ZBNUQ1NUYyNDlERkM5QzJEOUMwRDA0RUZDMjhFNDc3OTlFMEMxNDg2KSkpKHMoKCgweDA1QjNGMDgwNkZEOTI3MDU5Njg3ODEzRUQyNTUwNEY1OUIwQzAxM0I2REVDQjk4RkJENUU1RjYzQzI4MTIzNEMpKDB4MUY4MTc0MTU1RDFFQTM2MEQzQUZGRDJFMkI2OThBMThBQkMyNzI2MDE5OTA4REFEQzQ2RUNCMzA2N0VGNTlGOSkpKCgweDMwRUNBMTI1Q0QwNUU5NkU2M0U2NjA4NjAyRTlDQkE5QUU3RTIzQjA1REUyMjAwM0QxQTEwRDExMjUwMjVBQTcpKDB4MDhDMUIxNTQ4NEZBRkRGNDYwNzkxODM4MDYwQTYxRTg2NjM5OEJERUZFRTRGODBDNTkxQTU5OTJGMTE1OUM5OSkpKCgweDFGQkMzRTM0MzA2OUE3NUY5QTQ0MUM3RTRDMzBBRjVCMEI3NDJGQTNCMUIyQzk1RUFFMDIxREVGMTgwMDhCNUIpKDB4MDM3QTc4QzQ0M0YxNjI0N0Q3MkRCRTU0OUY4MkJFRkNBNzc5MDQ4MEQyRjIyNDY0QzgxODNBRTI4QzkzODFFMSkpKCgweDM3QjJFOTZBMkJERURCMzcxMzMzNzFGMjMyQzFGRDc5MzJFQTZFOENCNTJFQjhFN0ZBOTY3QjVCREI1NDhDQTYpKDB4MDA1QUU0OTRCOEQxNTE2NUI3N0ZBQzNBOTE0NzREQjYzQjdGMjZBM0ZFODYzNUI1MjY3QjZDQ0M5OEE3QkNDQykpKCgweDFCMDg4QjdCMjgzMjc2OTZCRjQ5NEU5RUQ0MzlEM0ZBMEU2M0Y4QUIxNzdBNERFOTkyQURFQ0M3NTMzMTA1RTcpKDB4MEVEQUVCNkQ1RDkxMjc0N0U1OUE0OTA4Q0E2NTgyQkQ3ODk2OEQzMkU1OUJFOTcxMjUwRjE2NDcxQjE2NjdFMCkpKCgweDI5OTA4RDBGNjQwQjk3OEVBNDA3Q0U1NUY1RTRDMDc3NTE2NkJDQTc5OUNBNTM3NjcwNEQwQzU0NjIxODc4MTMpKDB4M0Y3RDJFNzYzNDA0NzcyRTZFRjk2NjA1QUQ3RjE5N0U0ODYwM0M2NzA5MDY0NDZBMzI0NTRDRDk3RjVDNjc1NSkpKSkoZ2VuZXJpY19zZWxlY3RvcigoMHgxN0ExMTVEQkJCMTcwOTE3OUFCMzVEOTAzQzZGMDQ1NzJGQUZCQUFERjREOEVEOTdBODM4MTgyM0ZCOTA1RUZDKSgweDNDODUxMzNDNUJEMjI4NEZBNTIxREU3OEZDQTk1NzYwQ0E4NzM2ODA3NTg5OERGQjQ3M0U2NDIzRkZFRjBFRDIpKSkocG9zZWlkb25fc2VsZWN0b3IoKDB4Mjg1REUwOEZBRDI1Q0VFREM4MEM0OThERDhBQjlBMzhCQjJBMDkwMjI1Rjc0QTcyQUI4Q0ZEOEQxNkM2Rjc1QikoMHgyMUY0N0NFMkEzRURENEJGRURGNkFDRkREMjk0ODM0Q0QxRjI0RDM0MEE4MDA0QkJFMTZDRjIwQ0Y5N0E4RUE4KSkpKGxvb2t1cCgpKSkpKGZ0X2V2YWwxIDB4MjY2Q0M1M0E2RjM1N0FBMjM0NTQ3QkEwREQzQkUwNUY4Q0M1NjcyNjZENjNDMDg0MUQ3RkU0NUFBREEyRkZGMikpKSkpKQ=="
    }
    
    
    console.log('verify...');
    let ok = await verify(MyProof.fromJSON(proof), verificationKey);
    console.log('ok?', ok);
    
    await shutdown()
    
    bug 
    opened by zhfnjust 8
  • Issue sending batch payouts

    Issue sending batch payouts

    The behavior of the code to send multiple account updates in a single transaction has varied between all recent releases, but on the current network this code no longer works when making more than one balance update. https://gist.github.com/garethtdavies/576e86683ca8b7cd1722108d7efe9504

    When making a single transfer, the code works e.g. https://berkeley.minaexplorer.com/transaction/CkpaDtG9L5LqYguqTya6qYp6k8wvb1xZ1Pc25pEgd8votCS9sunPG

    When making two (or more) transactions, it fails with Update not permitted balance e.g. https://berkeley.minaexplorer.com/transaction/CkpYxz2WGF1HLng3D4DfyNwtAjbY7nY8bjfiUgJCcqrPEjaJjjAvx

    How can a zkApp make multiple balance updates in a single transaction?

    opened by garethtdavies 7
  • how to get the preimage size for implementing  sha256

    how to get the preimage size for implementing sha256

    hello , I am implementing sha256.

    before that i should know the preimage size to do the padding.

      export class Sha256 extends Circuit {
        @circuitMain
        static main(preimage: Field, @public_ hash: Field) {
    
          let size = preimage.toBits().length
    
        }
      }
    

    but when calling toBits().length, it returns 255 all the tme.

    so any idea to get the preimage size?

    opened by zhfnjust 7
  • Scaffold for zkApp VM application test

    Scaffold for zkApp VM application test

    Description

    Addresses #282

    This PR proposes a scaffold structure to add zkApp test applications and tests to Snarkyjs.

    File Structure and Location

    The smart contract used for this scaffold is located in the src/examples/hellow_world directory of the SnarkyJS repo. Creating future test applications, and adding them to the examples directory can serve the dual purpose of testing and providing more examples for the community. The tests are colocated with smart contract in a tests directory with the naming convention <SmartContractName>.integration.test.ts . Here is a basic folder structure for the contract and tests.

    - hello_world
      hellow_world.ts
      - tests
        hellow_world.integration.ts 
    

    Testing and CI

    These test are created using the Jest testing framework that is already included in the SnarkyJS repo. The Mina.LocalBlochain API is used to setup a local ledger to deploy the contract to and test it's logic. Theses tests and all future test application tests will be included in the SnarkyJS CI tests that are run here:

    https://github.com/o1-labs/snarkyjs/blob/b70511aa2f4dd9472f8b2e2b7edfa0486a699863/.github/workflows/build-action.yml#L24-L26

    opened by ymekuria 7
  • Make it possible to not have an init method, so the zkApp private key holder can't reset the state

    Make it possible to not have an init method, so the zkApp private key holder can't reset the state

    At the moment, all zkApps are given an init method, which the holder of the private key can call to reset state.

    It should be possible to create a zkApp where the private key holder can't reset the state.

    Would advocate for removing the init method, instead performing initialization inside deploy, to have fewer special functions for developers to learn how to use

    opened by es92 6
  • Expose `reduce` to ZkProgram, use for rolling up Merkle tree updates

    Expose `reduce` to ZkProgram, use for rolling up Merkle tree updates

    We should make it easier to roll up updates to a Merkle tree with actions /.reducer. Ideal (IMO) architecture is laid out here: https://github.com/o1-labs/snarkyjs/issues/659#issuecomment-1361320687

    opened by mitschabaude 0
  • Mina-signer: Legacy payment signatures

    Mina-signer: Legacy payment signatures

    closes #632

    • re-implements signing of payments, stake delegations and strings in JS
    • tested against reference signatures created with the OCaml mina-signer (test_signatures.js)
    • to support legacy signatures, we also implement Poseidon with legacy parameters
    • Legacy Poseidon is tested against test vectors created with the Rust tool
    opened by mitschabaude 0
  • RFC: Witness merging in reducer

    RFC: Witness merging in reducer

    Motivation

    The actions/reducer(link) paradigm looks very promising as a way to handle mutli-user, async state update events. However, it is currently not so obvious how to process these events for state which happens to be the root of a merkle tree. The ability to merge Witness objects would allow developers to reduce multiple Actions of the form { witness: Witness, leafValue: Field } into a new root hash which incorporates each separate action and does not require the full tree to be loaded in order to generate a new root.

    Current State

    Imagine a scenario in which we have a zkapp with some state which represents the root hash of a merkle tree. The merkle tree can store the current balance for each user in the zkapp. We have 3 users, Alice, Bob, and Charlie who each wish to deposit into the app.

    Standalone Transaction

    This can be accomplished without using a reducer, for instance:

    @state root = State<Field>();
    
    @method
    deposit(user: PublicKey, amount: Field, witness: Witness) {
    	// Simplified function calls
    	this.root.assertEquals(this.root.get());
    	this.root.assertEquals(witness.computeRoot(amount));
    	user.send(this.account, amount);
    	this.root.set(witness.computeRoot(amount));
    }
    

    The problem with this implementation is that it has a max throughput of one deposit per block (later transactions attempting to update the root will fail the second assertion. Their witness is now out of date). This is not acceptable for a variety of apps. In particular, imagine some kind of "mint" or auction event which goes live at a certain time with hundreds or thousands of users. I.e. ~20 transactions per hour might be appropriate in steady state for some apps, but there are certainly other apps with burst usage which far exceeds this rate.

    Actions/Reducer

    The best available way to solve our problem is to emit actions in the deposit method, then reduce them into one state update. This solves the problem of only allowing one transaction per block to succeed but it introduces a new problem.

    Naive Solution

    class MerkleUpdate extends Struct({ witness: Witness, newLeaf: Field }) {};
    
    -----------------------------------------------------
    
    @state root = State<Field>();
    @state actionsHash = State<Field>();
    reducer = Reducer({ actionType: MerkleUpdate})
    
    @method
    deposit(user: PublicKey, amount: Field, witness: Witness) {
    	// Simplified function calls
    	this.root.assertEquals(this.root.get());
    	this.root.assertEquals(witness.computeRoot(amount));
    	user.send(this.account, amount);
    	this.reducer.dispatch({ witness: witness, newLeaf: amount })
    }
    
    @method
    reduceMerkleUpdates() {
    	let root = this.root.get();
    	let actionsHash = this.actionsHash.get();
    	
    	let { state: newRoot, actionsHash: newActionsHash } = this.reducer.reduce(
    		this.reducer.getActions(actionsHash),
    		MerkleUpdate,
    		(state: Field, action: MerkleUpdate) => {
    			return action.witness.computeRoot(action.newLeaf);
    		}
    	);
    	
    	this.root.set(newRoot);
    	this.actionsHash.set(newActionsHash);
    }
    
    

    This seemingly solves the problem and would probably run, but there's an issue. This reduction does not fail when multiple users make an update, but the root hash that ends up on the chain will not reflect the sum of all three state changes. It will only reflect one of the state changes, whichever one happens to go last. That's becuase the Witness being used in action.witness.computeRoot(action.newLeaf); is the stale witness to the existing tree whose root is on chain, not to a tree which has already processed the other updates.

    It turns out that in order to process all of these updates at once, we'd have to build the full tree in javascript, make the updates on that tree, then compute the new root after all of the updates are done. This is a little weird since MerkleTree is not provable and can't be used as a parameter to the method. We would need the full tree to exist outside of the scope of the method. It also limits who is capable of running the reducer method to someone who has the enitre state stored.

    Proposal

    A more elegant, and scalable, solution would be to merge witnesses to the same tree into a single witness of a new tree.

    An API for this could look like:

    BaseMerkleWitness {
    	/*
    	 Observes the value of this witness with the value of leaf
    	 merges the paths of the other witness with the paths of this after observing the leaf
    	 @returns a new witness of the related path as other, but with hashes updated based on this' observation of leaf
    	*/
    	merge(leaf: Field, other: BaseMerkleWitness);
    }
    

    And usage in a reducer could look like

    let initialWitness = actions[0].witness;
    let leaf = actions[0].leaf;
    finalWitness = (state: Witness, action: MerkleUpdate) => {
    	newWitness = action.witness.merge(leaf, action.witness);
    	leaf = action.newLeaf;
    	return newWitness;
    }
    
    this.root.set(finalWitness.computeRoot(leaf));
    

    The algorithm I am imagining looks like:

    • Confirm that witnesses are the same heght and not identical
      • If pass then these witnesses are mergable
    • Identify the height within the path at which the witnesses differ (h)
      • example:
      • tree with height of 4, leaf count is 8
      • 1 and 5 are on different halves of the tree, so they differ at level 3
      • 7 and 8 are on the same half of the same half of the tree, so they differ at level 1
      • 6 and 8 would be 2
      • I believe this idea could be generalized
    • calculate the hash of witness1 at the leaf up to h (hash)
    • create a new witness with the same path at witness2 (w3)
    • set the path of w3 at index h to hash
    • returrn w3
    opened by 45930 17
  • All fetch errors are interpreted as

    All fetch errors are interpreted as "Could not find account"

    TODO: Better discriminate between different kinds of errors in places like Mina.getAccount. Example:

    • If the endpoint doesn't respond, that should always result in an error
    • A missing, on the other hand, account is sometimes an error (e.g. in getAccount) and sometimes just information that this account doesn't exist yet (in hasAccount)
    opened by mitschabaude 0
Owner
null
Hemsida för personer i Sverige som kan och vill erbjuda boende till människor på flykt

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

null 4 May 3, 2022
Kurs-repo för kursen Webbserver och Databaser

Webbserver och databaser This repository is meant for CME students to access exercises and codealongs that happen throughout the course. I hope you wi

null 14 Jan 3, 2023
Convert some JavaScript/TypeScript code string into a .d.ts TypeScript Declaration code string

convert-to-dts Converts the source code for any .js or .ts file into the equivalent .d.ts code TypeScript would generate. Usage import { convertToDecl

Lily Scott 11 Mar 3, 2022
A Serverless GraphQL Sample project using Apollo and Serverless Framework with TypeScript and Webpack.

Serverless GraphQL Boilerplate This is a base project with a structure that includes Serverless Framework, Apollo, TypeScript and Webpack. It can be d

Ravi Souza 5 Aug 23, 2022
Express.js framework boilerplate with TypeScript, Prisma, ESLint, Husky and Jest

Setup a Node.js project with Typescript, Prisma ESLint, Prettier, Husky Node.js boilerplate with Express.js, TypeScript, Prisma, ESLint, Prettier, Hus

Smart Geek 6 Dec 12, 2022
🚀 Using top-level await in AWS Lambda with TypeScript, esbuild and Serverless Framework

?? Top-level await in AWS Lamba with TypeScript Articles https://dev.to/oieduardorabelo/top-level-await-in-aws-lamba-with-typescript-1bf0 https://medi

Eduardo Rabelo 17 Nov 23, 2022
Fast, Bun-powered, and Bun-only(for now) Web API framework with full Typescript support.

Zarf Fast, Bun-powered, and Bun-only(for now) Web API framework with full Typescript support. Quickstart Starting with Zarf is as simple as instantiat

Zarf Framework 65 Dec 28, 2022
⚡️ Next-generation data transformation framework for TypeScript that puts developer experience first

TypeStream Next-generation data transformation framework for TypeScript that puts developer experience first Nowadays, almost every developer is worki

Scopas Technologies 53 Nov 26, 2022
A template for a vanilla(no ui-framework) project with webgi engine in typescript using parcel bundler.

WebGi starter project A template for a vanilla(no ui-framework) project with webgi engine in typescript using parcel bundler. For the latest version a

null 40 Jan 3, 2023
Serverless Framework + typescript + mongoDB atlas, sample

sls_ts6 Version: 0.9.1 Author : Kouji Nakashima / kuc-arc-f.com date : 2022/01/21 update : Summary Serverless Framework + typescript + mongoDB atlas,

Kouji Nakashima 1 Jan 22, 2022
📬 Lightweight Typescript-first framework built on top of Express

?? abstain Lightweight Typescript-first framework built on top of Express [WIP] ?? api // index.ts import { Application } from '@pinkcig/abstain'; imp

Faye Keller 5 May 26, 2022
Ergonomic, chaining-based Typescript framework for quick API development for Fastify

Ergonomic, chaining-based Typescript framework for quick API development for Fastify Installation > pnpm add @t3ned/channel Usage // index.ts import

T3NED 6 Aug 2, 2022
🖼 Simple file-upload utility that shows a preview of the uploaded image. Written in TypeScript. No dependencies. Works well with or without a framework.

file-upload-with-preview ?? Simple file-upload utility that shows a preview of the uploaded image. Written in TypeScript. No dependencies. Works well

John Datserakis 427 Dec 26, 2022
⚡️ CLI building blocks & framework for the TypeScript era.

molt A set of packages related to building CLIs. Alpha maturity. Each package has its own docs. ?? Package Path Description Use Case ?? molt packages/

Jason Kuhrt 44 Jan 6, 2023
TypeScript framework for deploying distributed indexers on Aleph VMs for Solana.

Aleph Indexer Framework v0.1 The Aleph Indexer Framework is a high-level abstraction for building multithreaded indexers on Aleph. It is designed to b

Aleph.im 11 Dec 15, 2022
:books: The definitive guide to TypeScript and possibly the best TypeScript book :book:. Free and Open Source 🌹

TypeScript Deep Dive I've been looking at the issues that turn up commonly when people start using TypeScript. This is based on the lessons from Stack

Basarat Ali Syed 18.7k Jan 4, 2023
This project is a boilerplate for Next and TypeScript projects. This template was built with Vite, TypeScript and Stitches.

Awesome Template Stitches — NextJS, TypeScript, Stitches and Design Tokens Summary About this template Avaliale scripts Other scripts available Main t

Diego Silva 14 Dec 29, 2022
Movehat is a TypeScript SDK for Move on Sui built on top of Sui's TypeScript SDK and our fork of Ian Macalinao's `move-ts`.

Movehat Movehat is a TypeScript SDK for Move on Sui built on top of Sui's TypeScript SDK and our fork of Ian Macalinao's move-ts. Movehat aspires to b

Pentagon 10 Sep 30, 2022
A CLI tool to create a NodeJS project with TypeScript CTSP is a CLI tool to make easier to start a new NodeJS project and configure Typescript on it.

CTSP- Create TS Project A CLI tool to create a NodeJS project with TypeScript CTSP is a CLI tool to make easier to start a new NodeJS project and conf

Jean Rodríguez 7 Sep 13, 2022