diff --git a/vms/avm/export_tx.go b/vms/avm/export_tx.go index 27d9e40..22fef13 100644 --- a/vms/avm/export_tx.go +++ b/vms/avm/export_tx.go @@ -8,7 +8,6 @@ import ( "github.com/ava-labs/gecko/database" "github.com/ava-labs/gecko/database/versiondb" "github.com/ava-labs/gecko/snow" - "github.com/ava-labs/gecko/snow/choices" "github.com/ava-labs/gecko/vms/components/ava" "github.com/ava-labs/gecko/vms/components/codec" "github.com/ava-labs/gecko/vms/components/verify" @@ -131,13 +130,7 @@ func (t *ExportTx) ExecuteWithSideEffects(vm *VM, batch database.Batch) error { Asset: ava.Asset{ID: out.AssetID()}, Out: out.Out, } - - utxoID := utxo.InputID() - if _, err := state.AVMStatus(utxoID); err == nil { - if err := state.SetAVMStatus(utxoID, choices.Unknown); err != nil { - return err - } - } else if err := state.SetAVMUTXO(utxoID, utxo); err != nil { + if err := state.FundAVMUTXO(utxo); err != nil { return err } } diff --git a/vms/avm/export_tx_test.go b/vms/avm/export_tx_test.go index 008df4f..a2af503 100644 --- a/vms/avm/export_tx_test.go +++ b/vms/avm/export_tx_test.go @@ -11,7 +11,6 @@ import ( "github.com/ava-labs/gecko/database/memdb" "github.com/ava-labs/gecko/ids" "github.com/ava-labs/gecko/snow" - "github.com/ava-labs/gecko/snow/choices" "github.com/ava-labs/gecko/snow/engine/common" "github.com/ava-labs/gecko/utils/crypto" "github.com/ava-labs/gecko/utils/logging" @@ -243,9 +242,6 @@ func TestIssueExportTx(t *testing.T) { if _, err := state.AVMUTXO(utxoID); err != nil { t.Fatal(err) } - if _, err := state.AVMStatus(utxoID); err == nil { - t.Fatalf("should have failed to read the status") - } } // Test force accepting an import transaction. @@ -374,7 +370,7 @@ func TestClearForceAcceptedExportTx(t *testing.T) { OutputIndex: 0, } utxoID := utxo.InputID() - if err := state.SetAVMStatus(utxoID, choices.Accepted); err != nil { + if err := state.SpendAVMUTXO(utxoID); err != nil { t.Fatal(err) } @@ -390,7 +386,4 @@ func TestClearForceAcceptedExportTx(t *testing.T) { if _, err := state.AVMUTXO(utxoID); err == nil { t.Fatalf("should have failed to read the utxo") } - if _, err := state.AVMStatus(utxoID); err == nil { - t.Fatalf("should have failed to read the status") - } } diff --git a/vms/avm/fx.go b/vms/avm/fx.go index ddf903f..3121031 100644 --- a/vms/avm/fx.go +++ b/vms/avm/fx.go @@ -30,9 +30,3 @@ type Fx interface { // credential, a non-nil error should be returned. VerifyOperation(tx interface{}, utxos, ins, creds, outs []interface{}) error } - -// FxAddressable is the interface a feature extension must provide to be able to -// be tracked as a part of the utxo set for a set of addresses -type FxAddressable interface { - Addresses() [][]byte -} diff --git a/vms/avm/import_tx.go b/vms/avm/import_tx.go index ed341be..41dc701 100644 --- a/vms/avm/import_tx.go +++ b/vms/avm/import_tx.go @@ -11,7 +11,6 @@ import ( "github.com/ava-labs/gecko/database/versiondb" "github.com/ava-labs/gecko/ids" "github.com/ava-labs/gecko/snow" - "github.com/ava-labs/gecko/snow/choices" "github.com/ava-labs/gecko/vms/components/ava" "github.com/ava-labs/gecko/vms/components/codec" "github.com/ava-labs/gecko/vms/components/verify" @@ -162,11 +161,7 @@ func (t *ImportTx) ExecuteWithSideEffects(vm *VM, batch database.Batch) error { state := ava.NewPrefixedState(vsmDB, vm.codec) for _, in := range t.Ins { 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 { + if err := state.SpendPlatformUTXO(utxoID); err != nil { return err } } diff --git a/vms/avm/import_tx_test.go b/vms/avm/import_tx_test.go index fb0bcfd..89b5dff 100644 --- a/vms/avm/import_tx_test.go +++ b/vms/avm/import_tx_test.go @@ -11,7 +11,6 @@ import ( "github.com/ava-labs/gecko/database/memdb" "github.com/ava-labs/gecko/ids" "github.com/ava-labs/gecko/snow" - "github.com/ava-labs/gecko/snow/choices" "github.com/ava-labs/gecko/snow/engine/common" "github.com/ava-labs/gecko/utils/crypto" "github.com/ava-labs/gecko/utils/logging" @@ -226,7 +225,7 @@ func TestIssueImportTx(t *testing.T) { } state := ava.NewPrefixedState(smDB, vm.codec) - if err := state.SetPlatformUTXO(utxoID.InputID(), utxo); err != nil { + if err := state.FundPlatformUTXO(utxo); err != nil { t.Fatal(err) } @@ -365,9 +364,5 @@ func TestForceAcceptImportTx(t *testing.T) { utxoSource := utxoID.InputID() if _, err := state.PlatformUTXO(utxoSource); err == nil { t.Fatalf("shouldn't have been able to read the utxo") - } else if status, err := state.PlatformStatus(utxoSource); err != nil { - t.Fatal(err) - } else if status != choices.Accepted { - t.Fatalf("should have marked the utxo as consumed") } } diff --git a/vms/avm/prefixed_state.go b/vms/avm/prefixed_state.go index e6ca8da..8a1898d 100644 --- a/vms/avm/prefixed_state.go +++ b/vms/avm/prefixed_state.go @@ -95,7 +95,7 @@ func (s *prefixedState) SpendUTXO(utxoID ids.ID) error { return err } - addressable, ok := utxo.Out.(FxAddressable) + addressable, ok := utxo.Out.(ava.Addressable) if !ok { return nil } @@ -124,7 +124,7 @@ func (s *prefixedState) FundUTXO(utxo *ava.UTXO) error { return err } - addressable, ok := utxo.Out.(FxAddressable) + addressable, ok := utxo.Out.(ava.Addressable) if !ok { return nil } diff --git a/vms/avm/service.go b/vms/avm/service.go index 4eacd75..a2a6bb3 100644 --- a/vms/avm/service.go +++ b/vms/avm/service.go @@ -947,3 +947,176 @@ func (service *Service) SignMintTx(r *http.Request, args *SignMintTxArgs, reply reply.Tx.Bytes = txBytes return nil } + +// SendExportArgs are arguments for passing into SendExport requests +type SendExportArgs struct { + Username string `json:"username"` + Password string `json:"password"` + Amount json.Uint64 `json:"amount"` + To string `json:"to"` +} + +// SendExportReply defines the Send replies returned from the API +type SendExportReply struct { + TxID ids.ID `json:"txID"` +} + +// SendExport returns the ID of the newly created atomic transaction +func (service *Service) SendExport(_ *http.Request, args *SendExportArgs, reply *SendExportReply) error { + service.vm.ctx.Log.Verbo("SendExport called with username: %s", args.Username) + + if args.Amount == 0 { + return errInvalidAmount + } + + toBytes, err := service.vm.Parse(args.To) + if err != nil { + return fmt.Errorf("problem parsing to address: %w", err) + } + to, err := ids.ToShortID(toBytes) + if err != nil { + return fmt.Errorf("problem parsing to address: %w", err) + } + + db, err := service.vm.ctx.Keystore.GetDatabase(args.Username, args.Password) + if err != nil { + return fmt.Errorf("problem retrieving user: %w", err) + } + + user := userState{vm: service.vm} + + addresses, _ := user.Addresses(db) + + addrs := ids.Set{} + addrs.Add(addresses...) + utxos, err := service.vm.GetUTXOs(addrs) + if err != nil { + return fmt.Errorf("problem retrieving user's UTXOs: %w", err) + } + + kc := secp256k1fx.NewKeychain() + for _, addr := range addresses { + sk, err := user.Key(db, addr) + if err != nil { + return fmt.Errorf("problem retrieving private key: %w", err) + } + kc.Add(sk) + } + + amountSpent := uint64(0) + time := service.vm.clock.Unix() + + ins := []*ava.TransferableInput{} + keys := [][]*crypto.PrivateKeySECP256K1R{} + for _, utxo := range utxos { + if !utxo.AssetID().Equals(service.vm.ava) { + continue + } + inputIntf, signers, err := kc.Spend(utxo.Out, time) + if err != nil { + continue + } + input, ok := inputIntf.(ava.Transferable) + if !ok { + continue + } + spent, err := math.Add64(amountSpent, input.Amount()) + if err != nil { + return errSpendOverflow + } + amountSpent = spent + + in := &ava.TransferableInput{ + UTXOID: utxo.UTXOID, + Asset: ava.Asset{ID: service.vm.ava}, + In: input, + } + + ins = append(ins, in) + keys = append(keys, signers) + + if amountSpent >= uint64(args.Amount) { + break + } + } + + if amountSpent < uint64(args.Amount) { + return errInsufficientFunds + } + + ava.SortTransferableInputsWithSigners(ins, keys) + + exportOuts := []*ava.TransferableOutput{&ava.TransferableOutput{ + Asset: ava.Asset{ID: service.vm.ava}, + Out: &secp256k1fx.TransferOutput{ + Amt: uint64(args.Amount), + Locktime: 0, + OutputOwners: secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{to}, + }, + }, + }} + + outs := []*ava.TransferableOutput{} + if amountSpent > uint64(args.Amount) { + changeAddr := kc.Keys[0].PublicKey().Address() + outs = append(outs, &ava.TransferableOutput{ + Asset: ava.Asset{ID: service.vm.ava}, + Out: &secp256k1fx.TransferOutput{ + Amt: amountSpent - uint64(args.Amount), + Locktime: 0, + OutputOwners: secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{changeAddr}, + }, + }, + }) + } + + ava.SortTransferableOutputs(outs, service.vm.codec) + + tx := Tx{UnsignedTx: &ExportTx{ + BaseTx: BaseTx{ + NetID: service.vm.ctx.NetworkID, + BCID: service.vm.ctx.ChainID, + Outs: outs, + Ins: ins, + }, + ExportOuts: exportOuts, + }} + + unsignedBytes, err := service.vm.codec.Marshal(&tx.UnsignedTx) + if err != nil { + return fmt.Errorf("problem creating transaction: %w", err) + } + hash := hashing.ComputeHash256(unsignedBytes) + + for _, credKeys := range keys { + cred := &secp256k1fx.Credential{} + for _, key := range credKeys { + sig, err := key.SignHash(hash) + if err != nil { + return fmt.Errorf("problem creating transaction: %w", err) + } + fixedSig := [crypto.SECP256K1RSigLen]byte{} + copy(fixedSig[:], sig) + + cred.Sigs = append(cred.Sigs, fixedSig) + } + tx.Creds = append(tx.Creds, cred) + } + + b, err := service.vm.codec.Marshal(tx) + if err != nil { + return fmt.Errorf("problem creating transaction: %w", err) + } + + txID, err := service.vm.IssueTx(b, nil) + if err != nil { + return fmt.Errorf("problem issuing transaction: %w", err) + } + + reply.TxID = txID + return nil +} diff --git a/vms/avm/state.go b/vms/avm/state.go index 809867a..6033b8b 100644 --- a/vms/avm/state.go +++ b/vms/avm/state.go @@ -63,43 +63,3 @@ func (s *state) SetTx(id ids.ID, tx *Tx) error { s.Cache.Put(id, tx) return s.DB.Put(id.Bytes(), tx.Bytes()) } - -// IDs returns a slice of IDs from storage -func (s *state) IDs(id ids.ID) ([]ids.ID, error) { - if idsIntf, found := s.Cache.Get(id); found { - if idSlice, ok := idsIntf.([]ids.ID); ok { - return idSlice, nil - } - return nil, errCacheTypeMismatch - } - - bytes, err := s.DB.Get(id.Bytes()) - if err != nil { - return nil, err - } - - idSlice := []ids.ID(nil) - if err := s.Codec.Unmarshal(bytes, &idSlice); err != nil { - return nil, err - } - - s.Cache.Put(id, idSlice) - return idSlice, nil -} - -// SetIDs saves a slice of IDs to the database. -func (s *state) SetIDs(id ids.ID, idSlice []ids.ID) error { - if len(idSlice) == 0 { - s.Cache.Evict(id) - return s.DB.Delete(id.Bytes()) - } - - s.Cache.Put(id, idSlice) - - bytes, err := s.Codec.Marshal(idSlice) - if err != nil { - return err - } - - return s.DB.Put(id.Bytes(), bytes) -} diff --git a/vms/components/ava/prefixed_state.go b/vms/components/ava/prefixed_state.go index 1cd0c8e..dd7f3e8 100644 --- a/vms/components/ava/prefixed_state.go +++ b/vms/components/ava/prefixed_state.go @@ -8,14 +8,23 @@ import ( "github.com/ava-labs/gecko/database" "github.com/ava-labs/gecko/ids" "github.com/ava-labs/gecko/snow/choices" + "github.com/ava-labs/gecko/utils/hashing" "github.com/ava-labs/gecko/vms/components/codec" ) +// Addressable is the interface a feature extension must provide to be able to +// be tracked as a part of the utxo set for a set of addresses +type Addressable interface { + Addresses() [][]byte +} + const ( platformUTXOID uint64 = iota platformStatusID + platformFundsID avmUTXOID avmStatusID + avmFundsID ) const ( @@ -23,65 +32,177 @@ const ( idCacheSize = 10000 ) +type chainState struct { + *State + + utxoIDPrefix, statusIDPrefix, fundsIDPrefix uint64 + utxoID, statusID, fundsID cache.Cacher +} + +// UTXO attempts to load a utxo from platform's storage. +func (s *chainState) UTXO(id ids.ID) (*UTXO, error) { + return s.State.UTXO(UniqueID(id, s.utxoIDPrefix, s.utxoID)) +} + +// Funds returns the mapping from the 32 byte representation of an +// address to a list of utxo IDs that reference the address. +func (s *chainState) Funds(id ids.ID) ([]ids.ID, error) { + return s.IDs(UniqueID(id, s.fundsIDPrefix, s.fundsID)) +} + +// SpendUTXO consumes the provided platform utxo. +func (s *chainState) SpendUTXO(utxoID ids.ID) error { + utxo, err := s.UTXO(utxoID) + if err != nil { + return s.setStatus(utxoID, choices.Accepted) + } else if err := s.setUTXO(utxoID, nil); err != nil { + return err + } + + if addressable, ok := utxo.Out.(Addressable); ok { + return s.removeUTXO(addressable.Addresses(), utxoID) + } + return nil +} + +// FundUTXO adds the provided utxo to the database +func (s *chainState) FundUTXO(utxo *UTXO) error { + utxoID := utxo.InputID() + if _, err := s.status(utxoID); err == nil { + return s.setStatus(utxoID, choices.Unknown) + } else if err := s.setUTXO(utxoID, utxo); err != nil { + return err + } + + if addressable, ok := utxo.Out.(Addressable); ok { + return s.addUTXO(addressable.Addresses(), utxoID) + } + return nil +} + +// setUTXO saves the provided utxo to platform's storage. +func (s *chainState) setUTXO(id ids.ID, utxo *UTXO) error { + return s.SetUTXO(UniqueID(id, s.utxoIDPrefix, s.utxoID), utxo) +} + +func (s *chainState) status(id ids.ID) (choices.Status, error) { + return s.Status(UniqueID(id, s.statusIDPrefix, s.statusID)) +} + +// setStatus saves the provided platform status to storage. +func (s *chainState) setStatus(id ids.ID, status choices.Status) error { + return s.State.SetStatus(UniqueID(id, s.statusIDPrefix, s.statusID), status) +} + +func (s *chainState) removeUTXO(addrs [][]byte, utxoID ids.ID) error { + for _, addr := range addrs { + addrID := ids.NewID(hashing.ComputeHash256Array(addr)) + utxos := ids.Set{} + funds, _ := s.Funds(addrID) + utxos.Add(funds...) + utxos.Remove(utxoID) + if err := s.setFunds(addrID, utxos.List()); err != nil { + return err + } + } + return nil +} + +func (s *chainState) addUTXO(addrs [][]byte, utxoID ids.ID) error { + for _, addr := range addrs { + addrID := ids.NewID(hashing.ComputeHash256Array(addr)) + utxos := ids.Set{} + funds, _ := s.Funds(addrID) + utxos.Add(funds...) + utxos.Add(utxoID) + if err := s.setFunds(addrID, utxos.List()); err != nil { + return err + } + } + return nil +} + +func (s *chainState) setFunds(id ids.ID, idSlice []ids.ID) error { + return s.SetIDs(UniqueID(id, s.fundsIDPrefix, s.fundsID), idSlice) +} + // PrefixedState wraps a state object. By prefixing the state, there will // be no collisions between different types of objects that have the same hash. type PrefixedState struct { - State - - platformUTXO, platformStatus, avmUTXO, avmStatus cache.Cacher + platform, avm chainState } // NewPrefixedState ... func NewPrefixedState(db database.Database, codec codec.Codec) *PrefixedState { + state := &State{ + Cache: &cache.LRU{Size: stateCacheSize}, + DB: db, + Codec: codec, + } return &PrefixedState{ - State: State{ - Cache: &cache.LRU{Size: stateCacheSize}, - DB: db, - Codec: codec, + platform: chainState{ + State: state, + + utxoIDPrefix: platformUTXOID, + statusIDPrefix: platformStatusID, + fundsIDPrefix: platformFundsID, + + utxoID: &cache.LRU{Size: idCacheSize}, + statusID: &cache.LRU{Size: idCacheSize}, + fundsID: &cache.LRU{Size: idCacheSize}, + }, + avm: chainState{ + State: state, + + utxoIDPrefix: avmUTXOID, + statusIDPrefix: avmStatusID, + fundsIDPrefix: avmFundsID, + + utxoID: &cache.LRU{Size: idCacheSize}, + statusID: &cache.LRU{Size: idCacheSize}, + fundsID: &cache.LRU{Size: idCacheSize}, }, - platformUTXO: &cache.LRU{Size: idCacheSize}, - platformStatus: &cache.LRU{Size: idCacheSize}, - avmUTXO: &cache.LRU{Size: idCacheSize}, - avmStatus: &cache.LRU{Size: idCacheSize}, } } // PlatformUTXO attempts to load a utxo from platform's storage. func (s *PrefixedState) PlatformUTXO(id ids.ID) (*UTXO, error) { - return s.UTXO(UniqueID(id, platformUTXOID, s.platformUTXO)) + return s.platform.UTXO(id) } -// SetPlatformUTXO saves the provided utxo to platform's storage. -func (s *PrefixedState) SetPlatformUTXO(id ids.ID, utxo *UTXO) error { - return s.SetUTXO(UniqueID(id, platformUTXOID, s.platformUTXO), utxo) +// PlatformFunds returns the mapping from the 32 byte representation of an +// address to a list of utxo IDs that reference the address. +func (s *PrefixedState) PlatformFunds(id ids.ID) ([]ids.ID, error) { + return s.platform.Funds(id) } -// PlatformStatus returns the platform status from storage. -func (s *PrefixedState) PlatformStatus(id ids.ID) (choices.Status, error) { - return s.Status(UniqueID(id, platformStatusID, s.platformStatus)) +// SpendPlatformUTXO consumes the provided platform utxo. +func (s *PrefixedState) SpendPlatformUTXO(utxoID ids.ID) error { + return s.platform.SpendUTXO(utxoID) } -// SetPlatformStatus saves the provided platform status to storage. -func (s *PrefixedState) SetPlatformStatus(id ids.ID, status choices.Status) error { - return s.SetStatus(UniqueID(id, platformStatusID, s.platformStatus), status) +// FundPlatformUTXO adds the provided utxo to the database +func (s *PrefixedState) FundPlatformUTXO(utxo *UTXO) error { + return s.platform.FundUTXO(utxo) } -// AVMUTXO attempts to load a utxo from AVM's storage. +// AVMUTXO attempts to load a utxo from avm's storage. func (s *PrefixedState) AVMUTXO(id ids.ID) (*UTXO, error) { - return s.UTXO(UniqueID(id, avmUTXOID, s.platformUTXO)) + return s.avm.UTXO(id) } -// SetAVMUTXO saves the provided utxo to AVM's storage. -func (s *PrefixedState) SetAVMUTXO(id ids.ID, utxo *UTXO) error { - return s.SetUTXO(UniqueID(id, avmUTXOID, s.platformUTXO), utxo) +// AVMFunds returns the mapping from the 32 byte representation of an +// address to a list of utxo IDs that reference the address. +func (s *PrefixedState) AVMFunds(id ids.ID) ([]ids.ID, error) { + return s.avm.Funds(id) } -// AVMStatus returns the AVM status from storage. -func (s *PrefixedState) AVMStatus(id ids.ID) (choices.Status, error) { - return s.Status(UniqueID(id, avmStatusID, s.platformStatus)) +// SpendAVMUTXO consumes the provided platform utxo. +func (s *PrefixedState) SpendAVMUTXO(utxoID ids.ID) error { + return s.avm.SpendUTXO(utxoID) } -// SetAVMStatus saves the provided platform status to storage. -func (s *PrefixedState) SetAVMStatus(id ids.ID, status choices.Status) error { - return s.SetStatus(UniqueID(id, avmStatusID, s.platformStatus), status) +// FundAVMUTXO adds the provided utxo to the database +func (s *PrefixedState) FundAVMUTXO(utxo *UTXO) error { + return s.avm.FundUTXO(utxo) } diff --git a/vms/components/ava/state.go b/vms/components/ava/state.go index f170d50..a9c5424 100644 --- a/vms/components/ava/state.go +++ b/vms/components/ava/state.go @@ -111,3 +111,43 @@ func (s *State) SetStatus(id ids.ID, status choices.Status) error { } return s.DB.Put(id.Bytes(), bytes) } + +// IDs returns a slice of IDs from storage +func (s *State) IDs(id ids.ID) ([]ids.ID, error) { + if idsIntf, found := s.Cache.Get(id); found { + if idSlice, ok := idsIntf.([]ids.ID); ok { + return idSlice, nil + } + return nil, errCacheTypeMismatch + } + + bytes, err := s.DB.Get(id.Bytes()) + if err != nil { + return nil, err + } + + idSlice := []ids.ID(nil) + if err := s.Codec.Unmarshal(bytes, &idSlice); err != nil { + return nil, err + } + + s.Cache.Put(id, idSlice) + return idSlice, nil +} + +// SetIDs saves a slice of IDs to the database. +func (s *State) SetIDs(id ids.ID, idSlice []ids.ID) error { + if len(idSlice) == 0 { + s.Cache.Evict(id) + return s.DB.Delete(id.Bytes()) + } + + s.Cache.Put(id, idSlice) + + bytes, err := s.Codec.Marshal(idSlice) + if err != nil { + return err + } + + return s.DB.Put(id.Bytes(), bytes) +} diff --git a/vms/platformvm/add_default_subnet_delegator_tx_test.go b/vms/platformvm/add_default_subnet_delegator_tx_test.go index 46ba21e..9d6d5cf 100644 --- a/vms/platformvm/add_default_subnet_delegator_tx_test.go +++ b/vms/platformvm/add_default_subnet_delegator_tx_test.go @@ -325,9 +325,9 @@ func TestAddDefaultSubnetDelegatorTxSemanticVerify(t *testing.T) { } tx, err = vm.newAddDefaultSubnetDelegatorTx( - defaultNonce+1, // nonce - defaultStakeAmount, // weight - uint64(newTimestamp.Unix()), // start time + defaultNonce+1, // nonce + defaultStakeAmount, // weight + uint64(newTimestamp.Unix()), // start time uint64(newTimestamp.Add(MinimumStakingDuration).Unix()), // end time defaultKey.PublicKey().Address(), // node ID defaultKey.PublicKey().Address(), // destination diff --git a/vms/platformvm/export_tx.go b/vms/platformvm/export_tx.go index f3e3af3..e19f482 100644 --- a/vms/platformvm/export_tx.go +++ b/vms/platformvm/export_tx.go @@ -10,7 +10,6 @@ import ( "github.com/ava-labs/gecko/database" "github.com/ava-labs/gecko/database/versiondb" "github.com/ava-labs/gecko/ids" - "github.com/ava-labs/gecko/snow/choices" "github.com/ava-labs/gecko/utils/crypto" "github.com/ava-labs/gecko/utils/hashing" "github.com/ava-labs/gecko/utils/math" @@ -156,13 +155,7 @@ func (tx *ExportTx) Accept(batch database.Batch) error { Asset: ava.Asset{ID: out.AssetID()}, Out: out.Out, } - - utxoID := utxo.InputID() - if _, err := state.PlatformStatus(utxoID); err == nil { - if err := state.SetPlatformStatus(utxoID, choices.Unknown); err != nil { - return err - } - } else if err := state.SetPlatformUTXO(utxoID, utxo); err != nil { + if err := state.FundPlatformUTXO(utxo); err != nil { return err } } diff --git a/vms/platformvm/import_tx.go b/vms/platformvm/import_tx.go index aaa7aad..b92649f 100644 --- a/vms/platformvm/import_tx.go +++ b/vms/platformvm/import_tx.go @@ -11,7 +11,6 @@ import ( "github.com/ava-labs/gecko/database" "github.com/ava-labs/gecko/database/versiondb" "github.com/ava-labs/gecko/ids" - "github.com/ava-labs/gecko/snow/choices" "github.com/ava-labs/gecko/utils/crypto" "github.com/ava-labs/gecko/utils/hashing" "github.com/ava-labs/gecko/utils/math" @@ -218,11 +217,7 @@ func (tx *ImportTx) Accept(batch database.Batch) error { state := ava.NewPrefixedState(vsmDB, Codec) for _, in := range tx.Ins { utxoID := in.UTXOID.InputID() - if _, err := state.AVMUTXO(utxoID); err == nil { - if err := state.SetAVMUTXO(utxoID, nil); err != nil { - return err - } - } else if err := state.SetAVMStatus(utxoID, choices.Accepted); err != nil { + if err := state.SpendAVMUTXO(utxoID); err != nil { return err } } diff --git a/vms/platformvm/vm_test.go b/vms/platformvm/vm_test.go index af9d41c..d19b06c 100644 --- a/vms/platformvm/vm_test.go +++ b/vms/platformvm/vm_test.go @@ -1128,7 +1128,7 @@ func TestAtomicImport(t *testing.T) { } state := ava.NewPrefixedState(smDB, Codec) - if err := state.SetAVMUTXO(utxoID.InputID(), utxo); err != nil { + if err := state.FundAVMUTXO(utxo); err != nil { t.Fatal(err) } @@ -1221,12 +1221,4 @@ func TestOptimisticAtomicImport(t *testing.T) { if newAccount.Balance != previousAccount.Balance+amount { t.Fatalf("failed to provide funds") } - - smDB := vm.Ctx.SharedMemory.GetDatabase(avmID) - defer vm.Ctx.SharedMemory.ReleaseDatabase(avmID) - - state := ava.NewPrefixedState(smDB, Codec) - if _, err := state.AVMStatus(utxoID.InputID()); err != nil { - t.Fatal(err) - } }