Initial commit: add .gitignore and README

This commit is contained in:
defiQUG
2026-02-09 21:51:48 -08:00
commit d4ba3d45e5
174 changed files with 32756 additions and 0 deletions

View File

@@ -0,0 +1,130 @@
const Web3 = require("web3");
const Web3Quorum = require("web3js-quorum");
const Tx = require("ethereumjs-tx");
const PromisePool = require("async-promise-pool");
const { tessera, besu } = require("../keys.js");
const chainId = 1337;
const web3 = new Web3Quorum(new Web3(besu.member1.url), chainId);
/*
Transactions are sent in batches.
TX_COUNT defines the total of transactions
BATCH_SIZE defines how many transactions will be sent at once
*/
const TX_COUNT = 100;
const BATCH_SIZE = 5;
// options used to create a privacy group with only one member
const privacyOptions = {
privateFrom: tessera.member1.publicKey,
privateFor: [tessera.member1.publicKey],
privateKey: besu.member1.accountPrivateKey,
};
const deployContractData =
"0x608060405234801561001057600080fd5b5060405161018e38038061018e8339818101604052602081101561003357600080fd5b8101908080519060200190929190505050806000819055507f85bea11d86cefb165374e0f727bacf21dc2f4ea816493981ecf72dcfb212a410816040518082815260200191505060405180910390a15060fd806100916000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c806360fe47b11460375780636d4ce63c146062575b600080fd5b606060048036036020811015604b57600080fd5b8101908080359060200190929190505050607e565b005b606860bf565b6040518082815260200191505060405180910390f35b806000819055507f85bea11d86cefb165374e0f727bacf21dc2f4ea816493981ecf72dcfb212a410816040518082815260200191505060405180910390a150565b6000805490509056fea265627a7a723158207735a32daa767059dd230ee7718eb7f09ff35ca8ba54249b53ea1c2e12b98f8564736f6c634300051100320000000000000000000000000000000000000000000000000000000000000001";
// get nonce of account in the privacy group
function getPrivateNonce(account) {
return web3.priv.getTransactionCount(
account,
web3.utils.generatePrivacyGroup(privacyOptions)
);
}
// get public nonce of account
function getPublicNonce(account) {
return web3.eth.getTransactionCount(account, "pending");
}
// distribute payload to participants
function distributePayload(payload, nonce) {
return web3.priv.generateAndDistributeRawTransaction({
...privacyOptions,
data: payload,
nonce,
});
}
// create and sign PMT
function sendPMT(sender, enclaveKey, nonce) {
const rawTx = {
nonce: web3.utils.numberToHex(nonce), // PMT nonce
from: sender,
to: "0x000000000000000000000000000000000000007e", // privacy precompile address
data: enclaveKey,
gasLimit: "0x5a88",
};
const tx = new Tx(rawTx);
tx.sign(Buffer.from(besu.member1.accountPrivateKey, "hex"));
const hexTx = `0x${tx.serialize().toString("hex")}`;
// eslint-disable-next-line promise/avoid-new
return new Promise((resolve, reject) => {
web3.eth
.sendSignedTransaction(hexTx)
.once("receipt", (rcpt) => {
resolve(rcpt);
})
.on("error", (error) => {
reject(error);
});
});
}
function printPrivTxDetails(pmtRcpt) {
return web3.priv
.waitForTransactionReceipt(pmtRcpt.transactionHash)
.then((privTxRcpt) => {
console.log(
`=== Private TX ${privTxRcpt.transactionHash}\n` +
` > Status ${privTxRcpt.status}\n` +
` > Block #${pmtRcpt.blockNumber}\n` +
` > PMT Index #${pmtRcpt.transactionIndex}\n` +
` > PMT Hash ${pmtRcpt.transactionHash}\n`
);
return Promise.resolve();
});
}
/*
Example of sending private transactions in batch.
The basic steps are:
1. Find the expected public and private nonce for the sender account
2. Ditribute the private transaction (incrementing the private nonce)
3. Create a PMT for each private transaction (incrementing the public nonce)
*/
module.exports = async () => {
const sender = "0xfe3b557e8fb62b89f4916b721be55ceb828dbd73";
const privateNonce = await getPrivateNonce(sender);
const publicNonce = await getPublicNonce(sender);
const pool = new PromisePool({ concurrency: BATCH_SIZE });
for (let i = 0; i < TX_COUNT; i += 1) {
pool.add(() => {
return distributePayload(deployContractData, privateNonce + i)
.then((enclaveKey) => {
return sendPMT(sender, enclaveKey, publicNonce + i);
})
.then(printPrivTxDetails);
});
}
await pool.all();
};
if (require.main === module) {
module.exports().catch((error) => {
console.log(error);
console.log(
"\nThis example requires ONCHAIN privacy to be DISABLED. \nCheck config for ONCHAIN privacy groups."
);
});
}

