Commit 093ec7eb authored by smartcontracts's avatar smartcontracts Committed by GitHub

feat: CanonicalTransactionChain gas optimizations (#228)

* Final gas optimizations for CTC

* delete and re-init

* Fix issue with tests failing

* Use encoding/decoding from core-utils

* dev: Use eth-optimism/core-utils where applicable (#281)

* Use core-utils where applicable

* Further reduce utils

* Update ovmCREATEEOA.spec.ts
Co-authored-by: default avatarMark Tyneway <mark.tyneway@gmail.com>

* remove unused import

* Revert "remove unused import"

This reverts commit 1df87926e7a3ca8ebdf7448ad5f0395fe7766ff7.

* actually fix imports

* A few more comments

* address PR feedback - add comments, renaming vars

* Optimization: Conditional Monotonicity Checking (#307)

* trying it

* cleanup

* remove duplicate comment

* fix docstring

* fix var name

* missing period
Co-authored-by: default avatarBen Jones <ben@pseudonym.party>
Co-authored-by: default avatarMaurelian <maurelian@protonmail.ch>
Co-authored-by: default avatarMark Tyneway <mark.tyneway@gmail.com>
Co-authored-by: default avatarKevin Ho <kevinjho1996@gmail.com>
parent 61b227d3
......@@ -187,6 +187,23 @@ contract OVM_ChainStorageContainer is iOVM_ChainStorageContainer, Lib_AddressRes
return buffer.get(uint40(_index));
}
/**
* @inheritdoc iOVM_ChainStorageContainer
*/
function get2(
uint256 _index
)
override
public
view
returns (
bytes32,
bytes32
)
{
return buffer.get2(uint40(_index));
}
/**
* @inheritdoc iOVM_ChainStorageContainer
*/
......
......@@ -104,6 +104,22 @@ interface iOVM_ChainStorageContainer {
bytes32
);
/**
* Retrieves two consecutive objects from the container.
* @param _index Index of the particular objects to access.
* @return 32 byte object value at index `_index`.
* @return 32 byte object value at index `_index + 1`.
*/
function get2(
uint256 _index
)
external
view
returns (
bytes32,
bytes32
);
/**
* Removes all objects after and including a given index.
* @param _index Object index to delete from.
......
......@@ -81,7 +81,7 @@ library Lib_OVMCodec {
}
struct QueueElement {
bytes32 queueRoot;
bytes32 transactionHash;
uint40 timestamp;
uint40 blockNumber;
}
......
......@@ -211,6 +211,30 @@ library Lib_RingBuffer {
}
}
/**
* Retrieves two consecutive elements from the buffer.
* @param _self Buffer to access.
* @param _index Element index to retrieve.
* @return Value of the element at index `_index`.
* @return Value of the element at index `_index + 1`.
*/
function get2(
RingBuffer storage _self,
uint256 _index
)
internal
view
returns (
bytes32,
bytes32
)
{
return (
_self.get(_index),
_self.get(_index + 1)
);
}
/**
* Deletes all elements after (and including) a given index.
* @param _self Buffer to access.
......
......@@ -36,6 +36,8 @@
"dependencies": {
"@eth-optimism/core-utils": "^0.1.8",
"@eth-optimism/dev": "^1.1.1",
"@eth-optimism/core-utils": "^0.1.5",
"@eth-optimism/solc": "^0.6.12-alpha.1",
"@ethersproject/abstract-provider": "^5.0.8",
"@ethersproject/contracts": "^5.0.5",
"@ethersproject/hardware-wallets": "^5.0.8",
......
/* External Imports */
import { ethers } from 'hardhat'
import { Signer, ContractFactory, Contract } from 'ethers'
import { smockit, MockContract } from '@eth-optimism/smock'
import {
AppendSequencerBatchParams,
encodeAppendSequencerBatch,
} from '@eth-optimism/core-utils'
import { TransactionResponse } from '@ethersproject/abstract-provider'
import { keccak256 } from 'ethers/lib/utils'
import _ from 'lodash'
/* Internal Imports */
import {
makeAddressManager,
setProxyTarget,
FORCE_INCLUSION_PERIOD_SECONDS,
FORCE_INCLUSION_PERIOD_BLOCKS,
getEthTime,
getNextBlockNumber,
} from '../../../helpers'
// Still have some duplication from OVM_CanonicalTransactionChain.spec.ts, but it's so minimal that
// this is probably cleaner for now. Particularly since we're planning to move all of this out into
// core-utils soon anyway.
const DECOMPRESSION_ADDRESS = '0x4200000000000000000000000000000000000008'
const MAX_GAS_LIMIT = 8_000_000
const appendSequencerBatch = async (
OVM_CanonicalTransactionChain: Contract,
batch: AppendSequencerBatchParams
): Promise<TransactionResponse> => {
const methodId = keccak256(Buffer.from('appendSequencerBatch()')).slice(2, 10)
const calldata = encodeAppendSequencerBatch(batch)
return OVM_CanonicalTransactionChain.signer.sendTransaction({
to: OVM_CanonicalTransactionChain.address,
data: '0x' + methodId + calldata,
})
}
describe('[GAS BENCHMARK] OVM_CanonicalTransactionChain', () => {
let sequencer: Signer
before(async () => {
;[sequencer] = await ethers.getSigners()
})
let AddressManager: Contract
let Mock__OVM_ExecutionManager: MockContract
let Mock__OVM_StateCommitmentChain: MockContract
before(async () => {
AddressManager = await makeAddressManager()
await AddressManager.setAddress(
'OVM_Sequencer',
await sequencer.getAddress()
)
await AddressManager.setAddress(
'OVM_DecompressionPrecompileAddress',
DECOMPRESSION_ADDRESS
)
Mock__OVM_ExecutionManager = await smockit(
await ethers.getContractFactory('OVM_ExecutionManager')
)
Mock__OVM_StateCommitmentChain = await smockit(
await ethers.getContractFactory('OVM_StateCommitmentChain')
)
await setProxyTarget(
AddressManager,
'OVM_ExecutionManager',
Mock__OVM_ExecutionManager
)
await setProxyTarget(
AddressManager,
'OVM_StateCommitmentChain',
Mock__OVM_StateCommitmentChain
)
Mock__OVM_ExecutionManager.smocked.getMaxTransactionGasLimit.will.return.with(
MAX_GAS_LIMIT
)
})
let Factory__OVM_CanonicalTransactionChain: ContractFactory
let Factory__OVM_ChainStorageContainer: ContractFactory
before(async () => {
Factory__OVM_CanonicalTransactionChain = await ethers.getContractFactory(
'OVM_CanonicalTransactionChain'
)
Factory__OVM_ChainStorageContainer = await ethers.getContractFactory(
'OVM_ChainStorageContainer'
)
})
let OVM_CanonicalTransactionChain: Contract
beforeEach(async () => {
OVM_CanonicalTransactionChain = await Factory__OVM_CanonicalTransactionChain.deploy(
AddressManager.address,
FORCE_INCLUSION_PERIOD_SECONDS,
FORCE_INCLUSION_PERIOD_BLOCKS,
MAX_GAS_LIMIT
)
const batches = await Factory__OVM_ChainStorageContainer.deploy(
AddressManager.address,
'OVM_CanonicalTransactionChain'
)
const queue = await Factory__OVM_ChainStorageContainer.deploy(
AddressManager.address,
'OVM_CanonicalTransactionChain'
)
await AddressManager.setAddress(
'OVM_ChainStorageContainer:CTC:batches',
batches.address
)
await AddressManager.setAddress(
'OVM_ChainStorageContainer:CTC:queue',
queue.address
)
await AddressManager.setAddress(
'OVM_CanonicalTransactionChain',
OVM_CanonicalTransactionChain.address
)
})
describe('appendSequencerBatch', () => {
beforeEach(() => {
OVM_CanonicalTransactionChain = OVM_CanonicalTransactionChain.connect(
sequencer
)
})
it('200 transactions in a single context', async () => {
console.log(`Benchmark: 200 transactions in a single context.`)
const timestamp = (await getEthTime(ethers.provider)) - 100
const blockNumber = await getNextBlockNumber(ethers.provider)
const transactionTemplate = '0x' + '11'.repeat(400)
const transactions = []
const numTxs = 200
for (let i = 0; i < numTxs; i++) {
transactions.push(transactionTemplate)
}
const fixedCalldataCost =
(transactionTemplate.slice(2).length / 2) * 16 * numTxs
const res = await appendSequencerBatch(OVM_CanonicalTransactionChain, {
shouldStartAtBatch: 0,
totalElementsToAppend: numTxs,
contexts: [
{
numSequencedTransactions: numTxs,
numSubsequentQueueTransactions: 0,
timestamp,
blockNumber,
},
],
transactions,
})
const receipt = await res.wait()
console.log('Benchmark complete.')
console.log('Gas used:', receipt.gasUsed.toNumber())
console.log('Fixed calldata cost:', fixedCalldataCost)
console.log(
'Non-calldata overhead gas cost per transaction:',
(receipt.gasUsed.toNumber() - fixedCalldataCost) / numTxs
)
}).timeout(100000000)
it('200 transactions in 200 contexts', async () => {
console.log(`Benchmark: 200 transactions in 200 contexts.`)
const timestamp = (await getEthTime(ethers.provider)) - 100
const blockNumber = await getNextBlockNumber(ethers.provider)
const transactionTemplate = '0x' + '11'.repeat(400)
const transactions = []
const numTxs = 200
for (let i = 0; i < numTxs; i++) {
transactions.push(transactionTemplate)
}
const fixedCalldataCost =
(transactionTemplate.slice(2).length / 2) * 16 * numTxs
const res = await appendSequencerBatch(OVM_CanonicalTransactionChain, {
shouldStartAtBatch: 0,
totalElementsToAppend: numTxs,
contexts: [...Array(numTxs)].map(() => {
return {
numSequencedTransactions: 1,
numSubsequentQueueTransactions: 0,
timestamp,
blockNumber,
}
}),
transactions,
})
const receipt = await res.wait()
console.log('Benchmark complete.')
console.log('Gas used:', receipt.gasUsed.toNumber())
console.log('Fixed calldata cost:', fixedCalldataCost)
console.log(
'Non-calldata overhead gas cost per transaction:',
(receipt.gasUsed.toNumber() - fixedCalldataCost) / numTxs
)
}).timeout(100000000)
})
})
......@@ -51,15 +51,20 @@
ganache-core "^2.12.1"
glob "^7.1.6"
"@eth-optimism/core-utils@^0.1.8":
version "0.1.8"
resolved "https://registry.yarnpkg.com/@eth-optimism/core-utils/-/core-utils-0.1.8.tgz#bf9ac0e537e99a56b2f03af525d317aac5933c41"
integrity sha512-IdRDkyVWmrmbA4P0hvupS+uLSQ1qwG1bWtdvwqeG1+wfDMC7nfZyjdeRWrRZbFvVs/hKk20LERC7L5/lJEo1hA==
"@eth-optimism/core-utils@^0.1.5":
version "0.1.5"
resolved "https://registry.yarnpkg.com/@eth-optimism/core-utils/-/core-utils-0.1.5.tgz#90b3f3d14b25abd8a210d78678d36e54f0c841f4"
integrity sha512-DLcHKFXcfoVb0yizfHeDGvgfHYb5DRRMnZyXLXIN0HNrAeavQEjgRFLOo/qy+xNjGI99LKSvyJtHP/C9ImJDag==
dependencies:
"@ethersproject/abstract-provider" "^5.0.9"
colors "^1.4.0"
debug "^4.3.1"
ethers "^5.0.31"
axios "^0.21.1"
body-parser "^1.19.0"
debug "^4.1.1"
dotenv "^8.2.0"
ethereumjs-util "^6.2.0"
ethers "^5.0.24"
express "^4.17.1"
uuid "^3.3.3"
"@eth-optimism/dev@^1.1.1":
version "1.1.1"
......@@ -133,6 +138,21 @@
fs-extra "^9.0.1"
hardhat "^2.0.3"
"@eth-optimism/solc@^0.6.12-alpha.1":
version "0.6.12-alpha.1"
resolved "https://registry.yarnpkg.com/@eth-optimism/solc/-/solc-0.6.12-alpha.1.tgz#041876f83b34c6afe2f19dfe9626568df6ed8590"
integrity sha512-Ky73mo+2iNJs/VTaT751nMeZ7hXns0TBAlffTOxIOsScjAZ/zi/KWsDUo3r89aV2JKXcYAU/bLidxF40MVJeUw==
dependencies:
command-exists "^1.2.8"
commander "3.0.2"
follow-redirects "^1.12.1"
fs-extra "^0.30.0"
js-sha3 "0.8.0"
memorystream "^0.3.1"
require-from-string "^2.0.0"
semver "^5.5.0"
tmp "0.0.33"
"@ethereum-waffle/chai@^3.0.0", "@ethereum-waffle/chai@^3.2.2":
version "3.2.2"
resolved "https://registry.yarnpkg.com/@ethereum-waffle/chai/-/chai-3.2.2.tgz#33a349688386c9a8fdc4da5baea329036b9fe75e"
......@@ -1449,6 +1469,13 @@ aws4@^1.8.0:
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59"
integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==
axios@^0.21.1:
version "0.21.1"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
dependencies:
follow-redirects "^1.10.0"
babel-code-frame@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
......@@ -2094,7 +2121,7 @@ bn.js@^5.0.0, bn.js@^5.1.1, bn.js@^5.1.2, bn.js@^5.1.3:
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.3.tgz#beca005408f642ebebea80b042b4d18d2ac0ee6b"
integrity sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ==
body-parser@1.19.0, body-parser@^1.16.0:
body-parser@1.19.0, body-parser@^1.16.0, body-parser@^1.19.0:
version "1.19.0"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a"
integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==
......@@ -2633,11 +2660,6 @@ color-name@~1.1.4:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
colors@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==
combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
......@@ -3143,6 +3165,11 @@ domutils@^2.4.3, domutils@^2.4.4:
domelementtype "^2.0.1"
domhandler "^4.0.0"
dotenv@^8.2.0:
version "8.2.0"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a"
integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==
dotignore@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/dotignore/-/dotignore-0.1.2.tgz#f942f2200d28c3a76fbdd6f0ee9f3257c8a2e905"
......@@ -3821,7 +3848,7 @@ ethers@^4.0.0-beta.1, ethers@^4.0.32:
uuid "2.0.1"
xmlhttprequest "1.8.0"
ethers@^5, ethers@^5.0.0, ethers@^5.0.1, ethers@^5.0.14, ethers@^5.0.2, ethers@^5.0.25, ethers@^5.0.31:
ethers@^5, ethers@^5.0.0, ethers@^5.0.1, ethers@^5.0.14, ethers@^5.0.2, ethers@^5.0.24, ethers@^5.0.25, ethers@^5.0.31:
version "5.0.31"
resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.0.31.tgz#60e3b1425864fe5d2babc147ede01be8382a7d2a"
integrity sha512-zpq0YbNFLFn+t+ibS8UkVWFeK5w6rVMSvbSHrHAQslfazovLnQ/mc2gdN5+6P45/k8fPgHrfHrYvJ4XvyK/S1A==
......@@ -3924,7 +3951,7 @@ expand-template@^2.0.3:
resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c"
integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==
express@^4.14.0:
express@^4.14.0, express@^4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134"
integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==
......@@ -4138,7 +4165,7 @@ flow-stoplight@^1.0.0:
resolved "https://registry.yarnpkg.com/flow-stoplight/-/flow-stoplight-1.0.0.tgz#4a292c5bcff8b39fa6cc0cb1a853d86f27eeff7b"
integrity sha1-SiksW8/4s5+mzAyxqFPYbyfu/3s=
follow-redirects@^1.12.1:
follow-redirects@^1.10.0, follow-redirects@^1.12.1:
version "1.13.2"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.2.tgz#dd73c8effc12728ba5cf4259d760ea5fb83e3147"
integrity sha512-6mPTgLxYm3r6Bkkg0vNM0HTjfGrOEtsfbhagQvbxDEsEkpNhw582upBaoRZylzen6krEmxXJgt9Ju6HiI4O7BA==
......@@ -8426,7 +8453,7 @@ uuid@3.3.2:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==
uuid@^3.3.2:
uuid@^3.3.2, uuid@^3.3.3:
version "3.4.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
......
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