Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
68 changes: 54 additions & 14 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,22 +1,62 @@
.PHONY: build-network test clean stop status lint
.PHONY: build-network test test-all suites clean stop status lint

# Which suite(s) under tests/ to run. Empty = every suite.
# make test SUITE=eip1153
# make test SUITE="eip1153 eip2935"
# make test-eip1153 (shorthand)
SUITE ?=
# Optional -run regexp: make test SUITE=eip1153 RUN=TestTransientStorage
RUN ?=
# -p 1: every suite shares one sender account, parallel packages fight over nonces.
TEST_FLAGS ?= -v -count=1 -p 1 -timeout 20m

ifeq ($(strip $(SUITE)),)
TEST_PKGS := ./...
else
TEST_PKGS := $(patsubst %,./%/,$(strip $(SUITE)))
endif

ifneq ($(strip $(RUN)),)
RUN_FLAG := -run '$(RUN)'
endif

# $(call run_tests,<go test package args>) — boot network, run tests, always tear down.
define run_tests
@/tmp/interstellar-network start & \
START_PID=$$! ; \
trap '/tmp/interstellar-network stop 2>/dev/null || true; kill $$START_PID 2>/dev/null || true' EXIT ; \
if ! NODE_URL=$$(/tmp/interstellar-network node-url); then \
echo "ERROR: network never became ready (node-url timed out or failed) — aborting instead of reporting a false pass"; exit 1; \
fi ; \
if ! NODE_P2P_PORT=$$(/tmp/interstellar-network node-p2p-port); then \
echo "ERROR: network never became ready (node-p2p-port timed out or failed) — aborting instead of reporting a false pass"; exit 1; \
fi ; \
if [ -z "$$NODE_URL" ] || [ -z "$$NODE_P2P_PORT" ]; then \
echo "ERROR: empty node connection details — refusing to run e2e tests against no network"; exit 1; \
fi ; \
cd tests && NODE_URL=$$NODE_URL NODE_P2P_PORT=$$NODE_P2P_PORT go test $(TEST_FLAGS) $(RUN_FLAG) $(1)
endef

build-network:
cd network && go build -o /tmp/interstellar-network github.com/vechain/interstellar-e2e/network && cd ..

test: build-network
@/tmp/interstellar-network start & \
START_PID=$$! ; \
trap '/tmp/interstellar-network stop 2>/dev/null || true; kill $$START_PID 2>/dev/null || true' EXIT ; \
if ! NODE_URL=$$(/tmp/interstellar-network node-url); then \
echo "ERROR: network never became ready (node-url timed out or failed) — aborting instead of reporting a false pass"; exit 1; \
fi ; \
if ! NODE_P2P_PORT=$$(/tmp/interstellar-network node-p2p-port); then \
echo "ERROR: network never became ready (node-p2p-port timed out or failed) — aborting instead of reporting a false pass"; exit 1; \
fi ; \
if [ -z "$$NODE_URL" ] || [ -z "$$NODE_P2P_PORT" ]; then \
echo "ERROR: empty node connection details — refusing to run e2e tests against no network"; exit 1; \
fi ; \
cd tests && NODE_URL=$$NODE_URL NODE_P2P_PORT=$$NODE_P2P_PORT go test -v -count=1 -timeout 20m ./...
@for s in $(strip $(SUITE)); do \
if [ ! -d "tests/$$s" ]; then \
echo "ERROR: no such suite 'tests/$$s'. Available:"; $(MAKE) -s suites; exit 1; \
fi ; \
done
$(call run_tests,$(TEST_PKGS))

# make test-eip1153 == make test SUITE=eip1153
test-%:
@$(MAKE) test SUITE=$*

test-all:
@$(MAKE) test SUITE=

suites:
@find tests -mindepth 1 -maxdepth 1 -type d ! -name helper -exec basename {} \; | sort | sed 's/^/ /'

