Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 143 additions & 0 deletions NeoBurgerTestnetVote.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
using System;
using System.Numerics;
using Neo;
using Neo.SmartContract.Framework;
using Neo.SmartContract.Framework.Native;
using Neo.SmartContract.Framework.Services;
using Neo.SmartContract.Framework.Attributes;
using Neo.SmartContract;

namespace NeoBurger
{
[ManifestExtra("Author", "NEOBURGER")]
[ManifestExtra("Description", "NeoBurger Governance Token")]
[SupportedStandards("NEP-17")]
[ContractPermission("*", "*")]
public class NeoBurgerVote:SmartContract
{
[InitialValue("[TODO]: ARGS", ContractParameterType.Hash160)]
private static readonly UInt160 OWNER = default;
private const ulong VOTING_PERIOD = 86400000 * 7;
private const byte PREFIX_PROPOSAL_LATEST_ID = 0x02;
private const byte PREFIX_PROPOSAL = 0x03;
private const byte PREFIX_PROPOSAL_EXECUTED_TIME = 0x05;
private const byte PREFIX_DELEGATE = 0x81;
private const byte PREFIX_VOTE = 0xc1;

public static BigInteger GetVotingPeriod() => VOTING_PERIOD;
public static BigInteger GetLatestProposalID() => (BigInteger)Storage.Get(Storage.CurrentContext, new byte[] { PREFIX_PROPOSAL_LATEST_ID });

public static object[] ProposalAttributes(BigInteger id)
{
StorageMap proposal_map = new(Storage.CurrentContext, new byte[] { PREFIX_PROPOSAL });
ProposalAttributesStruct proposal_attributes = (ProposalAttributesStruct)proposal_map.GetObject((ByteString)id);
StorageMap proposal_executed_time_map = new(Storage.CurrentContext, new byte[] { PREFIX_PROPOSAL_EXECUTED_TIME });
BigInteger executed_time = (BigInteger)proposal_executed_time_map.Get((ByteString)id);
UInt256 digest = (UInt256)CryptoLib.Sha256(StdLib.Serialize(new object[] { proposal_attributes.scripthash, proposal_attributes.method, proposal_attributes.args, proposal_attributes.nonce }));
return new object[] { proposal_attributes.provider, proposal_attributes.title, proposal_attributes.description, proposal_attributes.scripthash, proposal_attributes.method, proposal_attributes.args, proposal_attributes.nonce, proposal_attributes.created_time, proposal_attributes.voting_deadline, executed_time, digest };
}

public static UInt160 GetDelegate(UInt160 from) => (UInt160)new StorageMap(Storage.CurrentContext, PREFIX_DELEGATE).Get(from);
public static Iterator GetAllDelgates() => new StorageMap(Storage.CurrentContext, PREFIX_DELEGATE).Find(FindOptions.RemovePrefix);
public static BigInteger GetVote(UInt160 from, BigInteger proposal_index) => (BigInteger)new StorageMap(Storage.CurrentContext, (ByteString)new byte[] { PREFIX_VOTE } + (ByteString)proposal_index).Get(from);
public static Iterator GetVotersOfProposal(BigInteger proposal_id) => new StorageMap(Storage.CurrentContext, (ByteString)new byte[] { PREFIX_VOTE } + (ByteString)proposal_id).Find(FindOptions.RemovePrefix);
public static UInt256 ProposalDigest(UInt160 scripthash, string method, object[] args, BigInteger nonce) => (UInt256)CryptoLib.Sha256(StdLib.Serialize(new object[] { scripthash, method, args, nonce }));

struct ProposalAttributesStruct
{
public BigInteger id;
public UInt160 provider;
public string title;
public string description;
public UInt160 scripthash;
public string method;
public ByteString[] args;
public BigInteger nonce;
public BigInteger created_time;
public BigInteger voting_deadline;
}

public static void _deploy(object data, bool update)
{
if (update) return;
StorageMap proposal_id_map = new(Storage.CurrentContext, new byte[] { PREFIX_PROPOSAL });
ProposalAttributesStruct proposal_attributes = new();
proposal_attributes.id = 0;
proposal_id_map.PutObject((ByteString)(BigInteger)0, proposal_attributes);

This comment was marked as resolved.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

        public void PutObject(ByteString key, object value);
        public void PutObject(byte[] key, object value);

and 2 converts is necessary if use a fixed integer as key. An alternative way can be

proposal_id_map.PutObject(proposal_attributes.id, proposal_attributes);

}

public static BigInteger NewProposal(UInt160 provider, string title, string description, BigInteger proposal_id, UInt160 scripthash, string method, ByteString[] args)
{
ExecutionEngine.Assert(Runtime.CheckWitness(provider));
StorageMap proposal_id_map = new(Storage.CurrentContext, new byte[] { PREFIX_PROPOSAL });
if ((BigInteger)proposal_id_map.Get((ByteString)proposal_id) != 0 || ((ProposalAttributesStruct)proposal_id_map.GetObject((ByteString)(proposal_id - 1))).id != proposal_id - 1)
throw new Exception("Invalid proposal id");

Storage.Put(Storage.CurrentContext, new byte[] { PREFIX_PROPOSAL_LATEST_ID }, (ByteString)proposal_id);

ProposalAttributesStruct proposal_attributes = new();
proposal_attributes.id = proposal_id;

This comment was marked as resolved.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

judgement is made at https://github.com/Hecate2/code/blob/13414e510e422e7ae76204f8d1cf8b8a951ad98a/NeoBurgerTestnetVote.cs#L71 which says

((ProposalAttributesStruct)proposal_id_map.GetObject((ByteString)(proposal_id - 1))).id != proposal_id - 1

and proposal_id as 0 leads to exception.
It's even OK if proposal_id is really set as 0, because the TEE would ignore the 0-proposal

proposal_attributes.provider = provider;
proposal_attributes.title = title;
proposal_attributes.description = description;
proposal_attributes.scripthash = scripthash;
proposal_attributes.method = method;
proposal_attributes.args = args;
proposal_attributes.nonce = Runtime.GetRandom();
BigInteger current_time = Runtime.Time;
proposal_attributes.created_time = current_time;
proposal_attributes.voting_deadline = current_time + VOTING_PERIOD;
proposal_id_map.PutObject((ByteString)proposal_id, proposal_attributes);

return proposal_id;
}

public static void Delegate(UInt160 from, UInt160 to)
{
ExecutionEngine.Assert(Runtime.CheckWitness(from));
StorageMap delegate_map = new(Storage.CurrentContext, PREFIX_DELEGATE);
if (to == UInt160.Zero || to == from)
delegate_map.Delete(from);

This comment was marked as resolved.

Copy link
Contributor Author

@Hecate2 Hecate2 Dec 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping as zero adds difficulty for collecting delegation relationship information. This contract will be deployed on testnet, where GAS consumption is not a problem.

This comment was marked as resolved.

else
delegate_map.Put(from, to);
}

public static void Vote(UInt160 from, BigInteger proposal_index, bool for_or_against, bool unvote=false)
{
ExecutionEngine.Assert(Runtime.CheckWitness(from));
StorageMap proposal_id_map = new(Storage.CurrentContext, new byte[] { PREFIX_PROPOSAL });
ProposalAttributesStruct proposal_attributes = (ProposalAttributesStruct)proposal_id_map.GetObject((ByteString)proposal_index);
BigInteger voting_deadline = proposal_attributes.voting_deadline;
if (Runtime.Time > voting_deadline)
throw new Exception("Cannot vote after the deadline");
StorageMap proposal_executed_time_map = new(Storage.CurrentContext, new byte[] { PREFIX_PROPOSAL_EXECUTED_TIME });
BigInteger executed_time = (BigInteger)proposal_executed_time_map.Get((ByteString)proposal_index);
if (executed_time > 0)
throw new Exception("Cannot vote for executed proposal");
StorageMap vote_map = new(Storage.CurrentContext, (ByteString)new byte[] { PREFIX_VOTE } + (ByteString)proposal_index);
if (unvote)
{
vote_map.Delete(from);
return;
}
if (for_or_against)
vote_map.Put(from, 1);
else
vote_map.Put(from, -1);
}

public static void MarkProposalExecuted(BigInteger id, BigInteger time)
{
ExecutionEngine.Assert(Runtime.CheckWitness(OWNER));
ByteString proposal_executed_time_bytearray = (ByteString)new byte[] { PREFIX_PROPOSAL_EXECUTED_TIME };
StorageMap proposal_executed_time_map = new(Storage.CurrentContext, proposal_executed_time_bytearray);
proposal_executed_time_map.Put((ByteString)id, (ByteString)time);
}

public static void Update(ByteString nefFile, string manifest)
{
ExecutionEngine.Assert(Runtime.CheckWitness(OWNER));
ContractManagement.Update(nefFile, manifest, null);
}
}
}