Skip to content
This repository was archived by the owner on Jan 9, 2026. It is now read-only.

refactor initialization - #13

Draft
larskuhtz wants to merge 6 commits into
mainfrom
lars/refactor-initialization
Draft

refactor initialization#13
larskuhtz wants to merge 6 commits into
mainfrom
lars/refactor-initialization

Conversation

@larskuhtz

@larskuhtz larskuhtz commented Sep 8, 2025

Copy link
Copy Markdown
Contributor

[This PR currently builds on #12 and will be rebased when #12 is merged.]

It seems that calling selectFork in a constructor of a script or test leads to undefined and inconsistent behavior when switching between chains/forks.

This PR builds fixes this by refactoring the code such that initialization of chains is delayed until to the first call of switchChain.

Both usage patterns that are supported in the current version are also supported by this PR:

  1. Explicit setup and initialization of Chainweb chains in a setUp() of a script or test or
  2. Implicit setup and initialization of Chainweb by inheriting form ChainwebTest or ChainwebScript.

The code could be simplified considerably if we decided to drop the second approach to initialization and instead always require explicit setup via a setUp() function.

[The PR also includes code for mocking the SHA512/256 precompile that is available in the EVM sandbox and testnet networks. The precompile code needs more work and that is the reason why this PR is still in draft state. I plan to move that change into a separate PR]

@hswopeams

Copy link
Copy Markdown
Contributor

run forge fmt at the top level. This should take care of the current CI failures.
However, one of the tests at the top level fails, so the CI will still fail even after the formatting issues are fixed.

@hswopeams

Copy link
Copy Markdown
Contributor

There are a lot of public functions that should be external. I will clean that up in a separate PR after these all get merged.

@hswopeams

Copy link
Copy Markdown
Contributor

I noticed there aren't any changes to the Chainweb.t.sol file. This is the reason one of the tests if failing. Usually making code changes should means adding or modifying test cases.

@hswopeams
hswopeams self-requested a review September 8, 2025 10:19
Comment thread src/Chainweb.sol
* script that utilizes this contract. It is safe to call this functions on
* an uninitialized chain.
*/
function switchChain(uint256 chainId) public {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think we should mix switchChain with setup chains.
Because we use different ways for seting up chains in test and scripts mode. that was why I have created two setup functions.
Also deployChainWebChainIdContract and deploySha512_256Precompile should happen only for test because the script can run agains normal devnet not only anvil.
I will move fork creation to the setup function instead that I think can fix the nonce issue

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The chainweb chainid contract is deployed only in the tests. The function includes a check that deploys it only if it is not already present. Including this check seems the most robust approach to me.

deploySha512_256Precompile is tricky, because it is a precompile. Even if it is present on the forked chain, it wouldn't be available on the fork itself and still needs to be mocked.

However, as mentioned in the PR description, this PR is still a draft, and I intend to move the SHA512 precompile to a separate PR. So, anything related to the SHA512/256 will be removed from this PR (if we decide to move forward with it at all).

@hswopeams hswopeams left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

See comments for requested changes.

address constant SHA512_256_ADDRESS = 0x0000000000000000000000000000000000000420;


contract Sha512_256Precompile {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This contract name is not compliant with solidity standards. It should be Sha512256Precompile;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Sha512256Precompile is as name obfuscates that 512 and 256 are separate numbers. Would something like Sha512t256Precompile be acceptable?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Generally, as pointed out in the PR message, the SHA512_256 precompile addition is still in Draft mode and will be moved to a separate PR. The code is not yet final and subject to change.

I will incorporate any comments from this review in the follow up PR.

assembly {
let success := 0x00
let memPtr := 0x20
let ptr := add(memPtr, 0x60)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

In assembly, you need to be careful with memory management. It's easy to exceed or overwrite memory. You should check the max offset. If it exceeds the current free memory pointer (0x40), you should update it. Something like this after setting ptr:

// Calculate paddedLen: rounds up calldatasize() to the next multiple of 32 bytes (EVM word size).
// This ensures memory operations are word-aligned and safe for copying/returning data.
let paddedLen := shl(5, shr(5, add(calldatasize(), 31))) 

// Calculates the highest memory address the assembly code will use for writing or copying data.
let maxOffset := add(ptr, add(0x44, paddedLen))
let freePtr := mload(0x40)
if gt(maxOffset, freePtr) {
    mstore(0x40, maxOffset)
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yeah, the assembly needs more work. It may actually go away completely, because it does not seem to fix the issues during foundry simulation runs that I had hoped it would resolve. If it turns out that I can't get it to work without --skip-simulation we may as well implement the contract in plain solidity, which works fine in the test and dry-run environments.

fallback(bytes calldata) external returns (bytes memory) {
assembly {
let success := 0x00
let memPtr := 0x20

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Avoid using magic numbers and use constants for known values as well. See some examples below.

let MEMORY_START := 0x20 // Solidity memory starts at 0x20
let HEADER_SIZE := 0x60 // Size reserved for header, which this appears to be
let DATA_OFFSET := 0x44 // Offset for calldata after header

let memPtr := MEMORY_START
let ptr := add(memPtr, HEADER_SIZE)

}

// implicitely abi decode the return value
return(add(memPtr, 0x40), 0x20)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Are you sure the precompile will always return 96 bytes of data? If not, the buffer could contain data from previous calls. The pointer should be initialized before the delegate call on line 43 like this:

mstore(memPtr, 0x00) // Zero out first 32 bytes of output buffer
mstore(add(memPtr, 0x20), 0x00) // Zero out second 32 bytes of output buffer
mstore(add(memPtr, 0x40), 0x00) // Zero out third 32 bytes of output buffer

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That's a good point. But, yes, int this case it always returns the exact same amount of data.

}
}

function sha512_256(bytes memory input) public pure returns (bytes memory result) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This function name is not compliant with solidity naming standards. It should be sha512256. The function name cannot have an underscore in the name.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why is this function returning bytes when the library returns bytes32? Hashes are usually bytes32 in solidity.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah, should be bytes32 now. Originally, this was to simulate the return type of the recompile, but it's not use like this any more.

Comment thread lib/sha512/Sha2Ext.sol
}
}

function sha384(bytes memory message) internal pure returns (bytes32, bytes16) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Consider some input checks just to make sure people don't cause themselves problems when testing:
require(message.length <= 4096, "Input too large: may exceed block gas limit");

This would allow 4 KB of input. This could be added to sha512 and sha512_256 as well.

Comment thread lib/sha512/Sha2Ext.sol
);
}

function padMessage(bytes memory message) internal pure returns (bytes memory) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It would be good to comment on why this particular padding functionality is used. I assume it mimics sha-2.

@larskuhtz larskuhtz Sep 8, 2025

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yes, this is part of the SHA2 implementation. This code is from the original implementation.

I think, it would be better to not copy-and-past the Sha2Ext code, but use it as a dependency.

Comment thread src/Chainweb.sol
chainIds[i] = i + _chainwebChainIdOffset;
}
return chainIds;
function deploySha512_256Precompile() internal {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This function name does not conform to solidity standards. It should be deploySha512256Precompile. The name cannot contain and underscore.

Comment thread src/Chainweb.sol Outdated
Comment thread src/Chainweb.sol Outdated
larskuhtz and others added 2 commits September 8, 2025 15:44
Co-authored-by: Heather <42716568+hswopeams@users.noreply.github.com>
Co-authored-by: Heather <42716568+hswopeams@users.noreply.github.com>
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants