mirror of https://github.com/poanetwork/gecko.git
209 lines
5.7 KiB
Go
209 lines
5.7 KiB
Go
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package ava
|
|
|
|
import (
|
|
"github.com/ava-labs/gecko/cache"
|
|
"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 (
|
|
stateCacheSize = 10000
|
|
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 {
|
|
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{
|
|
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 attempts to load a utxo from platform's storage.
|
|
func (s *PrefixedState) PlatformUTXO(id ids.ID) (*UTXO, error) {
|
|
return s.platform.UTXO(id)
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
|
|
// SpendPlatformUTXO consumes the provided platform utxo.
|
|
func (s *PrefixedState) SpendPlatformUTXO(utxoID ids.ID) error {
|
|
return s.platform.SpendUTXO(utxoID)
|
|
}
|
|
|
|
// 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.
|
|
func (s *PrefixedState) AVMUTXO(id ids.ID) (*UTXO, error) {
|
|
return s.avm.UTXO(id)
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
|
|
// SpendAVMUTXO consumes the provided platform utxo.
|
|
func (s *PrefixedState) SpendAVMUTXO(utxoID ids.ID) error {
|
|
return s.avm.SpendUTXO(utxoID)
|
|
}
|
|
|
|
// FundAVMUTXO adds the provided utxo to the database
|
|
func (s *PrefixedState) FundAVMUTXO(utxo *UTXO) error {
|
|
return s.avm.FundUTXO(utxo)
|
|
}
|