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
4 changes: 4 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ clean-consensus-deployments:
just -f ./cartesi-rollups/contracts/justfile clean-deployments
bind-consensus:
just -f ./cartesi-rollups/contracts/justfile bind
build-devnet:
just -f ./cartesi-rollups/contracts/justfile build-devnet

build-prt:
just -f ./prt/contracts/justfile build
Expand Down Expand Up @@ -84,6 +86,8 @@ test-rollups-honeypot: build-rust-workspace
just -f ./prt/tests/rollups/justfile test-honeypot-all
test-rollups-honeypot-ci: build-rust-workspace
just -f ./prt/tests/rollups/justfile test-honeypot-ci
test-rollups-honeypot-case CASE: build-rust-workspace
just -f ./prt/tests/rollups/justfile test-honeypot-case {{CASE}}
view-rollups-logs:
just -f ./prt/tests/rollups/justfile read-node-logs

Expand Down
40 changes: 25 additions & 15 deletions prt/tests/common/blockchain/node.lua
Original file line number Diff line number Diff line change
@@ -1,26 +1,36 @@
local time = require "utils.time"
local helper = require "utils.helper"

local slots_in_an_epoch = 1
local default_account_number = 40

-- spawn an anvil node with 40 accounts, auto-mine, and finalize block at height N-2
local function start_blockchain(load_state)
local function start_blockchain(anvil_load_path, anvil_dump_path)
print(string.format("Starting blockchain with %d accounts...", default_account_number))

local cmd
if load_state then
cmd = string.format(
[[echo $$ ; exec anvil --load-state %s --preserve-historical-states --slots-in-an-epoch 1 -a %d > anvil.log 2>&1]],
load_state,
default_account_number
)
else
cmd = string.format(
[[echo $$ ; exec anvil --preserve-historical-states --block-time 1 --slots-in-an-epoch 1 -a %d > anvil.log 2>&1]],
default_account_number
)
local anvil_args = {
"--slots-in-an-epoch",
slots_in_an_epoch,
"-a",
default_account_number,
}

if anvil_load_path then
table.insert(anvil_args, "--load-state")
table.insert(anvil_args, anvil_load_path)
end

if anvil_dump_path then
table.insert(anvil_args, "--dump-state")
table.insert(anvil_args, anvil_dump_path)
table.insert(anvil_args, "--preserve-historical-states")
end

local cmd = string.format(
[[ echo $$ ; exec anvil %s > anvil.log 2>&1 ]],
table.concat(anvil_args, " ")
)

local reader = io.popen(cmd)
assert(reader, "`popen` returned nil reader")

Expand All @@ -45,10 +55,10 @@ end
local Blockchain = {}
Blockchain.__index = Blockchain

function Blockchain:new(load_state)
function Blockchain:new(anvil_load_path, anvil_dump_path)
local blockchain = {}

local handle = start_blockchain(load_state)
local handle = start_blockchain(anvil_load_path, anvil_dump_path)
blockchain.pks, blockchain.endpoint = capture_blockchain_data()

blockchain._handle = handle
Expand Down
1 change: 1 addition & 0 deletions prt/tests/rollups/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
*.log
_state/
anvil*.json
35 changes: 33 additions & 2 deletions prt/tests/rollups/dave/sender.lua
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ local function quote_args(args, not_quote)
else
table.insert(quoted_args, '"' .. v:hex_string() .. '"')
end
elseif type(v) == "table" and getmetatable(v) == bint then
if not_quote then
table.insert(quoted_args, tostring(v))
else
table.insert(quoted_args, '"' .. tostring(v) .. '"')
end
elseif type(v) == "table" then
if v._tag == "tuple" then
local qa = quote_args(v, true)
Expand Down Expand Up @@ -61,7 +67,7 @@ local cast_send_template = [[
cast send --private-key "%s" --rpc-url "%s" --value "%s" "%s" "%s" %s 2>&1
]]

function Sender:_send_tx(tournament_address, sig, args, value)
function Sender:_send_tx(destination, sig, args, value)
value = value or bint.zero()

local quoted_args = quote_args(args)
Expand All @@ -72,7 +78,7 @@ function Sender:_send_tx(tournament_address, sig, args, value)
self.pk,
self.endpoint,
value,
tournament_address,
destination,
sig,
args_str
)
Expand Down Expand Up @@ -154,4 +160,29 @@ function Sender:advance_blocks(blocks)
blockchain_utils.advance_time(blocks, self.endpoint)
end

function Sender:wallet_address()
local cmd = string.format([[cast wallet address -- "%s"]], self.pk)

local handle = io.popen(cmd)
assert(handle)

local ret = handle:read()

if ret:find "Error" then
local err_str = ret .. handle:read "*a"
handle:close()
error(
string.format(
"Could not derive wallet address from private key %s:\n%s",
self.pk,
err_str
)
)
end

handle:close()

return ret
end