View File

@@ -0,0 +1,200 @@
const path = require("path");
const fs = require("fs-extra");
const Web3 = require("web3");
const Web3Quorum = require("web3js-quorum");
// WARNING: the keys here are demo purposes ONLY. Please use a tool like EthSigner for production, rather than hard coding private keys
const { tessera, besu } = require("../keys.js");
const chainId = 1337;
// abi and bytecode generated from simplestorage.sol:
// > solcjs --bin --abi simplestorage.sol
const contractJsonPath = path.resolve(
__dirname,
"../../",
"contracts",
"SimpleStorage.json"
);
const contractJson = JSON.parse(fs.readFileSync(contractJsonPath));
const contractBytecode = contractJson.evm.bytecode.object;
const contractAbi = contractJson.abi;
// Besu doesn't support eth_sendTransaction so we use the eea_sendRawTransaction(https://besu.hyperledger.org/en/latest/Reference/API-Methods/#eea_sendrawtransaction) for things like simple value transfers, contract creation or contract invocation
async function createContract(
clientUrl,
fromPrivateKey,
fromPublicKey,
toPublicKey
) {
const web3 = new Web3(clientUrl);
const web3quorum = new Web3Quorum(web3, chainId);
// initialize the default constructor with a value `47 = 0x2F`; this value is appended to the bytecode
const contractConstructorInit = web3.eth.abi
.encodeParameter("uint256", "47")
.slice(2);
const txOptions = {
data: "0x" + contractBytecode + contractConstructorInit,
privateKey: fromPrivateKey,
privateFrom: fromPublicKey,
privateFor: [toPublicKey],
};
console.log("Creating contract...");
// Generate and send the Raw transaction to the Besu node using the eea_sendRawTransaction(https://besu.hyperledger.org/en/latest/Reference/API-Methods/#eea_sendrawtransaction) JSON-RPC call
const txHash = await web3quorum.priv.generateAndSendRawTransaction(txOptions);
console.log("Getting contractAddress from txHash: ", txHash);
const privateTxReceipt = await web3quorum.priv.waitForTransactionReceipt(
txHash
);
console.log("Private Transaction Receipt: ", privateTxReceipt);
return privateTxReceipt;
}
async function getValueAtAddress(
clientUrl,
nodeName = "node",
address,
contractAbi,
fromPrivateKey,
fromPublicKey,
toPublicKey
) {
const web3 = new Web3(clientUrl);
const web3quorum = new Web3Quorum(web3, chainId);
const contract = new web3quorum.eth.Contract(contractAbi);
// eslint-disable-next-line no-underscore-dangle
const functionAbi = contract._jsonInterface.find((e) => {
return e.name === "get";
});
const functionParams = {
to: address,
data: functionAbi.signature,
privateKey: fromPrivateKey,
privateFrom: fromPublicKey,
privateFor: [toPublicKey],
};
const transactionHash = await web3quorum.priv.generateAndSendRawTransaction(
functionParams
);
// console.log(`Transaction hash: ${transactionHash}`);
const result = await web3quorum.priv.waitForTransactionReceipt(
transactionHash
);
console.log(
"" + nodeName + " value from deployed contract is: " + result.output
);
return result;
}
async function setValueAtAddress(
clientUrl,
address,
value,
contractAbi,
fromPrivateKey,
fromPublicKey,
toPublicKey
) {
const web3 = new Web3(clientUrl);
const web3quorum = new Web3Quorum(web3, chainId);
const contract = new web3quorum.eth.Contract(contractAbi);
// eslint-disable-next-line no-underscore-dangle
const functionAbi = contract._jsonInterface.find((e) => {
return e.name === "set";
});
const functionArgs = web3quorum.eth.abi
.encodeParameters(functionAbi.inputs, [value])
.slice(2);
const functionParams = {
to: address,
data: functionAbi.signature + functionArgs,
privateKey: fromPrivateKey,
privateFrom: fromPublicKey,
privateFor: [toPublicKey],
};
const transactionHash = await web3quorum.priv.generateAndSendRawTransaction(
functionParams
);
console.log(`Transaction hash: ${transactionHash}`);
const result = await web3quorum.priv.waitForTransactionReceipt(
transactionHash
);
return result;
}
async function main() {
createContract(
besu.member1.url,
besu.member1.accountPrivateKey,
tessera.member1.publicKey,
tessera.member3.publicKey
)
.then(async function (privateTxReceipt) {
console.log("Address of transaction: ", privateTxReceipt.contractAddress);
let newValue = 123;
//wait for the blocks to propogate to the other nodes
await new Promise((r) => setTimeout(r, 20000));
console.log(
"Use the smart contracts 'get' function to read the contract's constructor initialized value .. "
);
await getValueAtAddress(
besu.member1.url,
"Member1",
privateTxReceipt.contractAddress,
contractAbi,
besu.member1.accountPrivateKey,
tessera.member1.publicKey,
tessera.member3.publicKey
);
console.log(
`Use the smart contracts 'set' function to update that value to ${newValue} .. - from member1 to member3`
);
await setValueAtAddress(
besu.member1.url,
privateTxReceipt.contractAddress,
newValue,
contractAbi,
besu.member1.accountPrivateKey,
tessera.member1.publicKey,
tessera.member3.publicKey
);
//wait for the blocks to propogate to the other nodes
await new Promise((r) => setTimeout(r, 20000));
console.log(
"Verify the private transaction is private by reading the value from all three members .. "
);
await getValueAtAddress(
besu.member1.url,
"Member1",
privateTxReceipt.contractAddress,
contractAbi,
besu.member1.accountPrivateKey,
tessera.member1.publicKey,
tessera.member3.publicKey
);
await getValueAtAddress(
besu.member2.url,
"Member2",
privateTxReceipt.contractAddress,
contractAbi,
besu.member2.accountPrivateKey,
tessera.member2.publicKey,
tessera.member1.publicKey
);
await getValueAtAddress(
besu.member3.url,
"Member3",
privateTxReceipt.contractAddress,
contractAbi,
besu.member3.accountPrivateKey,
tessera.member3.publicKey,
tessera.member1.publicKey
);
})
.catch(console.error);
}
if (require.main === module) {
main();
}
module.exports = exports = main;

