Skip to content

Commit

Permalink
add auction action
Browse files Browse the repository at this point in the history
  • Loading branch information
manojkgorle committed Oct 28, 2024
1 parent 8cc4cf2 commit 253a93d
Show file tree
Hide file tree
Showing 12 changed files with 283 additions and 22 deletions.
140 changes: 140 additions & 0 deletions actions/auction.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package actions

import (
"context"
"encoding/binary"
"fmt"

"github.com/AnomalyFi/hypersdk/chain"
"github.com/AnomalyFi/hypersdk/codec"
"github.com/AnomalyFi/hypersdk/consts"
"github.com/AnomalyFi/hypersdk/crypto/bls"
"github.com/AnomalyFi/hypersdk/state"
"github.com/AnomalyFi/hypersdk/utils"
"github.com/AnomalyFi/nodekit-seq/auth"
"github.com/AnomalyFi/nodekit-seq/storage"
"github.com/ava-labs/avalanchego/ids"
)

var _ chain.Action = (*Auction)(nil)

type AuctionInfo struct {
EpochNumber uint64 `json:"epochNumber"`
BidPrice uint64 `json:"bidPrice"`
BuilderSEQAddress codec.Address `json:"builderSEQAddress"`
}

type Auction struct {
AuctionInfo AuctionInfo `json:"auctionInfo"`
BuilderPublicKey []byte `json:"builderPublicKey"` // BLS public key of the bidder.
BuilderSignature []byte `json:"signature"`
}

func (*Auction) GetTypeID() uint8 {
return AuctionID
}

func (a *Auction) StateKeys(actor codec.Address, _ ids.ID) state.Keys {
return state.Keys{
string(storage.BalanceKey(actor)): state.Read | state.Write,
string(storage.BalanceKey(ArcadiaFundAddress())): state.All,
string(storage.ArcadiaBidKey(a.AuctionInfo.EpochNumber)): state.Write | state.Allocate,
}
}

func (*Auction) StateKeysMaxChunks() []uint16 {
return []uint16{storage.BalanceChunks, storage.BalanceChunks, storage.EpochExitChunks}
}

// This is a permissioned action, only authorized address can only pass the execution.
func (a *Auction) Execute(
ctx context.Context,
rules chain.Rules,
mu state.Mutable,
_ int64,
actor codec.Address,
_ ids.ID,
) ([][]byte, error) {
// TODO: this allows any whitelisted address to submit arcadia bid.
// change this to only allow the arcadia address to submit the bid.
if !IsWhiteListed(rules, actor) {
return nil, ErrNotWhiteListed
}
pubkey, err := bls.PublicKeyFromBytes(a.BuilderPublicKey)
if err != nil {
return nil, fmt.Errorf("failed to parse public key: %w", err)
}

msg := make([]byte, 0, 16)
binary.BigEndian.PutUint64(msg[:8], a.AuctionInfo.EpochNumber)
binary.BigEndian.PutUint64(msg[8:], a.AuctionInfo.BidPrice)

sig, err := bls.SignatureFromBytes(a.BuilderSignature)
if err != nil {
return nil, fmt.Errorf("failed to parse signature: %w", err)
}

// Verify the signature.
if !bls.Verify(msg, pubkey, sig) {
return nil, ErrInvalidBidderSignature
}
builderSEQAddress := codec.CreateAddress(auth.BLSID, utils.ToID(a.BuilderPublicKey))
if builderSEQAddress != a.AuctionInfo.BuilderSEQAddress {
return nil, ErrParsedBuilderSEQAddressMismatch
}
// deduct the bid amount from bidder.
if err := storage.SubBalance(ctx, mu, builderSEQAddress, a.AuctionInfo.BidPrice); err != nil {
return nil, err
}

// Bid amount is sent to the Arcadia fund address.
if err := storage.AddBalance(ctx, mu, ArcadiaFundAddress(), a.AuctionInfo.BidPrice, true); err != nil {
return nil, err
}

// Store bid information.
if err := storage.StoreArcadiaBidInfo(ctx, mu, a.AuctionInfo.EpochNumber, a.AuctionInfo.BidPrice, a.BuilderPublicKey, a.BuilderSignature); err != nil {
return nil, err
}

return nil, nil
}

