Коллекция и трэйты

В этой части мы будем использовать структуры, созданные ранее, и напишем общедоступные функции для добавления и извлечения данных из нашего контракта.

Коллекции

Для этого контракта мы собираемся хранить наших избирателей в HashMap с AccountId в качестве ключа и экземпляром Voter в качестве значения. HashMap можно импортировать из ящика ink_storage следующим образом:

use ink_storage::collections::HashMap;

Предложения будут храниться в коллекции «Vec», которую можно импортировать из ящика «ink_prelude». ink_prelude — это набор структур данных, которые работают с памятью контракта во время выполнения контракта. Вектор можно импортировать:

    use ink_prelude::vec::Vec;

Векторы могут быть созданы так же, как HashMap. Новые объекты можно добавлять или ссылаться на них из вектора, используя:

    let proposals: Vec<proposals> = Vec::new();
    // adding a new proposal
    proposals.push(
        Proposal{
            name:String::from("Proposal # 1"),
            vote_count: 0,
        });
    // returns the proposal at index 0 if exists else returns None
    let proposal = self.proposals.get(0).unwrap();

Помните, что vector.get возвращает Option, а не фактический объект!

Черты

Черта сообщает компилятору Rust о функциональности определенного типа и может использоваться совместно с другими типами. Подробнее о них можно прочитать здесь. Прежде чем использовать настраиваемые структуры внутри хранилища «Бюллетень», необходимо реализовать определенные черты для структур «Избиратель» и «Предложение». Эти черты включают в себя:

