/ contracts / DTwitter.sol
DTwitter.sol
  1  pragma solidity ^0.4.24;
  2  
  3  contract DTwitter {
  4      /**
  5       * User
  6       *
  7       * Struct holding the profile deatils of the user.
  8       */
  9      struct User {
 10          uint creationDate;      // date user was created
 11          string username;        // username of the user
 12          string description;     // user profile description
 13          address owner;          // address of the account who created the user
 14          string picture;         // IFPS hash of the user's profile picture
 15          string[] tweets;        // array that holds the user's tweets
 16      }
 17  
 18      /**
 19       * users
 20       *
 21       * Maps the keccak256 hash of a username to the deatils of the user
 22       *
 23       * {bytes32} [KEY] the keccak256 hash of the username
 24       * {User} the User struct containing the deatils of the user
 25       */
 26      mapping (bytes32 => User) public users;
 27  
 28      
 29      /**
 30       * owners
 31       *
 32       * Maps the address of the owner account to the username hash of the 
 33       * owned user. This is needed so we can retrieve an account from the
 34       * current address
 35       *
 36       * {address} [KEY] the address of the owner who owns the user
 37       * {bytes32} the keccak256 hash of the username
 38       */
 39      mapping (address => bytes32) public owners;
 40  
 41      /**
 42       * NewTweet
 43       *
 44       * Event to be emitted once a tweet is stored in the contract
 45       * {bytes32} _from - keccak256-hashed username of user who posted the tweet.
 46       *                          This field is indexed so it can be filtered.
 47       * {string} tweet - the tweet contents
 48       */
 49      event NewTweet(
 50          bytes32 indexed _from,
 51          string tweet,
 52          uint time
 53      );
 54  
 55      /**
 56       * createAccount
 57       *
 58       * Creates a user account, storing the user (and user details) in the contract.
 59       * Additionally, a mapping is created between the owner who created the user
 60       * (msg.sender) and the keccak256-hash of the username
 61       * {string} username - the username of the user
 62       * {string} description - the user profile description
 63       */
 64      function createAccount(string username, string description) public {
 65          // ensure a null or empty string wasn't passed in
 66          require(bytes(username).length > 0);
 67  
 68          // generate the username hash using keccak
 69          bytes32 usernameHash = keccak256(abi.encodePacked(username));
 70  
 71          // reject if username already registered
 72          require(users[usernameHash].creationDate == 0);
 73  
 74          // reject if sending adddress already created a user
 75          require(owners[msg.sender] == 0);
 76  
 77          // add a user to the users mapping and populate details
 78          users[usernameHash].creationDate = now;
 79          users[usernameHash].owner = msg.sender;
 80          users[usernameHash].username = username;
 81          users[usernameHash].description = description;
 82  
 83          // add entry to our owners mapping so we can retrieve
 84          // user by their address
 85          owners[msg.sender] = usernameHash;
 86      }
 87  
 88      /**
 89       * editAccount
 90       *
 91       * Edits the deteails of a user's profile.
 92       * {bytes32} usernameHash - the keccak256-hashed username of the user to edit
 93       * {string} description (optional) - the updated user profile description
 94       * {string} pictureHash (optional) - the IFPS hash of the user's updated profile picture
 95       */
 96      function editAccount(bytes32 usernameHash, string description, string pictureHash) public {
 97          // ensure the user exists and that the creator of the user is the
 98          // sender of the transaction
 99          require(users[usernameHash].owner == msg.sender);
100  
101          // update the description (could be empty)
102          users[usernameHash].description = description;
103  
104          // only update the user's picture if the hash passed in is
105          // not empty or null (essentially disallows deletions)
106          if (bytes(pictureHash).length > 0) {
107              users[usernameHash].picture = pictureHash;
108          }
109      }
110  
111      /**
112       * userExists
113       *
114       * Validates whether or not a user has an account in the user mapping
115       * {bytes32} usernameHash - the keccak256-hashed username of the user to validate
116       * {bool} - returns true if the hashed username exists in the user mapping, false otherwise
117       */
118      function userExists(bytes32 usernameHash) public view returns (bool) {
119          // must check a property... bc solidity!
120          return users[usernameHash].creationDate != 0;
121      }
122  
123      /**
124       * tweet
125       *
126       * Adds a tweet to the user's tweets and emits an event notifying listeners
127       * that a tweet happened. Assumes the user sending the transaction is the tweeter.
128       * {string} content - the tweet content
129       */
130      function tweet(string content) public {
131          // ensure the sender has an account
132          require(owners[msg.sender].length > 0);
133  
134          // get the username hash of the sender's account
135          bytes32 usernameHash = owners[msg.sender];
136  
137          // get our user
138          User storage user = users[usernameHash];
139  
140          // get our new tweet index
141          uint tweetIndex = user.tweets.length++;
142  
143          // update the user's tweets
144          user.tweets[tweetIndex] = content;
145  
146          // emit the tweet event and notify the listeners
147          emit NewTweet(usernameHash, content, now);
148      }
149  
150  }