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

Add gas price on initialization of Foreign erc-to-erc mode #91

Merged
merged 5 commits into from
Oct 17, 2018
Merged
Show file tree
Hide file tree
Changes from 2 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
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,18 @@ contract ForeignBridgeErcToErc is BasicBridge, BasicForeignBridge {
function initialize(
address _validatorContract,
address _erc20token,
uint256 _requiredBlockConfirmations
uint256 _requiredBlockConfirmations,
uint256 _gasPrice
) public returns(bool) {
require(!isInitialized(), "already initialized");
require(_validatorContract != address(0), "address cannot be empty");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's be consistent and return revert reason in all require in the initialize method.

My suggestion is to use values as numbers instead of strings since it should reduce the gas usage. You could do simple experiment in order to determine how gas consumption is reduced if a number will be used in require(_validatorContract != address(0), "address cannot be empty"); for example. You need to consider how the gas usage differs for both the contract deployment and the method invocation). If the difference is significant let's use numbers.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ran some tests to determine gas consumption, I deployed and call initialize method with the following variations on Kovan network:

  • No reason: require(_validatorContract != address(0));
  • Reason with number: require(_validatorContract != address(0), "1");
  • Reason with text: require(_validatorContract != address(0), "address cannot be empty");

Results:

Deploy:

  • No reason - Reason with number: + 22701
  • Reason with number - Reason with text: + 1344

Initialize method failed (called with address(0) for validatorContract parameter):

  • No reason - Reason with number: + 102
  • Reason with number - Reason with text: + 0

Initialize method success:

  • No reason - Reason with number: + 0
  • Reason with number - Reason with text: + 0

From the results, it seems that avoiding using reason on require saves a lot of gas on deployment and very little on calling the method. Changing a full text to a number have some impact on deployment I guess depending on the string length.

The other contracts doesn't use reason on require, except foreign erc20-to-native which was built based on this contract. Also it seems that reason string is not yet fully supported on toolings (web3/web3.js#1707) so I think that it could be a good idea to remove the reason string from these two contracts. What do you think @akolotov ?

Details from the tests:

require(_validatorContract != address(0), "address cannot be empty");

Deploy
https://kovan.etherscan.io/tx/0x42cb3448e31c2a873b77869759ddc92698cf9d89004e3d989a6952eb392f9d07
Gas Used By Transaction:2530163

Success initialization
https://kovan.etherscan.io/tx/0xe9d150b24edeb3007158d1997b77f19c690ab005c2e1e97deb0638e8799e581a
Gas Used By Transaction:152124

Failed initialization with zero_address on validators
https://kovan.etherscan.io/tx/0x1edcb0d738807b049df726c00ecb53b85b6d12292f9d2c83f252489e3e845f03
Gas Used By Transaction:26487


require(_validatorContract != address(0), "1");

Deploy
https://kovan.etherscan.io/tx/0x44690aa21eca360a28b85b6bacae44e8f11a603d28859f6115d70232df461295
Gas Used By Transaction:2528819

Success initialization
https://kovan.etherscan.io/tx/0x31b57c073d60820905a6b5db1e01a6cf28106f81a57862e23349d45df0fd2057
Gas Used By Transaction:152124

Failed initialization with zero_address on validators
https://kovan.etherscan.io/tx/0x74fbff2f43ba2d5dfe3b09e4c100265a15612d7bc6c20774b5c0a798d168f167
Gas Used By Transaction:26487


require(_validatorContract != address(0));

Deploy
https://kovan.etherscan.io/tx/0xbd67b40a0fba02aeac507535c8a33b61530733d56cde4233bc09b3b2532fba63
Gas Used By Transaction:2506118

Success initialization
https://kovan.etherscan.io/tx/0x539066f85a9a8d392f61bf15142d3322d49d669ad5bf336bc3fc1060babd846e
Gas Used By Transaction:152124

Failed initialization with zero_address on validators
https://kovan.etherscan.io/tx/0xc7f62f1abdaff1362a1baf282ecdc00fdd992ccb6b19b66580bd08522ec8d38e
Gas Used By Transaction:26385

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great! I vote for removing the revert reason.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

require(_requiredBlockConfirmations != 0, "requiredBlockConfirmations cannot be 0");
require(_gasPrice > 0);
addressStorage[keccak256(abi.encodePacked("validatorContract"))] = _validatorContract;
setErc20token(_erc20token);
uintStorage[keccak256(abi.encodePacked("deployedAtBlock"))] = block.number;
uintStorage[keccak256(abi.encodePacked("requiredBlockConfirmations"))] = _requiredBlockConfirmations;
uintStorage[keccak256(abi.encodePacked("gasPrice"))] = _gasPrice;
setInitialize(true);
return isInitialized();
}
Expand Down
6 changes: 4 additions & 2 deletions deploy/src/erc_to_erc/foreign.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ const {
FOREIGN_UPGRADEABLE_ADMIN_VALIDATORS,
FOREIGN_UPGRADEABLE_ADMIN_BRIDGE,
FOREIGN_REQUIRED_BLOCK_CONFIRMATIONS,
ERC20_TOKEN_ADDRESS
ERC20_TOKEN_ADDRESS,
FOREIGN_GAS_PRICE
} = env

