Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 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
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/...",
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.

@ARR4N is this an anti-pattern? I could just expose this to the packages as needed rather than for the whole sub-tree.

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.

Not an anti-pattern, just a question of what we believe the most appropriate scope to be. The others are only fine-grained because I had my agent (Maru) do it (with his agent). I think we could reasonably allow anything in //tests/... too but there's no need right now.

To me the more important thing is that the other //vms/saevm packages never import the //vms/saevm/cchain ones.

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.

I updated the visibility logic to match graft/coreth.

"//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 {
Comment thread
JonathanOppenheimer marked this conversation as resolved.
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
44 changes: 44 additions & 0 deletions vms/saevm/cchain/tx/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
load("//.bazel:defs.bzl", "go_test")

go_library(
name = "tx",
srcs = [
"codec.go",
"export.go",
"import.go",
"tx.go",
],
importpath = "github.com/ava-labs/avalanchego/vms/saevm/cchain/tx",
visibility = ["//visibility:public"],
Comment thread
StephenButtolph marked this conversation as resolved.
Outdated
deps = [
"//codec",
"//codec/linearcodec",
"//ids",
"//utils/hashing",
"//utils/wrappers",
"//vms/components/avax",
"//vms/secp256k1fx",
"@com_github_ava_labs_libevm//common",
],
)

