Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 18 additions & 4 deletions ocp/common/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/code-payments/ocp-server/ocp/config"
ocp_data "github.com/code-payments/ocp-server/ocp/data"
"github.com/code-payments/ocp-server/ocp/data/currency"
vm_metadata "github.com/code-payments/ocp-server/ocp/data/vm/metadata"
)

Expand Down Expand Up @@ -43,14 +44,27 @@ func GetVmConfigForMint(ctx context.Context, data ocp_data.Provider, mintAccount
return cached, nil
}

record, err := data.GetVmMetadataByMint(ctx, mintAddress)
currencyMetadataRecord, err := data.GetCurrencyMetadata(ctx, mintAddress)
if err == currency.ErrNotFound {
return nil, ErrUnsupportedMint
} else if err != nil {
return nil, err
}
if currencyMetadataRecord.State != currency.MetadataStateAvailable {
return nil, ErrUnsupportedMint
}

vmMetadataRecord, err := data.GetVmMetadataByMint(ctx, mintAddress)
if err == vm_metadata.ErrNotFound {
return nil, ErrUnsupportedMint
} else if err != nil {
return nil, err
}
if vmMetadataRecord.State != vm_metadata.StateAvailable {
return nil, ErrUnsupportedMint
}

vaultRecord, err := data.GetKey(ctx, record.Authority)
vaultRecord, err := data.GetKey(ctx, vmMetadataRecord.Authority)
if err != nil {
return nil, err
}
Expand All @@ -60,12 +74,12 @@ func GetVmConfigForMint(ctx context.Context, data ocp_data.Provider, mintAccount
return nil, err
}

vmAccount, err := NewAccountFromPublicKeyString(record.Vm)
vmAccount, err := NewAccountFromPublicKeyString(vmMetadataRecord.Vm)
if err != nil {
return nil, err
}

omnibusAccount, err := NewAccountFromPublicKeyString(record.Omnibus)
omnibusAccount, err := NewAccountFromPublicKeyString(vmMetadataRecord.Omnibus)
if err != nil {
return nil, err
}
Expand Down
17 changes: 12 additions & 5 deletions ocp/data/currency/memory/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import (
"sync"
"time"

"github.com/code-payments/ocp-server/ocp/data/currency"
"github.com/code-payments/ocp-server/database/query"
"github.com/code-payments/ocp-server/ocp/data/currency"
)

