forked from DED-EDU/DED-learning-session-showcase
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathLearningSession.sol
More file actions
167 lines (136 loc) · 5.96 KB
/
LearningSession.sol
File metadata and controls
167 lines (136 loc) · 5.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/// @title DED Learning Session (Showcase Version)
/// @notice Minimal, non-economic example of learning artifacts + voting + reputation.
/// @dev This is a simplified public demo, not the full DED protocol.
contract LearningSession {
// ------------------------------------------------------------
// Types
// ------------------------------------------------------------
/// @notice Type of artifact in a learning session.
enum LearningSessionArtifact {
VIDEO,
COMMENT
}
/// @notice Core artifact type representing a learning session output or discussion item.
struct Artifact {
LearningSessionArtifact artifactType;
uint256 id;
uint256 parentId; // 0 if no parent (root)
address author;
uint256 createdAtBlock;
uint256[] childIds;
string cid; // off-chain content identifier (e.g. IPFS/Filecoin)
}
/// @notice Per-artifact voting state.
struct ArtifactVotes {
int256 total; // net score = sum(votes)
mapping(bytes32 => int8) votes; // voterId => last vote (-1, 0, +1)
}
// ------------------------------------------------------------
// Storage
// ------------------------------------------------------------
/// @notice All artifacts by id.
mapping(uint256 => Artifact) private artifacts;
/// @notice Voting data for each artifact.
mapping(uint256 => ArtifactVotes) private artifactVotes;
/// @notice Aggregated reputation per author.
mapping(address => int256) private authorReputation;
/// @notice Incremental artifact id counter.
uint256 private nextArtifactId = 1;
// ------------------------------------------------------------
// Events
// ------------------------------------------------------------
event ArtifactCreated(
uint256 indexed id,
uint256 indexed parentId,
address indexed author,
LearningSessionArtifact artifactType,
string cid
);
event Voted(
uint256 indexed artifactId,
address indexed voter,
int8 oldVote,
int8 newVote
);
// ------------------------------------------------------------
// Public View Functions
// ------------------------------------------------------------
/// @notice Get a full artifact by id.
function getArtifact(uint256 artifactId) external view returns (Artifact memory) {
require(artifacts[artifactId].id == artifactId, "Artifact does not exist");
return artifacts[artifactId];
}
/// @notice Get the child ids for a given artifact.
function getChildIds(uint256 artifactId) external view returns (uint256[] memory) {
require(artifacts[artifactId].id == artifactId, "Artifact does not exist");
return artifacts[artifactId].childIds;
}
/// @notice Get the aggregated score for an artifact.
function getArtifactScore(uint256 artifactId) external view returns (int256) {
return artifactVotes[artifactId].total;
}
/// @notice Get total reputation for an author across all artifacts.
function getAuthorReputation(address author) external view returns (int256) {
return authorReputation[author];
}
// ------------------------------------------------------------
// Core Mutations
// ------------------------------------------------------------
/// @notice Create a new artifact (video or comment).
/// @param artifactType The type of artifact (VIDEO or COMMENT).
/// @param parentId The parent artifact id (0 if none).
/// @param cid Off-chain content identifier (e.g. IPFS/Filecoin CID).
function createArtifact(
LearningSessionArtifact artifactType,
uint256 parentId,
string calldata cid
) external returns (uint256) {
uint256 id = nextArtifactId++;
Artifact storage a = artifacts[id];
a.artifactType = artifactType;
a.id = id;
a.parentId = parentId;
a.author = msg.sender;
a.createdAtBlock = block.number;
a.cid = cid;
// If this artifact is a reply/comment, register it on the parent.
if (parentId != 0) {
require(artifacts[parentId].id == parentId, "Parent does not exist");
artifacts[parentId].childIds.push(id);
}
emit ArtifactCreated(id, parentId, msg.sender, artifactType, cid);
return id;
}
/// @notice Vote on an artifact with a value in {-1, 0, +1}.
/// @dev This is a simple, non-economic reputation system.
/// @param artifactId The id of the artifact being voted on.
/// @param voteValue The vote value: -1 (downvote), 0 (withdraw), +1 (upvote).
function vote(uint256 artifactId, int8 voteValue) external {
require(artifacts[artifactId].id == artifactId, "Artifact does not exist");
require(voteValue >= -1 && voteValue <= 1, "Invalid vote value");
bytes32 voterId = _voterId(msg.sender);
ArtifactVotes storage v = artifactVotes[artifactId];
int8 oldVote = v.votes[voterId];
if (oldVote == voteValue) {
// No change, do nothing.
return;
}
v.votes[voterId] = voteValue;
v.total = v.total - oldVote + voteValue;
address author = artifacts[artifactId].author;
if (author != msg.sender) {
authorReputation[author] = authorReputation[author] - oldVote + voteValue;
}
emit Voted(artifactId, msg.sender, oldVote, voteValue);
}
// ------------------------------------------------------------
// Internal Helpers
// ------------------------------------------------------------
/// @notice Derive a voter id from an address. In a more complex system this
/// could be replaced by an identity scheme (e.g. DIDs, badges, etc).
function _voterId(address voter) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(voter));
}
}