gecko/vms/avm/base_tx.go

151 lines
3.9 KiB
Go
Raw Normal View History

2020-03-10 12:20:34 -07:00
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package avm
import (
"errors"
2020-03-20 17:56:43 -07:00
"github.com/ava-labs/gecko/database"
2020-03-10 12:20:34 -07:00
"github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/snow"
2020-03-23 13:40:37 -07:00
"github.com/ava-labs/gecko/vms/components/ava"
2020-03-10 12:20:34 -07:00
"github.com/ava-labs/gecko/vms/components/codec"
2020-03-19 15:36:10 -07:00
"github.com/ava-labs/gecko/vms/components/verify"
2020-03-10 12:20:34 -07:00
)
var (
errNilTx = errors.New("nil tx is not valid")
errWrongNetworkID = errors.New("tx has wrong network ID")
errWrongChainID = errors.New("tx has wrong chain ID")
errOutputsNotSorted = errors.New("outputs not sorted")
errInputsNotSortedUnique = errors.New("inputs not sorted and unique")
errInputOverflow = errors.New("inputs overflowed uint64")
errOutputOverflow = errors.New("outputs overflowed uint64")
errInsufficientFunds = errors.New("insufficient funds")
)
// BaseTx is the basis of all transactions.
type BaseTx struct {
2020-03-23 13:40:37 -07:00
ava.Metadata
2020-03-10 12:20:34 -07:00
2020-03-29 11:46:18 -07:00
NetID uint32 `serialize:"true" json:"networkID"` // ID of the network this chain lives on
BCID ids.ID `serialize:"true" json:"blockchainID"` // ID of the chain on which this transaction exists (prevents replay attacks)
Outs []*ava.TransferableOutput `serialize:"true" json:"outputs"` // The outputs of this transaction
Ins []*ava.TransferableInput `serialize:"true" json:"inputs"` // The inputs to this transaction
2020-03-10 12:20:34 -07:00
}
// InputUTXOs track which UTXOs this transaction is consuming.
2020-03-23 13:40:37 -07:00
func (t *BaseTx) InputUTXOs() []*ava.UTXOID {
utxos := []*ava.UTXOID(nil)
2020-03-10 12:20:34 -07:00
for _, in := range t.Ins {
utxos = append(utxos, &in.UTXOID)
}
return utxos
}
// AssetIDs returns the IDs of the assets this transaction depends on
func (t *BaseTx) AssetIDs() ids.Set {
assets := ids.Set{}
for _, in := range t.Ins {
assets.Add(in.AssetID())
}
return assets
}
// UTXOs returns the UTXOs transaction is producing.
2020-03-23 13:40:37 -07:00
func (t *BaseTx) UTXOs() []*ava.UTXO {
2020-03-10 12:20:34 -07:00
txID := t.ID()
2020-03-23 13:40:37 -07:00
utxos := make([]*ava.UTXO, len(t.Outs))
2020-03-10 12:20:34 -07:00
for i, out := range t.Outs {
2020-03-23 13:40:37 -07:00
utxos[i] = &ava.UTXO{
UTXOID: ava.UTXOID{
2020-03-10 12:20:34 -07:00
TxID: txID,
OutputIndex: uint32(i),
},
2020-03-23 13:40:37 -07:00
Asset: ava.Asset{ID: out.AssetID()},
2020-03-20 17:56:43 -07:00
Out: out.Out,
2020-03-10 12:20:34 -07:00
}
}
return utxos
}
// SyntacticVerify that this transaction is well-formed.
func (t *BaseTx) SyntacticVerify(ctx *snow.Context, c codec.Codec, _ int) error {
switch {
case t == nil:
return errNilTx
case t.NetID != ctx.NetworkID:
return errWrongNetworkID
case !t.BCID.Equals(ctx.ChainID):
return errWrongChainID
}
2020-03-23 13:40:37 -07:00
fc := ava.NewFlowChecker()
2020-03-10 12:20:34 -07:00
for _, out := range t.Outs {
if err := out.Verify(); err != nil {
return err
}
2020-03-20 17:56:43 -07:00
fc.Produce(out.AssetID(), out.Output().Amount())
2020-03-10 12:20:34 -07:00
}
2020-03-25 20:48:21 -07:00
if !ava.IsSortedTransferableOutputs(t.Outs, c) {
2020-03-10 12:20:34 -07:00
return errOutputsNotSorted
}
for _, in := range t.Ins {
if err := in.Verify(); err != nil {
return err
}
2020-03-20 17:56:43 -07:00
fc.Consume(in.AssetID(), in.Input().Amount())
2020-03-10 12:20:34 -07:00
}
2020-03-25 20:48:21 -07:00
if !ava.IsSortedAndUniqueTransferableInputs(t.Ins) {
2020-03-10 12:20:34 -07:00
return errInputsNotSortedUnique
}
2020-03-20 17:56:43 -07:00
// TODO: Add the Tx fee to the produced side
2020-03-10 12:20:34 -07:00
2020-03-20 17:56:43 -07:00
if err := fc.Verify(); err != nil {
return err
2020-03-10 12:20:34 -07:00
}
2020-03-23 13:40:37 -07:00
return t.Metadata.Verify()
2020-03-10 12:20:34 -07:00
}
// SemanticVerify that this transaction is valid to be spent.
2020-03-19 15:36:10 -07:00
func (t *BaseTx) SemanticVerify(vm *VM, uTx *UniqueTx, creds []verify.Verifiable) error {
2020-03-10 12:20:34 -07:00
for i, in := range t.Ins {
cred := creds[i]
2020-03-19 15:36:10 -07:00
fxIndex, err := vm.getFx(cred)
2020-03-10 12:20:34 -07:00
if err != nil {
return err
}
fx := vm.fxs[fxIndex].Fx
2020-03-20 17:56:43 -07:00
utxo, err := vm.getUTXO(&in.UTXOID)
if err != nil {
2020-03-10 12:20:34 -07:00
return err
}
utxoAssetID := utxo.AssetID()
inAssetID := in.AssetID()
if !utxoAssetID.Equals(inAssetID) {
return errAssetIDMismatch
}
if !vm.verifyFxUsage(fxIndex, inAssetID) {
return errIncompatibleFx
}
2020-03-19 15:36:10 -07:00
if err := fx.VerifyTransfer(uTx, utxo.Out, in.In, cred); err != nil {
2020-03-10 12:20:34 -07:00
return err
}
}
return nil
}
2020-03-20 17:56:43 -07:00
2020-03-23 13:40:37 -07:00
// ExecuteWithSideEffects writes the batch with any additional side effects
func (t *BaseTx) ExecuteWithSideEffects(_ *VM, batch database.Batch) error { return batch.Write() }