Skip to content

Commit

Permalink
feat: verifiably derive public key from private input
Browse files Browse the repository at this point in the history
  • Loading branch information
iluxonchik committed Mar 6, 2024
1 parent 419e00d commit 7fcff45
Showing 1 changed file with 107 additions and 121 deletions.
228 changes: 107 additions & 121 deletions contracts/src/blockchain/contracts/bounty/BountyBulletinBoardContract.ts
Original file line number Diff line number Diff line change
@@ -1,124 +1,110 @@
import {
SmartContract,
method,
DeployArgs,
Permissions,
UInt64,
PublicKey,
Signature,
MerkleWitness,
state,
Field,
State,
MerkleMapWitness,
Poseidon,
Scalar,
PrivateKey,
Provable,
} from 'o1js';

/*
* Merkle Tree witness with a depth of 5. This number is beint iterated upon.
*/
class MerkleWitness5 extends MerkleWitness(5) {}

/*
"Witness for the key" - a witness for the key/value pair in the Merkle Map. The witness contains all of the
necessary intermediate node values in the tree up to the tree's root, in order to compute a root
for the tree for any "value" for the "key".
*/

/**
* Bounty Bulletin Board is the smart contract that exposes the "Fund Bounty" and "Claim Bounty" interfaces.
*
* In the "Fund Bounty" phase, a bounty with a particular ID is funded. In this process, the $ZKL amount
* associated with the bounty represented by (funder_addres, bounty_id) is locked up in the contract.
*
* In the "Claim Bounty" phase, a bounty with a particular ID is claimed. In this process, the the $ZKL amount
* associated with the (funder_addres, bounty_id) key is transferred to the claimer_address.
*
* This smart contract is an iterative development of the Bounty Bulletin Board standard, which will be materialized
* in a set of hierarchical interfaces and implementaions.
*/
export class BountyBulletinBoardContract extends SmartContract {
// Root of the Merkle Map containing the bounty funding information.
@state(PublicKey) bountyMapRoot = State<PublicKey>();

// @method initState(intialBountyMapRoot: Field) {
// this.bountyMapRoot.set(intialBountyMapRoot);
// }
SmartContract,
method,
DeployArgs,
Permissions,
UInt64,
PublicKey,
MerkleWitness,
state,
Field,
State,
Poseidon,
Provable,
Group,
} from 'o1js';
import { ZKLContract } from '../tokens/zkl/ZKLContract';

/*
Merkle Witness Mental Model Glossary:
"Witness for the key" - a witness for the key/value pair in the Merkle Map. The witness contains all of the
necessary intermediate node values in the tree up to the tree's root, in order to compute a root
for the tree for any "value" for the "key".
*/


type GroupHash = {
x: Field;
y: {
x0: Field;
x1: Field;
};
}