return Sender
67 changes: 34 additions & 33 deletions prt/tests/rollups/justfile
Original file line number Diff line number Diff line change
@@ -1,55 +1,56 @@
ECHO_DIR := "../../../test/programs/echo"
HONEYPOT_DIR := "../../../test/programs/honeypot"
YIELD_DIR := "../../../test/programs/yield"
ANVIL_PATH := "../../../cartesi-rollups/contracts/state.json"
ANVIL_LOAD_PATH := "../../../cartesi-rollups/contracts/state.json"
DEPLOYMENTS_DIR := "../../../cartesi-rollups/contracts/deployments/31337"

WEB3_PRIVATE_KEY := "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"

# run PRT rollups test
test MACH_PATH SCRIPT:
test PROGRAM SCRIPT:
rm -rf _state
WEB3_PRIVATE_KEY={{WEB3_PRIVATE_KEY}} \
ANVIL_PATH=`realpath {{ANVIL_PATH}}` \
TEMPLATE_MACHINE=`realpath {{MACH_PATH}}/machine-image` \
TEMPLATE_MACHINE_HASH=0x`xxd -p -c32 {{MACH_PATH}}/machine-image/hash` \
ANVIL_LOAD_PATH=`realpath {{ANVIL_LOAD_PATH}}` \
ANVIL_DUMP_PATH="anvil_{{PROGRAM}}_{{SCRIPT}}.json" \
TEMPLATE_MACHINE=`realpath ../../../test/programs/{{PROGRAM}}/machine-image` \
TEMPLATE_MACHINE_HASH=0x`xxd -p -c32 ../../../test/programs/{{PROGRAM}}/machine-image/hash` \
DAVE_APP_FACTORY=`jq -r .address {{DEPLOYMENTS_DIR}}/DaveAppFactory.json` \
INPUT_BOX=`jq -r .address {{DEPLOYMENTS_DIR}}/InputBox.json` \
lua test_cases/{{SCRIPT}}.lua
ERC20_PORTAL=`jq -r .address {{DEPLOYMENTS_DIR}}/ERC20Portal.json` \
ERC20_TOKEN=`jq -r .address {{DEPLOYMENTS_DIR}}/TestFungibleToken.json` \
lua5.4 test_cases/{{SCRIPT}}.lua

# run PRT rollups echo test
test-echo:
just test {{ECHO_DIR}} simple
test-echo: \
(test "echo" "simple")

# run PRT rollups honeypot test
test-honeypot-all:
just test {{HONEYPOT_DIR}} simple_no_input
just test {{HONEYPOT_DIR}} stf_all
just test {{HONEYPOT_DIR}} big_input
just test {{HONEYPOT_DIR}} gc_match
just test {{HONEYPOT_DIR}} gc_tournament
just test {{HONEYPOT_DIR}} bad_commitment
test-honeypot-all: \
(test "honeypot" "deposit_withdrawal") \
(test "honeypot" "simple_no_input") \
(test "honeypot" "stf_all") \
(test "honeypot" "big_input") \
(test "honeypot" "gc_match") \
(test "honeypot" "gc_tournament") \
(test "honeypot" "bad_commitment")

# run PRT rollups yield test
test-yield-all:
just test {{YIELD_DIR}} simple_no_input
just test {{YIELD_DIR}} stf_all
just test {{YIELD_DIR}} big_input
just test {{YIELD_DIR}} gc_match
just test {{YIELD_DIR}} gc_tournament
just test {{YIELD_DIR}} bad_commitment
test-yield-all: \
(test "yield" "simple_no_input") \
(test "yield" "stf_all") \
(test "yield" "big_input") \
(test "yield" "gc_match") \
(test "yield" "gc_tournament") \
(test "yield" "bad_commitment")

test-honeypot-ci:
just test {{HONEYPOT_DIR}} simple
test-honeypot-ci: \
(test "honeypot" "simple")

test-yield-ci:
just test {{YIELD_DIR}} simple
test-yield-ci: \
(test "yield" "simple")

test-honeypot-case CASE:
just test {{HONEYPOT_DIR}} {{CASE}}
test-honeypot-case CASE: \
(test "honeypot" CASE)

test-yield-case CASE:
just test {{YIELD_DIR}} {{CASE}}
test-yield-case CASE: \
(test "yield" CASE)

# read logs from PRT Rollups node, run in separate terminal after `test-echo`
read-node-logs:
Expand Down
123 changes: 123 additions & 0 deletions prt/tests/rollups/test_cases/deposit_withdrawal.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
require "setup_path"

local uint256 = require "utils.bint" (256)
local Hash = require "cryptography.hash"
local env = require "test_env"

local ERC20_PORTAL_ADDRESS = assert(os.getenv("ERC20_PORTAL"))
local ERC20_TOKEN_ADDRESS = assert(os.getenv("ERC20_TOKEN"))
local ERC20_AMOUNT = "1" .. string.rep("0", 18) -- 10^18

-- Main Execution
env.spawn_blockchain {}