func (*Auction) ComputeUnits(codec.Address, chain.Rules) uint64 {
return AuctionComputeUnits
}

func (a *Auction) Size() int {
return 2*consts.Uint64Len + bls.PublicKeyLen + bls.SignatureLen + codec.AddressLen
}

func (a *Auction) Marshal(p *codec.Packer) {
MarshalAuctionInfo(p, &a.AuctionInfo)
p.PackFixedBytes(a.BuilderPublicKey)
p.PackFixedBytes(a.BuilderSignature)
}

func UnmarshalAuction(p *codec.Packer) (chain.Action, error) {
var auction Auction
auctionInfo, err := UnmarshalAnchorInfo(p)
if err != nil {
return nil, err
}
auction.AuctionInfo = *auctionInfo
p.UnpackFixedBytes(bls.PublicKeyLen, &auction.BuilderPublicKey)
p.UnpackFixedBytes(bls.SignatureLen, &auction.BuilderSignature)
return &auction, nil
}

func (*Auction) ValidRange(chain.Rules) (int64, int64) {
// Returning -1, -1 means that the action is always valid.
return -1, -1
}

func (*Auction) NMTNamespace() []byte {
return DefaultNMTNamespace
}

func (*Auction) UseFeeMarket() bool {
return false
}
2 changes: 2 additions & 0 deletions actions/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ const (
TransferID uint8 = 0
MsgID uint8 = 1
ExitID uint8 = 2
AuctionID uint8 = 3
)

