mirror of https://github.com/poanetwork/gecko.git
Add address tracking to atomic utxos
This commit is contained in:
parent
a7f2a887ca
commit
9da52e70c0
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue