mirror of
https://codeberg.org/likwid/likwid.git
synced 2026-02-09 21:13:09 +00:00
173 lines
6.9 KiB
MySQL
173 lines
6.9 KiB
MySQL
|
|
-- Liquid Delegation System
|
||
|
|
-- Implements fluid, reversible, topic-based vote delegation
|
||
|
|
|
||
|
|
-- Delegation scopes (what the delegation applies to)
|
||
|
|
CREATE TYPE delegation_scope AS ENUM ('global', 'community', 'topic', 'proposal');
|
||
|
|
|
||
|
|
-- Topic categories for delegation
|
||
|
|
CREATE TABLE topics (
|
||
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||
|
|
community_id UUID NOT NULL REFERENCES communities(id) ON DELETE CASCADE,
|
||
|
|
name VARCHAR(100) NOT NULL,
|
||
|
|
slug VARCHAR(100) NOT NULL,
|
||
|
|
description TEXT,
|
||
|
|
parent_id UUID REFERENCES topics(id) ON DELETE SET NULL,
|
||
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
|
|
UNIQUE(community_id, slug)
|
||
|
|
);
|
||
|
|
|
||
|
|
-- Vote delegations
|
||
|
|
CREATE TABLE delegations (
|
||
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||
|
|
delegator_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||
|
|
delegate_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||
|
|
scope delegation_scope NOT NULL DEFAULT 'global',
|
||
|
|
community_id UUID REFERENCES communities(id) ON DELETE CASCADE,
|
||
|
|
topic_id UUID REFERENCES topics(id) ON DELETE CASCADE,
|
||
|
|
proposal_id UUID REFERENCES proposals(id) ON DELETE CASCADE,
|
||
|
|
weight DECIMAL(5,4) NOT NULL DEFAULT 1.0, -- 0.0001 to 1.0 (for fractional delegation)
|
||
|
|
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
|
|
revoked_at TIMESTAMPTZ,
|
||
|
|
-- Prevent self-delegation
|
||
|
|
CONSTRAINT no_self_delegation CHECK (delegator_id != delegate_id),
|
||
|
|
-- Ensure scope matches reference
|
||
|
|
CONSTRAINT scope_community_match CHECK (
|
||
|
|
(scope = 'global') OR
|
||
|
|
(scope = 'community' AND community_id IS NOT NULL) OR
|
||
|
|
(scope = 'topic' AND topic_id IS NOT NULL) OR
|
||
|
|
(scope = 'proposal' AND proposal_id IS NOT NULL)
|
||
|
|
)
|
||
|
|
);
|
||
|
|
|
||
|
|
-- Delegation chain cache (for transitive delegations)
|
||
|
|
CREATE TABLE delegation_chains (
|
||
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||
|
|
original_delegator_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||
|
|
final_delegate_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||
|
|
chain_path UUID[] NOT NULL, -- Array of user IDs in the chain
|
||
|
|
chain_length INT NOT NULL,
|
||
|
|
effective_weight DECIMAL(5,4) NOT NULL,
|
||
|
|
scope delegation_scope NOT NULL,
|
||
|
|
community_id UUID REFERENCES communities(id) ON DELETE CASCADE,
|
||
|
|
topic_id UUID REFERENCES topics(id) ON DELETE CASCADE,
|
||
|
|
computed_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||
|
|
);
|
||
|
|
|
||
|
|
-- Delegation activity log (for transparency)
|
||
|
|
CREATE TABLE delegation_log (
|
||
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||
|
|
delegation_id UUID REFERENCES delegations(id) ON DELETE SET NULL,
|
||
|
|
delegator_id UUID NOT NULL REFERENCES users(id),
|
||
|
|
delegate_id UUID NOT NULL REFERENCES users(id),
|
||
|
|
action VARCHAR(20) NOT NULL, -- 'created', 'updated', 'revoked', 'used'
|
||
|
|
scope delegation_scope NOT NULL,
|
||
|
|
community_id UUID REFERENCES communities(id),
|
||
|
|
topic_id UUID REFERENCES topics(id),
|
||
|
|
proposal_id UUID REFERENCES proposals(id),
|
||
|
|
metadata JSONB,
|
||
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||
|
|
);
|
||
|
|
|
||
|
|
-- Delegated votes cast (track when delegations are used)
|
||
|
|
CREATE TABLE delegated_votes (
|
||
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||
|
|
proposal_id UUID NOT NULL REFERENCES proposals(id) ON DELETE CASCADE,
|
||
|
|
original_voter_id UUID NOT NULL REFERENCES voting_identities(id),
|
||
|
|
delegate_id UUID NOT NULL REFERENCES users(id),
|
||
|
|
delegation_chain UUID[] NOT NULL,
|
||
|
|
vote_data JSONB NOT NULL, -- The actual vote (option_ids, scores, etc.)
|
||
|
|
cast_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
|
|
UNIQUE(proposal_id, original_voter_id)
|
||
|
|
);
|
||
|
|
|
||
|
|
-- Delegate profiles (public info about delegates)
|
||
|
|
CREATE TABLE delegate_profiles (
|
||
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE UNIQUE,
|
||
|
|
display_name VARCHAR(100),
|
||
|
|
bio TEXT,
|
||
|
|
expertise_topics UUID[], -- Array of topic IDs
|
||
|
|
accepting_delegations BOOLEAN NOT NULL DEFAULT TRUE,
|
||
|
|
delegation_policy TEXT, -- How they decide votes
|
||
|
|
total_delegators INT NOT NULL DEFAULT 0,
|
||
|
|
total_votes_cast INT NOT NULL DEFAULT 0,
|
||
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||
|
|
);
|
||
|
|
|
||
|
|
-- Add topic reference to proposals
|
||
|
|
ALTER TABLE proposals ADD COLUMN IF NOT EXISTS topic_id UUID REFERENCES topics(id);
|
||
|
|
|
||
|
|
-- Indexes
|
||
|
|
CREATE INDEX idx_topics_community ON topics(community_id);
|
||
|
|
CREATE INDEX idx_topics_parent ON topics(parent_id);
|
||
|
|
CREATE INDEX idx_delegations_delegator ON delegations(delegator_id);
|
||
|
|
CREATE INDEX idx_delegations_delegate ON delegations(delegate_id);
|
||
|
|
CREATE INDEX idx_delegations_active ON delegations(is_active) WHERE is_active = TRUE;
|
||
|
|
CREATE INDEX idx_delegations_scope ON delegations(scope);
|
||
|
|
CREATE INDEX idx_delegation_chains_original ON delegation_chains(original_delegator_id);
|
||
|
|
CREATE INDEX idx_delegation_chains_final ON delegation_chains(final_delegate_id);
|
||
|
|
CREATE INDEX idx_delegation_log_delegator ON delegation_log(delegator_id);
|
||
|
|
CREATE INDEX idx_delegation_log_delegate ON delegation_log(delegate_id);
|
||
|
|
CREATE INDEX idx_delegated_votes_proposal ON delegated_votes(proposal_id);
|
||
|
|
CREATE INDEX idx_delegate_profiles_accepting ON delegate_profiles(accepting_delegations) WHERE accepting_delegations = TRUE;
|
||
|
|
|
||
|
|
-- Triggers
|
||
|
|
CREATE TRIGGER delegations_updated_at BEFORE UPDATE ON delegations
|
||
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
||
|
|
|
||
|
|
CREATE TRIGGER delegate_profiles_updated_at BEFORE UPDATE ON delegate_profiles
|
||
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
||
|
|
|
||
|
|
-- Function to detect delegation cycles
|
||
|
|
CREATE OR REPLACE FUNCTION check_delegation_cycle()
|
||
|
|
RETURNS TRIGGER AS $$
|
||
|
|
DECLARE
|
||
|
|
current_delegate UUID;
|
||
|
|
visited UUID[];
|
||
|
|
max_depth INT := 20;
|
||
|
|
depth INT := 0;
|
||
|
|
BEGIN
|
||
|
|
-- Start from the new delegate
|
||
|
|
current_delegate := NEW.delegate_id;
|
||
|
|
visited := ARRAY[NEW.delegator_id];
|
||
|
|
|
||
|
|
WHILE depth < max_depth LOOP
|
||
|
|
-- Check if we've reached a cycle
|
||
|
|
IF current_delegate = ANY(visited) THEN
|
||
|
|
RAISE EXCEPTION 'Delegation would create a cycle';
|
||
|
|
END IF;
|
||
|
|
|
||
|
|
-- Add to visited
|
||
|
|
visited := visited || current_delegate;
|
||
|
|
|
||
|
|
-- Find next delegate in chain (matching scope)
|
||
|
|
SELECT d.delegate_id INTO current_delegate
|
||
|
|
FROM delegations d
|
||
|
|
WHERE d.delegator_id = current_delegate
|
||
|
|
AND d.is_active = TRUE
|
||
|
|
AND d.scope = NEW.scope
|
||
|
|
AND (NEW.scope = 'global' OR
|
||
|
|
(NEW.scope = 'community' AND d.community_id = NEW.community_id) OR
|
||
|
|
(NEW.scope = 'topic' AND d.topic_id = NEW.topic_id) OR
|
||
|
|
(NEW.scope = 'proposal' AND d.proposal_id = NEW.proposal_id))
|
||
|
|
LIMIT 1;
|
||
|
|
|
||
|
|
-- No more delegations in chain
|
||
|
|
IF current_delegate IS NULL THEN
|
||
|
|
EXIT;
|
||
|
|
END IF;
|
||
|
|
|
||
|
|
depth := depth + 1;
|
||
|
|
END LOOP;
|
||
|
|
|
||
|
|
RETURN NEW;
|
||
|
|
END;
|
||
|
|
$$ LANGUAGE plpgsql;
|
||
|
|
|
||
|
|
CREATE TRIGGER check_delegation_cycle_trigger
|
||
|
|
BEFORE INSERT OR UPDATE ON delegations
|
||
|
|
FOR EACH ROW EXECUTE FUNCTION check_delegation_cycle();
|