const (
// TODO: tune this
TransferComputeUnits = 1
EpochExitComputeUnits = 1
AuctionComputeUnits = 1

MsgComputeUnits = 15

Expand Down
2 changes: 1 addition & 1 deletion actions/epoch_exit.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func (t *EpochExit) Execute(
_ ids.ID,
) ([][]byte, error) {
if t.Epoch != t.Info.Epoch {
return nil, fmt.Errorf("Epoch is not equal to what's in the meta, expected: %d, actual: %d", t.Epoch, t.Info.Epoch)
return nil, fmt.Errorf("epoch is not equal to what's in the meta, expected: %d, actual: %d", t.Epoch, t.Info.Epoch)
}

epochExit, err := storage.GetEpochExit(ctx, mu, t.Epoch)
Expand Down
7 changes: 5 additions & 2 deletions actions/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ package actions
import "errors"

var (
ErrOutputValueZero = errors.New("value is zero")
ErrOutputMemoTooLarge = errors.New("memo is too large")
ErrOutputValueZero = errors.New("value is zero")
ErrNotWhiteListed = errors.New("not whitelisted")
ErrInvalidBidderSignature = errors.New("invalid bidder signature")
ErrParsedBuilderSEQAddressMismatch = errors.New("parsed builder SEQ address mismatch")
ErrOutputMemoTooLarge = errors.New("memo is too large")
)
45 changes: 45 additions & 0 deletions actions/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package actions

import (
"github.com/AnomalyFi/hypersdk/chain"
"github.com/AnomalyFi/hypersdk/codec"
)

func IsWhiteListed(rules chain.Rules, actor codec.Address) bool {
whitelistedAddressesB, _ := rules.FetchCustom("whitelisted.Addresses")
whitelistedAddresses := whitelistedAddressesB.([]codec.Address)
return ContainsAddress(whitelistedAddresses, actor)
}

func ContainsAddress(addrs []codec.Address, addr codec.Address) bool {
for _, a := range addrs {
if a == addr {
return true
}
}
return false
}

func ArcadiaFundAddress() codec.Address {
b := make([]byte, 33)
b[33] = 0x1
addr, _ := codec.ToAddress(b)
return addr
}

func MarshalAuctionInfo(p *codec.Packer, info *AuctionInfo) {
p.PackUint64(info.EpochNumber)
p.PackUint64(info.BidPrice)
p.PackAddress(info.BuilderSEQAddress)
}

func UnmarshalAnchorInfo(p *codec.Packer) (*AuctionInfo, error) {
var auctionInfo AuctionInfo
auctionInfo.EpochNumber = p.UnpackUint64(true)
auctionInfo.BidPrice = p.UnpackUint64(true)
p.UnpackAddress(&auctionInfo.BuilderSEQAddress)
if p.Err() != nil {
return nil, p.Err()
}
return &auctionInfo, nil
}
33 changes: 23 additions & 10 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,16 @@ type Config struct {
ContinuousProfilerDir string `json:"continuousProfilerDir"` // "*" is replaced with rand int

// Streaming settings
StreamingBacklogSize int `json:"streamingBacklogSize"`
StreamingBacklogSize int `json:"streamingBacklogSize"`
StoreBlockResultsOnDisk bool `json:"storeBlockResultsOnDisk"`

// Mempool
MempoolSize int `json:"mempoolSize"`
MempoolSponsorSize int `json:"mempoolSponsorSize"`
MempoolExemptSponsors []string `json:"mempoolExemptSponsors"`

// Order Book
//
// This is denoted as <asset 1>-<asset 2>
MaxOrdersPerPair int `json:"maxOrdersPerPair"`
TrackedPairs []string `json:"trackedPairs"` // which asset ID pairs we care about
// Whitelisted Address: Address used to get fee discounts, or are allowed to perform specific actions like submitting proofs.
WhitelistedAddresses []string `json:"whitelistedAddresses"`

// Misc
VerifyAuth bool `json:"verifyAuth"`
Expand All @@ -89,9 +87,10 @@ type Config struct {
AnchorURL string `json:"anchorURL"`
AnchorManager string `json:"anchorManager"`

loaded bool
nodeID ids.NodeID
parsedExemptSponsors []codec.Address
loaded bool
nodeID ids.NodeID
parsedExemptSponsors []codec.Address
parsedWhiteListedAddresses []codec.Address
}

func New(nodeID ids.NodeID, b []byte) (*Config, error) {
Expand All @@ -114,6 +113,16 @@ func New(nodeID ids.NodeID, b []byte) (*Config, error) {
}
c.parsedExemptSponsors[i] = p
}

// Parse whitelisted address. These addresses are authorized to perform certain actions.
c.parsedWhiteListedAddresses = make([]codec.Address, len(c.WhitelistedAddresses))
for i, addr := range c.WhitelistedAddresses {
p, err := codec.ParseAddressBech32(consts.HRP, addr)
if err != nil {
return nil, err
}
c.parsedWhiteListedAddresses[i] = p
}
return c, nil
}

Expand All @@ -135,7 +144,7 @@ func (c *Config) setDefault() {
c.StreamingBacklogSize = c.Config.GetStreamingBacklogSize()
c.VerifyAuth = c.Config.GetVerifyAuth()
c.StoreTransactions = defaultStoreTransactions
c.MaxOrdersPerPair = defaultMaxOrdersPerPair
c.StoreBlockResultsOnDisk = c.Config.GetStoreBlockResultsOnDisk()
c.ETHRPCAddr = c.Config.GetETHL1RPC()
c.ETHWSAddr = c.Config.GetETHL1WS()
c.AnchorURL = c.Config.GetAnchorURL()
Expand All @@ -162,6 +171,7 @@ func (c *Config) GetTraceConfig() *trace.Config {
}
func (c *Config) GetStateSyncServerDelay() time.Duration { return c.StateSyncServerDelay }
func (c *Config) GetStreamingBacklogSize() int { return c.StreamingBacklogSize }
func (c *Config) GetStoreBlockResultsOnDisk() bool { return c.StoreBlockResultsOnDisk }
func (c *Config) GetContinuousProfilerConfig() *profiler.Config {
if len(c.ContinuousProfilerDir) == 0 {
return &profiler.Config{Enabled: false}
Expand All @@ -185,3 +195,6 @@ func (c *Config) GetAnchorURL() string { return c.AnchorURL }
func (c *Config) GetAnchorManager() string {
return c.AnchorManager
} // default bls pubkey for anchor manager
func (c *Config) GetParsedWhiteListedAddress() []codec.Address {
return c.parsedWhiteListedAddresses
}
2 changes: 1 addition & 1 deletion controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ func (c *Controller) Initialize(

func (c *Controller) Rules(t int64) chain.Rules {
// TODO: extend with [UpgradeBytes]
return c.genesis.Rules(t, c.snowCtx.NetworkID, c.snowCtx.ChainID)
return c.genesis.Rules(t, c.snowCtx.NetworkID, c.snowCtx.ChainID, c.config.GetParsedWhiteListedAddress())
}

func (c *Controller) StateManager() chain.StateManager {
Expand Down
15 changes: 10 additions & 5 deletions genesis/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package genesis

import (
"github.com/AnomalyFi/hypersdk/chain"
"github.com/AnomalyFi/hypersdk/codec"
"github.com/AnomalyFi/hypersdk/fees"
"github.com/ava-labs/avalanchego/ids"

Expand All @@ -16,13 +17,14 @@ var _ chain.Rules = (*Rules)(nil)
type Rules struct {
g *Genesis

networkID uint32
chainID ids.ID
networkID uint32
chainID ids.ID
parsedWhiteListedAddress []codec.Address
}

// TODO: use upgradeBytes
func (g *Genesis) Rules(_ int64, networkID uint32, chainID ids.ID) *Rules {
return &Rules{g, networkID, chainID}
func (g *Genesis) Rules(_ int64, networkID uint32, chainID ids.ID, parsedWhiteListedAddresses []codec.Address) *Rules {
return &Rules{g, networkID, chainID, parsedWhiteListedAddresses}
}

func (r *Rules) NetworkID() uint32 {
Expand Down Expand Up @@ -113,6 +115,9 @@ func (r *Rules) GetFeeMarketMinUnitPrice() uint64 {
return r.g.FeeMarketMinUnits
}

func (*Rules) FetchCustom(string) (any, bool) {
func (r *Rules) FetchCustom(s string) (any, bool) {
if s == "whitelisted.Addresses" {
return r.parsedWhiteListedAddress, false
}
return nil, false
}
3 changes: 2 additions & 1 deletion rpc/jsonrpc_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

hactions "github.com/AnomalyFi/hypersdk/actions"
"github.com/AnomalyFi/hypersdk/chain"
"github.com/AnomalyFi/hypersdk/codec"
"github.com/AnomalyFi/hypersdk/requester"
"github.com/AnomalyFi/hypersdk/rpc"
"github.com/AnomalyFi/hypersdk/utils"
Expand Down Expand Up @@ -302,7 +303,7 @@ func (p *Parser) ChainID() ids.ID {
}

func (p *Parser) Rules(t int64) chain.Rules {
return p.genesis.Rules(t, p.networkID, p.chainID)
return p.genesis.Rules(t, p.networkID, p.chainID, []codec.Address{})
}

func (*Parser) Registry() (chain.ActionRegistry, chain.AuthRegistry) {
Expand Down
2 changes: 1 addition & 1 deletion rpc/jsonrpc_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ func (p *ServerParser) ChainID() ids.ID {
}

func (p *ServerParser) Rules(t int64) chain.Rules {
return p.genesis.Rules(t, p.networkID, p.chainID)
return p.genesis.Rules(t, p.networkID, p.chainID, []codec.Address{})
}

func (*ServerParser) Registry() (chain.ActionRegistry, chain.AuthRegistry) {
Expand Down
2 changes: 1 addition & 1 deletion scripts/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -164,13 +164,13 @@ cat <<EOF > "${TMPDIR}"/seqvm.config
"mempoolSize": 10000000,
"mempoolSponsorSize": 10000000,
"mempoolExemptSponsors":["${ADDRESS}"],
"whitelistedAddresses":["${ADDRESS}"],
"authVerificationCores": 2,
"rootGenerationCores": 2,
"transactionExecutionCores": 2,
"verifyAuth": true,
"storeTransactions": ${STORE_TXS},
"streamingBacklogSize": 10000000,
"trackedPairs":["*"],
"logLevel": "${LOG_LEVEL}",
"continuousProfilerDir":"${TMPDIR}/seqvm-e2e-profiles/*",
"stateSyncServerDelay": ${STATESYNC_DELAY},
Expand Down
Loading

0 comments on commit 253a93d

Please sign in to comment.