Skip to content
Merged
Show file tree
Hide file tree
Changes from 95 commits
Commits
Show all changes
111 commits
Select commit Hold shift + click to select a range
eb8c0a0
sae: Add C-chain custom tx serialization
StephenButtolph Apr 25, 2026
e248997
sae: Implement AsOp
StephenButtolph Apr 25, 2026
253c255
wip
StephenButtolph Apr 25, 2026
3cca8aa
comments
StephenButtolph Apr 25, 2026
6346d2e
reduce exported identifiers
StephenButtolph Apr 25, 2026
ab83fe6
wip
StephenButtolph Apr 26, 2026
8f3f888
wip
StephenButtolph Apr 26, 2026
83cc38b
wip
StephenButtolph Apr 26, 2026
d606762
Extend fuzz tests
StephenButtolph Apr 26, 2026
0a82734
nit
StephenButtolph Apr 26, 2026
5b86b6b
wip
StephenButtolph Apr 26, 2026
3d92bd5
wip
StephenButtolph Apr 26, 2026
893e17e
wip
StephenButtolph Apr 26, 2026
83b0ec0
wip
StephenButtolph Apr 26, 2026
c46bcc2
nit
StephenButtolph Apr 26, 2026
e92638f
nit
StephenButtolph Apr 26, 2026
f2faffb
lint
StephenButtolph Apr 26, 2026
7447b08
lint
StephenButtolph Apr 26, 2026
8c3b6f0
nits
StephenButtolph Apr 27, 2026
0369966
fix bazel
StephenButtolph Apr 28, 2026
85ae854
ci: free runner disk before publishing Antithesis images (#5310)
JonathanOppenheimer Apr 28, 2026
88a234a
Merge branch 'master' into StephenButtolph/atomic-tx-rewrite.1
StephenButtolph Apr 28, 2026
4e261c6
Merge branch 'master' into StephenButtolph/atomic-tx-rewrite.1
StephenButtolph Apr 28, 2026
7073c57
Merge branch 'master' into StephenButtolph/atomic-tx-rewrite.1
StephenButtolph Apr 28, 2026
d63857d
merged
StephenButtolph Apr 28, 2026
06d2a41
nit
StephenButtolph Apr 28, 2026
c28b2d2
fuzz full op
StephenButtolph Apr 28, 2026
aba7590
mark as compatibility
StephenButtolph Apr 28, 2026
ca2a5ea
nit
StephenButtolph Apr 28, 2026
4be05cf
nit
StephenButtolph Apr 28, 2026
9b4c83f
nit
StephenButtolph Apr 28, 2026
5d9dd0d
merged
StephenButtolph Apr 28, 2026
7e58c8e
nit
StephenButtolph Apr 28, 2026
8573565
nit
StephenButtolph Apr 28, 2026
2f01ee8
Unblock myself for sae progress
StephenButtolph Apr 29, 2026
b28d132
sae: Smarter tx fuzzing
StephenButtolph Apr 29, 2026
f12a1b4
nit
StephenButtolph Apr 29, 2026
258df6f
nit
StephenButtolph Apr 29, 2026
8cefe95
nit
StephenButtolph Apr 29, 2026
fa25c9a
Merge branch 'master' into StephenButtolph/atomic-tx-rewrite.1
StephenButtolph Apr 29, 2026
95e7256
nit
StephenButtolph Apr 29, 2026
5d2ef23
want got
StephenButtolph Apr 29, 2026
46814f4
want got
StephenButtolph Apr 29, 2026
58306f5
Merge branch 'StephenButtolph/atomic-tx-rewrite.1' of github.com:ava-…
StephenButtolph Apr 29, 2026
88c09d1
merged
StephenButtolph Apr 29, 2026
271658d
nit
StephenButtolph Apr 29, 2026
9da6af8
nit
StephenButtolph Apr 29, 2026
7bcb962
ci
StephenButtolph Apr 29, 2026
34b1457
nosec
StephenButtolph Apr 29, 2026
692f899
merged
StephenButtolph Apr 29, 2026
eedd62e
nit
StephenButtolph Apr 29, 2026
e1ece39
Merge branch 'StephenButtolph/smart-atomic-tx-fuzzing' into StephenBu…
StephenButtolph Apr 29, 2026
9e3bb98
ci
StephenButtolph Apr 29, 2026
a4bad9b
remove duplicate code
StephenButtolph Apr 29, 2026
56850ce
nit
StephenButtolph Apr 29, 2026
a41e9a6
Merge branch 'StephenButtolph/smart-atomic-tx-fuzzing' into StephenBu…
StephenButtolph Apr 29, 2026
cefd09f
nit
StephenButtolph Apr 29, 2026
2ed8fcb
Merge branch 'StephenButtolph/smart-atomic-tx-fuzzing' into StephenBu…
StephenButtolph Apr 29, 2026
05ddba9
oops
StephenButtolph Apr 29, 2026
192a266
Merge branch 'StephenButtolph/smart-atomic-tx-fuzzing' into StephenBu…
StephenButtolph Apr 29, 2026
fca7808
no moar recursion pls
StephenButtolph Apr 29, 2026
f47cf40
Merge branch 'StephenButtolph/smart-atomic-tx-fuzzing' into StephenBu…
StephenButtolph Apr 29, 2026
1ede276
Merge branch 'master' into StephenButtolph/atomic-tx-rewrite.1
StephenButtolph Apr 29, 2026
01cc4cf
Merge branch 'StephenButtolph/atomic-tx-rewrite.1' into StephenButtol…
StephenButtolph Apr 29, 2026
bc6aed5
Merge branch 'StephenButtolph/smart-atomic-tx-fuzzing' into StephenBu…
StephenButtolph Apr 29, 2026
0de4a41
nit
StephenButtolph Apr 29, 2026
87d29d2
nit
StephenButtolph Apr 29, 2026
6e9545c
Use the identifier rather than where the identifier is used
StephenButtolph Apr 30, 2026
47fe30d
Update visability
StephenButtolph Apr 30, 2026
adeaa57
Merge branch 'master' into StephenButtolph/atomic-tx-rewrite.1
StephenButtolph Apr 30, 2026
6350008
merged
StephenButtolph Apr 30, 2026
05bb2f4
Merge branch 'StephenButtolph/smart-atomic-tx-fuzzing' into StephenBu…
StephenButtolph Apr 30, 2026
ba1afe1
nit
StephenButtolph Apr 30, 2026
3e3e6aa
ci
StephenButtolph Apr 30, 2026
9ffcd6c
Merge branch 'StephenButtolph/smart-atomic-tx-fuzzing' into StephenBu…
StephenButtolph Apr 30, 2026
963c295
remove jank, use cmp
StephenButtolph Apr 30, 2026
937c6f8
merged
StephenButtolph Apr 30, 2026
8ae1a82
fix
StephenButtolph Apr 30, 2026
3d11e3b
Merge branch 'StephenButtolph/smart-atomic-tx-fuzzing' into StephenBu…
StephenButtolph Apr 30, 2026
d39ed4d
nit
StephenButtolph Apr 30, 2026
18c3c2b
merged
StephenButtolph Apr 30, 2026
05930e4
missed one
StephenButtolph Apr 30, 2026
9e0ffe5
Merge branch 'StephenButtolph/smart-atomic-tx-fuzzing' into StephenBu…
StephenButtolph Apr 30, 2026
99f52e1
nit
StephenButtolph Apr 30, 2026
18a656e
Merge branch 'StephenButtolph/smart-atomic-tx-fuzzing' into StephenBu…
StephenButtolph Apr 30, 2026
8bb04f6
bazel
StephenButtolph Apr 30, 2026
e6a1d35
Merge branch 'StephenButtolph/smart-atomic-tx-fuzzing' into StephenBu…
StephenButtolph Apr 30, 2026
149b1cb
Merge branch 'master' into StephenButtolph/atomic-tx-rewrite.1
StephenButtolph Apr 30, 2026
a781d21
Merge branch 'StephenButtolph/atomic-tx-rewrite.1' into StephenButtol…
StephenButtolph Apr 30, 2026
23b9916
Add comment
StephenButtolph Apr 30, 2026
c1b9fa8
nit
StephenButtolph Apr 30, 2026
b4d98b5
nit
StephenButtolph Apr 30, 2026
81c3c02
nit
StephenButtolph Apr 30, 2026
9e36643
nit
StephenButtolph Apr 30, 2026
61dcfbd
Merge branch 'StephenButtolph/smart-atomic-tx-fuzzing' into StephenBu…
StephenButtolph Apr 30, 2026
ca7c26b
merged
StephenButtolph Apr 30, 2026
268f6ed
Merge branch 'StephenButtolph/smart-atomic-tx-fuzzing' into StephenBu…
StephenButtolph Apr 30, 2026
40c3ba6
cleanup
StephenButtolph Apr 30, 2026
3ed6547
cleanup
StephenButtolph Apr 30, 2026
f0515ec
Add more cases
StephenButtolph Apr 30, 2026
6dbe653
add comments
StephenButtolph Apr 30, 2026
9ae76cc
Add interface check
StephenButtolph Apr 30, 2026
6838983
nit
StephenButtolph Apr 30, 2026
d44b9e6
grammar
StephenButtolph Apr 30, 2026
5370371
more succinct
StephenButtolph Apr 30, 2026
cfd18da
merged
StephenButtolph Apr 30, 2026
a5c21d1
pedantic
StephenButtolph Apr 30, 2026
eb701ee
ok
StephenButtolph Apr 30, 2026
1ad0654
Document codec weirdness
StephenButtolph Apr 30, 2026
ac82445
simplify
StephenButtolph May 2, 2026
cab3300
Merge branch 'master' into StephenButtolph/atomic-tx-rewrite.2
StephenButtolph May 2, 2026
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
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
/graft/evm @ava-labs/platform-evm
/graft/subnet-evm @ava-labs/platform-evm
/vms/saevm @ARR4N @StephenButtolph
/vms/saevm/cchain @StephenButtolph
1 change: 1 addition & 0 deletions graft/coreth/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package_group(
"//tests/reexecute/c",
"//tests/reexecute/chaos",
"//vms/evm/emulate",
"//vms/saevm/cchain/...",
"//wallet/chain/c",
"//wallet/subnet/primary",
],
Expand Down
10 changes: 10 additions & 0 deletions ids/short.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ func ShortFromString(idStr string) (ShortID, error) {
return ToShortID(bytes)
}

// ShortFromStringOrPanic is the same as ShortFromString, but will panic on
// error.
func ShortFromStringOrPanic(idStr string) ShortID {
id, err := ShortFromString(idStr)
if err != nil {
panic(err)
}
return id
}

// ShortFromPrefixedString returns a ShortID assuming the cb58 format is
// prefixed
func ShortFromPrefixedString(idStr, prefix string) (ShortID, error) {
Expand Down
11 changes: 11 additions & 0 deletions vms/saevm/cchain/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# gazelle:default_visibility //vms/saevm/cchain:__subpackages__,//vms/saevm/cchain:external_consumers

package(default_visibility = [
"//vms/saevm/cchain:__subpackages__",
"//vms/saevm/cchain:external_consumers",
])

package_group(
name = "external_consumers",
packages = [],
)
64 changes: 64 additions & 0 deletions vms/saevm/cchain/tx/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
load("//.bazel:defs.bzl", "go_test")

package(default_visibility = [
"//vms/saevm/cchain:__subpackages__",
"//vms/saevm/cchain:external_consumers",
])

go_library(
name = "tx",
srcs = [
"codec.go",
"export.go",
"import.go",
"tx.go",
],
importpath = "github.com/ava-labs/avalanchego/vms/saevm/cchain/tx",
deps = [
"//codec",
"//codec/linearcodec",
"//graft/coreth/plugin/evm/atomic",
"//graft/coreth/plugin/evm/upgrade/ap5",
"//ids",
"//utils/hashing",
"//utils/math",
"//utils/wrappers",
"//vms/components/avax",
"//vms/components/gas",
"//vms/saevm/hook",
"//vms/secp256k1fx",
"@com_github_ava_labs_libevm//common",
"@com_github_holiman_uint256//:uint256",
],
)

go_test(
name = "tx_test",
srcs = [
"compatibility_test.go",
"tx_test.go",
],
data = glob(["testdata/**"]),
embed = [":tx"],
deps = [
"//graft/coreth/plugin/evm/atomic",
"//graft/coreth/plugin/evm/atomic/vm",
"//ids",
"//snow",
"//utils/math",
"//vms/components/avax",
"//vms/components/gas",
"//vms/components/verify",
"//vms/saevm/cchain/tx/txtest",
"//vms/saevm/cmputils",
"//vms/saevm/hook",
"//vms/secp256k1fx",
"@com_github_ava_labs_libevm//common",
"@com_github_google_go_cmp//cmp",
"@com_github_google_go_cmp//cmp/cmpopts",
"@com_github_holiman_uint256//:uint256",
"@com_github_stretchr_testify//assert",
"@com_github_stretchr_testify//require",
],
)
75 changes: 75 additions & 0 deletions vms/saevm/cchain/tx/codec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright (C) 2019, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package tx

import (
"errors"

"github.com/ava-labs/avalanchego/codec"
"github.com/ava-labs/avalanchego/codec/linearcodec"
"github.com/ava-labs/avalanchego/utils/wrappers"
"github.com/ava-labs/avalanchego/vms/secp256k1fx"
)

const codecVersion uint16 = 0

var c codec.Manager

func init() {
c = codec.NewDefaultManager()

// Registration order impacts the typeID included in the canonical format.
// We skip registrations in specific locations so that UTXOs in shared
// memory share the same serialized format as on the P-Chain and X-Chain.
var (
lc = linearcodec.NewDefault()
errs = wrappers.Errs{}
)
errs.Add(
lc.RegisterType(&Import{}),
lc.RegisterType(&Export{}),
)
lc.SkipRegistrations(3)
errs.Add(
lc.RegisterType(&secp256k1fx.TransferInput{}),
)
lc.SkipRegistrations(1)
errs.Add(
lc.RegisterType(&secp256k1fx.TransferOutput{}),
)
lc.SkipRegistrations(1)
errs.Add(
lc.RegisterType(&secp256k1fx.Credential{}),
c.RegisterCodec(codecVersion, lc),
)
if errs.Errored() {
panic(errs.Err)
}
}

// MarshalSlice returns the canonical binary format of a slice of transactions.
func MarshalSlice(txs []*Tx) ([]byte, error) {
if len(txs) == 0 {
return nil, nil
}
return c.Marshal(codecVersion, txs)
}

var errInefficientSlicePacking = errors.New("inefficient slice packing: empty slices should be packed as nil")

// ParseSlice deserializes a slice of [Tx] from its canonical binary format.
func ParseSlice(b []byte) ([]*Tx, error) {
if len(b) == 0 {
return nil, nil
}

var txs []*Tx
if _, err := c.Unmarshal(b, &txs); err != nil {
return nil, err
}
if len(txs) == 0 {
return nil, errInefficientSlicePacking
}
return txs, nil
}
154 changes: 154 additions & 0 deletions vms/saevm/cchain/tx/compatibility_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// Copyright (C) 2019, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package tx_test

import (
"encoding/json"
"math/big"
"testing"

"github.com/ava-labs/libevm/common"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/holiman/uint256"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/atomic"
"github.com/ava-labs/avalanchego/snow"
"github.com/ava-labs/avalanchego/vms/components/gas"
"github.com/ava-labs/avalanchego/vms/saevm/cchain/tx/txtest"
"github.com/ava-labs/avalanchego/vms/saevm/hook"

. "github.com/ava-labs/avalanchego/vms/saevm/cchain/tx"
)

// fuzz seeds f with [NewTxs] and fuzzes the test.
func fuzz(f *testing.F, ff func(t *testing.T, tx *Tx)) {
fuzzer := &txtest.F{
F: f,
}
for _, tx := range NewTxs {
fuzzer.Add(tx)
}
fuzzer.Fuzz(ff)
}

func FuzzParseRoundTrip(f *testing.F) {
fuzz(f, func(t *testing.T, want *Tx) {
bytes, err := want.Bytes()
require.NoErrorf(t, err, "%T.Bytes()", want)

got, err := Parse(bytes)
require.NoError(t, err, "Parse()")
if diff := cmp.Diff(want, got, CmpOpt()); diff != "" {
t.Errorf("Parse() diff (-want +got):\n%s", diff)
}
})
}

func FuzzJSONCompatibility(f *testing.F) {
fuzz(f, func(t *testing.T, newTx *Tx) {
oldTx := ToOldTx(t, newTx)
want, err := json.Marshal(oldTx)
require.NoErrorf(t, err, "json.Marshal(%T)", oldTx)

got, err := json.Marshal(newTx)
require.NoErrorf(t, err, "json.Marshal(%T)", newTx)
assert.JSONEq(t, string(want), string(got))
})
}

func FuzzAsOpCompatibility(f *testing.F) {
fuzz(f, func(t *testing.T, newTx *Tx) {
got, err := newTx.AsOp(AVAXAssetID)
Comment thread
JonathanOppenheimer marked this conversation as resolved.
if err != nil {
t.Skip("invalid tx")
}
Comment thread
StephenButtolph marked this conversation as resolved.

oldTx := ToOldTx(t, newTx)
gasUsed, err := oldTx.UnsignedAtomicTx.GasUsed(true)
require.NoErrorf(t, err, "%T.GasUsed(true)", oldTx.UnsignedAtomicTx)

gasPrice, err := atomic.EffectiveGasPrice(oldTx.UnsignedAtomicTx, AVAXAssetID, true)
require.NoErrorf(t, err, "atomic.EffectiveGasPrice(%T, avaxAssetID, true)", oldTx)

state := newAsOpStateDB()
if export, ok := oldTx.UnsignedAtomicTx.(*atomic.UnsignedExportTx); ok {
for _, in := range export.Ins {
state.initialNonces[in.Address] = in.Nonce
}
}

ctx := &snow.Context{AVAXAssetID: AVAXAssetID}
require.NoErrorf(t, oldTx.UnsignedAtomicTx.EVMStateTransfer(ctx, state), "%T.EVMStateTransfer()", oldTx.UnsignedAtomicTx)

want := hook.Op{
ID: oldTx.ID(),
Gas: gas.Gas(gasUsed),
GasFeeCap: gasPrice,
Burn: state.op.Burn,
Mint: state.op.Mint,
}
if diff := cmp.Diff(want, got, cmpopts.EquateEmpty()); diff != "" {
t.Errorf("%T.AsOp() diff (-want +got):\n%s", newTx, diff)

Check warning on line 95 in vms/saevm/cchain/tx/compatibility_test.go

View workflow job for this annotation

GitHub Actions / Lint

`t.Errorf`" consider the assert and require libraries for succinct tests
}
})
}

// asOpStateDB is an in-memory [atomic.StateDB] for [FuzzAsOpCompatibility]. It
// constructs a [hook.Op] from [atomic.UnsignedAtomicTx.EVMStateTransfer].
type asOpStateDB struct {
initialNonces map[common.Address]uint64
op hook.Op
}

func newAsOpStateDB() *asOpStateDB {
return &asOpStateDB{
initialNonces: make(map[common.Address]uint64),
op: hook.Op{
Burn: make(map[common.Address]hook.AccountDebit),
Mint: make(map[common.Address]uint256.Int),
},
}
}

func (s *asOpStateDB) AddBalance(addr common.Address, amount *uint256.Int) {
b := s.op.Mint[addr]
b.Add(&b, amount)
s.op.Mint[addr] = b
}

func (s *asOpStateDB) SubBalance(addr common.Address, amount *uint256.Int) {
d := s.op.Burn[addr]
d.Amount.Add(&d.Amount, amount)
d.MinBalance = d.Amount
s.op.Burn[addr] = d
}

func (*asOpStateDB) GetBalance(common.Address) *uint256.Int {
// Large enough to never underflow, but small enough to never overflow.
return new(uint256.Int).Lsh(uint256.NewInt(1), 128)
}

func (*asOpStateDB) AddBalanceMultiCoin(common.Address, common.Hash, *big.Int) {}

func (*asOpStateDB) SubBalanceMultiCoin(common.Address, common.Hash, *big.Int) {}

func (*asOpStateDB) GetBalanceMultiCoin(common.Address, common.Hash) *big.Int {
// Large enough to never underflow, but small enough to never overflow.
return new(big.Int).Lsh(big.NewInt(1), 128)
}

func (s *asOpStateDB) SetNonce(addr common.Address, nonce uint64) {
d := s.op.Burn[addr]
// The op specifies what nonce is being consumed, not the next nonce. So we
// need to subtract 1.
d.Nonce = nonce - 1
s.op.Burn[addr] = d
}

func (s *asOpStateDB) GetNonce(addr common.Address) uint64 {
return s.initialNonces[addr]
}
Loading
Loading