Skip to content
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

transactionHashesRoot calculated in proposeBlock without challenge #639

Merged
merged 2 commits into from
May 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 34 additions & 14 deletions common-files/classes/transaction.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,51 @@
/**
An optimistic Transaction class
*/
import config from 'config';
import gen from 'general-number';
import Web3 from '../utils/web3.mjs';
import { compressProof } from '../utils/curve-maths/curves.mjs';

const { generalise } = gen;

const TOKEN_TYPES = { ERC20: 0, ERC721: 1, ERC1155: 2 };
const { TRANSACTION_TYPES } = config;

// function to compute the keccak hash of a transaction
function keccak(preimage) {
const web3 = Web3.connection();
// compute the solidity hash, using suitable type conversions
return web3.utils.soliditySha3(
{ t: 'uint112', v: preimage.value },
...preimage.historicRootBlockNumberL2.map(hi => ({ t: 'uint256', v: hi })),
{ t: 'uint8', v: preimage.transactionType },
{ t: 'uint8', v: preimage.tokenType },
{ t: 'bytes32', v: preimage.tokenId },
{ t: 'bytes32', v: preimage.ercAddress },
{ t: 'bytes32', v: preimage.recipientAddress },
...preimage.commitments.map(ch => ({ t: 'bytes32', v: ch })),
...preimage.nullifiers.map(nh => ({ t: 'bytes32', v: nh })),
...preimage.compressedSecrets.map(es => ({ t: 'bytes32', v: es })),
...compressProof(preimage.proof).map(p => ({ t: 'uint', v: p })),
);
const {
value,
historicRootBlockNumberL2,
transactionType,
tokenType,
tokenId,
ercAddress,
recipientAddress,
commitments,
nullifiers,
compressedSecrets,
} = preimage;
let { proof } = preimage;
proof = compressProof(proof);
const transaction = [
value,
historicRootBlockNumberL2,
transactionType,
tokenType,
tokenId,
ercAddress,
recipientAddress,
commitments,
nullifiers,
compressedSecrets,
proof,
];
const encodedTransaction = web3.eth.abi.encodeParameters([TRANSACTION_TYPES], [transaction]);
return web3.utils.soliditySha3({
t: 'bytes',
v: encodedTransaction,
});
}

