commit inicial do projeto

This commit is contained in:
Júnior
2025-05-23 10:44:32 -03:00
commit 8f04473c0b
106 changed files with 5673 additions and 0 deletions

104
internal/dao/proposal.go Normal file
View File

@ -0,0 +1,104 @@
package dao
import (
"encoding/json"
"log"
"sync"
"time"
)
type ProposalType string
const (
GenericProposal ProposalType = "GENERIC"
ParamChangeProposal ProposalType = "PARAM_CHANGE"
)
type Proposal struct {
ID uint64
Title string
Content string
Type ProposalType
Creator string
CreatedAt int64
Duration int64 // segundos até expiração
Votes map[string]bool
Closed bool
Approved bool
mu sync.Mutex
}
func NewProposal(id uint64, title, content, creator string, typ ProposalType, duration int64) *Proposal {
return &Proposal{
ID: id,
Title: title,
Content: content,
Type: typ,
Creator: creator,
CreatedAt: time.Now().Unix(),
Duration: duration,
Votes: make(map[string]bool),
Closed: false,
Approved: false,
}
}
func (p *Proposal) Vote(address string, approve bool) error {
p.mu.Lock()
defer p.mu.Unlock()
if p.Closed {
return nil
}
p.Votes[address] = approve
return nil
}
func (p *Proposal) Tally(stakes map[string]uint64) (yes uint64, no uint64, total uint64) {
p.mu.Lock()
defer p.mu.Unlock()
for addr, vote := range p.Votes {
stake := stakes[addr]
total += stake
if vote {
yes += stake
} else {
no += stake
}
}
return
}
func (p *Proposal) CheckAndClose(stakes map[string]uint64) {
if p.Closed {
return
}
if time.Now().Unix() >= p.CreatedAt+p.Duration {
yes, _, total := p.Tally(stakes)
if yes*100/total > 66 {
p.Approved = true
}
p.Closed = true
if p.Approved {
p.Execute()
}
}
}
// Execute aplica os efeitos da proposta, se for do tipo que altera estado
func (p *Proposal) Execute() {
if p.Type == ParamChangeProposal {
var payload struct {
Target string `json:"target"`
Value uint64 `json:"value"`
}
err := json.Unmarshal([]byte(p.Content), &payload)
if err != nil {
log.Printf("❌ Erro ao executar proposta PARAM_CHANGE: %v", err)
return
}
// Exemplo: muda parâmetro global (placeholder)
log.Printf("⚙️ Aplicando PARAM_CHANGE: %s = %d", payload.Target, payload.Value)
// Aqui você aplicaria no config real
}
}

78
internal/dao/store.go Normal file
View File

@ -0,0 +1,78 @@
package dao
import (
"encoding/gob"
"errors"
"os"
"sync"
)
type ProposalStore struct {
mu sync.RWMutex
proposals map[uint64]*Proposal
nextID uint64
}
func NewProposalStore() *ProposalStore {
return &ProposalStore{
proposals: make(map[uint64]*Proposal),
nextID: 1,
}
}
func (ps *ProposalStore) Create(title, content, creator string, typ ProposalType, duration int64) *Proposal {
ps.mu.Lock()
defer ps.mu.Unlock()
id := ps.nextID
ps.nextID++
p := NewProposal(id, title, content, creator, typ, duration)
ps.proposals[id] = p
return p
}
func (ps *ProposalStore) Get(id uint64) (*Proposal, error) {
ps.mu.RLock()
defer ps.mu.RUnlock()
p, ok := ps.proposals[id]
if !ok {
return nil, errors.New("proposta não encontrada")
}
return p, nil
}
func (ps *ProposalStore) List() []*Proposal {
ps.mu.RLock()
defer ps.mu.RUnlock()
var list []*Proposal
for _, p := range ps.proposals {
list = append(list, p)
}
return list
}
func (ps *ProposalStore) SaveToDisk(path string) error {
ps.mu.RLock()
defer ps.mu.RUnlock()
f, err := os.Create(path)
if err != nil {
return err
}
defer f.Close()
enc := gob.NewEncoder(f)
return enc.Encode(ps)
}
func (ps *ProposalStore) LoadFromDisk(path string) error {
ps.mu.Lock()
defer ps.mu.Unlock()
f, err := os.Open(path)
if err != nil {
if os.IsNotExist(err) {
return nil
}
return err
}
defer f.Close()
dec := gob.NewDecoder(f)
return dec.Decode(ps)
}