package dashboard import ( "crypto/ed25519" "fmt" "sync" "kesim.org/seal" ) type inmemory struct { sync.RWMutex auctions map[string]*auction } func NewInMemoryDashboard() *inmemory { return &inmemory{} } type auction struct { sync.RWMutex description *seal.SignedDescription state AuctionState round uint8 // which bit has been completed commitments map[string]SignedCommitment messages map[string][]SignedMessage // per-bidder and per-round messages, pre-allocated. } func (m *inmemory) NewAuction(description *seal.SignedDescription) (auctionId string, e error) { if ok, e := description.Verify(); e != nil { return "", e } else if !ok { return "", ErrSellerIncorrectSignature } auctionId, e = description.Hash() if e != nil { return "", e } auction := &auction{ description: description, state: AuctionStateReady, messages: make(map[string][]SignedMessage), } m.Lock() defer m.Unlock() if _, exists := m.auctions[auctionId]; exists { return "", ErrSellerAuctionExists } m.auctions[auctionId] = auction return auctionId, e } func (m *inmemory) Auctions(state AuctionState) map[string]*seal.SignedDescription { if state == AuctionStateUnknown || state >= AuctionStateLAST { return nil } m.RLock() defer m.RUnlock() r := make(map[string]*seal.SignedDescription) for id, auction := range m.auctions { if auction.state == state { r[id] = auction.description } } return r } func (m *inmemory) getAuction(auctionId string) (*auction, error) { m.RLock() defer m.RUnlock() auction, exists := m.auctions[auctionId] if !exists { return nil, ErrAuctionUnknown } return auction, nil } func (m *inmemory) Join(auctionId string, commitment SignedCommitment, pubkey ed25519.PublicKey) error { // Check signature first if !commitment.Verify(pubkey) { return ErrBidderIncorrectSignature } bidderId := Pub2String(pubkey) auction, e := m.getAuction(auctionId) if e != nil { return e } // We allow overwriting auction.Lock() defer auction.Unlock() if auction.state != AuctionStateReady { return ErrAuctionNotReady } auction.commitments[bidderId] = commitment auction.messages[bidderId] = make([]SignedMessage, auction.description.BitLength) return nil } func (m *inmemory) Commitments(auctionId string) (bidders map[string]SignedCommitment, e error) { auction, e := m.getAuction(auctionId) if e != nil { return nil, e } auction.RLock() defer auction.RUnlock() bidders = make(map[string]SignedCommitment) for id, commitment := range auction.commitments { bidders[id] = commitment } return bidders, nil } func (m *inmemory) Publish(auctionId string, round uint8, message SignedMessage, pubkey ed25519.PublicKey) error { if len(message.Message) == 0 { return ErrBidderEmptyMessage } // check signature first if !message.Verify(pubkey) { return ErrBidderIncorrectSignature } bidderId := Pub2String(pubkey) auction, e := m.getAuction(auctionId) if e != nil { return e } auction.Lock() defer auction.Unlock() if auction.state == AuctionStateFinished { return ErrAuctionFinished } else if auction.state != AuctionStateRunning { return ErrAuctionNotRunning } else if auction.round != round { return ErrBidderWrongRound } messages, ok := auction.messages[bidderId] if !ok { return ErrBidderUnknown } messages[auction.round] = message completed := true for _, messages := range auction.messages { completed = completed && len(messages[auction.round].Message) > 0 } if completed { auction.round += 1 if auction.round == auction.description.BitLength { auction.state = AuctionStateFinished } } return nil } func (m *inmemory) Messages(auctionId string, round uint8) (messages map[string]SignedMessage, e error) { return nil, fmt.Errorf("inmemory.Messages not implemented") }