Add txOrigin to approvals

This commit is contained in:
Amir Bandeali 2019-03-04 15:47:33 -08:00
parent 558ce4713c
commit 1136e58de7
12 changed files with 343 additions and 89 deletions

View File

@ -43,11 +43,13 @@ contract MixinCoordinatorApprovalVerifier is
/// @dev Validates that the 0x transaction has been approved by all of the feeRecipients
/// that correspond to each order in the transaction's Exchange calldata.
/// @param transaction 0x transaction containing salt, signerAddress, and data.
/// @param txOrigin Required signer of Ethereum transaction calling this function.
/// @param transactionSignature Proof that the transaction has been signed by the signer.
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata.
function assertValidCoordinatorApprovals(
LibZeroExTransaction.ZeroExTransaction memory transaction,
address txOrigin,
bytes memory transactionSignature,
uint256[] memory approvalExpirationTimeSeconds,
bytes[] memory approvalSignatures
@ -64,6 +66,7 @@ contract MixinCoordinatorApprovalVerifier is
assertValidTransactionOrdersApproval(
transaction,
orders,
txOrigin,
transactionSignature,
approvalExpirationTimeSeconds,
approvalSignatures
@ -74,12 +77,14 @@ contract MixinCoordinatorApprovalVerifier is
/// @dev Validates that the feeRecipients of a batch of order have approved a 0x transaction.
/// @param transaction 0x transaction containing salt, signerAddress, and data.
/// @param orders Array of order structs containing order specifications.
/// @param txOrigin Required signer of Ethereum transaction calling this function.
/// @param transactionSignature Proof that the transaction has been signed by the signer.
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order.
function assertValidTransactionOrdersApproval(
LibZeroExTransaction.ZeroExTransaction memory transaction,
LibOrder.Order[] memory orders,
address txOrigin,
bytes memory transactionSignature,
uint256[] memory approvalExpirationTimeSeconds,
bytes[] memory approvalSignatures
@ -87,6 +92,12 @@ contract MixinCoordinatorApprovalVerifier is
public
view
{
// Verify that Ethereum tx signer is the same as the approved txOrigin
require(
tx.origin == txOrigin,
"INVALID_SENDER"
);
// Hash 0x transaction
bytes32 transactionHash = getTransactionHash(transaction);
@ -98,6 +109,7 @@ contract MixinCoordinatorApprovalVerifier is
// Create approval message
uint256 currentApprovalExpirationTimeSeconds = approvalExpirationTimeSeconds[i];
CoordinatorApproval memory approval = CoordinatorApproval({
txOrigin: txOrigin,
transactionHash: transactionHash,
transactionSignature: transactionSignature,
approvalExpirationTimeSeconds: currentApprovalExpirationTimeSeconds
@ -118,6 +130,9 @@ contract MixinCoordinatorApprovalVerifier is
approvalSignerAddresses = approvalSignerAddresses.append(approvalSignerAddress);
}
// Ethereum transaction signer gives implicit signature of approval
approvalSignerAddresses = approvalSignerAddresses.append(tx.origin);
uint256 ordersLength = orders.length;
for (uint256 i = 0; i < ordersLength; i++) {
// Do not check approval if the order's senderAddress is null
@ -125,25 +140,13 @@ contract MixinCoordinatorApprovalVerifier is
continue;
}
// Ethereum transaction signer gives implicit signature of approval
address approverAddress = orders[i].feeRecipientAddress;
if (approverAddress == tx.origin) {
approvalSignerAddresses = approvalSignerAddresses.append(tx.origin);
continue;
}
// Ensure feeRecipient of order has approved this 0x transaction
address approverAddress = orders[i].feeRecipientAddress;
bool isOrderApproved = approvalSignerAddresses.contains(approverAddress);
require(
isOrderApproved,
"INVALID_APPROVAL_SIGNATURE"
);
// The Ethereum transaction signer must be the 0x transaction signer or an approver of the 0x transaction
require(
transaction.signerAddress == tx.origin || approvalSignerAddresses.contains(tx.origin),
"INVALID_SENDER"
);
}
}

View File

@ -32,11 +32,13 @@ contract MixinCoordinatorCore is
{
/// @dev Executes a 0x transaction that has been signed by the feeRecipients that correspond to each order in the transaction's Exchange calldata.
/// @param transaction 0x transaction containing salt, signerAddress, and data.
/// @param txOrigin Required signer of Ethereum transaction calling this function.
/// @param transactionSignature Proof that the transaction has been signed by the signer.
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata.
function executeTransaction(
LibZeroExTransaction.ZeroExTransaction memory transaction,
address txOrigin,
bytes memory transactionSignature,
uint256[] memory approvalExpirationTimeSeconds,
bytes[] memory approvalSignatures
@ -46,6 +48,7 @@ contract MixinCoordinatorCore is
// Validate that the 0x transaction has been approves by each feeRecipient
assertValidCoordinatorApprovals(
transaction,
txOrigin,
transactionSignature,
approvalExpirationTimeSeconds,
approvalSignatures

View File

@ -28,11 +28,13 @@ contract ICoordinatorApprovalVerifier {
/// @dev Validates that the 0x transaction has been approved by all of the feeRecipients
/// that correspond to each order in the transaction's Exchange calldata.
/// @param transaction 0x transaction containing salt, signerAddress, and data.
/// @param txOrigin Required signer of Ethereum transaction calling this function.
/// @param transactionSignature Proof that the transaction has been signed by the signer.
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata.
function assertValidCoordinatorApprovals(
LibZeroExTransaction.ZeroExTransaction memory transaction,
address txOrigin,
bytes memory transactionSignature,
uint256[] memory approvalExpirationTimeSeconds,
bytes[] memory approvalSignatures
@ -43,12 +45,14 @@ contract ICoordinatorApprovalVerifier {
/// @dev Validates that the feeRecipients of a batch of order have approved a 0x transaction.
/// @param transaction 0x transaction containing salt, signerAddress, and data.
/// @param orders Array of order structs containing order specifications.
/// @param txOrigin Required signer of Ethereum transaction calling this function.
/// @param transactionSignature Proof that the transaction has been signed by the signer.
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order.
function assertValidTransactionOrdersApproval(
LibZeroExTransaction.ZeroExTransaction memory transaction,
LibOrder.Order[] memory orders,
address txOrigin,
bytes memory transactionSignature,
uint256[] memory approvalExpirationTimeSeconds,
bytes[] memory approvalSignatures

View File

@ -26,11 +26,13 @@ contract ICoordinatorCore {
/// @dev Executes a 0x transaction that has been signed by the feeRecipients that correspond to each order in the transaction's Exchange calldata.
/// @param transaction 0x transaction containing salt, signerAddress, and data.
/// @param txOrigin Required signer of Ethereum transaction calling this function.
/// @param transactionSignature Proof that the transaction has been signed by the signer.
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata.
function executeTransaction(
LibZeroExTransaction.ZeroExTransaction memory transaction,
address txOrigin,
bytes memory transactionSignature,
uint256[] memory approvalExpirationTimeSeconds,
bytes[] memory approvalSignatures

View File

@ -27,14 +27,16 @@ contract LibCoordinatorApproval is
// Hash for the EIP712 Coordinator approval message
// keccak256(abi.encodePacked(
// "CoordinatorApproval(",
// "address txOrigin,",
// "bytes32 transactionHash,",
// "bytes transactionSignature,",
// "uint256 approvalExpirationTimeSeconds",
// ")"
// ));
bytes32 constant internal EIP712_COORDINATOR_APPROVAL_SCHEMA_HASH = 0x9f6a55727f39e17015e248e6457fbf0b18270e56c53093ff432d3db5703e191e;
bytes32 constant internal EIP712_COORDINATOR_APPROVAL_SCHEMA_HASH = 0x2fbcdbaa76bc7589916958ae919dfbef04d23f6bbf26de6ff317b32c6cc01e05;
struct CoordinatorApproval {
address txOrigin; // Required signer of Ethereum transaction that is submitting approval.
bytes32 transactionHash; // EIP712 hash of the transaction, using the domain separator of this contract.
bytes transactionSignature; // Signature of the 0x transaction.
uint256 approvalExpirationTimeSeconds; // Timestamp in seconds for which the signature expires.
@ -62,6 +64,7 @@ contract LibCoordinatorApproval is
{
bytes32 schemaHash = EIP712_COORDINATOR_APPROVAL_SCHEMA_HASH;
bytes memory transactionSignature = approval.transactionSignature;
address txOrigin = approval.txOrigin;
bytes32 transactionHash = approval.transactionHash;
uint256 approvalExpirationTimeSeconds = approval.approvalExpirationTimeSeconds;
@ -80,12 +83,13 @@ contract LibCoordinatorApproval is
// Load free memory pointer
let memPtr := mload(64)
mstore(memPtr, schemaHash) // hash of schema
mstore(add(memPtr, 32), transactionHash) // transactionHash
mstore(add(memPtr, 64), transactionSignatureHash) // transactionSignatureHash
mstore(add(memPtr, 96), approvalExpirationTimeSeconds) // approvalExpirationTimeSeconds
mstore(memPtr, schemaHash) // hash of schema
mstore(add(memPtr, 32), txOrigin) // txOrigin
mstore(add(memPtr, 64), transactionHash) // transactionHash
mstore(add(memPtr, 96), transactionSignatureHash) // transactionSignatureHash
mstore(add(memPtr, 128), approvalExpirationTimeSeconds) // approvalExpirationTimeSeconds
// Compute hash
result := keccak256(memPtr, 128)
result := keccak256(memPtr, 160)
}
return result;
}

View File

@ -126,10 +126,15 @@ describe('Coordinator tests', () => {
const transaction = takerTransactionFactory.newSignedTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory.newSignedApproval(transaction, approvalExpirationTimeSeconds);
const approval = approvalFactory.newSignedApproval(
transaction,
takerAddress,
approvalExpirationTimeSeconds,
);
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
await coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
takerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
@ -161,6 +166,7 @@ describe('Coordinator tests', () => {
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
await coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
feeRecipientAddress,
transaction.signature,
[],
[],
@ -192,6 +198,7 @@ describe('Coordinator tests', () => {
await expectTransactionFailedAsync(
coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
takerAddress,
transaction.signature,
[],
[],
@ -209,11 +216,16 @@ describe('Coordinator tests', () => {
const transaction = takerTransactionFactory.newSignedTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory.newSignedApproval(transaction, approvalExpirationTimeSeconds);
const approval = approvalFactory.newSignedApproval(
transaction,
takerAddress,
approvalExpirationTimeSeconds,
);
const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
await expectTransactionFailedAsync(
coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
takerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[signature],
@ -228,10 +240,15 @@ describe('Coordinator tests', () => {
const transaction = takerTransactionFactory.newSignedTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
const approval = approvalFactory.newSignedApproval(transaction, approvalExpirationTimeSeconds);
const approval = approvalFactory.newSignedApproval(
transaction,
takerAddress,
approvalExpirationTimeSeconds,
);
await expectTransactionFailedAsync(
coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
takerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
@ -246,10 +263,15 @@ describe('Coordinator tests', () => {
const transaction = takerTransactionFactory.newSignedTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory.newSignedApproval(transaction, approvalExpirationTimeSeconds);
const approval = approvalFactory.newSignedApproval(
transaction,
takerAddress,
approvalExpirationTimeSeconds,
);
await expectTransactionFailedAsync(
coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
takerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
@ -268,10 +290,15 @@ describe('Coordinator tests', () => {
const transaction = takerTransactionFactory.newSignedTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory.newSignedApproval(transaction, approvalExpirationTimeSeconds);
const approval = approvalFactory.newSignedApproval(
transaction,
takerAddress,
approvalExpirationTimeSeconds,
);
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
await coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
takerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
@ -305,6 +332,7 @@ describe('Coordinator tests', () => {
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
await coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
feeRecipientAddress,
transaction.signature,
[],
[],
@ -337,11 +365,16 @@ describe('Coordinator tests', () => {
const transaction = takerTransactionFactory.newSignedTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory.newSignedApproval(transaction, approvalExpirationTimeSeconds);
const approval = approvalFactory.newSignedApproval(
transaction,
takerAddress,
approvalExpirationTimeSeconds,
);
const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
await expectTransactionFailedAsync(
coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
takerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[signature],
@ -356,10 +389,15 @@ describe('Coordinator tests', () => {
const transaction = takerTransactionFactory.newSignedTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
const approval = approvalFactory.newSignedApproval(transaction, approvalExpirationTimeSeconds);
const approval = approvalFactory.newSignedApproval(
transaction,
takerAddress,
approvalExpirationTimeSeconds,
);
await expectTransactionFailedAsync(
coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
takerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
@ -374,10 +412,15 @@ describe('Coordinator tests', () => {
const transaction = takerTransactionFactory.newSignedTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory.newSignedApproval(transaction, approvalExpirationTimeSeconds);
const approval = approvalFactory.newSignedApproval(
transaction,
takerAddress,
approvalExpirationTimeSeconds,
);
await expectTransactionFailedAsync(
coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
takerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
@ -396,6 +439,7 @@ describe('Coordinator tests', () => {
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
await coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
makerAddress,
transaction.signature,
[],
[],
@ -423,6 +467,7 @@ describe('Coordinator tests', () => {
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
await coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
makerAddress,
transaction.signature,
[],
[],
@ -452,6 +497,7 @@ describe('Coordinator tests', () => {
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
await coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
makerAddress,
transaction.signature,
[],
[],

View File

@ -52,12 +52,18 @@ describe('Libs tests', () => {
signature: '0x5678',
};
const approvalExpirationTimeSeconds = new BigNumber(0);
const txOrigin = constants.NULL_ADDRESS;
const approval = {
txOrigin,
transactionHash: hashUtils.getTransactionHashHex(signedTx),
transactionSignature: signedTx.signature,
approvalExpirationTimeSeconds,
};
const expectedApprovalHash = hashUtils.getApprovalHashHex(signedTx, approvalExpirationTimeSeconds);
const expectedApprovalHash = hashUtils.getApprovalHashHex(
signedTx,
txOrigin,
approvalExpirationTimeSeconds,
);
const approvalHash = await testLibs.publicGetCoordinatorApprovalHash.callAsync(approval);
expect(expectedApprovalHash).to.eq(approvalHash);
});

View File

@ -137,10 +137,15 @@ describe('Mixins tests', () => {
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
const approval = approvalFactory1.newSignedApproval(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds,
);
await mixins.assertValidTransactionOrdersApproval.callAsync(
transaction,
orders,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
@ -148,6 +153,7 @@ describe('Mixins tests', () => {
);
await mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
@ -164,10 +170,15 @@ describe('Mixins tests', () => {
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
const approval = approvalFactory1.newSignedApproval(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds,
);
await mixins.assertValidTransactionOrdersApproval.callAsync(
transaction,
orders,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
@ -175,6 +186,7 @@ describe('Mixins tests', () => {
);
await mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
@ -188,14 +200,22 @@ describe('Mixins tests', () => {
await mixins.assertValidTransactionOrdersApproval.callAsync(
transaction,
orders,
approvalSignerAddress1,
transaction.signature,
[],
[],
{ from: approvalSignerAddress1 },
);
await mixins.assertValidCoordinatorApprovals.callAsync(transaction, transaction.signature, [], [], {
from: approvalSignerAddress1,
});
await mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
approvalSignerAddress1,
transaction.signature,
[],
[],
{
from: approvalSignerAddress1,
},
);
});
it(`Should be successful: function=${fnName}, caller=approver1, senderAddress=[verifier], approval_sig=[approver1], expiration=[invalid]`, async () => {
const orders = [defaultOrder];
@ -203,10 +223,15 @@ describe('Mixins tests', () => {
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
const approval = approvalFactory1.newSignedApproval(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds,
);
await mixins.assertValidTransactionOrdersApproval.callAsync(
transaction,
orders,
approvalSignerAddress1,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
@ -214,6 +239,7 @@ describe('Mixins tests', () => {
);
await mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
approvalSignerAddress1,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
@ -227,14 +253,22 @@ describe('Mixins tests', () => {
await mixins.assertValidTransactionOrdersApproval.callAsync(
transaction,
orders,
approvalSignerAddress1,
transaction.signature,
[],
[],
{ from: approvalSignerAddress1 },
);
await mixins.assertValidCoordinatorApprovals.callAsync(transaction, transaction.signature, [], [], {
from: approvalSignerAddress1,
});
await mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
approvalSignerAddress1,
transaction.signature,
[],
[],
{
from: approvalSignerAddress1,
},
);
});
it(`Should revert: function=${fnName}, caller=tx_signer, senderAddress=[verifier], approval_sig=[invalid], expiration=[valid]`, async () => {
const orders = [defaultOrder];
@ -242,12 +276,17 @@ describe('Mixins tests', () => {
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
const approval = approvalFactory1.newSignedApproval(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds,
);
const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
expectContractCallFailedAsync(
mixins.assertValidTransactionOrdersApproval.callAsync(
transaction,
orders,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[signature],
@ -258,6 +297,7 @@ describe('Mixins tests', () => {
expectContractCallFailedAsync(
mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[signature],
@ -272,11 +312,16 @@ describe('Mixins tests', () => {
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
const approval = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
const approval = approvalFactory1.newSignedApproval(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds,
);
expectContractCallFailedAsync(
mixins.assertValidTransactionOrdersApproval.callAsync(
transaction,
orders,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
@ -287,6 +332,7 @@ describe('Mixins tests', () => {
expectContractCallFailedAsync(
mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
@ -301,11 +347,16 @@ describe('Mixins tests', () => {
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
const approval = approvalFactory1.newSignedApproval(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds,
);
expectContractCallFailedAsync(
mixins.assertValidTransactionOrdersApproval.callAsync(
transaction,
orders,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
@ -316,6 +367,7 @@ describe('Mixins tests', () => {
expectContractCallFailedAsync(
mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
@ -338,10 +390,15 @@ describe('Mixins tests', () => {
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
const approval = approvalFactory1.newSignedApproval(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds,
);
await mixins.assertValidTransactionOrdersApproval.callAsync(
transaction,
orders,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
@ -349,6 +406,7 @@ describe('Mixins tests', () => {
);
await mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
@ -364,10 +422,15 @@ describe('Mixins tests', () => {
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
const approval = approvalFactory1.newSignedApproval(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds,
);
await mixins.assertValidTransactionOrdersApproval.callAsync(
transaction,
orders,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
@ -375,6 +438,7 @@ describe('Mixins tests', () => {
);
await mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
@ -391,14 +455,20 @@ describe('Mixins tests', () => {
await mixins.assertValidTransactionOrdersApproval.callAsync(
transaction,
orders,
transactionSignerAddress,
transaction.signature,
[],
[],
{ from: transactionSignerAddress },
);
await mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[],
[],
{ from: transactionSignerAddress },
);
await mixins.assertValidCoordinatorApprovals.callAsync(transaction, transaction.signature, [], [], {
from: transactionSignerAddress,
});
});
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[verifier,null], feeRecipient=[approver1,approver1], approval_sig=[approver1], expiration=[valid]`, async () => {
const orders = [defaultOrder, { ...defaultOrder, senderAddress: devConstants.NULL_ADDRESS }];
@ -406,10 +476,15 @@ describe('Mixins tests', () => {
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
const approval = approvalFactory1.newSignedApproval(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds,
);
await mixins.assertValidTransactionOrdersApproval.callAsync(
transaction,
orders,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
@ -417,6 +492,7 @@ describe('Mixins tests', () => {
);
await mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
@ -429,11 +505,20 @@ describe('Mixins tests', () => {
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval1 = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
const approval2 = approvalFactory2.newSignedApproval(transaction, approvalExpirationTimeSeconds);
const approval1 = approvalFactory1.newSignedApproval(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds,
);
const approval2 = approvalFactory2.newSignedApproval(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds,
);
await mixins.assertValidTransactionOrdersApproval.callAsync(
transaction,
orders,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds, approvalExpirationTimeSeconds],
[approval1.signature, approval2.signature],
@ -441,6 +526,7 @@ describe('Mixins tests', () => {
);
await mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds, approvalExpirationTimeSeconds],
[approval1.signature, approval2.signature],
@ -454,36 +540,54 @@ describe('Mixins tests', () => {
await mixins.assertValidTransactionOrdersApproval.callAsync(
transaction,
orders,
approvalSignerAddress1,
transaction.signature,
[],
[],
{ from: approvalSignerAddress1 },
);
await mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
approvalSignerAddress1,
transaction.signature,
[],
[],
{ from: approvalSignerAddress1 },
);
await mixins.assertValidCoordinatorApprovals.callAsync(transaction, transaction.signature, [], [], {
from: approvalSignerAddress1,
});
});
it(`Should be successful: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver2], approval_sig=[approver2], expiration=[valid]`, async () => {
it(`Should revert: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver2], approval_sig=[approver2], expiration=[valid]`, async () => {
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval2 = approvalFactory2.newSignedApproval(transaction, approvalExpirationTimeSeconds);
await mixins.assertValidTransactionOrdersApproval.callAsync(
const approval2 = approvalFactory2.newSignedApproval(
transaction,
orders,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval2.signature],
{ from: approvalSignerAddress1 },
transactionSignerAddress,
approvalExpirationTimeSeconds,
);
await mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval2.signature],
{ from: approvalSignerAddress1 },
expectContractCallFailedAsync(
mixins.assertValidTransactionOrdersApproval.callAsync(
transaction,
orders,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval2.signature],
{ from: approvalSignerAddress1 },
),
RevertReason.InvalidSender,
);
expectContractCallFailedAsync(
mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval2.signature],
{ from: approvalSignerAddress1 },
),
RevertReason.InvalidSender,
);
});
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[], expiration=[]`, async () => {
@ -494,6 +598,7 @@ describe('Mixins tests', () => {
mixins.assertValidTransactionOrdersApproval.callAsync(
transaction,
orders,
transactionSignerAddress,
transaction.signature,
[],
[],
@ -502,9 +607,14 @@ describe('Mixins tests', () => {
RevertReason.InvalidApprovalSignature,
);
expectContractCallFailedAsync(
mixins.assertValidCoordinatorApprovals.callAsync(transaction, transaction.signature, [], [], {
from: transactionSignerAddress,
}),
mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[],
[],
{ from: transactionSignerAddress },
),
RevertReason.InvalidApprovalSignature,
);
});
@ -514,12 +624,17 @@ describe('Mixins tests', () => {
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
const approval = approvalFactory1.newSignedApproval(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds,
);
const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
expectContractCallFailedAsync(
mixins.assertValidTransactionOrdersApproval.callAsync(
transaction,
orders,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[signature],
@ -530,6 +645,7 @@ describe('Mixins tests', () => {
expectContractCallFailedAsync(
mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[signature],
@ -544,13 +660,22 @@ describe('Mixins tests', () => {
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval1 = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
const approval2 = approvalFactory2.newSignedApproval(transaction, approvalExpirationTimeSeconds);
const approval1 = approvalFactory1.newSignedApproval(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds,
);
const approval2 = approvalFactory2.newSignedApproval(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds,
);
const approvalSignature2 = `${approval2.signature.slice(0, 4)}FFFFFFFF${approval2.signature.slice(12)}`;
expectContractCallFailedAsync(
mixins.assertValidTransactionOrdersApproval.callAsync(
transaction,
orders,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds, approvalExpirationTimeSeconds],
[approval1.signature, approvalSignature2],
@ -561,6 +686,7 @@ describe('Mixins tests', () => {
expectContractCallFailedAsync(
mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds, approvalExpirationTimeSeconds],
[approval1.signature, approvalSignature2],
@ -575,12 +701,17 @@ describe('Mixins tests', () => {
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval2 = approvalFactory2.newSignedApproval(transaction, approvalExpirationTimeSeconds);
const approval2 = approvalFactory2.newSignedApproval(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds,
);
const approvalSignature2 = `${approval2.signature.slice(0, 4)}FFFFFFFF${approval2.signature.slice(12)}`;
expectContractCallFailedAsync(
mixins.assertValidTransactionOrdersApproval.callAsync(
transaction,
orders,
approvalSignerAddress1,
transaction.signature,
[approvalExpirationTimeSeconds],
[approvalSignature2],
@ -591,6 +722,7 @@ describe('Mixins tests', () => {
expectContractCallFailedAsync(
mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
approvalSignerAddress1,
transaction.signature,
[approvalExpirationTimeSeconds],
[approvalSignature2],
@ -606,12 +738,21 @@ describe('Mixins tests', () => {
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds1 = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approvalExpirationTimeSeconds2 = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
const approval1 = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds1);
const approval2 = approvalFactory2.newSignedApproval(transaction, approvalExpirationTimeSeconds2);
const approval1 = approvalFactory1.newSignedApproval(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds1,
);
const approval2 = approvalFactory2.newSignedApproval(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds2,
);
expectContractCallFailedAsync(
mixins.assertValidTransactionOrdersApproval.callAsync(
transaction,
orders,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds1, approvalExpirationTimeSeconds2],
[approval1.signature, approval2.signature],
@ -622,6 +763,7 @@ describe('Mixins tests', () => {
expectContractCallFailedAsync(
mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds1, approvalExpirationTimeSeconds2],
[approval1.signature, approval2.signature],
@ -636,11 +778,16 @@ describe('Mixins tests', () => {
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
const approval2 = approvalFactory2.newSignedApproval(transaction, approvalExpirationTimeSeconds);
const approval2 = approvalFactory2.newSignedApproval(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds,
);
expectContractCallFailedAsync(
mixins.assertValidTransactionOrdersApproval.callAsync(
transaction,
orders,
approvalSignerAddress1,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval2.signature],
@ -651,6 +798,7 @@ describe('Mixins tests', () => {
expectContractCallFailedAsync(
mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
approvalSignerAddress1,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval2.signature],
@ -665,11 +813,16 @@ describe('Mixins tests', () => {
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval1 = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
const approval1 = approvalFactory1.newSignedApproval(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds,
);
expectContractCallFailedAsync(
mixins.assertValidTransactionOrdersApproval.callAsync(
transaction,
orders,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval1.signature],
@ -680,6 +833,7 @@ describe('Mixins tests', () => {
expectContractCallFailedAsync(
mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval1.signature],
@ -695,25 +849,40 @@ describe('Mixins tests', () => {
const orders = [defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.CANCEL_ORDERS, orders);
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
await mixins.assertValidCoordinatorApprovals.callAsync(transaction, transaction.signature, [], [], {
from: transactionSignerAddress,
});
await mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[],
[],
{ from: transactionSignerAddress },
);
});
it('should allow the tx signer to call `batchCancelOrders` without approval', async () => {
const orders = [defaultOrder, defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.BATCH_CANCEL_ORDERS, orders);
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
await mixins.assertValidCoordinatorApprovals.callAsync(transaction, transaction.signature, [], [], {
from: transactionSignerAddress,
});
await mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[],
[],
{ from: transactionSignerAddress },
);
});
it('should allow the tx signer to call `cancelOrdersUpTo` without approval', async () => {
const orders: SignedOrder[] = [];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.CANCEL_ORDERS_UP_TO, orders);
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
await mixins.assertValidCoordinatorApprovals.callAsync(transaction, transaction.signature, [], [], {
from: transactionSignerAddress,
});
await mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[],
[],
{ from: transactionSignerAddress },
);
});
});
});

View File

@ -13,6 +13,7 @@ export class ApprovalFactory {
}
public newSignedApproval(
transaction: SignedZeroExTransaction,
txOrigin: string,
approvalExpirationTimeSeconds: BigNumber,
signatureType: CoordinatorSignatureType = CoordinatorSignatureType.EthSign,
): SignedCoordinatorApproval {
@ -20,9 +21,14 @@ export class ApprovalFactory {
...transaction,
verifyingContractAddress: this._verifyingContractAddress,
};
const approvalHashBuff = hashUtils.getApprovalHashBuffer(coordinatorTransaction, approvalExpirationTimeSeconds);
const approvalHashBuff = hashUtils.getApprovalHashBuffer(
coordinatorTransaction,
txOrigin,
approvalExpirationTimeSeconds,
);
const signatureBuff = signingUtils.signMessage(approvalHashBuff, this._privateKey, signatureType);
const signedApproval = {
txOrigin,
transaction: coordinatorTransaction,
approvalExpirationTimeSeconds,
signature: ethUtil.addHexPrefix(signatureBuff.toString('hex')),

View File

@ -6,6 +6,7 @@ export const constants = {
COORDINATOR_APPROVAL_SCHEMA: {
name: 'CoordinatorApproval',
parameters: [
{ name: 'txOrigin', type: 'address' },
{ name: 'transactionHash', type: 'bytes32' },
{ name: 'transactionSignature', type: 'bytes' },
{ name: 'approvalExpirationTimeSeconds', type: 'uint256' },

View File

@ -7,7 +7,11 @@ import * as _ from 'lodash';
import { constants } from './index';
export const hashUtils = {
getApprovalHashBuffer(transaction: SignedZeroExTransaction, approvalExpirationTimeSeconds: BigNumber): Buffer {
getApprovalHashBuffer(
transaction: SignedZeroExTransaction,
txOrigin: string,
approvalExpirationTimeSeconds: BigNumber,
): Buffer {
const domain = {
name: constants.COORDINATOR_DOMAIN_NAME,
version: constants.COORDINATOR_DOMAIN_VERSION,
@ -15,6 +19,7 @@ export const hashUtils = {
};
const transactionHash = hashUtils.getTransactionHashHex(transaction);
const approval = {
txOrigin,
transactionHash,
transactionSignature: transaction.signature,
approvalExpirationTimeSeconds: approvalExpirationTimeSeconds.toString(),
@ -30,9 +35,13 @@ export const hashUtils = {
const hashBuffer = signTypedDataUtils.generateTypedDataHash(typedData);
return hashBuffer;
},
getApprovalHashHex(transaction: SignedZeroExTransaction, approvalExpirationTimeSeconds: BigNumber): string {
getApprovalHashHex(
transaction: SignedZeroExTransaction,
txOrigin: string,
approvalExpirationTimeSeconds: BigNumber,
): string {
const hashHex = `0x${hashUtils
.getApprovalHashBuffer(transaction, approvalExpirationTimeSeconds)
.getApprovalHashBuffer(transaction, txOrigin, approvalExpirationTimeSeconds)
.toString('hex')}`;
return hashHex;
},

View File

@ -3,6 +3,7 @@ import { BigNumber } from '@0x/utils';
export interface CoordinatorApproval {
transaction: SignedZeroExTransaction;
txOrigin: string;
approvalExpirationTimeSeconds: BigNumber;
}