-- Helper routine for calling functions that returns exactly one uint256 value
local call1 = function (...)
local ret = env.reader:_call(...)
assert(type(ret) == "table", "Expected a table")
local str = assert(ret[1], "Expected a non-empty array")
assert(type(str) == "string", "Expected a string")
local num = assert(str:match"(%d+)", "Expected a decimal number")
return assert(uint256.new(num), "Expected a uint256 value")
end

local first_epoch = assert(env.reader:read_epochs_sealed()[1])
assert(first_epoch.input_upper_bound == 0) -- there's no input for epoch 0!

-- Derive wallet address from private key
local wallet_address = env.sender:wallet_address()

local get_wallet_balance = function ()
return call1(
ERC20_TOKEN_ADDRESS,
"balanceOf(address)(uint256)",
{wallet_address}
)
end

-- Get wallet ERC-20 balance before minting
local initial_wallet_balance = get_wallet_balance()

-- Mint ERC-20 tokens
env.sender:_send_tx(
ERC20_TOKEN_ADDRESS,
"mint(uint256)",
{ERC20_AMOUNT}
)

assert(
get_wallet_balance() == initial_wallet_balance + ERC20_AMOUNT,
"Wallet balance has not been incremented by minted token amount after minting"
)

local get_portal_allowance = function ()
return call1(
ERC20_TOKEN_ADDRESS,
"allowance(address,address)(uint256)",
{wallet_address, ERC20_PORTAL_ADDRESS}
)
end

-- Get ERC-20 portal allowance before approval
local initial_portal_allowance = get_portal_allowance()

-- Approve ERC-20 portal to transfer ERC-20 tokens
env.sender:_send_tx(
ERC20_TOKEN_ADDRESS,
"approve(address,uint256)",
{ERC20_PORTAL_ADDRESS, ERC20_AMOUNT}
)

assert(
get_portal_allowance() == initial_portal_allowance + ERC20_AMOUNT,
"ERC-20 portal allowance has not been incremented by minted token amount after approval"
)

-- Deposit ERC-20 tokens into app
-- (This makes the portal send an input to the app)
env.sender:_send_tx(
ERC20_PORTAL_ADDRESS,
"depositERC20Tokens(address,address,uint256,bytes)",
{ERC20_TOKEN_ADDRESS, env.app_address, ERC20_AMOUNT, "0x"}
)

assert(
get_wallet_balance() == initial_wallet_balance,
"Wallet balance has not gone back to its initial value after deposit"
)

assert(
get_portal_allowance() == initial_portal_allowance,
"ERC-20 portal allowance has not gone back to its initial value after deposit"
)

local get_input_count = function ()
return call1(
env.input_box_address,
"getNumberOfInputs(address)(uint256)",
{env.app_address}
)
end

-- Get input count before withdrawal request
local initial_input_count = get_input_count()

-- Request withdrawal
env.sender:tx_add_input("0x")

assert(
get_input_count() == initial_input_count + uint256.new(1),
"Input count has not been incremented by 1 after withdrawal request"
)

-- Spawn Dave node
env.spawn_node()

-- advance such that epoch 0 is finished
local sealed_epoch = env.roll_epoch()

-- run epoch 1
env.run_epoch(sealed_epoch, {
-- ustep + reset
{ hash = Hash.zero, meta_cycle = 1 << 44 }
}, {})
8 changes: 5 additions & 3 deletions prt/tests/rollups/test_env.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ local PatchedCommitmentBuilder = require "runners.helpers.patched_commitment"
local CommitmentBuilder = require "computation.commitment"

-- anvil deployment state dump
local ANVIL_PATH = assert(os.getenv("ANVIL_PATH"))
local ANVIL_LOAD_PATH = assert(os.getenv("ANVIL_LOAD_PATH"))
local ANVIL_DUMP_PATH = assert(os.getenv("ANVIL_DUMP_PATH"))

-- machine template hash
local TEMPLATE_MACHINE = assert(os.getenv("TEMPLATE_MACHINE"))
Expand All @@ -28,7 +29,8 @@ local FAST_FORWARD_TIME = 16
local ECHO_MSG = "0x48656c6c6f2076726f6d204461766521"

local Env = {
anvil_path = ANVIL_PATH,
anvil_load_path = ANVIL_LOAD_PATH,
anvil_dump_path = ANVIL_DUMP_PATH,

input_box_address = INPUT_BOX_ADDRESS,
dave_app_factory_address = DAVE_APP_FACTORY_ADDRESS,
Expand All @@ -52,7 +54,7 @@ local Env = {
function Env.spawn_blockchain(inputs)
inputs = inputs or {}

local blockchain = Blockchain:new(ANVIL_PATH)
local blockchain = Blockchain:new(ANVIL_LOAD_PATH, ANVIL_DUMP_PATH)
Env.blockchain = blockchain
Env.reader = Reader:new(INPUT_BOX_ADDRESS, DAVE_APP_FACTORY_ADDRESS, TEMPLATE_MACHINE_HASH, SALT, blockchain.endpoint)
Env.app_address = Env.reader.app_address
Expand Down