View File

@@ -0,0 +1,232 @@
const path = require("path");
const fs = require("fs-extra");
const Web3 = require("web3");
const Web3Quorum = require("web3js-quorum");
// WARNING: the keys here are demo purposes ONLY. Please use a tool like EthSigner for production, rather than hard coding private keys
const { tessera, besu } = require("../keys.js");
const chainId = 1337;
// abi and bytecode generated from simplestorage.sol:
// > solcjs --bin --abi simplestorage.sol
const contractJsonPath = path.resolve(
__dirname,
"../../",
"contracts",
"SimpleStorage.json"
);
const contractJson = JSON.parse(fs.readFileSync(contractJsonPath));
const contractBytecode = contractJson.evm.bytecode.object;
const contractAbi = contractJson.abi;
// initialize the default constructor with a value `47 = 0x2F`; this value is appended to the bytecode
const contractConstructorInit =
"000000000000000000000000000000000000000000000000000000000000002F";
async function createPrivacyGroup(clientUrl, participantList) {
const web3 = new Web3(clientUrl);
const web3quorum = new Web3Quorum(web3, chainId);
const contractOptions = {
addresses: participantList,
name: "web3js-quorum",
description: "quickstart",
};
const result = await web3.priv.createPrivacyGroup(contractOptions);
console.log(
"Privacy group: " +
result +
" created between participants: " +
participantList
);
return result;
}
// Besu doesn't support eth_sendTransaction so we use the eea_sendRawTransaction for things like simple value transfers, contract creation or contract invocation
async function createContract(
clientUrl,
privacyGroupId,
fromPrivateKey,
fromPublicKey
) {
const web3 = new Web3(clientUrl);
const web3quorum = new Web3Quorum(web3, chainId);
const txOptions = {
data: "0x" + contractBytecode + contractConstructorInit,
privateKey: fromPrivateKey,
privateFrom: fromPublicKey,
privacyGroupId: privacyGroupId,
};
console.log("Creating contract...");
// Generate and send the Raw transaction to the Besu node using the eea_sendRawTransaction JSON-RPC call
const txHash = await web3quorum.priv.generateAndSendRawTransaction(txOptions);
console.log("Getting contractAddress from txHash: ", txHash);
const privateTxReceipt = await web3quorum.priv.waitForTransactionReceipt(
txHash
);
console.log("Private Transaction Receipt: ", privateTxReceipt);
return privateTxReceipt;
}
async function getValueAtAddress(
clientUrl,
nodeName = "node",
address,
contractAbi,
fromPrivateKey,
fromPublicKey,
privacyGroupId
) {
const web3 = new Web3(clientUrl);
const web3quorum = new Web3Quorum(web3, chainId);
const contract = new web3quorum.eth.Contract(contractAbi);
// eslint-disable-next-line no-underscore-dangle
const functionAbi = contract._jsonInterface.find((e) => {
return e.name === "get";
});
const functionParams = {
to: address,
data: functionAbi.signature,
privateKey: fromPrivateKey,
privateFrom: fromPublicKey,
privacyGroupId: privacyGroupId,
};
const transactionHash = await web3quorum.priv.generateAndSendRawTransaction(
functionParams
);
// console.log(`Transaction hash: ${transactionHash}`);
const result = await web3quorum.priv.waitForTransactionReceipt(
transactionHash
);
console.log(
"" + nodeName + " value from deployed contract is: " + result.output
);
return result;
}
async function setValueAtAddress(
clientUrl,
address,
value,
contractAbi,
fromPrivateKey,
fromPublicKey,
privacyGroupId
) {
const web3 = new Web3(clientUrl);
const web3quorum = new Web3Quorum(web3, chainId);
const contract = new web3quorum.eth.Contract(contractAbi);
// eslint-disable-next-line no-underscore-dangle
const functionAbi = contract._jsonInterface.find((e) => {
return e.name === "set";
});
const functionArgs = web3quorum.eth.abi
.encodeParameters(functionAbi.inputs, [value])
.slice(2);
const functionParams = {
to: address,
data: functionAbi.signature + functionArgs,
privateKey: fromPrivateKey,
privateFrom: fromPublicKey,
privacyGroupId,
};
const transactionHash = await web3quorum.priv.generateAndSendRawTransaction(
functionParams
);
console.log(`Transaction hash: ${transactionHash}`);
const result = await web3quorum.priv.waitForTransactionReceipt(
transactionHash
);
return result;
}
async function main() {
const participantList = [
tessera.member1.publicKey,
tessera.member3.publicKey,
];
const privacyGroupId = await createPrivacyGroup(
besu.member1.url,
participantList
);
createContract(
besu.member1.url,
privacyGroupId,
besu.member1.accountPrivateKey,
tessera.member1.publicKey,
tessera.member3.publicKey
)
.then(async function (privateTxReceipt) {
console.log("Address of transaction: ", privateTxReceipt.contractAddress);
let newValue = 123;
//wait for the blocks to propogate to the other nodes
await new Promise((r) => setTimeout(r, 10000));
console.log(
"Use the smart contracts 'get' function to read the contract's constructor initialized value .. "
);
await getValueAtAddress(
besu.member1.url,
"Member1",
privateTxReceipt.contractAddress,
contractAbi,
besu.member1.accountPrivateKey,
tessera.member1.publicKey,
privacyGroupId
);
console.log(
`Use the smart contracts 'set' function to update that value to ${newValue} .. - from member1 to member3`
);
await setValueAtAddress(
besu.member1.url,
privateTxReceipt.contractAddress,
newValue,
contractAbi,
besu.member1.accountPrivateKey,
tessera.member1.publicKey,
privacyGroupId
);
//wait for the blocks to propogate to the other nodes
await new Promise((r) => setTimeout(r, 10000));
console.log(
"Verify the private transaction is private by reading the value from all three members .. "
);
await getValueAtAddress(
besu.member1.url,
"Member1",
privateTxReceipt.contractAddress,
contractAbi,
besu.member1.accountPrivateKey,
tessera.member1.publicKey,
privacyGroupId
).catch(() => {
console.log("Member1 cannot obtain value");
});
await getValueAtAddress(
besu.member2.url,
"Member2",
privateTxReceipt.contractAddress,
contractAbi,
besu.member2.accountPrivateKey,
tessera.member2.publicKey,
privacyGroupId
).catch(() => {
console.log("Member2 cannot obtain value");
});
await getValueAtAddress(
besu.member3.url,
"Member3",
privateTxReceipt.contractAddress,
contractAbi,
besu.member3.accountPrivateKey,
tessera.member3.publicKey,
privacyGroupId
).catch(() => {
console.log("Member3 cannot obtain value");
});
})
.catch(console.error);
}
if (require.main === module) {
main();
}
module.exports = exports = main;