-
Notifications
You must be signed in to change notification settings - Fork 19
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
fix(deps): CDB-2128 Address nonce errors #1004
base: develop
Are you sure you want to change the base?
Conversation
CDB-2128 Nonce errors in Clay AND QA CAS
Coordinator: dbrowning Investigator: dbrowning Clay CAS:
QA CAS:
|
52e6a23
to
15740b1
Compare
@@ -90,7 +90,7 @@ async function makeCeramicCore( | |||
anchorServiceUrl, | |||
// TODO CDB-2317 Remove `indexing` config when Ceramic Core allows that | |||
indexing: { | |||
db: "TODO", | |||
db: 'TODO', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prettier
} | ||
} | ||
} | ||
return function (req: Request, _res: Response, next: NextFunction) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prettier
@@ -15,9 +15,11 @@ export class CeramicAnchorServer extends Server { | |||
super(true) | |||
|
|||
this.app.set('trust proxy', true) | |||
this.app.use(bodyParser.raw({inflate: true, type: 'application/vnd.ipld.car'})) | |||
this.app.use(bodyParser.raw({ inflate: true, type: 'application/vnd.ipld.car' })) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prettier
@@ -39,7 +38,7 @@ export enum METRIC_NAMES { | |||
// Anchor Service (histograms) | |||
|
|||
// request that moves from ready -> processing | |||
READY_PROCESSING_MS = "ready_processing_ms", | |||
READY_PROCESSING_MS = 'ready_processing_ms', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prettier
a25249b
to
118fcec
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most important note is to avoid changing the format of the AnchorProof.
src/services/blockchain/ethereum/ethereum-blockchain-service.ts
Outdated
Show resolved
Hide resolved
const baseFee = maxFeePerGas.sub(maxPriorityFeePerGas) | ||
txData.maxFeePerGas = baseFee.add(nextPriorityFee) | ||
// must be greater than baseFee + maxPriorityFeePerGas | ||
txData.maxFeePerGas = addPadding(baseFee + nextPriorityFee) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why are we adding padding here if we already added padding in increaseGasPricePerAttempt
? Is this redundant?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On upgrading ethers, we run into underpriced transaction errors. I'm not sure if ethers changed something in the data they report back, but this is required to not have underpriced transaction errors.
if (attempt == 0 || previousGas == undefined) { | ||
return increaseEstimate | ||
): bigint { | ||
if (previousGas == undefined) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so this is a behavior change to the gas logic. Previously we would add an increasing amount of padding per attempt on top of the gas estimate we get from ethers. So the first attempt we'd just use the gas estimate from ethers directly. The second attempt we'd add 10% on top of the estimate, and the third time we'd add 20% on top of the estimate.
In this new version we're just always adding 10% on top of the estimate every time.
I don't feel strongly about which logic is better, but could we possibly separate the changes to our estimation logic from the code changes required to keep the same logic with upgrading to the new version of the ethers package, each into their own PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So there's a few things going on here
ceramic-anchor-service/src/services/blockchain/ethereum/ethereum-blockchain-service.ts
Line 237 in 27b0d25
const prevPriorityFee = BigInt(txData.maxPriorityFeePerGas || feeData.maxPriorityFeePerGas) - Once we have a previous (say x), we will pass that in, and multiply that by 10%.
x * 110% * 110%
is always more thanx * 120%
. Double check my code reading there, but I don't think we'll ever choose increaseEstimate. - As mentioned above, we run into underpriced transaction with the ethers upgrade. We would have to change the gas logic before the ethers upgrade if we want to split the PRs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like calculation logic is okay. Minor technical improvements could be handy.
And emulation of StaticJsonRpcProvider
! That could require some additional refactoring though: I am not sure if staticNetwork
would work without giving explicit network to the provider.
src/services/blockchain/ethereum/ethereum-blockchain-service.ts
Outdated
Show resolved
Hide resolved
@dbcfd Oh, lovely ethers: https://github.com/ceramicnetwork/ceramic-anchor-service/actions/runs/4670215624/jobs/8269672491#step:10:3555 At one point I thought maybe we could ditch ethers with viem altogether. |
viem author here! We are happy to help 😉 |
Per ethers-io/ethers.js#3812, nonce may be incorrectly incremented, which can produce off by one errors which we are currently seeing in QA and Clay
61cebb5
to
e724438
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
only actually important comment is making sure that we're serializing the date timestamp into the ipfs proof correctly
src/services/anchor-service.ts
Outdated
@@ -404,7 +404,7 @@ export class AnchorService { | |||
if (this.includeBlockInfoInAnchorProof) { | |||
ipfsAnchorProof = { | |||
blockNumber: tx.blockNumber, | |||
blockTimestamp: tx.blockTimestamp, | |||
blockTimestamp: tx.blockTimestamp.getUTCDate(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this returns the date of the month. Ie for a Data object created for today it returns 17
. I think you want the unix timestamp - I believe it's getTime()
that you want, but do be careful of units. I'm not sure if that returns seconds or millis, and we need to make sure whatever we use matches what we are currently doing.
The fact that this change didn't cause any tests to fail is actually a bit worrying, maybe see if you can add a test that would catch this?
Please also make sure to do some manual testing to ensure that the AnchorProofs created before and after this change look the same.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. Will fix that and check the test.
@@ -115,20 +126,50 @@ function handleTimeoutError(transactionTimeoutSecs: number): void { | |||
function make(config: Config): EthereumBlockchainService { | |||
const ethereum = config.blockchain.connectors.ethereum | |||
const { host, port, url } = ethereum.rpc | |||
let network | |||
try { | |||
network = Network.from(ethereum.network) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm... this is likely to be a breaking change as now the network option is required in the config even when using an infura url (which implies the network and so network wasn't previously required).
That's fine since we're the only ones running a CAS, just something to look out for when we go to deploy. We should double check we have the network set so things don't break on the upgrade
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Edit: I see it's not actually required as if its not set we fall back to the non-static behavior. But that could cause our infura endpoint to be pummeled with chainid calls and blow up our bill (this happened in the past) so we'll still want to be really careful here and make sure have the network flag set on all 3 envs, and monitor our infura account as this gets deployed to various envs.
provider = new ethers.JsonRpcProvider(hostPort) | ||
} | ||
} else if (network) { | ||
logger.imp(`Connecting ethereum to etherscan provider for network ${ethereum.network}`) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what do you mean by "etherscan" provider here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The options make it so only etherscan works, since that's we use for our tests. All other providers would need host/port, and preferably name.
alchemy: "-", | ||
ankr: "-", | ||
cloudflare: "-", | ||
etherscan: ethereum.account.privateKey, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what does this do?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removes all providers other than etherscan, and sets the private key for etherscan.
} else if (host && port) { | ||
logger.imp(`Connecting ethereum provider to host: ${host} and port ${port}`) | ||
provider = new ethers.providers.StaticJsonRpcProvider(`${host}:${port}`) | ||
const hostPort = `${host}:${port}` | ||
if (network) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe we can reduce code duplication by creating an empty options
object and adding the staticNetwork
field to it if network
is set. Then we can always just pass the options object to the JSONRpcProvider
constructor and not need the extra if(network)
conditional in both blocks?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will reduce the duplication.
Per ethers-io/ethers.js#3812, nonce may be incorrectly incremented, which can produce off by one errors which we are currently seeing in QA and Clay