Commit b8f8da84 by Francisco Giordano

Merge upstream openzeppelin-contracts into upstream-patched

parents 0502cc31 508a879e
......@@ -20,23 +20,35 @@
## Unreleased
* `IERC20Metadata`: add a new extended interface that includes the optional `name()`, `symbol()` and `decimals()` functions. ([#2561](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2561))
* `ERC777`: make reception acquirement optional in `_mint`. ([#2552](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2552))
* `ERC20Permit`: add a `_useNonce` to enable further usage of ERC712 signatures. ([#2565](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2565))
## Unreleased
* Now targeting the 0.8.x line of Solidity compilers. For 0.6.x (resp 0.7.x) support, use version 3.4.0 (resp 3.4.0-solc-0.7) of OpenZeppelin.
* `Context`: making `_msgData` return `bytes calldata` instead of `bytes memory` ([#2492](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2492))
* `ERC20`: Removed the `_setDecimals` function and the storage slot associated to decimals. ([#2502](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2502))
* `ERC20`: removed the `_setDecimals` function and the storage slot associated to decimals. ([#2502](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2502))
* `Strings`: addition of a `toHexString` function. ([#2504](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2504))
* `EnumerableMap`: change implementation to optimize for `key → value` lookups instead of enumeration. ([#2518](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2518))
* `GSN`: Deprecate GSNv1 support in favor of upcomming support for GSNv2. ([#2521](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2521))
* `ERC165`: Remove uses of storage in the base ERC165 implementation. ERC165 based contracts now use storage-less virtual functions. Old behaviour remains available in the `ERC165Storage` extension. ([#2505](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2505))
* `Initializable`: Make initializer check stricter during construction. ([#2531](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2531))
* `GSN`: deprecate GSNv1 support in favor of upcoming support for GSNv2. ([#2521](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2521))
* `ERC165`: remove uses of storage in the base ERC165 implementation. ERC165 based contracts now use storage-less virtual functions. Old behavior remains available in the `ERC165Storage` extension. ([#2505](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2505))
* `Initializable`: make initializer check stricter during construction. ([#2531](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2531))
* `ERC721`: remove enumerability of tokens from the base implementation. This feature is now provided separately through the `ERC721Enumerable` extension. ([#2511](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2511))
* `AccessControl`: removed enumerability by default for a more lightweight contract. It is now opt-in through `AccessControlEnumerable`. ([#2512](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2512))
* Meta Transactions: add `ERC2771Context` and a `MinimalForwarder` for meta-transactions. ([#2508](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2508))
* Overall reorganisation of the contract folder to improve clarity and discoverability. ([#2503](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2503))
* `ERC20Capped`: optimize gas usage of by enforcing te check directly in `_mint`. ([#2524](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2524))
* Overall reorganization of the contract folder to improve clarity and discoverability. ([#2503](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2503))
* `ERC20Capped`: optimize gas usage by enforcing the check directly in `_mint`. ([#2524](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2524))
* Rename `UpgradeableProxy` to `ERC1967Proxy`. ([#2547](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2547))
* `ERC777`: Optimize the gas costs of the constructor. ([#2551](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2551))
* `ERC721URIStorage`: Add a new extension that implements the `_setTokenURI` behavior as it was available in 3.4.0. ([#2555](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2555))
* `AccessControl`: Added ERC165 interface detection. ([#2562](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2562))
* `ERC777`: optimize the gas costs of the constructor. ([#2551](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2551))
* `ERC721URIStorage`: add a new extension that implements the `_setTokenURI` behavior as it was available in 3.4.0. ([#2555](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2555))
* `AccessControl`: added ERC165 interface detection. ([#2562](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2562))
* `ERC1155`: make `uri` public so overloading function can call it using super. ([#2576](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2576))
### Bug fixes for beta releases
* `AccessControlEnumerable`: Fixed `renounceRole` not updating enumerable set of addresses for a role. ([#2572](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2572))
### How to upgrade from 3.x
......@@ -48,6 +60,10 @@ npx openzeppelin-contracts-migrate-imports
Make sure you're using git or another version control system to be able to recover from any potential error in our script.
### How to upgrade from 4.0-beta.x
Some further changes have been done between the different beta iterations. Transitions made during this period are configured in the `migrate-imports` script. Consequently, you can upgrade from any previous 4.0-beta.x version using the same script as described in the *How to upgrade from 3.x* section.
## 3.4.0 (2021-02-02)
* `BeaconProxy`: added new kind of proxy that allows simultaneous atomic upgrades. ([#2411](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2411))
......
......@@ -70,6 +70,14 @@ abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessCon
}
/**
* @dev Overload {renounceRole} to track enumerable memberships
*/
function renounceRole(bytes32 role, address account) public virtual override {
super.renounceRole(role, account);
_roleMembers[role].remove(account);
}
/**
* @dev Overload {_setupRole} to track enumerable memberships
*/
function _setupRole(bytes32 role, address account) internal virtual override {
......
......@@ -2,9 +2,9 @@
pragma solidity ^0.8.0;
import "./Address.sol";
import "./Context.sol";
import "./math/SafeMath.sol";
import "../utils/Address.sol";
import "../utils/Context.sol";
import "../utils/math/SafeMath.sol";
/**
* @title PaymentSplitter
......
= Finance
[.readme-notice]
NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/finance
This directory includes primitives for financial systems. We currently only offer the {PaymentSplitter} contract, but we want to grow this directory so we welcome ideas.
== PaymentSplitter
{{PaymentSplitter}}
= Governance
[.readme-notice]
NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/access
NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/governance
This directory includes primitives for on-chain governance. We currently only offer the {TimelockController} contract, that can be used as a component in a governance systems to introduce a delay between a proposal and its execution.
......
......@@ -27,6 +27,16 @@ contract ERC777Mock is Context, ERC777 {
_mint(to, amount, userData, operatorData);
}
function mintInternalExtended (
address to,
uint256 amount,
bytes memory userData,
bytes memory operatorData,
bool requireReceptionAck
) public {
_mint(to, amount, userData, operatorData, requireReceptionAck);
}
function approveInternal(address holder, address spender, uint256 value) public {
_approve(holder, spender, value);
}
......
......@@ -2,7 +2,7 @@
pragma solidity ^0.8.0;
import "../utils/Initializable.sol";
import "../proxy/utils/Initializable.sol";
/**
* @title InitializableMock
......
......@@ -2,7 +2,7 @@
pragma solidity ^0.8.0;
import "../utils/Initializable.sol";
import "../proxy/utils/Initializable.sol";
// Sample contracts showing upgradeability with multiple inheritance.
// Child contract inherits from Father and Mother contracts, and Father extends from Gramps.
......
......@@ -2,7 +2,7 @@
pragma solidity ^0.8.0;
import "../utils/Initializable.sol";
import "../proxy/utils/Initializable.sol";
contract Implementation1 is Initializable {
uint internal _value;
......
......@@ -2,7 +2,7 @@
pragma solidity ^0.8.0;
import "../utils/Initializable.sol";
import "../proxy/utils/Initializable.sol";
/**
* @title MigratableMockV1
......
......@@ -40,3 +40,7 @@ CAUTION: Using upgradeable proxies correctly and securely is a difficult task th
== Minimal Clones
{{Clones}}
== Utils
{{Initializable}}
......@@ -3,7 +3,7 @@
// solhint-disable-next-line compiler-version
pragma solidity ^0.8.0;
import "./Address.sol";
import "../../utils/Address.sol";
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
......
......@@ -55,7 +55,7 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
* Clients calling this function must replace the `\{id\}` substring with the
* actual token type ID.
*/
function uri(uint256) external view virtual override returns (string memory) {
function uri(uint256) public view virtual override returns (string memory) {
return _uri;
}
......
......@@ -3,6 +3,7 @@
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";
/**
......@@ -29,7 +30,7 @@ import "../../utils/Context.sol";
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20 is Context, IERC20 {
contract ERC20 is Context, IERC20, IERC20Metadata {
mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowances;
......@@ -56,7 +57,7 @@ contract ERC20 is Context, IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() public view virtual returns (string memory) {
function name() public view virtual override returns (string memory) {
return _name;
}
......@@ -64,7 +65,7 @@ contract ERC20 is Context, IERC20 {
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual returns (string memory) {
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
......@@ -81,7 +82,7 @@ contract ERC20 is Context, IERC20 {
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual returns (uint8) {
function decimals() public view virtual override returns (uint8) {
return 18;
}
......
......@@ -10,6 +10,7 @@ TIP: For an overview of ERC20 tokens and a walk through on how to create a token
There a few core contracts that implement the behavior specified in the EIP:
* {IERC20}: the interface all ERC20 implementations should conform to.
* {IERC20Metadata}: the extended ERC20 interface including the <<ERC20-name,`name`>>, <<ERC20-symbol,`symbol`>> and <<ERC20-decimals,`decimals`>> functions.
* {ERC20}: the implementation of the ERC20 interface, including the <<ERC20-name,`name`>>, <<ERC20-symbol,`symbol`>> and <<ERC20-decimals,`decimals`>> optional standard extension to the base interface.
Additionally there are multiple custom extensions, including:
......@@ -36,6 +37,8 @@ NOTE: This core set of contracts is designed to be unopinionated, allowing devel
{{IERC20}}
{{IERC20Metadata}}
{{ERC20}}
== Extensions
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
......@@ -47,7 +47,7 @@ abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {
owner,
spender,
value,
_nonces[owner].current(),
_useNonce(owner),
deadline
)
);
......@@ -57,14 +57,13 @@ abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {
address signer = ECDSA.recover(hash, v, r, s);
require(signer == owner, "ERC20Permit: invalid signature");
_nonces[owner].increment();
_approve(owner, spender, value);
}
/**
* @dev See {IERC20Permit-nonces}.
*/
function nonces(address owner) public view override returns (uint256) {
function nonces(address owner) public view virtual override returns (uint256) {
return _nonces[owner].current();
}
......@@ -75,4 +74,13 @@ abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {
function DOMAIN_SEPARATOR() external view override returns (bytes32) {
return _domainSeparatorV4();
}
/**
* @dev "Consume a nonce": return the current value and increment.
*/
function _useNonce(address owner) internal virtual returns (uint256 current) {
Counters.Counter storage nonce = _nonces[owner];
current = nonce.current();
nonce.increment();
}
}
......@@ -223,7 +223,7 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
* @dev Safely mints `tokenId` and transfers it to `to`.
*
* Requirements:
d*
*
* - `tokenId` must not exist.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
......
......@@ -313,6 +313,37 @@ contract ERC777 is Context, IERC777, IERC20 {
internal
virtual
{
_mint(account, amount, userData, operatorData, true);
}
/**
* @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* If `requireReceptionAck` is set to true, and if a send hook is
* registered for `account`, the corresponding function will be called with
* `operator`, `data` and `operatorData`.
*
* See {IERC777Sender} and {IERC777Recipient}.
*
* Emits {Minted} and {IERC20-Transfer} events.
*
* Requirements
*
* - `account` cannot be the zero address.
* - if `account` is a contract, it must implement the {IERC777Recipient}
* interface.
*/
function _mint(
address account,
uint256 amount,
bytes memory userData,
bytes memory operatorData,
bool requireReceptionAck
)
internal
virtual
{
require(account != address(0), "ERC777: mint to the zero address");
address operator = _msgSender();
......@@ -323,7 +354,7 @@ contract ERC777 is Context, IERC777, IERC20 {
_totalSupply += amount;
_balances[account] += amount;
_callTokensReceived(operator, address(0), account, amount, userData, operatorData, true);
_callTokensReceived(operator, address(0), account, amount, userData, operatorData, requireReceptionAck);
emit Minted(operator, account, amount, userData, operatorData);
emit Transfer(address(0), account, amount);
......
......@@ -40,6 +40,14 @@ Finally, {Create2} contains all necessary utilities to safely use the https://bl
{{EIP712}}
== Escrow
{{ConditionalEscrow}}
{{Escrow}}
{{RefundEscrow}}
== Introspection
This set of interfaces and contracts deal with https://en.wikipedia.org/wiki/Type_introspection[type introspection] of contracts, that is, examining which functions can be called on them. This is usually referred to as a contract's _interface_.
......@@ -84,7 +92,3 @@ Note that, in all cases, accounts simply _declare_ their interfaces, but they ar
{{Counters}}
{{Strings}}
== Other
{{Initializable}}
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -67,8 +67,10 @@
"ethereumjs-wallet": "^1.0.1",
"hardhat": "^2.0.6",
"hardhat-gas-reporter": "^1.0.4",
"keccak256": "^1.0.2",
"lodash.startcase": "^4.4.0",
"lodash.zip": "^4.2.0",
"merkletreejs": "^0.2.13",
"micromatch": "^4.0.2",
"mocha": "^8.0.1",
"rimraf": "^3.0.2",
......@@ -76,6 +78,5 @@
"solidity-coverage": "^0.7.11",
"solidity-docgen": "^0.5.3",
"web3": "^1.3.0"
},
"dependencies": {}
}
}
......@@ -30,7 +30,8 @@ const pathUpdates = {
'payment/escrow/ConditionalEscrow.sol': 'utils/escrow/ConditionalEscrow.sol',
'payment/escrow/Escrow.sol': 'utils/escrow/Escrow.sol',
'payment/escrow/RefundEscrow.sol': 'utils/escrow/RefundEscrow.sol',
'payment/PaymentSplitter.sol': 'utils/PaymentSplitter.sol',
'payment/PaymentSplitter.sol': 'finance/PaymentSplitter.sol',
'utils/PaymentSplitter.sol': 'finance/PaymentSplitter.sol',
'payment/PullPayment.sol': 'security/PullPayment.sol',
'presets/ERC1155PresetMinterPauser.sol': 'token/ERC1155/presets/ERC1155PresetMinterPauser.sol',
'presets/ERC20PresetFixedSupply.sol': 'token/ERC20/presets/ERC20PresetFixedSupply.sol',
......@@ -40,7 +41,8 @@ const pathUpdates = {
// 'proxy/BeaconProxy.sol': 'proxy/beacon/BeaconProxy.sol',
// 'proxy/Clones.sol': undefined,
// 'proxy/IBeacon.sol': 'proxy/beacon/IBeacon.sol',
'proxy/Initializable.sol': 'utils/Initializable.sol',
'proxy/Initializable.sol': 'proxy/utils/Initializable.sol',
'utils/Initializable.sol': 'proxy/utils/Initializable.sol',
// 'proxy/ProxyAdmin.sol': 'proxy/transparent/ProxyAdmin.sol',
// 'proxy/Proxy.sol': undefined,
// 'proxy/TransparentUpgradeableProxy.sol': 'proxy/transparent/TransparentUpgradeableProxy.sol',
......
......@@ -179,6 +179,13 @@ function shouldBehaveLikeAccessControlEnumerable (errorPrefix, admin, authorized
expect(bearers).to.have.members([authorized, otherAuthorized]);
});
it('role enumeration should be in sync after renounceRole call', async function () {
expect(await this.accessControl.getRoleMemberCount(ROLE)).to.bignumber.equal('0');
await this.accessControl.grantRole(ROLE, admin, { from: admin });
expect(await this.accessControl.getRoleMemberCount(ROLE)).to.bignumber.equal('1');
await this.accessControl.renounceRole(ROLE, admin, { from: admin });
expect(await this.accessControl.getRoleMemberCount(ROLE)).to.bignumber.equal('0');
});
});
}
......
const { keccak256, keccakFromString, bufferToHex } = require('ethereumjs-util');
class MerkleTree {
constructor (elements) {
// Filter empty strings and hash elements
this.elements = elements.filter(el => el).map(el => keccakFromString(el));
// Sort elements
this.elements.sort(Buffer.compare);
// Deduplicate elements
this.elements = this.bufDedup(this.elements);
// Create layers
this.layers = this.getLayers(this.elements);
}
getLayers (elements) {
if (elements.length === 0) {
return [['']];
}
const layers = [];
layers.push(elements);
// Get next layer until we reach the root
while (layers[layers.length - 1].length > 1) {
layers.push(this.getNextLayer(layers[layers.length - 1]));
}
return layers;
}
getNextLayer (elements) {
return elements.reduce((layer, el, idx, arr) => {
if (idx % 2 === 0) {
// Hash the current element with its pair element
layer.push(this.combinedHash(el, arr[idx + 1]));
}
return layer;
}, []);
}
combinedHash (first, second) {
if (!first) { return second; }
if (!second) { return first; }
return keccak256(this.sortAndConcat(first, second));
}
getRoot () {
return this.layers[this.layers.length - 1][0];
}
getHexRoot () {
return bufferToHex(this.getRoot());
}
getProof (el) {
let idx = this.bufIndexOf(el, this.elements);
if (idx === -1) {
throw new Error('Element does not exist in Merkle tree');
}
return this.layers.reduce((proof, layer) => {
const pairElement = this.getPairElement(idx, layer);
if (pairElement) {
proof.push(pairElement);
}
idx = Math.floor(idx / 2);
return proof;
}, []);
}
getHexProof (el) {
const proof = this.getProof(el);
return this.bufArrToHexArr(proof);
}
getPairElement (idx, layer) {
const pairIdx = idx % 2 === 0 ? idx + 1 : idx - 1;
if (pairIdx < layer.length) {
return layer[pairIdx];
} else {
return null;
}
}
bufIndexOf (el, arr) {
let hash;
// Convert element to 32 byte hash if it is not one already
if (el.length !== 32 || !Buffer.isBuffer(el)) {
hash = keccakFromString(el);
} else {
hash = el;
}
for (let i = 0; i < arr.length; i++) {
if (hash.equals(arr[i])) {
return i;
}
}
return -1;
}
bufDedup (elements) {
return elements.filter((el, idx) => {
return idx === 0 || !elements[idx - 1].equals(el);
});
}
bufArrToHexArr (arr) {
if (arr.some(el => !Buffer.isBuffer(el))) {
throw new Error('Array is not an array of buffers');
}
return arr.map(el => '0x' + el.toString('hex'));
}
sortAndConcat (...args) {
return Buffer.concat([...args].sort(Buffer.compare));
}
}
module.exports = {
MerkleTree,
};
......@@ -171,6 +171,133 @@ contract('ERC777', function (accounts) {
shouldBehaveLikeERC777InternalMint(to, operator, amount, data, operatorData);
});
});
describe('mint (internal extended)', function () {
const amount = new BN('5');
context('to anyone', function () {
beforeEach(async function () {
this.recipient = anyone;
});
context('with default operator', function () {
const operator = defaultOperatorA;
it('without requireReceptionAck', async function () {
await this.token.mintInternalExtended(
this.recipient,
amount,
data,
operatorData,
false,
{ from: operator },
);
});
it('with requireReceptionAck', async function () {
await this.token.mintInternalExtended(
this.recipient,
amount,
data,
operatorData,
true,
{ from: operator },
);
});
});
context('with non operator', function () {
const operator = newOperator;
it('without requireReceptionAck', async function () {
await this.token.mintInternalExtended(
this.recipient,
amount,
data,
operatorData,
false,
{ from: operator },
);
});
it('with requireReceptionAck', async function () {
await this.token.mintInternalExtended(
this.recipient,
amount,
data,
operatorData,
true,
{ from: operator },
);
});
});
});
context('to non ERC777TokensRecipient implementer', function () {
beforeEach(async function () {
this.tokensRecipientImplementer = await ERC777SenderRecipientMock.new();
this.recipient = this.tokensRecipientImplementer.address;
});
context('with default operator', function () {
const operator = defaultOperatorA;
it('without requireReceptionAck', async function () {
await this.token.mintInternalExtended(
this.recipient,
amount,
data,
operatorData,
false,
{ from: operator },
);
});
it('with requireReceptionAck', async function () {
await expectRevert(
this.token.mintInternalExtended(
this.recipient,
amount,
data,
operatorData,
true,
{ from: operator },
),
'ERC777: token recipient contract has no implementer for ERC777TokensRecipient',
);
});
});
context('with non operator', function () {
const operator = newOperator;
it('without requireReceptionAck', async function () {
await this.token.mintInternalExtended(
this.recipient,
amount,
data,
operatorData,
false,
{ from: operator },
);
});
it('with requireReceptionAck', async function () {
await expectRevert(
this.token.mintInternalExtended(
this.recipient,
amount,
data,
operatorData,
true,
{ from: operator },
),
'ERC777: token recipient contract has no implementer for ERC777TokensRecipient',
);
});
});
});
});
});
describe('operator management', function () {
......
require('@openzeppelin/test-helpers');
const { MerkleTree } = require('../../helpers/merkleTree.js');
const { keccakFromString, bufferToHex } = require('ethereumjs-util');
const { MerkleTree } = require('merkletreejs');
const keccak256 = require('keccak256');
const { expect } = require('chai');
......@@ -15,43 +15,43 @@ contract('MerkleProof', function (accounts) {
describe('verify', function () {
it('returns true for a valid Merkle proof', async function () {
const elements = ['a', 'b', 'c', 'd'];
const merkleTree = new MerkleTree(elements);
const merkleTree = new MerkleTree(elements, keccak256, { hashLeaves: true });
const root = merkleTree.getHexRoot();
const proof = merkleTree.getHexProof(elements[0]);
const leaf = keccak256(elements[0]);
const leaf = bufferToHex(keccakFromString(elements[0]));
const proof = merkleTree.getHexProof(leaf);
expect(await this.merkleProof.verify(proof, root, leaf)).to.equal(true);
});
it('returns false for an invalid Merkle proof', async function () {
const correctElements = ['a', 'b', 'c'];
const correctMerkleTree = new MerkleTree(correctElements);
const correctMerkleTree = new MerkleTree(correctElements, keccak256, { hashLeaves: true });
const correctRoot = correctMerkleTree.getHexRoot();
const correctLeaf = bufferToHex(keccakFromString(correctElements[0]));
const correctLeaf = keccak256(correctElements[0]);
const badElements = ['d', 'e', 'f'];
const badMerkleTree = new MerkleTree(badElements);
const badProof = badMerkleTree.getHexProof(badElements[0]);
const badProof = badMerkleTree.getHexProof(badElements[0], keccak256, { hashLeaves: true });
expect(await this.merkleProof.verify(badProof, correctRoot, correctLeaf)).to.equal(false);
});
it('returns false for a Merkle proof of invalid length', async function () {
const elements = ['a', 'b', 'c'];
const merkleTree = new MerkleTree(elements);
const merkleTree = new MerkleTree(elements, keccak256, { hashLeaves: true });
const root = merkleTree.getHexRoot();
const proof = merkleTree.getHexProof(elements[0]);
const badProof = proof.slice(0, proof.length - 5);
const leaf = keccak256(elements[0]);
const leaf = bufferToHex(keccakFromString(elements[0]));
const proof = merkleTree.getHexProof(leaf);
const badProof = proof.slice(0, proof.length - 5);
expect(await this.merkleProof.verify(badProof, root, leaf)).to.equal(false);
});
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment