Skip to content

Commit

Permalink
Continue to clean up tx handling
Browse files Browse the repository at this point in the history
  • Loading branch information
lrettig committed Oct 9, 2024
1 parent 2e94bda commit b764ab8
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 103 deletions.
5 changes: 1 addition & 4 deletions vm/core/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package core
import (
"fmt"

"github.com/spacemeshos/go-scale"
"github.com/spacemeshos/go-spacemesh/common/types"
)

Expand All @@ -22,13 +21,11 @@ type Context struct {
PrincipalTemplate Template
PrincipalAccount Account

ParseOutput ParseOutput
Gas struct {
Gas struct {
BaseGas uint64
FixedGas uint64
}
Header Header
Args scale.Encodable

// consumed is in gas units and will be used
consumed uint64
Expand Down
2 changes: 1 addition & 1 deletion vm/core/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ var (
// ErrSpawned raised if account already spawned.
ErrSpawned = errors.New("account already spawned")
// ErrNotSpawned raised if account is not spawned.
ErrNotSpawned = errors.New("account is not spawned")
ErrNotSpawned = errors.New("principal account is not spawned")
// ErrTemplateMismatch raised if target account doesn't match template account.
ErrTemplateMismatch = errors.New("relay template mismatch")
// ErrTxLimit overflows max tx size.
Expand Down
6 changes: 1 addition & 5 deletions vm/core/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,9 @@ type (
type Handler interface {
// Parse header and arguments from the payload.
Parse(*scale.Decoder) (ParseOutput, error)
// TODO(lane): update to use the VM
// Args returns method arguments for the method.
Args() scale.Type

// TODO(lane): update to use the VM
// Exec dispatches execution request based on the method selector.
Exec(Host, scale.Encodable) error
Exec(Host, []byte) error

// New instantiates Template from spawn arguments.
New(any) (Template, error)
Expand Down
8 changes: 2 additions & 6 deletions vm/templates/wallet/handler.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package wallet

import (
"bytes"
"fmt"

"github.com/spacemeshos/go-scale"
Expand Down Expand Up @@ -46,16 +45,13 @@ func (*handler) New(args any) (core.Template, error) {

// Load single sig wallet from stored state.
func (*handler) Load(state []byte) (core.Template, error) {
decoder := scale.NewDecoder(bytes.NewReader(state))
// TODO(lane): pass blob into VM to instantiate the template instance (program)
var wallet Wallet
if _, err := wallet.DecodeScale(decoder); err != nil {
return nil, fmt.Errorf("%w: malformed state %w", core.ErrInternal, err)
}
return &wallet, nil
}

// Exec spawn or spend based on the method selector.
func (*handler) Exec(host core.Host, args scale.Encodable) error {
func (*handler) Exec(host core.Host, args []byte) error {
// TODO(lane): rewrite to use the VM
// switch method {
// case core.MethodSpawn:
Expand Down
114 changes: 27 additions & 87 deletions vm/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,6 @@ func (v *VM) execute(
continue
}
ctx := req.ctx
args := req.args

if header.GasPrice == 0 {
logger.Warn("ineffective transaction. zero gas price",
Expand Down Expand Up @@ -388,7 +387,7 @@ func (v *VM) execute(

err = ctx.Consume(ctx.Header.MaxGas)
if err == nil {
err = ctx.PrincipalHandler.Exec(ctx, args)
err = ctx.PrincipalHandler.Exec(ctx, tx.Raw)
}
if err != nil {
logger.Debug("transaction failed",
Expand Down Expand Up @@ -437,9 +436,8 @@ type Request struct {
raw types.RawTx
decoder *scale.Decoder

// both ctx and args are set after successful Parse
ctx *core.Context
args scale.Encodable
// ctx set after successful Parse
ctx *core.Context
}

// Parse header from the raw transaction.
Expand All @@ -448,12 +446,11 @@ func (r *Request) Parse() (*core.Header, error) {
if len(r.raw.Raw) > core.TxSizeLimit {
return nil, fmt.Errorf("%w: tx size (%d) > limit (%d)", core.ErrTxLimit, len(r.raw.Raw), core.TxSizeLimit)
}
header, ctx, args, err := parse(r.vm.logger, r.lid, r.vm.registry, r.cache, r.vm.cfg, r.raw.Raw, r.decoder)
header, ctx, err := parse(r.vm.logger, r.lid, r.vm.registry, r.cache, r.vm.cfg, r.raw.Raw, r.decoder)
if err != nil {
return nil, err
}
r.ctx = ctx
r.args = args
transactionDurationParse.Observe(float64(time.Since(start)))
return header, nil
}
Expand All @@ -477,131 +474,74 @@ func parse(
cfg Config,
raw []byte,
decoder *scale.Decoder,
) (*core.Header, *core.Context, scale.Encodable, error) {
) (*core.Header, *core.Context, error) {
version, _, err := scale.DecodeCompact8(decoder)
if err != nil {
return nil, nil, nil, fmt.Errorf("%w: failed to decode version %w", core.ErrMalformed, err)
return nil, nil, fmt.Errorf("%w: failed to decode version %w", core.ErrMalformed, err)
}
// v1 is an Athena-compatible transaction
if version != 1 {
return nil, nil, nil, fmt.Errorf("%w: unsupported version %d", core.ErrMalformed, version)
return nil, nil, fmt.Errorf("%w: unsupported version %d", core.ErrMalformed, version)
}

var principal core.Address
if _, err := principal.DecodeScale(decoder); err != nil {
return nil, nil, nil, fmt.Errorf("%w failed to decode principal: %w", core.ErrMalformed, err)
return nil, nil, fmt.Errorf("%w failed to decode principal: %w", core.ErrMalformed, err)
}
account, err := loader.Get(principal)
principalAccount, err := loader.Get(principal)
if err != nil {
return nil, nil, nil, fmt.Errorf(
return nil, nil, fmt.Errorf(
"%w: failed load state for principal %s - %w",
core.ErrInternal,
principal,
err,
)
}
logger.Debug("loaded principal account state", zap.Inline(&account))
logger.Debug("loaded principal account state", zap.Inline(&principalAccount))

ctx := &core.Context{
GenesisID: cfg.GenesisID,
Registry: reg,
Loader: loader,
PrincipalAccount: account,
PrincipalAccount: principalAccount,
LayerID: lid,
}

if account.TemplateAddress != nil {
ctx.PrincipalHandler = reg.Get(*account.TemplateAddress)
// principal must already have been spawned
// otherwise, we cannot verify the tx in order to pay fees
if principalAccount.TemplateAddress == nil {
return nil, nil, core.ErrNotSpawned
} else {
ctx.PrincipalHandler = reg.Get(*principalAccount.TemplateAddress)
if ctx.PrincipalHandler == nil {
return nil, nil, nil, fmt.Errorf("%w: unknown template %s", core.ErrMalformed, *account.TemplateAddress)
return nil, nil, fmt.Errorf("%w: unknown template %s", core.ErrMalformed, *principalAccount.TemplateAddress)
}
ctx.PrincipalTemplate, err = ctx.PrincipalHandler.Load(account.State)
ctx.PrincipalTemplate, err = ctx.PrincipalHandler.Load(principalAccount.State)
if err != nil {
return nil, nil, nil, err
return nil, nil, err
}
}

var (
templateAddress *core.Address
handler core.Handler
)
// TODO(lane): rewrite to use VM
// if method == core.MethodSpawn {
// // the transaction is either spawn or self-spawn
// templateAddress = &core.Address{}
// if _, err := templateAddress.DecodeScale(decoder); err != nil {
// return nil, nil, nil, fmt.Errorf("%w failed to decode template address %w", core.ErrMalformed, err)
// }
// handler = reg.Get(*templateAddress)
// if handler == nil {
// return nil, nil, nil, fmt.Errorf("%w: unknown template %s", core.ErrMalformed, *templateAddress)
// }
// if ctx.PrincipalHandler == nil {
// // spawn is not possible if principal is not spawned
// // so this must be self-spawn, but we can't tell before decoding arguments
// ctx.PrincipalHandler = handler
// }
// } else {
// this is any other call transaction
if account.TemplateAddress == nil {
return nil, nil, nil, core.ErrNotSpawned
}
templateAddress = account.TemplateAddress
handler = ctx.PrincipalHandler
// }
output, err := ctx.PrincipalHandler.Parse(decoder)
if err != nil {
return nil, nil, nil, err
}
args := handler.Args()
if args == nil {
return nil, nil, nil, fmt.Errorf("%w: unknown method %s", core.ErrMalformed, *templateAddress)
}
if _, err := args.DecodeScale(decoder); err != nil {
return nil, nil, nil, fmt.Errorf("%w failed to decode method arguments %w", core.ErrMalformed, err)
}
// TODO(lane): rewrite to use VM
// if method == core.MethodSpawn {
// if core.ComputePrincipal(*templateAddress, args) == principal {
// // this is a self spawn. if it fails validation - discard it immediately
// panic("self-spawn not implemented")
// // ctx.PrincipalTemplate, err = ctx.PrincipalHandler.New(args)
// // if err != nil {
// // return nil, nil, nil, err
// // }
// // ctx.Gas.FixedGas += ctx.PrincipalTemplate.ExecGas()
// } else if account.TemplateAddress == nil {
// return nil, nil, nil, fmt.Errorf("%w: account can't spawn until it is spawned itself",
// core.ErrNotSpawned)
// } else {
// target, err := handler.New(args)
// if err != nil {
// return nil, nil, nil, err
// }
// ctx.Gas.FixedGas += ctx.PrincipalTemplate.LoadGas()
// ctx.Gas.FixedGas += target.ExecGas()
// }
// } else {
return nil, nil, err
}
ctx.Gas.FixedGas += ctx.PrincipalTemplate.LoadGas()
ctx.Gas.FixedGas += ctx.PrincipalTemplate.ExecGas()
// }
ctx.Gas.BaseGas = ctx.PrincipalTemplate.BaseGas()

ctx.ParseOutput = output

ctx.Header.Principal = principal
ctx.Header.TemplateAddress = *templateAddress
ctx.Header.TemplateAddress = *principalAccount.TemplateAddress
ctx.Header.MaxGas = core.MaxGas(ctx.Gas.BaseGas, ctx.Gas.FixedGas, raw)
ctx.Header.GasPrice = output.GasPrice
ctx.Header.Nonce = output.Nonce
ctx.Args = args

maxspend, err := ctx.PrincipalTemplate.MaxSpend(args)
maxspend, err := ctx.PrincipalTemplate.MaxSpend(raw)
if err != nil {
return nil, nil, nil, err
return nil, nil, err
}
ctx.Header.MaxSpend = maxspend
return &ctx.Header, ctx, args, nil
return &ctx.Header, ctx, nil
}

func verify(ctx *core.Context, raw []byte, dec *scale.Decoder) bool {
Expand Down

0 comments on commit b764ab8

Please sign in to comment.