gecko/vms/avm/import_tx.go

184 lines
4.4 KiB
Go
Raw Normal View History

2020-03-23 08:41:02 -07:00
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package avm
import (
"errors"
2020-03-23 13:40:37 -07:00
"github.com/ava-labs/gecko/chains/atomic"
2020-03-23 08:41:02 -07:00
"github.com/ava-labs/gecko/database"
2020-03-24 09:39:25 -07:00
"github.com/ava-labs/gecko/database/versiondb"
2020-03-23 08:41:02 -07:00
"github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/snow"
2020-03-24 09:39:25 -07:00
"github.com/ava-labs/gecko/snow/choices"
2020-03-23 13:40:37 -07:00
"github.com/ava-labs/gecko/vms/components/ava"
2020-03-23 08:41:02 -07:00
"github.com/ava-labs/gecko/vms/components/codec"
"github.com/ava-labs/gecko/vms/components/verify"
)
2020-03-23 13:40:37 -07:00
var (
errNilUTXOID = errors.New("nil utxo ID is not valid")
)
2020-03-23 08:41:02 -07:00
// ImportTx is a transaction that imports an asset from another blockchain.
type ImportTx struct {
BaseTx `serialize:"true"`
Outs []*TransferableOutput `serialize:"true"` // The outputs of this transaction
2020-03-24 09:39:25 -07:00
Ins []*TransferableInput `serialize:"true"` // The inputs to this transaction
2020-03-23 08:41:02 -07:00
}
// InputUTXOs track which UTXOs this transaction is consuming.
2020-03-23 13:40:37 -07:00
func (t *ImportTx) InputUTXOs() []*ava.UTXOID {
2020-03-23 08:41:02 -07:00
utxos := t.BaseTx.InputUTXOs()
for _, in := range t.Ins {
2020-03-24 09:39:25 -07:00
in.Symbol = true
utxos = append(utxos, &in.UTXOID)
2020-03-23 08:41:02 -07:00
}
return utxos
}
// AssetIDs returns the IDs of the assets this transaction depends on
func (t *ImportTx) AssetIDs() ids.Set {
assets := t.BaseTx.AssetIDs()
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 *ImportTx) UTXOs() []*ava.UTXO {
2020-03-23 08:41:02 -07:00
txID := t.ID()
utxos := t.BaseTx.UTXOs()
for _, out := range t.Outs {
2020-03-23 13:40:37 -07:00
utxos = append(utxos, &ava.UTXO{
UTXOID: ava.UTXOID{
2020-03-23 08:41:02 -07:00
TxID: txID,
OutputIndex: uint32(len(utxos)),
},
2020-03-23 13:40:37 -07:00
Asset: ava.Asset{ID: out.AssetID()},
2020-03-23 08:41:02 -07:00
Out: out.Out,
})
}
return utxos
}
var (
2020-03-24 09:39:25 -07:00
errNoImportInputs = errors.New("no import inputs")
2020-03-23 08:41:02 -07:00
)
// SyntacticVerify that this transaction is well-formed.
func (t *ImportTx) SyntacticVerify(ctx *snow.Context, c codec.Codec, numFxs int) error {
switch {
case t == nil:
return errNilTx
case len(t.Ins) == 0:
2020-03-24 09:39:25 -07:00
return errNoImportInputs
2020-03-23 08:41:02 -07:00
}
if err := t.BaseTx.SyntacticVerify(ctx, c, numFxs); err != nil {
return err
}
2020-03-23 13:40:37 -07:00
fc := ava.NewFlowChecker()
2020-03-23 08:41:02 -07:00
for _, out := range t.Outs {
if err := out.Verify(); err != nil {
return err
}
fc.Produce(out.AssetID(), out.Output().Amount())
}
if !IsSortedTransferableOutputs(t.Outs, c) {
return errOutputsNotSorted
}
for _, in := range t.Ins {
if err := in.Verify(); err != nil {
return err
}
fc.Consume(in.AssetID(), in.Input().Amount())
}
2020-03-24 09:39:25 -07:00
if !isSortedAndUniqueTransferableInputs(t.Ins) {
2020-03-23 08:41:02 -07:00
return errInputsNotSortedUnique
}
// TODO: Add the Tx fee to the produced side
2020-03-24 09:39:25 -07:00
return fc.Verify()
2020-03-23 08:41:02 -07:00
}
// SemanticVerify that this transaction is well-formed.
func (t *ImportTx) SemanticVerify(vm *VM, uTx *UniqueTx, creds []verify.Verifiable) error {
if err := t.BaseTx.SemanticVerify(vm, uTx, creds); err != nil {
return err
}
bID := ids.Empty // TODO: Needs to be set to the platform chain
smDB := vm.ctx.SharedMemory.GetDatabase(bID)
defer vm.ctx.SharedMemory.ReleaseDatabase(bID)
2020-03-23 13:40:37 -07:00
state := ava.NewPrefixedState(smDB, vm.codec)
2020-03-23 08:41:02 -07:00
offset := len(t.BaseTx.Ins)
for i, in := range t.Ins {
cred := creds[i+offset]
fxIndex, err := vm.getFx(cred)
if err != nil {
return err
}
fx := vm.fxs[fxIndex].Fx
2020-03-24 09:39:25 -07:00
utxoID := in.UTXOID.InputID()
2020-03-23 13:40:37 -07:00
utxo, err := state.PlatformUTXO(utxoID)
2020-03-23 08:41:02 -07:00
if err != nil {
return err
}
utxoAssetID := utxo.AssetID()
inAssetID := in.AssetID()
if !utxoAssetID.Equals(inAssetID) {
return errAssetIDMismatch
}
if !vm.verifyFxUsage(fxIndex, inAssetID) {
return errIncompatibleFx
}
if err := fx.VerifyTransfer(uTx, utxo.Out, in.In, cred); err != nil {
return err
}
}
return nil
}
2020-03-23 13:40:37 -07:00
// ExecuteWithSideEffects writes the batch with any additional side effects
func (t *ImportTx) ExecuteWithSideEffects(vm *VM, batch database.Batch) error {
bID := ids.Empty // TODO: Needs to be set to the platform chain
smDB := vm.ctx.SharedMemory.GetDatabase(bID)
defer vm.ctx.SharedMemory.ReleaseDatabase(bID)
vsmDB := versiondb.New(smDB)
state := ava.NewPrefixedState(vsmDB, vm.codec)
for _, in := range t.Ins {
2020-03-24 09:39:25 -07:00
utxoID := in.UTXOID.InputID()
if _, err := state.PlatformUTXO(utxoID); err == nil {
if err := state.SetPlatformUTXO(utxoID, nil); err != nil {
return err
}
} else if err := state.SetPlatformStatus(utxoID, choices.Accepted); err != nil {
2020-03-23 13:40:37 -07:00
return err
}
}
sharedBatch, err := vsmDB.CommitBatch()
if err != nil {
return err
}
return atomic.WriteAll(batch, sharedBatch)
}