const (
Expand Down Expand Up @@ -158,21 +158,28 @@ func (s *store) GetExchangeRatesInRange(ctx context.Context, symbol string, inte
return all, nil
}

func (s *store) PutMetadata(ctx context.Context, data *currency.MetadataRecord) error {
func (s *store) SaveMetadata(ctx context.Context, data *currency.MetadataRecord) error {
if err := data.Validate(); err != nil {
return err
}

s.mu.Lock()
defer s.mu.Unlock()

// Not ideal but fine for testing the currency store
for _, item := range s.metadataRecords {
for i, item := range s.metadataRecords {
if item.Mint == data.Mint {
return currency.ErrExists
if item.Version != data.Version {
return currency.ErrStaleMetadataVersion
}

data.Version = item.Version + 1
data.Id = item.Id
s.metadataRecords[i] = data.Clone()
return nil
}
}

data.Version = 1
data.Id = s.lastMetadataIndex
s.metadataRecords = append(s.metadataRecords, data.Clone())
s.lastMetadataIndex = s.lastMetadataIndex + 1
Expand Down
17 changes: 17 additions & 0 deletions ocp/data/currency/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ import (
"github.com/code-payments/ocp-server/solana/currencycreator"
)

type MetadataState uint8

const (
MetadataStateUnknown MetadataState = iota
MetadataStateAvailable
// todo: define more states
)

type SocialLinkType uint8

const (
Expand Down Expand Up @@ -66,6 +74,9 @@ type MetadataRecord struct {

Alt string

State MetadataState
Version uint64

CreatedBy string
CreatedAt time.Time
}
Expand Down Expand Up @@ -197,6 +208,9 @@ func (m *MetadataRecord) Clone() *MetadataRecord {

Alt: m.Alt,

State: m.State,
Version: m.Version,

CreatedBy: m.CreatedBy,
CreatedAt: m.CreatedAt,
}
Expand Down Expand Up @@ -236,6 +250,9 @@ func (m *MetadataRecord) CopyTo(dst *MetadataRecord) {

dst.Alt = m.Alt

dst.State = m.State
dst.Version = m.Version

dst.CreatedBy = m.CreatedBy
dst.CreatedAt = m.CreatedAt
}
Expand Down
27 changes: 22 additions & 5 deletions ocp/data/currency/postgres/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ type metadataModel struct {

Alt string `db:"alt"`

State uint8 `db:"state"`
Version uint64 `db:"version"`

CreatedBy string `db:"created_by"`
CreatedAt time.Time `db:"created_at"`
}
Expand Down Expand Up @@ -127,6 +130,9 @@ func toMetadataModel(obj *currency.MetadataRecord) (*metadataModel, error) {

Alt: obj.Alt,

State: uint8(obj.State),
Version: obj.Version,

CreatedBy: obj.CreatedBy,
CreatedAt: obj.CreatedAt,
}, nil
Expand Down Expand Up @@ -172,6 +178,9 @@ func fromMetadataModel(obj *metadataModel) *currency.MetadataRecord {

Alt: obj.Alt,

State: currency.MetadataState(obj.State),
Version: obj.Version,

CreatedBy: obj.CreatedBy,
CreatedAt: obj.CreatedAt,
}
Expand Down Expand Up @@ -273,9 +282,15 @@ func (m *metadataModel) dbSave(ctx context.Context, db *sqlx.DB) error {
return pgutil.ExecuteInTx(ctx, db, sql.LevelDefault, func(tx *sqlx.Tx) error {
err := tx.QueryRowxContext(ctx,
`INSERT INTO `+metadataTableName+`
(name, symbol, description, image_url, bill_colors, social_links, seed, authority, mint, mint_bump, decimals, currency_config, currency_config_bump, liquidity_pool, liquidity_pool_bump, vault_mint, vault_mint_bump, vault_core, vault_core_bump, sell_fee_bps, alt, created_by, created_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23)
RETURNING id, name, symbol, description, image_url, bill_colors, social_links, seed, authority, mint, mint_bump, decimals, currency_config, currency_config_bump, liquidity_pool, liquidity_pool_bump, vault_mint, vault_mint_bump, vault_core, vault_core_bump, sell_fee_bps, alt, created_by, created_at`,
(name, symbol, description, image_url, bill_colors, social_links, seed, authority, mint, mint_bump, decimals, currency_config, currency_config_bump, liquidity_pool, liquidity_pool_bump, vault_mint, vault_mint_bump, vault_core, vault_core_bump, sell_fee_bps, alt, state, version, created_by, created_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23 + 1, $24, $25)

ON CONFLICT (mint)
DO UPDATE
SET state = $22, version = `+metadataTableName+`.version + 1
WHERE `+metadataTableName+`.mint = $9 AND `+metadataTableName+`.version = $23

RETURNING id, name, symbol, description, image_url, bill_colors, social_links, seed, authority, mint, mint_bump, decimals, currency_config, currency_config_bump, liquidity_pool, liquidity_pool_bump, vault_mint, vault_mint_bump, vault_core, vault_core_bump, sell_fee_bps, alt, state, version, created_by, created_at`,
m.Name,
m.Symbol,
m.Description,
Expand All @@ -297,11 +312,13 @@ func (m *metadataModel) dbSave(ctx context.Context, db *sqlx.DB) error {
m.VaultCoreBump,
m.SellFeeBps,
m.Alt,
m.State,
m.Version,
m.CreatedBy,
m.CreatedAt,
).StructScan(m)

return pgutil.CheckUniqueViolation(err, currency.ErrExists)
return pgutil.CheckNoRows(err, currency.ErrStaleMetadataVersion)
})
}

Expand Down Expand Up @@ -375,7 +392,7 @@ func dbGetAllExchangeRatesForRange(ctx context.Context, db *sqlx.DB, symbol stri
func dbGetMetadataByMint(ctx context.Context, db *sqlx.DB, mint string) (*metadataModel, error) {
res := &metadataModel{}
err := db.GetContext(ctx, res,
`SELECT id, name, symbol, description, image_url, bill_colors, social_links, seed, authority, mint, mint_bump, decimals, currency_config, currency_config_bump, liquidity_pool, liquidity_pool_bump, vault_mint, vault_mint_bump, vault_core, vault_core_bump, sell_fee_bps, alt, created_by, created_at
`SELECT id, name, symbol, description, image_url, bill_colors, social_links, seed, authority, mint, mint_bump, decimals, currency_config, currency_config_bump, liquidity_pool, liquidity_pool_bump, vault_mint, vault_mint_bump, vault_core, vault_core_bump, sell_fee_bps, alt, state, version, created_by, created_at
FROM `+metadataTableName+`
WHERE mint = $1`,
mint,
Expand Down
2 changes: 1 addition & 1 deletion ocp/data/currency/postgres/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func (s *store) GetExchangeRatesInRange(ctx context.Context, symbol string, inte
return res, nil
}

func (s *store) PutMetadata(ctx context.Context, record *currency.MetadataRecord) error {
func (s *store) SaveMetadata(ctx context.Context, record *currency.MetadataRecord) error {
model, err := toMetadataModel(record)
if err != nil {
return err
Expand Down
3 changes: 3 additions & 0 deletions ocp/data/currency/postgres/store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ const (

alt TEXT NOT NULL,

state INTEGER NOT NULL,
version BIGINT NOT NULL,

created_by TEXT NOT NULL,
created_at TIMESTAMP WITH TIME ZONE NOT NULL
);
Expand Down
15 changes: 9 additions & 6 deletions ocp/data/currency/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ import (
)

var (
ErrNotFound = errors.New("record not found")
ErrInvalidRange = errors.New("the provided range is not valid")
ErrInvalidInterval = errors.New("the provided interval is not valid")
ErrExists = errors.New("record exists")
ErrNotFound = errors.New("record not found")
ErrInvalidRange = errors.New("the provided range is not valid")
ErrInvalidInterval = errors.New("the provided interval is not valid")
ErrExists = errors.New("record exists")
ErrStaleMetadataVersion = errors.New("metadata version is stale")
)

type Store interface {
Expand Down Expand Up @@ -43,8 +44,10 @@ type Store interface {
// ErrInvalidInterval is returned if the interval is not valid
GetExchangeRatesInRange(ctx context.Context, symbol string, interval query.Interval, start time.Time, end time.Time, ordering query.Ordering) ([]*ExchangeRateRecord, error)

// PutMetadata puts currency creator metadata into the store
PutMetadata(ctx context.Context, record *MetadataRecord) error
// SaveMetadata creates or updates currency creator metadata in the store.
// On insert, Version is set to 1. On update, Version is incremented.
// ErrStaleMetadataVersion is returned when the provided version doesn't match.
SaveMetadata(ctx context.Context, record *MetadataRecord) error

// GetMetadata gets currency creator mint metadata by the mint address
GetMetadata(ctx context.Context, mint string) (*MetadataRecord, error)
Expand Down
76 changes: 73 additions & 3 deletions ocp/data/currency/tests/tests.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ func RunTests(t *testing.T, s currency.Store, teardown func()) {
testExchangeRateRoundTrip,
testGetExchangeRatesInRange,
testMetadataRoundTrip,
testMetadataSaveWithVersioning,
testGetAllMints,
testReserveRoundTrip,
testGetReservesInRange,
Expand Down Expand Up @@ -181,12 +182,15 @@ func testMetadataRoundTrip(t *testing.T, s currency.Store) {
assert.Equal(t, currency.ErrNotFound, err)

cloned := expected.Clone()
require.NoError(t, s.PutMetadata(context.Background(), expected))
require.NoError(t, s.SaveMetadata(context.Background(), expected))
assert.EqualValues(t, 1, expected.Id)
assert.EqualValues(t, 1, expected.Version)

actual, err := s.GetMetadata(context.Background(), expected.Mint)
require.NoError(t, err)
assertEquivalentMetadataRecords(t, cloned, actual)
assert.EqualValues(t, currency.MetadataStateUnknown, actual.State)
assert.EqualValues(t, 1, actual.Version)
}

func testGetAllMints(t *testing.T, s currency.Store) {
Expand Down Expand Up @@ -243,8 +247,8 @@ func testGetAllMints(t *testing.T, s currency.Store) {
record2.VaultCore = "vcore22222222222222222222222222222222222222222"
record2.Alt = "alt222222222222222222222222222222222222222222222"

require.NoError(t, s.PutMetadata(context.Background(), record1))
require.NoError(t, s.PutMetadata(context.Background(), record2))
require.NoError(t, s.SaveMetadata(context.Background(), record1))
require.NoError(t, s.SaveMetadata(context.Background(), record2))

mints, err = s.GetAllMints(context.Background())
require.NoError(t, err)
Expand Down Expand Up @@ -347,6 +351,71 @@ func testGetReservesInRange(t *testing.T, s currency.Store) {
require.NoError(t, err)
}

func testMetadataSaveWithVersioning(t *testing.T, s currency.Store) {
record := &currency.MetadataRecord{
Name: "Versioned",
Symbol: "VER",
Description: "A versioned test currency",
ImageUrl: "https://example.com/ver.png",
BillColors: []string{"#000000"},
SocialLinks: []currency.SocialLink{{Type: currency.SocialLinkTypeWebsite, Value: "https://example.com"}},

Seed: "verseed1",
Authority: "verauth1",

Mint: "vermint1111111111111111111111111111111111111111",
MintBump: 255,
Decimals: currencycreator.DefaultMintDecimals,

CurrencyConfig: "verconfig111111111111111111111111111111111111",
CurrencyConfigBump: 255,

LiquidityPool: "verpool1111111111111111111111111111111111111111",
LiquidityPoolBump: 255,

VaultMint: "vervmint111111111111111111111111111111111111111",
VaultMintBump: 255,

VaultCore: "vervcore111111111111111111111111111111111111111",
VaultCoreBump: 255,

SellFeeBps: currencycreator.DefaultSellFeeBps,

Alt: "veralt11111111111111111111111111111111111111111",

CreatedBy: "vercreator1",
CreatedAt: time.Now(),
}

// First save — insert
require.NoError(t, s.SaveMetadata(context.Background(), record))
assert.EqualValues(t, 1, record.Version)
assert.EqualValues(t, currency.MetadataStateUnknown, record.State)

// Update state and save again with correct version
record.State = currency.MetadataStateAvailable
require.NoError(t, s.SaveMetadata(context.Background(), record))
assert.EqualValues(t, 2, record.Version)
assert.EqualValues(t, currency.MetadataStateAvailable, record.State)

// Verify via get
actual, err := s.GetMetadata(context.Background(), record.Mint)
require.NoError(t, err)
assert.EqualValues(t, 2, actual.Version)
assert.EqualValues(t, currency.MetadataStateAvailable, actual.State)

// Attempt save with stale version
record.State = currency.MetadataStateUnknown
record.Version = 1
assert.Equal(t, currency.ErrStaleMetadataVersion, s.SaveMetadata(context.Background(), record))

// Verify via get
actual, err = s.GetMetadata(context.Background(), record.Mint)
require.NoError(t, err)
assert.EqualValues(t, 2, actual.Version)
assert.EqualValues(t, currency.MetadataStateAvailable, actual.State)
}

func assertEquivalentMetadataRecords(t *testing.T, obj1, obj2 *currency.MetadataRecord) {
assert.Equal(t, obj1.Name, obj2.Name)
assert.Equal(t, obj1.Symbol, obj2.Symbol)
Expand All @@ -369,6 +438,7 @@ func assertEquivalentMetadataRecords(t *testing.T, obj1, obj2 *currency.Metadata
assert.Equal(t, obj1.VaultCoreBump, obj2.VaultCoreBump)
assert.Equal(t, obj1.SellFeeBps, obj2.SellFeeBps)
assert.Equal(t, obj1.Alt, obj2.Alt)
assert.Equal(t, obj1.State, obj2.State)
assert.Equal(t, obj1.CreatedBy, obj2.CreatedBy)
assert.Equal(t, obj1.CreatedAt.Unix(), obj2.CreatedAt.Unix())
}
Loading