const DEPLOYMENT_ACCOUNT_ADDRESS = privateKeyToAddress(DEPLOYMENT_ACCOUNT_PRIVATE_KEY)
Expand Down Expand Up @@ -142,7 +143,8 @@ async function deployForeign() {
.initialize(
storageValidatorsForeign.options.address,
ERC20_TOKEN_ADDRESS,
FOREIGN_REQUIRED_BLOCK_CONFIRMATIONS
FOREIGN_REQUIRED_BLOCK_CONFIRMATIONS,
FOREIGN_GAS_PRICE
)
.encodeABI({ from: DEPLOYMENT_ACCOUNT_ADDRESS })
const txInitializeBridge = await sendRawTxForeign({
Expand Down
20 changes: 13 additions & 7 deletions test/erc_to_erc/foreign_bridge.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ const BridgeValidators = artifacts.require("BridgeValidators.sol");
const EternalStorageProxy = artifacts.require("EternalStorageProxy.sol");

const ERC677BridgeToken = artifacts.require("ERC677BridgeToken.sol");
const {ERROR_MSG, ZERO_ADDRESS} = require('../setup');
const {ERROR_MSG, ZERO_ADDRESS, INVALID_ARGUMENTS} = require('../setup');
const {createMessage, sign, signatureToVRS} = require('../helpers/helpers');
const halfEther = web3.toBigNumber(web3.toWei(0.5, "ether"));
const requireBlockConfirmations = 8;
const gasPrice = web3.toWei('1', 'gwei')

contract('ForeignBridge_ERC20_to_ERC20', async (accounts) => {
let validatorContract, authorities, owner, token;
Expand All @@ -28,14 +29,19 @@ contract('ForeignBridge_ERC20_to_ERC20', async (accounts) => {
'0'.should.be.bignumber.equal(await foreignBridge.deployedAtBlock())
false.should.be.equal(await foreignBridge.isInitialized())
'0'.should.be.bignumber.equal(await foreignBridge.requiredBlockConfirmations())
await foreignBridge.initialize(validatorContract.address, token.address, requireBlockConfirmations);

await foreignBridge.initialize(validatorContract.address, token.address, requireBlockConfirmations).should.be.rejectedWith(INVALID_ARGUMENTS);
akolotov marked this conversation as resolved.
Show resolved Hide resolved

await foreignBridge.initialize(validatorContract.address, token.address, requireBlockConfirmations, gasPrice);
akolotov marked this conversation as resolved.
Show resolved Hide resolved

token.address.should.be.equal(await foreignBridge.erc20token());
true.should.be.equal(await foreignBridge.isInitialized())
validatorContract.address.should.be.equal(await foreignBridge.validatorContract());
token.address.should.be.equal(await foreignBridge.erc20token());
(await foreignBridge.deployedAtBlock()).should.be.bignumber.above(0);
requireBlockConfirmations.should.be.bignumber.equal(await foreignBridge.requiredBlockConfirmations())
const contractGasPrice = await foreignBridge.gasPrice()
contractGasPrice.should.be.bignumber.equal(gasPrice)
const bridgeMode = '0xba4690f5' // 4 bytes of keccak256('erc-to-erc-core')
const mode = await foreignBridge.getBridgeMode();
mode.should.be.equal(bridgeMode)
Expand All @@ -50,7 +56,7 @@ contract('ForeignBridge_ERC20_to_ERC20', async (accounts) => {
beforeEach(async () => {
foreignBridge = await ForeignBridge.new()
token = await ERC677BridgeToken.new("Some ERC20", "RSZT", 18);
await foreignBridge.initialize(validatorContract.address, token.address, requireBlockConfirmations);
await foreignBridge.initialize(validatorContract.address, token.address, requireBlockConfirmations, gasPrice);
await token.mint(foreignBridge.address,value);
})
it('should allow to executeSignatures', async () => {
Expand Down Expand Up @@ -134,7 +140,7 @@ contract('ForeignBridge_ERC20_to_ERC20', async (accounts) => {
await multisigValidatorContract.initialize(2, twoAuthorities, ownerOfValidatorContract, {from: ownerOfValidatorContract})
foreignBridgeWithMultiSignatures = await ForeignBridge.new()
const oneEther = web3.toBigNumber(web3.toWei(1, "ether"));
await foreignBridgeWithMultiSignatures.initialize(multisigValidatorContract.address, token.address, requireBlockConfirmations, {from: ownerOfValidatorContract});
await foreignBridgeWithMultiSignatures.initialize(multisigValidatorContract.address, token.address, requireBlockConfirmations, gasPrice, {from: ownerOfValidatorContract});
await token.mint(foreignBridgeWithMultiSignatures.address,value);
})
it('withdraw should fail if not enough signatures are provided', async () => {
Expand Down Expand Up @@ -191,7 +197,7 @@ contract('ForeignBridge_ERC20_to_ERC20', async (accounts) => {
await foreignBridgeProxy.upgradeTo('1', foreignBridgeImpl.address).should.be.fulfilled;

foreignBridgeProxy = await ForeignBridge.at(foreignBridgeProxy.address);
await foreignBridgeProxy.initialize(validatorsProxy.address, token.address, requireBlockConfirmations)
await foreignBridgeProxy.initialize(validatorsProxy.address, token.address, requireBlockConfirmations, gasPrice)

// Deploy V2
let foreignImplV2 = await ForeignBridgeV2.new();
Expand All @@ -211,7 +217,7 @@ contract('ForeignBridge_ERC20_to_ERC20', async (accounts) => {
let storageProxy = await EternalStorageProxy.new().should.be.fulfilled;
let foreignBridge = await ForeignBridge.new();
let data = foreignBridge.initialize.request(
fakeValidatorsAddress, fakeTokenAddress, requireBlockConfirmations).params[0].data
fakeValidatorsAddress, fakeTokenAddress, requireBlockConfirmations, gasPrice).params[0].data
await storageProxy.upgradeToAndCall('1', foreignBridge.address, data).should.be.fulfilled;
let finalContract = await ForeignBridge.at(storageProxy.address);
true.should.be.equal(await finalContract.isInitialized());
Expand All @@ -224,7 +230,7 @@ contract('ForeignBridge_ERC20_to_ERC20', async (accounts) => {
const owner = accounts[0];
token = await ERC677BridgeToken.new("Some ERC20", "RSZT", 18);
foreignBridge = await ForeignBridge.new();
await foreignBridge.initialize(validatorContract.address, token.address, requireBlockConfirmations);
await foreignBridge.initialize(validatorContract.address, token.address, requireBlockConfirmations, gasPrice);

let tokenSecond = await ERC677BridgeToken.new("Roman Token", "RST", 18);

Expand Down
1 change: 1 addition & 0 deletions test/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ require('chai')
exports.ERROR_MSG = 'VM Exception while processing transaction: revert';
exports.ERROR_MSG_OPCODE = 'VM Exception while processing transaction: invalid opcode';
exports.ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'
exports.INVALID_ARGUMENTS = 'Invalid number of arguments to Solidity function'