/**
* Bounty Bulletin Board is the smart contract that exposes the "Fund Bounty" and "Claim Bounty" interfaces.
*
* In the "Fund Bounty" phase, a bounty with a particular ID is funded. In this process, the $ZKL amount
* associated with the bounty represented by (funder_addres, bounty_id) is locked up in the contract.
*
* In the "Claim Bounty" phase, a bounty with a particular ID is claimed. In this process, the the $ZKL amount
* associated with the (funder_addres, bounty_id) key is transferred to the claimer_address.
*
* This smart contract is an iterative development of the Bounty Bulletin Board standard, which will be materialized
* in a set of hierarchical interfaces and implementaions.
*/
export class BountyBulletinBoardContract extends SmartContract {
// Root of the Merkle Map containing the bounty funding information.
@state(PublicKey) bountyMapRoot = State<PublicKey>();

// @method initState(intialBountyMapRoot: Field) {
// this.bountyMapRoot.set(intialBountyMapRoot);
// }


@method fundBounty(
funderAddress: PublicKey,
bountyId: UInt64, // the size of the tree will be fixed, but a self-replication mechanism will be implemented
zklAmountIncrement: UInt64,
//funderSignature: Signature // checking whether this is required, and what will it contain
) {

// 1. Derive the a PublicKey, which will represent the address of the account
// which contains the bounty $ZKL amount

// //@method
// fundBounty(
// funderAddress: PublicKey,
// bountyId: UInt64, // the size of the tree will be fixed, but a self-replication mechanism will be implemented
// zklAmountIncrement: UInt64,
// keyWitness: MerkleMapWitness, // witness to the (funderAddress, bountyId) key in the Merkle Map
// zklAmountBefore: UInt64, // the amount of $ZKL in the funder's account before the funding
// //funderSignature: Signature // checking whether this is required, and what will it contain
// ) {
// // 0. Verify Merkle Map root state
// const initialBountyMapRoot: Field = this.bountyMapRoot.get();
// this.bountyMapRoot.requireEquals(initialBountyMapRoot);

// // 1/2. Convert amounts data into Field
// const claimedZKLAmountBeforeField: Field = Poseidon.hash(zklAmountBefore.toFields());

// // 1. Verify that current $ZKL amount in the funder's account is equal to zkAmountBefore
// // 2. Transfer the zklAmount from the sender's account to the contract's account
// // 3. Increment the amount by zklAmount

// // Here, we will verify the witness and update the value for the key
// const actualKey: Field = Poseidon.hash([...funderAddress.toFields(), ...bountyId.toFields()]);

// const [claimedInitialBountyMapRoot, claimedKey] = keyWitness.computeRootAndKey(claimedZKLAmountBeforeField);
// claimedInitialBountyMapRoot.assertEquals(initialBountyMapRoot, "Claimed root is not the actual root");
// claimedKey.assertEquals(actualKey, "Claimed key is not the actual key");

// // Compute root after incremeneting the value
// const newZKLAmount: UInt64 = zklAmountBefore.add(zklAmountIncrement);
// const newZKLAmountField: Field = Poseidon.hash(newZKLAmount.toFields());

// const [newBountyMapRoot, _] = keyWitness.computeRootAndKey(newZKLAmountField);

// // 4. Update the Merkle Map root with zkAmountBefore + zklAmount
// this.bountyMapRoot.set(newBountyMapRoot);
//}


@method fundBounty2(
funderAddress: PublicKey,
bountyId: UInt64, // the size of the tree will be fixed, but a self-replication mechanism will be implemented
zklAmountIncrement: UInt64,
//funderSignature: Signature // checking whether this is required, and what will it contain
) {

// 1. Derive the a PublicKey, which will represent
// the address of the account which contains the bounty $ZKL amount

const publicKeyFields: Field[] = funderAddress.toFields();
const newFirstElement: Field = Poseidon.hash([...funderAddress.toFields(), ...bountyId.toFields()]);
publicKeyFields[publicKeyFields.length - 1] = Poseidon.hash(bountyId.toFields());

const bountyPublicKey: PublicKey = PublicKey.fromFields([newFirstElement, publicKeyFields[1]]);

this.bountyMapRoot.set(bountyPublicKey);

Provable.asProver(() => {
Provable.log(bountyPublicKey.toFields());
Provable.log(bountyPublicKey.toBase58())
});
}

deploy(args: DeployArgs) {
super.deploy(args);
this.account.permissions.set({
...Permissions.default(),
editState: Permissions.proofOrSignature(),
});
}

@method init() {
super.init();
}
// https://github.com/o1-labs/o1js/blob/0ec1c9da8c714298e6088ffa88178abb3961d200/src/lib/nullifier.ts#L64
const newGroupHash: GroupHash = Poseidon.hashToGroup([...funderAddress.toFields(), ...bountyId.toFields()]);
const group: Group = new Group({x: newGroupHash.x, y: newGroupHash.y.x0});
const bountyPublicKey: PublicKey = PublicKey.fromGroup(group);

this.bountyMapRoot.set(bountyPublicKey);

// Temporary log statements
Provable.asProver(() => {
Provable.log(bountyPublicKey.toFields());
Provable.log(bountyPublicKey.toBase58())
});
}

@method mintFromZKL(
zklAddress: PublicKey,
amount: UInt64,
) {
const zklContract: ZKLContract = new ZKLContract(zklAddress);
zklContract.sendTo(this.address, amount);

// Mint custom token of the same amount
}

@method burnToZKL(
amount: UInt64,
) {
// Burn an amount of BBB_ZKL to ZKL

}

deploy(args: DeployArgs) {
super.deploy(args);
this.account.permissions.set({
...Permissions.default(),
editState: Permissions.proofOrSignature(),
});
}

@method init() {
super.init();
}
}

0 comments on commit 7fcff45

Please sign in to comment.