Вы можете узнать больше об этих трейтах по здесь и [здесь](https://paritytech.github.io /ink/ink_storage/traits/index.html). Эти черты реализуются с помощью атрибута derive:

    #[derive(Clone, Copy, Debug, scale::Encode, scale::Decode, SpreadLayout, PackedLayout, scale_info::TypeInfo)]
    struct XYZ {
        ...
        ...
    }

Твоя очередь!

Тебе надо:

Не забудьте запустить cargo +nightly test, чтобы проверить свою работу.

{% tabs %} {% tab title="🔨Starting Point" %}

#![cfg_attr(not(feature = "std"), no_std)]

use ink_lang as ink;

#[ink::contract]    
mod ballot {
    use ink_storage::collections::HashMap;
    use ink_prelude::vec::Vec;
    use ink_storage::traits::{PackedLayout, SpreadLayout};

    // Structure to store Proposal information
    #[derive(Clone, Debug, scale::Encode, scale::Decode, SpreadLayout, PackedLayout,scale_info::TypeInfo)]
    struct Proposal {
        name: String,
        vote_count: u32, 
    }

    // Structure to store Proposal information
    #[derive(Clone, Debug, scale::Encode, scale::Decode, SpreadLayout, PackedLayout,scale_info::TypeInfo)]
    pub struct Voter {
        weight: u32,
        voted: bool,
        delegate: Option<AccountId>, 
        vote: Option<i32>, 
    }

    /// Defines the storage of your contract.
    /// Add new fields the struct in order
    /// to add new static storage fields to your contract.
    #[ink(storage)]
    pub struct Ballot {
        chair_person: AccountId,
        //  ACTION: create a voters hash map with account id as key and Voter as value
        voters: 

        //  ACTION: create a proposals vector
        proposals: 
    }

    impl Ballot {
        #[ink(constructor)]
        pub fn new() -> Self {
            // get chair person address
            let chair_person =  Self::env().caller();

            //  ACTION: create empty proposals and voters variables
            //          * let proposals = 
            //          * let mut voters =

            // ACTION: add chair persons voter object in voters hash map
            // HINT: Use hashmap.insert(key,value)

            Self {
                chair_person,
                voters,
                proposals,
            }
        }

        // return chair person id
        #[ink(message)]
        pub fn get_chairperson(&self) -> AccountId {
            self.chair_person
        }

        // return the provided voter object
        pub fn get_voter(&self, voter_id: AccountId) -> Option<&Voter>{
        }

        // return the count of voters
        pub fn get_voter_count(&self) -> usize{
        }


        /// the function adds the provided voter id into possible
        /// list of voters. By default the voter has no voting right,
        /// the contract owner must approve the voter before he can cast a vote
        #[ink(message)]
        pub fn add_voter(&mut self, voter_id: AccountId) -> bool{
            // ACTION: check if voter already exits, if yes return false       
            //      * if not exists, create an entry in hash map 
            //      * with default weight set to 0 and voted to false
            //      * and return true

            // HINT: use hashmap.get() to get voter
            //        and use options.some() to check if voter exists
        }

        /// given an index returns the name of the proposal at that index
        pub fn get_proposal_name_at_index(&self, index:usize) -> &String {
        }

        /// returns the number of proposals in ballet
        pub fn get_proposal_count(&self) -> usize {
        }


        /// adds the given proposal name in ballet
        pub fn add_proposal(&mut self, proposal_name: String){
        }




    }

    /// Unit tests in Rust are normally defined within such a `#[cfg(test)]`
    /// module and test functions are marked with a `#[test]` attribute.
    /// The below code is technically just normal Rust code.
    #[cfg(test)]
    mod tests {
        /// Imports all the definitions from the outer scope so we can use them here.
        use super::*;

        // Alias `ink_lang` so we can use `ink::test`.
        use ink_lang as ink;

        #[ink::test]
        fn new_works() {
            let mut proposal_names: Vec<String> = Vec::new();
            proposal_names.push(String::from("Proposal # 1"));  
            let ballot = Ballot::new();
            assert_eq!(ballot.get_voter_count(),1);
        }

        #[ink::test]
        fn adding_proposals_works() {
            let mut ballot = Ballot::new();
            ballot.add_proposal(String::from("Proposal #1"));
            assert_eq!(ballot.get_proposal_count(),1);
        }

        #[ink::test]
        fn adding_voters_work() {
            let mut ballot = Ballot::new();
            let account_id = AccountId::from([0x0; 32]);
            assert_eq!(ballot.add_voter(account_id),true);
            assert_eq!(ballot.add_voter(account_id),false);
        }
    }
}

{% endtab %}

{% tab title="✅Potential Solution" %}

#![cfg_attr(not(feature = "std"), no_std)]

use ink_lang as ink;

#[ink::contract]    
mod ballot {
    // use Hash
    use ink_storage::collections::HashMap;
    use ink_prelude::vec::Vec;
    use ink_storage::traits::{PackedLayout, SpreadLayout};

    // Structure to store Proposal information
    #[derive(Clone, Debug, scale::Encode, scale::Decode, SpreadLayout, PackedLayout,scale_info::TypeInfo)]
    struct Proposal {
        name: String,
        vote_count: u32, 
    }

    // Structure to store Proposal information
    #[derive(Clone, Debug, scale::Encode, scale::Decode, SpreadLayout, PackedLayout,scale_info::TypeInfo)]
    pub struct Voter {
        weight: u32,
        voted: bool,
        delegate: Option<AccountId>, 
        vote: Option<i32>, 
    }

    /// Defines the storage of your contract.
    /// Add new fields to the below struct in order
    /// to add new static storage fields to your contract.
    #[ink(storage)]
    pub struct Ballot {
        chair_person: AccountId,
        voters: HashMap<AccountId, Voter>,
        proposals: Vec<Proposal>    
    }

    impl Ballot {
        #[ink(constructor)]
        pub fn new() -> Self {
            // get chair person address
            let chair_person =  Self::env().caller();

            // create empty propsal and voters
            let proposals: Vec<Proposal> = Vec::new();
            let mut voters = HashMap::new();

            // initialize chair person's vote
            voters.insert(chair_person, Voter{
                weight:1,
                voted:false,
                delegate: None,
                vote: None,
            });

            Self {
                chair_person,
                voters,
                proposals,
            }
        }


        #[ink(message)]
        pub fn get_chairperson(&self) -> AccountId {
            self.chair_person
        }



        pub fn get_voter(&self, voter_id: AccountId) -> Option<&Voter>{
            self.voters.get(&voter_id)
        }

        pub fn get_voter_count(&self) -> usize{
            self.voters.len() as usize
        }

                /// the function adds the provided voter id into possible
        /// list of voters. By default the voter has no voting right,
        /// the contract owner must approve the voter before he can cast a vote
        #[ink(message)]
        pub fn add_voter(&mut self, voter_id: AccountId) -> bool{

            let voter_opt = self.voters.get(&voter_id);
            // the voter does not exists
            if voter_opt.is_some() {
                return false
            }

            self.voters.insert(voter_id, Voter{
                weight:0,
                voted:false,
                delegate: None,
                vote: None,
            });
            return true
        }



        /// given an index returns the name of the proposal at that index
        pub fn get_proposal_name_at_index(&self, index:usize) -> &String {
            let proposal = self.proposals.get(index).unwrap();
            return &proposal.name
        }

        /// returns the number of proposals in ballet
        pub fn get_proposal_count(&self) -> usize {
            return self.proposals.len()
        }

        /// adds the given proposal name in ballet
        /// to do: check unqiueness of proposal,
        pub fn add_proposal(&mut self, proposal_name: String){
            self.proposals.push(
                Proposal{
                    name:String::from(proposal_name),
                    vote_count: 0,
            });
        }

    }

    /// Unit tests in Rust are normally defined within such a `#[cfg(test)]`
    /// module and test functions are marked with a `#[test]` attribute.
    /// The below code is technically just normal Rust code.
    #[cfg(test)]
    mod tests {
        /// Imports all the definitions from the outer scope so we can use them here.
        use super::*;

        // Alias `ink_lang` so we can use `ink::test`.
        use ink_lang as ink;

        #[ink::test]
        fn new_works() {
            let mut proposal_names: Vec<String> = Vec::new();
            proposal_names.push(String::from("Proposal # 1"));  
            let ballot = Ballot::new();
            assert_eq!(ballot.get_voter_count(),1);

{% endtab %} {% endtabs %}