stop:
/tmp/interstellar-network stop 2>/dev/null || true
Expand Down
2 changes: 1 addition & 1 deletion network/setup/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (

const (
defaultThorRepo = "https://github.com/vechain/thor"
defaultThorBranch = "evm-upgrades"
defaultThorBranch = "release/interstellar"
)

// BuildNetwork constructs the 3-node network configuration for interstellar testing.
Expand Down
167 changes: 167 additions & 0 deletions tests/eip1153/contracts/TransientProbe.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract TransientProbe {
error Boom();

event Loaded(uint256 indexed key, uint256 value);


function write(uint256 k, uint256 v) external {
assembly {
tstore(k, v)
}
}


function read(uint256 k) external view returns (uint256 r) {
assembly {
r := tload(k)
}
}

function readToEvent(uint256 k) external {
uint256 r;
assembly {
r := tload(k)
}
emit Loaded(k, r);
}


function writeThenReadToEvent(uint256 k, uint256 v) external {
uint256 r;
assembly {
tstore(k, v)
r := tload(k)
}
emit Loaded(k, r);
}

function callThenStaticWrite(address target, uint256 k, uint256 v)
external
returns (bool callOk, bool staticOk, uint256 staticRetLen)
{
bytes memory payload = abi.encodeWithSelector(this.write.selector, k, v);

(callOk, ) = target.call(payload);

(staticOk, ) = target.staticcall(payload);
assembly {
staticRetLen := returndatasize()
}
}

function writeThenStaticRead(address target, uint256 k, uint256 v)
external
returns (bool ok, uint256 value)
{
TransientProbe(target).write(k, v);

bytes memory ret;
(ok, ret) = target.staticcall(abi.encodeWithSelector(this.read.selector, k));
if (ok && ret.length == 32) {
value = abi.decode(ret, (uint256));
}
}


function writeThenRevert(uint256 k, uint256 v) external {
assembly {
tstore(k, v)
}
revert Boom();
}

function rollbackAfterInnerRevert(uint256 k, uint256 pre, uint256 inner)
external
returns (uint256 r)
{
assembly {
tstore(k, pre)
}
(bool ok, ) = address(this).call(
abi.encodeWithSelector(this.writeThenRevert.selector, k, inner)
);
require(!ok, "inner call was expected to revert");
assembly {
r := tload(k)
}
}

function writeCallWriteThenRevert(uint256 k, uint256 a, uint256 b) external {
assembly {
tstore(k, a)
}
TransientProbe(address(this)).write(k, b);
revert Boom();
}


function rollbackIncludesInnerCallWrites(uint256 k, uint256 pre, uint256 a, uint256 b)
external
returns (uint256 r)
{
assembly {
tstore(k, pre)
}
(bool ok, ) = address(this).call(
abi.encodeWithSelector(this.writeCallWriteThenRevert.selector, k, a, b)
);
require(!ok, "inner call was expected to revert");
assembly {
r := tload(k)
}
}


function writeThenCallOtherWrite(address other, uint256 k, uint256 mine, uint256 theirs)
external
returns (uint256 ourValue, uint256 theirValue)
{
assembly {
tstore(k, mine)
}
TransientProbe(other).write(k, theirs);
assembly {
ourValue := tload(k)
}
theirValue = TransientProbe(other).read(k);
}


function delegateWrite(address impl, uint256 k, uint256 v)
external
returns (uint256 ourValue, uint256 implValue)
{
(bool ok, ) = impl.delegatecall(abi.encodeWithSelector(this.write.selector, k, v));
require(ok, "delegatecall failed");
assembly {
ourValue := tload(k)
}
implValue = TransientProbe(impl).read(k);
}


function readFrom(address origin, uint256 k) external view returns (uint256) {
return TransientProbe(origin).read(k);
}


function reentrantRead(address other, uint256 k, uint256 v)
external
returns (uint256 seen)
{
assembly {
tstore(k, v)
}
seen = TransientProbe(other).readFrom(address(this), k);
}

function innerWritePersists(uint256 k, uint256 v) external returns (uint256 r) {
TransientProbe(address(this)).write(k, v);
assembly {
r := tload(k)
}
}
}
14 changes: 14 additions & 0 deletions tests/eip1153/contracts/gen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) 2018 The VeChainThor developers
// Distributed under the GNU Lesser General Public License v3.0 software license, see the accompanying
// file LICENSE or <https://www.gnu.org/licenses/lgpl-3.0.html>

package contracts

// --evm-version cancun is pinned explicitly: TSTORE/TLOAD first exist at
// Cancun, and letting solc pick its own default risks emitting opcodes from a
// later fork that the INTERSTELLAR EVM does not implement.

// --platform linux/amd64 is required because ghcr.io/argotorg/solc publishes no
// arm64 manifest; without it the pull fails outright on Apple Silicon.

//go:generate sh -c "docker run --rm --platform linux/amd64 -v $(pwd):/src ghcr.io/argotorg/solc:stable --evm-version cancun --optimize --optimize-runs 200 --combined-json abi,bin,bin-runtime,hashes /src/TransientProbe.sol | docker run --rm -i -v $(pwd):/src otherview/solgen:latest --out /src/generated"
Loading
Loading