class Transaction {
Expand Down
3 changes: 3 additions & 0 deletions config/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ module.exports = {
BN128_GROUP_ORDER: 21888242871839275222246405745257275088548364400416034343698204186575808495617n,
BN128_PRIME_FIELD: 21888242871839275222246405745257275088696311157297823662689037894645226208583n,
TRANSACTIONS_PER_BLOCK: Number(process.env.TRANSACTIONS_PER_BLOCK) || 2,
BLOCK_TYPES: '(uint48,address,bytes32,uint256,bytes32,bytes32)',
IlyasRidhuan marked this conversation as resolved.
Show resolved Hide resolved
TRANSACTION_TYPES:
'(uint112,uint64[2],uint8,uint8,bytes32,bytes32,bytes32,bytes32[2],bytes32[2],bytes32[8],uint[4])',
PROPOSE_BLOCK_TYPES: [
'(uint48,address,bytes32,uint256,bytes32,bytes32)',
'(uint112,uint64[2],uint8,uint8,bytes32,bytes32,bytes32,bytes32[2],bytes32[2],bytes32[8],uint[4])[]',
Expand Down
2 changes: 0 additions & 2 deletions nightfall-client/src/event-handlers/block-proposed.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import {
saveTransaction,
saveBlock,
setTransactionHashSiblingInfo,
setTransactionsHashForBlock,
} from '../services/database.mjs';
import { decryptCommitment } from '../services/commitment-sync.mjs';

Expand Down Expand Up @@ -114,7 +113,6 @@ async function blockProposedEventHandler(data) {
if (await isTransactionHashWithdraw(transactionHash)) {
const siblingPathTransactionHash =
updatedTransctionHashesTimber.getSiblingPath(transactionHash);
await setTransactionsHashForBlock(transactionHash, block.transactionsHash);
return setTransactionHashSiblingInfo(
transactionHash,
siblingPathTransactionHash,
Expand Down
11 changes: 0 additions & 11 deletions nightfall-client/src/services/database.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -266,17 +266,6 @@ export async function getTransactionByTransactionHash(transactionHash) {
return db.collection(TRANSACTIONS_COLLECTION).findOne(query);
}

// function to set the path of the transaction hash leaf in transaction hash timber
export async function setTransactionsHashForBlock(transactionHash, transactionsHash) {
const connection = await mongo.connection(MONGO_URL);
const query = { transactionHash };
const update = {
$set: { transactionsHash },
};
const db = connection.db(COMMITMENTS_DB);
return db.collection(TRANSACTIONS_COLLECTION).updateMany(query, update);
}

// function to set the path of the transaction hash leaf in transaction hash timber
export async function setTransactionHashSiblingInfo(
transactionHash,
Expand Down
1 change: 0 additions & 1 deletion nightfall-client/src/services/finalise-withdrawal.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ export async function finaliseWithdrawal(transactionHash) {
const rawTransaction = await shieldContractInstance.methods
.finaliseWithdrawal(
buildSolidityStruct(block),
block.transactionsHash,
Transaction.buildSolidityStruct(transactions[index]),
index,
siblingPath,
Expand Down
1 change: 0 additions & 1 deletion nightfall-client/src/services/instant-withdrawal.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ const setInstantWithdrawl = async ({ transactionHash }) => {
const rawTransaction = await shieldContractInstance.methods
.setAdvanceWithdrawalFee(
buildSolidityStruct(block),
block.transactionsHash,
Transaction.buildSolidityStruct(transactions[index]),
index,
siblingPath,
Expand Down
6 changes: 0 additions & 6 deletions nightfall-client/src/services/process-calldata.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,6 @@ async function getProposeBlockCalldata(eventData) {
});

block.transactionHashes = transactions.map(t => t.transactionHash);
const encodedTransactions = `0x${tx.input.slice(394)}`; // retrieve only transactions data
block.transactionsHash = web3.utils.soliditySha3({
t: 'bytes',
v: encodedTransactions,
});

return { transactions, block };
}

Expand Down
1 change: 0 additions & 1 deletion nightfall-client/src/services/valid-withdrawal.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ export async function isValidWithdrawal({ block, transaction, index, siblingPath
const valid = await shieldContractInstance.methods
.isValidWithdrawal(
buildSolidityStruct(block),
block.transactionsHash,
Transaction.buildSolidityStruct(transaction),
index,
siblingPath,
Expand Down
46 changes: 16 additions & 30 deletions nightfall-deployer/contracts/Challenges.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ contract Challenges is Stateful, Key_Registry, Config {
) external onlyBootChallenger {
checkCommit(msg.data);
// check if the block hash is correct and the block hash exists for the block and prior block. Also if the transactions are part of these block
state.isBlockReal(priorBlockL2, priorBlockTransactions);
state.isBlockReal(blockL2, transactions);
state.areBlockAndTransactionsReal(priorBlockL2, priorBlockTransactions);
state.areBlockAndTransactionsReal(blockL2, transactions);
ChallengesUtil.libChallengeLeafCountCorrect(
priorBlockL2,
priorBlockTransactions,
Expand Down Expand Up @@ -73,8 +73,8 @@ contract Challenges is Stateful, Key_Registry, Config {
) external onlyBootChallenger {
checkCommit(msg.data);
// check if the block hash is correct and the block hash exists for the block and prior block
state.isBlockReal(priorBlockL2, priorBlockTransactions);
state.isBlockReal(blockL2, transactions);
state.areBlockAndTransactionsReal(priorBlockL2, priorBlockTransactions);
state.areBlockAndTransactionsReal(blockL2, transactions);
// see if the challenge is valid
ChallengesUtil.libChallengeNewRootCorrect(
priorBlockL2,
Expand Down Expand Up @@ -102,8 +102,8 @@ contract Challenges is Stateful, Key_Registry, Config {
) external onlyBootChallenger {
checkCommit(msg.data);
// first, check we have real, in-train, contiguous blocks
state.isBlockReal(block1, transactions1);
state.isBlockReal(block2, transactions2);
state.areBlockAndTransactionsReal(block1, transactions1);
state.areBlockAndTransactionsReal(block2, transactions2);
// If the duplicate exists in the same block, the index cannot be the same
if (block1.blockNumberL2 == block2.blockNumberL2)
require(transactionIndex1 != transactionIndex2, 'Cannot be the same index');
Expand All @@ -128,7 +128,7 @@ contract Challenges is Stateful, Key_Registry, Config {
bytes32 salt
) external onlyBootChallenger {
checkCommit(msg.data);
state.isBlockReal(blockL2, transactions);
state.areBlockAndTransactionsReal(blockL2, transactions);
ChallengesUtil.libChallengeTransactionType(transactions[transactionIndex]);
// Delete the latest block of the two
challengeAccepted(blockL2);
Expand All @@ -143,7 +143,7 @@ contract Challenges is Stateful, Key_Registry, Config {
bytes32 salt
) external onlyBootChallenger {
checkCommit(msg.data);
state.isBlockReal(blockL2, transactions);
state.areBlockAndTransactionsReal(blockL2, transactions);
// first check the transaction and block do not overflow
ChallengesUtil.libCheckOverflows(blockL2, transactions[transactionIndex]);
// now we need to check that the proof is correct
Expand Down Expand Up @@ -171,8 +171,8 @@ contract Challenges is Stateful, Key_Registry, Config {
bytes32 salt
) external onlyBootChallenger {
checkCommit(msg.data);
state.isBlockReal(blockL2, transactions);
state.isBlockReal(
state.areBlockAndTransactionsReal(blockL2, transactions);
state.areBlockAndTransactionsReal(
blockL2ContainingHistoricRoot,
transactionsOfblockL2ContainingHistoricRoot
);
Expand Down Expand Up @@ -209,12 +209,12 @@ contract Challenges is Stateful, Key_Registry, Config {
bytes32 salt
) external onlyBootChallenger {
checkCommit(msg.data);
state.isBlockReal(blockL2, transactions);
state.isBlockReal(
state.areBlockAndTransactionsReal(blockL2, transactions);
state.areBlockAndTransactionsReal(
blockL2ContainingHistoricRoot[0],
transactionsOfblockL2ContainingHistoricRoot
);
state.isBlockReal(
state.areBlockAndTransactionsReal(
blockL2ContainingHistoricRoot[1],
transactionsOfblockL2ContainingHistoricRoot2
);
Expand Down Expand Up @@ -264,8 +264,8 @@ contract Challenges is Stateful, Key_Registry, Config {
txs2[transactionIndex2],
nullifierIndex2
);
state.isBlockReal(block1, txs1);
state.isBlockReal(block2, txs2);
state.areBlockAndTransactionsReal(block1, txs1);
state.areBlockAndTransactionsReal(block2, txs2);

// The blocks are different and we prune the later block of the two
// as we have a block number, it's easy to see which is the latest.
Expand All @@ -288,7 +288,7 @@ contract Challenges is Stateful, Key_Registry, Config {
bytes32 salt
) external onlyBootChallenger {
checkCommit(msg.data);
state.isBlockReal(blockL2, transactions);
state.areBlockAndTransactionsReal(blockL2, transactions);
if (
transactions[transactionIndex].transactionType ==
Structures.TransactionTypes.DOUBLE_TRANSFER
Expand Down Expand Up @@ -319,20 +319,6 @@ contract Challenges is Stateful, Key_Registry, Config {
challengeAccepted(blockL2);
}

/*
This is a challenge that transactionHashesRoot is incorrectly calculated
*/
function challengeTransactionHashesRoot(
Block memory blockL2,
Transaction[] memory transactions,
bytes32 salt
) external onlyBootChallenger {
checkCommit(msg.data);
bytes32 currentBlockHash = state.isBlockReal(blockL2, transactions);
ChallengesUtil.libChallengeTransactionHashesRoot(blockL2, transactions, currentBlockHash);
challengeAccepted(blockL2);
}

// This gets called when a challenge succeeds
function challengeAccepted(Block memory badBlock) private {
// Check to ensure that the block being challenged is less than a week old
Expand Down
48 changes: 16 additions & 32 deletions nightfall-deployer/contracts/ChallengesUtil.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ import './Structures.sol';
library ChallengesUtil {
bytes32 public constant ZERO =
0x0000000000000000000000000000000000000000000000000000000000000000;
uint256 public constant MAX31 = 2 ** 249 - 1;
uint256 public constant MAX20 = 2 ** 161 - 1;
uint256 public constant BN128_GROUP_ORDER = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
uint256 public constant MAX31 = 2**249 - 1;
uint256 public constant MAX20 = 2**161 - 1;
uint256 public constant BN128_GROUP_ORDER =
21888242871839275222246405745257275088548364400416034343698204186575808495617;

function libChallengeLeafCountCorrect(
Structures.Block memory priorBlockL2,
Expand Down Expand Up @@ -207,16 +208,19 @@ library ChallengesUtil {
}

function libCheckOverflows(
Structures.Block calldata blockL2,
Structures.Transaction calldata transaction
Structures.Block calldata blockL2,
Structures.Transaction calldata transaction
) public pure {
require(uint256(transaction.ercAddress) <= MAX20, 'ERC address out of range');
require(uint256(transaction.recipientAddress) <= MAX20, 'Recipient ERC address out of range');
require(uint256(transaction.commitments[0]) <= MAX31, 'Commitment 0 out of range');
require(uint256(transaction.commitments[1]) <= MAX31, 'Commitment 1 out of range');
require(uint256(transaction.nullifiers[0]) <= MAX31, 'Nullifier 0 out of range');
require(uint256(transaction.nullifiers[1]) <= MAX31, 'Nullifier 1 out of range');
require(uint256(blockL2.root) < BN128_GROUP_ORDER, 'root out of range');
require(uint256(transaction.ercAddress) <= MAX20, 'ERC address out of range');
require(
uint256(transaction.recipientAddress) <= MAX20,
'Recipient ERC address out of range'
);
require(uint256(transaction.commitments[0]) <= MAX31, 'Commitment 0 out of range');
require(uint256(transaction.commitments[1]) <= MAX31, 'Commitment 1 out of range');
require(uint256(transaction.nullifiers[0]) <= MAX31, 'Nullifier 0 out of range');
require(uint256(transaction.nullifiers[1]) <= MAX31, 'Nullifier 1 out of range');
require(uint256(blockL2.root) < BN128_GROUP_ORDER, 'root out of range');
}

function libChallengeNullifier(
Expand All @@ -234,24 +238,4 @@ library ChallengesUtil {
'Transactions need to be different'
);
}

function libChallengeTransactionHashesRoot(
Structures.Block memory blockL2,
Structures.Transaction[] memory transactions,
bytes32 currentBlockHash
) public pure {
bytes32 correctBlockHash =
keccak256(
abi.encode(
blockL2.leafCount,
blockL2.proposer,
blockL2.root,
blockL2.blockNumberL2,
blockL2.previousBlockHash,
Utils.hashTransactionHashes(transactions),
transactions
)
);
require(correctBlockHash != currentBlockHash, 'txHashRoot incorrect');
}
}
Loading