go_test(
name = "tx_test",
srcs = ["tx_test.go"],
data = glob(["testdata/**"]),
embed = [":tx"],
deps = [
"//graft/coreth/plugin/evm/atomic",
"//graft/coreth/plugin/evm/atomic/vm",
"//ids",
"//vms/components/avax",
"//vms/components/verify",
"//vms/secp256k1fx",
"@com_github_ava_labs_libevm//common",
"@com_github_google_go_cmp//cmp",
"@com_github_google_go_cmp//cmp/cmpopts",
"@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
Comment thread
StephenButtolph marked this conversation as resolved.

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
}
38 changes: 38 additions & 0 deletions vms/saevm/cchain/tx/export.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (C) 2019, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package tx

import (
"github.com/ava-labs/libevm/common"

"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/vms/components/avax"
)

// Export is the unsigned component of a transaction that transfers assets from
// the C-Chain to either the P-Chain or the X-Chain. It modifies the C-Chain
Comment thread
StephenButtolph marked this conversation as resolved.
// state and produces UTXOs in the shared memory between the C-Chain and the
// destination chain.
type Export struct {
NetworkID uint32 `serialize:"true" json:"networkID"`
BlockchainID ids.ID `serialize:"true" json:"blockchainID"`
DestinationChain ids.ID `serialize:"true" json:"destinationChain"`
Ins []Input `serialize:"true" json:"inputs"`
ExportedOutputs []*avax.TransferableOutput `serialize:"true" json:"exportedOutputs"`
}
Comment thread
StephenButtolph marked this conversation as resolved.

// TODO(StephenButtolph): Remove this with its removal from the interface.
func (*Export) isUnsigned() {}

// Input identifies an account + nonce pair on the C-Chain that authorizes the
// asset and quantity to deduct.
//
// If the AssetID is AVAX, the amount will be scaled up to account for the EVM's
// higher denomination.
type Input struct {
Address common.Address `serialize:"true" json:"address"`
Amount uint64 `serialize:"true" json:"amount"`
AssetID ids.ID `serialize:"true" json:"assetID"`
Nonce uint64 `serialize:"true" json:"nonce"`
}
37 changes: 37 additions & 0 deletions vms/saevm/cchain/tx/import.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright (C) 2019, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package tx

import (
"github.com/ava-labs/libevm/common"

"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/vms/components/avax"
)

// Import is the unsigned component of a transaction that transfers assets from
// either the P-Chain or the X-Chain to the C-Chain. It consumes UTXOs in the
// shared memory between the C-Chain and the source chain and increases balances
// in the C-Chain state.
type Import struct {
NetworkID uint32 `serialize:"true" json:"networkID"`
BlockchainID ids.ID `serialize:"true" json:"blockchainID"`
SourceChain ids.ID `serialize:"true" json:"sourceChain"`
ImportedInputs []*avax.TransferableInput `serialize:"true" json:"importedInputs"`
Outs []Output `serialize:"true" json:"outputs"`
}
Comment thread
StephenButtolph marked this conversation as resolved.

// TODO(StephenButtolph): Remove this with its removal from the interface.
func (*Import) isUnsigned() {}

// Output specifies an account on the C-Chain whose balance of the specified
// asset should be increased.
//
// If the AssetID is AVAX, the amount will be scaled up to account for the EVM's
// higher denomination.
type Output struct {
Address common.Address `serialize:"true" json:"address"`
Amount uint64 `serialize:"true" json:"amount"`
AssetID ids.ID `serialize:"true" json:"assetID"`
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
go test fuzz v1
[]byte("\x00\x00\x00\x00\x00\x0000000000000000000000000000000000000000000000000000000000000000000000\x00\x00\x00\x0100000000000000000000000000000000000000000000000000000000000000000000\x00\x00\x00\x0500000000\x00\x00\x00\x010000\x00\x00\x00\x01000000000000000000000000000000000000000000000000000000000000\x00\x00\x00\x01\x00\x00\x00\x0500000000\x00\x00\x00\x010000")
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
go test fuzz v1
[]byte("\x00\x00\x00\x00\x00\x0000000000000000000000000000000000000000000000000000000000000000000000\x00\x00\x00\x0100000000000000000000000000000000000000000000000000000000000000000000\x00\x00\x00\x0500000000\x00\x00\x00\x010000\x00\x00\x00\x01000000000000000000000000000000000000000000000000000000000000\x00\x00\x00\x01\x00\x00\x00\n\x00\x00\x00\x010000")
72 changes: 72 additions & 0 deletions vms/saevm/cchain/tx/tx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright (C) 2019, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

// Package tx defines the Avalanche-specific transaction types used on the
// C-Chain to interact with the shared memory between the C-Chain and other
// chains on the Primary Network.
package tx

import (
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/utils/hashing"
"github.com/ava-labs/avalanchego/vms/secp256k1fx"
)

// Tx is a signed transaction that interacts with shared memory.
// The [Unsigned] body can be implemented by either [Export] or [Import].
// The [Credential] values are implemented by [secp256k1fx.Credential].
type Tx struct {
Unsigned `serialize:"true" json:"unsignedTx"`
Creds []Credential `serialize:"true" json:"credentials"`
}

// Unsigned is a common interface implemented by [Import] and [Export].
//
// TODO(StephenButtolph): Expand this interface to include UTXO handling,
// verification, and state execution.
type Unsigned interface {
// This function ensures that [Tx.Unsigned] can only be parsed as [Export]
// or [Import].
//
// TODO(StephenButtolph): Once [Unsigned] includes other unexported
// functions, remove this function.
isUnsigned()
}

// Credential is used in [Tx] to authorize an input of a transaction.
//
// It is only implemented by [secp256k1fx.Credential]. An interface must be used
// to correctly produce the canonical binary format during serialization.
type Credential interface {
Self() *secp256k1fx.Credential
}

// ID returns the unique hash of the transaction.
func (t *Tx) ID() ids.ID {
// TODO(StephenButtolph): Optimize ID by caching previously calculated
// values.
bytes, err := t.Bytes()
// This error can happen, but only with invalid transactions. To avoid
// polluting the interface, we represent all invalid transactions with
// the zero ID.
if err != nil {
return ids.ID{}
Comment thread
StephenButtolph marked this conversation as resolved.
}
return hashing.ComputeHash256Array(bytes)
}

// Bytes returns the canonical binary format of the transaction.
func (t *Tx) Bytes() ([]byte, error) {
// TODO(StephenButtolph): Optimize Bytes by caching previously calculated
// values.
return c.Marshal(codecVersion, t)
}

// Parse deserializes a [Tx] from its canonical binary format.
func Parse(b []byte) (*Tx, error) {
var tx Tx
if _, err := c.Unmarshal(b, &tx); err != nil {
return nil, err
}
return &tx, nil
}
Loading
Loading